diff --git a/fat12.c b/fat12.c new file mode 100644 index 0000000..d7f67df --- /dev/null +++ b/fat12.c @@ -0,0 +1,138 @@ +#include +#include + +#define kSectorsPerCluster 2 +#define kSectorsPerTrack 9 +#define kHeads 2 +#define kFatSizeSectors 2 +#define kRootDirSizeSectors 7 +#define kDataRegionStartSector (1 + kFatSizeSectors*2 + kRootDirSizeSectors) + + +typedef struct { + char name[8]; + char ext[3]; + uint8_t attr; + + uint8_t dontcare[14]; + + uint16_t cluster; + uint32_t size; +} direntry; + +static uint8_t* gFat; +static direntry* gRootdir; + + +static int readsector(int c, int h, int s, uint8_t* addr) { + register uint8_t* dest asm ("bx") = addr; + register uint8_t nsects asm ("al") = 1; + register uint8_t func asm ("ah") = 0x02; + register uint8_t sect asm ("cl") = s; + register uint8_t cyl asm ("ch") = c; + register uint8_t head asm ("dh") = h; + register uint8_t drive asm ("dl") = 0; + register uint16_t seg asm ("es") = 0; + + register uint8_t ret asm("ah"); + + for (int i = 0; i < 3; i++) { + asm volatile ("int $0x13" + : "=r" (ret) + : "r" (dest), "r" (nsects), "r" (func), "r" (sect), + "r" (cyl), "r" (head), "r" (drive), "r" (seg)); + if (ret == 0x80) { + continue; + } else { + return ret; + } + } + + return ret; +} + +static int readcluster(int cluster) { + int offs = cluster * 3 / 2; + if (cluster % 2) { + // high nibble is lsb + whole byte + return ((gFat[offs] & 0xf0) >> 4) + (gFat[offs+1] << 4); + } else { + return gFat[offs] + ((gFat[offs+1] & 0x0f) << 8); + } +} + +static void cluster2chs(int cluster, int* c, int* h, int* s) { + int logicalsector = kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster; + *s = (logicalsector % kSectorsPerTrack) + 1; + *h = (logicalsector / kSectorsPerTrack) % kHeads; + *c = logicalsector / (kHeads * kSectorsPerTrack); +} + +static int strncmp(const char* s1, const char *s2, size_t len) { + for (int i = 0; i < len && s1[i]; i++) { + if (s1[i] != s2[i]) { + return 1; + } + } + // XXX: really we should return 0 only if *s1 == *s2 here too + return 0; +} + +static int loadfile(direntry* entry, void* addr) { + int cluster = entry->cluster; + for (int i = 0; i < entry->size; i+=1024) { + int c, h, s; + cluster2chs(cluster, &c, &h, &s); + if (readsector(c, h, s, addr + i)) { + return -5; + } + s++; + if (s > 9) { + s = 1; + h++; + } + if (h > 1) { + h = 0; + c++; + } + if (readsector(c, h, s, addr + i + 512)) { + return -5; + }; + cluster = readcluster(cluster); + } + return 0; +} + +int fat12_init(void* fat_addr, void* rootdir_addr) { + if (readsector(0, 0, 2, fat_addr)) { + return -2; + } + if (readsector(0, 0, 6, rootdir_addr)) { + return -3; + } + + gFat = fat_addr; + gRootdir = rootdir_addr; + + return 0; +} + +int fat12_readfile(const char* name, void* addr) { + direntry* file = 0; + + for (int i = 0; i < 16; i++) { + direntry* entry = &gRootdir[i]; + if (entry->name[0] == 0) { + break; + } + if (!strncmp(entry->name, name, 11)) { + file = entry; + break; + } + } + if (!file) { + return -4; + } + + return loadfile(file, addr); +} diff --git a/fat12.h b/fat12.h new file mode 100644 index 0000000..85eb0b3 --- /dev/null +++ b/fat12.h @@ -0,0 +1,15 @@ +#pragma once + +/* + * Arguments: + * - fat_addr: 1 KiB + * - rootdir_addr: 3.5 KiB + */ +int fat12_init(void* fat_addr, void* rootdir_addr); + +/* + * Returns: + * -4 if file is not found + * -5 if there is a disk error + */ +int fat12_readfile(const char* name, void* addr);