ぐだぐだ低レベルプログラミング(238)x86(16bit)、PUSHA、POPA

Joseph Halfmoon

今回は、8086になく80186/80286で拡張されている命令の練習その3ということでPUSHA、POPA命令です。汎用レジスタマシンと言うにはレジスタ型付けが強いx86の全「汎用レジスタ」を一気にPUSH/POPする命令デス。でもスタックポインタ(SP)もPUSH/POPの対象に見えるのよ。大丈夫なのか?

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

※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3
PUSHA、POPA命令

x86は、「汎用レジスタ・マシン」というには「汎用」に?が付きそうなひんまがったアーキテクチャで有名です(個人の感想デス。)まあ、建前8本の汎用レジスタが存在してます。「スクな」とか言わないこと。登場は半世紀も前なのだから。

それら8本の「汎用」レジスタをスタックに一気にPUSH、POPするのが、PUSHAとPOPA命令です。ただし、スタック・ポインタ(SP)も汎用レジスタの1本として含まれてます。PUSHAの時にプッシュされるSPって、どんな値なの? POPAの時にSPにポップされる値で何か良からぬことが起きないの?気になって夜も眠れなくなります。大げさな。

実際には以下を御覧じろ。pusha_popa_diagram

    • PUSHAの時にPUSHされるSPの値は、上記で SP before PUSHAの値である
    • POPAの時にメモリ上のSP対応の値は読み出されるけど捨てられる

なんてこったい、SPだけはちゃんと特別扱いであります。まあ、Armが持っている複数レジスタをまとめてセーブしてくれる命令などをみてしまうと、x86のPUSHA、POPAは古色蒼然とした感じがしないでもないけど、まあ、使うときは使うよ(Armの命令は、それでもRISCか、と難癖つけたくなるケド。)

なお、PUSHA、POPAのレジスタの順番、PUSH時でならべると

AX, CX, DX, BX, SP, BP, SI, DI

という順番デス。AXの次がなぜBXでないのかは、御先祖の8ビットCPU8080に遡のぼる長い歴史あり。過去回のどこかに書いた気がします。

今回実験のプログラム

今回は以下のような処理を行ってみてます。

    1. レジスタの順番が分かるような即値を各レジスタに格納(SPだけは個別)
    2. PUSHAを実行(実行後、メモリ上のスタックをデバッガで確認)
    3. メモリ上のSP対応のメモリに「殊更な」値を書きこんで破壊
    4. POPAを実行(レジスタの値、特にSPについて確認)

以下は「強力なx86用アセンブラNASM」用のソースです(MSのMASMともインテルASM86とも微妙に異なるけど、まあ分かるっしょ。)

segment code
..start:
    mov ax, data
    mov ds, ax
    mov ax, stack
    mov ss, ax
    mov sp, stacktop
prepare:
    mov ax, 1000h
    mov cx, 2000h
    mov dx, 3000h
    mov bx, 4000h
    mov bp, 6000h
    mov si, 7000h
    mov di, 8000h
test:
    pusha
    mov bp, sp
    mov word [bp + 6], 5000h
    popa
    nop
fin:
    mov ax, 0x4c00
    int     0x21
    resb    2048

segment data    align=16
    resb    1024 * 63

segment stack   class=STACK align=16
    resb    2048
stacktop:
    dw 1024 dup (0)
stackend:
アセンブルして実行

MS-DOS互換で機能強化されているフリーなFreeDOS上、以下のステップで上記のアセンブラソース pusha.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)

nasm -f obj -l pusha.lst pusha.asm
wlink name pusha.exe format dos file pusha.obj
debugx pusha.exe

まずは逆アセンブルリスト。unassemPUSHA

赤枠1個目のPUSHA直後の黄色枠内で、メモリ上にPUSHされたSP対応の値を破壊(0x5000を書き込み)してます。なお、ベースレジスタにBPを使うと、SSセグメントに対する操作となります、念のため。

さて、PUSHA前のレジスタの様子が2行目の黄色枠+赤色枠部分に。PUSHA

PUSHA後のSP(レジスタ値)が緑枠に。

中ほどのメモリダンプでは、2行目の黄色枠+赤色枠部分の値がメモリ上に送られていることが分かります(順番は説明図の順番ね。)なお、繰り返しになりますが、「リトルエンディアン」なので、00 80 と書いてあったら 0x8000 のことです。また、メモリ上のSP対応部分はPUSHA前のSP値っす。

そしてピンクな0x5000 で、メモリ上のSP対応部分を「つぶして」ます。POPA後の様子が以下に。POPA

メモリダンプのピンク部分のように「つぶされて」いますけど、POPAすると、どこ吹く風で元に戻ってます。メモリ上でSPを弄ってもダメなのよ。

ぐだぐだ低レベルプログラミング(237)x86(16bit)、BOUND、添え字のチェック へ戻る

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です