您没有提供最小的完整示例,并且有迹象表明您可能不知道实模式 20-bit segment:offset addressing 的工作原理。实模式下的每个内存位置都由一个 16 位段和一个 16 位偏移量组成。两者结合起来计算 20 位物理地址,公式为:PhysicalAddress = (Segment
在内存中的物理地址 0x07c00 处加载引导加载程序。您必须选择一个 ORG 并在引导加载程序中设置这些段,以便它们引用物理地址 0x07c00。一个以上的 20 位段:偏移地址可以指向同一个物理地址。引导加载程序的 2 个常见部分是使用 ORG 0x7c00 段设置为 0x0000 ((0x0000ORG 0x0000 和段 0x07c0 ((0x07c0
我在Stackoverflow answer 中有一些常规引导加载程序提示。如果您可能正在编写引导加载程序以在 FDD 模式下在 USB 驱动器上运行,您还需要阅读我的 Stackoverflow answer 关于使用表示软盘的 BIOS 数据区 (BDA)。
此示例是一个简单的引导加载程序,其 BDA 基于您的代码将扇区 9 复制到扇区 1 (MBR),然后使用 int 0x19 重新启动。该代码还使用内存中引导加载程序之后的内存 (@ 0x0000:0x7e00) 进行临时存储以进行扇区复制。我还提供了一个测试引导加载程序,该引导加载程序放置在第 9 扇区中,它在运行时会显示一条消息。
boot.asm:
org 0x7c00
bits 16
boot:
jmp main
TIMES 3-($-$$) DB 0x90 ; Support 2 or 3 byte encoded JMPs before BPB.
; Use a BIOS Parameter Block if you intend to use this on USB in FDD mode
; Dos 4.0 EBPB 1.44MB floppy
OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x29
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "
main:
; BIOS passes our boot drive number in DL
; Setup segments
xor ax, ax ; AX=0
mov ds, ax ; DS=ES=0 because we use an org of 0x7c00
; segment<<4+offset = 0x0000<<4+0x7c00 = 0x07c00
mov es, ax
mov ss, ax
mov sp, 0x7c00 ; SS:SP= 0x0000:0x7c00 stack just below bootloader
;---read sector - 9th
mov bx, buffer ; ES: BX point to buffer (ES set to zero previously)
; mov dl,0 ; use boot drive passed to bootloader by BIOS in DL
mov dh,0 ; head number
mov ch,0 ; track number
mov cl,9 ; sector number
mov al,1 ; number of sectors to read
mov ah,2 ; read function number
int 13h
;---write sector - 1th
; The following commented lines aren't required. Int AH=13h/AH=2 only
; destroys AX and the following registers remain unchanged from the
; read disk BIOS call
; mov bx, buffer ; ES: BX must point to the buffer
; mov dl,0 ; use boot drive passed to bootloader by BIOS in DL
; mov dh,0 ; head number
; mov ch,0 ; track number
mov cl,1 ; sector number
mov al,1 ; number of sectors to write
mov ah,3 ; write function number
int 13h
mov si, message
call print_string ; Print a banner to the console
int 19h ; Warm reboot, should run bootloader that was in sector 9
; Function: print_string
; Display a string to the console on display page 0
;
; Inputs: SI = Offset of address to print
; Clobbers: AX, BX, SI
print_string: ; Routine: output string in SI to screen
mov ah, 0eh ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 10h ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
message: db "Running original bootloader...", 0x0a, 0x0d, 0
times 510 - ($-$$) db 0
dw 0xaa55 ; Boot signature
; This buffer is right after the bootloader and will be at offset 0x7e00.
; 0x0000:0x7e00 is the memory location starting right after the 512 byte
; bootloader loaded into memory by the BIOS
buffer:
sector9.asm:
org 0x7c00
bits 16
boot:
jmp main
TIMES 3-($-$$) DB 0x90 ; Support 2 or 3 byte encoded JMPs before BPB.
; Use a BIOS Parameter Block if you intend to use this on USB in FDD mode
; Dos 4.0 EBPB 1.44MB floppy
OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x29
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "
main:
; Setup segments
xor ax, ax ; AX=0
mov ds, ax ; DS=ES=0 because we use an org of 0x7c00
; segment<<4+offset = 0x0000<<4+0x7c00 = 0x07c00
mov ss, ax
mov sp, 0x7c00 ; SS:SP= 0x0000:0x7c00 stack just below bootloader
mov si, message
call print_string ; Print a banner to the console
cli
.endloop: ; Infinite loop to end bootloader
hlt
jmp .endloop
; Function: print_string
; Display a string to the console on display page 0
;
; Inputs: SI = Offset of address to print
; Clobbers: AX, BX, SI
print_string: ; Routine: output string in SI to screen
mov ah, 0eh ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 10h ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
message: db "Running sector 9 bootloader...", 0x0a, 0x0d, 0
times 510 - ($-$$) db 0
dw 0xaa55 ; boot signature
要构建 1.44MiB 软盘映像并将主引导加载程序放置在扇区 1 并将辅助引导加载程序放置在扇区 9,如果您在具有 dd 命令的系统上运行,则可以使用如下命令:
nasm -f bin boot.asm -o boot.bin
nasm -f bin sector9.asm -o sector9.bin
dd if=/dev/zero of=disk.img bs=1024 count=1440
dd if=boot.bin of=disk.img conv=notrunc seek=0
dd if=sector9.bin of=disk.img conv=notrunc seek=8
您可以使用 QEMU 运行此代码:
qemu-system-i386 -fda disk.img
如果你在模拟器或像 QEMU 这样的虚拟机中运行它,它应该会显示如下内容:
发生的情况是,boot.bin 中的原始引导加载程序将扇区 9 复制到扇区 1,然后机器重新启动。重新启动后,它运行sector9.bin 中的引导加载程序代码,该代码从扇区 9 复制到 MBR。它应该打印:
正在运行原始引导加载程序...
稍后应该打印:
正在运行第 9 扇区引导加载程序...
注意:您需要确保您的磁盘没有写保护,并且您使用的任何 BIOS 都没有使用 MBR 安全性。 MBR 安全性 防止 BIOS 调用覆盖引导驱动器上的 MBR(扇区 1)。