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?

CUIOS自作 DISK読み込み機能改良

Last updated at Posted at 2025-07-07

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

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

目的

これまで使用していたDISK読み込み関数は読みこむセクタが数十を超えると失敗したり、読みこむLBAと読み込み先のアドレス指定が複雑だったりと、問題を抱えていました。
そこで早い内にこの関数を改良しておくことにしました。

今までの関数はdriver/disk.asmの中に入っています。
https://github.com/ooe1220/sourouOS/tree/20250628/boot

改良版

disk.asm
;---------------------------------------------------
; 任意LBAの任意セクタ数を任意メモリへ読み込む
; 入力:
;   DX = 保存先セグメント (ESに設定される)
;   BX = 保存先オフセット (DIに設定される)
;   CX:SI = LBAアドレス (32bit)
;   AL = 読み込むセクタ数 (最大128)
;---------------------------------------------------
read_sectors:
    ; 引数をメモリへ退避(レジスタを上書きする前に保存)
    mov [tmp_sectors], al  ; セクタ数
    mov [tmp_seg], dx      ; 保存先セグメント
    mov [tmp_ofs], bx      ; 保存先オフセット
    mov [tmp_lba_low], si  ; LBA下位16bit
    mov [tmp_lba_high], cx ; LBA上位16bit

    ; 必要レジスタ退避(呼び出し規約に従って保存)
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es

    mov dx, [tmp_seg]      ; ESセグメント設定用
    mov bx, [tmp_ofs]      ; DIオフセット設定用

    ; メモリ転送用設定
    mov es, dx             ; 保存先セグメントをESに設定
    mov di, bx             ; 保存先オフセットをDIに設定

    ;-----------------------------------------
    ; IDEコントローラへのLBA設定
    ;-----------------------------------------

    ; 0x1F6: ドライブ/ヘッドレジスタ
    ; bit7-4: 0xE0 (LBAモード + マスタードライブ)
    ; bit3-0: LBAアドレスのbit24-27
    mov dx, 0x1F6
    mov cx, [tmp_lba_high] ; LBA上位
    mov ah, ch             ; LBA bit24-27 (CXの上位8bit)
    and ah, 0x0F           ; 下位4bitのみ有効
    mov al, 0xE0           ; LBAモード + マスタードライブ
    or  al, ah             ; LBAアドレス上位と結合
    out dx, al

    ; 0x1F2: セクタカウントレジスタ
    mov dx, 0x1F2
    mov al, [tmp_sectors]  ; 読み込むセクタ数
    out dx, al

    ; 0x1F3: セクタ番号レジスタ (LBA bit0-7)
    mov dx, 0x1F3
    mov si, [tmp_lba_low]  ; LBA下位
    mov ax, si             ; LBA下位16bit (SI)
    out dx, al             ; bit0-7を出力

    ; 0x1F4: シリンダ低位レジスタ (LBA bit8-15)
    mov dx, 0x1F4
    shr ax, 8              ; bit8-15をALに
    out dx, al

    ; 0x1F5: シリンダ高位レジスタ (LBA bit16-23)
    mov dx, 0x1F5
    mov cx, [tmp_lba_high] ; LBA上位
    mov al, cl             ; LBA bit16-23 (CXの下位8bit)
    out dx, al

    ; 0x1F7: コマンドレジスタ
    mov dx, 0x1F7
    mov al, 0x20           ; 読み込みコマンド (READ SECTORS)
    out dx, al

    ;-----------------------------------------
    ; データ読み込み処理
    ;-----------------------------------------
    
    mov byte [already_read_sectors], 0 ;既に読みこんだセクタ数を数える

.next_sector:
    ; データ準備完了(DRQ)を待つ
.wait_drq:
    mov dx, 0x1F7
    in  al, dx
    test al, 8             ; bit3 (DRQ)が立っているか
    jz   .wait_drq         ; 準備できてなければ待機

    ; 1セクタ(512バイト=256ワード)を転送
    mov cx, 256            ; ループカウンタ
    mov dx, 0x1F0          ; データポート

.read_word:
    in  ax, dx             ; データポートから1ワード(2バイト)読み込み
    mov [es:di], ax        ; メモリに保存
    add di, 2              ; 次のワード位置へ
    loop .read_word

    ; 次のセクタへ
    inc byte [already_read_sectors]     ; 読み込んだセクタ数を増やす
    mov al, [already_read_sectors]
    cmp al, [tmp_sectors]               ; 総セクタ数と比較
    jb  .next_sector       ; 未完なら継続

    ;-----------------------------------------
    ; 終了処理
    ;-----------------------------------------
    ; レジスタ復旧(退避した逆順で)
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    ret

;-----------------------------------------
; データ領域
;-----------------------------------------
section .data
tmp_sectors   db 0    ; 読み込むセクタ数
tmp_seg       dw 0    ; 保存先セグメント
tmp_ofs       dw 0    ; 保存先オフセット
tmp_lba_low   dw 0    ; LBAアドレス下位
tmp_lba_high  dw 0    ; LBAアドレス上位
already_read_sectors db 0  ; 読み込み済みセクタカウンタ(0初期化)
呼び出し部分
testcode.asm
[bits 16]
[org 0x7E00]

start:
    ; レジスタ初期化
    xor ax, ax
    mov ds, ax
    mov es, ax
    
    ; スタック設定
    mov ss, ax
    mov sp, 0x7C00

    mov dx, 0x900       ; 保存先セグメント (ES = 0x200)
    mov bx, 0x100       ; 保存先オフセット (DI = 0x100)
    mov cx, 0x0000      ; LBA上位16bit (200 < 2^16なので0)
    mov si, 201         ; LBA下位16bit (200 = 0x00C8)
    mov al, 100           ; 読み込むセクタ数 (1セクタ)
    call read_sectors   ; ディスク読み込み実行
    
    
    ;;;;
    mov si,testmsg
    call print_string
    ;;;;

    jc .read_error             ; エラー処理
    
.read_error:
    cli
    hlt
    
print_string:
    pusha
    mov ah, 0x0E     ;1文字出力
    mov bh, 0
.print_loop:
    lodsb            ;SIの示すアドレスから1バイトをALに読み込み、SIを+1(次の文字へ)
    test al, al      ;alが0かどうかを確認する
    jz .done         ;文字が 0 か(=終端記号か)を確認。※'hello',0 文字列の最後に目印の0を入れている
    int 0x10         ;BIOS割り込みで AL の文字を表示
    jmp .print_loop
.done:
    popa
    ret
        
%include "disk.asm"
testmsg db "aaaaaaa"

times 510-($-$$) db 0
dw 0xAA55
boot.asm
boot.asm
; 第2セクタを読み込み、実行するブートローダー
[bits 16]
org 0x7C00

start:
    ; セグメント初期化
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; スタック設定
    mov ss, ax
    mov sp, 0x7C00
    
    mov si, msg1
    call print_string

    ; ディスクから第2セクタを 0x7E00 に読み込む
    mov ah, 0x02        ; INT 13h サービス 2: 読み込み
    mov al, 1           ; 読み込むセクタ数 = 4
    mov ch, 0           ; シリンダ = 0
    mov cl, 2           ; セクタ番号 = 2(1始まり)
    mov dh, 0           ; ヘッド = 0
    mov dl, 0x80           ; ドライブ番号
    mov bx, 0x7E00      ; 読み込み先アドレス
    int 0x13
    jc disk_error       ; エラー時に無限ループ

    ; 読み込んだコードへ跳ぶ(第2セクタ)
    jmp 0x0000:0x7E00

disk_error:
    mov si, msg2
    call print_string
    jmp $
    
print_string:
    pusha
    mov ah, 0x0E     ;1文字出力
    mov bh, 0
.print_loop:
    lodsb            ;SIの示すアドレスから1バイトをALに読み込み、SIを+1(次の文字へ)
    test al, al      ;alが0かどうかを確認する
    jz .done         ;文字が 0 か(=終端記号か)を確認。※'hello',0 文字列の最後に目印の0を入れている
    int 0x10         ;BIOS割り込みで AL の文字を表示
    jmp .print_loop
.done:
    popa
    ret

msg1 db 'boot loader', 0x0D, 0x0A,0
msg2 db 'disk_error', 0x0D, 0x0A,0

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

100セクタ(128kb)分1埋めしたデータを作る。

testdata.asm
times 51200 db 1

動作確認

# 仮想HDD生成128MB
dd if=/dev/zero of=os.img bs=1M count=128

nasm boot.asm -o boot.bin
nasm testcode.asm -o testcode.bin

dd if=boot.bin of=os.img bs=512 seek=0 conv=notrunc
dd if=testcode.bin of=os.img bs=512 seek=1 conv=notrunc

# 検証用データをLBA201の位置へ複製
dd if=testdata.bin of=os.img bs=512 seek=201 conv=notrunc

qemu-system-i386 -hda os.img  -monitor stdio 

実行中にQEMU上から
メモリ0x0900:0x100から100セクタ分のダンプを見ると
無事読み込まれている。

(qemu) xp /51200bx 0x9100
0000000000009100: 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
0000000000009108: 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
...省略...
00000000000158f0: 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
00000000000158f8: 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01

原理

0x1F6:

ビット 意味
7 1=LBAモード, 0=CHSモード
6 1=マスタードライブ, 0=スレーブドライブ
5 常に1
4 0=ドライブ選択
3-0 LBAアドレスのbit24-27 (CHSモード時はヘッド番号)

よって上位4ビットは1110(=0xE)を設定
下位4ビットはLBA bit24-27を設定

0x1F2:読み込むセクタ数を設定
0x1F3:LBA bit0-7を設定
0x1F4:LBA bit8-15を設定
0x1F5:LBA bit16-23を設定
0x1F7:0x20(データ転送開始の指令)を設定

0x1F7番口へ0x20を送信したあと
0x1F7番口から取得したデータのbit3(4ビット目)が1かどうかを確認して
0x1F0番口からデータを1セクタずつ取得して[es:di]へ格納していく。

0x1F7番口のbit3が1で無ければまだデータの転送準備が整っていないのでループして待つ。
毎セクタbit3を確認しなければならない。

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?