h0m*_*0m3 2 assembly bios nasm bootloader x86-16
编写在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
Run Code Online (Sandbox Code Playgroud)
我[ORG 0x7c00]用作原点.我测试了printChar标签,并在AL中用一些字母调用它,它工作正常.当我尝试将内存地址加载到这样的消息时:
loadMsg db "Loading",0
mov SI, loadMessage
call print
Run Code Online (Sandbox Code Playgroud)
我在QEMU仿真器上输出像'U'这样的垃圾作为输出.昨天,我编写了一个与此类似的代码,完全没有问题.是什么导致了我的问题以及如何解决?
我最近在Stackoverflow的答案中写了一些常用的Bootloader提示,可能对你有用.可能的提示#1适用于您的问题:
当BIOS跳转到您的代码时,您不能依赖具有有效或预期值的CS,DS,ES,SS,SP寄存器.应在引导加载程序启动时正确设置它们.您只能保证将从物理地址0x00007c00加载并运行引导加载程序,并将引导驱动器号加载到DL寄存器中.
基于printChar工作的事实,并且写出整个字符串并不表示DS:SI没有指向字符串所在的内存中的正确位置.通常的原因是,当BIOS跳转到引导加载程序时,开发人员错误地认为CS和/或DS寄存器已正确设置.必须手动设置.在原点为0x7c00的情况下,DS需要设置为0.在16位实模式下,使用公式从segment:offset对计算物理内存地址(segment<<4)+offset.在您的情况下,您使用的偏移量为0x7C00.所述的值DS的0将产生正确的物理地址(0 << 4)+ 0x7c00 = 0x07c00.
您可以在程序开头将DS设置为0,例如:
xor ax, ax ; set AX to zero
mov ds, ax ; DS = 0
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
这可能适用于某些仿真器,如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
Run Code Online (Sandbox Code Playgroud)