From ca7f1b3684d9c747a9951427dcbf1c40118c6948 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Mon, 14 Jun 2021 23:09:30 +0200 Subject: [PATCH] Implemented creating, removing and writing to files --- Inc/control.h | 16 ++- Src/control.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 293 insertions(+), 13 deletions(-) diff --git a/Inc/control.h b/Inc/control.h index 1bf9e1f..56ebcff 100644 --- a/Inc/control.h +++ b/Inc/control.h @@ -5,15 +5,12 @@ #include "fatfs.h" #include -typedef struct { - // Block and size are in blocks of 128 - uint8_t user; - char name[8]; - char ext[3]; - uint32_t size; - uint16_t allocation[8]; - char* filename; -} DiskEntry; +struct DirtyBlock { + uint32_t block; + struct DirtyBlock* next; + uint8_t size; + uint8_t* data; +}; typedef struct { FATFS *fs; @@ -25,6 +22,7 @@ typedef struct { uint8_t* buffer; uint8_t* directory; unsigned int size; + struct DirtyBlock* dirty_block; } Storage; typedef struct { diff --git a/Src/control.c b/Src/control.c index 86d0f38..44ef6df 100644 --- a/Src/control.c +++ b/Src/control.c @@ -166,7 +166,260 @@ void handle_io_write() { // Write byte to disk case 0x08: - // @todo Implement this from scratch + // @todo What if for whatever reason we write less then a record to disk? + if (control.storage.ready && control.storage.command == 0x30) { + control.storage.buffer[control.storage.counter] = value; + control.storage.counter++; + + if (control.storage.counter >= CPM_RECORD_SIZE) { + char filename[128] = {0}; + uint32_t offset = 0; + if (control.storage.lba == 0) { + snprintf(filename, 128, "0:loader.bin"); + offset = control.storage.lba - 0; + } else if (control.storage.lba >= 1 && control.storage.lba < 45) { + snprintf(filename, 128, "0:cpm22.bin"); + offset = control.storage.lba - 1; + } else if (control.storage.lba >= 45 && control.storage.lba < 51) { + snprintf(filename, 128, "0:bios.bin"); + offset = control.storage.lba - 45; + } else if (control.storage.lba >= 256 && control.storage.lba < 384) { + offset = control.storage.lba - 256; + + for (uint8_t index = 0; index < 4; ++index) { + char name[8]; + for (int j = 0; j < 8; ++j) { + char c = control.storage.buffer[index*32 + 1 + j]; + if (c == ' ') { + c = 0x00; + } + name[j] = c; + } + + char ext[3]; + for (int j = 0; j < 3; ++j) { + char c = control.storage.buffer[index*32 + 9 + j]; + if (c == ' ') { + c = 0x00; + } + ext[j] = c; + } + + char temp_filename[128]; + snprintf(temp_filename, 128, "0:A/%.8s.%.3s", name, ext); + + uint8_t user = control.storage.buffer[index*32 + 0]; + uint8_t user_old = control.storage.directory[offset*128 + index*32 + 0]; + + FIL file; + if (user != user_old) { + printf("User has changed for %s: %i -> %i\n\r", temp_filename, user_old, user); + + // @todo For some reason we keep getting a file entry that changes + // from 0 -> 0xe5 with no name + if (user == 0xe5) { + printf("Index: %i\n\r", index); + printf("Deleting file\n\r"); + FRESULT fr = f_unlink(temp_filename); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + } else if (user_old == 0xe5) { + printf("Creating file\n\r"); + + FRESULT fr = f_open(&file, temp_filename, FA_READ | FA_CREATE_ALWAYS); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + f_sync(&file); + f_close(&file); + } + } + + for (uint8_t i = 0; i < 8; ++i) { + uint16_t allocation = control.storage.buffer[index*32 + 16 + i*2]; + allocation += control.storage.buffer[index*32 + 17 + i*2] << 8; + + if (allocation == 0) { + break; + } + + struct DirtyBlock* prev = NULL; + struct DirtyBlock* current = control.storage.dirty_block; + while (current) { + if (allocation == current->block) { + printf("Found dirty block\n\r"); + + FRESULT fr = f_open(&file, temp_filename, FA_WRITE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + offset = i*CPM_RECORDS_PER_BLOCK; + fr = f_lseek(&file, offset * CPM_RECORD_SIZE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_write(&file, current->data, current->size * CPM_RECORD_SIZE, &control.storage.size); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_sync(&file); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_close(&file); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + if (prev) { + prev->next = current->next; + } else { + control.storage.dirty_block = NULL; + } + + free(current->data); + free(current); + + break; + } + + prev = current; + current = current->next; + } + } + } + + printf("Write to directory (offset: %li)\n\r", offset); + memcpy(control.storage.directory + offset*CPM_RECORD_SIZE, control.storage.buffer, CPM_RECORD_SIZE); + } else if (control.storage.lba >= 384) { + uint32_t block = (control.storage.lba - 256) / CPM_RECORDS_PER_BLOCK; + + uint8_t found = 0; + for (int entry = 0; entry < 127; ++entry) { + uint8_t user = control.storage.directory[entry*32 + 0]; + if (user == 0xe5) { + continue; + } + + for (int i = 0; i < 8; ++i) { + uint16_t allocation = control.storage.directory[entry*32 + 16 + 2*i]; + allocation += control.storage.directory[entry*32 + 17 + 2*i] << 8; + + if (allocation == block) { + found = 1; + offset = (control.storage.lba - 256 - block*CPM_RECORDS_PER_BLOCK) + i*CPM_RECORDS_PER_BLOCK; + + char name[8]; + for (int j = 0; j < 8; ++j) { + char c = control.storage.directory[entry*32 + 1 + j]; + if (c == ' ') { + c = 0x00; + } + name[j] = c; + } + + char ext[3]; + for (int j = 0; j < 3; ++j) { + char c = control.storage.directory[entry*32 + 9 + j]; + if (c == ' ') { + c = 0x00; + } + ext[j] = c; + } + + snprintf(filename, 128, "0:A/%.8s.%.3s", name, ext); + + break; + } + } + + if (found) { + break; + } + } + + if (!found) { + printf("Write to unlinked block\n\r"); + + // Check if we already have a dirty block for this block + struct DirtyBlock* dirty_block = control.storage.dirty_block; + while (dirty_block) { + if (dirty_block->block == block) { + printf("Found existing dirty block\n\r"); + break; + } + } + + // @todo It appears that it updates the file entry after every block written + // So we might not need a linked list, altough it probably is more robust this way + if (!dirty_block) { + printf("Creating new dirty block\n\r"); + dirty_block = malloc(sizeof(struct DirtyBlock)); + dirty_block->block = block; + dirty_block->next = control.storage.dirty_block; + control.storage.dirty_block = dirty_block; + dirty_block->size = 0; + dirty_block->data = malloc(CPM_BLOCK_SIZE); + } + + offset = (control.storage.lba - 256 - block*CPM_RECORDS_PER_BLOCK); + memcpy(dirty_block->data + offset * CPM_RECORD_SIZE, control.storage.buffer, CPM_RECORD_SIZE); + + if (offset+1 > dirty_block->size) { + dirty_block->size = offset+1; + } + } + } + + if (filename[0]) { + FIL file; + // @0todo Cache the currently opened file so we are not opening it every 128 bytes + FRESULT fr = f_open(&file, filename, FA_WRITE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_lseek(&file, offset * CPM_RECORD_SIZE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_write(&file, control.storage.buffer, control.storage.counter, &control.storage.size); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_sync(&file); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_close(&file); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + } + + control.storage.ready = 0; + } + } break; case 0x0b: { @@ -214,12 +467,23 @@ void handle_io_write() { } else if (control.storage.lba >= 384) { uint32_t block = (control.storage.lba - 256) / CPM_RECORDS_PER_BLOCK; + uint8_t found = 0; for (int entry = 0; entry < 127; ++entry) { + uint8_t user = control.storage.directory[entry*32 + 0]; + if (user == 0xe5) { + continue; + } + for (int i = 0; i < 8; ++i) { uint16_t allocation = control.storage.directory[entry*32 + 16 + 2*i]; allocation += control.storage.directory[entry*32 + 17 + 2*i] << 8; + if (allocation == 0) { + break; + } + if (allocation == block) { + found = 1; offset = (control.storage.lba - 256 - block*CPM_RECORDS_PER_BLOCK) + i*CPM_RECORDS_PER_BLOCK; char name[8]; @@ -241,21 +505,29 @@ void handle_io_write() { } snprintf(filename, 128, "0:A/%.8s.%.3s", name, ext); + + break; } } + + if (found) { + break; + } } } + // @todo Maybe add a mechanism to read dirty blocks? + if (filename[0]) { FIL file; - // @0todo Cache the currently opened file so we are not opening it every 128 bytes + // @todo Cache the currently opened file so we are not opening it every 128 bytes FRESULT fr = f_open(&file, filename, FA_READ); if (fr) { printf("File error: %i\n\r", fr); return; } - fr = f_lseek(&file, offset * 0x80); + fr = f_lseek(&file, offset * CPM_RECORD_SIZE); if (fr) { printf("File error: %i\n\r", fr); return; @@ -274,6 +546,9 @@ void handle_io_write() { } } + control.storage.ready = 1; + } else if (control.storage.command == 0x30) { + // We don't need to prepare enything control.storage.ready = 1; } break; @@ -424,7 +699,14 @@ void control_reset() { f_mount(0, "0:", 0); free(control.storage.fs); - Control temp = {0, {NULL, NULL, 0, 0, 0, 0, NULL, NULL, 0}}; + while (control.storage.dirty_block) { + struct DirtyBlock* next = control.storage.dirty_block->next; + free(control.storage.dirty_block->data); + free(control.storage.dirty_block); + control.storage.dirty_block = next; + } + + Control temp = {0, {NULL, NULL, 0, 0, 0, 0, NULL, NULL, 0, NULL}}; control = temp; control.storage.buffer = (uint8_t*)malloc(CPM_RECORD_SIZE);