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 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.
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).