Files
stevia/include/fat32/FAT32_SYS.nasm
2025-09-20 08:29:43 -04:00

290 lines
9.1 KiB
NASM
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; Copyright (C) 2025 Elaina Claus
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <https://www.gnu.org/licenses/>.
%ifndef __INC_FAT32_SYS
%include "partition_table.inc"
%include "fat32/fat32_structures.inc"
; int read_mbr(int boot_drive, void* dst)
; destination buffer needs 512 bytes of space
FAT32_load_mbr:
__CDECL16_PROC_ENTRY
.proc:
; read mbr on boot drive to memory (for the partition table)
movzx ax, byte [bp + 4]
push ax ; drive_num (2)
push 0x01 ; count (2)
push dword 0x0 ; lba (4)
push word [bp + 6] ; offset = dst
push __STAGE2_SEGMENT ; this segment
call BIOS_int13h_ext_read
add sp, 0xC
.check_sig:
mov bx, [bp + 6]
cmp word [bx + 0x1FE], 0xAA55 ; check for bytes at end
jne .error_nosign
; TODO: this needs more error checking, zero checking, check the sig a bunch of stuff...
.endp:
__CDECL16_PROC_EXIT
ret
.error_nosign:
ERROR STAGE2_ERROR_BAD_MBR
; int read_vbr(int boot_drive, void* buf)
; read vbr on boot partition to memory (for fat bpb/ebpb)
; TODO: seperate validation and loading the sector, just check the 0xAA55 at the end here...
FAT32_load_vbr:
__CDECL16_PROC_ENTRY
.proc:
mov bx, SteviaInfo
mov ax, word [bx + SteviaInfoStruct_t.p16_MbrPtr]
add ax, DISK_PARTITION_TABLE_OFFSET
mov bx, ax ; offset to part table
mov cx, 4 ; only checking 4 entries
.find_active_L0:
mov al, [bx + PartEntry_t.attributes]
cmp al, 0x80 ; 0x80 == 1000_0000b
je .check_fstype
add bx, PartEntry_t_size ; next part entry's attributes
loop .find_active_L0
jmp .error_noactive
.check_fstype:
; check for part_type = 0x0C (DOS 7.1+/W95 FAT32 w/ LBA) or 0x1C (Hidden 0x0C)
__BOCHS_MAGIC_DEBUG
mov al, [bx + PartEntry_t.part_type]
cmp al, 0x0C
je .active_ok
; *or*
cmp al, 0x1C
je .active_ok
jmp .error_badparttype ; error if part_type != 0x1C or 0x0C
.active_ok:
movzx ax, byte [bp + 4]
push ax ; drive_num (2)
push 0x01 ; count (2)
mov eax, dword [bx + PartEntry_t.lba_start]
push dword eax ; lba (4)
push word [bp + 6] ; offset = dst
push __STAGE2_SEGMENT ; this segment
call BIOS_int13h_ext_read
add sp, 0xC
; vbr (with fat bpb/ebpb) is at the buffer now
.check_sig:
mov bx, [bp + 6]
cmp word [bx + 0x1FE], 0xAA55 ; check for bytes at end
jne .error_nosign
.check_FAT_sanity:
; we only quickly validate that this is *probably* a FAT32 volume
add bx, 11 ; point bx at start of bpb (skip jmp code and ident)
cmp word [bx + FAT32_bpb_t.u16_TotalSectors16], 0 ; TotalSectors16 should be 0 (use TotalSectors32 in bpb)
jne .error_totsectors
cmp word [bx + FAT32_bpb_t.u16_FATSize16], 0 ; FatSize16 will be 0 if FAT32 (use FATSize32 in ebpb)
jne .error_fatsz
cmp word [bx + FAT32_bpb_t.u16_RootEntryCount16], 0 ; root dir info is in data clusters on fat32
jne .error_rootdir
.endp:
__CDECL16_PROC_EXIT
ret
.error_noactive:
ERROR STAGE2_VBR_E_ACTIVE
.error_nosign:
ERROR STAGE2_VBR_E_SIGN
.error_totsectors:
ERROR STAGE2_VBR_E_TOT
.error_fatsz:
ERROR STAGE2_VBR_E_FATSZ
.error_rootdir:
ERROR STAGE2_VBR_E_DIRENT
.error_badparttype:
ERROR STAGE2_VBR_E_PARTTYPE
; what a 'mount' should probably do at this point...
; - read VBR at partition_lba
; - validate FAT32 signatures
; - fill fat32_bpb_t and compute derived fields
; - read FSInfo (free count/next free)
; - ???
; int fat32_mount(FAT32_State_t* state, uint32_t partition_lba);
FAT32_mountfs:
__CDECL16_PROC_ENTRY
.proc:
; mount: parse BPB, derive fat0_lba, data_lba, cluster0_lba.
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; int fat32_read_fat(FAT32_State_t* state, uint32_t clus, uint32_t* out);
FAT32_read_fat:
__CDECL16_PROC_ENTRY
.proc:
; Read 32-bit FAT entry for cluster n
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; FAT32 entry is 32-bits; only low 28 bits are meaningful.
; EOC if (val & 0x0FFFFFFF) >= 0x0FFFFFF8.
; bad if == 0x0FFFFFF7.
; free if == 0x00000000.
; int fat32_next_clus(FAT32_State_t* state, uint32_t clus, uint32_t* out_next);
FAT32_next_cluster:
__CDECL16_PROC_ENTRY
.proc:
; Walk to next cluster in chain (validates EOC/bad)
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; e.g:
; uint64_t clus_to_lba(FAT32_State_t* state, uint32_t clus) {
; return v->data_lba + (uint64_t)(clus - 2) * v->secs_per_clus;
; }
FAT32_clus_to_lba:
__CDECL16_PROC_ENTRY
.proc:
; Convert cluster -> LBA of first sector of that cluster
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; Prototyping for now...
; TODO:
; - bio_read_sectors (BIOS int13h LBA or CHS) + tiny sector cache.
; - mount: parse BPB, derive fat0_lba, data_lba, cluster0_lba.
; - FAT read: fat32_read_fat, fat32_next_clus.
; - dir iterator (SFN only) + path lookup.
; - file reader (fopen/fread) with cluster crossing.
; - contiguity probe for speed (optional).
; - (later) LFN support & code page handling.
; keep count <= 127
; int bio_read_sectors(uint64_t lba, uint16_t count, void* dst);
; will also need a bio_bios_int13h() (refactor ext_disk_read?)
; to abstract the backend 'sector getter'
; Error codes
; FS_OK 0
; FS_E_IO 1 (bio read failed)
; FS_E_FMT 2 (not FAT32 or bad BPB)
; FS_E_RANGE 3 (bad cluster or out of range)
; FS_E_END 4 (iterator end)
; FS_E_NOSUCH 5 (file/dir not found)
; FS_E_ISDIR 6 (opened dir as file)
; FS_E_TOOLONG 7 (path segment too long for 8.3)
; FS_E_UNSUPPORTED 8 (LFN present but disabled)
; FS_E_UNIMPLEMENTED 0xffff (procedure call unimplmented)
;
; Cache methods
;
; TODO: make a cache for ~4-8 disk sectors
; TODO: make a cache for FATs to avoid having to re-read the FAT
;
; // uses bio_read_sectors + cache
; int cache_read_sector(uint64_t lba, void* dst);
; int cache_invalidate_all(void);
;
; SFN directories
;
; typedef struct {
; char name83[12]; // "FILENAMEEXT" (no dot), NUL-terminated
; uint8_t attr; // ATTR_DIRECTORY=0x10, ATTR_VOLUME=0x08
; uint32_t first_clus; // (hi<<16)|lo
; uint32_t size; // bytes
; } fat32_dirent_sfn_t;
; typedef struct { // iterator
; fat32_bpb_t* v;
; uint32_t cur_clus;
; uint32_t offs_in_clus; // byte offset
; } fat32_dir_iter_t;
;int fat32_dir_iter_open_root(fat32_bpb_t* v, fat32_dir_iter_t* it);
;int fat32_dir_iter_open(fat32_bpb_t* v, uint32_t first_clus, fat32_dir_iter_t* it);
;int fat32_dir_iter_next(fat32_dir_iter_t* it, fat32_dirent_sfn_t* out); // FS_OK or FS_E_END
; typedef struct {
; uint32_t first_clus;
; uint32_t size;
; uint8_t attr; // dir or file
; } fat32_node_t;
; split by / or \.
; start at root (v->root_clus).
; for each component:
; iterate directory with dir_iter_* to find case-insensitive 8.3 match (fold to upper and space-pad during compare).
; if final component: return files cluster & size.
; if intermediate: ensure ATTR_DIRECTORY and descend.
;
; int fat32_path_lookup(fat32_bpb_t* v, const char* path, fat32_node_t* out);
; an example 'do the thing' proc to load a file to a location
;
; // loads file at path into dst, up to max_bytes; returns size read.
; int fat32_load_file(fat32_bpb_t* v, const char* path, void* dst,
; uint32_t max_bytes, uint32_t* out_size);
; streamed reads
;
; typedef struct {
; fat32_bpb_t* v;
; uint32_t first_clus;
; uint32_t cur_clus;
; uint32_t cur_clus_index; // which cluster within file were on
; uint32_t size; // total bytes
; uint32_t pos; // current byte position
; } fat32_file_t;
;
;
; int fat32_fopen(fat32_bpb_t* v, const char* path, fat32_file_t* f);
; int fat32_fread(fat32_file_t* f, void* dst, uint32_t nbytes, uint32_t* out_read);
; int fat32_fseek(fat32_file_t* f, uint32_t new_pos); // forward seeks only is fine
; int fat32_fclose(fat32_file_t* f);
; an example 'do the thing' proc to load a file to a location
;
; // loads file at path into dst, up to max_bytes; returns size read.
; int fat32_load_file(fat32_bpb_t* v, const char* path, void* dst,
; uint32_t max_bytes, uint32_t* out_size);
%endif
%define __INC_FAT32_SYS