diff --git a/Inc/control.h b/Inc/control.h index acf8571..1381931 100644 --- a/Inc/control.h +++ b/Inc/control.h @@ -5,28 +5,16 @@ #include "fatfs.h" #include -enum Type { - TYPE_NONE, - TYPE_FILE, - TYPE_DIR -}; - typedef struct { // Block and size are in blocks of 128 - uint32_t start; - uint32_t size; - char* filename; + uint8_t user; char name[8]; char ext[3]; + uint32_t size; + uint16_t allocation[8]; + char* filename; } DiskEntry; -typedef struct { - uint8_t entry_count; - uint8_t current; - DiskEntry entries[127]; - uint32_t end; -} Disk; - typedef struct { FATFS *fs; FIL *file; @@ -34,11 +22,8 @@ typedef struct { uint8_t ready; uint32_t counter; uint32_t lba; - uint32_t offset; uint8_t* buffer; unsigned int size; - enum Type type; - Disk A; } Storage; typedef struct { diff --git a/Inc/ffconf.h b/Inc/ffconf.h index 06ab95f..b3843f3 100644 --- a/Inc/ffconf.h +++ b/Inc/ffconf.h @@ -67,7 +67,7 @@ #define _USE_EXPAND 0 /* This option switches f_expand function. (0:Disable or 1:Enable) */ -#define _USE_CHMOD 0 +#define _USE_CHMOD 1 /* This option switches attribute manipulation functions, f_chmod() and f_utime(). / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ diff --git a/Src/control.c b/Src/control.c index 56c3519..ef06c70 100644 --- a/Src/control.c +++ b/Src/control.c @@ -114,16 +114,12 @@ void handle_io_read() { // Read byte from disk case 0x08: if (control.storage.ready && control.storage.command == 0x20) { - if (control.storage.counter < control.storage.size) { - write_data(control.storage.buffer[control.storage.counter]); - control.storage.counter++; - } else { - write_data(0x00); - } + write_data(control.storage.buffer[control.storage.counter]); + control.storage.counter++; if (control.storage.counter >= control.storage.size) { - control.storage.ready = 0; control.storage.command = 0; + control.storage.ready = 0; } } else { write_data(0x00); @@ -132,80 +128,29 @@ void handle_io_read() { // Check if disk is ready case 0x0f: - if (control.storage.command == 0x20 && !control.storage.ready) { - switch (control.storage.type) { - case TYPE_NONE: - control.storage.size = 0; - control.storage.ready = 1; - break; - - case TYPE_FILE: { - FRESULT fr = f_lseek(control.storage.file, control.storage.offset * CPM_RECORD_SIZE); - if (fr) { - printf("File error: %i\n\r", fr); - } else { - control.storage.ready = 1; - } - - fr = f_read(control.storage.file, control.storage.buffer, CPM_RECORD_SIZE, &control.storage.size); - if (fr) { - printf("File error: %i\n\r", fr); - } else { - control.storage.ready = 1; - } - - break; - } - - case TYPE_DIR: { - for (int i = 0; i < 128; i += 32) { - control.storage.buffer[i] = 0xe5; - } - - for (int i = 0; i < 4; ++i) { - if (4*control.storage.offset + i >= control.storage.A.entry_count) { - break; - } - - control.storage.buffer[32*i + 0] = 0x00; - for (int j = 0; j < 8; ++j) { - control.storage.buffer[32*i + 1 + j] = control.storage.A.entries[4*control.storage.offset + i].name[j]; - } - - for (int j = 0; j < 3; ++j) { - control.storage.buffer[32*i + 9 + j] = control.storage.A.entries[4*control.storage.offset + i].ext[j]; - } - - uint16_t extents = 0x00; - uint16_t records = control.storage.A.entries[4*control.storage.offset + i].size; - - while (records >= 0x80) { - extents++; - records -= 0x80; - } - - control.storage.buffer[32*i + 12] = extents & 0xFF; // Extent low - control.storage.buffer[32*i + 13] = 0x00; // Reserved 0x00 - control.storage.buffer[32*i + 14] = (extents >> 8) & 0xFF; // Extent high - control.storage.buffer[32*i + 15] = records; - - for (int j = 0; j < 16; ++j) { - control.storage.buffer[32*i + 16 + j] = 0x00; - } - - for (int j = 0; j < (extents+1); ++j) { - control.storage.buffer[32*i + 16 + j*2] = control.storage.A.entries[4*control.storage.offset + i].start + j; - control.storage.buffer[32*i + 17 + j*2] = 0x00; - } - } - - control.storage.size = 128; - control.storage.ready = 1; - - break; - } - + if (control.storage.command == 0x30 && !control.storage.ready) { + printf("Write: 0x%lx\n\r", control.storage.lba * CPM_RECORD_SIZE); + for (int i = 0; i < 128; ++i) { + control.storage.buffer[i] = 0x00; } + control.storage.ready = 1; + } + + if (control.storage.command == 0x20 && !control.storage.ready) { + printf("Read: 0x%lx\n\r", control.storage.lba * CPM_RECORD_SIZE); + FRESULT fr = f_lseek(control.storage.file, control.storage.lba * CPM_RECORD_SIZE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_read(control.storage.file, control.storage.buffer, CPM_RECORD_SIZE, &control.storage.size); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + control.storage.ready = 1; } write_data(0x08*control.storage.ready); @@ -242,6 +187,145 @@ void handle_io_write() { // Write byte to disk case 0x08: + if (control.storage.ready && control.storage.command == 0x30) { + control.storage.buffer[control.storage.counter] = value; + control.storage.counter++; + + // Commit changes + if (control.storage.counter == CPM_RECORD_SIZE) { + FRESULT fr = f_lseek(control.storage.file, control.storage.lba * CPM_RECORD_SIZE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + unsigned int bytes_written; + fr = f_write(control.storage.file, control.storage.buffer, CPM_RECORD_SIZE, &bytes_written); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + if (control.storage.lba >= 0x100 && control.storage.lba < 0x180) { + for (int i = 0; i < 4; ++i) { + DiskEntry entry; + + entry.user = control.storage.buffer[32*i + 0]; + printf("User: %i\n\r", entry.user); + + for (int j = 0; j < 8; ++j) { + char c = control.storage.buffer[32*i + 1 + j]; + if (c == ' ') { + c = 0x00; + } + entry.name[j] = c; + } + printf("Name: %.8s\n\r", entry.name); + + for (int j = 0; j < 3; ++j) { + char c = control.storage.buffer[32*i + 9 + j]; + if (c == ' ') { + c = 0x00; + } + entry.ext[j] = c; + } + printf("Ext: %.3s\n\r", entry.ext); + + entry.size = control.storage.buffer[32*i + 15]; + + for (int j = 0; j < 8; ++j) { + uint16_t allocation = control.storage.buffer[32*i + 2*j + 16]; + allocation += control.storage.buffer[32*i + 2*j + 17] << 8; + entry.allocation[j] = allocation; + printf("Allocation: %i\n\r", allocation); + if (j && allocation) { + entry.size += 0x80; + } + } + printf("Size: %li records (%li)\n\r", entry.size, entry.size * CPM_RECORD_SIZE); + + char filename[128]; + snprintf(filename, 128, "0:A/%.8s.%.3s", entry.name, entry.ext); + FIL file; + if (entry.user == 0xe5) { + FILINFO nfo; + fr = f_stat(filename, &nfo); + if (fr == FR_OK) { + printf("Removing file %s\n\r", filename); + fr = f_unlink(filename); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + } + } else { + printf("Opening %s\n\r", filename); + fr = f_open(&file, filename, FA_WRITE | FA_CREATE_ALWAYS); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + for (int j = 0; j < 8; ++j) { + if (entry.allocation[j] == 0) { + continue; + } + + uint8_t buffer[CPM_BLOCK_SIZE] = {0}; + + printf("Copying allocation %i (%i) to %s\n\r", j, entry.allocation[j], filename); + + fr = f_lseek(control.storage.file, 0x8000 + entry.allocation[j]*CPM_BLOCK_SIZE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + unsigned int bytes_read; + fr = f_read(control.storage.file, buffer, entry.size * CPM_RECORD_SIZE, &bytes_read); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + printf("Bytes read: %i\n\r", bytes_read); + + fr = f_lseek(&file, j*CPM_BLOCK_SIZE); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + unsigned int bytes_written; + fr = f_write(&file, buffer, bytes_read, &bytes_written); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + printf("Bytes written: %i\n\r", bytes_written); + + f_sync(control.storage.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; + } + } + } + // @todo Verify that this is how it works + // If there was a write here a file got modified + printf("File(s) in record %li (0x%lx) changed\n\r", control.storage.lba, control.storage.lba * CPM_RECORD_SIZE); + } + + control.storage.command = 0; + control.storage.ready = 0; + } + } break; case 0x0b: { @@ -267,59 +351,6 @@ void handle_io_write() { control.storage.ready = 0; control.storage.counter = 0; control.storage.command = value; - FRESULT fr = 0; - - if (control.storage.type == TYPE_FILE) { - fr = f_close(control.storage.file); - if (fr) { - printf("File error: %i\n\r", fr); - } - } - - // @todo We need to make it easier to set this up - if (control.storage.lba == 0) { - // bootloader - control.storage.offset = control.storage.lba - 0; - fr = f_open(control.storage.file, "0:loader.bin", FA_READ); - control.storage.type = TYPE_FILE; - } else if (control.storage.lba >= 1 && control.storage.lba < 45) { - // cpm - control.storage.offset = control.storage.lba - 1; - fr = f_open(control.storage.file, "0:cpm22.bin", FA_READ); - control.storage.type = TYPE_FILE; - } else if (control.storage.lba >= 45 && control.storage.lba < 51) { - // bios - control.storage.offset = control.storage.lba - 45; - fr = f_open(control.storage.file, "0:bios.bin", FA_READ); - control.storage.type = TYPE_FILE; - } else if (control.storage.lba >= 256 && control.storage.lba < (256+32)) { - // @todo Max out at end of disk A - control.storage.offset = control.storage.lba - 256; - control.storage.type = TYPE_DIR; - } else if (control.storage.lba > (256+32)) { - control.storage.type = TYPE_NONE; - - for (uint8_t i = 0; i < control.storage.A.entry_count; ++i) { - if (control.storage.lba >= (control.storage.A.entries[i].start-1)*CPM_RECODS_PER_BLOCK+384 && control.storage.lba < ((control.storage.A.entries[i].start-1)*CPM_RECODS_PER_BLOCK+384 + control.storage.A.entries[i].size)) { - control.storage.offset = control.storage.lba - (control.storage.A.entries[i].start-1)*CPM_RECODS_PER_BLOCK - 384; - - char buf[128]; - snprintf(buf, 128, "0:A/%s", control.storage.A.entries[i].filename); - fr = f_open(control.storage.file, buf, FA_READ); - - control.storage.type = TYPE_FILE; - break; - } - } - } else { - // Empty - printf("Unknown %li\n\r", control.storage.lba); - control.storage.type = TYPE_NONE; - } - - if (fr) { - printf("File error: %i\n\r", fr); - } break; } @@ -363,39 +394,77 @@ void control_cycle() { set_clock(0); } -// @todo Properly reset everything -void control_reset() { - free(control.storage.buffer); - - // @todo Pretty sure this is not actually correct, - // since the pointer is always not NULL once we malloc, - // even if we do not have a file open/disk mounted - if (control.storage.file) { - f_close(control.storage.file); - free(control.storage.file); - } - if (control.storage.fs) { - f_mount(0, "0:", 0); - free(control.storage.fs); - } - - Control temp = {0, {NULL, NULL, 0, 0, 0, 0, 0, NULL, 0, TYPE_NONE, {0}}}; - control = temp; - - control.storage.buffer = (uint8_t*)malloc(CPM_RECORD_SIZE); - - control.storage.fs = (FATFS*)malloc(sizeof(FATFS)); - control.storage.file = (FIL*)malloc(sizeof(FIL)); - FRESULT fr = f_mount(control.storage.fs, "0:", 0); +void copy_file_to_image(char* filename, FIL* disk, uint32_t source, uint32_t destination, uint32_t size) { + FIL file; + FRESULT fr = f_open(&file, filename, FA_READ); if (fr) { printf("File error: %i\n\r", fr); + return; + } + + fr = f_lseek(&file, source); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + uint8_t* buffer = malloc(size); + unsigned int bytes_read; + fr = f_read(&file, buffer, size, &bytes_read); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_lseek(disk, destination); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + printf("copying: %s %lx @ %lx (%i)\n\r", filename, source, destination, bytes_read); + + unsigned int bytes_written; + fr = f_write(disk, buffer, bytes_read, &bytes_written); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + fr = f_close(&file); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + free(buffer); +} + +DiskEntry* get_disk_entries(char* diskpath) { + printf("Scanning: %s\n\r", diskpath); + DiskEntry* entries = malloc(127*sizeof(DiskEntry)); + + // Clear all entries + for (int i = 0; i < 127; ++i) { + entries[i].user = 0xe5; + for (int j = 0; j < 8; ++j) { + entries[i].name[j] = 0x00; + } + for (int j = 0; j < 3; ++j) { + entries[i].ext[j] = 0x00; + } + entries[i].size = 0; + for (int j = 0; j < 8; ++j) { + entries[i].allocation[j] = 0x00; + } } DIR dir; FILINFO fno; - fr = f_opendir(&dir, "0:A"); - control.storage.A.end = 1; + FRESULT fr = f_opendir(&dir, diskpath); if (fr == FR_OK) { + uint16_t end = 1; + uint8_t index = 0; for (;;) { fr = f_readdir(&dir, &fno); if (fr != FR_OK || fno.fname[0] == 0) { @@ -403,20 +472,27 @@ void control_reset() { } if (!(fno.fattrib & AM_DIR)) { - uint8_t index = control.storage.A.entry_count; - control.storage.A.entries[index].start = control.storage.A.end; + entries[index].user = 0x00; // Calculate the number of records uint32_t size = (fno.fsize + CPM_RECORD_SIZE - 1) / CPM_RECORD_SIZE; - // Increment the end by the number of blocks - control.storage.A.end += (size + CPM_RECODS_PER_BLOCK) / CPM_RECODS_PER_BLOCK; + entries[index].allocation[0] = end; + end++; + + int i = 1; + while (size >= 0x80) { + size -= 0x80; + entries[index].allocation[i] = end; + end++; + i++; + } // Store the size in the number of records - control.storage.A.entries[index].size = size; + entries[index].size = size; + /* free(entries[index].filename); */ + entries[index].filename = strdup(fno.fname); - control.storage.A.entries[index].filename = strdup(fno.fname); - uint8_t name_len = 9; for (uint8_t i = 0; i < 8; ++i) { char c = ' '; @@ -427,7 +503,7 @@ void control_reset() { name_len = i+1; } - control.storage.A.entries[index].name[i] = c; + entries[index].name[i] = c; } uint8_t done = 0; @@ -440,15 +516,131 @@ void control_reset() { done = 1; } - control.storage.A.entries[index].ext[i] = c; + entries[index].ext[i] = c; } - control.storage.A.entry_count++; + index++; + } + } + } else { + printf("File error: %i\n\r", fr); + return entries; + } + + fr = f_closedir(&dir); + if (fr) { + printf("File error: %i\n\r", fr); + return entries; + } + + return entries; +} + +// @todo Properly reset everything +void control_reset() { + free(control.storage.buffer); + + f_close(control.storage.file); + free(control.storage.file); + + f_mount(0, "0:", 0); + free(control.storage.fs); + + Control temp = {0, {NULL, NULL, 0, 0, 0, 0, NULL, 0}}; + control = temp; + + control.storage.buffer = (uint8_t*)malloc(CPM_RECORD_SIZE); + + control.storage.fs = (FATFS*)malloc(sizeof(FATFS)); + control.storage.file = (FIL*)malloc(sizeof(FIL)); + + FRESULT fr = f_mount(control.storage.fs, "0:", 0); + if (fr) { + printf("File error: %i\n\r", fr); + } + + // Open the image file + fr = f_open(control.storage.file, "0:A.img", FA_READ | FA_WRITE | FA_CREATE_ALWAYS); + if (fr) { + printf("File error: %i\n\r", fr); + } + + fr = f_chmod("0:A.img", AM_HID, 0xFF); + if (fr) { + printf("File error: %i\n\r", fr); + } + + copy_file_to_image("0:loader.bin", control.storage.file, 0x00, 0x00, 0x80); + copy_file_to_image("0:cpm22.bin", control.storage.file, 0x00, 0x80, 0x1600); + copy_file_to_image("0:bios.bin", control.storage.file, 0x00, 0x1680, 0x600); + + f_lseek(control.storage.file, 0x8000); + + DiskEntry* entries = get_disk_entries("0:A"); + for (int i = 0; i < 127; ++i) { + char buffer[32]; + + buffer[0] = entries[i].user; + for (int j = 0; j < 8; ++j) { + buffer[1+j] = entries[i].name[j]; + } + + for (int j = 0; j < 3; ++j) { + buffer[9+j] = entries[i].ext[j]; + } + + uint16_t extents = 0x00; + for (int j = 1; j < 8; ++j) { + if (entries[i].allocation[j]) { + extents++; + } + } + + buffer[12] = extents & 0xFF; + buffer[13] = 0x00; + buffer[14] = (extents >> 8) & 0xFF; + buffer[15] = entries[i].size; + + for (int j = 0; j < 8; ++j) { + uint16_t allocation = entries[i].allocation[j]; + buffer[16 + j*2] = allocation & 0xFF; + buffer[17 + j*2] = (allocation >> 8) & 0xFF; + } + + unsigned int bytes_written; + fr = f_write(control.storage.file, buffer, 32, &bytes_written); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + } + + for (int i = 0; i < 127; ++i) { + if (entries[i].user == 0xe5) { + continue; + } + + char buf[128]; + snprintf(buf, 128, "0:A/%s", entries[i].filename); + for (int j = 0; j < 8; ++j) { + uint16_t allocation = entries[i].allocation[j]; + if (allocation) { + printf("allocation: %s (%i)\n\r", buf, allocation); + copy_file_to_image(buf, control.storage.file, j*CPM_BLOCK_SIZE, 0x8000+allocation*CPM_BLOCK_SIZE, CPM_BLOCK_SIZE); } } } + f_sync(control.storage.file); + if (fr) { + printf("File error: %i\n\r", fr); + return; + } + + // @todo Pretty sure we have to free things within DiskEntry + free(entries); + set_reset(1); for (int i = 0; i <= 10; ++i) { set_clock(i % 2);