スタック
スタックにデータを積む
あるデータを一時的な保護のために保存し
後でそれを再び呼び出す処理を行う場合はスタックを用います
スタックのアルゴリズムについては説明不要でしょう
スタックにデータを積むことをプッシュ、取り出すことをポップと呼びます
8086はスタックのアルゴリズムを提供しますが、メモリは特別な場所ではなく
私たちが普段使っているメモリ領域と同じ場所です
そのため、データ領域などの保護もプログラマの責任になります
スタック領域は、レジスタSS : SPの位置がスタックの対象となります
スタックにデータを積むにはPUSH命令を用います
第一オペランドに、16ビットレジスタ、またはメモリを指定します
8086のスタック命令は常に2バイト(1ワード)が対象となります
最後に積んだスタックからデータを取り出すにはPOP命令を指定します
第一オペランドにはポップしたデータを格納する16ビットのレジスタまたはメモリを指定します
CPUはPUSH命令を指定されるとSPを2デクリメントし、POPされればSPをインクリメントします
これらの動作は自動的に行われるため、プログラマが明示的にコードを書く必要はありません
SPレジスタの初期値は、DEBUGでは最初に割り当てられるので明示する必要はありませんが
環境によっては、SPレジスタの値を最初にMOVで初期化しなければいけません
15F2:0100 MOV AX , AABB
15F2:0103 PUSH AX
15F2:0104 MOV AX , 0
15F2:0107 POP AX
15F2:0108
-T =100 4
AX=AABB BX=0304 CX=0000 DX=EEFF SP=FFEE BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0103 NV UP EI PL NZ NA PO NC
15F2:0103 50 PUSH AX
AX=AABB BX=0304 CX=0000 DX=EEFF SP=FFEC BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0104 NV UP EI PL NZ NA PO NC
15F2:0104 B80000 MOV AX,0000
AX=0000 BX=0304 CX=0000 DX=EEFF SP=FFEC BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0107 NV UP EI PL NZ NA PO NC
15F2:0107 58 POP AX
AX=AABB BX=0304 CX=0000 DX=EEFF SP=FFEE BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0108 NV UP EI PL NZ NA PO NC
プログラムを作って、それをトレースして一つ一つの動作を確認しています
SPレジスタを見ると、プッシュやポップするたびに値が変動しています
PUSH AX では、AXレジスタの内容をスタックにプッシュしています
その後プログラムではAXレジスタの内容を変更していますが
POP命令でスタックから再びその値をポップしています
一度取り出したデータは論理的に削除されるので、もう呼び出すことはできません
スタックとサブルーチン
サブルーチンは、実はこのスタックを利用しています
サブルーチンをコールした時に、現在のIPレジスタの内容をプッシュしているのです
そのため、サブルーチンは呼び出し元に正確に帰ることができたのです
-A 200
15F2:0200 CALL 204
15F2:0203 RET
15F2:0204 CALL 208
15F2:0207 RET
15F2:0208 RET
15F2:0209
-A 100
15F2:0100 CALL 200
15F2:0103
-T =100 6
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEC BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0200 NV UP EI PL NZ NA PO NC
15F2:0200 E80100 CALL 0204
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEA BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0204 NV UP EI PL NZ NA PO NC
15F2:0204 E80100 CALL 0208
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFE8 BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0208 NV UP EI PL NZ NA PO NC
15F2:0208 C3 RET
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEA BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0207 NV UP EI PL NZ NA PO NC
15F2:0207 C3 RET
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEC BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0203 NV UP EI PL NZ NA PO NC
15F2:0203 C3 RET
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0103 NV UP EI PL NZ NA PO NC
サブルーチンの入れ子構造のプログラムをトレースしたものです
CALLするたびにSPの値が減少し、RETするごとに元に戻っています
これは、CALLするとプッシュしてRETでポップしていることを表します
何をスタックに積んでいるかというと、戻るべきIPレジスタの値です
RETが指定されるとIPレジスタにスタックからPOPすることで元の場所に戻っているのです
プログラマはこのことに注意しながらプログラムを記述しなければいけません
サブルーチン内でプッシュしたりポップするなどをしてもかまいませんが
RETが指定される時は、必ずCALL時にプッシュした時のSPの値でなければ
論理的にデタラメな場所にジャンプし、プログラムが暴走してしまいます
-A 200
15F2:0200 PUSH AX
15F2:0201 MUL AX , 2
15F2:0203
-A 200
15F2:0200 PUSH AX
15F2:0201 ADD AX , DX
15F2:0203 MOV DX , AX
15F2:0205 POP AX
15F2:0206 RET
15F2:0207
-A 100
15F2:0100 MOV AX , A
15F2:0103 MOV DX , 5
15F2:0106 CALL 200
15F2:0109
-G =100 109
AX=000A BX=0000 CX=0000 DX=000F SP=FFEE BP=0000 SI=0000 DI=0000
DS=15F2 ES=15F2 SS=15F2 CS=15F2 IP=0109 NV UP EI PL NZ NA PE NC
このプログラムでは、スタックを利用してAXレジスタの内容を保護しています
サブルーチンへ移行して、サブルーチンはAXレジスタの内容を変更しますが
戻ってきた時はAXレジスタの値は呼び出した時の値と同じであることが保証されます
このプログラムで重要なのは、サブルーチン内でプッシュしたのならば
必ずポップしてからRETするということです
そうでなければ、予想しない値でリターンしてしまうのでプログラムが暴走します