我有这个程序,我想将字符串写入屏幕,但输出是空的,我在每一行都添加了注释。
[org 0x7c00] ; Tell the assembler the place to load this code
mov bx, HELLO_MSG ; move the data to the bx register
pusha ; push all register values to stack?
call printstr ; call printstr routine
printstr:
mov ah, 0x0e ; BIOS teletype output
pop bx ; pop to bx
mov al, bl ; put low byte of bx to al
int 0x10 ; print it.
cmp al, 0 ; Compare if its a string termination character?
jne printstr ; if not go back to top
ret ; return to the caller?
; Data
HELLO_MSG:
db 'Hello world!', 0
jmp $
times 510-($-$$) db 0
dw 0xaa55
我正在 qemu 中从磁盘的引导扇区运行此程序,模拟 AT 兼容的基于 x86 的计算机。
1) BX 寄存器不保存要打印的字符,但保存的是 可以找到要打印的字符的内存地址(@HELLO_MSG)。 因此你真正想说的不是“mov al,bl”而是“mov al,[bx]”,也就是说
“将 AL 设置为内存地址 BX 处存储的字节”(mov AL,[BX])
2) BX 寄存器由 BIOS 用于电传打字的颜色分量。您不能使用该寄存器作为 HELLO_MSG 处内存的索引。尝试使用 SI 寄存器 - “mov al,[SI]”。
3)在每个循环中,您需要程序移动到字符串中的下一个字符,以便它既打印下一个字符并将下一个字符与“0”进行比较,因此您需要增加索引寄存器(SI,我建议第 2 点)。所以你需要在 al 设置之后、jne 跳转之前的某个地方放置一条“inc si”指令。
4)你也滥用了堆栈。 Pusha 指令会将所有寄存器压入堆栈,并按特定顺序将它们放入堆栈。从 Pusha 中恢复过来的最好方法是 Popa。如果弹出 bx,实际上会将 ax 上原来的内容弹出到堆栈上,并且堆栈指针将不同步大约 10 个字节左右。我什至不会使用堆栈。
这就是我做你想做的事情的方式:
pusha ; save all the registers to the stack
mov si, HELLO_MSG ; set SI to to the address of the message
printstr:
mov ah, 0x0e ; BIOS teletype output
mov bh, 00h ; teletyping to the default page at B800h (IBM PC)
mov bl, 07h ; the CGA colour white
mov al,[si] ; put byte of memory address si into al
int 0x10 ; print it.
inc si
cmp al, 0 ; Compare if its a string termination character?
jne printstr ; if not go back to top
popa ; retrieve all the registers from the stack
ret ; return to the caller?
; Data
HELLO_MSG:
db 'Hello world!', 0
请注意,我使用 Pusha 和 Popa 只是为了演示它们的用法。由于我只有 ax、bx 和 si,如果确实需要在完整程序的上下文中保存这些寄存器,那么在最后对这些寄存器进行 3 次压入和 3 次弹出会更便宜。我尝试编写以便可以立即转储寄存器,除非保存其内容是不可避免的必要。
POP BX
指令并没有像你想象的那样做。它从堆栈中获取一个值并将其存储在 BX
中。它不访问 BX
指向的内存。您需要类似 MOV AL, [BX]
的内容来检索 BX
指向的字符,然后使用指令使 BX
指向下一个字符。
此外,您没有正确终止程序。
CALL
保存当前位置,然后开始打印字符串。当您 RET
时,处理器会在 CALL
指令之后继续执行。您需要一些东西来告诉处理器停止执行东西,否则它可能会尝试打印更多不应该打印的东西。
正如您在评论中提到的,您在 qemu 中使用引导扇区。似乎没有告诉 qemu 退出的概念,所以你应该阻止你的程序。实现此目的的典型方法是先使用指令
CLI
告诉处理器不应处理任何中断,然后使用指令 HLT
告诉处理器在处理中断之前不要执行任何操作。当您阻止中断时,HLT
指令将永远等待。