使引导加载程序可以加载USB的第二个扇区。

7

我正在学习汇编语言。我写了一个简单的引导程序。在测试后,它没有起作用。以下是我的代码:

[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

我的启动加载程序为什么不能正常工作?我做错了什么吗?有什么建议吗?

你已经尝试了什么?具体是什么“不起作用”?你是否得到任何输出? - Yexo
不,我得到了一个输出,上面写着“kernel.bin未找到”。这意味着Carry被设置为失败。 - Praveen Kumar
无法从第二扇区加载“hello.bin”。 - Praveen Kumar
你尝试在像QEMU这样的模拟器中运行它,以便更轻松地进行调试了吗? - Yexo
我的代码是 mov ax,0 mov es,ax xor bx,bx 这样写对吗?感谢您宝贵的回答。 - Praveen Kumar
显示剩余2条评论
1个回答

13
您的代码假定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

在你的第二阶段,由于printRET指令结尾,你将其创建为一个函数。应该将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的最新版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

在我的联想L520笔记本电脑(非EFI BIOS)上,使用16GB的USB闪存驱动器,我看到了以下内容:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接