I am writing a stage 1 bootloader in assembly with which I am attempting to load the FAT12 filesystem into memory so that I can load my stage 2 bootloader. I have managed to load the FATs into memory, however I am struggling to load the root directory into memory.
I am currently using this for reference and have produced the following:
.load_root:
;es is 0x7c0
xor dx, dx ; blank dx for division
mov si, fat_loaded ; inform user that FAT is loaded
call print
mov al, [FATcount] ; calculate how many sectors into the disk must be loaded
mul word [SectorsPerFAT]
add al, [ReservedSectors]
div byte [SectorsPerTrack]
mov ch, ah ; Store quotient in ch for cylinder number
mov cl, al ; Store remainder in cl for sector number
xor dx, dx
xor ax, ax
mov al, ch ; get back to "absolute" sector number
mul byte [SectorsPerTrack]
add al, cl
mul word [BytesPerSector]
mov bx,ax ; Memory offset to load to data into memory after BOTH FATs (should be 0x2600, physical address should be 0xA200)
xor dx, dx ; blank dx for division
mov ax, 32
mul word [MaxDirEntries]
div word [BytesPerSector] ; number of sectors root directory takes up (should be 14)
xor dh, dh ; head 0
mov dl, [boot_device] ; boot device
mov ah, 0x02 ; select read mode
int 13h
cmp ah, 0
je .load_OS
mov si, error_text
call print
jmp $
However, if I inspect the memory at 0xA200
with gdb, I just see 0s. My root directory does contain a file -- I have put a file called OS.BIN in the root directory to test with.
Using info registers
in gdb after the read operation gives the following output:
eax 0xe 14
ecx 0x101 257
edx 0x0 0
ebx 0x2600 9728
esp 0x76d0 0x76d0
ebp 0x0 0x0
esi 0x16d 365
edi 0x0 0
eip 0x7cdd 0x7cdd
eflags 0x246 [ PF ZF IF ]
cs 0x0 0
ss 0x53 83
ds 0x7c0 1984
es 0x7c0 1984
fs 0x0 0
gs 0x0 0
The status of the operation is 0, the number of sectors read is 14, and es:bx
points to 0xA200, but x/32b 0xa200
shows 32 0s, when I would expecting to see the data for OS.BIN.
EDIT
I did info registers
before the interrupt and the output is the following:
eax 0x20e 526
ecx 0x101 257
edx 0x0 0
ebx 0x2600 9728
esp 0x76d0 0x76d0
ebp 0x0 0x0
esi 0x161 353
edi 0x0 0
eip 0x7cc8 0x7cc8
eflags 0x246 [ PF ZF IF ]
cs 0x0 0
ss 0x53 83
ds 0x7c0 1984
es 0x7c0 1984
fs 0x0 0
gs 0x0 0
Which is the same as after, except the function request number has been replaced with the status code.
Where am I going wrong? Am I reading from the wrong CHS address? Or some other simple mistake? And how can I correct this?
I am using fat_imgen
to make my disk image. Command for creating the disk image is fat_imgen -c -f floppy.flp -F -s bootloader.bin
and command for adding OS.BIN
to the image is fat_imgen -m -f floppy.flp -i OS.BIN
I have a BIOS Parameter Block (BPB) that represents a 1.44MB floppy using FAT12:
jmp short loader
times 9 db 0
BytesPerSector: dw 512
SectorsPerCluster: db 1
ReservedSectors: dw 1
FATcount: db 2
MaxDirEntries: dw 224
TotalSectors: dw 2880
db 0
SectorsPerFAT: dw 9
SectorsPerTrack: dw 18
NumberOfHeads: dw 2
dd 0
dd 0
dw 0
BootSignature: db 0x29
VolumeID: dd 77
VolumeLabel: db "Bum'dOS ",0
FSType: db "FAT12 "
I have another function that appears to work that loads the FAT12 table to memory address 0x7c0:0x0200 (physical address 0x07e00):
;;;Start loading File Allocation Table (FAT)
.load_fat:
mov ax, 0x07c0 ; address from start of programs
mov es, ax
mov ah, 0x02 ; set to read
mov al, [SectorsPerFAT] ; how many sectors to load
xor ch, ch ; cylinder 0
mov cl, [ReservedSectors] ; Load FAT1
add cl, byte 1
xor dh, dh ; head 0
mov bx, 0x0200 ; read data to 512B after start of code
int 13h
cmp ah, 0
je .load_root
mov si, error_text
call print
hlt
I ended up scrapping loading the root directory after loading the FATs. In the end, I modified my .load_fat routine to load both FATs and the root directory at the same time (essentially reading 32 sectors after the boot sector, but in a way that still allows me to easily modify the disk geometry).
The code for this is below:
.load_fat:
mov ax, 0x07c0 ; address from start of programs
mov es, ax
mov al, [SectorsPerFAT] ; how many sectors to load
mul byte [FATcount] ; load both FATs
mov dx, ax
push dx
xor dx, dx ; blank dx for division
mov ax, 32
mul word [MaxDirEntries]
div word [BytesPerSector] ; number of sectors for root directory
pop dx
add ax, dx ; add root directory length and FATs length -- load all three at once
xor dh,dh
mov dl, [boot_device]
xor ch, ch ; cylinder 0
mov cl, [ReservedSectors] ; Load from after boot sector
add cl, byte 1
xor dh, dh ; head 0
mov bx, 0x0200 ; read data to 512B after start of code
mov ah, 0x02 ; set to read
int 13h
cmp ah, 0
je .load_root
mov si, error_text
call print
hlt
Though not the way I intended to solve the problem, it does the job and I can move on from this to continue development.
EDIT
I think I worked out where the old code was going wrong, anyway. I was incrementing the cylinder after sector 18, when I should have been incrementing the head. It's CHS, not HCS, for a reason!