BIOS int 10h 在 QEMU 上打印垃圾 [英] BIOS int 10h printing garbage on QEMU

查看:27
本文介绍了BIOS int 10h 在 QEMU 上打印垃圾的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a problem while writing an x86 real mode assembly program that runs as a bootloader in QEMU. I'm trying to print text through BIOS interrupt 0x10. My code is:

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 中的一些字母调用它,它工作正常.当我尝试将内存地址加载到这样的消息时:

I'm using [ORG 0x7c00] as an origin point. I tested the printChar label and calling it with some letter in AL and it works fine. When I try to load a memory address to a message like this:

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

我在 QEMU 模拟器上得到像U"这样的垃圾作为输出.昨天,我写了一个和这个非常相似的代码,完全没有问题.是什么导致了我的问题,如何解决?

I get garbage like 'U' as output on QEMU emulator. Yesterday, I wrote a code really similar to this one and have no problem at all. What is causing my problem and how can it be fixed?

推荐答案

我最近在一篇文章中写了一些一般引导加载程序提示Stackoverflow 答案可能对您有用.可能提示 #1 适用于您的问题:

I recently wrote some General Bootloader Tips in a Stackoverflow answer that may be of some use to you. Likely Tip #1 applies to your problem:

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

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 的情况下,DS 需要设置为 0.在 16 位实模式中,物理内存地址从 segment:offset pair 使用公式(segment<<<;4)+偏移量.在您的情况下,您使用的偏移量为 0x7C00.DS 中的值为 0 将产生 (0<<4)+0x7c00 = 0x07c00 的正确物理地址.

Based on the fact that printChar works, and that writing an entire string out doesn't suggests that DS:SI is not pointing at the proper location in memory where your string resides. The usual cause of this is that developers incorrectly assume the CS and/or DS register is set appropriately when the BIOS jumps to the bootloader. It has to be manually set. In the case of an origin point of 0x7c00, DS needs to be set to 0. In 16-bit real mode physical memory addresses are computed from segment:offset pairs using the formula (segment<<4)+offset . In your case you are using an offset of 0x7C00. A value in DS of 0 would yield the correct physical address of (0<<4)+0x7c00 = 0x07c00 .

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

You can set DS to 0 at the start of your program with something like:

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

对于 QEMU,BIOS 跳转到 0x07c0:0x0000 .这也表示相同的物理内存位置 (0x07c0<<4)+0 = 0x07c00.这样的跳转将设置 CS=0x07c0(而不是 CS=0).由于有很多segment:offset 对映射到相同的物理内存位置,您需要适当地设置DS.您不能依赖 CS 是您期望的价值.所以在 QEMU 中,这样的代码甚至不能正确设置 DS(当使用 ORG 0x7c00 时):

In the case of QEMU, the BIOS jumps to 0x07c0:0x0000 . This also represents the same physical memory location (0x07c0<<4)+0 = 0x07c00 . Such a jump will set CS=0x07c0 (not CS=0). Since there are many segment:offset pairs that map to the same physical memory location, you need to set DS appropriately. You can't rely on CS being the value you expect. So in QEMU, code like this wouldn't even set DS correctly (when using ORG 0x7c00):

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

这可能适用于某些模拟器,例如 DOSBOX 和某些物理硬件,但不是全部.此代码适用的环境是 BIOS 跳转到 0x0000:0x7c00 时.在这种情况下,CS 在到达您的引导加载程序代码时将为零,并且将 CS 复制到 DS 将起作用.不要假设 CS 在所有环境中都为零,这是我要提出的主要观点.始终将段寄存器显式设置为您想要的.

This may work on some emulators like DOSBOX and some physical hardware, but not all. The environments where this code would work is when the BIOS jumps to 0x0000:0x7c00 . In that case CS would be zero when it reaches your bootloader code, and copying CS to DS would work. Don't assume CS will be zero in all environments is the main point I am making. Always set the segment registers to what you want explicitly.

应该工作的代码示例是:

An example of code that should work is:

    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

这篇关于BIOS int 10h 在 QEMU 上打印垃圾的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆