
前回はストリング命令SCASとCMPSを練習しました。どちらもREPプリフィックスでCX回繰り返し、途中ゼロフラグで継続か中断する判断するのに向いてます。しかしストリング命令と括られる中にはREPプリフィックスと「相性」の良くない命令があります。LODSとSTOSです。でもこいつら意外と便利なんだ、ホントか?
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
LODSとSTOS
LODSは、DS:SIで指定されるメモリからAL(バイト)またはAX(ワード)レジスタにメモリ上の値をロードする命令です。実行後、DF(ディレクション・フラグ)の指定に従いSIの値が転送幅に応じて更新されます。転送先はキメウチのレジスタなのでこれにREPプリフィックスを付加して繰り返しても有意義とは思えませぬ(時間つぶしだけ?)しかし、x86系のメモリアドレシングにはスタック操作を除くと自動インクリメント/デクリメント系のアドレシングが欠けてます。そこにLODS使う意義があるみたいです。
一方STOSは、ES:DIで指定されるメモリへAL(バイト)またはAX(ワード)の内容を転送する命令です。こちらも、実行後、DFの指定に従いDIの値が転送幅に応じて更新されます。STOSの場合、メモリフィル操作のようなケースでは、REPプリフィックスを使って繰り返す意義がある、というものです。しかしそれ以外ではREP無で、自動インクリメント/デクリメント系のアドレシングを使うための命令とみた方が良い感じです。
今回実験のプログラム
強力なx86用アセンブラNASM用のソースです。メモリにおいた “Hello worldza”復帰改行というソース文字列中の英小文字のみを大文字に変換してデスティネーション文字列として書きこむもの。末尾のzaはテスト用です。変換前と変換後でMS-DOSのファンクション0x09を使い2つの文字列を印字してます。小文字以外は何もせずに転送するだけなので、変換後は全英大文字の文字列が印字される筈。
segment code ..start: mov ax, data mov ds, ax mov es, ax mov ax, stack mov ss, ax mov sp, stacktop beforet: mov dx, srcblk mov ax, 0x0900 int 0x21 mov dx, dstblk mov ax, 0x0900 int 0x21 mov si, srcblk mov di, dstblk test: lodsb cmp al, '$' jz aftert cmp al, 'a' jae lbla jmp next lbla: cmp al, 'z' ja next sub al, 'a' - 'A' next: stosb jmp test aftert: stosb mov dx, srcblk mov ax, 0x0900 int 0x21 mov dx, dstblk mov ax, 0x0900 int 0x21 fin: mov ax, 0x4c00 int 0x21 resb 2048 segment data align=16 resb 1024 * 63 srcblk: db 'Hello worldza',13,10,'$' dstblk: db '.............',13,10,'$' segment stack class=STACK align=16 resb 2048 stacktop: dw 1024 dup (0) stackend:
アセンブルして実行
MS-DOS互換で機能強化されてるフリーなFreeDOS上、以下のステップで上記のアセンブラソース lods.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)
nasm -f obj -l lods.lst lods.asm wlink name lods.exe format dos file lods.obj lods.exe
今回はいつもと異なり、デバッガを使わず、MS-DOSのシステムコールで文字列を画面に印字して、動作を確認しています。
一番上が、変換実行前のソース文字列。2番目が転送前のデスティネーション文字列、最初はピリオドが詰まっております。そして3番目が変換後のソース文字列、当然、さきほどと同じ。最後4番目が変換後のデスティネーション文字列です。全部大文字になっとります。