I'm trying to develop a basic bootloader, but I ran into a problem when I tried to create a function for reading additional sectors from a hard drive. I'm developing it on Kali Linux in NASM and using QEMU as my emulator. This is my main bootloader file:
[org 0x7c00]
mov bp, 0x8000
mov sp, bp
call read_disk
mov si, my_string
call print ;prints a string, si points to the string to be printed
jmp $
read_disk
mov ah, 0x02 ;read from disk
mov al, 0x01 ;read one sector
mov ch, 0x00 ;read from cylinder 0
mov dh, 0x00 ;read from head 0
mov cl, 0x02 ;read the second sector
mov bx, 0
mov es, bx
mov bx, 0x7c00+512
int 0x13
jc disk_error ;BIOS sets the carry flag if disk read was unsuccessful
ret
disk_error:
mov si, error_msg
call print
jmp $
;
;Functions
;
%include "functions/print.asm"
%include "functions/print_hex.asm"
%include "functions/print_nl.asm"
%include "functions/calc_len.asm"
%include "functions/find_string.asm"
;
;Data
;
error_msg:
db 'Error reading disk', 0
times 510-($-$$) db 0 ;pad out the rest of the bootloader with zeros to increase the size to 512 bytes
dw 0xaa55 ;Magic bytes so BIOS recognizes the hard drive as bootable
;
;SECOND SECTOR
;
my_string:
db 'Disk read successful', 0
times 512 db 0 ;need to pad out the rest of the sector with zeros since QEMU requires it
As you can see, my_string lies after 512 bytes, in the second sector of the emulated hard drive. But when I compile and run the bootloader, it doesn't output anything. In the code I provided above, I'm printing my_string after the read_disk function is over. But strangely enough, If I move the two lines that print my_string inside the function, it works.
This is the code that works:
[org 0x7c00]
mov bp, 0x8000
mov sp, bp
call read_disk
jmp $
read_disk
mov ah, 0x02 ;read from disk
mov al, 0x01 ;read one sector
mov ch, 0x00 ;read from cylinder 0
mov dh, 0x00 ;read from head 0
mov cl, 0x02 ;read the second sector
mov bx, 0
mov es, bx
mov bx, 0x7c00+512
int 0x13
jc disk_error ;BIOS sets the carry flag if disk read was unsuccessful
mov si, my_string
call print ;prints a string, si points to the string to be printed
ret
disk_error:
mov si, error_msg
call print
jmp $
;
;Functions
;
%include "functions/print.asm"
%include "functions/print_hex.asm"
%include "functions/print_nl.asm"
%include "functions/calc_len.asm"
%include "functions/find_string.asm"
;
;Data
;
error_msg:
db 'Error reading disk', 0
times 510-($-$$) db 0 ;pad out the rest of the bootloader with zeros to increase the size to 512 bytes
dw 0xaa55 ;Magic bytes so BIOS recognizes the hard drive as bootable
;
;SECOND SECTOR
;
my_string:
db 'Disk read successful', 0
times 512 db 0 ;need to pad out the rest of the sector with zeros since QEMU requires it
I'd be very grateful if someone can explain this strange oddity to me.
You should set SS:SP and not just as SP before reading into memory. SS may be zero or it may not be. If SS happens to be 0x0000 then your stack is at 0x0000:0x8000 and it will grow down from there.
Your code reads the second 512 byte sector on the disk to memory at 0x0000:0x7e00 which includes all the bytes up to and including the return address of the disk_read function that was placed on the stack at 0x0000:0x7ffe to 0x0000:0x7fff.
Since you clobbered the stack, the int 0x13 will likely never return because the internal data, return address and the flags have been corrupted. Corrupting the stack like this will have unpredictable results. Consider putting the stack at 0x0000:0x7c00 below the bootloader so as to not interfere with the data and code you load after the bootloader.
Note: You should set all the segment registers you need to values you expect. You shouldn't rely on any of the segment registers containing a specific value. The BIOS doesn't guarantee their values, although in most emulators they will be 0x0000.
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