assemblyx86nasmbootloaderosdev

Placement of %include in my NASM bootloader affects program behavior


I am learning to write a 16-bit bootloader using NASM with BIOS interrupts to print strings to the screen. I’ve created a simple print subroutine in an external file (printer.asm), and I'm using %include to bring it into my main bootloader file.

Here’s where it gets weird: depending on where I place the %include directive, I get completely different outputs.

Here's the code and all the placements of the include with the different outputs it gives:

org 0x7C00
bits 16

%include "printer.asm" ; placing include here prints "S" (probably garbage)

mov bx, GREETING
call print

%include "printer.asm" ; placing include here prints the GREETING message twice (Hello WorldHello World)

loop:
    jmp loop

%include "printer.asm" ; placing include here prints the GREETING message as expected (Hello World)

GREETING:
    db "Hello World", 0

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

I understand that NASM uses a flat binary format and memory layout matters, but I thought %include is just a textual paste. Why is it behaving this way?

Is the string or code being overlapped? Is NASM putting instructions and data in conflicting places? What’s the correct way to organize includes and data to prevent this?

This is the printer.asm file (included file):

; prints contents stored at BX

print:
    pusha
    mov ah, 0x0E ; TTY mode

start:
    mov al, [bx]

    cmp al, 0
    je done

    int 0x10
    
    add bx, 1

    jmp start

done:
    popa
    ret

Solution

  • %includeputs the external file exactly where the command was used. The procedure from the external file thus appears on that spot in the bootloader program. Because this kind of program always gets executed from the top, you couldn't have the subroutine before anything else because then you would not have the opportunity to call it!

    The second emplacement allows the normal execution to fall-through in the subroutine, again violating the requirement that a subroutine should be called.

    Only the third emplacement is fine, since it stays out of the direct execution path.

    Ask yourself the question about where you would write that subroutine if it was not to come from an external file. Then only the 3rd emplacement would be a good choice. Possibly even writing the subroutine below the 'Hello' message...