前回は8086範囲をはみ出すENTER/LEAVE命令でしたが、今回は8086以来のファーポインタ(セグメント間参照)のロード命令、LESとLDSです。そろそろ8086命令は一巡するんじゃないかなどと思っていたのですが、数えてみたらまだまだ残ってます。先が長いのでそろそろ8086範囲は終わらせたいのだけれど。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
ファー・ポインタのロード命令、LESとLDS
8086以来の16ビットx86の場合、マジメにセグメントと向き合わないとならないので、メモリを指すポインタにもセグメント内のオフセットアドレスだけを使うケース(ニア)と、セグメント値とオフセットを組で使うケース(ファー)を使いわけないとなりませぬ。
そのうち、「組」での操作を強いられるファーポインタの場合、セグメント・レジスタへのロードと、ベース・レジスタへのロードを2回にわけるのもなんだかな~ということで以下の2命令が用意されとります。
-
- LDS、DSセグメントレジスタとベースレジスタの2本に値をロード
- LES、ESセグメントレジスタとベースレジスタの2本に値をロード
ここではオフセットアドレスをロードしてアドレシングに使われるのであろうレジスタをベースと表現しています。マニュアル的にはいろいろありーの。オペコードマップ的には以下です。
8086的にはセグメント・レジスタは4本ありますが、ES、DSのみ対象です。残りの2本に対しては用意されてません。コードセグメントCSにロードしてしまうとJMPになってしまうのでデータ・ポインタのロードとは違うし。またスタックセグメントSSは、「ころころ変えるものでもない」(スタックフレームとか割り込みとかあるもんね、変えるときは関係各位の整合性をとらないと)ということなのでしょう。知らんけど。
今回実験のプログラム
強力なx86用アセンブラNASM用のソースです。ファーポインタをロードしてみるのにデータ・セグメントを複数定義してます(ポインタロードだけで使うわけでもないんだけれども。)ブロック転送命令の準備でも使える?雰囲気を醸すためにES:DI、とDS:SIを組にしてロードしています。
segment code ..start: mov ax, data0 mov ds, ax mov es, ax mov ax, stack mov ss, ax mov sp, stacktop test: mov bx, espointer les di, [bx] add bx, 4 lds si, [bx] fin: mov ax, 0x4c00 int 0x21 resb 2048 segment data0 align=16 resb 1024 * 63 espointer: dw estarget dw data1 dspointer: dw dstarget dw data2 segment data1 align=16 resb 1024 * 63 estarget: dw 0 segment data2 align=16 resb 1024 * 63 dstarget: dw 0 segment stack class=STACK align=16 resb 2048 stacktop: dw 1024 dup (0) stackend:
アセンブルして実行
MS-DOS互換で機能強化されてるフリーなFreeDOS上、以下のステップで上記のアセンブラソース leslds.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)今回からアセンブル時にリスティング・ファイルも生成されるように -l オプションを使うことにいたしました。
nasm -f obj -l leslds.lst leslds.asm wlink name leslds.exe format dos file leslds.obj debug leslds.exe
今回実行プログラムはごく短いので、デバッガ上で一画面の逆アセンブルで足ります。
オフセット0x000F番地以降がテスト本体です。以下のトレース履歴で、赤枠をつけてあるのが、LES命令と関係レジスタの前後の値です。また、緑枠をつけてあるのが、LDS命令と関係レジスタの前後の値です。なお、メモリ参照のDS:~のところが両方緑枠になっているのは誤りで上の方は赤枠です。すみません。
まあ、赤枠のLES命令で、ES:DIの2レジスタが1命令でロードされており、緑枠のLDS命令で、DS:SIの2レジスタが1命令でロードされていることが分かると思います。こんだけ。