136 lines
3.2 KiB
C
136 lines
3.2 KiB
C
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#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) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|