【问题标题】:How do I fix the error "Media Type Not Found" when attempting to load a sector with int 13h ah=02h?尝试使用 int 13h ah=02h 加载扇区时,如何修复错误“找不到媒体类型”?
【发布时间】:2019-09-20 16:42:29
【问题描述】:

我有一些代码可以在实模式下从软盘加载第二个扇区,但是 int 0x13 失败并出现错误“找不到媒体类型”。这是为什么呢?

我尝试将柱面、磁头和扇区从 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 也是零(表示成功)。

【问题讨论】:

  • 如果跳过重置磁盘命令会发生什么?在流行操作系统的引导扇区代码中也没有重置磁盘调用。
  • @E.vanPutten 也不行。
  • 你在什么样的模拟器(或真实硬件)上运行它?如果您是从 USB 启动,驱动器号可能不是 0,而是其他值。
  • @E.vanPutten 我在 macOS mojave 上使用 qemu-system-i386
  • 如果你还在 qemu 中配置了一个硬盘,你可以从那个设备中读取一个扇区吗(DL 必须设置为 80 hex 而不是 00 hex 我想)?

标签: assembly x86 bootloader bios osdev


【解决方案1】:

存在许多潜在问题:

  • 您将驱动器号硬编码为 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,因此您可以使用以下方法组装和构建磁盘映像:

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

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

【讨论】:

  • 但是 INT 13,02 调用失败的原因是什么。是驱动器索引还是段寄存器指向了错误的内存位置?
  • @E.vanPutten :我无法具体回答,因为不清楚。我的方法是展示如何调用 QEMU。我的一部分认为,可能的罪魁祸首是正在使用的引导驱动器不是 0。如果他运行qemu-system-i386 disk.img,那么它将从 HDA 启动,并且可能会按照他所看到的方式失败。我的测试表明可能是访问了错误的驱动器。在磁盘映像之外读取可能会导致不同的错误(但它可能因模拟器而异)。段寄存器 ES 和 BX(偏移量)对于 Int 13h/ah=2 工作是正确的。
  • 将 CS 复制到 DS 的问题不是 QEMU 下的问题,也不是他看到的问题的原因。在特定情况下的真实硬件和仿真器下,它可能会导致问题。如果出现这样的故障,他最有可能看到的是没有显示任何字符串。
  • 没错。并且看到媒体错误进一步支持了您的理论,即不是从软盘启动。在评论中,我让他尝试第一个 HDA。我仍然想知道这是否使他的测试通过了。
  • 您的回答很完美,这就是我投票的原因。我只是想知道 OP 到底看到了什么。
猜你喜欢
  • 2015-02-18
  • 2014-09-16
  • 2011-04-08
  • 2012-02-17
  • 2013-11-20
  • 2020-04-12
  • 1970-01-01
  • 2019-09-05
相关资源
最近更新 更多