您没有提供任何上下文,而且您的代码似乎并不完整。这些行中似乎存在一个明显的错误:
mov si,[4Ch]
cs
mov di,[4Eh]
cs
mov [180h],cx
cs
mov [182h],dx
看来你的意思是最后 4 行是 SI 和 DI:
cs
mov [180h],si
cs
mov [182h],di
如果chain loading 您无法在代码已经执行的区域顶部加载新的引导加载程序。您可能会出现异常行为。您需要将引导加载程序复制到其他地方;继续在该新位置执行代码;然后将新的引导加载程序加载到物理地址 0x07C00 的内存中。
为了便于阅读,最好将段覆盖(本例中为CS)放在内存操作数内。这段代码:
cs
mov [180h],si
可以写成:
mov [cs:180h],si
在 FASM 中完成实现
下面的代码示例:
- 设置堆栈指针,以便我们知道我们的磁盘读取不会破坏它
- 保存由 BIOS 通过 DL 传入的引导驱动器号。如果没有必要,最好不要硬编码引导驱动器号。
- 将引导加载程序代码和数据复制到 0x0060:0x0000,以便在将新引导扇区链接到 0x07c0:0x0000 时不会破坏它。内存位置 0x0060:0x0000 位于中断表、BIOS 数据区和保留内存的上方。
- 使用 FAR JMP 继续执行 0x0060 段中的代码
- 将新的 Int 13h 中断处理程序复制到 0x2000:0x0000
- 安装新的 Int 13h 中断处理程序
- 在物理地址 0x07c00 的原始磁盘之上读取引导磁盘的第 7 扇区
-
FAR JMP 到 0x0000:0x7c00 处新加载的引导加载程序
- 将汇编文件填充到 1.44MiB 软盘大小
实现这一点的 FASM 代码:
RESIDENT_SEG equ 0x2000
BOOT_SEG equ 0x07c0
BOOTCOPY_SEG equ 0x0060
STACK_OFFSET equ 0x7c00
INT13_OFFSET equ 0x13 * 4
org 0x0000
boot_start:
; Set up our own stack just below bootloader @ 0x0000:0x7c00
xor ax, ax
mov ss, ax
mov sp, STACK_OFFSET
; Copy the bootloader code and data from 0x07C0:0x0000 to 0x0060:0x0000
mov ax, BOOT_SEG
mov ds, ax
mov [boot_drive], dl
mov ax, BOOTCOPY_SEG
mov es, ax
cld
xor si, si
mov di, si
mov cx, SIZEOF_BOOT+SIZEOF_PAYLOAD
rep movsb
; Transfer control to copy of bootloader relocated to segment 0x0060
jmp BOOTCOPY_SEG:bootcopy_cont
bootcopy_cont:
; Set DS = ES= 0x0060
push es
pop ds
; ES = 0 to access IVT that begins at 0x0000:0x0000
xor ax, ax
mov es, ax
; Place the original FAR PTR of Int 13 into BX:AX
cli
mov ax, [es:INT13_OFFSET]
mov bx, [es:INT13_OFFSET+2]
; Install our new int 0x13 handler
mov si, int13
mov di, RESIDENT_SEG
mov [es:INT13_OFFSET], si
mov [es:INT13_OFFSET+2], di
; Copy the interrupt handler to 0x2000:0x0000
mov es, di
mov si, payload_boot_offset
xor di, di
mov cx, SIZEOF_PAYLOAD
rep movsb
; Now that interrupt and interrupt data (payload) is copied to 0x2000 segment
; save the orig_int13 to the interrupt data area. AX and BX were set earlier
mov [es:orig_int13], ax
mov [es:orig_int13+2], bx
sti
; Read sector 7(CHS=0,0,7) to 0x07c0:0x0000 (phys address 0x07c00)
mov ax, 201h
mov bp, BOOT_SEG
mov es, bp
xor bx, bx
mov cx, 7
xor dx, dx
mov dl, [boot_drive]
call do_orig_int13
; Jump to newly loaded bootloader
jmp 0:0x7C00
; Function : do_orig_int13
; Return : none
; Clobbers : Same as int 0x13
;
; Description:
; Manually call the original Int 0x13 handler. Int instruction pushes
; the flags and then calls through the interrupt vector table. We
; Do that same process. This avoids having to use Int 0x60. The
; Old interrupt vector was saved to RESIDENT_SEG(0x2000):orig_int13
; earlier.
do_orig_int13:
push ds
push bp
mov bp, RESIDENT_SEG
mov ds, bp
pop bp
pushf
call far [ds:orig_int13]
pop ds
ret
; Boot data
boot_drive: db 0x00
SIZEOF_BOOT = $-$$
payload_boot_offset:
org 0x0000
int13: ;NEW INT 0x13
; Print MDP to upper left corner of screen in magenta on white
push ax
push es
mov ax, 0xb800
mov es, ax
mov word [es:0x0000], (0x75 shl 8) or 'M'
mov word [es:0x0002], (0x75 shl 8) or 'D'
mov word [es:0x0004], (0x75 shl 8) or 'P'
pop es
pop ax
; Do original disk interrupt routine. We jump to it
; instead of using the INT instruction because
; The Int 0x13 original routine sets the flags and we
; want them to be returned to whoever called us.
jmp far [cs:orig_int13]
; The original interrupt handler will do the IRET for us
; Will never return to this point
; Insert data used by the interrupt handler here
orig_int13: dd 0x0000 ; Copy of the original Int 13h handler
SIZEOF_PAYLOAD = $-$$
;Pad first sector with 0, add boot signature to last 2 bytes
times 510-(SIZEOF_BOOT+SIZEOF_PAYLOAD) db 0
dw 0xaa55
; Pad out 5 more sectors with 0
times 512 * 5 db 0x00
; Sector 7 test code
org 0x7c00
sector7:
; Set DS = 0x0000
xor ax, ax
mov ds, ax
; Call Int 0x13 to reset disk. Our interrupt handler should also display MDP
; in upper left corner of the screen. AX set to zero above. Int 0x13/AH=0 is reset.
; DL set by the code that loaded this bootloader just like the BIOS would have done.
int 0x13
cli
; Infinite loop to end the code
.endloop:
hlt
jmp .endloop
; Pad out remainder of sector 7
times 512-($-$$) db 0x00
; Pad out remaining bytes to create a 1.44MiB floppy image
times (2880-7)*512 db 0x00
您可以使用以下方法将其组装成软盘映像:
fasm bootload.asm disk.img
运行它:
qemu-system-i386 -fda disk.img
输出类似于:
注意事项:
- FASM 和 NASM 的 ORG 指令的工作方式不同。
- 代码不再将原始 Int 0x13 中断向量保存到 Int 0x60。我们通过存储到内存中的地址执行 PUSHF 和 FAR CALL 手动调用旧中断。
- 代码修改 Int 0x13,使其在屏幕左上角以白色洋红色显示
MDP,然后将控制权传递给原始处理程序。
- 扇区 7 (CHS=0,0,7) 中的代码使用 Int 0x13/AH=2 来重置引导驱动器。此代码用于测试目的,但将是您希望链式加载的实际引导加载程序。
- 此代码假定我们正在从启动的同一个磁盘链式加载不同的启动扇区 (@ CHS=0,0,7)。