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

Assembly 启用引导加载程序以加载USB的第二个扇区,assembly,x86-16,bootloader,osdev,bochs,Assembly,X86 16,Bootloader,Osdev,Bochs,我正在学习汇编语言。我写了一个简单的引导程序。经过测试,它没有工作。这是我的密码: [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_

我正在学习汇编语言。我写了一个简单的引导程序。经过测试,它没有工作。这是我的密码:

[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
dd if=f:\hello.bin of=\\.\d: bs=512 seek=1 count=1
使用以下命令将一个简单程序加载到USB的第二个扇区:

dd if=f:\boot.bin of=\\.\d: bs=512 count=1
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
jmp 0x0:0x1000
为什么我的引导加载程序不工作?我做错什么了吗?有什么建议吗?

您的代码假定DS设置为0。你不能这么想。如果您使用的是
org 0x7c00
,那么代码的第一部分应该显式地将DS设置为0

您应该认真考虑通过设置SS:SP来定义堆栈。您不知道现有的堆栈在哪里,也不知道它是否足够大以处理您想要做的事情

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

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

在我的回答中,我有更多关于上述问题的信息

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

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

[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
jmp 0x0:0x1000
问题是,当您阅读扇区设置ES:BX时,如下所示:

read_sector:
    mov ax, 0x0
    mov es, ax
    xor bx, bx
这会将ES:BX设置为0x0000:0x0000,这显然不是JMP期望代码的位置。您需要将ES:BX设置为要读取磁盘扇区的内存位置

INT 13h/AH=02h要求正确设置气缸/缸盖/扇区号。扇区从1开始编号,但圆柱体和封头以零为基础。磁盘的第二个扇区位于气缸0、磁头0和扇区2。您的代码将圆柱体设置为1而不是0。此代码错误,因为您确实应该将其设置为0:

mov ch, 01
在第二阶段中,您创建了一个函数
print
,因为它以
RET
指令结束<代码>jmp打印应更改为
调用打印

使用上面建议的所有更改,包括我的常规引导加载程序提示中的更改,您的代码可以修改为:

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
[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
你好。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
[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:所在的分区,而不是磁盘本身的开头

我建议您使用来自的最新DD。到今天为止,最新的是。我推荐使用此版本,因为它允许您相对于驱动器的开头正确访问磁盘(或U盘),而不是相对于特定分区的开头。这可能会导致在尝试正确存储第一和第二扇区时出现严重问题。对于最新版本,我将使用具有管理员权限的以下命令写入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:上,正如您在问题中所建议的那样警告:未能使用正确的驱动器可能会导致其他设备上的数据丢失和损坏

如果这些命令正常工作,则输出应如下所示:

图像文件
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 U盘的联想L520笔记本电脑(非EFI BIOS)上,我看到的是:


您已经尝试过什么?到底什么“不起作用”?你得到任何输出吗?没有,我得到的输出是“kernel.bin not found”。这意味着已设置进位(未成功)。“hello.bin”未从第二个扇区加载。您是否尝试在诸如QEMU之类的模拟器中运行它以简化调试?我在Bochs模拟器中进行了尝试,并启动了我的系统。我的代码正确吗?