我如何编写一个在 NASM 16 位实模式下打印空终止字符串的函数?

How do i write a function that prints a null terminated string in NASM 16 bit real mode?

我有一个简单的程序,可以将一些以 null 结尾的字符串移动到 bx 寄存器:

[org 0x7c00]    ; Tells the assembler where the code will be loaded
    mov ah, 0x0e

    mov bx, HELLO_MSG   ; Moves string inside of HELLO_MSG to bx
    call print_string   ; Calls the print_string function inside of ./print_string.asm

    mov bx, GOODBYE_MSG ; Moves string inside of GOODBYE_MSG to bx
    call print_string

    jmp $   ; Hangs

%include "print_string.asm"

HELLO_MSG:
    db 'Hello, World!', 0

GOODBYE_MSG:
    db 'Goodbye!', 0

    times 510-($-$$) db 0
    dw 0xaa55

./print_string.asm 中的 print_string 函数:

print_string:
    mov ah, 0x0e
    int 0x10
    ret

print_string 功能不起作用。根据我的理解,ah 存储了值 0x0e,所以如果 al 的值是 Xint 0x10 是 运行,它告诉BIOS 在屏幕上显示 al 的值。我将如何为字符串复制它?

print_string:
  mov ah, 0x0e
  int 0x10
  ret

您的 print_string 例程使用 BIOS.Teletype 函数 0Eh。此函数将显示保存在 AL 寄存器中的 单个字符 。由于此 BIOS 函数还要求您在 BH 中提供所需的 DisplayPage 并在 BL 中提供所需的 GraphicsColor(仅当显示器处于图形视频模式时),这可能不是最好的使用方法BX 注册为这个 print_string 例程的参数。

您的新例程将必须遍历字符串并对字符串中包含的每个字符使用单字符输出函数。因为您的字符串以零结尾,所以您一遇到那个零字节就停止循环。

[org 7C00h]

cld                  ; This makes sure that below LODSB works fine
mov  si, HELLO_MSG
call print_string
mov  si, GOODBYE_MSG
call print_string

jmp  $

print_string:
    push bx          ; Preserve BX if you need to!
    mov  bx, 0007h   ; DisplayPage BH=0, GraphicsColor BL=7 (White)
    jmp  .fetch
  .print:
    mov  ah, 0Eh     ; BIOS.Teletype
    int  10h
  .fetch:
    lodsb            ; Reads 1 character and also advances the pointer
    test al, al      ; Test if this is the terminating zero
    jnz  .print      ; It's not, so go print the character
    pop  bx          ; Restore BX
    ret

这个问题已经很老了,但我使用的 document 我在网上找到的和你一样,所以我想我会分享我找到的解决方案。作者(英国伯明翰大学计算机科学学院的Nick Blundell)希望你使用寄存器bx来存储指向字符串开头的内存地址。然后在你打印一个值之后你递增 bx 等等直到零。这是我的解决方案。

[org 0x7c00]

mov bx, HELLO_MSG
call print_string_mem

mov bx, GOODBYE_MSG
call print_string_mem

jmp $                   ; Hang
    
%include "print_string.asm"

; Data
HELLO_MSG:
    db 'Hello, World!', 0
    
GOODBYE_MSG:
    db 'Goodbye!', 0

times 510-($-$$) db 0
dw 0xaa55
print_string_mem:
    jmp test_mem
    
    test_mem:
        mov al, [bx]
        cmp al, 0
        je end_mem
        jmp print_mem
        
    print_mem:
        mov ah, 0x0e
        int 0x10
        add bx, 1
        jmp test_mem
        
    end_mem:
        ret

    ret

我上面的答案有效,更适合实际开发,但作者希望您使用在前几页中学到的工具来制定您的答案。我绞尽脑汁思考我到底应该如何得到这个答案,直到我阅读了他提到将 bx 设置为消息的内存地址的文档。希望这能帮助到我所在位置的人。

我几乎可以肯定你是在阅读 Nick Blundell 关于如何从头编写自己的 OS 的 this 文档的背景下问这个问题的,并试图找出问题 4第 21 页。

Sep Roland 的回答很好,但比作者试图通过练习教授的内容更高级。文本中的这一点尚未涵盖图形模式或 lodsbtest 指令。 Blundell 正在做一些更简单的事情,它试图利用你对迄今为止在阅读中学到的工具的理解:cmpjmpadd、标签和各种条件跳转(jejne、等等...)。

我的解决方案如下所示,效果很好:

[org 0x7c00]     ; tell NASM what address this will be loaded at
; execution starts here
 mov ax, 0
 mov ds, ax      ; make segmentation agree with NASM about data addresses
; your code can start here, after the magic boilerplate

mov bx, HELLO_MSG
call print_string

mov bx, GOODBYE_MSG
call print_string

jmp $               ; infinite loop because there's nothing to exit to

print_string:
    pusha           ; preserve our general purpose registers on the stack
    mov ah, 0x0e    ; teletype function
.work:
    mov al, [bx]    ; move the value pointed at by bx to al
    cmp al, 0       ; check for null termination
    je .done        ; jump to finish if null
    int 0x10        ; fire our interrupt: int 10h / AH=0E
    add bx, 1       ; increment bx pointer by one
    jmp .work       ; loop back
.done:
    popa            ; pop our preserved register values back from stack
    ret

;; Data placed where execution won't fall into it
HELLO_MSG:
    db `Hello World!\n\r`, 0   ; use backticks to allow C style escape

GOODBYE_MSG:
    db 'Goodbye', 0

;; More boilerplate to make this a bootable MBR
times 510-($-$$) db 0    ; pad out to 510 bytes
dw 0xaa55                ; 2-byte signature so BIOS can recognize this as a bootable MBR

同样,这个答案显然可以做得更好 - 我只是按照作者最有可能希望您在这里学习的方式来回答这个问题。