编写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”之类的垃圾作为输出。昨天,我编写了一个与此类似的代码,完全没有问题。是什么导致我的问题以及如何解决?
答案 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)+offset
从segment: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