#include #include #include #include static fat16_t fat; #pragma pack(push, 1) typedef struct { uint8_t jmp[3]; char oem[8]; uint16_t bytes_per_sector; uint8_t sectors_per_cluster; uint16_t reserved_sectors; uint8_t fat_count; uint16_t root_entries; uint16_t total_sectors_short; uint8_t media_desc; uint16_t fat_size_sectors; uint16_t sectors_per_track; uint16_t heads; uint32_t hidden_sectors; uint32_t total_sectors_long; // Extended Boot Record... } fat16_bpb_t; typedef struct { char name[8]; char ext[3]; uint8_t attr; uint8_t reserved; uint8_t create_time_tenth; uint16_t create_time; uint16_t create_date; uint16_t access_date; uint16_t first_cluster_hi; uint16_t write_time; uint16_t write_date; uint16_t first_cluster_lo; uint32_t size; } fat16_direntry_raw_t; #pragma pack(pop) static uint8_t sector_buffer[512]; uint32_t cluster_to_lba(uint16_t cluster) { return fat.data_start + ((cluster - 2) * fat.sectors_per_cluster); } int32_t fat16_init(uint8_t drive) { if (ide_read48(drive, 0, 1, sector_buffer) < 0) { return -1; } fat16_bpb_t* bpb = (fat16_bpb_t*)sector_buffer; fat.drive = drive; fat.bytes_per_sector = bpb->bytes_per_sector; fat.sectors_per_cluster = bpb->sectors_per_cluster; fat.root_entry_count = bpb->root_entries; fat.fat_start = bpb->reserved_sectors; fat.root_dir_start = fat.fat_start + bpb->fat_count * bpb->fat_size_sectors; fat.data_start = fat.root_dir_start + ((bpb->root_entries * 32 + fat.bytes_per_sector - 1) / fat.bytes_per_sector); fat.current_dir_cluster = 0; return 0; } uint16_t fat16_get_fat_entry(uint16_t cluster) { uint32_t fat_offset = cluster * 2; uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector); uint32_t ent_offset = fat_offset % fat.bytes_per_sector; if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0) { return 0xFFFF; } return *(uint16_t*)§or_buffer[ent_offset]; } uint16_t fat16_alloc_cluster(void) { for (uint16_t cluster = 2; cluster < 0xFFF0; ++cluster) { uint16_t entry = fat16_get_fat_entry(cluster); if (entry == 0x0000) { // Mark as end of chain uint32_t fat_offset = cluster * 2; uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector); uint32_t ent_offset = fat_offset % fat.bytes_per_sector; if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0) return 0; *(uint16_t*)§or_buffer[ent_offset] = 0xFFFF; if (ide_write48(fat.drive, fat_sector, 1, sector_buffer) < 0) return 0; return cluster; } } return 0; } int32_t fat16_list_dir(fat16_dir_entry_t* entries, size_t max_entries) { size_t count = 0; if (fat.current_dir_cluster == 0) { uint16_t root_sectors = ((fat.root_entry_count * 32) + (fat.bytes_per_sector - 1)) / fat.bytes_per_sector; for (uint32_t i = 0; i < root_sectors && count < max_entries; ++i) { if (ide_read48(fat.drive, fat.root_dir_start + i, 1, sector_buffer) < 0) { return -1; } fat16_direntry_raw_t* de = (fat16_direntry_raw_t*)sector_buffer; for (int32_t j = 0; j < 512 / 32 && count < max_entries; ++j, ++de) { if (de->name[0] == 0x00 || de->name[0] == 0xE5) { continue; } memcpy(entries[count].name, de->name, 8); entries[count].name[8] = '\0'; entries[count].attr = de->attr; entries[count].cluster = de->first_cluster_lo; entries[count].size = de->size; count++; } } } /* TODO: Add support for listing subdirectories if current_dir_cluster != 0 */ return count; } int32_t fat16_read_file(const char* filename, void* buffer, size_t max_size) { fat16_dir_entry_t entries[64]; int32_t n = fat16_list_dir(entries, 64); for (int32_t i = 0; i < n; ++i) { if (!(entries[i].attr & FAT16_ATTR_DIRECTORY) && strncmp(entries[i].name, filename, FAT16_MAX_FILENAME) == 0) { uint16_t cluster = entries[i].cluster; uint8_t* out = (uint8_t*)buffer; size_t remaining = entries[i].size < max_size ? entries[i].size : max_size; while (cluster >= 0x0002 && cluster < 0xFFF8 && remaining > 0) { uint32_t lba = cluster_to_lba(cluster); size_t to_read = fat.bytes_per_sector * fat.sectors_per_cluster; if (ide_read48(fat.drive, lba, fat.sectors_per_cluster, sector_buffer) < 0) return -1; size_t copy_size = (remaining < to_read) ? remaining : to_read; memcpy(out, sector_buffer, copy_size); out += copy_size; remaining -= copy_size; cluster = fat16_get_fat_entry(cluster); } return (int32_t)(out - (uint8_t*)buffer); } } return -1; } int32_t fat16_write_file(const char* filename, const void* data, size_t size) { fat16_dir_entry_t entries[64]; int32_t n = fat16_list_dir(entries, 64); for (int32_t i = 0; i < n; ++i) { if (strncmp(entries[i].name, filename, FAT16_MAX_FILENAME) == 0) { uint16_t cluster = entries[i].cluster; const uint8_t* src = (const uint8_t*)data; size_t remaining = size; while (remaining > 0) { uint32_t lba = cluster_to_lba(cluster); size_t cluster_bytes = fat.sectors_per_cluster * fat.bytes_per_sector; size_t to_write = remaining < cluster_bytes ? remaining : cluster_bytes; memset(sector_buffer, 0, cluster_bytes); // Clear old data memcpy(sector_buffer, src, to_write); if (ide_write48(fat.drive, lba, fat.sectors_per_cluster, sector_buffer) < 0) return -1; src += to_write; remaining -= to_write; // Last chunk? if (remaining == 0) break; // Check next cluster uint16_t next = fat16_get_fat_entry(cluster); if (next >= 0xFFF8 || next == 0x0000) { // Need to allocate a new cluster uint16_t new_cluster = fat16_alloc_cluster(); if (new_cluster == 0) return -1; // Update FAT to point to new cluster uint32_t fat_offset = cluster * 2; uint32_t fat_sector = fat.fat_start + (fat_offset / fat.bytes_per_sector); uint32_t ent_offset = fat_offset % fat.bytes_per_sector; if (ide_read48(fat.drive, fat_sector, 1, sector_buffer) < 0) return -1; *(uint16_t*)§or_buffer[ent_offset] = new_cluster; if (ide_write48(fat.drive, fat_sector, 1, sector_buffer) < 0) return -1; next = new_cluster; } cluster = next; } return (int32_t)size; } } return -1; } int32_t fat16_change_dir(const char* dirname) { fat16_dir_entry_t entries[64]; int32_t n = fat16_list_dir(entries, 64); for (int32_t i = 0; i < n; ++i) { if ((entries[i].attr & FAT16_ATTR_DIRECTORY) && strncmp(entries[i].name, dirname, FAT16_MAX_FILENAME) == 0) { fat.current_dir_cluster = entries[i].cluster; return 0; } } return -1; }