スタック


スタックにデータを積む

あるデータを一時的な保護のために保存し
後でそれを再び呼び出す処理を行う場合はスタックを用います

スタックのアルゴリズムについては説明不要でしょう
スタックにデータを積むことをプッシュ、取り出すことをポップと呼びます

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するということです
そうでなければ、予想しない値でリターンしてしまうのでプログラムが暴走します



前のページへ戻る次のページへ