197 lines
6.1 KiB
C
197 lines
6.1 KiB
C
#include "disk.h"
|
|
#include "v86defs.h"
|
|
#include "print.h"
|
|
#include "stdint.h"
|
|
|
|
extern void *memcpy(void *restrict dest, const void *restrict src, uintptr_t n);
|
|
|
|
#define SECTOR_SIZE 512
|
|
#define DISKCACHEBLOCKSIZE 0x1000 // 512 * 4
|
|
#define DISKCACHESECTORMASK 7
|
|
#define DISKCACHESECTORSIZE 8
|
|
#define DISKCACHEBLOCKCOUNT 128
|
|
uint8_t (*const DiskCache)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE] = (uint8_t (* const)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE])0x280000;
|
|
uint32_t DiskCacheLastRead[DISKCACHEBLOCKCOUNT];
|
|
uint32_t DiskCacheSector[DISKCACHEBLOCKCOUNT];
|
|
|
|
extern uint32_t _gpf_eax_save;
|
|
extern uint32_t _gpf_eflags_save;
|
|
extern uint16_t error_screen[80*50]; // defined in kernel.c
|
|
__attribute__((__no_caller_saved_registers__))
|
|
__attribute__((__noreturn__))
|
|
extern void error_environment(); // defined in kernel.c
|
|
uint32_t numHead;
|
|
uint32_t secPerTrack;
|
|
uint32_t maxCylinder;
|
|
char useCHS = -1;
|
|
// TODO This function also inits cache, make that go somewhere else
|
|
void Disk_SetupCHS() {
|
|
union V86Regs_t regs;
|
|
// Check for INT 13 Extensions support
|
|
regs.h.ah = 0x41;
|
|
regs.w.bx = 0x55AA;
|
|
regs.h.dl = 0x80;
|
|
V8086Int(0x13, ®s);
|
|
// LBA supported if CF clear
|
|
if (!(_gpf_eflags_save & 0x1)) {
|
|
useCHS = 0;
|
|
} else {
|
|
FARPTR v86_entry = i386LinearToFp(v86DiskGetGeometry);
|
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
|
// check CF for error
|
|
if (_gpf_eflags_save & 0x1) {
|
|
uint16_t *vga = error_screen;
|
|
vga += printStr("Could not get Disk Geometry.", vga);
|
|
error_environment();
|
|
for(;;);
|
|
}
|
|
numHead = ((_gpf_eax_save & 0xff00) >> 8) + 1;
|
|
secPerTrack = _gpf_eax_save & 0x3f;
|
|
maxCylinder = ((_gpf_eax_save & 0xff0000) >> 16) | ((_gpf_eax_save & 0xc0) << 2);
|
|
useCHS = 1;
|
|
}
|
|
}
|
|
|
|
extern uint32_t TIMERVAL;
|
|
uint8_t *FindInCache(uint32_t sector) {
|
|
uint32_t maskedSector = sector & ~DISKCACHESECTORMASK;
|
|
for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) {
|
|
// Found
|
|
if (DiskCacheSector[i] == maskedSector) {
|
|
DiskCacheLastRead[i] = TIMERVAL;
|
|
return &(*DiskCache)[i][(sector & DISKCACHESECTORMASK) << 9];
|
|
}
|
|
}
|
|
// Not Found
|
|
return 0;
|
|
}
|
|
|
|
void AddToCache(uint32_t sector, uint8_t *buffer) {
|
|
uintptr_t lowestFoundTime = DiskCacheLastRead[0];
|
|
uintptr_t lowestFoundIdx = 0;
|
|
// TODO We should really use something more fast and less lazy
|
|
for (int i = 1; i < DISKCACHEBLOCKCOUNT; i++) {
|
|
if (DiskCacheLastRead[i] < lowestFoundTime) {
|
|
lowestFoundTime = DiskCacheLastRead[i];
|
|
lowestFoundIdx = i;
|
|
}
|
|
}
|
|
for (int i = 0; i < DISKCACHEBLOCKSIZE / sizeof(uint32_t); i++)
|
|
((uint32_t *)((*DiskCache)[lowestFoundIdx]))[i] = ((uint32_t*)buffer)[i];
|
|
DiskCacheLastRead[lowestFoundIdx] = TIMERVAL;
|
|
DiskCacheSector[lowestFoundIdx] = sector;
|
|
}
|
|
|
|
// NOTE Only updates one sector (512B)
|
|
void UpdateCache(uint32_t sector, uint8_t *buffer) {
|
|
uint8_t *cache = FindInCache(sector);
|
|
// Not in cache, nothing to update
|
|
if (!cache) return;
|
|
for (int i = 0; i < SECTOR_SIZE/sizeof(uint32_t); i++)
|
|
((uint32_t*)cache)[i] = ((uint32_t*)buffer)[i];
|
|
}
|
|
|
|
void InitCache() {
|
|
for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) {
|
|
DiskCacheLastRead[i] = 0;
|
|
DiskCacheSector[i] = -1;
|
|
}
|
|
}
|
|
|
|
// NOTE Must be run before reading from disk
|
|
void InitDisk() {
|
|
InitCache();
|
|
Disk_SetupCHS();
|
|
}
|
|
|
|
uint32_t Disk_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
|
|
uint8_t *cache = FindInCache(sector);
|
|
if (cache) {
|
|
memcpy(buffer, cache, count * SECTOR_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
// NOTE If the buffer provided is outside the 0x20000-0x2F000 range,
|
|
// the function will use 0x20000 for the Virtual 8086 process
|
|
// and copy to the other buffer after
|
|
uint8_t *v86buf = (uintptr_t)buffer >= 0x20000 && (uintptr_t)buffer <= 0x2F000 ?
|
|
buffer :
|
|
(uint8_t*)0x20000;
|
|
|
|
// TODO Do error handling
|
|
if (!useCHS) {
|
|
// LBA Read
|
|
v86disk_addr_packet.start_block = sector & ~DISKCACHESECTORMASK;
|
|
v86disk_addr_packet.blocks = DISKCACHESECTORSIZE;
|
|
v86disk_addr_packet.transfer_buffer =
|
|
(uintptr_t)v86buf & 0x000F |
|
|
(((uintptr_t)v86buf & 0xFFFF0) << 12);
|
|
union V86Regs_t regs;
|
|
regs.h.ah = 0x42;
|
|
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
|
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
|
if (v86disk_addr_packet.blocks != DISKCACHESECTORSIZE) {
|
|
uint16_t *vga = error_screen;
|
|
vga += printStr("INT13 Read Secs: ", vga);
|
|
vga += printDec(v86disk_addr_packet.blocks, vga);
|
|
error_environment();
|
|
for(;;);
|
|
}
|
|
AddToCache(sector & ~DISKCACHESECTORMASK, v86buf);
|
|
v86buf = &v86buf[(sector & DISKCACHESECTORMASK) << 9];
|
|
} else {
|
|
uint32_t tmp = sector / secPerTrack;
|
|
uint32_t sec = (sector % (secPerTrack)) + 1;
|
|
uint32_t head = tmp % numHead;
|
|
uint32_t cyl = tmp / numHead;
|
|
union V86Regs_t regs;
|
|
regs.w.ax = 0x0201;
|
|
regs.h.ch = cyl & 0xff;
|
|
regs.h.cl = sec | ((cyl >> 2) & 0xc0);
|
|
regs.h.dh = head;
|
|
regs.h.dl = 0x80;
|
|
regs.w.bx = (uintptr_t)v86buf & 0xFFFF;
|
|
FARPTR v86_entry = i386LinearToFp(v86DiskReadCHS);
|
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
|
}
|
|
if (v86buf != buffer)
|
|
memcpy(buffer, v86buf, count * SECTOR_SIZE);
|
|
return 0;
|
|
}
|
|
uint32_t Disk_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
|
|
// NOTE If the buffer provided is outside the 0x20000-0x2FE00 range,
|
|
// the function will use copy that buffer into the Virtual 8086 disk range
|
|
uint8_t *v86buf = buffer;
|
|
if ((uintptr_t)v86buf < 0x20000 || (uintptr_t)v86buf > 0x2FE00) {
|
|
v86buf = (uint8_t *)0x20000;
|
|
memcpy(v86buf, buffer, count * SECTOR_SIZE);
|
|
}
|
|
|
|
// TODO This check should probably happen at the kernel level
|
|
if (useCHS == -1) {
|
|
Disk_SetupCHS();
|
|
}
|
|
|
|
// TODO Do error handling
|
|
if (!useCHS) {
|
|
// LBA Write
|
|
v86disk_addr_packet.start_block = sector;
|
|
v86disk_addr_packet.blocks = count;
|
|
v86disk_addr_packet.transfer_buffer =
|
|
(uintptr_t)v86buf & 0x000F |
|
|
(((uintptr_t)v86buf & 0xFFFF0) << 12);
|
|
union V86Regs_t regs;
|
|
regs.w.ax = 0x4300;
|
|
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
|
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
|
UpdateCache(sector, buffer);
|
|
} else {
|
|
uint16_t *vga = error_screen;
|
|
vga += printStr("CHS Write Unimplemented", vga);
|
|
error_environment();
|
|
for(;;);
|
|
}
|
|
return 0;
|
|
}
|
|
|