尝试使用 int 13h ah=02h 加载扇区时如何修复错误 "Media Type Not Found"?

How do I fix the error "Media Type Not Found" when attempting to load a sector with int 13h ah=02h?

我有一些代码可以在实模式下从软盘加载第二个扇区,但是 int 0x13 失败并出现错误 "media type not found"。这是为什么?

我试过把柱面、磁头和扇区从0、0、2分别改成1、1、1,没用(不知道CHS寻址是用0还是1开始) .我也多次重写这段代码以更好地组织它的功能,但无济于事。它失败但不打印我的错误字符串,这让我很困惑。它似乎总是因同样的错误而失败。

代码如下:

bits 16
org 0x7c00

start:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00
    mov si, msg

    mov ah, 0x00
    mov al, 0x03
    int 0x10

    call reset_disk
    mov si, suc_reset
    call puts

    call load_stage2
    mov si, suc_load
    call puts

    hlt

; routine to reset disk state
reset_disk:
    xor ah, ah          ; int 0x13 ah = 0x00
    xor dl, dl          ; drive 0
    int 0x13
    jc .error           ; error if carry flag is set
    ret
.error:
    mov si, err_reset
    call puts
    hlt

; routine to load stage 2
load_stage2:
    mov ah, 0x02        ; int 0x13 ah = 0x02 (read sectors)
    mov al, 0x01        ; number of sectors to read
    mov ch, 0x01        ; cylinder 0
    mov cl, 0x02        ; sector 2
    xor dh, dh          ; head 0
    xor dl, dl          ; drive 0
    mov bx, 0x9c00      ; address 9c00
    mov es, bx
    xor bx, bx          ; 0x9c00:0x0000

    int 0x13
    or ah, ah
    jnz .error
    ret
.error:
    mov si, err_load
    call puts
    hlt

; routine to print a string
puts:
    mov ah, 0x0e        ; int 0x10 ah = 0x0e (putchar)
.loop:
    lodsb               ; load string byte from si
    or al, al           ; check if al is zero
    jz .end             ; if zero jump to end (null terminator)
    int 0x10            ; print character
    jmp .loop           ; loop
.end:
    ret

err_reset: db "Failed to reset disk", 0x0a, 0x0d, 0
err_load: db "Failed to load stage 2", 0x0a, 0x0d, 0
suc_reset: db "Successfully reset disk 0...", 0x0a, 0x0d, 0
suc_load: db "Successfully loaded stage 2...", 0x0a, 0x0d, 0
msg: db "Test", 0
times 510-($-$$) db 0
dw 0xaa55

我希望输出是成功消息,但 EAX 也是零(表示成功)。

存在许多潜在问题:

  • 您将驱动器编号硬编码为 0(使用 xor dl, dl)。这样做意味着如果您以不使用软盘驱动器 A (FDA) 的方式启动 QEMU,您的代码将无法工作。如果您作为硬盘驱动器启动,它将失败。在将控制权转移到您的引导加载程序之前,BIOS 将从 DL 中引导的驱动器编号。只需使用该值即可。通过删除 xor dl, dl
  • 的两次出现,可以在您的代码中轻松实现这一点
  • 你复制CS到其他段寄存器。在某些硬件和模拟器上 CS 可能不是 0(在某些硬件和模拟器上可能是 0x07c0)。不要依赖 CS 作为特定值。因为您使用原点 (org 0x7c00),所以您需要将 0 放入段寄存器中(尤其是 DS)。
  • 在柱面、磁头、扇区寻址 (CHS) 中,柱面从 0 开始,磁头从 0 开始,只有扇区号从 1 开始。磁盘上的第二个扇区是 CHS=(0,0,2)。您的代码显示为 CHS=(1,0,2),这是不正确的。
  • HLT指令只等待下一次中断发生。当中断(即:定时器)发生时,处理器将继续执行HLT之后的代码。在执行 HLT 之前,您需要使用 CLI 关闭外部中断。您还应该将 HLT 放入循环中,因为在真实硬件上可能会发生不可屏蔽中断 (NMI)。要正确使用 HLT 你可以这样做:

        cli
    .hltloop:
        hlt
        jmp .hltloop
    

    对于引导加载程序,一个简单的无限循环就足够了:jmp $.


通过对您的代码进行这些类型的更改,以及用于测试目的的简单第二阶段,我们可以创建将第二阶段读取到内存的代码 0x9c00:0x0000 然后 FAR JMP 到它并执行代码。在此示例中,MDP 将直接显示在显示器上,洋红色属性为白色。

boot.asm:

bits 16
org 0x7c00

start:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00

    mov ah, 0x00
    mov al, 0x03
    int 0x10

    call reset_disk
    mov si, suc_reset
    call puts

    call load_stage2
    mov si, suc_load
    call puts

    ; As a test if stage2 is loaded jump to code contained in stage2
    jmp 0x9c00:0x0000
;    jmp halt


; routine to reset disk state
reset_disk:
    xor ah, ah          ; int 0x13 ah = 0x00
    int 0x13
    jc .error           ; error if carry flag is set
    ret
.error:
    mov si, err_reset
    call puts
    jmp halt

; routine to load stage 2
load_stage2:
    mov ah, 0x02        ; int 0x13 ah = 0x02 (read sectors)
    mov al, 0x01        ; number of sectors to read
    mov ch, 0x00        ; cylinder 0
    mov cl, 0x02        ; sector 2
    xor dh, dh          ; head 0
    mov bx, 0x9c00      ; address 9c00
    mov es, bx
    xor bx, bx          ; 0x9c00:0x0000

    int 0x13
    or ah, ah
    jnz .error
    ret
.error:
    mov si, err_load
    call puts

halt:
    cli
.hltloop:
    hlt
    jmp .hltloop

; routine to print a string
puts:
    mov ah, 0x0e        ; int 0x10 ah = 0x0e (putchar)
.loop:
    lodsb               ; load string byte from si
    or al, al           ; check if al is zero
    jz .end             ; if zero jump to end (null terminator)
    int 0x10            ; print character
    jmp .loop           ; loop
.end:
    ret

err_reset: db "Failed to reset disk", 0x0a, 0x0d, 0
err_load: db "Failed to load stage 2", 0x0a, 0x0d, 0
suc_reset: db "Successfully reset disk 0...", 0x0a, 0x0d, 0
suc_load: db "Successfully loaded stage 2...", 0x0a, 0x0d, 0

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

stage2.asm:

org 0x0000
bits 16

stage2:
    ; Display MDP with white on magenta on 4th line of text display
    mov ax, 0xb800
    mov es, ax
    mov word [es:480], 0x57<<8 | 'M'
    mov word [es:482], 0x57<<8 | 'D'
    mov word [es:484], 0x57<<8 | 'P'

    jmp $

我通常使用 DD 创建磁盘映像,但由于您使用的是 CAT,您可以 assemble 并使用以下命令构建磁盘映像:

nasm -f bin boot.asm -o boot.bin
nasm -f bin stage2.asm -o stage2.bin
cat boot.bin stage2.bin >disk.img

您可以 运行 QEMU 并从软盘 (FDA) 或硬盘 (HDA) 启动。它应该可以使用:

qemu-system-i386 -fda disk.img

或:

qemu-system-i386 -hda disk.img

如果它正常工作,输出应该类似于: