在 16 位引导加载程序中将字符串打印到屏幕的汇编程序

问题描述 投票:0回答:2

我有这个程序,我想将字符串写入屏幕,但输出是空的,我在每一行都添加了注释。

[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 的计算机。

assembly nasm x86-16 bootloader bios
2个回答
3
投票

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 次弹出会更便宜。我尝试编写以便可以立即转储寄存器,除非保存其内容是不可避免的必要。


2
投票

POP BX
指令并没有像你想象的那样做。它从堆栈中获取一个值并将其存储在
BX
中。它不访问
BX
指向的内存。您需要类似
MOV AL, [BX]
的内容来检索
BX
指向的字符,然后使用指令使
BX
指向下一个字符。

此外,您没有正确终止程序。

CALL
保存当前位置,然后开始打印字符串。当您
RET
时,处理器会在
CALL
指令之后继续执行。您需要一些东西来告诉处理器停止执行东西,否则它可能会尝试打印更多不应该打印的东西。

正如您在评论中提到的,您在 qemu 中使用引导扇区。似乎没有告诉 qemu 退出的概念,所以你应该阻止你的程序。实现此目的的典型方法是先使用指令

CLI
告诉处理器不应处理任何中断,然后使用指令
HLT
告诉处理器在处理中断之前不要执行任何操作。当您阻止中断时,
HLT
指令将永远等待。

© www.soinside.com 2019 - 2024. All rights reserved.