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);