Int 10, ah 0eh 在引导加载程序中给出了错误的输出

Int 10, ah 0eh gives wrong output in a bootloader

我正在尝试编写自己的 Os 并且我需要一些 print_string 函数来在 sI 中打印字符串 它是这样的

print_string:
    Pusha
    mov ah,0Eh

    .repeat:
    lodsb
    cmp al,0
    je .done
    int 10h
    Jmp .repeat

    .done:
    Popa
    Ret

但是

中的输出
mov si,Msg
call print_string

就像Wn=Wn=

而消息是 "hello"

这不是一个最小的完整示例,但这个问题很常见。即使没有看到其余代码,我猜您在启动时处理器(或模拟处理器)使用的段与汇编代码使用的偏移量不匹配。

对于 16 位 segment:offset addressing,有许多指向相同物理内存地址的段和偏移量的组合。 BIOS 最常用的是 0x07c0:0x0000 和 0x0000:0x0000。 (0x07c0 << 4)+0x0000 = 物理地址 0x07c00。 (0x0000 << 4) + 0x7c00 = 物理地址也为 0x07c00。这个想法是您希望您的 ORG 指令反映您要使用的偏移部分,然后在引导加载程序启动时手动设置 DS 。 NASM 中缺少 ORG 指令(使用 -f bin 构建时)默认为 org 0x0000.

我有这样的一般引导加载程序提示:

  • When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register. [snip]
  • The direction flag used by lodsb, movsb etc could be either set or cleared. If the direction flag is set improperly SI/DI registers may be adjusted in the wrong direction. Use STD/CLD to set it to the direction you wish (CLD=forward/STD=backwards). In this case the code assumes forward movement so one should use CLD. More on this can be found in an instruction set reference

您不能依赖方向 DF 标志(LODSB 需要)和段寄存器(如 DS)作为特定值(或正确值),当您引导加载程序启动。您需要在代码的开头明确设置它。如果不这样做,您希望打印的字符串将从 RAM 的错误部分读取,结果将是乱码。您似乎两次打印相同的乱码这一事实表明了另一个问题,那就是您没有某种无限循环来结束您的引导加载程序。我还有一个 我认为是相关的。

我将填写一个缺少的引导加载程序,它调用您提供的 print_string 函数,该函数使用 0x0000 作为段,我们使用 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 得到的输出: