Search code examples
assemblyx86nasmbootloader

Bootloader doesn't jump to the right address and doesn't print anything


I'm trying to build a small bootloader that loads the second sector of a floppy disk.

The bootloader I started is pretty basic:

  • I print a hello world message.
  • Load the second sector.

I compile it and put along with a sample. I run qemu with gdb, and see if the I could jump to the loaded code.

here is how I compile:

nasm -f bin boot.asm -o boot.bin
nasm -f bin sample.asm -o sample.bin

dd if=/dev/zero of=disk.img bs=512 count=2880
dd conv=notrunc if=boot.bin of=disk.img bs=512 count=1 seek=0
dd conv=notrunc if=sample.bin of=disk.img bs=512 count=1 seek=1

Here is how I run qemu: qemu-system-i386 -fda build/disk.img -nographic

here is my code:

boot.asm:

org 0x7c00
bits 16

;; msg db "Hello world", 0ah, 0h --> EDIT: REMOVED


start:
  jmp setup

setup:
    xor ax, ax
    mov ds, ax
    mov bx, 0x7c00

    cli
    mov ss, ax
    mov sp, bx
    sti

    cld

boot:

  ;; call print_startup --> EDIT: REMOVED

  mov ax, 0x7e0

  mov es, ax
  xor bx, bx
load:
  mov al, 2                     
  mov ch, 0                     
  mov cl, 2                     
  mov dh, 0 
  mov dl, 0 

  mov ah, 0x2 
  int 0x13 
  jc load                
  jmp 0x7E0:0x0   ; jmp 0x7E00 works


times 510 - ($ - $$) db 0

dw 0xaa55

sample.asm

print_startup: --> Edited
    mov  si, msg
    mov  bx, 0x0007     ; DisplayPage 0, GraphicsColor 7 (White)
    lodsb               ; We don't have empty messages, do we?
.more:
    mov  ah, 0x0E
    int  0x10
    lodsb
    test al, al
    jnz  .more
    ret

msg db "Hello world", 13, 10, 0

this line jmp 0x7E0:0x0 is supposed to jump at the address 0x7E0 << 4 + 0x0 = 0x7E00 right ? But when running it, it is jumping somewhere else.

Looking in GDB, the address where it is jumping is 0x7E00000, i.e. adding 4 * 0 after.

However if I replace it this line by jmp 0x7E00 or jmp 0x0:0x7E00 it works perfectly (Which actually make more sense to me since it is segment(0x0):offset(0x7E00)), but doesn't correspond to what it is explained in the internet and more specifically in stack-overflow.

Moreover, I am trying to print the message hello world using int 0x10 but doesn't seem to locate properly the message and or al, al results to 0 on the first iteration.

Can please someone assist me on this and tell me what am I doing wrong? I'm pretty sure there are points that I'm missing and therefore I'm doing something silly. `

EDIT I edited the code to add the correction from the answer, but left the bootloader as the question was asked to avoid confusion.

The issue I am having now is that it still not jumping on the right address for some reason even with the code provided by Sep Roland.


Solution

    • Execution in a bootloader starts from the top. You should never place data items there. See Assembly (x86): <label> db 'string',0 does not get executed unless there's a jump instruction. Just move your message near the bottom right above the times directive.

    • In the absence of an ORG directive, the assembler assumes ORG 0. This will not match with your initialization of DS=0. As a consequence mov si, msg will not setup correctly and you will not see the message displayed. What you need is ORG 0x7C00 at the top of the program.

    • The print routine forgets to advance the SI pointer. You will get stuck in an infinite loop! In below code I have solved this using lodsb.

    • You state that you load the second sector, but code like load: mov al, 2 is asking BIOS to load 2 sectors (sector 2 and sector 3). Make sure this is what you need.

    • Going for perfection, the jump in code like jmp setup setup: is redundant since execution can just fall through.

    Looking in GDB, the address where it is jumping is 0x7E00000

    Don't worry. Many a time offset (low word) and segment (high word) are displayed separately but adjacent. What you then see is the true operand from your far jump instruction jmp 0x7E0:0x0.


    bits 16
    org 0x7C00
    
      xor ax, ax
      mov ds, ax
      mov ss, ax
      mov sp, 0x7C00
      cld
    
      call print_startup
    
      mov  ax, 0x7E0
      mov  es, ax
      xor  bx, bx
    load:
      mov  dh, 0            ; Use DL like BIOS passed it to your bootloader
      mov  cx, 0x0002
      mov  ax, 0x0201 
      int  0x13             ; -> AX CF
      jc   load             ; Better put a limit on this, say max 3 tries
      jmp  0x07E0:0x0000
    
    print_startup:
        mov  si, msg
        mov  bx, 0x0007     ; DisplayPage 0, GraphicsColor 7 (White)
        lodsb               ; We don't have empty messages, do we?
    .more:
        mov  ah, 0x0E
        int  0x10
        lodsb
        test al, al
        jnz  .more
        ret
    
    msg db "Hello world", 13, 10, 0
    
    times 510 - ($ - $$) db 0
    
    dw 0xAA55
    

    For BIOS, the newline code is actually carriage return (13) plus linefeed (10).