Assembly INT 0x13驱动器初始化失败,并出现超时错误

Assembly INT 0x13驱动器初始化失败,并出现超时错误,assembly,x86,bootloader,disk,bios,Assembly,X86,Bootloader,Disk,Bios,我正在尝试开发一个引导加载程序,它只需扫描它自己的引导介质(带有FAT16的软盘)的根目录中的一个文件,然后跳转到它。我最终遇到了一个问题,我在网上找不到,我觉得我做错了什么:在我的代码开始时,当我使用INT 0x13读取驱动器的根目录时,设置了进位标志,在我让它打印出错误代码后,我得到了0x80,似乎对应于磁盘超时。我已经尝试将DL硬编码为0x00(软盘#1‒与以前相同)、0x01(软盘#2‒AH=0x01非法函数)和0x80(硬盘#1‒实际上有数据,但正如预期的那样,不是软盘映像中的数据)。

我正在尝试开发一个引导加载程序,它只需扫描它自己的引导介质(带有FAT16的软盘)的根目录中的一个文件,然后跳转到它。我最终遇到了一个问题,我在网上找不到,我觉得我做错了什么:在我的代码开始时,当我使用INT 0x13读取驱动器的根目录时,设置了进位标志,在我让它打印出错误代码后,我得到了0x80,似乎对应于磁盘超时。我已经尝试将DL硬编码为0x00(软盘#1‒与以前相同)、0x01(软盘#2‒AH=0x01非法函数)和0x80(硬盘#1‒实际上有数据,但正如预期的那样,不是软盘映像中的数据)。我还尝试硬编码参数的计算,并尝试只读取一个扇区。以下是发生错误的代码:

    BITS 16

    jmp short bootload
    nop

    ; Drive parameters

bootload:
    ; Segment registers
    mov ax, 0x07C0+544
    cli
        mov ss, ax
        mov sp, 4096
    sti

    mov ax, 0x07C0
    mov ds, ax
    mov es, ax

    ; Boot device
    mov [bootdev], dl

    ; Calculations (I just hardcoded them in this example to make it easier to understand)
    mov byte [rootdirsize], 14
    mov byte [rootdirchssec], 1
    mov word [rootdirchstrack], 1

    ; Read sectors
    mov ah, 0x02                        ; Read sectors
    mov al, byte [rootdirsize]          ; The amount of sectors needed by the root dir entries
                                        ; (RootDirEntries / 16)
    mov dl, byte [bootdev]
    mov dh, 0                           ; Heads are ignored... yet
    mov cl, byte [rootdirchssec]        ; Sector number of the root dir in CHS
    and cx, 0b0000_0000_0011_1111       ; Sector only uses bits 0-5 of CX
    mov bx, word [rootdirchstrack]      ; Track number of the root dir in CHS
    shl bx, 6                           ; Track uses bits 6-15 of CX
    or cx, bx                           ; Transfer to CX
    mov bx, 0x0100                      ; Segment where it is loaded
    mov es, bx
    mov bx, 0                           ; Offset = 0
    int 0x13

    jc disk_error                       ; CF = error

    jmp $                               ; the rest of the bootloader

disk_error:
    mov al, ah                          ; AH is the error code
    mov ah, 0x0E                        ; print it
    int 0x10                            ; returns 'Ç' = 0x80 = timeout

    jmp $

data:
    bootdev         db 0
    rootdirsec      dw 0
    rootdirchssec   db 0
    rootdirchstrack dw 0
    rootdirsize     db 0

    times 510-($-$$) db 0
    dw 0xAA55
真正的代码当然要长得多,我试着只写问题的关键部分。其他可能有帮助的细节:

  • 我在用NASM
  • 我正在使用虚拟软盘在VMWare工作站上进行测试
  • 其他代码工作正常(例如,打印内容或与键盘交互)
  • 我用一个十六进制编辑器做了多个快照来检查虚拟内存,磁盘数据(除了引导代码)从未加载到内存中

正确的是,柱面(磁道)可以是10位数字,要读取的扇区号为6位。它们都被打包到一个16位寄存器(CX)中,用于
int 0x13
BIOS磁盘读取调用。10位柱面编号仅适用于硬盘类型的介质(或任何模拟为硬盘驱动器的介质)。对于软盘介质,柱面限制为8位值(0-255),扇区号仍然限制为1-63(6位)之间的值

将包含圆柱体的16位字加载到BX中以执行计算。将BX向左移位6位。这会将气缸计数的下2位放入BL的上2位,将上8位放入BH寄存器。这不是气缸号的编码方式。文件说明:

这表示必须存储在CL的上2位中的是气缸号的上2位,扇区号是CL的下6位。CH包含气缸号的下8位

要解决此问题,可以更改以下行:

mov bx, word [rootdirchstrack]  ; Track number of the root dir in CHS
shl bx, 6                       ; Track uses bits 6-15 of CX
or cx, bx                       ; Transfer to CX
类似于:

mov bx, word [rootdirchstrack]  ; Track*Cylinder) number of the root dir in CHS    
xchg bl, bh                     ; Place lower 8 bits of Cylinder in BH
                                ; Upper 2 bits of Cylinder are now the lower 2 bits of BL
ror bl, 2                       ; Rotate the lower 2 bits into the upper 2 bits of BL
or cx, bx                       ; Transfer to CX already containing sec # in lower 6 bits

您是否尝试过使用调试器单步执行代码?BOCHS仿真器具有内置调试器;其他一些允许您附加GDB。如果您的设置无法使调试成为可能,请获取新设置;调试器使捕捉愚蠢的错误和注意错误的假设变得非常容易,而不必首先考虑错误并编写调试打印来检查。mov al,[不要读取任何扇区]。如果你的负载是100:0,那就等于0:1000。当您读取dl=2时,您看到的数据是“加载的”,而不仅仅是bios加载的数据吗POST@MichaelPetch谢谢,这实际上解决了问题!我搞砸这件事的原因可能是你可以想象CH和CL之间的边界,我假设这个边界刚好偏离了两位,以允许一侧有一个更高的值。你介意把这个贴出来作为答案吗?这样我就可以接受了。“高2位的柱面(6-7位,仅硬盘)”只是硬盘而已。这是针对软盘的(没有分区,不使用“int 0xx13扩展”),所以这些位不存在。如果是单面软盘,您可以直接执行
mov-cl、[rootdirchssec]
mov-ch、[rootdirchstrack]
。对于双面软盘,您可能需要将
[rootdirchstrack]
的最低位移到
dh
(比如可能
mov-cx,[rootdirchstrack]
mov-dh,cl
shl-cx,8-1
和dl,1
)。@Brendan,这是完全正确的。事实上,我重新审视了这个问题,只是在你的评论之后才注意到“floppy”。然而,如果他尝试移动到一个不是软盘的介质(使用类似FAT16的东西),这仍然是有价值的。对于这样的引导加载程序,您必须为其他任何东西重写它们(所有硬盘都有分区,几乎所有硬盘都需要使用LBA的“int 0x13扩展”,因为它们太大,CHS无法工作)。我可能还应该提到,在真正的软盘驱动器上,如果出现错误,您希望重试3次以上,因为通常会出现“在磁盘以正确的速度旋转之前尝试读取”的问题,而且媒体相对不可靠,因此如果出现读取错误,多重试几次可能会有所帮助。@Brendan我有一个答案,讨论了真正的失败和重试,这超出了这个问题的范围。OP在他们的问题中指出,他们已经精简了他们的代码以产生一个新的问题,所以我并不真正关心导致他们问题的实际主要错误,因为他们只提供测试代码来重现。
mov bx, word [rootdirchstrack]  ; Track*Cylinder) number of the root dir in CHS    
xchg bl, bh                     ; Place lower 8 bits of Cylinder in BH
                                ; Upper 2 bits of Cylinder are now the lower 2 bits of BL
ror bl, 2                       ; Rotate the lower 2 bits into the upper 2 bits of BL
or cx, bx                       ; Transfer to CX already containing sec # in lower 6 bits