
前回はストリング命令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番目が変換後のデスティネーション文字列です。全部大文字になっとります。
