简体   繁体   中英

Boot loader always messes up the first two bytes of Int 13h call to load kernel

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 , ie 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 segment
  • si 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 0

So 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.

I found the critical detail regarding my question of getting the first jump of my kernel here . I didn't know how necessary it was (with some BIOSes, at least) to follow short jmp with nop .

Once I put the nop s in and set up my ds register, it finally worked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM