I am trying to write my own bootloader. While it works fine in QEMU, Bochs and VirtualBox, I cannot seem to make it work on my laptop.
On my laptop, the bootloader behaves very differently to all emulators, hanging seemingly random places, refusing to print, even skipping some jmp $
instructions.
While I have a lot of trouble with "real-hardware", I figure there is one cause to them all.
The following code is a short bootloader that should print a "TEST" message 3 times, then hang by jumping to the same location:
[BITS 16]
[ORG 0x7C00]
jmp 0x0000:start_16 ; In case bootloader is at 0x07C0:0x0000
start_16:
xor ax, ax
mov ds, ax
mov es, ax
cli ; Disable interrupts
mov ss, ax
mov sp, 0x7C00
sti ; Enable interrupts
cld ; Clear Direction Flag
; Store the drive number
mov [drive_number], dl
; Print message(s)
mov si, msg
call print_string
mov si, msg
call print_string
mov si, msg
call print_string
jmp $ ; HALT
; print_string
; si = string
print_string:
pusha
mov ah, 0x0E
.repeat:
lodsb
cmp al, 0x00
je .done
int 0x10
jmp short .repeat
.done:
popa
ret
; Variables
drive_number db 0x00
msg db 'TEST', 0x0D, 0x0A, 0x00
times 510-($-$$) db 0x00
db 0x55
db 0xAA
Compile and emulate the code with:
$ nasm -f bin bootloader.asm
$ qemu-system-x86_64 bootloader
On emulators, it prints "TEST" three times and hangs, On my laptop, it prints "TEST" followed by 3 weird characters:
Most bootloader code from http://wiki.osdev.org does not work either. For example, none of the code-snippets from http://wiki.osdev.org/Babystep2 work on my laptop.
What is wrong with my code? How can I fix it?
Additional information
If I remove the 2 unnecessary mov si, msg
, the "TEST" message is printed twice.
Laptop:
- Asus Vivobook S200,
- CPU: Intel i3-3217U
- BIOS: American Megatrends, Version 210.
- The computer works just fine with any other bootloader such as Grub.
Assembly and writing:
$ nasm -f bin bootloader.asm
$ qemu-system-x86_64 bootloader # TEST 1
$ sudo dd if=/dev/zero of=/dev/sdd bs=1M count=1 # clean the USB
$ sudo dd if=bootloader of=/dev/sdd conv=fsync # write to USB
$ qemu-system-x86_64 /dev/sdd # TEST 2
Edit 1
Ross Ridge noticed in the comments that Ω♣|
are the first 3 bytes of the bootloader.
Edit 2
Updated print function and string:
print_string:
pusha
.repeat:
mov ah, 0x0E
xor bx, bx
cld ; Clear Direction Flag
lodsb
cmp al, 0x00
je .done
int 0x10
jmp short .repeat
.done:
popa
ret
msg db 'TEST', 0x00
Output
A single TEST
. Additional two are missing.
Edit 3
As suggested by Ross Ridge, dumpregs
have been added for better debugging. The int 0x10
does not modify any registers.
After some testing, I have moved the dumpregs
function around drive_number
assignment and a jmp $
just after. The code should print 1 line of register dump and halt. Instead, it continues:
The full code: https://gist.github.com/anonymous/0ddc146f73ff3a13dd35
Edit 4
Disassembly of the current bootloader using:
$ ndisasm -b16 bootload2 -o 0x7c00