* Fix issue where multiple clusters would be loaded 0x800 apart rather than
contiguously.
* Get rid of some global variables, saving space.
* si points to the start LBA address rather than the partition table entry,
saving a bit of space as we no longer need to use offsets.
* In read_cluster_chain, drop push/popad as they in fact do not
save the upper 16 bytes of the registers. Instead, just push
and pops the registers that are used.
; FAT32 data structures
; Because they have to be defined before use
; FAT32 data structures
; Because they have to be defined before use
-struc part_entry
- .attrib: resb 1
- .chs_start: resb 3
- .type: resb 1
- .chs_end: resb 3
- .lba_start: resd 1
- .lba_end: resd 1
-endstruc
-
struc dir_entry
.name: resb 11
.attr: resb 1
struc dir_entry
.name: resb 11
.attr: resb 1
-; BOOT_DRIVE, part_table_entry and bp must be set up by the code using this driver.
-; first_data_sec is to be set up by calling get_1st_data_sec.
+; BOOT_DRIVE, si and bp must be set up by the code using this driver.
+; (Specifically, si must be a pointer to the partition's partition
+; table entry and bp must be a pointer to the BPB.)
+; Before reading any cluster chains, callers must call get_1st_data_sec
+; and must be careful not to trash ecx after calling it.
; eax - start cluster number
; es:di - where to load the cluster chain
; NOTE: Cluster chain might be the root directory or a file.
read_cluster_chain:
; eax - start cluster number
; es:di - where to load the cluster chain
; NOTE: Cluster chain might be the root directory or a file.
read_cluster_chain:
+ push eax
+ push ebx
+ push ecx
+ push edx
+ push es
+ push di
.loop:
; get the first sector of the cluster to read
push eax
.loop:
; get the first sector of the cluster to read
push eax
xor ebx, ebx
mov bl, BPB_SecPerClus
mul ebx
xor ebx, ebx
mov bl, BPB_SecPerClus
mul ebx
- add eax, [first_data_sec]
mov ebx, eax
pop eax
; read the cluster
mov ebx, eax
pop eax
; read the cluster
movzx cx, BPB_SecPerClus
call read_sectors
movzx cx, BPB_SecPerClus
call read_sectors
; increment the load offset (and segment if needed)
push eax
; increment the load offset (and segment if needed)
push eax
.get_next_cluster:
pop eax
; get FAT sector number and offset containing the next cluster number
.get_next_cluster:
pop eax
; get FAT sector number and offset containing the next cluster number
- xor edx, edx
- mov ebx, 4
- mul dword ebx
+ xor ebx, ebx
+ mov bl, 4 ; put 4 in EBX, doing this instead of "mov ebx, 4" saves us an entire byte
+ mul ebx
movzx ebx, word BPB_BytsPerSec
movzx ebx, word BPB_BytsPerSec
movzx ebx, word BPB_RsvdSecCnt
add eax, ebx
movzx ebx, word BPB_RsvdSecCnt
add eax, ebx
- push bp
- mov bp, [part_table_entry]
- add eax, [bp+part_entry.lba_start]
- pop bp
mov ebx, edx
; reminder for myself: EAX is FAT sector number, (E)BX is offset into FAT sector
; load the FAT sector we're looking for
mov ebx, edx
; reminder for myself: EAX is FAT sector number, (E)BX is offset into FAT sector
; load the FAT sector we're looking for
push ebx ; offset
push es ; we want to read at 0:1000, not STAGE3_SEGMENT:1000
push eax ; desired LBA
push ebx ; offset
push es ; we want to read at 0:1000, not STAGE3_SEGMENT:1000
push eax ; desired LBA
; find the cluster we're looking for
pop es ; restore STAGE3_SEGMENT
pop ebx ; pop FAT offset back into EBX for cmp
; find the cluster we're looking for
pop es ; restore STAGE3_SEGMENT
pop ebx ; pop FAT offset back into EBX for cmp
- cmp dword [di+bx], 0xffffff7
- jge .done ; if cluster number is greater than or equal to the defective cluster value, we're done
- ; TODO: should this perhaps be changed so that equal makes it error out?
- ; otherwise, load the next sector number in eax and read again
+ pop di
+ pop cx
+ cmp eax, 0xffffff7
+ jl .loop ; if cluster number is lower than the defective cluster value, we're not done
+ ; TODO: perhaps check is it equal defective and error out in that case?
.done:
; cleanup and return
.done:
; cleanup and return
+ pop di
+ pop es
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
ret
; es:di - where to load the sector(s)
ret
; es:di - where to load the sector(s)
+; ds:si - pointer to partition table entry
+; RETURN: ecx - first data sector
get_1st_data_sec:
push eax
push ebx
get_1st_data_sec:
push eax
push ebx
movzx ax, BPB_NumFats
mul dword BPB_FatSz32 ; space occupied by all FATs
movzx ebx, word BPB_RsvdSecCnt
add eax, ebx ; space occupied by the reserved area
movzx ax, BPB_NumFats
mul dword BPB_FatSz32 ; space occupied by all FATs
movzx ebx, word BPB_RsvdSecCnt
add eax, ebx ; space occupied by the reserved area
- mov bx, [part_table_entry]
- add eax, [bx+part_entry.lba_start] ; space before the partition
- mov [first_data_sec], eax
+ add eax, [si] ; space before the partition
+ mov ecx, eax
read_error: db "Read error", 0
BOOT_DRIVE: db 0
read_error: db "Read error", 0
BOOT_DRIVE: db 0
-part_table_entry: dw 0
-first_data_sec: dd 0
+ cli ; we really don't want to be interrupted during relocation
; set up segment registers
xor ax, ax
mov ds, ax
; set up segment registers
xor ax, ax
mov ds, ax
call check_int13_ext
mov byte [BOOT_DRIVE], dl
; look for active partition
call check_int13_ext
mov byte [BOOT_DRIVE], dl
; look for active partition
mov cx, 4
.check_part_loop:
mov cx, 4
.check_part_loop:
test al, 0x80
jnz .part_found
test al, 0x80
jnz .part_found
loop .check_part_loop
.error:
mov si, no_os
call print
loop .check_part_loop
.error:
mov si, no_os
call print
hlt
jmp short $-1
.part_found:
; look for any other active partitions, if they exist the partition table is invalid
cmp cx, 0
je .load_vbr
hlt
jmp short $-1
.part_found:
; look for any other active partitions, if they exist the partition table is invalid
cmp cx, 0
je .load_vbr
- add bx, 16
- mov al, [bx]
+ add si, 16
+ mov al, [si]
test al, 0x80
jnz .invalid_mbr
loop .look_other_loop
.load_vbr:
; load active partition's VBR
test al, 0x80
jnz .invalid_mbr
loop .look_other_loop
.load_vbr:
; load active partition's VBR
- mov edx, [bx+0x8]
- push bx ; save pointer to partition table entry
+ add si, 8
+ mov edx, [si]
xor bx, bx
call read_sector
; check is the VBR bootable (ends with 0x55 0xaa), if not halt
cmp word [0x7dfe], 0xaa55
jne .not_bootable
mov dl, [BOOT_DRIVE]
xor bx, bx
call read_sector
; check is the VBR bootable (ends with 0x55 0xaa), if not halt
cmp word [0x7dfe], 0xaa55
jne .not_bootable
mov dl, [BOOT_DRIVE]
- pop si ; hand off partition table entry to VBR
- jmp 0x7c00
.not_bootable:
mov si, no_os
call print
.not_bootable:
mov si, no_os
call print
hlt
jmp short $-1
.invalid_mbr:
mov si, invalid_mbr
call print
hlt
jmp short $-1
.invalid_mbr:
mov si, invalid_mbr
call print
hlt
jmp short $-1
check_int13_ext:
hlt
jmp short $-1
check_int13_ext:
.no_ext:
mov si, no_int13_ext
call print
.no_ext:
mov si, no_int13_ext
call print
.error:
mov si, read_fail
call print
.error:
mov si, read_fail
call print
; we expect the boot drive to be in DL and our partition table entry in DS:SI
mov [BOOT_DRIVE], dl
; we expect the boot drive to be in DL and our partition table entry in DS:SI
mov [BOOT_DRIVE], dl
- mov [part_table_entry], si
; calculate the 1st sector of the data area
call get_1st_data_sec
; calculate the 1st sector of the data area
call get_1st_data_sec
mov eax, BPB_RootClus
call read_cluster_chain
mov eax, BPB_RootClus
call read_cluster_chain
.find_stage_3:
mov si, STAGE3_NAME
cmp byte [es:di], 0 ; we have no more entries to look at
.find_stage_3:
mov si, STAGE3_NAME
cmp byte [es:di], 0 ; we have no more entries to look at
add bx, 0x1000
mov es, bx
.stage3_found:
add bx, 0x1000
mov es, bx
.stage3_found:
- sub di, 0xb ; "repe cmpsb" incremented this
+ pop si
+ pop cx
+ add di, 9 ; knowing that cmpsb incremented this by 11, we can also increment it by 9
+ ; right here so we can use 1 less offset below
; stage 3 has been found and ES:DI points to its directory entry
; stage 3 has been found and ES:DI points to its directory entry
- mov ax, [es:di+dir_entry.firstclushi] ; load high half of the 1st cluster
+ mov ax, [es:di] ; load high half of the 1st cluster
- mov ax, [es:di+dir_entry.firstcluslo] ; load low half of the 1st cluster
+ mov ax, [es:di+(dir_entry.firstcluslo-dir_entry.firstclushi)] ; load low half of the 1st cluster
mov bx, STAGE3_SEGMENT
mov es, bx
mov di, STAGE3_OFFSET
mov bx, STAGE3_SEGMENT
mov es, bx
mov di, STAGE3_OFFSET
mov si, stage3_missing
call print
.halt:
mov si, stage3_missing
call print
.halt: