int 13h 似乎没有读取包含我的内核的扇区

int 13h doesn't appear to read sectors containing my kernel

我正在尝试使用 USB 上的引导加载程序加载一些数据,但显然 int 13h 不起作用!

引导程序:

[bits 16]
[ORG 0x7c00]

jmp 0x0000:start
start:

cli
xor ax, ax
mov ss, ax
mov sp, 0x7c00

mov ax, cs
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
sti

mov [driveno], dl

reset:
    ;reset drive
xor ax, ax
mov dl, [driveno]
int 0x13
or ah, ah
jnz reset


mov ah, 0x02
mov al, 0x01
mov bx, 0x0000
mov es, bx
mov bx, 0x7e00

mov dl, [driveno]

xor dh, dh

mov cx, 0x0002

int 0x13
or ah, ah
jnz reset


mov dx, [0x7e00]   ;So i can check and see if it has been loaded
call printhex

cli
hlt
jmp $

print:
loop:
lodsb
or al, al
jz done
mov ah, 0x0e
mov bx, 0x0003 ;page 0 and default color
int 0x10
jmp loop
done:
ret

printhex:
push bx
push si
mov si, hex_template

mov bx, dx
shr bx, 12
mov bx, [bx+hexabet]
mov [hex_template+2], bl

mov bx, dx
shr bx, 8
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+3], bl

mov bx, dx
shr bx, 4
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+4], bl

mov bx, dx
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+5], bl

call print
pop si
pop bx
ret

hex_template db '0x????',0
hexabet db '0123456789abcdef'

driveno db 0      ;Storing dl

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

dw 0x1234  ;This wont load!!!!

times 510 db 0

我希望我的十六进制转储方法在屏幕上打印出 0x1234,但它却打印出 0x0000!我知道我的十六进制转储方法有效,问题是 0x1234 从一开始就不会加载。有什么想法吗?

我 运行 Windows。我编译并生成一个图像:

nasm -f bin -o boot.bin boot.asm
dd if=boot.bin of=\.\e: bs=512 count=2

我正在使用 chrysocome 中的 dd。谁能解释这种行为?

虽然这个问题很老,但它确实揭示了一个我知道其他人遇到过的问题,这个问题确实值得一个正确的答案。

As F运行k Kotler 指出您的代码看起来不错。我有几个小问题:

  • 在使用 lodsb 之前,您没有使用指令 CLDSTD[=97= 指定方向标志].在您的情况下,它应该是 CLD,因为您希望 LODSB 朝正方向前进。你可以在我写的另一篇 中看到更详尽的解释。
  • 虽然您的目标是 16 位代码,但您正在设置 fsgs 段寄存器,这些寄存器在 8086/ 8088/80286.

问题与解决方案

问题是您对 DD 程序的使用。你这样做:

dd if=boot.bin of=\.\e: bs=512 count=2

通常您会使用带有双反斜杠的 of=\.\e:,但这不是问题的原因。 \.\e: 实际上并不指向磁盘(或 USB 驱动器)的开头。它指向 E: 指向的分区的开头。因此,您在分区数据的开头编写了引导加载程序,而不是 Master Boot Record(MBR) 。 DD 的较新版本支持指定整个设备(不仅仅是分区)开头的选项:

Changes in version 0.6beta1

new feature id=/od= for input disk and output disk. if is the only partition on a disk, then the entire disk is selected. Eg: if you insert a USB disk and it is mounted as f: then 'id=f:' will select the USB disk (not just the partition like if=\.\f: would do)

要写入分区 E: 所在的 USB 驱动器的开头,你可以这样做(我假设你是 运行ning命令提示符作为具有管理员权限的用户):

dd if=boot.bin od=e: bs=512 count=2

请注意我现在是如何指定的 (od=e:)。 od=(output device)只是表示我们要使用分区E:所属的整个物理设备。

观察到的行为的解释

可能感兴趣的是为什么您的引导加载程序似乎可以工作但打印出来 0x0000。我将根据 Windows.

的知识提供我的最佳猜测

由于 DD 写入分区开头(而不是驱动器开头)扇区 #2(基于 1)的问题实际上并不包含我们的小内核。扇区 #1 甚至不包含我们编写的引导加载程序!

现在如果我们的代码写在分区中,为什么我们的引导加载程序甚至 运行 并且它为什么打印错误的值 (0x0000)?

如果您使用 Windows 格式化 USB 驱动器,就会发生这种情况。您可能没有意识到,USB 驱动器在默认情况下被格式化为具有单个分区的硬盘驱动器,并且该分区被标记为 bootable。 Microsoft 在 MBR(磁盘的第一个扇区)中安装引导加载程序。这个 Microsoft bootloader 是一个 chainloader,它的典型功能是这样的(一些细节可能会有所不同):

  • 适当设置段和堆栈
  • 保存 DL [我们启动的驱动器]
  • 将我们自己从物理地址 0x00007C00 移到其他地方。
  • 跳转到我们应该继续的新内存位置的偏移量
  • 查看标记为 bootable
  • 的分区 table
  • bootable分区(非磁盘)的第一个扇区(512字节)读入内存0x00007C00
  • 用第一步保存的值恢复DL
  • FAR JMP 到 0x0000:0x7C00
  • 分区引导加载程序开始执行,就好像 BIOS 已加载并直接跳转到它一样。

我们现在拥有的是 Microsoft MBR 引导加载程序读取 USB 驱动器上 bootable 分区的第一个扇区并执行它。分区的第一个扇区恰好是我们的引导加载程序,因为我们使用了 dd if=boot.bin of=\.\e: bs=512 count=2 。我们实际上写了两个扇区。 分区(不是磁盘)的第二个扇区包含我们的内核。我们的引导加载程序如此有效 运行s!

所以现在我们知道为什么我们的引导加载程序运行,为什么它打印出错误的值了?现在可能更清楚了,磁盘的第二个扇区没有我们的内核,分区包含它。磁盘读取 (int 13h) 代码执行此操作:

mov ah, 0x02            ; Disk Read
mov al, 0x01            ; Number of Sectors to read
mov bx, 0x0000
mov es, bx
mov bx, 0x7e00          ; ES:BX location to read to 0x0000:0x7E00
mov dl, [driveno]
xor dh, dh
mov cx, 0x0002          ; Read sector #2 (1-based, not 0-based)
int 0x13

我们只是读取了磁盘的第二个扇区,而不是分区。第二个扇区很可能被清零,因此我们的引导加载程序读取值 0x0000 的原因。

结论

如果我们正确地覆盖了磁盘的前 2 个扇区(1024 字节)(DD),那么我们的引导加载程序和内核就会正常工作..

感谢 Microsoft 及其链式加载程序 - 我们的引导加载程序 运行 但内核在磁盘上的位置错误,我们打印出一个可能填充零的扇区。这一系列事件恰好使我们的引导加载程序 运行 看起来 int 13h 失败了。它可能根本没有失败,它只是读取了一个不包含我们内核的扇区。

注意:我使用了kernel这个词,但在这个问题的上下文中,它指的是存储在第二个扇区中的数据(0x1234)。