Search code examples
nasmbootloader16-bit

NASM - Use labels for code loaded from disk


As a learning experience, I'm writing a boot-loader for BIOS in NASM on 16-bit real mode in my x86 emulator, Qemu.

BIOS loads your boot-sector at address 0x7C00. NASM assumes you start at 0x0, so your labels are useless unless you do something like specify the origin with [org 0x7C00] (or presumably other techniques). But, when you load the 2nd stage boot-loader, its RAM origin is different, which complicates the hell out of using labels in that newly loaded code.

What's the recommended way to deal with this? It this linker territory? Should I be using segment registers instead of org?

Thanks in advance!

p.s. Here's the code that works right now:

[bits 16]
[org 0x7c00]
LOAD_ADDR: equ 0x9000   ; This is where I'm loading the 2nd stage in RAM.
start:
    mov bp, 0x8000      ; set up the stack
    mov sp, bp          ; relatively out of the way

    call disk_load      ; load the new instructions
                        ; at 0x9000

    jmp LOAD_ADDR

%include "disk_load.asm"
times 510 - ($ - $$) db 0
dw 0xaa55 ;; end of bootsector

seg_two:

    ;; this is ridiculous. Better way?

    mov cx, LOAD_ADDR + print_j - seg_two
    jmp cx
    jmp $

print_j:
    mov ah, 0x0E
    mov al, 'k'
    int 0x10
    jmp $

times 2048 db 0xf

Solution

  • You may be making this harder than it is (not that this is trivial by any means!)

    Your labels work fine and will continue to work fine. Remember that, if you look under the hood at the machine code generated, your short jumps (everything after seg_two in what you've posted) are relative jumps. This means that the assembler doesn't actually need to compute the real address, it simply needs to calculate the offset from the current opcode. However, when you load your code into RAM at 0x9000, that is an entirely different story.

    Personally, when writing precisely the kind of code that you are, I would separate the code. The boot sector stops at the dw 0xaa55 and the 2nd stage gets its own file with an ORG 0x9000 at the top.

    When you compile these to object code you simply need to concatenate them together. Essentially, that's what you're doing now except that you are getting the assembler to do it for you.

    Hope this makes sense. :)