前回はBCD補正命令のうち、アンパックドBCD数を扱うASCII ADJUST一族について練習しました。アンパックドあればパックドあり、今回はDECIMAL ADJUSTです。一族といっても2命令だけ。DAAとDAS。しかしDAAこそは御先祖の8ビット機(8080/8085)から受け継ぐ伝統の?命令であります。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認には以下を使用させていただいております。
ASCII ADJUSTとDECIMAL ADJUST一族の全貌
前回オペコードマップを掲げていなかったので以下に示します。といっても16x16サイズのファーストバイトマップの中で合計6バイト分を占めるにすぎませぬ。以下は縦にハイ側の4ビット分、横にロー側の4ビット分で枠を作ってます。関係ない命令が詰まっているところは飛ばしてます。
上記をみると、DAAとAAA、DASとAASが仲良く並んで上がパックド、下がアンパックドとなってます。また、DAAとDASの差はロー側のビット3の1ビットのみなので、マップ上は飛んでいるように見えますが実はエンコーディング上は「お隣」です。
一方、AAMとAADは飛び離れたところにオペコードが位置してます。近代的なプロセッサでは、オペコードのエンコーディングなどあまり気にする必要もないかもですが、ことx86_16に関しては意味が大ありです。AAA一族といってもAAMとAADは毛色の違う奴らだ、ということになります。
DAAとDAS
ALレジスタとソースレジスタの両方にパックドBCD数を置いておいて、ALレジスタをデスティネーションとするADD命令(ここではバイナリとして計算)を発した後、ALレジスタに残った値をパックドBCD数に「戻す(補正)」のがDAA命令です。同じ意味の操作をSUB命令後に行うのがDAS命令です。
-
- DAA、DECIMAL ADJUST FOR ADDITION
- DAS、DECIMAL ADJUST FOR SUBTRACTION
なお、ここでのパックドBCD数は、1バイト(8ビット)の下4ビット(下位ニブル)に0~9の数を、上4ビット(上位ニブル)に0~9の数を置いて、あわせて0~99までの10進数として解釈できるようにしたものです。
今回動かしてみるアセンブリ言語ソース
NASMアセンブラ用のソース、FreeDOS上でCOM形式のオブジェクトにして実行するつもりのものが以下に。
org 100h section .text start: mov sp, stacktop test: mov al, 15h mov dl, 08h mov dl, 08h add al, dl daa mov al, 11h add al, dl daa nop mov al, 22h sub al, dl das mov al, 29h sub al, dl das fin: mov ax, 0x4c00 int 0x21 section .data align=16 workw: dw 5678h section .bss align=16 resb 2048 stacktop:
動作確認
上記のソース (daa.asm)から、以下のコマンドライン一発で実行可能なオブジェクトが得られます。
nasm daa.asm -fbin -o daa.com
動作確認は、FreeDOS上の debug コマンド(御本家 マイクロソフト社のdebugと「上位互換」のデバッガ)を使って行っております。
赤枠のADD命令の実行前のALの値は 0x15、DLの値は 0x08 です。しかしその心はBCD数であるので、0x15とエンコードしてますが十進数の「15」であります。ADD命令実行後のALは 0x1Dです。0x15+0x08=0x1Dなのでこれは2進数そのままの加算結果です。あったりまえか。しかしここで青枠のDAAを発行するとALレジスタは0x23と変わります。十進数で15+8=23なので、BCD数として足し算が出来たことになりました。
上記のような場合は、「補正」の必要がないのでDAAを発行しても何も起こりませぬ。
加算のときと同じで、AL=0x22、DL=0x08としてバイナリで減算を行ってAL=0x1Aという結果を得ていますが、DAS命令を発行することでAL=0x14と補正されます。すなわち10進にて、22-8=14 なる計算をやったことになりました。
補正の必要がない場合、DASも何もしないことがあります。こんな感じ。
しかし、アンパックドのASCII ADJUST系命令には乗算と除算用の補正があったのに、パックドのDECIMAL ADJUSTには不在。まあ、考えてみるととってもメンドイことになりそうなので、やらなかったのか?知らんけど。