I've run into a bit of snag in a boot loader that I am writing. I have spent a considerable amount of time debugging and performing tests and eventually just rewriting the entire boot sector from scratch, but to no luck. So, here is what I am trying to have my boot sector do (very simple):
1. Load LBA sector 1 (the second sector) from a floppy disk into the memory address 0x0:0x7E00 (ES : BX)
2. Jump to 0x0:0x7E00.
Now, when I boot from a floppy disk, the second sector does not load (at all) into my memory location. In fact, it does not load at all, but there is no error code from the BIOS interrupt, nor is the carry flag set. I have tried both a virtual floppy (via qemu's -fda option) and a physical one (via my real computer), and both fail. However, when I change the floppy to a hard drive (via qemu's -hda option), it works 100%. When I say it works, I mean that it executes the code in sector 1 (the second sector) and works just like I expect.
With that said, below are the important parts of my boot sector. I have put a wrapper around the loading code. Hopefully my code is easy to read and understand.
The "LoadSector" procedure takes 2 arguments.
AX = the sector number (LBA, i.e. starting at 0) to load
ES:BX = the buffer to load the sector into
Code: Select all
[BITS 16]
[ORG 0x7C00]
%define GlobalStackSegment 0x7000
%define GlobalStackTop 0xFFFF
%define GlobalStage2Start 0x7E00
%define BootDevice 0x500 ; 0x00 = floppy 1, 0x01 = floppy 2, 0x80 = hdd 1, 0x81 = hdd 2
%define BootDeviceType 0x501 ; 0 = floppy, 1 = hard drive
%define SectorCount 0x502 ; the number of sectors on the boot device, starting at 1
%define HeadCount 0x503 ; the number of heads on the boot device
%define CylinderCount 0x504 ; the number of cylinders on the boot device
%define DriveCount 0x506 ; the number of drives on the system
; jump to our entry point (and set CS to 0)
jmp 0x0:main
; entry point
main:
; setup our segment registers
mov AX, 0
mov DS, AX
mov ES, AX
mov GS, AX
mov FS, AX
; setup our stack
mov AX, GlobalStackSegment
mov SS, AX
mov SP, GlobalStackTop
; copy the ID of the device that loaded this boot sector,
; and then determine whether it is a floppy or hard drive.
mov [BootDevice], DL
; now we need to grab boot disk information
call GetBootDiskInfo
; =========================================
; PROBLEM AREA
; =========================================
; load sector #2
mov AX, 1
mov BX, 0x7E00
call LoadSector
jmp 0x7E00
; =========================================
cli
hlt
;; GetBootDiskInfo()
;; Initializes boot disk information such as number of sectors, cylinders, heads, etc.
;;
;; i.e.
;;
;; call GetBootDiskInfo
;;
GetBootDiskInfo:
enter 0, 0
pusha
mov AH, 0x08 ; read drive parameters function
mov DL, [BootDevice]
int 0x13 ; BIOS interrupt
jnc .setBootDiskInfo
; if clear flag is set, an error occurred
push InfoLoadErr
call PrintString
call HandleFatalError
; no error occurred, we can set our boot data
.setBootDiskInfo:
mov [DriveCount], DL
mov [HeadCount], DH
mov [SectorCount], CL
and byte [SectorCount], 00111111b
and CX, 1111111111000000b
mov [CylinderCount], CX
popa
leave
ret
;; LoadSector()
;; Loads the given 0-indexed LBA sector into buffer ES:BX.
;; AX = sector to load
;; ES:BX = buffer to load to
LoadSector:
enter 0, 0
pusha
; first we need to convert the given LBA sector into a CHS tuple
; C = LBA / (sectors per track * heads per cylinder)
; H = (LBA / sectors per track) % heads per cylinder
; S = (LBA % sectors per track) + 1
div byte [SectorCount] ; AX / SectorCount, AL = quotient, AH = remainder
mov CL, AH ; S = (LBA % sectors per track) + 1
add CL, 1 ; ^^^
and CL, 00111111b ; S is only 6 bits.
mov AH, 0 ; AX = AL
div byte [HeadCount] ; AX / HeadCount, AL = quotient, AH = remainder
mov DH, AH ; H = (LBA / sectors per track) % heads per cylinder
mov AX, 0
mov AL, byte [SectorCount]
mul byte [HeadCount] ; AX = AL * HeadCount
mov DI, AX ; DI = AX
mov DX, 0
pop AX ; AX = LBA
div DI ; LBA / (sectors per track * heads per cylinder), AX = quotient, DX = remainder
; C = LBA / (sectors per track * heads per cylinder)
and AX, 0000001111111111b
shl AX, 6
mov CH, 0
or CX, AX
; CL = sector
; CH = cylinder
; DH = head
mov DL, [BootDevice]
mov AL, 1 ; number of sectors to read
mov AH, 0x02 ; read sector function
; ES:BX = buffer
; now we can actually read the sector
mov SI, 0
.tryLoadSector:
mov AH, 0x00 ;reset drive
int 0x13
mov AH, 0x02 ; load sector function
int 0x13
jnc .finishLoadSector
inc SI
cmp SI, 5
je .badFinishLoadSector
jmp .tryLoadSector
; something went wrong
.badFinishLoadSector:
push SectorLoadErr
call PrintString
mov AL, AH
add AL, 48
mov AH, 0x0E
mov BH, 0x00
mov BL, 0x07
int 0x10 ; <--- this is just for debugging, prints '0', meaning the error code from read sectors is 0 (OK).
call HandleFatalError
; everything is OK!
.finishLoadSector:
popa
leave
ret
;; HandleFatalError()
;; This procedure is called when an unrecoverable error occurs.
;; Control is NOT handed back to the caller.
;;
;; i.e.
;;
;; push errorString
;; call PrintString
;; call HandleFatalError
HandleFatalError:
enter 0, 0
cli
hlt
leave ; <-- this is never called, but put it for consistency
ret
; ----------------------- SIGNATURE --------------------
times 510-($-$$) db 0
dw 0xAA55
push HelloWorld
call PrintString
cli
hlt
times 1024-($-$$) db 0