From fcd784ba975d2bdd218c048b64d3117984617973 Mon Sep 17 00:00:00 2001 From: dentnz Date: Fri, 13 May 2022 20:23:15 +1000 Subject: [PATCH] snes: MSU1 audio support by dent^nz and ElectronAsh (#602) Co-authored-by: Karl Lurman --- support/snes/snes.cpp | 229 ++++++++++++++++++++++++++++++++++++++++++ support/snes/snes.h | 5 + user_io.cpp | 8 ++ 3 files changed, 242 insertions(+) diff --git a/support/snes/snes.cpp b/support/snes/snes.cpp index 2f7876a..c64ca97 100644 --- a/support/snes/snes.cpp +++ b/support/snes/snes.cpp @@ -3,10 +3,22 @@ #include #include #include +#include #include "../../file_io.h" +#include "../../user_io.h" +#include "../../spi.h" static uint8_t hdr[512]; +static uint16_t msu_currenttrack = 0x0000; +const uint64_t MSU_AUDIO_SECTOR_SENT = 0x00000000101; +const uint64_t MSU_AUDIO_TRACKMOUNTED = 0x00000000201; +const uint64_t MSU_AUDIO_SENDING_SECTOR = 0x00000000301; +const uint64_t MSU_AUDIO_TRACKMISSING = 0x00000000401; + +const uint64_t MSU_DATA_SECTOR_SENT = 0x00000000102; +const uint64_t MSU_DATA_FILEMOUNTED = 0x00000000202; +const uint64_t MSU_DATA_SENDING_SECTOR = 0x00000000302; enum HeaderField { CartName = 0x00, @@ -309,4 +321,221 @@ void snes_patch_bs_header(fileTYPE *f, uint8_t *buf) buf[0xFDA] = 0x33; } } +} + +// This gets set by snes_msu_init for use by MSU-1 support later +char snes_romFileName[1024] = { 0 }; +uint8_t topup_buffer = 0; + +//uint8_t msu_data_array[0x2000000]; +uint8_t msu_data_loaded = 0x00; + +void snes_msu_send_command(uint64_t cmd) +{ + spi_uio_cmd_cont(UIO_CD_SET); + spi_w((cmd >> 0) & 0xFFFF); + spi_w((cmd >> 16) & 0xFFFF); + spi_w(((cmd >> 32) & 0x00FF) | 0x00 << 8); + DisableIO(); +} + +void snes_msu_init(const char* name) +{ + fileTYPE f = {}; +// static char msuDataFileName[1024] = { 0 }; + // Clear our our rom file name + memset(snes_romFileName, 0, 1024); + strncpy(snes_romFileName, name, strlen(name) - 4); + + printf("SNES MSU - Rom named '%s' initialised\n", name); + msu_currenttrack = 0x0000; + +// TODO msu1 data file +// sprintf(msuDataFileName, "%s.msu", snes_romFileName); +// printf("SNES MSU - Checking for MSU datafile: %s\n", msuDataFileName); +// if (!FileOpen(&f, msuDataFileName)) { +// printf("SNES MSU - MSU datafile not found"); +// return; +// } + //else user_io_file_mount(msuDataFileName, 2); + msu_data_loaded = 0x01; + topup_buffer = 0; +} + +static int need_reset=0; +static uint8_t has_command = 0; + +int snes_msu_send_data(fileTYPE *f, uint8_t *buf) +{ + int chunk = 1024; + FileReadAdv(f, buf, chunk); + // set index byte + user_io_set_index(2); + user_io_set_download(1); + user_io_file_tx_data(buf, chunk); + user_io_set_download(0); + return 1; +} + +int snes_msu_jump_sector(fileTYPE *f, uint32_t sector) +{ + __off64_t off64 = sector; + off64 = off64 * 1024; + printf("SNES MSU - jumping to sector: 0x%X\n", sector); + + return FileSeek(f, off64, SEEK_SET); +} + +void snes_poll(void) +{ + static fileTYPE f = {}; + static char SelectedPath[1024] = { 0 }; + static char msuErrorMessage[256] = { 0 }; + static uint8_t last_req = 255; + static uint16_t command = 0X0000; + static uint16_t command_payload_lower = 0X0000; + static uint16_t command_payload_middle = 0X0000; + static uint16_t command_payload_upper = 0X0000; + static uint8_t buf[1024]; + + static uint16_t msu_trackout = 0; + static uint8_t msu_trackrequest = 0; + static uint8_t msu_trackmounted = 0; + static uint8_t msu_trackmissing = 0; + static uint8_t msu_sector_jumping = 0; + static uint8_t msu_sector_requested = 0; + static uint8_t send_sector = 0; + static uint8_t data_req = 0; + + if (has_command) { + // What was the command? + + if (command == 0x0034) { + // Next sector requested + send_sector = 1; + msu_trackrequest = 0; + } + + if (command == 0x0035) { + // Unmount any existing tracks + msu_trackmounted = 0; + // track requested is in next word + msu_trackrequest = 1; + send_sector = 0; + printf("\x1b[32mSNES MSU: Track requested\n\x1b[0m"); + } + + if (command == 0x0036) { + // A particular sector was requested + printf("\x1b[32mSNES MSU: Sector requested\n\x1b[0m"); + msu_sector_requested = 1; + } + + has_command = 0; + } + + // Detect incoming command via CD_GET (which we are repurposing for MSU1) + uint8_t req = spi_uio_cmd_cont(UIO_CD_GET); + if (req != last_req) + { + last_req = req; + + // 49 bit messaging (48 usable) + 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("SNES: request to reset\n"); + need_reset = 1; + // TODO need to reset everything at this point + need_reset = 0; + //cdd.Reset(); + } + + has_command = 1; + command = data_in[0]; + command_payload_lower = data_in[1]; + command_payload_middle = data_in[2]; + command_payload_upper = data_in[3]; + + //printf("\x1b[32mSNES MSU: Get command, full command = %04X%04X%04X, has_command = %u\n\x1b[0m", data_in[2], data_in[1], data_in[0], has_command); + } + else + { + DisableIO(); + } + + // New MSU1 Track? + if (msu_trackrequest == 1 && msu_trackmounted == 0) + { + send_sector = 0; + // Track number is in the first word + msu_trackout = command_payload_lower; + printf("SNES MSU - New track selected: 0x%X\n", msu_trackout); + msu_currenttrack = msu_trackout; + + sprintf(SelectedPath, "%s-%d.pcm", snes_romFileName, msu_trackout); + printf("SNES MSU - Full MSU track path is: %s\n", SelectedPath); + + if (strlen(snes_romFileName) == 0) + { + printf(msuErrorMessage, "SNES MSU - No romname\nReload the rom or core"); + } + else + { + if (!FileOpen(&f, SelectedPath)) + { + snes_msu_send_command(MSU_AUDIO_TRACKMISSING); + sprintf(msuErrorMessage, "SNES MSU - Track not found: %d\n", 1); + printf(msuErrorMessage, 3000); + msu_trackrequest = 0; + msu_trackmounted = 0; + } + else + { + // Track wasn't missing! Let's mount it and wait for sector requests + user_io_file_mount(SelectedPath, 2); + FileSeek(&f, 0, SEEK_SET); + msu_trackmounted = 1; + msu_trackrequest = 0; + // Note that track request will be set to 0 AFTER the track mounted message is sent to FPGA + printf("SNES MSU - Track mounted\n"); + msu_trackmissing = 0; + } + } + } + + if (msu_trackmounted == 1 && msu_trackrequest == 0 && send_sector == 1) + { + if (msu_sector_jumping == 1) { + printf("SNES MSU - Sending a sector as part of a jump\n"); + msu_sector_jumping = 0; + } + + snes_msu_send_data(&f, buf); + + msu_sector_jumping = 0; + send_sector = 0; + data_req = !data_req; + } + else if (msu_trackmounted == 1 && msu_trackrequest == 1 && send_sector == 0) + { + // Tell the core that the track has been mounted + msu_trackrequest = 0; + printf("SNES MSU: sending track mounted - 201\n"); + // @todo We may need to buffer on the linux side at this point + snes_msu_send_command(MSU_AUDIO_TRACKMOUNTED); + send_sector = 0; + } + else if (msu_trackmounted == 1 && msu_sector_requested == 1 && send_sector == 0) + { + // We received a jump sector message + msu_sector_requested = 0; + send_sector = 1; + msu_sector_jumping = 1; + snes_msu_jump_sector(&f, command_payload_middle << 8 | command_payload_lower); + } } \ No newline at end of file diff --git a/support/snes/snes.h b/support/snes/snes.h index 7f43d79..da558f5 100644 --- a/support/snes/snes.h +++ b/support/snes/snes.h @@ -3,5 +3,10 @@ uint8_t* snes_get_header(fileTYPE *f); void snes_patch_bs_header(fileTYPE *f, uint8_t *buf); +void snes_msu_init(const char* name); +void snes_send_command(uint64_t); +char* snes_read_track_out(void); +int snes_send_data(void); +void snes_poll(void); #endif diff --git a/user_io.cpp b/user_io.cpp index e267e2d..446ecb7 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -2573,6 +2573,13 @@ int user_io_file_tx(const char* name, unsigned char index, char opensave, char m } ProgressMessage(0, 0, 0, 0); + + if (is_snes()) + { + // Setup MSU + snes_msu_init(name); + } + return 1; } @@ -2819,6 +2826,7 @@ void user_io_poll() else if ((core_type == CORE_TYPE_8BIT) && !is_menu() && !is_minimig()) { if (is_st()) tos_poll(); + if (is_snes()) snes_poll(); for (int i = 0; i < 4; i++) {