Int 10,啊0eh在引导加载程序中输出错误

时间:2018-03-21 12:33:18

标签: assembly x86 nasm x86-16 bootloader

我正在尝试编写自己的Os,我需要一些print_string func,它将在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 =

而Msg是"你好"

1 个答案:

答案 0 :(得分:1)

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

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

我有一般的Bootloader提示说:

  
      
  • 当BIOS跳转到您的代码时,您不能依赖 CS DS ES SS SP 寄存器具有有效或预期的值。应在引导加载程序启动时正确设置它们。您只能保证从物理地址0x00007c00加载并运行引导加载程序,并将引导驱动器号加载到 DL 寄存器中。   [剪断]
  •   
  • 可以设置或清除lodsbmovsb等使用的方向标记。如果方向标志设置不正确 SI / DI 寄存器可能会以错误的方向调整。使用STD / CLD将其设置为您希望的方向(CLD =向前/ STD =向后)。在这种情况下,代码假定向前移动,因此应使用CLD。有关详细信息,请参阅instruction set reference
  •   

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

我将填写一个缺少的引导加载程序,它调用您提供的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获得的两个版本的输出:

enter image description here