From 2bc233b85199bdff2ff824ff89a00a2b2d2931e5 Mon Sep 17 00:00:00 2001 From: Sergey Dvodnenko Date: Sun, 22 May 2022 15:09:13 +0300 Subject: [PATCH] Saturn: initial support. (#612) --- cd.h | 8 + menu.cpp | 7 +- osd.cpp | 1 + support.h | 3 + support/saturn/saturn.cpp | 231 +++++++++ support/saturn/saturn.h | 96 ++++ support/saturn/saturncdd.cpp | 965 +++++++++++++++++++++++++++++++++++ user_io.cpp | 16 +- user_io.h | 1 + 9 files changed, 1326 insertions(+), 2 deletions(-) create mode 100644 support/saturn/saturn.cpp create mode 100644 support/saturn/saturn.h create mode 100644 support/saturn/saturncdd.cpp diff --git a/cd.h b/cd.h index cd04dab..59cd60a 100644 --- a/cd.h +++ b/cd.h @@ -39,7 +39,15 @@ typedef struct uint8_t f; } msf_t; +#define BCD(v) ((uint8_t)((((v)/10) << 4) | ((v)%10))) typedef int (*SendDataFunc) (uint8_t* buf, int len, uint8_t index); +inline int FindIndexInTOC(toc_t* toc, int lba) +{ + int index = 0; + while ((toc->tracks[index].end <= lba) && (index < toc->last)) index++; + return index; +} + #endif diff --git a/menu.cpp b/menu.cpp index ecb13f3..fa9f23e 100644 --- a/menu.cpp +++ b/menu.cpp @@ -2156,6 +2156,7 @@ void HandleUI(void) } if (is_pce() && !bit) pcecd_reset(); + if (is_saturn() && !bit) saturn_reset(); user_io_status_set(opt, 1, ex); user_io_status_set(opt, 0, ex); @@ -2278,6 +2279,10 @@ void HandleUI(void) psx_mount_cd(user_io_ext_idx(selPath, fs_pFileExt) << 6 | (menusub + 1), ioctl_index, selPath); cheats_init(selPath, 0); } + else if (is_saturn()) + { + saturn_set_image(ioctl_index, selPath); + } else { user_io_set_index(user_io_ext_idx(selPath, fs_pFileExt) << 6 | (menusub + 1)); @@ -4800,7 +4805,7 @@ void HandleUI(void) char type = flist_SelectedItem()->de.d_type; memcpy(name, flist_SelectedItem()->de.d_name, sizeof(name)); - if ((fs_Options & SCANO_UMOUNT) && (is_megacd() || is_pce() || (is_psx() && !(fs_Options & SCANO_SAVES))) && type == DT_DIR && strcmp(flist_SelectedItem()->de.d_name, "..")) + if ((fs_Options & SCANO_UMOUNT) && (is_megacd() || is_pce() || (is_psx() && !(fs_Options & SCANO_SAVES)) || is_saturn()) && type == DT_DIR && strcmp(flist_SelectedItem()->de.d_name, "..")) { int len = strlen(selPath); strcat(selPath, "/"); diff --git a/osd.cpp b/osd.cpp index b8e293b..6cc7a38 100644 --- a/osd.cpp +++ b/osd.cpp @@ -671,6 +671,7 @@ void OsdUpdate() DisableOsd(); if (is_megacd()) mcd_poll(); if (is_pce()) pcecd_poll(); + if (is_saturn()) saturn_poll(); } } diff --git a/support.h b/support.h index 4bffb98..65d11bb 100644 --- a/support.h +++ b/support.h @@ -40,3 +40,6 @@ // UEF support #include "support/uef/uef_reader.h" + +// Saturn support +#include "support/saturn/saturn.h" diff --git a/support/saturn/saturn.cpp b/support/saturn/saturn.cpp new file mode 100644 index 0000000..fb950cb --- /dev/null +++ b/support/saturn/saturn.cpp @@ -0,0 +1,231 @@ +#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 "saturn.h" + +static int need_reset = 0; +uint32_t frame_cnt = 0; + +static uint32_t CalcTimerOffset(uint8_t speed) { + static uint8_t adj = 0x1; + + uint32_t offs; + if (speed == 2) { + offs = 6 + ((adj & 0x03) != 0x00 ? 1 : 0); //6.6 + adj <<= 1; + if (adj >= 0x08) adj = 0x01; + } + else if (speed == 1) { + offs = 13 + ((adj & 0x01) != 0x00 ? 1 : 0); //13.3 + adj <<= 1; + if (adj >= 0x08) adj = 0x01; + } + else { + offs = 16 + ((adj & 0x03) != 0x00 ? 1 : 0); //16.7 + adj <<= 1; + if (adj >= 0x08) adj = 0x01; + } + return offs; +} + +void saturn_poll() +{ + static unsigned long poll_timer = 0; + static uint8_t last_req = 255; + + if (!poll_timer || CheckTimer(poll_timer)) + { + poll_timer = GetTimer(0); + + uint16_t data_in[6]; + uint8_t req = spi_uio_cmd_cont(UIO_CD_GET); + if (req != last_req) + { + last_req = req; + + for (int i = 0; i < 6; i++) data_in[i] = spi_w(0); + DisableIO(); + + satcdd.SetCommand((uint8_t*)data_in); + satcdd.CommandExec(); + } + else + DisableIO(); + + uint8_t time_mode; + satcdd.Process(&time_mode); + + poll_timer += CalcTimerOffset(time_mode); + + uint16_t* s = (uint16_t*)satcdd.GetStatus(); + spi_uio_cmd_cont(UIO_CD_SET); + for (int i = 0; i < 6; i++) spi_w(s[i]); + DisableIO(); + + satcdd.Update(); + + frame_cnt++; + } +} + +static char buf[1024]; +static void saturn_mount_save(const char *filename) +{ + /*user_io_set_index(SAVE_IO_INDEX); + user_io_set_download(1); + if (strlen(filename)) + { + FileGenerateSavePath(filename, buf); + user_io_file_mount(buf, 0, 1); + } + else + { + user_io_file_mount(""); + } + user_io_set_download(0);*/ +} + +static int saturn_load_rom(const char *basename, const char *name, int sub_index) +{ + strcpy(buf, basename); + char *p = strrchr(buf, '/'); + if (p) + { + p++; + strcpy(p, name); + if (user_io_file_tx(buf, sub_index << 6)) return 1; + } + + return 0; +} +void saturn_set_image(int num, const char *filename) +{ + static char last_dir[1024] = {}; + + (void)num; + + satcdd.Unload(); + satcdd.state = Open; + + int same_game = *filename && *last_dir && !strncmp(last_dir, filename, strlen(last_dir)); + strcpy(last_dir, filename); + char *p = strrchr(last_dir, '/'); + if (p) *p = 0; + + int loaded = 1; + if (!same_game) + { + saturn_mount_save(""); + + user_io_status_set("[0]", 1); + user_io_status_set("[0]", 0); + saturn_reset(); + + loaded = 0; + strcpy(buf, last_dir); + char *p = strrchr(buf, '/'); + if (p) + { + strcpy(p + 1, "cd_bios.rom"); + loaded = user_io_file_tx(buf); + } + + if (!loaded) + { + sprintf(buf, "%s/boot.rom", HomeDir()); + loaded = user_io_file_tx(buf); + } + + if (!loaded) Info("CD BIOS not found!", 4000); + } + + if (strlen(filename)) + { + if (satcdd.Load(filename) > 0) + { + satcdd.state = satcdd.loaded ? Stop : Open; + satcdd.SendData = saturn_send_data; + + if (!same_game) + { + saturn_load_rom(filename, "cd_bios.rom", 0); + //saturn_load_rom(filename, "cart.rom", 1); + saturn_mount_save(filename); + //cheats_init(filename, 0); + } + } + else + { + satcdd.state = Open; + } + } +} + +void saturn_reset() { + need_reset = 1; +} + +int saturn_send_data(uint8_t* buf, int len, uint8_t index) { + // set index byte + user_io_set_index(index); + + user_io_set_download(1); + user_io_file_tx_data(buf, len); + user_io_set_download(0); + return 1; +} + +static char int_blank[] = { + 0x42, 0x61, 0x63, 0x6B, 0x55, 0x70, 0x52, 0x61, 0x6D, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x61, 0x74, + 0x42, 0x61, 0x63, 0x6B, 0x55, 0x70, 0x52, 0x61, 0x6D, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x61, 0x74, + 0x42, 0x61, 0x63, 0x6B, 0x55, 0x70, 0x52, 0x61, 0x6D, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x61, 0x74, + 0x42, 0x61, 0x63, 0x6B, 0x55, 0x70, 0x52, 0x61, 0x6D, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void saturn_fill_blanksave(uint8_t *buffer, uint32_t lba) +{ + if (lba == 0) + { + memcpy(buffer, int_blank, 512); + } + else + { + memset(buffer, 0, 512); + } +} \ No newline at end of file diff --git a/support/saturn/saturn.h b/support/saturn/saturn.h new file mode 100644 index 0000000..f504598 --- /dev/null +++ b/support/saturn/saturn.h @@ -0,0 +1,96 @@ +#ifndef SATURN_H +#define SATURN_H + +#include "../../cd.h" + +// CDD command +#define SATURN_COMM_NOP 0x00 +#define SATURN_COMM_SEEK_RING 0x02 +#define SATURN_COMM_TOC 0x03 +#define SATURN_COMM_STOP 0x04 +#define SATURN_COMM_READ 0x06 +#define SATURN_COMM_PAUSE 0x08 +#define SATURN_COMM_SEEK 0x09 +#define SATURN_COMM_FW_SCAN 0x0A +#define SATURN_COMM_RW_SCAN 0x0B + +// CDD status +#define SATURN_STAT_NOP 0x00 +#define SATURN_STAT_TOC 0x06 +#define SATURN_STAT_STOP 0x12 +#define SATURN_STAT_SEEK 0x22 +#define SATURN_STAT_AUDIO 0x34 +#define SATURN_STAT_DATA 0x36 +#define SATURN_STAT_IDLE 0x46 +#define SATURN_STAT_OPEN 0x80 +#define SATURN_STAT_NODISK 0x83 +#define SATURN_STAT_SEEK_RING 0xB2 +#define SATURN_STAT_SEEK_RING2 0xB6 + +typedef enum { + Idle, + Open, + ReadTOC, + Read, + Play, + Pause, + Stop, + Seek, + SeekRead, + SeekRing +} satstate_t; + +class satcdd_t +{ +public: + int loaded; + satstate_t state; + int speed; + SendDataFunc SendData; + + satcdd_t(); + int Load(const char *filename); + void Unload(); + void Reset(); + void Process(uint8_t* time_mode); + void Update(); + void CommandExec(); + uint8_t* GetStatus(); + int SetCommand(uint8_t* data); + +private: + toc_t toc; + int lba; + int index; + int seek_lba; + uint16_t sectorSize; + int toc_pos; + satstate_t next_state; + uint8_t stat[12]; + uint8_t comm[12]; + uint8_t cd_buf[4096 + 2]; + int audioLength; + int audioFirst; + + int LoadCUE(const char* filename); + void LBAToMSF(int lba, msf_t* msf); + int GetFAD(uint8_t* cmd); + void SetChecksum(uint8_t* stat); + void ReadData(uint8_t *buf); + int ReadCDDA(uint8_t *buf); + void MakeSecureRingData(uint8_t *buf); + int DataSectorSend(uint8_t* header, int speed); + int AudioSectorSend(); + int RingDataSend(uint8_t* header, int speed); +}; + +extern satcdd_t satcdd; +extern uint32_t frame_cnt; + +void saturn_poll(); +void saturn_set_image(int num, const char *filename); +void saturn_reset(); +void saturn_fill_blanksave(uint8_t *buffer, uint32_t lba); +int saturn_send_data(uint8_t* buf, int len, uint8_t index); + +#endif diff --git a/support/saturn/saturncdd.cpp b/support/saturn/saturncdd.cpp new file mode 100644 index 0000000..b356532 --- /dev/null +++ b/support/saturn/saturncdd.cpp @@ -0,0 +1,965 @@ +#include +#include +#include +#include +#include + +#include "saturn.h" +#include "../chd/mister_chd.h" + +#define CD_DATA_IO_INDEX 4 + +satcdd_t satcdd; + +satcdd_t::satcdd_t() { + loaded = 0; + state = Open, + index = 0; + lba = 0; + seek_lba = 0; + speed = 0; + audioLength = 0; + audioFirst = 0; + //chd_hunkbuf = NULL; + //chd_hunknum = -1; + SendData = NULL; + + stat[0] = SATURN_STAT_OPEN; + stat[1] = 0x00; + stat[2] = 0x00; + stat[3] = 0x00; + stat[4] = 0x00; + stat[5] = 0x00; + stat[6] = 0x00; + stat[7] = 0x00; + stat[8] = 0x00; + stat[9] = 0x00; + stat[11] = 0x00; + SetChecksum(stat); +} + +static int sgets(char *out, int sz, char **in) +{ + *out = 0; + do + { + char *instr = *in; + int cnt = 0; + + while (*instr && *instr != 10) + { + if (*instr == 13) + { + instr++; + continue; + } + + if (cnt < sz - 1) + { + out[cnt++] = *instr; + out[cnt] = 0; + } + + instr++; + } + + if (*instr == 10) instr++; + *in = instr; + } while (!*out && **in); + + return *out; +} + +int satcdd_t::LoadCUE(const char* filename) { + static char fname[1024 + 10]; + static char line[128]; + char *ptr, *lptr; + static char toc[100 * 1024]; + + strcpy(fname, filename); + + memset(toc, 0, sizeof(toc)); + if (!FileLoad(fname, toc, sizeof(toc) - 1)) return 1; + + printf("\x1b[32mSaturn: Open CUE: %s\n\x1b[0m", fname); + + int mm, ss, bb, pregap = 0; + + char *buf = toc; + while (sgets(line, sizeof(line), &buf)) + { + 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; + + if (!FileOpen(&this->toc.tracks[this->toc.last].f, fname)) return -1; + + printf("\x1b[32mSaturn: 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") && !strstr(lptr, "WAVE")) + { + FileClose(&this->toc.tracks[this->toc.last].f); + printf("\x1b[32mSaturn: 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)) + { + FileClose(&this->toc.tracks[this->toc.last].f); + printf("\x1b[32mSaturn: missing tracks: %s\n\x1b[0m", fname); + break; + } + + if (strstr(lptr, "MODE1/2048")) + { + this->sectorSize = 2048; + this->toc.tracks[this->toc.last].type = 1; + } + else if (strstr(lptr, "MODE1/2352")) + { + this->sectorSize = 2352; + this->toc.tracks[this->toc.last].type = 1; + + //FileSeek(&this->toc.tracks[0].f, 0x10, SEEK_SET); + } + else if (strstr(lptr, "MODE2/2352")) + { + this->sectorSize = 2352; + this->toc.tracks[this->toc.last].type = 2; + + //FileSeek(&this->toc.tracks[0].f, 0x10, SEEK_SET); + } + + if (!this->toc.last) + { + /*if (strstr(lptr, "MODE1/2048")) + { + this->sectorSize = 2048; + this->toc.tracks[0].type = 1; + } + else if (strstr(lptr, "MODE1/2352") || strstr(lptr, "MODE2/2352")) + { + this->sectorSize = 2352; + this->toc.tracks[0].type = 1; + + FileSeek(&this->toc.tracks[0].f, 0x10, SEEK_SET); + } + + if (this->sectorSize) + { + this->toc.tracks[0].type = 1; + + FileReadAdv(&this->toc.tracks[0].f, header, 0x210); + FileSeek(&this->toc.tracks[0].f, 0, SEEK_SET); + }*/ + } + else + { + if (!this->toc.tracks[this->toc.last].f.opened()) + { + 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].f.opened()) + { + FileOpen(&this->toc.tracks[this->toc.last].f, fname); + 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 + { + FileSeek(&this->toc.tracks[this->toc.last].f, 0, SEEK_SET); + + this->toc.tracks[this->toc.last].start = this->toc.end + pregap; + 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; + 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); + this->toc.end = this->toc.tracks[this->toc.last].end; + } + + printf("\x1b[32mSaturn: Track = %u, start = %u, end = %u, offset = %u, type = %u\n\x1b[0m", this->toc.last, this->toc.tracks[this->toc.last].start, this->toc.tracks[this->toc.last].end, this->toc.tracks[this->toc.last].offset, this->toc.tracks[this->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; + } + + FileClose(&this->toc.tracks[this->toc.last].f); + return 0; +} + +int satcdd_t::Load(const char *filename) +{ + //static char header[32]; + //fileTYPE *fd_img; + + Unload(); + + const char *ext = filename + strlen(filename) - 4; + if (!strncasecmp(".cue", ext, 4)) + { + if (LoadCUE(filename)) { + return (-1); + } + } + /*else if (!strncasecmp(".chd", ext, 4)) { + chd_error err = mister_load_chd(filename, &this->toc); + if (err != CHDERR_NONE) + { + printf("ERROR %s\n", chd_error_string(err)); + return -1; + } + + if (this->chd_hunkbuf) + { + free(this->chd_hunkbuf); + } + + this->chd_hunkbuf = (uint8_t *)malloc(CD_FRAME_SIZE * CD_FRAMES_PER_HUNK); + this->chd_hunknum = -1; + }*/ + else { + return (-1); + + } + + /*if (this->toc.chd_f) + { + mister_chd_read_sector(this->toc.chd_f, 0, 0, 0, 0x10, (uint8_t *)header, this->chd_hunkbuf, &this->chd_hunknum); + } + else { + fd_img = &this->toc.tracks[0].f; + + FileSeek(fd_img, 0, SEEK_SET); + FileReadAdv(fd_img, header, 0x10); + }*/ + + /*if (!memcmp("SEGADISCSYSTEM", header, 14)) + { + this->sectorSize = 2048; + } + else + { + this->sectorSize = 2352; + }*/ + + printf("\x1b[32mSaturn: 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; + + printf("\x1b[32mSaturn: CD mounted, last track = %u\n\x1b[0m", this->toc.last); + + return 1; + } + + return 0; +} + +void satcdd_t::Unload() +{ + if (this->loaded) + { + /*if (this->toc.chd_f) + { + chd_close(this->toc.chd_f); + } + + if (this->chd_hunkbuf) + { + free(this->chd_hunkbuf); + this->chd_hunkbuf = NULL; + }*/ + + for (int i = 0; i < this->toc.last; i++) + { + if (this->toc.tracks[i].f.opened()) + { + FileClose(&this->toc.tracks[i].f); + } + } + + this->loaded = 0; + } + + memset(&this->toc, 0x00, sizeof(this->toc)); + this->sectorSize = 0; +} + + +void satcdd_t::Reset() { + state = Open, + index = 0; + lba = 0; + seek_lba = 0; + speed = 0; + audioLength = 0; + audioFirst = 0; + //chd_audio_read_lba = 0; + + stat[0] = SATURN_STAT_OPEN; + stat[1] = 0x00; + stat[2] = 0x00; + stat[3] = 0x00; + stat[4] = 0x00; + stat[5] = 0x00; + stat[6] = 0x00; + stat[7] = 0x00; + stat[8] = 0x00; + stat[9] = 0x00; + stat[11] = 0x00; + SetChecksum(stat); +} + +void satcdd_t::Process(uint8_t* time_mode) { + msf_t amsf = { 0,2,0 }; + msf_t msf = { 0,2,0 }; + uint8_t idx = 0; + uint8_t q = this->lba < this->toc.end && this->toc.tracks[this->index].type ? 0x40 : 0x00; + static int seek_time = 8; + + this->next_state = this->state; + switch (this->state) + { + case Idle: + this->lba++; + if (this->lba > this->seek_lba + 5) this->lba = this->seek_lba; + + LBAToMSF(this->lba + 150, &amsf); + LBAToMSF(this->lba - this->toc.tracks[this->index].start + 150, &msf); + + stat[0] = SATURN_STAT_IDLE; + stat[1] = q | 0x01; + stat[2] = this->lba < this->toc.end ? BCD(this->index + 1) : 0xAA; + stat[3] = this->lba < this->toc.end ? 0x00 : 0x01; + stat[4] = BCD(msf.m); + stat[5] = BCD(msf.s); + stat[6] = BCD(msf.f); + stat[7] = 0x04; + stat[8] = BCD(amsf.m); + stat[9] = BCD(amsf.s); + stat[10] = BCD(amsf.f); + break; + + case Open: + stat[0] = SATURN_STAT_OPEN; + stat[1] = 0x41; + stat[2] = 0x01; + stat[3] = 0x01; + stat[4] = 0x00; + stat[5] = 0x00; + stat[6] = 0x00; + stat[7] = 0x06; + stat[8] = 0x00; + stat[9] = 0x00; + stat[10] = 0x00; + break; + + case ReadTOC: + if (toc_pos < 0x100) + { + int lba_ = this->toc.tracks[toc_pos & 0xFF].start + 150; + LBAToMSF(lba_, &msf); + idx = BCD(toc_pos + 1); + q = this->toc.tracks[toc_pos & 0xFF].type ? 0x40 : 0x00; + + toc_pos++; + if (toc_pos >= this->toc.last) toc_pos = 0x100; + } + else { + if (toc_pos == 0x100) {//track A0 + msf.m = 1; + msf.s = 0; + msf.f = 0; + idx = 0xA0; + q = this->toc.tracks[0].type ? 0x40 : 0x00; + } + else if (toc_pos == 0x101) {//track A1 + msf.m = this->toc.last; + msf.s = 0; + msf.f = 0; + idx = 0xA1; + q = this->toc.tracks[this->toc.last - 1].type ? 0x40 : 0x00; + } + else if (toc_pos == 0x102) {//track A2 + int lba_ = this->toc.end + 150; + LBAToMSF(lba_, &msf); + idx = 0xA2; + q = 0x00; + + this->next_state = Idle; + } + toc_pos++; + } + + stat[0] = SATURN_STAT_TOC; + stat[1] = q | 0x01; + stat[2] = 0x00; + stat[3] = idx; + stat[4] = 0x00; + stat[5] = 0x00; + stat[6] = 0x00; + stat[7] = 0x06; + stat[8] = BCD(msf.m); + stat[9] = BCD(msf.s); + stat[10] = BCD(msf.f); + + printf("\x1b[32mSaturn: "); + printf("Process read TOC: index = %02X, msf = %02X:%02X:%02X, q = %02X", idx, BCD(msf.m), BCD(msf.s), BCD(msf.f), q); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case Read: + LBAToMSF(this->lba + 150, &amsf); + LBAToMSF(this->lba - this->toc.tracks[this->index].start + 150, &msf); + + stat[0] = SATURN_STAT_DATA; + stat[1] = q | 0x01; + stat[2] = this->lba < this->toc.end ? BCD(this->index + 1) : 0xAA; + stat[3] = this->lba < 0 ? 0x00 : 0x01; + stat[4] = BCD(msf.m); + stat[5] = BCD(msf.s); + stat[6] = BCD(msf.f); + stat[7] = 0x04; + stat[8] = BCD(amsf.m); + stat[9] = BCD(amsf.s); + stat[10] = BCD(amsf.f); + + printf("\x1b[32mSaturn: "); + printf("Process read data, fad = %i, msf = %02X:%02X:%02X, q = %02X", this->lba + 150, BCD(msf.m), BCD(msf.s), BCD(msf.f), q); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case Play: + break; + + case Pause: + stat[0] = SATURN_STAT_IDLE; + stat[1] = 0x41; + stat[2] = 0x01; + stat[3] = 0x01; + stat[4] = 0x00; + stat[5] = 0x00; + stat[6] = 0x00; + stat[7] = 0x06; + stat[8] = 0x00; + stat[9] = 0x00; + stat[10] = 0x00; + + this->next_state = Idle; + break; + + case Stop: + stat[0] = SATURN_STAT_STOP; + stat[1] = 0x41; + stat[2] = 0x01; + stat[3] = 0x01; + stat[4] = 0x00; + stat[5] = 0x00; + stat[6] = 0x00; + stat[7] = 0x06; + stat[8] = 0x00; + stat[9] = 0x00; + stat[10] = 0x00; + break; + + case Seek: + case SeekRead: + LBAToMSF(this->seek_lba + 150, &amsf); + LBAToMSF(this->seek_lba - this->toc.tracks[this->index].start + 150, &msf); + + stat[0] = SATURN_STAT_SEEK; + stat[1] = q | 0x01; + stat[2] = this->seek_lba < this->toc.end ? BCD(this->index + 1) : 0xAA; + stat[3] = this->seek_lba < this->toc.end ? 0x00 : 0x01; + stat[4] = BCD(msf.m); + stat[5] = BCD(msf.s); + stat[6] = BCD(msf.f); + stat[7] = 0x06; + stat[8] = BCD(amsf.m); + stat[9] = BCD(amsf.s); + stat[10] = BCD(amsf.f); + + if (this->seek_lba == this->lba) { + if (seek_time) seek_time--; + else { + this->next_state = this->state == SeekRead ? Read : Idle; + seek_time = 2; + } + } + + //printf("\x1b[32mSaturn: "); + //printf("Process seek, fad = %i, amsf = %02X:%02X:%02X, msf = %02X:%02X:%02X, q = %02X", this->seek_lba + 150, BCD(amsf.m), BCD(amsf.s), BCD(amsf.f), BCD(msf.m), BCD(msf.s), BCD(msf.f), q | 0x01); + //printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case SeekRing: + int fad = this->lba + 150; + + stat[0] = SATURN_STAT_SEEK_RING2; + stat[1] = 0x48; + stat[2] = 0x5A; + stat[3] = fad>>16; + stat[4] = fad>>8; + stat[5] = fad>>0; + stat[6] = 0x09; + stat[7] = 0x09; + stat[8] = 0x09; + stat[9] = 0x09; + stat[10] = 0x00; + + //printf("\x1b[32mSaturn: "); + //printf("Process seek ring, fad = %i", fad); + //printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + } + + SetChecksum(stat); + + if (this->next_state == Idle || this->next_state == Pause || this->next_state == Stop) *time_mode = 0; + else *time_mode = this->speed; +} + +void satcdd_t::Update() { + msf_t msf = { 0,2,0 }; + + switch (this->state) + { + case Idle: + case Open: + case ReadTOC: + break; + + case Read: + LBAToMSF(this->lba + 150, &msf); + + if (lba >= this->toc.end) + { + // CD-ROM Security Ring Data (Mode 2) + uint8_t header[12]; + header[0] = BCD(msf.m); + header[1] = BCD(msf.s); + header[2] = BCD(msf.f); + header[3] = 0x02; + header[4] = 0x00; + header[5] = 0x00; + header[6] = 0x28; + header[7] = 0x00; + header[8] = 0x00; + header[9] = 0x00; + header[10] = 0x28; + header[11] = 0x00; + + RingDataSend(header, this->speed); + + //printf("\x1b[32mSaturn: "); + //printf("Read ring data, fad = %i, msf = %02X:%02X:%02X", this->lba + 150, BCD(msf.m), BCD(msf.s), BCD(msf.f)); + //printf(" (%u)\n\x1b[0m", frame_cnt); + } + else if (this->toc.tracks[this->index].type) + { + // CD-ROM Data (Mode 1/2) + uint8_t header[4]; + + if (this->sectorSize == 2048 || this->lba < 0) { + header[0] = BCD(msf.m); + header[1] = BCD(msf.s); + header[2] = BCD(msf.f); + header[3] = 0x01; + DataSectorSend(header, this->speed); + } + else { + DataSectorSend(0, this->speed); + } + + if (this->toc.tracks[this->index].type == 2) { + printf("\x1b[32mSaturn: "); + printf("Update read data, track = %i, lba = %i, msf = %u:%u:%u, mode = 2", this->index + 1, this->lba + 150, msf.m, msf.s, msf.f); + printf(" (%u)\n\x1b[0m", frame_cnt); + } + } + else + { + if (this->lba >= this->toc.tracks[this->index].start) + { + //this->isData = 0x00; + } + AudioSectorSend(); + } + + //printf("\x1b[32mSaturn: "); + //printf("Update read data, lba = %i, msf = %u:%u:%u", this->lba, msf.m, msf.s, msf.f); + //printf(" (%u)\n\x1b[0m", frame_cnt); + + this->lba++; + this->seek_lba++; + this->index = FindIndexInTOC(&this->toc, this->lba); + //this->chd_audio_read_lba++; + + break; + + case Play: + case Pause: + case Stop: + break; + + case Seek: + case SeekRead: + if (this->seek_lba != this->lba) this->seek_lba++; + this->index = FindIndexInTOC(&this->toc, this->seek_lba); + break; + + case SeekRing: + break; + } + + this->state = this->next_state; +} + +void satcdd_t::CommandExec() { + int fad = GetFAD(comm); + + //int last_lba = this->lba; + + switch (comm[0]) { + case SATURN_COMM_NOP: + + //printf("\x1b[32mSaturn: "); + //printf("Command Nop, status = %02X", status); + //printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case SATURN_COMM_SEEK_RING: + this->lba = this->seek_lba = fad - 150; + this->index = FindIndexInTOC(&this->toc, this->lba); + this->state = SeekRing; + this->speed = comm[10] == 1 ? 1 : 2; + + printf("\x1b[32mSaturn: "); + printf("Command Seek Security Ring: FAD = %u, track = %u", fad, this->index + 1); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case SATURN_COMM_TOC: + this->toc_pos = 0; + this->state = ReadTOC; + this->speed = comm[10] == 1 ? 1 : 2; + + printf("\x1b[32mSaturn: "); + printf("Command TOC Read"); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case SATURN_COMM_STOP: + this->state = Stop; + + printf("\x1b[32mSaturn: "); + printf("Command Stop"); + //printf(", last FAD = %u", last_lba + 150); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case SATURN_COMM_READ: + this->lba = fad - 150 - 4; + this->seek_lba = fad - 150 - 4 - 2; + this->index = FindIndexInTOC(&this->toc, this->lba); + //if (this->toc.tracks[this->index].type) { + // this->lba -= 4; + // this->seek_lba -= 4; + //} + this->state = SeekRead; + this->speed = comm[10] == 1 ? 1 : 2; + + this->audioFirst = 1; + + printf("\x1b[32mSaturn: "); + printf("Command Read Data: FAD = %u, track = %u, speed = %u", fad, this->index + 1, this->speed); + //printf(", last FAD = %u", last_lba+150); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case SATURN_COMM_PAUSE: + this->state = Pause; + + printf("\x1b[32mSaturn: "); + printf("Command Pause"); + //printf(", last FAD = %u", last_lba + 150); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + case SATURN_COMM_SEEK: + this->lba = fad - 150; + this->seek_lba = fad - 150; + this->index = FindIndexInTOC(&this->toc, this->lba); + //if (this->toc.tracks[this->index].type) { + // this->lba -= 4; + // this->seek_lba -= 4; + //} + this->state = Seek; + this->speed = comm[10] == 1 ? 1 : 2; + + printf("\x1b[32mSaturn: "); + printf("Command Seek: FAD = %u, track = %u, speed = %u", fad, this->index + 1, this->speed); + //printf(", command = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", comm[0], comm[1], comm[2], comm[3], comm[4], comm[5], comm[6], comm[7], comm[8], comm[9], comm[10], comm[11]); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + + default: + printf("\x1b[32mSaturn: "); + printf("Command undefined, command = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", comm[0], comm[1], comm[2], comm[3], comm[4], comm[5], comm[6], comm[7], comm[8], comm[9], comm[10], comm[11]); + printf(" (%u)\n\x1b[0m", frame_cnt); + break; + } +} + +void satcdd_t::SetChecksum(uint8_t* stat) { + uint8_t sum = 0; + for (int i = 0; i < 11; i++) sum += stat[i]; + stat[11] = ~sum; +} + +void satcdd_t::LBAToMSF(int lba, msf_t* msf) { + msf->m = (lba / 75) / 60; + msf->s = (lba / 75) % 60; + msf->f = (lba % 75); +} + +int satcdd_t::GetFAD(uint8_t* cmd) { + int fad = 0; + fad |= cmd[3] << 0; + fad |= cmd[2] << 8; + fad |= cmd[1] << 16; + return fad; +} + +uint8_t* satcdd_t::GetStatus() { + return this->stat; +} + +int satcdd_t::SetCommand(uint8_t* data) { + memcpy(this->comm, data, 12); + return 0; +} + +void satcdd_t::MakeSecureRingData(uint8_t *buf) { + int i, j; + uint16_t lfsr = 1; + uint8_t a; + for (i = 12; i < 2348; i++) + { + a = (i & 1) ? 0x59 : 0xa8; + for (j = 0; j < 8; j++) + { + a ^= (lfsr & 1); + a = (a >> 1) | (a << (7)); + + uint16_t x = (lfsr >> 1) ^ lfsr; + lfsr |= x << 15; + lfsr >>= 1; + } + buf[i] = a; + } +} + +void satcdd_t::ReadData(uint8_t *buf) +{ + if (this->toc.tracks[this->index].type && (this->lba >= 0)) + { + + /*if (this->toc.chd_f) + { + int read_offset = 0; + if (this->sectorSize != 2048) + { + read_offset += 16; + } + + mister_chd_read_sector(this->toc.chd_f, this->lba + this->toc.tracks[this->index].offset, 0, read_offset, 2048, buf, this->chd_hunkbuf, &this->chd_hunknum); + } + else*/ { + if (this->sectorSize == 2048) + { + FileSeek(&this->toc.tracks[this->index].f, this->lba * 2048, SEEK_SET); + FileReadAdv(&this->toc.tracks[this->index].f, buf, 2048); + } + else { + FileSeek(&this->toc.tracks[this->index].f, this->lba * 2352, SEEK_SET); + FileReadAdv(&this->toc.tracks[this->index].f, buf, 2352); + } + } + } +} + +int satcdd_t::ReadCDDA(uint8_t *buf) +{ + this->audioLength = 2352; + if (this->audioFirst) this->audioLength += 4096 - 2352; + + //printf("\x1b[32mMCD: AUDIO LENGTH %d LBA: %d INDEX: %d START: %d END %d\n\x1b[0m", this->audioLength, this->lba, this->index, this->toc.tracks[this->index].start, this->toc.tracks[this->index].end); + // + + /*if (this->isData) + { + return this->audioLength; + }*/ + + int offs = 0; + /*if (this->toc.chd_f) + { + for (int i = 0; i < this->audioLength / 2352; i++) + { + mister_chd_read_sector(this->toc.chd_f, this->chd_audio_read_lba + this->toc.tracks[this->index].offset, 2352 * i, 0, 2352, buf, this->chd_hunkbuf, &this->chd_hunknum); + } + + //CHD audio requires byteswap. There's probably a better way to do this... + + for (int swapidx = 0; swapidx < this->audioLength; swapidx += 2) + { + uint8_t temp = buf[swapidx]; + buf[swapidx] = buf[swapidx + 1]; + buf[swapidx + 1] = temp; + } + + if ((this->audioLength / 2352) > 1) + { + this->chd_audio_read_lba++; + } + + } + else*/ if (this->toc.tracks[this->index].f.opened()) { + offs = (this->lba * 2352) - this->toc.tracks[this->index].offset; + if (!this->audioFirst) offs += 4096 - 2352; + FileSeek(&this->toc.tracks[this->index].f, offs, SEEK_SET); + FileReadAdv(&this->toc.tracks[this->index].f, buf, this->audioLength); + } + + /*printf("\x1b[32mSaturn: "); + printf("Read CD DA sector: Length = %u, First = %u, offset = %u", this->audioLength, this->audioFirst, offs); + printf(" (%u)\n\x1b[0m", frame_cnt);*/ + + this->audioFirst = 0; + + return this->audioLength; +} + +int satcdd_t::DataSectorSend(uint8_t* header, int speed) +{ + int len = 2352; + uint8_t* data_ptr = cd_buf + 2; + + if (header) { + memcpy(data_ptr + 12 , header, 4); + ReadData(data_ptr + 16); + } + else { + ReadData(data_ptr); + } + cd_buf[0] = cd_buf[1] = (speed == 2 ? 0x01 : 0x00); + + if (SendData) + return SendData(cd_buf, len + 2, CD_DATA_IO_INDEX /*| (!header ? 2 : 0) | (speed == 2 ? 1 : 0)*/); + + return 0; +} + +int satcdd_t::AudioSectorSend() +{ + int len = 2352; + uint8_t* data_ptr = cd_buf + 2; + + len = ReadCDDA(data_ptr); + cd_buf[0] = cd_buf[1] = 0x02; + + if (SendData) + return SendData(cd_buf, len + 2, CD_DATA_IO_INDEX /*| (!header ? 2 : 0) | (speed == 2 ? 1 : 0)*/); + + return 0; +} + +int satcdd_t::RingDataSend(uint8_t* header, int speed) +{ + uint8_t* data_ptr = cd_buf + 2; + + if (header) { + MakeSecureRingData(data_ptr); + memcpy(data_ptr + 12, header, 12); + memset(data_ptr + 2348, 0, 4); + } + cd_buf[0] = cd_buf[1] = (!header ? 2 : 0) | (speed == 2 ? 1 : 0); + + if (SendData) + return SendData(cd_buf, 2352 + 2, CD_DATA_IO_INDEX); + + return 0; +} + + diff --git a/user_io.cpp b/user_io.cpp index 5b9ec6e..fba4217 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -287,6 +287,14 @@ char is_electron() return (is_electron_type == 1); } +static int is_saturn_type = 0; +char is_saturn() +{ + if (!is_saturn_type) is_saturn_type = strcasecmp(core_name, "Saturn") ? 2 : 1; + return (is_saturn_type == 1); +} + + static int is_no_type = 0; static int disable_osd = 0; char has_menu() @@ -2691,7 +2699,8 @@ void user_io_send_buttons(char force) { if (is_minimig()) minimig_reset(); if (is_megacd()) mcd_reset(); - if (is_pce()) pcecd_reset(); + if (is_pce()) pcecd_reset(); + if (is_saturn()) saturn_reset(); if (is_x86()) x86_init(); ResetUART(); } @@ -3013,6 +3022,10 @@ void user_io_poll() { psx_fill_blanksave(buffer[disk], lba, blks); } + else if (is_saturn()) + { + saturn_fill_blanksave(buffer[disk], lba); + } else { memset(buffer[disk], -1, sizeof(buffer[disk])); @@ -3349,6 +3362,7 @@ void user_io_poll() if (is_megacd()) mcd_poll(); if (is_pce()) pcecd_poll(); + if (is_saturn()) saturn_poll(); process_ss(0); } diff --git a/user_io.h b/user_io.h index d97bbab..0284886 100644 --- a/user_io.h +++ b/user_io.h @@ -272,6 +272,7 @@ char is_c64(); char is_st(); char is_psx(); char is_arcade(); +char is_saturn(); #define HomeDir(x) user_io_get_core_path(x) #define CoreName user_io_get_core_name()