BIOS int 10h在QEMU上打印垃圾

时间:2015-11-28 17:15:44

标签: assembly nasm x86-16 bootloader bios

编写x86实模式汇编程序时遇到问题,该程序在 QEMU 中作为引导加载程序运行。我正在尝试通过BIOS中断0x10打印文本。我的代码是:

print:
    pusha
.loop:
    mov AL, [SI]
    cmp AL, 0
    je .end
    call printChar
    inc SI
    jmp .loop
.end:
    popa
    ret

printChar:
    pusha
    mov AH, 0x0E
    mov BH, 0
    mov BL, 0x0F
    int 0x10
    popa
    ret

我正在使用[ORG 0x7c00]作为原点。我测试了 printChar 标签,并在 AL 中用一些字母调用它,它工作正常。当我尝试将内存地址加载到这样的消息时:

loadMsg      db "Loading",0
mov SI, loadMessage
call print

我在 QEMU 模拟器上输出“U”之类的垃圾作为输出。昨天,我编写了一个与此类似的代码,完全没有问题。是什么导致我的问题以及如何解决?

2 个答案:

答案 0 :(得分:5)

我最近在Stackoverflow的答案中写了一些General Bootloader Tips,这些答案可能对你有用。可能提示#1 适用于您的问题:

  

当BIOS跳转到您的代码时,您不能依赖具有有效或预期值的CS,DS,ES,SS,SP寄存器。应在引导加载程序启动时正确设置它们。您只能保证您的引导加载程序将从物理地址0x00007c00加载并运行,并且引导驱动器号将加载到DL寄存器中。

基于 printChar 的工作原理,并且写出整个字符串并不表示 DS:SI 没有指向正确的位置字符串所在的内存。通常的原因是,当BIOS跳转到引导加载程序时,开发人员错误地认为 CS 和/或 DS 寄存器已正确设置。必须手动设置。在原点为0x7c00的情况下, DS 需要设置为0.在16位实模式下,使用公式(segment<<4)+offsetsegment:offset pairs计算物理内存地址。在您的情况下,您使用的偏移量为0x7C00。 DS 中的值为0将产生(0 <4)+ 0x7c00 = 0x07c00的正确物理地址。

您可以在程序开头将 DS 设置为0,例如:

xor ax, ax       ; set AX to zero
mov ds, ax       ;     DS = 0  

对于 QEMU ,BIOS跳转到0x07c0:0x0000。这也表示相同的物理存储器位置(0x07c0 <&lt; 4)+0 = 0x07c00。这样的跳转将设置 CS = 0x07c0(不是 CS = 0)。由于有许多段:偏移对映射到相同的物理内存位置,因此您需要适当地设置 DS 。您不能依赖 CS 作为您期望的价值。所以在 QEMU 中,像这样的代码甚至无法正确设置 DS (使用ORG 0x7c00时):

mov ax, cs
mov ds, ax       ; Make DS=CS

这可能适用于某些仿真器,如 DOSBOX 和一些物理硬件,但不是全部。此代码可以工作的环境是BIOS跳转到0x0000:0x7c00。在这种情况下, CS 在到达您的引导加载程序代码时将为零,并且将 CS 复制到 DS 将起作用。不要假设 CS 在所有环境中都是零,这是我正在制定的要点。始终将段寄存器设置为您想要的内容。

应该有效的代码示例是:

    BITS  16
    ORG   0x7c00
    GLOBAL main

main:
    xor ax, ax        ; AX = 0
    mov ds, ax        ; DS = 0
    mov bx, 0x7c00

    cli               ; Turn off interrupts for SS:SP update
                      ; to avoid a problem with buggy 8088 CPUs
    mov ss, ax        ; SS = 0x0000
    mov sp, bx        ; SP = 0x7c00
                      ; We'll set the stack starting just below
                      ; where the bootloader is at 0x0:0x7c00. The
                      ; stack can be placed anywhere in usable and
                      ; unused RAM.
    sti               ; Turn interrupts back on

    mov SI, loadMsg
    call print

    cli
.endloop:
    hlt
    jmp .endloop      ; When finished effectively put our bootloader
                      ; in endless cycle

print:
    pusha
.loop:
    mov AL, [SI]      ; No segment on [SI] means implicit DS:[SI]
    cmp AL, 0
    je .end
    call printChar
    inc SI
    jmp .loop
.end:
    popa
    ret

printChar:
    pusha
    mov AH, 0x0E
    mov BH, 0
    mov BL, 0x0F
    int 0x10
    popa
    ret

; Place the Data after our code
loadMsg db "Loading",0

times 510 - ($ - $$) db 0   ; padding with 0 at the end
dw 0xAA55                   ; PC boot signature

答案 1 :(得分:1)

这是一个问题:

loadMsg    db "Loading",0
mov        SI, loadMessage
call    print

除非程序跳过&#34; loading&#34;文本,它执行那些字节可能(或可能不)表示的任何指令。像这样的东西解决了它:

    jmp      print_msg
    loadMsg    db "Loading",0
print_msg:
    mov        SI, loadMessage
    call    print