From 646f96c395997ad81c842c4af1e97f5680eb7a38 Mon Sep 17 00:00:00 2001 From: paulb-nl Date: Thu, 6 Apr 2023 17:27:39 +0200 Subject: [PATCH] Add NeoGeo CD support (#767) support/megacdd: add Play track command for Neo Geo neocd: use separate index for audio --- menu.cpp | 6 ++ osd.cpp | 1 + support.h | 2 + support/megacd/megacd.cpp | 4 +- support/megacd/megacd.h | 10 ++- support/megacd/megacdd.cpp | 159 ++++++++++++++++++++---------------- support/neogeo/neogeocd.cpp | 135 ++++++++++++++++++++++++++++++ support/neogeo/neogeocd.h | 11 +++ user_io.cpp | 6 ++ user_io.h | 1 + 10 files changed, 258 insertions(+), 77 deletions(-) create mode 100644 support/neogeo/neogeocd.cpp create mode 100644 support/neogeo/neogeocd.h diff --git a/menu.cpp b/menu.cpp index 1458f8f..26587e1 100644 --- a/menu.cpp +++ b/menu.cpp @@ -2267,6 +2267,7 @@ void HandleUI(void) if (fs_Options & SCANO_NEOGEO) { + neocd_set_en(0); neogeo_romset_tx(selPath); } else @@ -2331,6 +2332,11 @@ void HandleUI(void) { saturn_set_image(ioctl_index, selPath); } + else if (is_neogeo()) + { + neocd_set_en(1); + neocd_set_image(selPath); + } else { user_io_set_index(user_io_ext_idx(selPath, fs_pFileExt) << 6 | (menusub + 1)); diff --git a/osd.cpp b/osd.cpp index cabd1ac..d29c61a 100644 --- a/osd.cpp +++ b/osd.cpp @@ -674,6 +674,7 @@ void OsdUpdate() if (is_megacd()) mcd_poll(); if (is_pce()) pcecd_poll(); if (is_saturn()) saturn_poll(); + if (is_neogeo_cd()) neocd_poll(); } } diff --git a/support.h b/support.h index 65d11bb..521799d 100644 --- a/support.h +++ b/support.h @@ -22,6 +22,8 @@ // NeoGeo support #include "support/neogeo/neogeo_loader.h" +#include "support/neogeo/neogeocd.h" + // Arcade support #include "support/arcade/mra_loader.h" diff --git a/support/megacd/megacd.cpp b/support/megacd/megacd.cpp index f22e48c..42655c2 100644 --- a/support/megacd/megacd.cpp +++ b/support/megacd/megacd.cpp @@ -30,7 +30,7 @@ void mcd_poll() if (has_command) { spi_uio_cmd_cont(UIO_CD_SET); - uint64_t s = cdd.GetStatus(); + uint64_t s = cdd.GetStatus(0); spi_w((s >> 0) & 0xFFFF); spi_w((s >> 16) & 0xFFFF); spi_w(((s >> 32) & 0x00FF) | (cdd.isData ? 0x01 << 8 : 0x00 << 8)); @@ -63,7 +63,7 @@ void mcd_poll() } uint64_t c = *((uint64_t*)(data_in)); - cdd.SetCommand(c); + cdd.SetCommand(c, 0); cdd.CommandExec(); has_command = 1; diff --git a/support/megacd/megacd.h b/support/megacd/megacd.h index a0130b5..36e097b 100644 --- a/support/megacd/megacd.h +++ b/support/megacd/megacd.h @@ -31,10 +31,13 @@ #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_TRACK_PLAY 0x0B #define CD_COMM_TRAY_CLOSE 0x0C #define CD_COMM_TRAY_OPEN 0x0D +#define MCD_DATA_IO_INDEX 2 +#define MCD_SUB_IO_INDEX 3 + #include "../../cd.h" #include @@ -53,8 +56,8 @@ public: void Reset(); void Update(); void CommandExec(); - uint64_t GetStatus(); - int SetCommand(uint64_t c); + uint64_t GetStatus(uint8_t crc_start); + int SetCommand(uint64_t c, uint8_t crc_start); private: toc_t toc; @@ -80,6 +83,7 @@ private: 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); + void SeekToLBA(int lba, int play); }; #define BCD(v) ((uint8_t)((((v)/10) << 4) | ((v)%10))) diff --git a/support/megacd/megacdd.cpp b/support/megacd/megacdd.cpp index bbf0abd..2fc48bc 100644 --- a/support/megacd/megacdd.cpp +++ b/support/megacd/megacdd.cpp @@ -8,9 +8,6 @@ #include "megacd.h" #include "../chd/mister_chd.h" -#define CD_DATA_IO_INDEX 2 -#define CD_SUB_IO_INDEX 3 - cdd_t cdd; cdd_t::cdd_t() { @@ -149,16 +146,16 @@ int cdd_t::LoadCUE(const char* filename) { { if (strstr(lptr, "MODE1/2048")) { - this->sectorSize = 2048; + this->toc.tracks[0].sector_size = 2048; } else if (strstr(lptr, "MODE1/2352")) { - this->sectorSize = 2352; + this->toc.tracks[0].sector_size = 2352; FileSeek(&this->toc.tracks[0].f, 0x10, SEEK_SET); } - if (this->sectorSize) + if (this->toc.tracks[0].sector_size) { this->toc.tracks[0].type = 1; @@ -212,7 +209,7 @@ int cdd_t::LoadCUE(const char* filename) { this->toc.tracks[this->toc.last].offset += this->toc.end * 2352; int sectorSize = 2352; - if (this->toc.tracks[this->toc.last].type) sectorSize = this->sectorSize; + if (this->toc.tracks[this->toc.last].type) sectorSize = this->toc.tracks[0].sector_size; this->toc.tracks[this->toc.last].end = this->toc.tracks[this->toc.last].start + ((this->toc.tracks[this->toc.last].f.size + sectorSize - 1) / sectorSize); this->toc.tracks[this->toc.last].start += (bb + ss * 75 + mm * 60 * 75); @@ -283,7 +280,11 @@ int cdd_t::Load(const char *filename) FileReadAdv(fd_img, header, 0x10); } - if (!memcmp("SEGADISCSYSTEM", header, 14)) + if (this->toc.tracks[0].sector_size) + { + this->sectorSize = this->toc.tracks[0].sector_size; + } + else if (!memcmp("SEGADISCSYSTEM", header, 14)) { this->sectorSize = 2048; } @@ -673,42 +674,11 @@ void cdd_t::CommandExec() { 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 */ - FileSeek(&this->toc.tracks[0].f, lba_ * this->sectorSize, SEEK_SET); - } - else if (this->toc.tracks[index].f.opened()) - { - /* PCM AUDIO track */ - FileSeek(&this->toc.tracks[index].f, (lba_ * 2352) - this->toc.tracks[index].offset, SEEK_SET); - } - - this->chd_audio_read_lba = this->lba; - this->audioOffset = 0; - - if (this->toc.sub.opened()) FileSeek(&this->toc.sub, lba_ * 96, SEEK_SET); + SeekToLBA(lba_, 1); this->isData = 1; @@ -729,35 +699,11 @@ void cdd_t::CommandExec() { 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 - FileSeek(&this->toc.tracks[0].f, lba_ * this->sectorSize, SEEK_SET); - } - else if (this->toc.tracks[index].f.opened()) - { - // AUDIO track - FileSeek(&this->toc.tracks[index].f, (lba_ * 2352) - this->toc.tracks[index].offset, SEEK_SET); - } - - if (this->toc.sub.opened()) FileSeek(&this->toc.sub, lba_ * 96, SEEK_SET); + SeekToLBA(lba_, 0); this->isData = 1; @@ -783,7 +729,6 @@ void cdd_t::CommandExec() { 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; @@ -812,6 +757,34 @@ void cdd_t::CommandExec() { stat[0] = this->status; break; + case CD_COMM_TRACK_PLAY: { + int index = comm[2] * 10 + comm[3]; + if (index > 0) + { + index -= 1; + } + int lba = this->toc.tracks[index].start; + + SeekToLBA(lba, 1); + + this->isData = 1; + + this->status = CD_STAT_PLAY; + + stat[0] = CD_STAT_SEEK; + 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 CD_COMM_TRACK_PLAY, index: %u, status = %u \n\x1b[0m", index, this->status); + } + break; + case CD_COMM_TRAY_CLOSE: this->isData = 1; this->status = this->loaded ? CD_STAT_TOC : CD_STAT_NO_DISC; @@ -836,8 +809,8 @@ void cdd_t::CommandExec() { } } -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]); +uint64_t cdd_t::GetStatus(uint8_t crc_start) { + uint8_t n9 = ~(crc_start + 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) | @@ -850,7 +823,7 @@ uint64_t cdd_t::GetStatus() { ((uint64_t)(stat[0] & 0xF) << 0); } -int cdd_t::SetCommand(uint64_t c) { +int cdd_t::SetCommand(uint64_t c, uint8_t crc_start) { comm[0] = (c >> 0) & 0xF; comm[1] = (c >> 4) & 0xF; comm[2] = (c >> 8) & 0xF; @@ -862,7 +835,7 @@ int cdd_t::SetCommand(uint64_t c) { 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; + uint8_t crc = (~(crc_start + 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; @@ -883,6 +856,48 @@ void cdd_t::MSFToLBA(int* lba, msf_t* msf) { *lba = msf->f + msf->s * 75 + msf->m * 60 * 75; } +void cdd_t::SeekToLBA(int lba, int play) { + int index = 0; + + this->latency = 0; + if (play) + { + 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 */ + FileSeek(&this->toc.tracks[0].f, lba * this->sectorSize, SEEK_SET); + } + else if (this->toc.tracks[index].f.opened()) + { + /* PCM AUDIO track */ + FileSeek(&this->toc.tracks[index].f, (lba * 2352) - this->toc.tracks[index].offset, SEEK_SET); + } + + if (play) + { + this->chd_audio_read_lba = this->lba; + this->audioOffset = 0; + } + + if (this->toc.sub.opened()) FileSeek(&this->toc.sub, lba * 96, SEEK_SET); + +} + void cdd_t::ReadData(uint8_t *buf) { if (this->toc.tracks[this->index].type && (this->lba >= 0)) @@ -999,7 +1014,7 @@ int cdd_t::SectorSend(uint8_t* header) SubcodeSend(); if (SendData) - return SendData(buf, len, CD_DATA_IO_INDEX); + return SendData(buf, len, MCD_DATA_IO_INDEX); return 0; } @@ -1012,7 +1027,7 @@ int cdd_t::SubcodeSend() ReadSubcode(buf); if (SendData) - return SendData((uint8_t*)buf, 98, CD_SUB_IO_INDEX); + return SendData((uint8_t*)buf, 98, MCD_SUB_IO_INDEX); return 0; } diff --git a/support/neogeo/neogeocd.cpp b/support/neogeo/neogeocd.cpp new file mode 100644 index 0000000..8f55d74 --- /dev/null +++ b/support/neogeo/neogeocd.cpp @@ -0,0 +1,135 @@ + +#include +#include +#include +#include + +#include "../../file_io.h" +#include "../../user_io.h" +#include "../../spi.h" +#include "../../hardware.h" +#include "../../menu.h" +#include "../../cheats.h" +#include "../megacd/megacd.h" +#include "neogeocd.h" +#include "neogeo_loader.h" + +static int need_reset=0; +static uint8_t has_command = 0; +static uint8_t neo_cd_en = 0; + +#define CRC_START 5 + +#define NEOCD_AUDIO_IO_INDEX 4 + +void neocd_poll() +{ + static uint32_t poll_timer = 0; + static uint8_t last_req = 255; + static uint8_t adj = 0; + + if (!poll_timer || CheckTimer(poll_timer)) + { + poll_timer = GetTimer(13 + (!adj ? 1 : 0)); + if (++adj >= 3) adj = 0; + + if (has_command) { + spi_uio_cmd_cont(UIO_CD_SET); + uint64_t s = cdd.GetStatus(CRC_START); + spi_w((s >> 0) & 0xFFFF); + spi_w((s >> 16) & 0xFFFF); + spi_w((s >> 32) & 0x00FF); + DisableIO(); + + has_command = 0; + + //printf("\x1b[32mNEOCD: Send status, status = %04X%04X%04X \n\x1b[0m", (uint16_t)((s >> 32) & 0x00FF), (uint16_t)((s >> 16) & 0xFFFF), (uint16_t)((s >> 0) & 0xFFFF)); + } + + cdd.Update(); + } + + + 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(); + + if (need_reset || data_in[0] == 0xFF) { + printf("NEOCD: request to reset\n"); + need_reset = 0; + cdd.Reset(); + } + + uint64_t c = *((uint64_t*)(data_in)); + cdd.SetCommand(c, CRC_START); + cdd.CommandExec(); + has_command = 1; + + //printf("\x1b[32mNEOCD: 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(); +} + +void neocd_set_image(char *filename) +{ + cdd.Unload(); + cdd.status = CD_STAT_OPEN; + + if (*filename) + { + neogeo_romset_tx(filename); + + if (cdd.Load(filename) > 0) + { + cdd.status = cdd.loaded ? CD_STAT_STOP : CD_STAT_NO_DISC; + cdd.latency = 10; + cdd.SendData = neocd_send_data; + } + else + { + cdd.status = CD_STAT_NO_DISC; + } + } + + neocd_reset(); +} + +void neocd_reset() { + need_reset = 1; +} + +int neocd_send_data(uint8_t* buf, int len, uint8_t index) { + uint8_t idx = index; + if (idx == MCD_DATA_IO_INDEX && !cdd.isData) + { + // The MegaCD core sends the isData bit through the status to differentiate between data/audio. + // This requires that more commands come in after the initial play command. + // NeoGeo does not always send another command so use another index for audio. + idx = NEOCD_AUDIO_IO_INDEX; + } + + // set index byte + user_io_set_index(idx); + + user_io_set_download(1); + user_io_file_tx_data(buf, len); + user_io_set_download(0); + return 1; +} + +int neocd_is_en() { + return (neo_cd_en == 1); +} + +void neocd_set_en(int enable) { + neo_cd_en = enable; +} diff --git a/support/neogeo/neogeocd.h b/support/neogeo/neogeocd.h new file mode 100644 index 0000000..06bee1f --- /dev/null +++ b/support/neogeo/neogeocd.h @@ -0,0 +1,11 @@ +#ifndef NEOGEOCD_H +#define NEOGEOCD_H + + +void neocd_poll(); +void neocd_set_image(char *filename); +void neocd_reset(); +int neocd_send_data(uint8_t* buf, int len, uint8_t index); +int neocd_is_en(); +void neocd_set_en(int enable); +#endif diff --git a/user_io.cpp b/user_io.cpp index 3da0efa..0756d3b 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -231,6 +231,10 @@ char is_neogeo() return (is_neogeo_type == 1); } +char is_neogeo_cd() { + return is_neogeo() && neocd_is_en(); +} + static int is_minimig_type = 0; char is_minimig() { @@ -2770,6 +2774,7 @@ void user_io_send_buttons(char force) { if (is_minimig()) minimig_reset(); if (is_megacd()) mcd_reset(); + if (is_neogeo_cd()) neocd_reset(); if (is_pce()) pcecd_reset(); if (is_saturn()) saturn_reset(); if (is_x86() || is_pcxt()) x86_init(); @@ -3438,6 +3443,7 @@ void user_io_poll() if (is_pce()) pcecd_poll(); if (is_saturn()) saturn_poll(); if (is_psx()) psx_poll(); + if (is_neogeo_cd()) neocd_poll(); process_ss(0); } diff --git a/user_io.h b/user_io.h index 2fba278..affffb3 100644 --- a/user_io.h +++ b/user_io.h @@ -268,6 +268,7 @@ char is_x86(); char is_snes(); char is_sgb(); char is_neogeo(); +char is_neogeo_cd(); char is_megacd(); char is_pce(); char is_archie();