BIOS int 13 中的"invalid command (error code 0x01)"是什么意思



我正在和我的一个朋友一起研究一个简单的引导加载程序,没有任何借口让它变得可用。在编写了屏幕上的输入和输出函数之后,我们继续编写函数以从磁盘读取扇区,这就是出现第一个问题的地方。我声明在 qemu 和 bochs 上一切正常。对于物理硬件,我不能说同样的话,我遇到错误0x0001,这意味着无效命令

但是,我没有找到有关此错误的太多信息。在我看来,这可能意味着我弄错了一些论点,但我打印了屏幕上的所有日志,没有找到任何奇怪的值来证明这种行为是合理的。

我正在从闪存驱动器启动。我认为这也可能是个问题(因为它不是实际的软盘),但如果 BIOS 可以加载引导扇区,那么加载下一个扇区也应该没有问题。

但是,下面是read_sector函数的代码:

read_sector:
start_f
pusha
mov     (drive_number), %dl    # drive number is stored from the main function into a global variable
mov     $0x03, %si             # try three times
1:
mov     $0x0201, %ax
int     $disk_int
jnc     end
dec     %si
jz      2f
xor     %ah, %ah
int     $disk_int
jmp     1b
2:
movzx   %ah, %dx
call    printh                 # print error code
end:
popa
end_f

下面是调用者函数 (dl = 0):

# ...
mov     $0x0002, %cx
xor     %dh, %dh
mov     $0x7e00, %bx
call    read_sector
# ...

我们可能做错了什么?

这里的问题出在文件init.s中。在初始化代码段之前,我将一个值放入drive_number中。

CPU 如何计算物理地址

实模式内存分段

当我将此列表转换为机器语言时,链接器通过引用它所在的部分的开头来计算drive_number的地址(在本例中为.text部分,从地址7c00开始,如链接器脚本中指定):

.text 0x7c00 :
{
*(.text);
}

这意味着指令mov $0, (drive_number)被翻译成mov $0, 7c2e。但是,这不是实际的物理地址,而只是偏移量。 在实模式下,通过将特定段寄存器中的值移位 4 位(与乘以 16 相同)添加到偏移量(如本例中7c2e)来计算物理地址。通常我们看到表示法AAAA:BBBB表示地址AAAA * 16 + BBBB。为了确定内存中用于读取或写入某种数据的位置的物理地址,默认情况下,CPU利用存储在数据段寄存器中的值%ds。这意味着我们存储数据的实际地址确实是ds * 16 + drive_number.

当 BIOS 跳转到引导扇区中编写的代码时,它不能保证段寄存器中的值是我们想要的值。因此,在每个程序开始时,我们必须初始化这些寄存器以包含我们需要的值。如果%ds不为零,则在初始化%ds为零后,对应于drive_number的物理地址将不同,这意味着此标签根据%ds中包含的值指向内存中的不同位置。

BIOS 告诉我们要从哪个驱动器启动

BIOS 中断13, 2要求 dl 包含指示我们应该从哪个驱动器读取的代码。但是,无需在手册后查阅手册即可从我们正在启动的驱动器中读取文本:实际上,在跳转到07c0:0000并开始在引导扇区中执行代码之前,BIOS 会将对应于我们正在引导的驱动器的值放在%dl。 在%dl中使用 BIOS 传递的代码可以使代码更加可靠和健壮。

最新更新