您的代码假定
DS设置为0。您不能这样假设。如果您正在使用
org 0x7c00
,则代码的第一部分应明确将
DS设置为0。
您应该认真考虑通过设置
SS:SP来定义您的堆栈。您不知道现有堆栈在哪里,或者它是否足够大以处理您打算执行的操作。
在调用引导加载程序之前,BIOS会使用启动设备号设置
DL寄存器。在从引导驱动器发出驱动器请求时,您不应该在代码中将
DL设置为0。您应该使用在调用引导加载程序时存在于
DL中的值。
由于您正在使用
LODSB指令期望向内存前进,因此您应该使用
CLD指令清除方向标志。不能保证方向标志将正确设置,因此您应该使用
CLD(向前)或
STD(向后)显式地设置所需的方向。
我在我的StackOverflow答案中有关于上述问题的更多信息,链接为General Bootloader Tips。
由于您没有使用BPB,因此我强烈建议将jmp start
作为引导加载程序的第一条指令删除。相反,将代码后的数据移动到引导扇区签名(0xAA55
)之前。原因是一些BIOS会尝试根据出现在引导加载程序的第一条指令的JMP指令来查找BPB,如果找到,则会在内存中覆盖引导加载程序的某些部分,导致潜在的未定义行为。
您的引导加载程序使用此指令启动从第二扇区加载的第二阶段:
jmp 0x0:0x1000
问题在于,当你这样读取设置 ES:BX 的扇区时:
read_sector:
mov ax, 0x0
mov es, ax
xor bx, bx
这会将
ES:BX设置为0x0000:0x0000,这显然不是你的
JMP期望代码所在的位置。你需要将
ES:BX设置为你想要
INT 13/AH=02h读取磁盘扇区的内存位置。
INT 13h/AH=02h需要正确设置柱面/磁头/扇区号。扇区编号从1开始,但柱面和磁头是从0开始的。磁盘的第二个扇区位于柱面0、磁头0、扇区2。你的代码将柱面设置为1,而不是0。这段代码是错误的,因为你真正应该将它设置为0:
mov ch, 01
在你的第二阶段,由于
print
以
RET
指令结尾,你将其创建为一个函数。应该将
jmp print
更改为
call print
。
通过上述所有建议的更改,包括我一般的引导加载程序提示中的更改,你的代码可以修改为:
boot.asm
[bits 16]
[org 0x7c00]
start:
xor ax,ax
mov ds,ax
mov es,ax
mov bx,0x8000
mov ss,bx
mov sp,ax
cld
mov si, wolf_wel_msg
call wolf_print
mov si, wolf_kernel_load
call wolf_print
pushf
stc
mov ah,00
int 13h
read_sector:
mov ax, 0x0
mov es, ax
mov bx, 0x1000
mov ah, 02
mov al, 01
mov ch, 00
mov cl, 02
mov dh, 00
int 13h
jc wolf_error
popf
jmp 0x0:0x1000
cli
hlt
wolf_error:
mov si, wolf_error_msg
call wolf_print
mov si, wolf_error_msg1
call wolf_print
mov ah,00
int 16h
xor ax,ax
int 19h
wolf_print:
lodsb
or al,al
jz exit
mov ah,0x0e
int 10h
jmp wolf_print
exit:
ret
wolf_wel_msg db 'Welcome to Bootloader!!!',0x0D,0x0A,0
wolf_kernel_load db 'Loading kernel....',0x0D,0x0A,0
wolf_error_msg db 'Kernel.bin not found!',0x0D,0x0A,0
wolf_error_msg1 db 'Press any key to restart..',0
times 510-($-$$) db 0
dw 0xAA55
hello.asm
[org 0x1000]
jmp start
data:
msg db 'Hello',0
start:
mov si, msg
call print
cli
hlt
print:
lodsb
or al, al
jz exit
mov ah,0x0e
int 10h
jmp print
exit:
ret
根据你提供的DD命令信息,似乎你正在使用Windows操作系统,可能会遇到另一个问题。我不知道你使用哪个DD,但of=\\.\d:
不会写入磁盘(USB驱动器)的开头,它会写入D:所在的分区,而不是磁盘本身的开头。
我建议你使用Chrysocome的最新版DD。截至今天,最新版本为0.6beta3。我推荐这个版本,因为它允许你正确访问磁盘(或USB设备),相对于驱动器的开头,而不是相对于特定分区的开头。这可能会导致存储第一和第二扇区时出现严重问题。使用最新版本时,我会使用以下命令以管理员权限写入USB驱动器:
dd if=f:\boot.bin od=d: bs=512 count=1
dd if=f:\hello.bin od=d: bs=512 seek=1 count=1
假设您的
USB驱动器在D盘,这是根据您的问题建议的。
警告:使用错误的驱动器可能会导致其他设备上的数据丢失和损坏!!
如果这些命令正常工作,则输出应该类似于:
dd if=boot.bin od=d: bs=512 count=1
rawwrite dd for windows version 0.6beta3.
Written by John Newbigin <jn@it.swin.edu.au>
This program is covered by terms of the GPL Version 2.
Device d: is a link to \\?\Device\HarddiskVolume5 \\?\Device\HarddiskVolume5 is a partition on \Device\Harddisk1
512 100%
1+0 records in
1+0 records out
dd if=hello.bin od=d: bs=512 seek=1 count=1
rawwrite dd for windows version 0.6beta3.
Written by John Newbigin <jn@it.swin.edu.au>
This program is covered by terms of the GPL Version 2.
Device d: is a link to \\?\Device\HarddiskVolume5 \\?\Device\HarddiskVolume5 is a partition on \Device\Harddisk1
28 5%
0+1 records in
0+1 records out
一旦您发出这些命令,Windows 可能会自动检测到驱动器不再正确格式化。不要允许 Windows 格式化驱动器。如果您允许它格式化驱动器,它将重新分区并格式化它。这样做会破坏您编写的引导扇区。在提示时,只需取消可能出现的“格式化”对话框即可。
请记得在从系统中移除 USB 驱动器之前正确卸载/弹出它。未正确卸载可能导致数据未被完全正确写入驱动器。
如果您希望为 Bochs、QEMU、DOSbox 等创建一个磁盘映像,可以在命令提示符下使用这些命令创建一个 720k 软盘:
dd if=/dev/zero of=disk.img bs=1024 count=720
dd if=f:\boot.bin of=disk.img bs=512 count=1 conv=notrunc
dd if=f:\hello.bin of=disk.img bs=512 seek=1 count=1 conv=notrunc
图像文件disk.img
可用于Bochs、QEMU、DOSbox等,或写入720k软盘以在实际计算机上使用。
/dev/zero
看起来像是典型的Unix/Linux设备。我建议你使用的Windows中的DD命令理解/dev/zero
作为一个特殊的输入设备,只生成零。Windows没有/dev/zero
设备,但DD将其视为特殊的内部设备并进行模拟。
当在 MS Windows 上使用 Bochs 2.6.8 运行时,我看到了这个:
![enter image description here](https://istack.dev59.com/EkHZs.webp)
在我的联想L520笔记本电脑(非EFI BIOS)上,使用16GB的USB闪存驱动器,我看到了以下内容:
![enter image description here](https://istack.dev59.com/EF485.webp)