diff --git a/Makefile b/Makefile index 49dd3dd..9860708 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ CPP_SRC = $(wildcard *.cpp) \ $(wildcard ./support/neogeo/*.cpp) \ $(wildcard ./support/arcade/*.cpp) \ $(wildcard ./support/megacd/*.cpp) \ + $(wildcard ./support/c64/*.cpp) \ lib/lodepng/lodepng.cpp IMG = $(wildcard *.png) diff --git a/file_io.cpp b/file_io.cpp index 71826ec..f96fac3 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -82,10 +82,10 @@ static int FileIsZipped(char* path, char** zip_path, char** file_path) return 0; } -static char* make_fullpath(const char *path, int mode = 0) +static char* make_fullpath(const char *path, int mode = 0, bool absolutePath = false) { const char *root = getRootDir(); - if (strncasecmp(getRootDir(), path, strlen(root))) + if (!absolutePath && strncasecmp(getRootDir(), path, strlen(root))) { sprintf(full_path, "%s/%s", (mode == -1) ? "" : root, path); } @@ -253,9 +253,9 @@ static int zip_search_by_crc(mz_zip_archive *zipArchive, uint32_t crc32) return -1; } -int FileOpenZip(fileTYPE *file, const char *name, uint32_t crc32) +int FileOpenZip(fileTYPE *file, const char *name, uint32_t crc32, bool absolutePath) { - make_fullpath(name); + make_fullpath(name, 0, absolutePath); FileClose(file); file->mode = 0; file->type = 0; @@ -317,9 +317,9 @@ int FileOpenZip(fileTYPE *file, const char *name, uint32_t crc32) return 1; } -int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute) +int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute, bool absolutePath) { - make_fullpath((char*)name, mode); + make_fullpath((char*)name, mode, absolutePath); FileClose(file); file->mode = 0; file->type = 0; @@ -461,9 +461,9 @@ __off64_t FileGetSize(fileTYPE *file) return 0; } -int FileOpen(fileTYPE *file, const char *name, char mute) +int FileOpen(fileTYPE *file, const char *name, char mute, bool absolutePath) { - return FileOpenEx(file, name, O_RDONLY, mute); + return FileOpenEx(file, name, O_RDONLY, mute, absolutePath); } int FileSeek(fileTYPE *file, __off64_t offset, int origin) diff --git a/file_io.h b/file_io.h index 853d036..87b9ae0 100644 --- a/file_io.h +++ b/file_io.h @@ -59,9 +59,9 @@ int getStorage(int from_setting); void setStorage(int dev); int isUSBMounted(); -int FileOpenZip(fileTYPE *file, const char *name, uint32_t crc32); -int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute = 0); -int FileOpen(fileTYPE *file, const char *name, char mute = 0); +int FileOpenZip(fileTYPE *file, const char *name, uint32_t crc32, bool absolutePath = false); +int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute = 0, bool absolutePath = false); +int FileOpen(fileTYPE *file, const char *name, char mute = 0, bool absolutePath = false); void FileClose(fileTYPE *file); __off64_t FileGetSize(fileTYPE *file); diff --git a/menu.cpp b/menu.cpp index c660ffb..8f06400 100644 --- a/menu.cpp +++ b/menu.cpp @@ -1727,8 +1727,9 @@ void HandleUI(void) } else { - user_io_set_index(user_io_ext_idx(SelectedPath, fs_pFileExt) << 6 | (menusub + 1)); - user_io_file_mount(SelectedPath, ioctl_index); + unsigned char ioctl_subindex = user_io_ext_idx(SelectedPath, fs_pFileExt); + user_io_set_index(ioctl_subindex << 6 | (menusub + 1)); + user_io_file_mount(SelectedPath, ioctl_index, 0, ioctl_subindex); } menustate = SelectedPath[0] ? MENU_NONE1 : MENU_8BIT_MAIN1; diff --git a/support.h b/support.h index 0050092..553bf42 100644 --- a/support.h +++ b/support.h @@ -28,3 +28,6 @@ // MEGACD support #include "support/megacd/megacd.h" + +// C64 support +#include "support/c64/c64.h" diff --git a/support/c64/c64.cpp b/support/c64/c64.cpp new file mode 100644 index 0000000..38d8eab --- /dev/null +++ b/support/c64/c64.cpp @@ -0,0 +1,281 @@ +#include +#include +#include +#include + +#include +#include + +#include "../../file_io.h" +#include "../../hardware.h" + +#define CBM_PRG 0x82 + +#define T64_BYTE_PER_HEADER 32U +#define T64_FILL_VALUE 0x20 + +#define D64_FILE_STRIDE 10U +#define D64_DIR_STRIDE 3U + +#define D64_TRACK_PER_DISK 35U +#define D64_SECTOR_PER_DISK 683U +#define D64_FILE_SECTOR_PER_DISK (D64_SECTOR_PER_DISK - 19U) +#define D64_BYTE_PER_DIR 32U +#define D64_BYTE_PER_SECTOR 256U +#define D64_BYTE_PER_FILE_SECTOR 254U +#define D64_DIR_PER_SECTOR 8U +#define D64_FILE_PER_DISK 144U +#define D64_BYTE_PER_BAM_ENTRY 4U +#define D64_BYTE_PER_STRING 16U + +#define D64_BAM_TRACK 18U +#define D64_BAM_SECTOR 0U +#define D64_DIR_TRACK 18U +#define D64_DIR_SECTOR 1U +#define D64_FILE_TRACK 1U // 1-35 +#define D64_FILE_SECTOR 0U + +#define D64_FILL_VALUE 0xA0 +#define D64_INIT_VALUE 0x00 // DIR relies on 0x00 initial value + +// leave file for debug +#define CHECK_SUCCESS(op) do { if (!(op)) { FileClose(&fd); /*unlink(path);*/ return 0; } } while(0) + +struct FileRecord { + char name[D64_BYTE_PER_STRING]; + unsigned char cbm; + unsigned short start; + unsigned short size; + unsigned offset; + unsigned index; +}; + +static bool cmp_offset(const FileRecord& a, const FileRecord& b) { return a.offset < b.offset; } +static bool cmp_index(const FileRecord& a, const FileRecord& b) { return a.index < b.index; } + +static unsigned char d64_sector_per_track(unsigned char trackNum) { + return (trackNum <= 17) ? 21U + : (trackNum <= 24) ? 19U + : (trackNum <= 30) ? 18U + : 17U; +} + +static unsigned d64_file_sector(unsigned size) { + return (size + D64_BYTE_PER_FILE_SECTOR - 1) / D64_BYTE_PER_FILE_SECTOR; +} + +static unsigned d64_offset(unsigned char trackNum, unsigned char sectorNum) +{ + static unsigned TrackFileOffset[D64_TRACK_PER_DISK] = { + 0x00000, /* 1 */ 0x01500, /* 2 */ 0x02A00, /* 3 */ 0x03F00, /* 4 */ 0x05400, /* 5 */ + 0x06900, /* 6 */ 0x07E00, /* 7 */ 0x09300, /* 8 */ 0x0A800, /* 9 */ 0x0BD00, /* 10 */ + 0x0D200, /* 11 */ 0x0E700, /* 12 */ 0x0FC00, /* 13 */ 0x11100, /* 14 */ 0x12600, /* 15 */ + 0x13B00, /* 16 */ 0x15000, /* 17 */ 0x16500, /* 18 */ 0x17800, /* 19 */ 0x18B00, /* 20 */ + 0x19E00, /* 21 */ 0x1B100, /* 22 */ 0x1C400, /* 23 */ 0x1D700, /* 24 */ 0x1EA00, /* 25 */ + 0x1FC00, /* 26 */ 0x20E00, /* 27 */ 0x22000, /* 28 */ 0x23200, /* 29 */ 0x24400, /* 30 */ + 0x25600, /* 31 */ 0x26700, /* 32 */ 0x27800, /* 33 */ 0x28900, /* 34 */ 0x29A00, /* 35 */ + }; + + return TrackFileOffset[trackNum - 1] + sectorNum * D64_BYTE_PER_SECTOR; +} + +static void d64_advance_pointer(unsigned char& trackNum, unsigned char& sectorNum) { + unsigned stride = trackNum == D64_DIR_TRACK ? D64_DIR_STRIDE : D64_FILE_STRIDE; + unsigned sectorPerTrack = d64_sector_per_track(trackNum); + + sectorNum = (sectorNum + stride) % sectorPerTrack; + // 18 sectors with a stride of 10 has a common divisor of 2. NOTE: this doesn't handle all combinations of sectorsPerTrack and stride + if (!(sectorPerTrack & 1) && !(stride & 1) && !(sectorNum >> 1)) sectorNum ^= 1; + // caller needs to handle DIR track + if (!sectorNum) trackNum = trackNum % D64_TRACK_PER_DISK + 1; +} + +static void d64_advance_dir_pointer(unsigned char& trackNum, unsigned char& sectorNum) { + unsigned stride = trackNum == D64_DIR_TRACK ? D64_DIR_STRIDE : D64_FILE_STRIDE; + unsigned sectorPerTrack = d64_sector_per_track(trackNum); + + // don't leave current track + sectorNum = (sectorNum + stride) % sectorPerTrack; + // caller needs to handle BAM sector +} + +int c64_convert_t64_to_d64(fileTYPE* f, const char *path) +{ + fileTYPE fd; + std::vector files; + char header[T64_BYTE_PER_HEADER]; + char name[D64_BYTE_PER_STRING]; + + // remove old file to make sure we don't use it + unlink(path); + + CHECK_SUCCESS(FileSeek(f, 0, SEEK_SET)); + // ignore signature + CHECK_SUCCESS(FileReadAdv(f, header, sizeof(header))); + CHECK_SUCCESS(!memcmp(header, "C64", strlen("C64"))); + // header + CHECK_SUCCESS(FileReadAdv(f, header, sizeof(header))); + + unsigned short numRecords = (header[0x03] << 8) | header[0x02]; + memcpy(name, header + 0x08, sizeof(name)); + + for (unsigned i = 0; i < numRecords; i++) + { + // record + CHECK_SUCCESS(FileReadAdv(f, header, sizeof(header))); + if (!header[0x00] || !header[0x01]) continue; + + FileRecord r; + r.cbm = CBM_PRG; // header[0x01]; + r.start = (header[0x03] << 8) | header[0x02]; + r.size = ((header[0x05] << 8) | header[0x04]) - r.start; + r.offset = (header[0x0B] << 24) | (header[0x0A] << 16) | (header[0x09] << 8) | header[0x08]; + memcpy(r.name, header + 0x10, sizeof(r.name)); + r.index = i; + + files.push_back(r); + } + + // workaround incorrect end/limit address + std::sort(files.begin(), files.end(), cmp_offset); + for (unsigned i = 0; i < files.size(); i++) + { + unsigned short size = (i < files.size() - 1 ? files[i + 1].offset : f->size) - files[i].offset; + if (size < files[i].size) files[i].size = size; + } + std::sort(files.begin(), files.end(), cmp_index); + + // account for start address in PRG format + for (unsigned i = 0; i < files.size(); i++) files[i].size += 2; + + // drop files until they fit + if (files.size() > D64_FILE_PER_DISK) files.resize(D64_FILE_PER_DISK); + for (unsigned i = 0, fileSectors = 0; i < files.size(); i++) + { + fileSectors += d64_file_sector(files[i].size); + if (fileSectors > D64_FILE_SECTOR_PER_DISK) + { + files.resize(i); + break; + } + } + + //printf("T64: %d records\n", files.size()); for (auto r : files) printf("start: %x, size: %x, offset %x, index: %d\n", r.start, r.size, r.offset, r.index); + + CHECK_SUCCESS(files.size()); + + CHECK_SUCCESS(FileOpenEx(&fd, path, O_CREAT | O_TRUNC | O_RDWR | O_SYNC, 0, true)); + + unsigned char sector[D64_BYTE_PER_SECTOR]; + memset(sector, D64_INIT_VALUE, sizeof(sector)); + for (unsigned i = 0; i < D64_SECTOR_PER_DISK; i++) CHECK_SUCCESS(FileWriteAdv(&fd, sector, sizeof(sector))); + CHECK_SUCCESS(FileSeek(&fd, 0, SEEK_SET)); + + unsigned char bam[D64_BYTE_PER_SECTOR]; + memset(bam, D64_INIT_VALUE, sizeof(bam)); + + unsigned char dir[D64_BYTE_PER_DIR]; + + unsigned char fileTrackNum = D64_FILE_TRACK, fileSectorNum = D64_FILE_SECTOR, dirTrackNum = D64_DIR_TRACK, dirSectorNum = D64_DIR_SECTOR, dirEntry = 0; + for (unsigned i = 0; i < files.size(); i++) { + FileRecord& r = files[i]; + + // DIR sector + if (dirEntry == 0 && i != 0) + { + // set next track/sector pointer of prev node + CHECK_SUCCESS(FileSeek(&fd, d64_offset(dirTrackNum, dirSectorNum), SEEK_SET)); + do d64_advance_dir_pointer(dirTrackNum, dirSectorNum); while (dirTrackNum == D64_BAM_TRACK && dirSectorNum == D64_BAM_SECTOR); + dir[0x00] = dirTrackNum; + dir[0x01] = dirSectorNum; + CHECK_SUCCESS(FileWriteAdv(&fd, dir, 2)); + + // check for overflow + bool success = dirSectorNum != D64_DIR_SECTOR; + if (!success) printf("T64: dir overflow on file: %d\n", i); + CHECK_SUCCESS(success); + } + + CHECK_SUCCESS(FileSeek(&fd, d64_offset(dirTrackNum, dirSectorNum) + dirEntry * D64_BYTE_PER_DIR, SEEK_SET)); + dir[0x00] = 0x00; + dir[0x01] = dirEntry == 0 ? 0xFF : 0x00; + dir[0x02] = r.cbm; + dir[0x03] = fileTrackNum; + dir[0x04] = fileSectorNum; + memcpy(dir + 0x05, r.name, sizeof(r.name)); + for (int o = sizeof(r.name) - 1; o >= 0; o--) if (dir[0x05 + o] != T64_FILL_VALUE) break; else dir[0x05 + o] = D64_FILL_VALUE; + memset(dir + 0x15, 0x00, 0x17 - 0x15 + 1); // REL + memset(dir + 0x18, 0x00, 0x1D - 0x18 + 1); + unsigned sectorSize= d64_file_sector(files[i].size); + dir[0x1E] = (sectorSize >> 0) & 0xFF; + dir[0x1F] = (sectorSize >> 8) & 0xFF; + CHECK_SUCCESS(FileWriteAdv(&fd, dir, sizeof(dir))); + + dirEntry = (dirEntry + 1) % D64_DIR_PER_SECTOR; + + // file sectors + CHECK_SUCCESS(FileSeek(f, r.offset, SEEK_SET)); + bool writeAddress = true; + for (unsigned s = 0; s < r.size; s+= D64_BYTE_PER_FILE_SECTOR) + { + CHECK_SUCCESS(FileSeek(&fd, d64_offset(fileTrackNum, fileSectorNum), SEEK_SET)); + d64_advance_pointer(fileTrackNum, fileSectorNum); + if (fileTrackNum == D64_DIR_TRACK) fileTrackNum += 1; + memset(sector, D64_FILL_VALUE, sizeof(sector)); + + unsigned cnt = std::min(D64_BYTE_PER_FILE_SECTOR, r.size - s); + sector[0x00] = (s + D64_BYTE_PER_FILE_SECTOR < r.size) ? fileTrackNum : 0x00; + sector[0x01] = (s + D64_BYTE_PER_FILE_SECTOR < r.size) ? fileSectorNum : (cnt + 2 - 1); + if (writeAddress) + { + sector[0x02] = (r.start >> 0) & 0xFF; + sector[0x03] = (r.start >> 8) & 0xFF; + } + CHECK_SUCCESS(FileReadAdv(f, sector + (writeAddress ? 4 : 2), cnt - (writeAddress ? 2 : 0))); + + CHECK_SUCCESS(FileWriteAdv(&fd, sector, sizeof(sector))); + writeAddress = false; + } + } + + // BAM + bam[0x00] = D64_DIR_TRACK; + bam[0x01] = D64_DIR_SECTOR; + bam[0x02] = 0x41; + bam[0x03] = 0x00; + // set available + while (true) { + d64_advance_pointer(fileTrackNum, fileSectorNum); + if (fileTrackNum == D64_FILE_TRACK && fileSectorNum == D64_FILE_SECTOR) break; + if (fileTrackNum == D64_DIR_TRACK) continue; + + bam[0x04 + (fileTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY] += 1; + bam[0x04 + (fileTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY + 1 + fileSectorNum / 8] |= 1 << (fileSectorNum % 8); + }; + while (true) { + d64_advance_dir_pointer(dirTrackNum, dirSectorNum); + if (dirTrackNum == D64_DIR_TRACK && dirSectorNum == D64_DIR_SECTOR) break; + if (dirTrackNum == D64_BAM_TRACK && dirSectorNum == D64_BAM_SECTOR) continue; + + bam[0x04 + (dirTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY] += 1; + bam[0x04 + (dirTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY + 1 + dirSectorNum / 8] |= 1 << (dirSectorNum % 8); + } + memcpy(bam + 0x90, name, sizeof(name)); + for (int o = sizeof(name) - 1; o >= 0; o--) if (bam[0x90 + o] != T64_FILL_VALUE) break; else bam[0x90 + o] = D64_FILL_VALUE; + bam[0xA0] = D64_FILL_VALUE; + bam[0xA1] = D64_FILL_VALUE; + bam[0xA2] = 0x30; + bam[0xA3] = 0x30; + bam[0xA4] = D64_FILL_VALUE; + bam[0xA5] = 0x32; + bam[0xA6] = 0x41; + memset(bam + 0xA7, D64_FILL_VALUE, 0xAA - 0xA7 + 1); + memset(bam + 0xAB, 0x00, 0xFF - 0xAB + 1); + + CHECK_SUCCESS(FileSeek(&fd, d64_offset(D64_BAM_TRACK, D64_BAM_SECTOR), SEEK_SET)); + CHECK_SUCCESS(FileWriteAdv(&fd, bam, sizeof(bam))); + + FileClose(&fd); + + return 1; +} \ No newline at end of file diff --git a/support/c64/c64.h b/support/c64/c64.h new file mode 100644 index 0000000..70d3bca --- /dev/null +++ b/support/c64/c64.h @@ -0,0 +1,6 @@ +#ifndef C64_H +#define C64_H + +int c64_convert_t64_to_d64(fileTYPE* f, const char *path); + +#endif diff --git a/user_io.cpp b/user_io.cpp index d7c95e5..eef208e 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -256,6 +256,13 @@ char is_gba_core() return (is_gba_type == 1); } +static int is_c64_type = 0; +char is_c64_core() +{ + if (!is_c64_type) is_c64_type = strcasecmp(core_name, "C64") ? 2 : 1; + return (is_c64_type == 1); +} + char is_sharpmz() { return(core_type == CORE_TYPE_SHARPMZ); @@ -1180,7 +1187,7 @@ void user_io_set_download(unsigned char enable) DisableFpga(); } -int user_io_file_mount(char *name, unsigned char index, char pre) +int user_io_file_mount(char *name, unsigned char index, char pre, unsigned char subindex) { int writable = 0; int ret = 0; @@ -1238,6 +1245,24 @@ int user_io_file_mount(char *name, unsigned char index, char pre) strcpy(sd_image[index].path, name); } + if (is_c64_core()) + { + if (subindex == 1) { + // mount T64 + size = 0; + writable = false; + const char* path = "/tmp/c64_t64_tmp.d64"; + ret = c64_convert_t64_to_d64(&sd_image[index], path); + FileClose(&sd_image[index]); + + if (ret) + { + ret = FileOpenEx(&sd_image[index], path, O_RDONLY, 0, true); + size = sd_image[index].size; + } + } + } + if (io_ver) { spi_w((uint16_t)(size)); diff --git a/user_io.h b/user_io.h index 5aa9e54..0e7c654 100644 --- a/user_io.h +++ b/user_io.h @@ -216,7 +216,7 @@ void user_io_file_tx_write(const uint8_t *addr, uint16_t len); int user_io_get_width(); uint32_t user_io_get_file_crc(); -int user_io_file_mount(char *name, unsigned char index = 0, char pre = 0); +int user_io_file_mount(char *name, unsigned char index = 0, char pre = 0, unsigned char subindex = 0); char user_io_serial_status(serial_status_t *, uint8_t); char *user_io_make_filepath(const char *path, const char *filename); char *user_io_get_core_name();