Search code examples
assemblyx86nasmx86-16bios

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


I have a simple program which moves some null-terminated strings to the bx register:

[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

and a print_string function inside of ./print_string.asm:

print_string:
    mov ah, 0x0e
    int 0x10
    ret

The print_string function doesn't work. From my understanding, ah has the value 0x0e stored, so if al has a value of X and int 0x10 is ran, it tells the BIOS to display the value of al on the screen. How would I replicate this for strings?


Solution

  • print_string:
      mov ah, 0x0e
      int 0x10
      ret
    

    Your print_string routine uses the BIOS.Teletype function 0Eh. This function will display the single character held in the AL register. Since this BIOS function additionally expects you to supply the desired DisplayPage in BH and the desired GraphicsColor in BL (only for when the display is in a graphics video mode), it's perhaps not the best idea to use the BX register as an argument to this print_string routine.

    Your new routine will have to loop over the string and use the single character output function for every character contained in the string. Because your strings are zero-terminated, you stop looping as soon as you encounter that zero byte.

    [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