previously missed bug in my FAT file reading code
Posted: Fri Aug 19, 2022 10:53 pm
I have moved into a new phase with my boot loader, having created a new project called Ordo, a simple C based OS kernel which uses the Verbum boot loader as a sub-module.
been running into a problem with the code I use to read files into memory. While this code works for reading the second stage boot loader, it hangs when trying to read the kernel file.
The working code is
The code which is failing uses the existing FAT and directory buffers generated by the code above.
The latter used the following string constants:
and the following memory offsets:
The second stage parameter block, which transfers critical data from the boot sector to the second stage, is defined as:
The disk reading code is spread across several source files, the most relevant ones being
and
been running into a problem with the code I use to read files into memory. While this code works for reading the second stage boot loader, it hangs when trying to read the kernel file.
The working code is
Code: Select all
mov cx, fat_buffer
mov [bp - stg2_parameters.drive], dx
mov [bp - stg2_parameters.fat_0], cx
mov [bp - stg2_parameters.directory_buffer], word dir_buffer
mov [bp - stg2_parameters.PnP_Entry_Seg], bx ; BX == old ES value
mov [bp - stg2_parameters.PnP_Entry_Off], di
mov [bp - stg2_parameters.boot_sig], word bootsig
mov [bp - stg2_parameters.bpb], word boot_bpb
;;; reset the disk drive
call near reset_disk
mov ax, Reserved_Sectors ; get location of the first FAT sector
mov bx, fat_buffer
call read_fat
mov ax, dir_sectors
mov bx, dir_buffer
call near read_root_directory
mov si, snd_stage_file
mov di, dir_buffer
mov cx, Root_Entries
mov bx, dir_entry_size
call near seek_directory_entry
cmp di, word 0
jz .no_file
call read_directory_details
mov di, fat_buffer
mov si, stage2_buffer
call near fat_to_file
.stg2_read_finished:
Code: Select all
load_kernel_code:
mov si, kernel_filename
mov di, word [bp - stg2_parameters.directory_buffer]
mov cx, Root_Entries
mov bx, dir_entry_size
call near seek_directory_entry
cmp di, word 0
jnz .read_directory
write no_kernel
jmp local_halt_loop
.read_directory:
call read_directory_details
write kernel_file_found
; reset the disk drive
mov dl, byte [bp - stg2_parameters.drive]
call near reset_disk
mov di, [bp - stg2_parameters.fat_0]
mov si, kcode_offset
push ax
push es
mov ax, kernel_base
mov es, ax
mov dl, byte [bp - stg2_parameters.drive]
call near fat_to_file
pop es
pop ax
write kernel_loaded
jmp find_kernel_code_block
The latter used the following string constants:
Code: Select all
kernel_filename db "KERNEL SYS", NULL
no_kernel db 'KERNEL.SYS not found.', NULL
kernel_file_found db 'KERNEL.SYS found...', NULL
kernel_loaded db 'loaded.', CR, LF, NULL
Code: Select all
kernel_base equ 0xffff
kdata_offset equ 0xfffc
struc KData
.mmap_cnt resd 1
.mmap resd High_Mem_Map_size
.drive resd 1
.fat resd fat_size
endstruc
kcode_offset equ 0x1000
The second stage parameter block, which transfers critical data from the boot sector to the second stage, is defined as:
Code: Select all
struc stg2_parameters
.drive resw 1 ; include a padding byte for stack alignment
.fat_0 resw 1 ; offset for the FAT buffer
.directory_buffer resw 1
.PnP_Entry_Seg resw 1
.PnP_Entry_Off resw 1
.boot_sig resw 1
.bpb resw 1
.reserved resw 9
endstruc
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; routines for basic floppy disk handling
%ifndef SIMPLE_DISK_HANDLING_CODE__INC
%define SIMPLE_DISK_HANDLING_CODE__INC
%include "bios.inc"
%include "macros.inc"
%include "bpb.inc"
%include "dir_entry.inc"
%include "stage2_parameters.inc"
bits 16
tries equ 0x03 ; number of times to attempt to access the FDD
;;; reset_disk - reset the floppy drive
;;; Inputs:
;;; DL - the disk ID
reset_disk:
mov si, 0
mov di, tries ; set count of attempts for disk resets
.try_reset:
mov ah, disk_reset
int DBIOS
jnc short .reset_end
dec di
jnz short .try_reset
;;; if repeated attempts to reset the disk fail, report error code
; write failure_state
; write reset_failed
; write exit
jmp halted
.reset_end:
ret
;;; read_LBA_sector - read a sector from a Linear Block Address
;;; Inputs:
;;; AX = Linear Block Address to read from
;;; ES = Segment to write result to
;;; BX = offset to write result to
;;; Outputs:
;;; AX = LBA+1 (i.e., the increment of previous LBA value)
;;; ES:BX - buffer written to
read_LBA_sector:
pusha
call near LBA_to_CHS
mov ah, dh ; temporary swap
mov dx, [bp + stg2_parameters.drive] ; get the value for DL
mov dh, ah
mov al, 1
call near read_sectors
.read_end: ; read_LBA_sector
popa
inc ax
ret
;;; LBA_to_CHS - compute the cylinder, head, and sector
;;; from a linear block address
;;; Inputs:
;;; AX = Linear Block Address
;;; Outputs:
;;; CH = Cylinder
;;; DH = Head
;;; CL = Sector (bits 0-5)
LBA_to_CHS:
push bx
push ax ; save so it can be used twice
zero(dx)
mov bx, Sectors_Per_Cylinder
;; Sector = (LBA % sectors per cyl) + 1 => in DL
div bx
inc dl
mov cl, dl
pop ax ; retrieve LBA value
;; Cylinder = LBA / (sectors per cyl * # of heads) => in AL
imul bx, Heads
zero(dx)
div bx ; AX = Cyl #, DL = partial result for Head
mov ch, al ; put previous AL into CH
;; Head = (LBA % (sectors per cyl * # of heads)) / sectors per cyl
;; => first part in DL, final in AL
mov ax, dx
zero(dx)
mov bx, Sectors_Per_Cylinder
div bx ; get the final value for Head
mov dh, al
pop bx
ret
;;; read_sectors -
;;; Inputs:
;;; AL = # of sectors to read
;;; DL = drive number
;;; CH = Cylinder
;;; DH = Head
;;; CL = Sector (bits 0-5)
;;; Outputs:
;;; ES:BX = segment and offset for the buffer
;;; to save the read sector into
read_sectors:
pusha
mov si, 0
mov di, tries ; set count of attempts for disk reads
mov ah, disk_read
.try_read:
push ax
int DBIOS
pop ax
jnc short .read_end
dec di
jnz short .try_read
jmp halted
.read_end:
popa
ret
%endif
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; routine for finding a directory entry
%ifndef DIRECTORY_ENTRY_SEEK_CODE__INC
%define DIRECTORY_ENTRY_SEEK_CODE__INC
bits 16
%include "bios.inc"
%include "macros.inc"
%include "bpb.inc"
%include "dir_entry.inc"
%include "stage2_parameters.inc"
;;; seek_directory_entry - seek the directory for the given filename
;;; Inputs:
;;; SI - address of the filename to match against
;;; ES:DI - directory buffer
;;; CX - max. number of entries
;;; BX - size of an entry
;;; Outputs:
;;; DI - location of the entry
seek_directory_entry:
.dir_entry_test:
push di
push si
push cx
mov cx, filename_length
repe cmpsb ; does the directory entry match?
pop cx
pop si
pop di
je .entry_found
add di, bx
loop .dir_entry_test
mov di, 0x0000 ; if not found, return 0
.entry_found:
ret
;;; read_directory_details -
;;; Inputs:
;;; ES:DI - directory entry
;;; Outputs:
;;; BX - First FAT entry of the file
read_directory_details:
;; position of first sector
mov bx, es:[di + directory_entry.cluster_lobits]
ret
%endif
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; routine for finding a directory entry
%ifndef FAT_TO_FILE_CODE__INC
%define FAT_TO_FILE_CODE__INC
bits 16
%include "bios.inc"
%include "macros.inc"
%include "bpb.inc"
%include "dir_entry.inc"
%include "stage2_parameters.inc"
%include "simple_disk_handling_code.inc"
%include "simple_text_print_code.inc"
%include "print_hex_code.inc"
hi_nibble_mask equ 0x0FFF
lo_nibble_shift equ 4
end_of_chain_mask equ 0x0FF8
;;; fat_to_file - read a chain of FAT entries to
;;; get the file sectors it maps to
;;; and read those sectors into a buffer
;;; Inputs:
;;; BX - starting FAT entry
;;; DI - FAT buffer
;;; SI - buffer to read the entry to
;;; Outputs:
;;; buffer to read file into
fat_to_file:
.read_loop:
mov ax, bx
sub ax, 2
add ax, first_data_sector
; call print_hex_word
push bx
mov bx, si
call read_LBA_sector
pop bx
call extract_next_fat12_entry
mov bx, ax
add si, Bytes_Per_Sector
; test ax, end_of_chain_mask
; jnz .read_loop
cmp ax, end_of_chain_mask
jb .read_loop
.end_of_file:
; call print_hex_word
ret
;;; extract_next_fat12_entry - read a FAT entry to
;;; see where the next FAT entry is,
;;; if any
;;; Inputs:
;;; DI - FAT entry buffer
;;; BX - current FAT entry's value
;;; Outputs:
;;; AX - next FAT entry's value
extract_next_fat12_entry:
;; address_of_FAT_entry = fat_buffer + (current_cluster + current_cluster / 2)
mov ax, bx ; BX == current cluster
shr ax, 1 ; current_cluster / 2
add ax, bx ; (current_cluster + current_cluster / 2) == FAT entry offset
push di
add di, ax ; index fat buffer by offset
mov ax, [di] ; get the indexed entry
pop di
test bx, 1 ; check if the existing entry is odd
jz .even
.odd:
shr ax, lo_nibble_shift ; extract the high bits
; call print_hex_word
ret
.even:
and ax, hi_nibble_mask ; extract the low bits
; call print_hex_word
ret
%endif