Here's my boot loader code, and here's a document showing pictures of the registers (in case that's of importance in what I'm doing wrong), what was in memory at location 0x10000 (where I told the boot loader to load the kernel), the source assembly of my kernel, and the screen output when Qemu is run.
kernelStub.bin has EB 1B (the right jump command) at the very beginning. hda.img has EB 1B right after 55 AA at the beginning of the second sector. The carry flag is clear in my load_mem subroutine indicating that it believes the load was good. All the bytes are right in memory except the first two are always 63 61.
Why would the load_mem routine always load the first two bytes of sector 2 into address 0x10000 wrong and then get the rest right?
The bootloader code:
Update: Changed jmp SYSADDR:0000
to jmp 0x1000:0x0000
per Matthew Slattery's correction.
;Very minimal boot loader
BITS 16 ;Tell assembler to use 16-bit mode
jmp start ;Jump over defines
SYSADDR dw 0x1000 ;Load system at 0x10000
DRIVENUM db 0x80 ;Variable for drive number
HEADNUM db 0
CYLNUM db 0 ;Low bits of cylinder number
SECTNUM db 2 ;Bits 6 and 7 high bits of cylinder number (0),
;Bits 0-5 starting sector number (2)
NUMKERNELSECTS db 0x01 ;Will Probably Change! Number of sectors
;to read from disk
load_msg db 'Loading OS', 0
msg_2 db 'carry flag not clear', 0
load_worked db 'Load worked', 0
start:
mov ax, 0x07C0 ;Set data segment to where BIOS loaded boot loader
mov ds, ax
mov si, load_msg ;Simple text string to indicate loading
call show_message
call load_mem ;Subroutine to load bytes from disk to location
;pointed to by es
jmp 0x1000:0x0000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Subroutines
;;;Show Message;;;
show_message:
mov ah, 0x0E ;int 0x10 print character to screen function
.repeat:
lodsb ;Get char pointed to by si, puts in al
cmp al, 0 ;see if char is 0 (null)
je .done ;null signifies done
int 0x10 ;If not null, print to screen
jmp .repeat ;Get next char
.done:
ret
;;;Load Memory;;;
load_mem:
xor ah, ah ;ah=0, reset drive
int 0x13 ;Call drive reset
mov ax, [SYSADDR]
mov es, ax ;Destination- es:bx
mov bx, 0
mov dl, [DRIVENUM]
mov dh, [HEADNUM]
mov al, [NUMKERNELSECTS]
mov ch, [CYLNUM]
mov cl, [SECTNUM]
mov ah, 0x02 ;ah=2, read drive
int 0x13 ;Call read interrupt
jnc exit ;If carry flag is clear, exit
exit:
ret
times 510 - ($-$$) db 0;Pad sector with 0
dw 0xAA55 ;Boot signature
SYSADDR dw 0x1000 ;Load system at 0x10000 ... jmp SYSADDR:0000
doesn't do what you want it to. SYSADDR
is the location of the value you want (0x0002
here), not the value itself.
I reckon that executing whatever happens to be at 0x0002:0000
(which won't even be real code; it's part of the interrupt vector table) is causing your data to be scribbled over.
(By the way, there is at least one more problem as well: the kernel stub code in your second linked doc isn't resetting ds
.)
EDIT:
I see that you've got this working now, but for completeness, I have a
full explanation for the mysterious values in those two bytes, as a result
of the jmp SYSADDR:0000
bug:
0002:0000
, i.e. address 0x20
, is the vector for INT 08h
.
The traditional entry point address for the INT 08h
handler is f000:fea5
(BIOSes
tend to maintain the tradition, for compatibility with code that uses the
entry points directly). So the bytes at this address will almost certainly
be a5 fe 00 f0 ...
.
If you execute the first of those bytes, a5
, as code, it's a movsw
instruction, which copies two bytes from ds:si
to es:di
.
ds
is still your boot loader's data segmentsi
points to the beginning of msg_2
(where your show_message
routine
left it after printing load_msg
)es
is still 0x1000
di
presumably contains 0So the very first thing that happens after the jmp 0x0002:0000
is that the
c
and a
from the start of msg_2
get copied to the first two bytes of
the sector which was loaded.