From b33d930bfabb9cf7a7bc544029f4870edc25fa63 Mon Sep 17 00:00:00 2001 From: sorgelig Date: Wed, 20 Nov 2019 00:15:29 +0800 Subject: [PATCH] Add MegaCD support code. --- Makefile | 1 + MiSTer.vcxproj | 3 + MiSTer.vcxproj.filters | 9 + menu.cpp | 6 + support.h | 5 +- support/megacd/cdd.cpp | 884 ++++++++++++++++++++++++++++++++++++++ support/megacd/megacd.cpp | 219 ++++++++++ support/megacd/megacd.h | 120 ++++++ user_io.cpp | 11 + user_io.h | 3 + 10 files changed, 1260 insertions(+), 1 deletion(-) create mode 100644 support/megacd/cdd.cpp create mode 100644 support/megacd/megacd.cpp create mode 100644 support/megacd/megacd.h diff --git a/Makefile b/Makefile index 34ea846..ccbddd3 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,7 @@ CPP_SRC = $(wildcard *.cpp) \ $(wildcard ./support/x86/*.cpp) \ $(wildcard ./support/snes/*.cpp) \ $(wildcard ./support/neogeo/*.cpp) \ + $(wildcard ./support/megacd/*.cpp) \ lib/lodepng/lodepng.cpp IMG = $(wildcard *.png) diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj index e54964b..3845d6c 100644 --- a/MiSTer.vcxproj +++ b/MiSTer.vcxproj @@ -75,6 +75,8 @@ + + @@ -126,6 +128,7 @@ + diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters index 9c04bcb..3209fb7 100644 --- a/MiSTer.vcxproj.filters +++ b/MiSTer.vcxproj.filters @@ -148,6 +148,12 @@ Source Files + + Source Files + + + Source Files + @@ -300,5 +306,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/menu.cpp b/menu.cpp index 946983b..c132190 100644 --- a/menu.cpp +++ b/menu.cpp @@ -1575,6 +1575,8 @@ void HandleUI(void) } else { + if (mask == 1 && is_megacd_core()) mcd_set_image(0, ""); + uint32_t status = user_io_8bit_set_status(0, 0, ex); user_io_8bit_set_status(status ^ mask, mask, ex); @@ -1621,6 +1623,10 @@ void HandleUI(void) { x86_set_image(drive_num, SelectedPath); } + else if (is_megacd_core()) + { + mcd_set_image(drive_num, SelectedPath); + } else { user_io_set_index(user_io_ext_idx(SelectedPath, fs_pFileExt) << 6 | (menusub + 1)); diff --git a/support.h b/support.h index ad5ca18..bc3c52d 100644 --- a/support.h +++ b/support.h @@ -21,4 +21,7 @@ #include "support/snes/snes.h" // NeoGeo support -#include "support/neogeo/loader.h" \ No newline at end of file +#include "support/neogeo/loader.h" + +// MEGACD support +#include "support/megacd/megacd.h" \ No newline at end of file diff --git a/support/megacd/cdd.cpp b/support/megacd/cdd.cpp new file mode 100644 index 0000000..1129506 --- /dev/null +++ b/support/megacd/cdd.cpp @@ -0,0 +1,884 @@ + +#include +#include +#include +#include + +#include "../../file_io.h" +#include "megacd.h" + + +cdd_t cdd; + + +uint32_t frame = 0; + +cdd_t::cdd_t() { + cycles = 0; + latency = 10; + type = 0; + loaded = 0; + index = 0; + lba = 0; + scanOffset = 0; + isData = 1; + status = CD_STAT_NO_DISC; + audioLength = 0; + audioOffset = 0; + + stat[0] = 0xB; + stat[1] = 0x0; + stat[2] = 0x0; + stat[3] = 0x0; + stat[4] = 0x0; + stat[5] = 0x0; + stat[6] = 0x0; + stat[7] = 0x0; + stat[8] = 0x0; + stat[9] = 0x4; +} + + +int cdd_t::LoadCUE(const char* filename) { + char fname[1024 + 10]; + char line[128]; + char *ptr, *lptr; + char header[1024]; + FILE *fd; + + strcpy(fname, filename); + + if (!(fd = fopen(getFullPath(fname), "r"))) + return -1; + + printf("\x1b[32mMCD: Open CUE: %s\n\x1b[0m", fname); + + int mm, ss, bb, pregap = 0; + + while (fgets(line, 128, fd)) + { + lptr = line; + while (*lptr == 0x20) lptr++; + + /* decode FILE commands */ + if (!(memcmp(lptr, "FILE", 4))) + { + ptr = fname + strlen(fname) - 1; + while ((ptr - fname) && (*ptr != '/') && (*ptr != '\\')) ptr--; + if (ptr - fname) ptr++; + + lptr += 4; + while (*lptr == 0x20) lptr++; + + if (*lptr == '\"') + { + lptr++; + while ((*lptr != '\"') && (lptr <= (line + 128)) && (ptr < (fname + 1023))) + *ptr++ = *lptr++; + } + else + { + while ((*lptr != 0x20) && (lptr <= (line + 128)) && (ptr < (fname + 1023))) + *ptr++ = *lptr++; + } + *ptr = 0; + + this->toc.tracks[this->toc.last].fd = fopen(getFullPath(fname), "r"); + if (!this->toc.tracks[this->toc.last].fd) + return -1; + + printf("\x1b[32mMCD: Open track file: %s\n\x1b[0m", fname); + + pregap = 0; + + this->toc.tracks[this->toc.last].offset = 0; + + if (!strstr(lptr, "BINARY") && !strstr(lptr, "MOTOROLA")) + { + fclose(this->toc.tracks[this->toc.last].fd); + this->toc.tracks[this->toc.last].fd = 0; + + printf("\x1b[32mMCD: unsupported file: %s\n\x1b[0m", fname); + + return -1; + } + } + + /* decode TRACK commands */ + else if ((sscanf(lptr, "TRACK %02d %*s", &bb)) || (sscanf(lptr, "TRACK %d %*s", &bb))) + { + if (bb != (this->toc.last + 1)) + { + if (this->toc.tracks[this->toc.last].fd) + { + fclose(this->toc.tracks[this->toc.last].fd); + this->toc.tracks[this->toc.last].fd = 0; + } + + printf("\x1b[32mMCD: missing tracks: %s\n\x1b[0m", fname); + + break; + } + + if (!this->toc.last) + { + if (strstr(lptr, "MODE1/2048")) + { + this->sectorSize = 2048; + } + else if (strstr(lptr, "MODE1/2352")) + { + this->sectorSize = 2352; + + fseek(this->toc.tracks[0].fd, 0x10, SEEK_SET); + } + + if (this->sectorSize) + { + this->toc.tracks[0].type = 1; + + fread(header, 0x210, 1, this->toc.tracks[0].fd); + fseek(this->toc.tracks[0].fd, 0, SEEK_SET); + } + } + else + { + if (!this->toc.tracks[this->toc.last].fd) + { + this->toc.tracks[this->toc.last - 1].end = 0; + } + } + } + + /* decode PREGAP commands */ + else if (sscanf(lptr, "PREGAP %02d:%02d:%02d", &mm, &ss, &bb) == 3) + { + pregap += bb + ss * 75 + mm * 60 * 75; + } + + /* decode INDEX commands */ + else if ((sscanf(lptr, "INDEX 00 %02d:%02d:%02d", &mm, &ss, &bb) == 3) || + (sscanf(lptr, "INDEX 0 %02d:%02d:%02d", &mm, &ss, &bb) == 3)) + { + if (this->toc.last && !this->toc.tracks[this->toc.last - 1].end) + { + this->toc.tracks[this->toc.last - 1].end = bb + ss * 75 + mm * 60 * 75 + pregap; + } + } + else if ((sscanf(lptr, "INDEX 01 %02d:%02d:%02d", &mm, &ss, &bb) == 3) || + (sscanf(lptr, "INDEX 1 %02d:%02d:%02d", &mm, &ss, &bb) == 3)) + { + this->toc.tracks[this->toc.last].offset += pregap * 2352; + + if (!this->toc.tracks[this->toc.last].fd) + { + this->toc.tracks[this->toc.last].fd = this->toc.tracks[0].fd; + this->toc.tracks[this->toc.last].start = bb + ss * 75 + mm * 60 * 75 + pregap; + if (this->toc.last && !this->toc.tracks[this->toc.last - 1].end) + { + this->toc.tracks[this->toc.last - 1].end = this->toc.tracks[this->toc.last].start; + } + } + else + { + this->toc.tracks[this->toc.last].start = this->toc.end + pregap; + this->toc.tracks[this->toc.last].offset += this->toc.end * 2352; + + { + fseek(this->toc.tracks[this->toc.last].fd, 0, SEEK_END); + if (this->toc.tracks[this->toc.last].type) + { + this->toc.tracks[this->toc.last].end = this->toc.tracks[this->toc.last].start + ((ftell(this->toc.tracks[this->toc.last].fd) + this->sectorSize - 1) / this->sectorSize); + } + else + { + this->toc.tracks[this->toc.last].end = this->toc.tracks[this->toc.last].start + ((ftell(this->toc.tracks[this->toc.last].fd) + 2351) / 2352); + } + fseek(this->toc.tracks[this->toc.last].fd, 0, SEEK_SET); + } + + this->toc.tracks[this->toc.last].start += (bb + ss * 75 + mm * 60 * 75); + this->toc.end = this->toc.tracks[this->toc.last].end; + } + + printf("\x1b[32mMCD: Track = %u, start = %u, end = %u, offset = %u, type = %u\n\x1b[0m", cdd.toc.last, cdd.toc.tracks[cdd.toc.last].start, cdd.toc.tracks[cdd.toc.last].end, cdd.toc.tracks[cdd.toc.last].offset, cdd.toc.tracks[cdd.toc.last].type); + + this->toc.last++; + if (this->toc.last == 99) break; + } + } + + if (this->toc.last && !this->toc.tracks[this->toc.last - 1].end) + { + this->toc.end += pregap; + this->toc.tracks[this->toc.last - 1].end = this->toc.end; + } + + if (this->toc.tracks[this->toc.last].fd) + { + fclose(this->toc.tracks[this->toc.last].fd); + } + + fclose(fd); + + return 0; +} + +int cdd_t::Load(const char *filename) +{ + char fname[1024 + 10]; + char header[1024]; + FILE *fd_img; + + Unload(); + + if (LoadCUE(filename)) { + return (-1); + } + + fd_img = this->toc.tracks[0].fd; + + + fseek(fd_img, 0, SEEK_SET); + fread(header, 0x10, 1, fd_img); + + if (!memcmp("SEGADISCSYSTEM", header, 14)) + { + this->sectorSize = 2048; + } + else + { + fread(header, 0x10, 1, fd_img); + if (!memcmp("SEGADISCSYSTEM", header, 14)) + { + this->sectorSize = 2352; + } + } + + if (this->sectorSize) + { + fread(header + 0x10, 0x200, 1, fd_img); + fseek(fd_img, 0, SEEK_SET); + } + else + { + fclose(fd_img); + + return (-1); + } + + printf("\x1b[32mMCD: Sector size = %u, Track 0 end = %u\n\x1b[0m", this->sectorSize, this->toc.tracks[0].end); + + if (this->toc.last) + { + this->toc.tracks[this->toc.last].start = this->toc.end; + this->loaded = 1; + + memcpy(&fname[strlen(fname) - 4], ".sub", 4); + this->toc.sub = fopen(getFullPath(fname), "r"); + + printf("\x1b[32mMCD: CD mounted , last track = %u\n\x1b[0m", this->toc.last); + + return 1; + } + + return 0; +} + +void cdd_t::Unload() +{ + if (this->loaded) + { + for (int i = 0; i < this->toc.last; i++) + { + if (this->toc.tracks[i].fd) + { + if ((i > 0) && (this->toc.tracks[i].fd == this->toc.tracks[i - 1].fd)) + { + i++; + } + else + { + fclose(this->toc.tracks[i].fd); + } + } + } + + if (this->toc.sub) { + fclose(this->toc.sub); + } + this->loaded = 0; + } + + memset(&this->toc, 0x00, sizeof(this->toc)); + this->sectorSize = 0; +} + +void cdd_t::Update() { + if (this->status == CD_STAT_STOP || this->status == CD_STAT_TRAY || this->status == CD_STAT_OPEN) + { + if (this->latency > 0) + { + this->latency--; + return; + } + this->status = this->loaded ? CD_STAT_TOC : CD_STAT_NO_DISC; + } + else if (this->status == CD_STAT_SEEK) + { + if (this->latency > 0) + { + this->latency--; + return; + } + this->status = CD_STAT_PAUSE; + } + else if (this->status == CD_STAT_PLAY) + { + if (this->latency > 0) + { + this->latency--; + return; + } + + if (this->index >= this->toc.last) + { + this->status = CD_STAT_END; + return; + } + + if (this->toc.sub) + { + //mcd_sub_send(); + } + + if (this->toc.tracks[this->index].type) + { + // CD-ROM (Mode 1) + uint8_t header[4]; + msf_t msf; + LBAToMSF(this->lba + 150, &msf); + header[0] = BCD(msf.m); + header[1] = BCD(msf.s); + header[2] = BCD(msf.f); + header[3] = 0x01; + + SectorSend(header); + + //printf("\x1b[32mMCD: Data sector send = %i, frame = %u\n\x1b[0m", this->lba, frame); + } + else + { + if (this->lba >= this->toc.tracks[this->index].start) + { + this->isData = 0x00; + } + + SectorSend(0); + } + + this->lba++; + + if (this->lba >= this->toc.tracks[this->index].end) + { + this->index++; + + this->isData = 0x01; + + if (this->toc.tracks[this->index].fd) + { + fseek(this->toc.tracks[this->index].fd, (this->toc.tracks[this->index].start * 2352) - this->toc.tracks[this->index].offset, SEEK_SET); + } + } + } + else if (cdd.status == CD_STAT_SCAN) + { + this->lba += this->scanOffset; + + if (this->lba >= this->toc.tracks[this->index].end) + { + this->index++; + if (this->index < this->toc.last) + { + this->lba = this->toc.tracks[this->index].start; + } + else + { + this->lba = this->toc.end; + this->status = CD_STAT_END; + this->isData = 0x01; + return; + } + } + else if (this->lba < this->toc.tracks[this->index].start) + { + if (this->index > 0) + { + this->index--; + this->lba = this->toc.tracks[this->index].end; + } + else + { + this->lba = 0; + } + } + + this->isData = this->toc.tracks[this->index].type; + + if (this->toc.sub) + { + fseek(this->toc.sub, this->lba * 96, SEEK_SET); + } + + if (this->toc.tracks[this->index].type) + { + // DATA track + fseek(this->toc.tracks[0].fd, this->lba * this->sectorSize, SEEK_SET); + } + else if (this->toc.tracks[this->index].fd) + { + // AUDIO track + fseek(this->toc.tracks[this->index].fd, (this->lba * 2352) - this->toc.tracks[this->index].offset, SEEK_SET); + } + } +} + +void cdd_t::CommandExec() { + msf_t msf; + + switch (comm[0]) { + case CD_COMM_IDLE: + stat[0] = this->status; + + if (stat[1] == 0x0f) + { + if (!this->latency) + { + stat[0] = this->status; + stat[1] = 0x2; + stat[2] = (cdd.index < this->toc.last) ? BCD(this->index + 1) >> 4 : 0xA; + stat[3] = (cdd.index < this->toc.last) ? BCD(this->index + 1) & 0xF : 0xA; + } + } + + //printf("MCD: Command IDLE, status = %u\n\x1b[0m", this->status); + break; + + case CD_COMM_STOP: + this->status = CD_STAT_STOP; + this->isData = 1; + + stat[0] = this->status; + stat[1] = 0; + stat[2] = 0; + stat[3] = 0; + stat[4] = 0; + stat[5] = 0; + stat[6] = 0; + stat[7] = 0; + stat[8] = 0; + + //printf("\x1b[32mMCD: Command STOP, status = %u, frame = %u\n\x1b[0m", this->status, frame); + break; + + case CD_COMM_TOC: + switch (comm[3]) { + case 0: { + int lba_ = this->lba + 150; + LBAToMSF(lba_, &msf); + + stat[0] = this->status; + stat[1] = 0x0; + stat[2] = BCD(msf.m) >> 4; + stat[3] = BCD(msf.m) & 0xF; + stat[4] = BCD(msf.s) >> 4; + stat[5] = BCD(msf.s) & 0xF; + stat[6] = BCD(msf.f) >> 4; + stat[7] = BCD(msf.f) & 0xF; + stat[8] = this->toc.tracks[this->index].type << 2; + + //printf("\x1b[32mMCD: Command TOC 0, lba = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, status = %02X%08X, frame = %u\n\x1b[0m", lba, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], (uint32_t)(GetStatus() >> 32), (uint32_t)GetStatus(), frame); + } + break; + + case 1: { + int lba_ = abs(this->lba - this->toc.tracks[this->index].start); + LBAToMSF(lba_, &msf); + + stat[0] = this->status; + stat[1] = 0x1; + stat[2] = BCD(msf.m) >> 4; + stat[3] = BCD(msf.m) & 0xF; + stat[4] = BCD(msf.s) >> 4; + stat[5] = BCD(msf.s) & 0xF; + stat[6] = BCD(msf.f) >> 4; + stat[7] = BCD(msf.f) & 0xF; + stat[8] = this->toc.tracks[this->index].type << 2; + + //printf("\x1b[32mMCD: Command TOC 1, lba = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, status = %02X%08X, frame = %u\n\x1b[0m", lba, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], (uint32_t)(GetStatus() >> 32), (uint32_t)GetStatus(), frame); + } + break; + + case 2: { + stat[0] = this->status; + stat[1] = 0x2; + stat[2] = ((this->index < this->toc.last) ? BCD(this->index + 1) >> 4 : 0xA); + stat[3] = ((this->index < this->toc.last) ? BCD(this->index + 1) & 0xF : 0xA); + stat[4] = 0; + stat[5] = 0; + stat[6] = 0; + stat[7] = 0; + stat[8] = 0; + + //printf("\x1b[32mMCD: Command TOC 2, index = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, status = %02X%08X, frame = %u\n\x1b[0m", this->index, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], (uint32_t)(GetStatus() >> 32), (uint32_t)GetStatus(), frame); + } + break; + + case 3: { + int lba_ = this->toc.end + 150; + LBAToMSF(lba_, &msf); + + stat[0] = this->status; + stat[1] = 0x3; + stat[2] = BCD(msf.m) >> 4; + stat[3] = BCD(msf.m) & 0xF; + stat[4] = BCD(msf.s) >> 4; + stat[5] = BCD(msf.s) & 0xF; + stat[6] = BCD(msf.f) >> 4; + stat[7] = BCD(msf.f) & 0xF; + stat[8] = 0; + + //printf("\x1b[32mMCD: Command TOC 3, lba = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, frame = %u\n\x1b[0m", lba, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], frame); + } + break; + + case 4: { + stat[0] = this->status; + stat[1] = 0x4; + stat[2] = 0; + stat[3] = 1; + stat[4] = BCD(this->toc.last) >> 4; + stat[5] = BCD(this->toc.last) & 0xF; + stat[6] = 0; + stat[7] = 0; + stat[8] = 0; + + //printf("\x1b[32mMCD: Command TOC 4, last = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, frame = %u\n\x1b[0m", this->toc.last, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], frame); + } + break; + + case 5: { + int track = comm[4] * 10 + comm[5]; + int lba_ = this->toc.tracks[track - 1].start + 150; + LBAToMSF(lba_, &msf); + + stat[0] = this->status; + stat[1] = 0x5; + stat[2] = BCD(msf.m) >> 4; + stat[3] = BCD(msf.m) & 0xF; + stat[4] = BCD(msf.s) >> 4; + stat[5] = BCD(msf.s) & 0xF; + stat[6] = (BCD(msf.f) >> 4) | (this->toc.tracks[track - 1].type << 3); + stat[7] = BCD(msf.f) & 0xF; + stat[8] = BCD(track) & 0xF; + + //printf("\x1b[32mMCD: Command TOC 5, lba = %i, track = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, frame = %u\n\x1b[0m", lba_, track, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], frame); + } + break; + + case 6: + stat[0] = this->status; + stat[1] = 0x6; + stat[2] = 0; + stat[3] = 0; + stat[4] = 0; + stat[5] = 0; + stat[6] = 0; + stat[7] = 0; + stat[8] = 0; + break; + + default: + + break; + } + break; + + case CD_COMM_PLAY: { + int index = 0; + int lba_; + MSFToLBA(&lba_, comm[2] * 10 + comm[3], comm[4] * 10 + comm[5], comm[6] * 10 + comm[7]); + lba_ -= 150; + + if (!this->latency) + { + this->latency = 11; + } + + this->latency += abs(((lba_ - this->lba) * 120) / 270000); + + this->lba = lba_; + + while ((this->toc.tracks[index].end <= lba_) && (index < this->toc.last)) index++; + this->index = index; + if (lba_ < this->toc.tracks[index].start) + { + lba_ = this->toc.tracks[index].start; + } + + if (this->toc.tracks[index].type) + { + /* DATA track */ + fseek(this->toc.tracks[0].fd, lba_ * this->sectorSize, SEEK_SET); + } + else if (cdd.toc.tracks[index].fd) + { + /* PCM AUDIO track */ + fseek(this->toc.tracks[index].fd, (lba_ * 2352) - this->toc.tracks[index].offset, SEEK_SET); + } + + this->audioOffset = 0; + + if (this->toc.sub) + { + fseek(this->toc.sub, lba_ * 96, SEEK_SET); + } + + this->isData = 1; + + this->status = CD_STAT_PLAY; + + stat[0] = this->status; + stat[1] = 0xf; + stat[2] = 0; + stat[3] = 0; + stat[4] = 0; + stat[5] = 0; + stat[6] = 0; + stat[7] = 0; + stat[8] = 0; + + //printf("\x1b[32mMCD: Command PLAY, lba = %i, index = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, frame = %u\n\x1b[0m", lba_, this->index, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], frame); + } + break; + + case CD_COMM_SEEK: { + int index = 0; + int lba_; + MSFToLBA(&lba_, comm[2] * 10 + comm[3], comm[4] * 10 + comm[5], comm[6] * 10 + comm[7]); + lba_ -= 150; + + this->latency = abs((lba_ - this->lba) * 120) / 270000; + + this->lba = lba_; + + while ((this->toc.tracks[index].end <= lba_) && (index < this->toc.last)) index++; + this->index = index; + + if (lba_ < this->toc.tracks[index].start) + { + lba_ = this->toc.tracks[index].start; + } + + if (this->toc.tracks[index].type) + { + // DATA track + fseek(this->toc.tracks[0].fd, lba_ * this->sectorSize, SEEK_SET); + + } + else if (this->toc.tracks[index].fd) + { + // AUDIO track + fseek(this->toc.tracks[index].fd, (lba_ * 2352) - this->toc.tracks[index].offset, SEEK_SET); + } + + //this->audioOffset = 0; + + if (this->toc.sub) + { + fseek(this->toc.sub, lba_ * 96, SEEK_SET); + } + + this->isData = 1; + + this->status = CD_STAT_SEEK; + + stat[0] = this->status; + stat[1] = 0xf; + stat[2] = 0; + stat[3] = 0; + stat[4] = 0; + stat[5] = 0; + stat[6] = 0; + stat[7] = 0; + stat[8] = 0; + + //printf("\x1b[32mMCD: Command PLAY, lba = %i, index = %i, command = %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, frame = %u\n\x1b[0m", lba_, this->index, comm[9], comm[8], comm[7], comm[6], comm[5], comm[4], comm[3], comm[2], comm[1], comm[0], frame); + } + break; + + case CD_COMM_PAUSE: + this->isData = 0x01; + + this->status = CD_STAT_PAUSE; + + stat[0] = this->status; + + //printf("\x1b[32mMCD: Command PAUSE, status = %X, frame = %u\n\x1b[0m", this->status, frame); + break; + + case CD_COMM_RESUME: + this->status = CD_STAT_PLAY; + stat[0] = this->status; + this->audioOffset = 0; + + printf("\x1b[32mMCD: Command RESUME, status = %X, frame = %u\n\x1b[0m", this->status, frame); + break; + + case CD_COMM_FW_SCAN: + this->scanOffset = CD_SCAN_SPEED; + this->status = CD_STAT_SCAN; + stat[0] = this->status; + break; + + case CD_COMM_RW_SCAN: + this->scanOffset = -CD_SCAN_SPEED; + this->status = CD_STAT_SCAN; + stat[0] = this->status; + break; + + case CD_COMM_TRACK_MOVE: + this->isData = 1; + this->status = CD_STAT_PAUSE; + stat[0] = this->status; + break; + + case CD_COMM_TRAY_CLOSE: + this->isData = 1; + this->status = this->loaded ? CD_STAT_TOC : CD_STAT_NO_DISC; + stat[0] = CD_STAT_STOP; + + //printf("\x1b[32mMCD: Command TRAY_CLOSE, status = %u, frame = %u\n\x1b[0m", this->status, frame); + break; + + case CD_COMM_TRAY_OPEN: + this->isData = 1; + this->status = CD_STAT_OPEN; + stat[0] = CD_STAT_OPEN; + + //printf("\x1b[32mMCD: Command TRAY_OPEN, status = %u, frame = %u\n\x1b[0m", this->status, frame); + break; + + default: + stat[0] = this->status; + + //printf("\x1b[32mMCD: Command undefined, status = %u, frame = %u\n\x1b[0m", this->status, frame); + break; + } +} + +uint64_t cdd_t::GetStatus() { + uint8_t n9 = ~(stat[0] + stat[1] + stat[2] + stat[3] + stat[4] + stat[5] + stat[6] + stat[7] + stat[8]); + return ((uint64_t)(n9 & 0xF) << 36) | + ((uint64_t)(stat[8] & 0xF) << 32) | + ((uint64_t)(stat[7] & 0xF) << 28) | + ((uint64_t)(stat[6] & 0xF) << 24) | + ((uint64_t)(stat[5] & 0xF) << 20) | + ((uint64_t)(stat[4] & 0xF) << 16) | + ((uint64_t)(stat[3] & 0xF) << 12) | + ((uint64_t)(stat[2] & 0xF) << 8) | + ((uint64_t)(stat[1] & 0xF) << 4) | + ((uint64_t)(stat[0] & 0xF) << 0); +} + +int cdd_t::SetCommand(uint64_t c) { + comm[0] = (c >> 0) & 0xF; + comm[1] = (c >> 4) & 0xF; + comm[2] = (c >> 8) & 0xF; + comm[3] = (c >> 12) & 0xF; + comm[4] = (c >> 16) & 0xF; + comm[5] = (c >> 20) & 0xF; + comm[6] = (c >> 24) & 0xF; + comm[7] = (c >> 28) & 0xF; + comm[8] = (c >> 32) & 0xF; + comm[9] = (c >> 36) & 0xF; + + uint8_t crc = (~(comm[0] + comm[1] + comm[2] + comm[3] + comm[4] + comm[5] + comm[6] + comm[7] + comm[8])) & 0xF; + if (comm[9] != crc) + return -1; + + return 0; +} + +void cdd_t::LBAToMSF(int lba, msf_t* msf) { + msf->m = (lba / 75) / 60; + msf->s = (lba / 75) % 60; + msf->f = (lba % 75); +} + +void cdd_t::MSFToLBA(int* lba, uint8_t m, uint8_t s, uint8_t f) { + *lba = f + s * 75 + m * 60 * 75; +} + +void cdd_t::MSFToLBA(int* lba, msf_t* msf) { + *lba = msf->f + msf->s * 75 + msf->m * 60 * 75; +} + + + + +void cdd_t::ReadData(uint8_t *buf) +{ + if (this->toc.tracks[this->index].type && (this->lba >= 0)) + { + if (this->sectorSize == 2048) + { + fseek(this->toc.tracks[0].fd, this->lba * 2048, SEEK_SET); + } + else + { + fseek(this->toc.tracks[0].fd, this->lba * 2352 + 16, SEEK_SET); + } + + fread(buf, 2048, 1, this->toc.tracks[0].fd); + } +} + +int cdd_t::ReadCDDA(uint8_t *buf) +{ + this->audioLength = 2352 + 2352 - this->audioOffset; + this->audioOffset = 2352; + + if (!this->isData && this->toc.tracks[this->index].fd) + { + fread(buf, this->audioLength, 1, this->toc.tracks[this->index].fd); + } + + return this->audioLength; +} + +void cdd_t::ReadSubcode(uint16_t* buf) +{ + uint8_t subc[96]; + int i, j, n; + + fread(subc, 96, 1, this->toc.sub); + + for (i = 0, n = 0; i < 96; i += 2, n++) + { + int code = 0; + for (j = 0; j < 8; j++) + { + int bits = (subc[(j * 12) + (i / 8)] >> (6 - (i & 6))) & 3; + code |= ((bits & 1) << (7 - j)); + code |= ((bits >> 1) << (15 - j)); + } + + buf[n] = code; + } +} + + + + + diff --git a/support/megacd/megacd.cpp b/support/megacd/megacd.cpp new file mode 100644 index 0000000..201a607 --- /dev/null +++ b/support/megacd/megacd.cpp @@ -0,0 +1,219 @@ + +#include +#include +#include +#include + +#include "../../file_io.h" +#include "../../user_io.h" +#include "../../spi.h" +#include "../../hardware.h" +#include "megacd.h" + + +int loaded = 0, unloaded = 0; +static uint8_t has_command = 0; + +void mcd_poll() +{ + static uint32_t poll_timer = 0, stat_timer = 0; + static uint8_t last_req = 255; + static uint8_t adj = 0; + + if (!stat_timer || CheckTimer(stat_timer)) + { + stat_timer = GetTimer(15); + + if (has_command) { + spi_uio_cmd_cont(UIO_CD_SET); + uint64_t s = cdd.GetStatus(); + spi_w((s >> 0) & 0xFFFF); + spi_w((s >> 16) & 0xFFFF); + spi_w(((s >> 32) & 0x00FF) | (cdd.isData ? 0x01 << 8 : 0x00 << 8)); + DisableIO(); + + has_command = 0; + + //printf("\x1b[32mMCD: Send status, status = %04X%04X%04X, frame = %u\n\x1b[0m", (uint16_t)((s >> 32) & 0x00FF), (uint16_t)((s >> 16) & 0xFFFF), (uint16_t)((s >> 0) & 0xFFFF), frame); + } + + } + + + uint8_t req = spi_uio_cmd_cont(UIO_CD_GET); + if (req != last_req) + { + last_req = req; + + uint16_t data_in[4]; + data_in[0] = spi_w(0); + data_in[1] = spi_w(0); + data_in[2] = spi_w(0); + DisableIO(); + + uint64_t c = *((uint64_t*)(data_in)); + cdd.SetCommand(c); + cdd.CommandExec(); + has_command = 1; + + + //printf("\x1b[32mMCD: Get command, command = %04X%04X%04X, has_command = %u\n\x1b[0m", data_in[2], data_in[1], data_in[0], has_command); + } + else + DisableIO(); + + if (!poll_timer || CheckTimer(poll_timer)) + { + poll_timer = GetTimer(13 + (!adj ? 1 : 0)); + if (++adj >= 3) adj = 0; + + cdd.Update(); + } + + + //static uint8_t state = 0; + // + //if (!poll_timer || CheckTimer(poll_timer)) + //{ + // if (!state) { + // poll_timer = GetTimer(5 + (!adj ? 1 : 0)); + + // if (++adj >= 4) adj = 0; + + // if (has_command) { + // uint64_t s = cdd.GetStatus(); + // spi_uio_cmd_cont(UIO_CD_SET); + // spi_w((s >> 0) & 0xFFFF); + // spi_w((s >> 16) & 0xFFFF); + // spi_w(((s >> 32) & 0x00FF) | (cdd.isData ? 0x01 << 8 : 0x00 << 8)); + // DisableIO(); + // //printf("\x1b[32mMCD: Send status, status = %02X%08X, frame = %u\n\x1b[0m", (uint32_t)(status >> 32), (uint32_t)status, frame); + // } + + // frame++; + // } + // else { + // poll_timer = GetTimer(8); + + // cdd.Update(); + + // uint16_t data_in[4]; + + // uint8_t req = spi_uio_cmd_cont(UIO_CD_GET); + // //get the data from FPGA + // data_in[0] = spi_w(0); + // data_in[1] = spi_w(0); + // data_in[2] = spi_w(0); + // DisableIO(); + + // has_command = 0; + // if (req != last_req) + // { + // last_req = req; + + // uint64_t c = *((uint64_t*)(data_in)); + + // cdd.CommandExec(c); + + // has_command = 1; + + // //printf("\x1b[32mMCD: Receive command, command = %02X%08X, frame = %u\n\x1b[0m", (uint32_t)(c >> 32), (uint32_t)c, frame); + // } + // } + + // state = ~state; + //} +} + + +void mcd_set_image(int num, const char *filename) +{ + (void)num; + + if (*filename) { + + if (cdd.Load(filename) > 0) { + loaded = 1; + cdd.status = cdd.loaded ? CD_STAT_STOP : CD_STAT_NO_DISC; + cdd.latency = 10; + //status = MakeStatus(CD_STAT_STOP, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0); + } + else { + cdd.status = CD_STAT_NO_DISC; + } + } + else { + cdd.Unload(); + unloaded = 1; + cdd.status = CD_STAT_OPEN; + } +} + + +int cdd_t::SectorSend(uint8_t* header) +{ + uint8_t buf[2352+2352]; + int len = 2352; + + if (header) { + memcpy(buf + 12, header, 4); + ReadData(buf + 16); + } + else { + len = ReadCDDA(buf); + } + + // set index byte + user_io_set_index(CD_DATA_IO_INDEX); + + // prepare transmission of new file + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0xff); + DisableFpga(); + + EnableFpga(); + spi8(UIO_FILE_TX_DAT); + spi_write(buf, len, 1); + DisableFpga(); + + // signal end of transmission + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0x00); + DisableFpga(); + + return 1; +} + + +int cdd_t::SubcodeSend() +{ + uint16_t buf[98/2]; + + ReadSubcode(buf); + + // set index byte + user_io_set_index(CD_SUB_IO_INDEX); + + // prepare transmission of new file + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0xff); + DisableFpga(); + + EnableFpga(); + spi8(UIO_FILE_TX_DAT); + spi_write((uint8_t*)buf, 98, 1); + DisableFpga(); + + // signal end of transmission + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0x00); + DisableFpga(); + + return 1; +} + + diff --git a/support/megacd/megacd.h b/support/megacd/megacd.h new file mode 100644 index 0000000..ada06fd --- /dev/null +++ b/support/megacd/megacd.h @@ -0,0 +1,120 @@ +#ifndef MEGACD_H +#define MEGACD_H + + +// CDD status +#define CD_STAT_STOP 0x00 +#define CD_STAT_PLAY 0x01 +#define CD_STAT_SEEK 0x02 +#define CD_STAT_SCAN 0x03 +#define CD_STAT_PAUSE 0x04 +#define CD_STAT_OPEN 0x05 +#define CD_STAT_NO_VALID_CHK 0x06 +#define CD_STAT_NO_VALID_CMD 0x07 +#define CD_STAT_ERROR 0x08 +#define CD_STAT_TOC 0x09 +#define CD_STAT_TRACK_MOVE 0x0A +#define CD_STAT_NO_DISC 0x0B +#define CD_STAT_END 0x0C +#define CD_STAT_TRAY 0x0E +#define CD_STAT_TEST 0x0F + +// CDD command +#define CD_COMM_IDLE 0x00 +#define CD_COMM_STOP 0x01 +#define CD_COMM_TOC 0x02 +#define CD_COMM_PLAY 0x03 +#define CD_COMM_SEEK 0x04 +//#define CD_COMM_OPEN 0x05 +#define CD_COMM_PAUSE 0x06 +#define CD_COMM_RESUME 0x07 +#define CD_COMM_FW_SCAN 0x08 +#define CD_COMM_RW_SCAN 0x09 +#define CD_COMM_TRACK_MOVE 0x0A +//#define CD_COMM_NO_DISC 0x0B +#define CD_COMM_TRAY_CLOSE 0x0C +#define CD_COMM_TRAY_OPEN 0x0D + +typedef struct +{ + FILE *fd; + int offset; + int start; + int end; + int type; +} track_t; + +typedef struct +{ + int end; + int last; + track_t tracks[100]; + FILE *sub; +} toc_t; + +typedef struct +{ + uint8_t m; + uint8_t s; + uint8_t f; +} msf_t; + +class cdd_t +{ +public: + uint32_t latency; + uint8_t status; + uint8_t isData; + int loaded; + + cdd_t(); + int Load(const char *filename); + void Unload(); + void Update(); + void CommandExec(); + int SectorSend(uint8_t* header); + int SubcodeSend(); + + uint64_t GetStatus(); + int SetCommand(uint64_t c); + +private: + toc_t toc; + int index; + int lba; + uint32_t cycles; + int type; + uint16_t sectorSize; + int scanOffset; + int audioLength; + int audioOffset; + + uint8_t stat[10]; + uint8_t comm[10]; + + int LoadCUE(const char* filename); + void ReadData(uint8_t *buf); + int ReadCDDA(uint8_t *buf); + void ReadSubcode(uint16_t* buf); + void LBAToMSF(int lba, msf_t* msf); + void MSFToLBA(int* lba, msf_t* msf); + void MSFToLBA(int* lba, uint8_t m, uint8_t s, uint8_t f); +}; + +#define BCD(v) ((uint8_t)((((v)/10) << 4) | ((v)%10))) + +#define CD_SCAN_SPEED 30 + +#define CD_DATA_IO_INDEX 2 +#define CD_SUB_IO_INDEX 3 + + +//cdd.cpp +extern cdd_t cdd; +extern uint32_t frame; + + +void mcd_poll(); +void mcd_set_image(int num, const char *filename); + +#endif diff --git a/user_io.cpp b/user_io.cpp index 54b866c..29bfbd7 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -294,6 +294,13 @@ char is_minimig() return (is_minimig_type == 1); } +static int is_megacd_type = 0; +char is_megacd_core() +{ + if (!is_megacd_type) is_megacd_type = strcasecmp(core_name, "MEGACD") ? 2 : 1; + return (is_megacd_type == 1); +} + static int is_no_type = 0; static int disable_osd = 0; char has_menu() @@ -1826,6 +1833,8 @@ void user_io_send_buttons(char force) minimig_reset(); } + if (is_megacd_core()) mcd_set_image(0, ""); + key_map = map; spi_uio_cmd16(UIO_BUT_SW, map); printf("sending keymap: %X\n", map); @@ -2835,6 +2844,8 @@ void user_io_poll() fpga_set_led(0); diskled_is_on = 0; } + + if (is_megacd_core()) mcd_poll(); } static void send_keycode(unsigned short key, int press) diff --git a/user_io.h b/user_io.h index a9e705d..73f3265 100644 --- a/user_io.h +++ b/user_io.h @@ -72,6 +72,8 @@ #define UIO_SET_MEMSZ 0x31 // Send memory size to the core #define UIO_SET_GAMMA 0x32 // Enable/disable Gamma correction #define UIO_SET_GAMCURV 0x33 // Set Gamma curve +#define UIO_CD_GET 0x34 +#define UIO_CD_SET 0x35 // codes as used by 8bit for file loading from OSD #define UIO_FILE_TX 0x53 @@ -214,6 +216,7 @@ char *user_io_make_filepath(const char *path, const char *filename); char *user_io_get_core_name(); char *user_io_get_core_path(); const char *user_io_get_core_name_ex(); +char is_megacd_core(); char has_menu(); const char *get_image_name(int i);