Assembly INT 0x13驱动器初始化失败,并出现超时错误
我正在尝试开发一个引导加载程序,它只需扫描它自己的引导介质(带有FAT16的软盘)的根目录中的一个文件,然后跳转到它。我最终遇到了一个问题,我在网上找不到,我觉得我做错了什么:在我的代码开始时,当我使用INT 0x13读取驱动器的根目录时,设置了进位标志,在我让它打印出错误代码后,我得到了0x80,似乎对应于磁盘超时。我已经尝试将DL硬编码为0x00(软盘#1‒与以前相同)、0x01(软盘#2‒AH=0x01非法函数)和0x80(硬盘#1‒实际上有数据,但正如预期的那样,不是软盘映像中的数据)。我还尝试硬编码参数的计算,并尝试只读取一个扇区。以下是发生错误的代码: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‒实际上有数据,但正如预期的那样,不是软盘映像中的数据)。
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工作站上进行测试
- 其他代码工作正常(例如,打印内容或与键盘交互)
- 我用一个十六进制编辑器做了多个快照来检查虚拟内存,磁盘数据(除了引导代码)从未加载到内存中
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