启用引导加载程序以加载USB的第二个扇区

Pra*_*mar 6 assembly osdev bootloader bochs x86-16

我正在学习汇编语言.我写了一个简单的bootloader.测试后,它没有用.这是我的代码:

[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
Run Code Online (Sandbox Code Playgroud)

使用以下命令将此代码放在USB的第一个扇区中:

dd if=f:\boot.bin of=\\.\d: bs=512 count=1
Run Code Online (Sandbox Code Playgroud)

使用以下命令在USB的第二个扇区中加载一个简单的程序:

dd if=f:\hello.bin of=\\.\d: bs=512 seek=1 count=1
Run Code Online (Sandbox Code Playgroud)

这是加载到第二个扇区的程序的代码:

[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
Run Code Online (Sandbox Code Playgroud)

为什么我的引导装载程序不工作?我做错了什么吗?有什么建议?

Mic*_*tch 13

您的代码假定DS设置为0.您不能假设.如果您使用,代码的第一部分应明确将DS设置为0 org 0x7c00.

您应该认真考虑通过设置SS:SP来定义堆栈.您不知道现有的位置,或者它是否足以处理您打算执行的操作.

在调用引导加载程序之前,BIOS将使用引导设备编号设置DL寄存器.从引导驱动器发出驱动器请求时,不应在代码中将DL设置为0.您应该在调用引导加载程序时使用DL中存在的值.

您应该使用CLD指令清除方向标志,因为您正在使用期望在内存中向前移动的LODSB指令.无法保证方向标志将被正确设置,因此您应明确将其设置为CLD(前进)或STD(后向)所需的方向.

我在StackOverflow回答中使用General Bootloader Tips获得了有关上述问题的更多信息.

由于您没有使用BPB,因此我强烈建议您删除jmp start引导加载程序的第一条指令.而是在代码之后但在引导扇区签名(0xAA55)之前移动数据.原因是一些BIOS会尝试根据作为引导加载程序的第一条指令出现的JMP指令来查找BPB,如果发现它在内存中覆盖引导加载程序的某些部分,则会导致潜在的未定义行为.

您的引导加载程序使用此指令启动从第二个扇区加载的第二个阶段:

jmp 0x0:0x1000
Run Code Online (Sandbox Code Playgroud)

问题是,当您通过以下方式阅读扇区设置ES:BX时:

read_sector:
    mov ax, 0x0
    mov es, ax
    xor bx, bx
Run Code Online (Sandbox Code Playgroud)

这将ES:BX设置为0x0000:0x0000,这显然不是您的JMP期望代码的位置.您需要将ES:BX设置为您想要的内存位置INT 13/AH = 02h以将磁盘扇区读入.

INT 13h/AH = 02h需要正确设置气缸/扬程/扇区号.扇区的编号从1开始,但是Cylinders和Heads的编号为零.磁盘的第二个扇区位于Cylinder 0,Head 0,Sector 2.您的代码将Cylinder设置为1而不是0.此代码错误,因为您确实应将其设置为0:

mov ch, 01
Run Code Online (Sandbox Code Playgroud)

在第二阶段,您创建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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

由于看起来您正在使用基于DD命令中给出的信息的Windows ,因此您可能遇到了另一个问题.我不知道你使用的是哪个DD但是of=\\.\d:没有写入磁盘的开头(USB驱动器),它会写入D:所在的分区,而不是磁盘本身的开头.

我建议你使用最新的DDChrysocome.截至今天,最新的是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
Run Code Online (Sandbox Code Playgroud)

这假设您的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
Run Code Online (Sandbox Code Playgroud)

一旦发出这些命令,Windows可能会自动检测到驱动器不再正确格式化.不允许Windows格式化驱动器.如果您允许它格式化驱动器,它将重新对其进行分区,并对其进行格式化.这样做会破坏你写的引导扇区.出现提示时,只需取消可能出现的格式对话框.

在从系统中卸下USB驱动器之前,请务必正确卸下/弹出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
Run Code Online (Sandbox Code Playgroud)

图像文件disk.img应该可以被Bochs,QEMU,DOSbox等使用,或者写入720k软盘以便在真实计算机上使用.

/dev/zero看起来像一个典型的Unix/Linux设备.用于Windows 的DD命令我建议您将其理解/dev/zero为仅生成零的特殊输入设备.Windows没有/dev/zero设备,但DD将其视为特殊的内部设备并对其进行模拟.


在MS Windows上使用Bochs 2.6.8运行时,这就是我所看到的:

在此输入图像描述

在我的联想L520笔记本电脑(非EFI BIOS)上配有16GB USB记忆棒,这就是我所看到的:

在此输入图像描述