2025-07-17 21:23:38 +02:00

258 lines
11 KiB
C

#include "../neonucleus.h"
typedef struct nn_drive {
nn_refc refc;
nn_guard *lock;
nn_Context ctx;
nn_size_t currentSector;
nn_driveTable table;
nn_driveControl ctrl;
} nn_drive;
nn_drive *nn_newDrive(nn_Context *context, nn_driveTable table, nn_driveControl control) {
nn_drive *d = nn_alloc(&context->allocator, sizeof(nn_drive));
if(d == NULL) return NULL;
d->lock = nn_newGuard(context);
if(d->lock == NULL) {
nn_dealloc(&context->allocator, d, sizeof(nn_drive));
return NULL;
}
d->refc = 1;
d->table = table;
d->ctrl = control;
d->currentSector = 0;
d->ctx = *context;
return d;
}
nn_guard *nn_getDriveLock(nn_drive *drive) {
return drive->lock;
}
void nn_retainDrive(nn_drive *drive) {
nn_incRef(&drive->refc);
}
nn_bool_t nn_destroyDrive(nn_drive *drive) {
if(!nn_decRef(&drive->refc)) return false;
if(drive->table.deinit != NULL) {
drive->table.deinit(drive->table.userdata);
}
nn_Context ctx = drive->ctx;
nn_deleteGuard(&ctx, drive->lock);
nn_dealloc(&ctx.allocator, drive, sizeof(nn_drive));
return true;
}
void nn_drive_destroy(void *_, nn_component *component, nn_drive *drive) {
nn_destroyDrive(drive);
}
void nni_drive_readCost(nn_component *component, nn_drive *drive) {
nn_driveControl ctrl = drive->ctrl;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, 1, ctrl.readSectorsPerTick);
nn_addHeat(computer, ctrl.readHeatPerSector);
nn_removeEnergy(computer, ctrl.readEnergyPerSector);
}
void nni_drive_writeCost(nn_component *component, nn_drive *drive) {
nn_driveControl ctrl = drive->ctrl;
nn_computer *computer = nn_getComputerOfComponent(component);
nn_simulateBufferedIndirect(component, 1, ctrl.writeSectorsPerTick);
nn_addHeat(computer, ctrl.writeHeatPerSector);
nn_removeEnergy(computer, ctrl.writeEnergyPerSector);
}
void nni_drive_seekTo(nn_component *component, nn_drive *drive, nn_size_t sector) {
sector = sector - 1; // switch to 0 to N-1 sector addressing,
// which is much nicer to do math with
// and Lua made a big oopsie.
nn_size_t old = drive->currentSector;
nn_driveControl ctrl = drive->ctrl;
if(ctrl.seekSectorsPerTick == 0) return; // seek latency disabled
nn_computer *computer = nn_getComputerOfComponent(component);
// Compute important constants
nn_size_t sectorSize = drive->table.sectorSize;
nn_size_t platterCount = drive->table.platterCount;
nn_size_t capacity = drive->table.capacity;
nn_size_t sectorsPerPlatter = (capacity / sectorSize) / platterCount;
sector %= sectorsPerPlatter;
if(old == sector) return;
drive->currentSector = sector;
nn_size_t moved = 0;
if(sector > old) {
moved = sector - old; // moved forwards
} else if(sector < old) {
// moved back, depends on some options
if(ctrl.reversable) {
// horribly fucking unrealistic, as HDDs
// spin at ~7200 RPM, and if it decides
// to spontaneously instantly reverse direction,
// the force would crack the disk
// However, real life is a myth.
moved = old - sector;
} else {
// full turn
moved = sectorsPerPlatter - old;
}
}
nn_simulateBufferedIndirect(component, moved, ctrl.seekSectorsPerTick);
nn_addHeat(computer, ctrl.motorHeatPerSector * moved);
nn_removeEnergy(computer, ctrl.motorEnergyPerSector * moved);
}
void nn_drive_getLabel(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
char buf[NN_LABEL_SIZE];
nn_size_t l = NN_LABEL_SIZE;
nn_lock(&drive->ctx, drive->lock);
drive->table.getLabel(drive->table.userdata, buf, &l);
nn_unlock(&drive->ctx, drive->lock);
if(l == 0) {
nn_return(computer, nn_values_nil());
} else {
nn_return_string(computer, buf, l);
}
}
void nn_drive_setLabel(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t l = 0;
nn_value label = nn_getArgument(computer, 0);
const char *buf = nn_toString(label, &l);
if(buf == NULL) {
nn_setCError(computer, "bad label (string expected)");
return;
}
nn_lock(&drive->ctx, drive->lock);
l = drive->table.setLabel(drive->table.userdata, buf, l);
nn_unlock(&drive->ctx, drive->lock);
nn_return_string(computer, buf, l);
}
void nn_drive_getSectorSize(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t sector_size = drive->table.sectorSize;
nn_return(computer, nn_values_integer(sector_size));
}
void nn_drive_getPlatterCount(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t platter_count = drive->table.platterCount;
nn_return(computer, nn_values_integer(platter_count));
}
void nn_drive_getCapacity(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_size_t capacity = drive->table.capacity;
nn_return(computer, nn_values_integer(capacity));
}
void nn_drive_readSector(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value sectorValue = nn_getArgument(computer, 0);
int sector = nn_toInt(sectorValue);
nn_size_t sector_size = drive->table.sectorSize;
// we leave the +1 intentionally to compare the end of the real sector
if (sector < 1 || (sector * sector_size > drive->table.capacity)) {
nn_setCError(computer, "bad argument #1 (sector out of range)");
return;
}
char buf[sector_size];
nn_lock(&drive->ctx, drive->lock);
drive->table.readSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return_string(computer, buf, sector_size);
}
void nn_drive_writeSector(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value sectorValue = nn_getArgument(computer, 0);
int sector = nn_toInt(sectorValue);
nn_size_t sector_size = drive->table.sectorSize;
nn_value bufValue = nn_getArgument(computer, 1);
nn_size_t buf_size = 0;
const char *buf = nn_toString(bufValue, &buf_size);
if (buf_size != sector_size) {
nn_setCError(computer, "bad argument #2 (expected buffer of length `sectorSize`)");
return;
}
// we leave the +1 intentionally to compare the end of the real sector
if (sector < 1 || (sector * sector_size > drive->table.capacity)) {
nn_setCError(computer, "bad argument #1 (sector out of range)");
return;
}
nn_lock(&drive->ctx, drive->lock);
drive->table.writeSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return_boolean(computer, true);
}
void nn_drive_readByte(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value offsetValue = nn_getArgument(computer, 0);
nn_size_t disk_offset = nn_toInt(offsetValue) - 1;
nn_size_t sector_size = drive->table.sectorSize;
int sector = (disk_offset / sector_size) + 1;
nn_size_t sector_offset = disk_offset % sector_size;
if (disk_offset >= drive->table.capacity) {
nn_setCError(computer, "bad argument #1 (index out of range)");
return;
}
char buf[sector_size];
nn_lock(&drive->ctx, drive->lock);
drive->table.readSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return(computer, nn_values_integer(buf[sector_offset]));
}
void nn_drive_writeByte(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) {
nn_value offsetValue = nn_getArgument(computer, 0);
nn_value writeValue = nn_getArgument(computer, 1);
nn_size_t disk_offset = nn_toInt(offsetValue) - 1;
nn_intptr_t write = nn_toInt(writeValue);
nn_size_t sector_size = drive->table.sectorSize;
int sector = (disk_offset / sector_size) + 1;
nn_size_t sector_offset = disk_offset % sector_size;
if (write < -128 || write > 255) {
nn_setCError(computer, "bad argument #2 (byte out of range)");
return;
}
if (disk_offset >= drive->table.capacity) {
nn_setCError(computer, "bad argument #1 (index out of range)");
return;
}
nn_lock(&drive->ctx, drive->lock);
char buf[sector_size];
drive->table.readSector(drive->table.userdata, sector, buf);
buf[sector_offset] = write;
drive->table.writeSector(drive->table.userdata, sector, buf);
nn_unlock(&drive->ctx, drive->lock);
nn_return_boolean(computer, true);
}
void nn_loadDriveTable(nn_universe *universe) {
nn_componentTable *driveTable = nn_newComponentTable(nn_getAllocator(universe), "drive", NULL, NULL, (nn_componentDestructor *)nn_drive_destroy);
nn_storeUserdata(universe, "NN:DRIVE", driveTable);
nn_defineMethod(driveTable, "getLabel", (nn_componentMethod *)nn_drive_getLabel, "getLabel():string - Get the current label of the drive.");
nn_defineMethod(driveTable, "setLabel", (nn_componentMethod *)nn_drive_setLabel, "setLabel(value:string):string - Sets the label of the drive. Returns the new value, which may be truncated.");
nn_defineMethod(driveTable, "getSectorSize", (nn_componentMethod *)nn_drive_getSectorSize, "getSectorSize():number - Returns the size of a single sector on the drive, in bytes.");
nn_defineMethod(driveTable, "getPlatterCount", (nn_componentMethod *)nn_drive_getPlatterCount, "getPlatterCount():number - Returns the number of platters in the drive.");
nn_defineMethod(driveTable, "getCapacity", (nn_componentMethod *)nn_drive_getCapacity, "getCapacity():number - Returns the total capacity of the drive, in bytes.");
nn_defineMethod(driveTable, "readSector", (nn_componentMethod *)nn_drive_readSector, "readSector(sector:number):string - Read the current contents of the specified sector.");
nn_defineMethod(driveTable, "writeSector", (nn_componentMethod *)nn_drive_writeSector, "writeSector(sector:number, value:string) - Write the specified contents to the specified sector.");
nn_defineMethod(driveTable, "readByte", (nn_componentMethod *)nn_drive_readByte, "readByte(offset:number):number - Read a single byte at the specified offset.");
nn_defineMethod(driveTable, "writeByte", (nn_componentMethod *)nn_drive_writeByte, "writeByte(offset:number, value:number) - Write a single byte to the specified offset.");
}
nn_component *nn_addDrive(nn_computer *computer, nn_address address, int slot, nn_drive *drive) {
nn_componentTable *driveTable = nn_queryUserdata(nn_getUniverse(computer), "NN:DRIVE");
return nn_newComponent(computer, address, slot, driveTable, drive);
}