这不是一个最小的完整示例,但问题很常见。即使没有看到其余代码,我猜你在启动时处理器(或模拟处理器)使用的段和汇编代码使用的偏移量不匹配。
对于 16 位 segment:offset addressing,存在许多指向同一物理内存地址的段和偏移量组合。 BIOS 最常用的是 0x07c0:0x0000 和 0x0000:0x0000。 (0x07c0 DS。 NASM 中缺少 ORG 指令(使用 -f bin 构建时)默认为 org 0x0000。
我有通用引导加载程序提示是这样说的:
- 当 BIOS 跳转到您的代码时,您不能依赖 CS、DS、ES、SS,SP 寄存器具有有效或预期值。当您的引导加载程序启动时,它们应该被适当地设置。您只能保证您的引导加载程序将从物理地址 0x00007c00 加载和运行,并且引导驱动器号加载到 DL 寄存器中。
[剪辑]
-
lodsb、movsb 等使用的方向标志可以设置或清除。如果方向标志设置不当SI/DI 寄存器可能会在错误的方向上调整。使用STD/CLD 将其设置为您希望的方向(CLD=forward/STD=backwards)。在这种情况下,代码假定向前移动,因此应该使用CLD。更多信息请参见instruction set reference
当您的引导加载程序启动时,您不能依赖方向 DF 标志(LODSB 需要)和段寄存器(如 DS)作为特定值(或正确的值)。您需要在代码开头明确设置它。如果不这样做,您希望打印的字符串将从 RAM 的错误部分读取,结果将是乱码。您似乎两次打印相同的乱码这一事实表明了另一个问题,那就是您没有某种无限循环来结束引导加载程序。我还有另一个 Stackoverflow Answer 我认为是相关的。
我将填写一个缺少的引导加载程序,它调用您提供的使用 0x0000 作为段的 print_string 函数,我们使用 org 0x7c00 作为起始偏移量:
; Create bootloader with: nasm -f bin boot.asm -o boot.bin
; To test in QEMU use: qemu-system-i386 -fda boot.bin
org 0x7c00
main:
xor ax, ax ; XOR register with itself zeroes the register
mov ds, ax ; We want DS:ORG to be 0x0000:0x7c00
cld ; Forward direction for string instructions like LODSB
mov si,Msg
call print_string ; Print the message
; This is a preferred way to do an infinite loop but
; you could also do: JMP $. This prevents falling through
; the code below and off to memory we don't intend to execute
cli
.endloop:
hlt
loop .endloop
print_string:
Pusha
mov ah,0Eh
xor bx,bx ; Ensure we are writing to page 0
; XOR register with itself zeroes the register
.repeat:
lodsb
cmp al,0
je .done
int 10h
Jmp .repeat
.done:
Popa
Ret
; Place data after the code but before the boot signature.
Msg db "hello",0 ; Nul terminated string
times 510 - ($ - $$) db 0 ; padding with 0 at the end
dw 0xAA55 ; PC boot signature
或者,我们可以使用 0x07c0 的段并使用 org 0x0000(或省略它),它应该仍然可以工作。代码的开头可能如下所示:
org 0x0000
main:
mov ax, 0x07c0
mov ds, ax ; We want DS:ORG to be 0x07c0:0x0000
cld ; Forward direction for string instructions like LODSB
这是我在两个版本中使用 QEMU 得到的输出: