「手探りでCUI OS作成に挑む」連載
この記事は「手探りでCUI OS作成に挑む」連載の一部です。
全体の目次は「手探りでCUI OS作成に挑む」連載目次を御覧下さい。
ソースコード
前回までの分です。
今回の変更分はまだ反映していません。
https://github.com/ooe1220/sourouOS/tree/20250625
やりたいこと
DIRコマンドを実装する前段階として、今回はDISKから1セクタ分読み込む関数を実装します。
検証中のコード
今回追加したファイル
kernerl.asmから直接呼び出して検証する(臨時)。
0セクタ目にあるVBRのデータを1セクタ分0x9000へ読み込む。
kernel.asm
[BITS 16]
[ORG 0x8000]
start:
; レジスタ初期化
xor ax, ax
mov ds, ax
mov es, ax
; スタック設定
mov ss, ax
mov sp, 0x7C00
mov si, message
call print_string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pusha
push di
mov di, 0x9000
mov bl, 0x00 ; LBA 0-7
mov bh, 0x00 ; LBA 8-15
mov cl, 0x00 ; LBA 16-23
call read_sector ; 1セクタ読み込み
jc .read_error ; エラー
jmp command_loop
pop di
popa
.read_error:
cli
hlt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
...省略...
; ここで64KBまで0埋めする
times 65536-($-$$) db 0
disk.asm
; IDEHDD読み込み
read_sector:
push ax
push bx
push cx
push dx
push ds
; 引数を保存
push bx ; BXレジスタをスタックに保存(bl、bhを含む)
push cx ; CXレジスタをスタックに保存(clを含む)
; ===== 1. HDDが使用可能か =====
mov dx, 0x1F7 ; 状態レジスタポート
mov cx, 0xFFFF ; 最大試行回数
.wait_not_busy:
in al, dx
test al, 0x80 ; 最高位ビット=1かどうか(0x80 = 1000 0000)
jz .ready ; 1でなければ使用可能 →readyへ遷移
dec cx ; 試行回数を減算
jnz .wait_not_busy ; 使用不可の場合は待ち続ける
jmp .error ; 時間切れ
.ready:
; ===== 2. どのドライブを選択するかとLBAアドレスの上位4ビットを設定 =====
mov dx, 0x1F6 ; 「ドライブ/ヘッドレジスタ」
mov al, 0xE0 ; 本文を参照
or al, bh ;
and al, 0xEF ; bit4=0(固定)
out dx, al
; ===== 3. 読み込みセクタ数の設定 =====
mov dx, 0x1F2 ; セクタ数レジスタ:転送するセクタ数を指定するために使用
mov al, 1 ; 1セクタ読み込み
out dx, al
; ===== 4. LBAアドレスの指定 =====
; 引数を取り出す
pop cx
pop bx
mov dx, 0x1F3 ; LBA 0-7
mov al, bl ; bl = LBA 0-7
out dx, al
mov dx, 0x1F4 ; LBA 8-15
mov al, bh ; bh = LBA 8-15
out dx, al
mov dx, 0x1F5 ; LBA 16-23
mov al, cl
out dx, al
; ===== 5. 読み込み命令送信 =====
mov dx, 0x1F7
mov al, 0x20 ; 0x20: セクタ読み込み (参考 0x30: セクタ書き込み)
out dx, al ; 一般的な書き方 outb(0x1F7, 0x20) → 読み込み開始
; ===== 6. データ転送が可能となるのを待つ =====
mov cx, 0xFFFF ; 最大試行回数
.wait_data_ready:
in al, dx
test al, 0x80 ; 読み込み可能か
jnz .wait_data_ready
test al, 0x08
jnz .data_ready
dec cx
jnz .wait_data_ready
jmp .error
.data_ready:
; ===== 7. エラー確認 =====
test al, 0x01 ; 状態レジスタ 0bit目=1はエラー
jnz .error
; ===== 8. データ読み込み =====
mov cx, 256 ; 512バイト(WORD単位で÷2) IDEデータレジスタ(0x1F0)は16ビット(2バイト)単位でデータを扱う
mov dx, 0x1F0 ; データレジスタ(読み書きに使用される)
cld ; 念のため明示的にDF=0に設定し、insw実行時にメモリアドレス(DI)を自動増加(+2)させる。std(DF=1)だと、アドレスが減少(-2)
rep insw ; ES:DIへ読み込む cxの回数だけinswを繰り返し、
pop ds
pop dx
pop cx
pop bx
pop ax
clc ; 成功
ret
.error:
pop ds
pop dx
pop cx
pop bx
pop ax
stc ; 失败
ret
disktest db 'disktest', 0x0D, 0x0A, 0
print_stringにESを破壊してしまう不具合があったのでスタックへ退避するようにした。
vga.asm
print_string:
pusha
push si
push es
...省略...
pop es
pop si
popa
ret
動作確認
qemu上で0x9000から1セクタ分(512バイト)ダンプし、メモリ上に読み込まれているかを確認すると、以下の用にデータが入っているのが分かる。
(qemu) xp /512bx 0x9000
0000000000009000: 0x31 0xc0 0x8e 0xd8 0xbe 0x00 0x7c 0xbf
0000000000009008: 0x00 0x06 0xb9 0x00 0x02 0xf3 0xa4 0x31
0000000000009010: 0xc0 0x8e 0xd8 0x8e 0xc0 0x8e 0xd0 0xbc
0000000000009018: 0x00 0x7c 0xbe 0x57 0x7c 0xe8 0x23 0x00
0000000000009020: 0xea 0x25 0x06 0x00 0x00 0xbe 0x82 0x7c
0000000000009028: 0xe8 0x18 0x00 0xb4 0x02 0xb0 0x01 0xb5
0000000000009030: 0x00 0xb1 0x01 0xb6 0x01 0xb2 0x80 0xbb
0000000000009038: 0x00 0x7c 0xcd 0x13 0x72 0x11 0xea 0x00
0000000000009040: 0x7c 0x00 0x00 0xac 0x08 0xc0 0x74 0x06
0000000000009048: 0xb4 0x0e 0xcd 0x10 0xeb 0xf5 0xc3 0xbe
0000000000009050: 0xa5 0x7c 0xe8 0xee 0xff 0xeb 0xfe 0x5b
0000000000009058: 0x4d 0x42 0x52 0x5d 0x20 0x45 0x78 0x65
0000000000009060: 0x63 0x75 0x74 0x69 0x6f 0x6e 0x20 0x73
0000000000009068: 0x74 0x61 0x72 0x74 0x65 0x64 0x20 0x61
0000000000009070: 0x74 0x20 0x30 0x78 0x30 0x30 0x30 0x30
0000000000009078: 0x3a 0x30 0x78 0x37 0x43 0x30 0x30 0x0d
0000000000009080: 0x0a 0x00 0x5b 0x4d 0x42 0x52 0x5d 0x20
0000000000009088: 0x52 0x65 0x6c 0x6f 0x63 0x61 0x74 0x65
0000000000009090: 0x64 0x20 0x74 0x6f 0x20 0x30 0x78 0x30
0000000000009098: 0x30 0x30 0x30 0x3a 0x30 0x78 0x30 0x36
00000000000090a0: 0x30 0x30 0x0d 0x0a 0x00 0x44 0x69 0x73
00000000000090a8: 0x6b 0x20 0x72 0x65 0x61 0x64 0x20 0x65
00000000000090b0: 0x72 0x72 0x6f 0x72 0x21 0x0d 0x0a 0x00
00000000000090b8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090c0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090c8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090d0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090d8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090e0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000090f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009100: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009108: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009110: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009118: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009120: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009128: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009130: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009138: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009140: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009148: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009150: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009158: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009160: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009168: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009170: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009178: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009180: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009188: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009190: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0000000000009198: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091a0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091b0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091b8: 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x01
00000000000091c0: 0x01 0x00 0x04 0xff 0xff 0xff 0x3f 0x00
00000000000091c8: 0x00 0x00 0x00 0x10 0x00 0x00 0x00 0x00
00000000000091d0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091d8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091e0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
00000000000091f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x55 0xaa