x86(16bit)の練習の最初の方で、ADD、ADC、SUB、SBB、AND、OR、XOR、CMPの8命令を練習。こいつらオペコードマップの目抜き通りに綺麗にならんでます。その中でCMP命令はSUBに似ています。一方AND命令に似ているTEST命令はオペコード的にはトビトビの場所に冷遇?されているんだこれが。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
ファースト・バイト・オペコードマップ(部分)
今回練習するのは以下のオペコードマップの中のオレンジ色の部分を割り当てられているTEST命令です。
TEST命令は、2つのオペランドのANDをとってその結果でフラグを変化させるもの。AND命令との差は、AND取った結果をデスティネーションに書き込むAND命令と、捨ててしまうTEST命令というところだけです。
上のオペコードマップの黄色いところを見ると、AND命令の方は一列に綺麗に割り当てられています。メモリに対する即値の操作のところだけ飛び地になってますが、ここではADD以下の8人衆の一員としてならんでます。
一方、ALUの動作的には同じANDを行うTEST命令はオレンジのところにトビトビに割り当てられてます。ちょっと冷遇されてないか?メモリに対する即値の操作も縁のないものどもと一緒にされちまっているし。
冷遇された理由はTEST命令のオペランドの左と右が交換可能、という1点にあったのじゃないかと思います。同様に「SUBした結果を捨てる」CMP命令の場合、引く数になるか引かれる数になるかで結果は異なるのでSUB命令同様に8個分のオペコードが必要。しかし、TEST命令の場合左右が交換可能なので、オペコード6個あれば足ります。そういう事情があったがために冷遇されて、あちこちのスキマを割り当てられたんでないかと。個人の感想デス。
そんなわけで後回しになっていたTEST命令を今回は練習してみます。
今回実験のプログラム
強力なx86用アセンブラNASM用のソースです。フラグの値はデバッガでトレース実行して確認する予定なので、だらだらとフラグを上げたり下げたりするだけのプログラムです。TEST命令の場合、見なければならないフラグはZF(ゼロフラッグ)っす。
segment code ..start: mov ax, data0 mov ds, ax mov es, ax mov ax, stack mov ss, ax mov sp, stacktop test: mov ax, 0xA55A mov bx, targetw mov cx, 0x0AA0 mov dx, 0xA000 test ax, 0x0100 test ax, 0x5AA5 test al, 0x40 test al, 0x04 test word [bx], 0x2000 test word [bx], 0x1000 test ax, cx test ax, dx fin: mov ax, 0x4c00 int 0x21 resb 2048 segment data0 align=16 resb 1024 * 63 targetw: dw 0x1000 segment stack class=STACK align=16 resb 2048 stacktop: dw 1024 dup (0) stackend:
アセンブルして実行
MS-DOS互換で機能強化されてるフリーなFreeDOS上、以下のステップで上記のアセンブラソース test.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)
nasm -f obj -l test.lst test.asm wlink name test.exe format dos file test.obj debug test.exe
デバッガ起動後、今回のプログラムの逆アセンブルを行ったところ。
さて、まずはAX、ALレジスタに対する即値をとるTEST命令4連発です。見なければならないオペランドの値と結果のZFのところに同色の色枠をつけてあります。
つづいてメモリ(EA)に対する即値TESTと、レジスタ間のTESTです。やはり対応するオペランドの値と結果のZFに同色の枠を描いたもの。
オペコードの位置はトビトビで冷遇されとるが、動作はフツー。出来ることも変わらん。