启用引导加载程序以加载 USB 的第二个扇区
Enable the boot loader to load the second sector of a USB
我正在学习汇编语言。我写了一个简单的引导程序。试了之后,还是不行。这是我的代码:
[bits 16]
[org 0x7c00]
jmp start
data:
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
start:
mov si, wolf_wel_msg
call wolf_print
mov si, wolf_kernel_load
call wolf_print
pushf
stc
mov ah,00
mov dl,00
int 13h
read_sector:
mov ax, 0x0
mov es, ax
xor bx, bx
mov ah, 02
mov al, 01
mov ch, 01
mov cl, 02
mov dh, 00
mov dl, 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
times 510-($-$$) db 0
dw 0xAA55
此代码使用此命令放置在 USB 的第一个扇区:
dd if=f:\boot.bin of=\.\d: bs=512 count=1
使用此命令将一个简单的程序加载到 USB 的第二扇区:
dd if=f:\hello.bin of=\.\d: bs=512 seek=1 count=1
这是加载到第二扇区的程序代码:
[bits 16]
[org 0x1000]
jmp start
data:
msg db 'Hello',0
start:
mov si, msg
jmp print
cli
hlt
print:
lodsb
or al, al
jz exit
mov ah,0x0e
int 10h
jmp print
exit:
ret
为什么我的引导加载程序不工作?我做错了什么吗?有什么建议吗?
您的代码假定 DS 设置为 0。您不能这样假定。如果您使用 org 0x7c00
.
,则代码的第一部分应明确将 DS 设置为 0
您应该认真考虑通过设置 SS:SP 来定义堆栈。你不知道现有的在哪里或者它是否足够大来处理你打算做的事情。
就在您的引导加载程序被调用之前,BIOS 将设置 DL 寄存器为引导设备号。从引导驱动器发出驱动器请求时,您不应在代码中将 DL 设置为 0。当您的引导加载程序被调用时,您应该使用 DL 中存在的值。
你应该使用 CLD 指令来清除方向标志,因为你正在使用 LODSB 指令期望在内存中向前移动.不能保证方向标志将被正确设置,因此您应该使用 CLD(forward) 或 STD[=121= 将其明确设置为您需要的方向](向后)。
我在 的 Whosebug 回答中有关于上述问题的更多信息。
因为你没有使用 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 需要正确设置 Cylinder/Head/Sector 数字。扇区从 1 开始编号,但柱面和磁头从零开始。磁盘的第二个扇区位于 Cylinder 0,Head 0,Sector 2。您的代码将 Cylinder 设置为 1 而不是 0。此代码是错误的,因为您确实应该将其设置为 0:
mov ch, 01
在您的第二阶段中,您创建了 print
作为函数,因为它以 RET
指令结束。 jmp print
应该改为 call print
.
根据上面推荐的所有更改,包括我的一般引导加载程序提示中的更改,您的代码可以修改为:
boot.asm
[bits 16]
[org 0x7c00]
; Use the boot drive number passed to us by BIOS in register DL
start:
xor ax,ax ; We want a segment of 0 for DS for this question
mov ds,ax ; Set AX to appropriate segment value for your situation
mov es,ax ; In this case we'll default to ES=DS
mov bx,0x8000 ; Stack segment can be any usable memory
mov ss,bx ; This places it with the top of the stack @ 0x80000.
mov sp,ax ; Set SP=0 so the bottom of stack will be @ 0x8FFFF
cld ; Set the direction flag to be positive direction
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 ; ES = 0
mov bx, 0x1000 ; BX = 0x1000. ES:BX=0x0:0x1000
; ES:BX = starting address to read sector(s) into
mov ah, 02 ; Int 13h/AH=2 = Read Sectors From Drive
mov al, 01 ; Sectors to read = 1
mov ch, 00 ; CH=Cylinder. Second sector of disk
; is at Cylinder 0 not 1
mov cl, 02 ; Sector to read = 2
mov dh, 00 ; Head to read = 0
; DL hasn't been destroyed by our bootloader code and still
; contains boot drive # passed to our bootloader by the BIOS
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
; Moved the data before the boot signature but after the code
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 ; print is a function, use CALL instead of JMP
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. As of today the latest is 0.6beta3 的最新 DD。我推荐这个版本,因为它允许您相对于驱动器的开头正确访问磁盘(或 USB 记忆棒),而不是相对于特定分区的开头。这可能会在尝试正确存储第 1 和第 2 扇区时导致严重问题。对于最新版本,我将使用这些命令和 管理员权限 来写入 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 驱动器之前正确地 dismount/eject。未能正确卸载可能会导致数据无法 properly/completely 写入驱动器。
如果您希望为 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 时,这是我看到的:
在我的带有 16GB USB 记忆棒的 Lenovo L520 笔记本电脑(非 EFI BIOS)上,这是我看到的:
我正在学习汇编语言。我写了一个简单的引导程序。试了之后,还是不行。这是我的代码:
[bits 16]
[org 0x7c00]
jmp start
data:
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
start:
mov si, wolf_wel_msg
call wolf_print
mov si, wolf_kernel_load
call wolf_print
pushf
stc
mov ah,00
mov dl,00
int 13h
read_sector:
mov ax, 0x0
mov es, ax
xor bx, bx
mov ah, 02
mov al, 01
mov ch, 01
mov cl, 02
mov dh, 00
mov dl, 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
times 510-($-$$) db 0
dw 0xAA55
此代码使用此命令放置在 USB 的第一个扇区:
dd if=f:\boot.bin of=\.\d: bs=512 count=1
使用此命令将一个简单的程序加载到 USB 的第二扇区:
dd if=f:\hello.bin of=\.\d: bs=512 seek=1 count=1
这是加载到第二扇区的程序代码:
[bits 16]
[org 0x1000]
jmp start
data:
msg db 'Hello',0
start:
mov si, msg
jmp print
cli
hlt
print:
lodsb
or al, al
jz exit
mov ah,0x0e
int 10h
jmp print
exit:
ret
为什么我的引导加载程序不工作?我做错了什么吗?有什么建议吗?
您的代码假定 DS 设置为 0。您不能这样假定。如果您使用 org 0x7c00
.
您应该认真考虑通过设置 SS:SP 来定义堆栈。你不知道现有的在哪里或者它是否足够大来处理你打算做的事情。
就在您的引导加载程序被调用之前,BIOS 将设置 DL 寄存器为引导设备号。从引导驱动器发出驱动器请求时,您不应在代码中将 DL 设置为 0。当您的引导加载程序被调用时,您应该使用 DL 中存在的值。
你应该使用 CLD 指令来清除方向标志,因为你正在使用 LODSB 指令期望在内存中向前移动.不能保证方向标志将被正确设置,因此您应该使用 CLD(forward) 或 STD[=121= 将其明确设置为您需要的方向](向后)。
我在
因为你没有使用 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 需要正确设置 Cylinder/Head/Sector 数字。扇区从 1 开始编号,但柱面和磁头从零开始。磁盘的第二个扇区位于 Cylinder 0,Head 0,Sector 2。您的代码将 Cylinder 设置为 1 而不是 0。此代码是错误的,因为您确实应该将其设置为 0:
mov ch, 01
在您的第二阶段中,您创建了 print
作为函数,因为它以 RET
指令结束。 jmp print
应该改为 call print
.
根据上面推荐的所有更改,包括我的一般引导加载程序提示中的更改,您的代码可以修改为:
boot.asm
[bits 16]
[org 0x7c00]
; Use the boot drive number passed to us by BIOS in register DL
start:
xor ax,ax ; We want a segment of 0 for DS for this question
mov ds,ax ; Set AX to appropriate segment value for your situation
mov es,ax ; In this case we'll default to ES=DS
mov bx,0x8000 ; Stack segment can be any usable memory
mov ss,bx ; This places it with the top of the stack @ 0x80000.
mov sp,ax ; Set SP=0 so the bottom of stack will be @ 0x8FFFF
cld ; Set the direction flag to be positive direction
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 ; ES = 0
mov bx, 0x1000 ; BX = 0x1000. ES:BX=0x0:0x1000
; ES:BX = starting address to read sector(s) into
mov ah, 02 ; Int 13h/AH=2 = Read Sectors From Drive
mov al, 01 ; Sectors to read = 1
mov ch, 00 ; CH=Cylinder. Second sector of disk
; is at Cylinder 0 not 1
mov cl, 02 ; Sector to read = 2
mov dh, 00 ; Head to read = 0
; DL hasn't been destroyed by our bootloader code and still
; contains boot drive # passed to our bootloader by the BIOS
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
; Moved the data before the boot signature but after the code
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 ; print is a function, use CALL instead of JMP
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. As of today the latest is 0.6beta3 的最新 DD。我推荐这个版本,因为它允许您相对于驱动器的开头正确访问磁盘(或 USB 记忆棒),而不是相对于特定分区的开头。这可能会在尝试正确存储第 1 和第 2 扇区时导致严重问题。对于最新版本,我将使用这些命令和 管理员权限 来写入 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 驱动器之前正确地 dismount/eject。未能正确卸载可能会导致数据无法 properly/completely 写入驱动器。
如果您希望为 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 时,这是我看到的:
在我的带有 16GB USB 记忆棒的 Lenovo L520 笔记本电脑(非 EFI BIOS)上,这是我看到的: