汇编 – QEMU上的BIOS int 10h打印垃圾

编写在QEMU中作为引导加载程序运行的x86实模式汇编程序时遇到问题.我正在尝试通过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’这样的垃圾作为输出.昨天,我编写了一个与此类似的代码,完全没有问题.是什么导致了我的问题以及如何解决?

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

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.

基于printChar工作的事实,并且写出整个字符串并不表示DS:SI没有指向字符串所在的内存中的正确位置.通常的原因是当BIOS跳转到引导加载程序时,开发人员错误地认为CS和/或DS寄存器已正确设置.必须手动设置.在原点为0x7c00的情在您的情况下,您使用的偏移量为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 <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
相关文章
相关标签/搜索