IDEセクタ読み込み実行結果

## 「手探りでCUI OS作成に挑む」連載
この記事は「手探りでCUI OS作成に挑む」連載の一部です。
全体の目次は[「手探りでCUI OS作成に挑む」連載目次](https://qiita.com/earthen94/items/b5d77f98f0cf85a13346)を御覧下さい。

## 目的
将来BIOSを開発にあたりディスク読み込みの実装が必要となるため、IDEの使用を調べながら第一セクタを読み込むプログラムを書いてみます。
その後読み取ったデータをダンプし第一セクタ(ブートローダ)が読み込めているかを確認します。

## 検証コード
```boot.asm
bits 16
org 0x7C00

start:

    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00
    
    mov si, loading_msg
    call print_string
    
    ; LBA 0(第1セクタ)を0x1000:0x0000へ読み込む
    mov ax, 0x1000
    mov es, ax
    xor di, di          ; ES:DI = 0x1000:0x0000
    
    xor ax, ax        ; LBA = 0
    call read_sector
    
    jc disk_error       ; エラー
    
    ; 成功の文言
    mov si, success_msg
    call print_string
    
    jmp $
    
disk_error:
    mov si, error_msg
    call print_string
    
    jmp $
    
; 文字列表示
print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret
    
loading_msg db "Loading...", 0x0D, 0x0A, 0
success_msg db "Sector read successfully!", 0x0D, 0x0A, 0
error_msg db "Disk read error!", 0x0D, 0x0A, 0

%include "readdisk.asm"
```
```readdisk.asm
read_sector:
    pusha
    push ds
    
    ; ===== 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アドレスの指定 =====
    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   

    ; ===== 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
    popa
    clc                 ; 成功
    ret

.error:
    pop ds
    popa
    stc                 ; 失败
    ret
```


## 補足

### 「ドライブ/ヘッドレジスタ」設定値　0xE0 の意味
| ビット位置 | 値 (0xE0) | 意味                                |
|------------|----------|-------------------------------------|
| ビット7    | 1        | LBAモード有効化（固定で1にする必要あり） |
| ビット6    | 1        | 固定値（IDE仕様で1に設定）            |
| ビット5    | 1        | 固定値（IDE仕様で1に設定）            |
| ビット4    | 0        | ドライブ選択（0=1台目、1=2台目） |
| ビット3-0  | 0000     | LBAアドレスの24-27ビット目（後で`or al, bh`で設定される予定） |

### LBAのアドレスを設定するポート
| ポート番号 | 対応するLBAのビット範囲 | 使用レジスタ | 値の例       |
|---------------|------------------------|-------------|-------------|
| 0x1F3         | LBA 0-7（下位8ビット）  | bl          | bl = 0x12   |
| 0x1F4         | LBA 8-15（中位8ビット） | bh          | bh = 0x34   |
| 0x1F5         | LBA 16-23（上位8ビット）| cl          | cl = 0x56   |
| 0x1F6         | LBA 24-27（最上位4ビット）| bh（別の値）| bh = 0x07   |

## 検証
操作手順は以下の通りです。
```
nasm -f bin boot.asm -o boot.bin                   # コンパイル
qemu-system-i386 -hda boot.bin -monitor stdio      # HDDとして起動
(qemu)pmemsave 0x10000 512 dump.bin                # 0x10000から512バイト分をダンプ
hexdump -v -C dump.bin                             # 取り出したバイナリを確認
```
![图片.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/f4d58358-c32d-4124-ac5f-fffa8b6bc09e.png)
![图片.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/a6f566fe-39a2-4675-89d0-f64233750f9f.png)
```
test@test-ThinkPad-X280:~/kaihatsu/test4$ hexdump -v -C dump.bin
00000000  31 c0 8e d8 8e c0 8e d0  bc 00 7c be 3b 7c e8 1e  |1.........|.;|..|
00000010  00 b8 00 10 8e c0 31 ff  31 c0 e8 5a 00 72 08 be  |......1.1..Z.r..|
00000020  48 7c e8 0a 00 eb fe be  64 7c e8 02 00 eb fe ac  |H|......d|......|
00000030  08 c0 74 06 b4 0e cd 10  eb f5 c3 4c 6f 61 64 69  |..t........Loadi|
00000040  6e 67 2e 2e 2e 0d 0a 00  53 65 63 74 6f 72 20 72  |ng......Sector r|
00000050  65 61 64 20 73 75 63 63  65 73 73 66 75 6c 6c 79  |ead successfully|
00000060  21 0d 0a 00 44 69 73 6b  20 72 65 61 64 20 65 72  |!...Disk read er|
00000070  72 6f 72 21 0d 0a 00 60  1e ba f7 01 b9 ff ff ec  |ror!...`........|
00000080  a8 80 74 05 49 75 f8 eb  49 ba f6 01 b0 e0 08 f8  |..t.Iu..I.......|
00000090  24 ef ee ba f2 01 b0 01  ee ba f3 01 88 d8 ee ba  |$...............|
000000a0  f4 01 88 f8 ee ba f5 01  88 c8 ba f7 01 b0 20 ee  |.............. .|
000000b0  b9 ff ff ec a8 80 75 fb  a8 08 75 05 49 75 f4 eb  |......u...u.Iu..|
000000c0  11 a8 01 75 0d b9 00 01  ba f0 01 fc f3 6d 1f 61  |...u.........m.a|
000000d0  f8 c3 1f 61 f9 c3 00 00  00 00 00 00 00 00 00 00  |...a............|
000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000140  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000170  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000190  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200
```


## 最後に
正しく読み込めるようになりました。
次はこの関数を利用して、INT13Hを使わずに第2セクタを読み込むブートローダを書いてみる予定です。

作業風景
![_cgi-bin_mmwebwx-bin_webwxgetmsgimg__&MsgID=3514173339891062523&skey=@crypt_e58c0397_5c233ecc7ccc238713b9d3f6b8dbdf72&mmweb_appid=wx_webfilehelper.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/09d7eb05-63c0-4982-97c8-9e747314ac43.jpeg)

## 当日追記
今回書いたプログラムを利用して、BIOSを呼び出さないブートローダを書いてみました。
LBAアドレスの指定方法に問題があり第0セクタ目以外が読み込めない問題があったので併せて修正しました。
https://qiita.com/earthen94/items/edb134dbbe5c632266fc