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?

「手探りでCUI OS作成に挑む」処理篇 x86スタックの初期化

Last updated at Posted at 2025-05-20

「手探りでCUI OS作成に挑む」連載

この記事は「手探りでCUI OS作成に挑む」連載の一部です。
全体の目次は「手探りでCUI OS作成に挑む」連載目次を御覧下さい。

初期化処理

ss:spを9000:FFFFで初期化するコード。
(必ずしもスタックをこの場所に設置しないといけない訳ではない。)

    mov ax,0x9000
    mov ss,ax
    mov sp,0xFFFF

メモリ分布

ブートローダから0x7E00に第2セクタ以降のプログラムを読み込んだ場合のメモリ分布。
今回はブートローダのみで0x7E00にプログラムは読み込んでいない。

+-------------------+ 0x00000
|     IVT           | 割り込みベクタテーブル (1KB)
| (割り込みベクタ)   | BIOSが使用
+-------------------+ 0x00400
|    BIOSデータ領域  | BIOSデータ
+-------------------+ 0x00500
|                   |
|   利用可能メモリ領域| (約28KB)
|                   |
+-------------------+ 0x7C00
| [BIOSブートセクタ] | 512バイト
| (ブートローダ)     | 
+-------------------+ 0x7E00
|自分で書いたプログラム | ← org 0x7E00
|   (startラベル開始) | 
+-------------------+ 0x9000
|                   |
|   スタック領域     | ← SS:SP = 0x9000:0xFFFF
|   (下方向に成長)    | 実際のスタックトップは 0x9000:0xFFFE
|                   | (push時にSPが2減るため)
+-------------------+ 

POPの時 SP = SP - 2
PUSHの時 SP = SP + 2
SPが0xFFFF→0x0000まで
(0xFFFF - 0x0000 + 1) / 2 = 32768回 POP可能

STACK操作の例とレジスタの変化

mov ax, 0x1234
mov bx, 0x5678
push ax
push bx
mov ax, 0xAAAA
mov bx, 0xBBBB
pop bx
pop ax

検証コード

stack.asm
BITS 16
org 0x7C00

start:
    ; スタックとレジスタの初期化
    ; SS:SP = 0x9000:0xFFFF (スタックは0x9FFFFから0x00000向きに伸びる)
    mov ax,0x9000
    mov ss,ax
    mov sp,0xFFFF

    ; レジスタ状態表示
    
    mov ax, 0x1234 
    call print_state

    mov bx, 0x5678
    call print_state

    push ax
    call print_state
    
    push bx
    call print_state

    mov ax, 0xAAAA
    call print_state
    
    mov bx, 0xBBBB
    call print_state
    
    pop bx
    call print_state
    
    pop ax
    call print_state

    cli             ; 割り込み禁止
    hlt             ; プロセッサを停止

; 現在のレジスタ状態を表示(AX/BX/SS/SPは変更しない)
print_state:
    ; 1. AXレジスタの値を表示
    mov si, ax_str
    call print_str
    mov cx, ax          ; AXの値をCXにコピー
    call print_hex_word ; CXの値を16進数で表示

    ; 2. BXレジスタの値を表示
    mov si, bx_str
    call print_str
    mov cx, bx          ; BXの値をCXにコピー
    call print_hex_word

    ; 3. SSレジスタ(スタックセグメント)の値を表示
    mov si, ss_str
    call print_str
    mov cx, ss          ; SSの値をCXにコピー
    call print_hex_word

    ; 4. SPレジスタ(スタックポインタ)の値を表示
    mov si, sp_str
    call print_str
    mov cx, sp          ; SPの値をCXにコピー
    call print_hex_word
    
    ; 改行
    mov si, newline
    call print_str
    ret

; 補助関数:CXレジスタの16進数値を表示
print_hex_word:
    push ax             ; AXをスタックに保存
    push bx             ; BXをスタックに保存
    mov bx, cx          ; CXの値をBXにコピー
    mov al, bh          ; 上位バイトをALに移動
    call print_hex_byte ; 上位バイトを表示
    mov al, bl          ; 下位バイトをALに移動
    call print_hex_byte ; 下位バイトを表示
    pop bx              ; BXを復元
    pop ax              ; AXを復元
    ret

; 1バイトの16進数表示 (ALレジスタの値)
print_hex_byte:
    push ax
    push cx
    mov cl, 4
    shr al, cl          ; 上位4ビットを取得
    call .nibble        ; 上位4ビットを表示
    pop cx
    pop ax
    push ax
    push cx
    and al, 0x0F        ; 下位4ビットを取得
    call .nibble        ; 下位4ビットを表示
    pop cx
    pop ax
    ret
.nibble:
    add al, '0'         ; ASCIIコードに変換
    cmp al, '9'         ; 数字か確認
    jbe .digit          ; 0-9ならそのまま表示
    add al, 7           ; A-Fの場合の補正
.digit:
    mov ah, 0x0E        ; BIOSテレタイプ機能
    int 0x10            ; 文字表示
    ret
    
; 文字列表示関数 (DS:SI = 文字列ポインタ)
print_str:
    pusha               ; すべての汎用レジスタを保存
    mov ah, 0x0E        ; BIOSテレタイプ機能
    mov bx, 0x0007      ; ページ番号0、表示属性7(灰色)
.loop:
    lodsb               ; [DS:SI]からALに読み込み、SIをインクリメント
    test al, al         ; 終端文字(0)かチェック
    jz .done            ; 終端なら終了
    int 0x10            ; BIOS文字表示
    jmp .loop
.done:
    popa                ; 汎用レジスタを復元
    ret

; 表示用文字列定義
ax_str db " ax=0x", 0
bx_str db " bx=0x", 0
ss_str db " ss=0x", 0
sp_str db " sp=0x", 0
stack_header db " stack: 0x", 0
newline db 13, 10, 0  ; CR+LF

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

動作確認

nasm stack.asm -f bin -o stack.bin
qemu-system-i386 -fda stack.bin

image.png
POPの時 SP = SP - 2
PUSHの時 SP = SP + 2
が確認できる。

※追記
msiパソコン(bios起動)、8086実機でも試してみた
8086は何故か無限に同じものを表示し続けてしまう。
IMG_1856.jpeg
IMG_1851.jpeg

追記

※callした段階で戻りアドレスが保存されるのでspが2減ります。
pushする前にspが既に0xFFFDとなっているのはその為です
慎重に検証するには関数呼び出しにjmpを使う必要がありました。
後日試します。

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?