Loading from disk works on qemu but not on hardware
Posted: Thu Apr 09, 2020 11:29 pm
I've got a simple bootloader which uses int 13h, ah=2 to load the 2nd sector of the boot drive into memory right after where the boot sector is loaded- at 0x7e00. Then I check that the disk read didn't report an error by checking the carry flag, then print out some of the loaded data, and finally show the status after the disk read. On qemu and bochs, this gives the expected result:
Where No Error is based on the carry flag value, A1B2C3 are the first three bytes of the sector that was loaded, and 0100 are the number of sectors loaded (just sector 2) and the status code, both of which were stored in ax after the interrupt and pushed to the stack for safekeeping.
On real hardware however, (Specifically on a 32-bit Intel from the Celeron family and a 64-bit AMD FX-8320) it outputs:
Indicating by all accounts that the code ran smoothly, but for some reason failing to load and print the actual values. I know that these values are being loading onto my USB because I copied the compiled code on and off again and the copy works fine. I also made sure to properly setup the stack and segment registers, Checked the compiled code to ensure that the read and write locations were in fact identical, and tried it with both the x86_64 and i386 emulators. I also tried removing the org statement and setting all the segment registers to 0x7c0 instead. Not sure why that would help, but I was running out of ideas. Here's the code:
And the debug functions. They both preserve all registers and appear to work just fine.
Code: Select all
No Error.
A1B2C3
0100
On real hardware however, (Specifically on a 32-bit Intel from the Celeron family and a 64-bit AMD FX-8320) it outputs:
Code: Select all
No Error.
000000
0100
Code: Select all
[org 0x7c00]
[bits 16]
; Init segment registers
mov bx, 0x0
mov ds, bx
mov ss, bx
mov es, bx
mov fs, bx
mov gs, bx
; Setup Stack
mov bp, 0x9000
mov sp, bp
; Save this number, it's our boot drive by default
mov [BOOT_DRIVE], dl
; After the boot sector, some data is included which must be loaded from the disk manually.
mov ah, 0x02 ; Read sectors from disk
mov ch, 0 ; Cylinder 0
mov dh, 0 ; Head 0
mov cl, 2 ; Sector 2
mov al, 1 ; Read 1 sector
; Data will be read to es:bx
mov bx, data
int 0x13
push ax ; Save the status
jc disk_error
mov bx, nermsg
call prn_str
mov bx, data
mov cx, 3
call prn_hex
jmp post_disk_error_check
disk_error:
mov bx, errmsg
call prn_str
post_disk_error_check:
; Move the cursor to the next line
mov ah, 0x0e
mov al, 10
int 0x10
mov al, 13
int 0x10
; Prints out two bytes: al (Number of sectors read), and ah (status). These values were saved to the stack immediately after the interrupt.
mov bx, sp
mov cx, 2
call prn_hex
pop ax
jmp $
%include "lib/prn_str.asm"
%include "lib/prn_hex.asm"
errmsg: db "General disk read error.", 10, 13, 0
nermsg: db "No error.", 10, 13, 0
BOOT_DRIVE: db 0
times 510-($-$$) db 0
dw 0xaa55
data:
db 0xA1, 0xB2, 0xC3
times 1024-($-$$) db 0
Code: Select all
; Prints a null terminated ASCII string using the int 10h teletype routine.
; Takes: BX as pointer to start of string
prn_str:
push ax
push bx
mov ah, 0x0e
prn_str_loop:
; Get value at pointer
mov al, [bx]
; Check for null value
cmp al, 0
je prn_str_ret
int 0x10
inc bx
jmp prn_str_loop
prn_str_ret:
pop bx
pop ax
ret
Code: Select all
; Prints the 16-bit hexadecimal value pointed to using the int 10h teletype routine.
; Takes: BX as pointer to first value to print, CX as number of values to print
prn_hex:
push ax
push bx
push cx
mov ah, 0x0e
prn_hex_loop:
cmp cx, 0
je prn_hex_end ; If number of bytes remaining is zero, quit
mov al, [bx] ; Isolate the current significant byte, at bx
shr al, 0x04 ; Isolate the most significant 4 bits
cmp al, 0xA ; Test if this value is represented by a number (<10) or a letter (>=10)
jl prn_hex_high_is_number
; high is a letter
add al, 55
int 0x10
jmp prn_hex_high
prn_hex_high_is_number:
; high is a number
add al, 48
int 0x10
prn_hex_high:
mov al, [bx] ; Isolate the current significant byte, at bx
and al, 0x0F ; Isolate the least significant 4 bits
cmp al, 0xA ; Test if this value is represented by a number (<10) or a letter (>=10)
jl prn_hex_low_is_number
; low is a letter
add al, 55
int 0x10
jmp prn_hex_low
prn_hex_low_is_number:
; low is a number
add al, 48
int 0x10
prn_hex_low:
; Now that this byte is printed, get to next byte and decrement the number of bytes remaining counter (cx)
inc bx
dec cx
jmp prn_hex_loop
prn_hex_end:
pop cx
pop bx
pop ax
ret