0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

8086:jmp/call/intによるSPとスタックの変化

0
Last updated at Posted at 2025-09-16

目的

x86 16bitモードではCS:IPで実行中のアドレスを指します。
このCS:IPを変更する命令は主に3つあります。
jmp:単純に別のアドレスへ跳ぶ
call:関数呼び出しに使う。跳んだ先の処理が終わるとretで呼び出し元へ返る
int:主にBIOSやMS-DOSの割り込みに使用する

今回はこれらの実行時のスタックの変化を観察します。

x86 16bit 実行時のスタック挙動

命令 スタックに積まれる値 備考
jmp なし 単純ジャンプ。呼び出し元の情報は積まれない
call IP(次の命令のアドレス) Far call(セグメントを跨ぐ) の場合は CS も積まれる
int FLAGS、CS、IP 割り込み処理へ跳ぶ前に自動で積まれる

実験用ソース

※実機で検証する前にqemuで動作確認をしているので注釈中にそのままコマンドを残しています。

jmp

何も積まれません。
727b7ca5d42828.jpg

7C00
7C00
jmp.asm
; nasm -f bin jmp.asm -o jmp.bin
; qemu-system-i386 -drive format=raw,file=jmp.bin

[org 0x7C00]
bits 16

start:
    cli
    xor ax, ax
    mov ds, ax
    mov ss, ax
    mov sp, 0x07C00
    sti

    ; 呼び出し前のSP表示
    mov ax, sp
    call print_ax_hex

    jmp testsg

testsg:
    ; 呼び出し後のSP表示
    mov ax, sp
    call print_ax_hex
.hlt_loop:
    hlt
    jmp .hlt_loop

%include "util.asm"

buf db '0000'

times 510-($-$$) db 0
dw 0xAA55

call

a3361f09855cb8.jpg

7C00
7BFE
7C13
7C00

呼び出す前のSP=7C00
関数内のSP=7BFE
スタックに積まれた値=7C13
返った後のSP=7C00
call後SP7C007BFEへ変化していることから2バイト(レジスタ1つ分)積まれていることが分かる。
スタック中の値7C13からIPが積まれていることが分かり、16ビットモードの仕様と一致する。

call.asm
; nasm -f bin call.asm -o call.bin
; qemu-system-i386 -drive format=raw,file=call.bin

[org 0x7C00]
bits 16

start:
    cli
    xor ax, ax
    mov ds, ax
    mov ss, ax
    mov sp, 0x07C00
    sti

    ; 呼び出し前のSP表示
    mov ax, sp
    call print_ax_hex
    call testsg
    
    ; 返った後のSP表示
    mov ax, sp
    call print_ax_hex

.hlt_loop:
    hlt
    jmp .hlt_loop

testsg:
    ; 呼び出し後のSP表示
    mov ax, sp
    call print_ax_hex
    
    pop bx ; IP取り出し
    
    mov ax, bx
    call print_ax_hex
    
    push bx
    
    ret


%include "util.asm"

buf db '0000'

times 510-($-$$) db 0
dw 0xAA55

far call

b598449ee43b88.jpg

7C00
7BFC
7C2D
0000
7C00

呼び出す前のSP=7C00
関数内のSP=7BFC
スタックに積まれた値=7C2D
スタックに積まれた値=0000
返った後のSP=7C00
call後SP7C007BFCへ変化していることから4バイト(レジスタ2つ分)積まれていることが分かる。
スタック中の値7C2D,0000からIP及びCSが積まれていることが分かり、16ビットモードの仕様と一致する。

callfar.asm
; nasm -f bin callfar.asm -o callfar.bin
; qemu-system-i386 -drive format=raw,file=callfar.bin

org 0x7C00
bits 16

start:
    cli
    xor ax, ax
    mov ds, ax
    mov ss, ax
    mov sp, 0x07C00
    sti

    ; Far Call 用コードを 0x1000:0x0000 に複製
    mov si, far_label_source
    mov cx, far_label_end - far_label_source
    mov di, 0
    mov ax, 0x1000
    mov es, ax
.copy_loop:
    mov al, ds:[si]
    mov es:[di], al
    inc si
    inc di
    loop .copy_loop

    ; SP を表示 (call前)
    mov ax, sp
    call print_ax_hex

    ; Far Call
    call 0x1000:0x0000
    call print_ax_hex ; spは既にaxへ格納済み
    
    ; IP
    mov ax,bx
    call print_ax_hex 
    
    ; CS
    mov ax,cx
    call print_ax_hex 

    ; SP を表示 (call後)
    mov ax, sp
    call print_ax_hex

.hlt_loop:
    hlt
    jmp .hlt_loop

; Far Call 先のコード(複製用)
far_label_source:
    mov ax, sp
    
    pop bx ; IP取り出し
    pop cx ; CS取り出し
    
    push cx ;呼び出し元へ返る為にスタックを直す
    push bx
    
    retf
far_label_end:

%include "util.asm"

buf db '0000',0

; -----------------------
times 510-($-$$) db 0
dw 0xAA55

int

82e323ca049dd8.jpg

7C00
7BFA
7C1E
0000
F246
7C00

呼び出す前のSP=7C00
関数内のSP=7BFA
スタックに積まれた値=7C1E
スタックに積まれた値=0000
スタックに積まれた値=F246
返った後のSP=7C00
call後SP7C007BFAへ変化していることから6バイト(レジスタ3つ分)積まれていることが分かる。
スタック中の値7C2D,0000,0246からIP,CS及びフラグレジスタが積まれていることが分かり、16ビットモードの仕様と一致する。

int.asm
; nasm -f bin int.asm -o int.bin
; qemu-system-i386 -drive format=raw,file=int.bin

[org 0x7C00]
bits 16

start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; 0x80番の割り込みにハンドラをセット
    mov word [0x80*4], handler     ; IP
    mov word [0x80*4+2], cs        ; CS
    sti

    ; main SP
    mov ss, ax
    mov sp, 0x7C00

    ; 呼び出し前のSP表示
    mov ax, sp
    call print_ax_hex

    ; INT 0x80 実行
    int 0x80
    call print_ax_hex
    
    ; IP
    mov ax,bx
    call print_ax_hex
    
    ; CS
    mov ax,cx
    call print_ax_hex
    
    ; FLAG
    mov ax,dx
    call print_ax_hex

    ; 返った後のSP表示
    mov ax, sp
    call print_ax_hex
    

.hlt_loop:
    hlt
    jmp .hlt_loop

; 割り込みハンドラ
handler:
    ; 割り込み時のSP表示
    mov ax, sp
    
    pop bx ; IP
    pop cx ; CS
    pop dx ; FLAG
    
    push dx ;呼び出し元へ返る為にスタックを直す
    push cx
    push bx

    iret

%include "util.asm"

buf db '0000',0

; -----------------------
times 510-($-$$) db 0
dw 0xAA55

レジスタ値表示関数(全ソース共通)

util.asm
; AX を16進4桁で表示(BIOSで表示)
print_ax_hex:
    push ax
    push bx
    push cx
    push dx
    push si

    mov bx, ax
    lea si, [buf+3]
    mov cx, 4

.next_nibble:
    mov al, bl
    and al, 0x0F
    cmp al, 10
    jb .digit
    add al, 'A'-10
    jmp .store
.digit:
    add al, '0'
.store:
    mov [si], al
    shr bx, 4
    dec si
    loop .next_nibble

    ; ---- BIOSで1文字ずつ表示 ----
    mov si, buf
.print_loop:
    mov al, [si]
    mov ah, 0x0E
    int 0x10
    inc si
    cmp si, buf+4
    jne .print_loop

    ; 改行
    mov al, 0x0D
    mov ah, 0x0E
    int 0x10
    mov al, 0x0A
    int 0x10

    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    ret

値まとめ

命令 SP 呼び出し前 スタックに積まれた値 SP 関数内 SP 返った後
jmp 7C00 無し 7C00 7C00
call 7C00 7C13 (IP) 7BFE 7C00
far call 7C00 7C2D (IP), 0000 (CS) 7BFC 7C00
int 7C00 7C1E (IP), 0000 (CS), F246(FLAGS) 7BFA 7C00

実行環境

・PC: Pocket8086
・CPU: 8086
・BIOS: PC-XT System BIOS
・OS: MS-DOS 6.22

おまけ

現代のPCでもBIOS設定を変えると直接起動可能です。

4017bee5843f1.jpg
90448606542078.jpg

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?