I'm trying to write a bootloader and kernel in assembly for learning purposes. When I assemble my code, and boot a virtual machine with it, the bootloader seems to work properly, but the kernel never starts. I'm thinking that I'm jumping to the wrong instruction, but don't know what corrections I need to make to resolve my issue. There's also the possibility that I'm trying to make the floppy incorrectly, but I don't think this is the issue. When I look at the image files in a hex editor it appears that they were appended correctly. Another cause might be reading the wrong sector from the floppy disk. I'm trying to get the kernel to be in, and be read from, the sector right after the bootloader.
To build and run this code I'm doing the following in Windows Vista x64:
nasm bootloader_2.asm -f bin -o bootloader_2.bin
nasm kernel_2.asm -f bin -o kernel.bin
partcopy bootloader_2.bin bootloader_2.img 0d 511d
partcopy kernel_2.bin kernel_2.img 0d 511d
copy bootloader.img /b + kernel.img POS_2.img
I'm then using Oracle VM Virtual Box to mount the POS_2.img as a floppy drive, and run it on a guest system.
The results are
Patrick's Bootloader Started. Floppy has been reset. Reading Kernel Sector Kernel Sector Loaded
. And the kernel never starts.
Here's my code
bits 16
org 0x7C00
boot: jmp loader
; OEM Parameter block / BIOS Parameter block (wtf is this for?)
times 0Bh-$+boot DB 0
bpbBytesPerSector: DW 512
bpbSectorsPerCluster: DB 1
bpbReservedSectors: DW 1
bpbNumberOfFATs: DB 2
bpbRootEntries: DW 224
bpbTotalSectors: DW 2880
bpbMedia: DB 0xF0
bpbSectorsPerFAT: DW 9
bpbSectorsPerTrack: DW 18
bpbHeadsPerCylinder: DW 2
bpbHiddenSectors: DD 0
bpbTotalSectorsBig: DD 0
bsDriveNumber: DB 0
bsUnused: DB 0
bsExtBootSignature: DB 0x29
bsSerialNumber: DD 0xa0a1a2a3
bsVolumeLabel: DB "MOS FLOPPY"
bsFileSystem: DB "FAT12"
; END PARAMETER BLOCK
; ----- Variables -----
started db "Patrick's Bootloader Started...", 0x0D, 0x0A, 0
floppy_reset_done db "Floppy has been reset.", 0x0D, 0x0A, 0
loading_msg db "Reading Kernel Sector", 0x0D, 0x0A, 0
loading_sucess db "Kernel Sector Loaded", 0x0D, 0x0A, 0
done db "Bootloader Done.", 0x0D, 0x0A, 0
; ----- End Variables -----
; ----- Calls -----
reset_floppy:
mov ah, 0
mov dl, 0
int 0x13
jc reset_floppy
mov si, floppy_reset_done
call print_string
ret
read_kernel:
mov si, loading_msg
call print_string
mov si, 0x0
mov ax, 0x1000 ; setting up the address to read into
mov es, ax ; moving the value in to es
xor bx, bx ; clearing bx
mov ah, 0x02 ; floppy function
mov al, 1 ; read 1 sector
mov ch, 1 ; track
mov cl, 2 ; sector to read
mov dh, 0 ; head number
mov dl, 0 ; drive number
int 0x13 ; BIOS Interrupt Call
jc read_kernel
mov si, loading_sucess
call print_string
ret
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
; input is ax, cx is destroyed
print_hex:
mov cx, 4
.next_digit:
push cx
mov cl, 4
rol ax, cl
push ax
and al, 0x0F
add al, '0'
cmp al, '9'
jle .not_a_leter
add al, 'A'-'9'-1
.not_a_leter:
mov ah, 0x0E
int 0x10
pop ax
pop cx
loop .next_digit
ret
; ----- End of Calls -----
; ===== Bootloader Main =====
loader:
mov si, started
call print_string
call reset_floppy
call read_kernel
jmp 0x1000:0x0
mov si, done ; never reached. Intentional for debugging
call print_string ; these lines failure to produce a result tell us that the jmp was attempted
; ===== End of Bootloader Main =====
times 510-($-$$) db 0
dw 0xAA55
kernel:
jmp k_main
welcome_msg db "Welcome to Patrick's Operating System!", 0x0D, 0x0A, 0
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
k_main:
mov si, welcome_msg
call print_string
.k_main_loop:
jmp .k_main_loop
cli
hlt
times 512-($-$$) db 0
At the risk of pointing out the obvious, your script doesn't copy the right file. In some places you refer to kernel.bin
and in others kernel_2.bin
. Try this instead:
nasm bootloader_2.asm -f bin -o bootloader_2.bin
nasm kernel_2.asm -f bin -o kernel_2.bin
partcopy bootloader_2.bin bootloader_2.img 0d 511d
partcopy kernel_2.bin kernel_2.img 0d 511d
copy bootloader.img /b + kernel_2.img POS_2.img
You may also find it easier for such things to use dd
instead. It's available as part of Cygwin and has the advantage that it can write to either files or physical devices such as a real floppy disk.
Also, you should consider using a Makefile
instead of script. It will help as you expand your project further.
That said, there are three basic problems with the code. First, the next sector to be loaded is on cylinder 0, not cylinder 1. Second, the code can't simply safely ret
to the kernel - you must jump there explicitly. That portion of the code would then look like this:
read_kernel:
mov si, loading_msg
call print_string
mov si, 0x0
mov ax, 0x1000 ; setting up the address to read into
mov es, ax ; moving the value in to es
xor bx, bx ; clearing bx
mov ah, 0x02 ; floppy function
mov al, 1 ; read 1 sector
mov ch, 0 ; cylinder
mov cl, 2 ; sector to read
mov dh, 0 ; head number
mov dl, 0 ; drive number
int 0x13 ; BIOS Interrupt Call
jc read_kernel
push es ; either push the address and retf or use far jmp
push bx ;
mov si, loading_sucess
call print_string
; jmp 0x1000:0 ; alternative to push/retf is simple long jump
retf
The third problem is the kernel. You haven't changed the DS
register to point to the new offset so even if you get to that code, it won't print the string you intend. Change that code to look like this instead:
k_main:
push cs ; save the cs reg
pop ds ; use as ds also
mov si, welcome_msg
call print_string
.k_main_loop:
jmp .k_main_loop
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With