ぐだぐだ低レベルプログラミング(187)x86(16bit)、DECIMAL ADJUST族

Joseph Halfmoon

前回はBCD補正命令のうち、アンパックドBCD数を扱うASCII ADJUST一族について練習しました。アンパックドあればパックドあり、今回はDECIMAL ADJUSTです。一族といっても2命令だけ。DAAとDAS。しかしDAAこそは御先祖の8ビット機(8080/8085)から受け継ぐ伝統の?命令であります。

※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら

※実機動作確認には以下を使用させていただいております。

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3
ASCII ADJUSTとDECIMAL ADJUST一族の全貌

前回オペコードマップを掲げていなかったので以下に示します。といっても16x16サイズのファーストバイトマップの中で合計6バイト分を占めるにすぎませぬ。以下は縦にハイ側の4ビット分、横にロー側の4ビット分で枠を作ってます。関係ない命令が詰まっているところは飛ばしてます。DAA_AAAopmap

上記をみると、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と「上位互換」のデバッガ)を使って行っております。

まずはDAA(加算後の補正)命令からDAA_1

赤枠のADD命令の実行前のALの値は 0x15、DLの値は 0x08 です。しかしその心はBCD数であるので、0x15とエンコードしてますが十進数の「15」であります。ADD命令実行後のALは 0x1Dです。0x15+0x08=0x1Dなのでこれは2進数そのままの加算結果です。あったりまえか。しかしここで青枠のDAAを発行するとALレジスタは0x23と変わります。十進数で15+8=23なので、BCD数として足し算が出来たことになりました。

同じDAA命令でも、DAA_2

上記のような場合は、「補正」の必要がないのでDAAを発行しても何も起こりませぬ。

つづいてDAS(減算後の補正)命令です。DAS_1

加算のときと同じで、AL=0x22、DL=0x08としてバイナリで減算を行ってAL=0x1Aという結果を得ていますが、DAS命令を発行することでAL=0x14と補正されます。すなわち10進にて、22-8=14 なる計算をやったことになりました。

補正の必要がない場合、DASも何もしないことがあります。こんな感じ。DAS_2

 

しかし、アンパックドのASCII ADJUST系命令には乗算と除算用の補正があったのに、パックドのDECIMAL ADJUSTには不在。まあ、考えてみるととってもメンドイことになりそうなので、やらなかったのか?知らんけど。

ぐだぐだ低レベルプログラミング(186)x86(16bit)、ASCII ADJUST一族 へ戻る