Skip to content
Permalink
Browse files

MSU1: auto-save when card usage allows and during short reset

  • Loading branch information...
mrehkopf committed Jul 15, 2019
1 parent c8fa056 commit 417b6c7c35d830b36c23321430d207de16837d5a
Showing with 122 additions and 26 deletions.
  1. +122 −26 src/msu1.c
@@ -11,53 +11,111 @@
#include "snes.h"
#include "timer.h"
#include "smc.h"
#include "fpga.h"
#include "memory.h"
#include "led.h"

FIL msufile;
FIL msudata;
FIL msuaudio;
FRESULT msu_res;
DWORD msu_cltbl[CLTBL_SIZE] IN_AHBRAM;
DWORD pcm_cltbl[CLTBL_SIZE] IN_AHBRAM;
UINT msu_audio_bytes_read = MSU_DAC_BUFSIZE / 2;
UINT msu_data_bytes_read = 1;

enum MSU_USAGE {
MSU_IDLE = 0, // not in use
MSU_BUSY // in use
};

tick_t msu_last_sram_check;
uint32_t msu_last_crc;

extern snes_romprops_t romprops;
uint32_t msu_loop_point = 0;
uint32_t msu_page1_start = 0x0000;
uint32_t msu_page2_start = 0x2000;
uint16_t fpga_status_prev = 0;
uint16_t fpga_status_now = 0;

inline int is_msu_free_to_save(void);

int msu_audio_usage = MSU_IDLE;
int msu_data_usage = MSU_IDLE;

void save_during_msu_shortreset(void) {
snes_reset(1);
delay_ms(1);
if(romprops.ramsize_bytes && fpga_test() == FPGA_TEST_TOKEN) {
writeled(1);
save_srm(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR);
writeled(0);
}
snes_reset(0);
}

/* returns true if no MSU feature is in use at the moment so the SD card
may be used to save the game */
int is_msu_free_to_save() {
return (msu_audio_usage == MSU_IDLE)
&& (msu_data_usage == MSU_IDLE);
}

/* check if SRAM content has changed and save
* immediate: 0 = do not check again before one second has expired
* 1 = check immediately
*/
void msu_savecheck(int immediate) {
uint32_t currentcrc;
if(immediate || (getticks() > msu_last_sram_check + 100)) {
currentcrc = calc_sram_crc(SRAM_SAVE_ADDR, romprops.ramsize_bytes);
if(msu_last_crc != currentcrc) {
writeled(1);
save_srm(file_lfn, romprops.ramsize_bytes, SRAM_SAVE_ADDR);
writeled(0);
msu_last_crc = currentcrc;
}
msu_last_sram_check = getticks();
}
}

void prepare_audio_track(uint16_t msu_track, uint32_t audio_offset) {
uint32_t audio_sect = audio_offset & ~0x1ff;
uint32_t audio_sect_offset_sample = (audio_offset & 0x1ff) >> 2;
DBG_MSU1 printf("offset=%08lx sect=%08lx sample=%08lx\n", audio_offset, audio_sect, audio_sect_offset_sample);
/* open file, fill buffer */
char suffix[11];
f_close(&file_handle);
f_close(&msuaudio);
msu_audio_usage = MSU_IDLE;
if(is_msu_free_to_save()) {
msu_savecheck(1);
}
snprintf(suffix, sizeof(suffix), "-%d.pcm", msu_track);
strcpy((char*)file_buf, (char*)file_lfn);
strcpy(strrchr((char*)file_buf, (int)'.'), suffix);
DBG_MSU1 printf("filename: %s\n", file_buf);
dac_pause();
dac_reset(audio_sect_offset_sample);
set_msu_status(MSU_SNES_STATUS_CLEAR_AUDIO_PLAY | MSU_SNES_STATUS_CLEAR_AUDIO_REPEAT);
if(f_open(&file_handle, (const TCHAR*)file_buf, FA_READ) == FR_OK) {
file_handle.cltbl = pcm_cltbl;
if(f_open(&msuaudio, (const TCHAR*)file_buf, FA_READ) == FR_OK) {
msuaudio.cltbl = pcm_cltbl;
pcm_cltbl[0] = CLTBL_SIZE;
f_lseek(&file_handle, CREATE_LINKMAP);
f_lseek(&file_handle, MSU_PCM_OFFSET_LOOPPOINT);
f_read(&file_handle, &msu_loop_point, sizeof(msu_loop_point), &msu_audio_bytes_read);
f_lseek(&msuaudio, CREATE_LINKMAP);
f_lseek(&msuaudio, MSU_PCM_OFFSET_LOOPPOINT);
f_read(&msuaudio, &msu_loop_point, sizeof(msu_loop_point), &msu_audio_bytes_read);
DBG_MSU1 printf("loop point: %ld samples\n", msu_loop_point);
ff_sd_offload=1;
sd_offload_tgt=1;
f_lseek(&file_handle, audio_sect);
f_lseek(&msuaudio, audio_sect);
set_dac_addr(0);
ff_sd_offload=1;
sd_offload_tgt=1;
f_read(&file_handle, file_buf, MSU_DAC_BUFSIZE, &msu_audio_bytes_read);
f_read(&msuaudio, file_buf, MSU_DAC_BUFSIZE, &msu_audio_bytes_read);
/* reset audio_busy + audio_error */
set_msu_status(MSU_SNES_STATUS_CLEAR_AUDIO_BUSY | MSU_SNES_STATUS_CLEAR_AUDIO_ERROR);
// msu_audio_usage = MSU_BUSY;
} else {
f_close(&file_handle);
f_close(&msuaudio);
/* reset audio_busy, set audio_error */
set_msu_status(MSU_SNES_STATUS_CLEAR_AUDIO_BUSY | MSU_SNES_STATUS_SET_AUDIO_ERROR);
}
@@ -67,6 +125,11 @@ void prepare_data(uint32_t msu_offset) {
uint32_t msu_sect = msu_offset & ~0x1ff;
uint32_t msu_sect_offset = msu_offset & 0x1ff;

msu_data_usage = MSU_IDLE;
if(is_msu_free_to_save()) {
msu_savecheck(1);
}

DBG_MSU1 printf("Data requested! Offset=%08lx page1=%08lx page2=%08lx\n", msu_offset, msu_page1_start, msu_page2_start);
if( ((msu_offset < msu_page1_start)
|| (msu_offset >= msu_page1_start + MSU_DATA_BUFSIZE / 2))
@@ -78,11 +141,11 @@ void prepare_data(uint32_t msu_offset) {
set_msu_addr(0x0);
sd_offload_tgt=2;
ff_sd_offload=1;
msu_res = f_lseek(&msufile, msu_sect);
msu_res = f_lseek(&msudata, msu_sect);
DBG_MSU1 printf("seek to %08lx, res = %d\n", msu_sect, msu_res);
sd_offload_tgt=2;
ff_sd_offload=1;
msu_res = f_read(&msufile, file_buf, MSU_DATA_BUFSIZE, &msu_data_bytes_read);
msu_res = f_read(&msudata, file_buf, MSU_DATA_BUFSIZE, &msu_data_bytes_read);
DBG_MSU1 printf("read res = %d\n", msu_res);
DBG_MSU1 printf("read %d bytes\n", msu_data_bytes_read);
msu_reset(msu_sect_offset);
@@ -96,7 +159,7 @@ void prepare_data(uint32_t msu_offset) {
set_msu_addr(MSU_DATA_BUFSIZE / 2);
sd_offload_tgt=2;
ff_sd_offload=1;
f_read(&msufile, file_buf, MSU_DATA_BUFSIZE / 2, &msu_data_bytes_read);
f_read(&msudata, file_buf, MSU_DATA_BUFSIZE / 2, &msu_data_bytes_read);
DBG_MSU1 printf("next page dirty (was: %08lx), loaded page2 (start now: ", msu_page2_start);
msu_page2_start = msu_page1_start + MSU_DATA_BUFSIZE / 2;
DBG_MSU1 printf("%08lx)\n", msu_page2_start);
@@ -108,13 +171,27 @@ void prepare_data(uint32_t msu_offset) {
set_msu_addr(0x0);
sd_offload_tgt=2;
ff_sd_offload=1;
f_read(&msufile, file_buf, MSU_DATA_BUFSIZE / 2, &msu_data_bytes_read);
f_read(&msudata, file_buf, MSU_DATA_BUFSIZE / 2, &msu_data_bytes_read);
DBG_MSU1 printf("next page dirty (was: %08lx), loaded page1 (start now: ", msu_page1_start);
msu_page1_start = msu_page2_start + MSU_DATA_BUFSIZE / 2;
DBG_MSU1 printf("%08lx)\n", msu_page1_start);
}
} else printf("!!!WATWATWAT!!!\n");
}

/* If EOF is reached after last buffering then it's safe to assume
that no further streaming is required unless a new data offset
is requested.
-> Set data_usage IDLE to enable saving.
This is also the case if the MSU data file is 0 bytes so no special
case will be required.
Otherwise set data_usage BUSY as expected. */
if(f_eof(&msudata)) {
msu_data_usage = MSU_IDLE;
} else {
msu_data_usage = MSU_BUSY;
}

/* clear bank bit to mask bank reset artifact */
fpga_status_now &= ~MSU_FPGA_STATUS_MSU_READ_MSB;
fpga_status_prev &= ~MSU_FPGA_STATUS_MSU_READ_MSB;
@@ -127,13 +204,13 @@ int msu1_check(uint8_t* filename) {
strcpy((char*)file_buf, (char*)filename);
strcpy(strrchr((char*)file_buf, (int)'.'), ".msu");
printf("MSU datafile: %s\n", file_buf);
if(f_open(&msufile, (const TCHAR*)file_buf, FA_READ) != FR_OK) {
if(f_open(&msudata, (const TCHAR*)file_buf, FA_READ) != FR_OK) {
printf("MSU datafile not found\n");
return 0;
}
msufile.cltbl = msu_cltbl;
msudata.cltbl = msu_cltbl;
msu_cltbl[0] = CLTBL_SIZE;
if(f_lseek(&msufile, CREATE_LINKMAP)) {
if(f_lseek(&msudata, CREATE_LINKMAP)) {
printf("Error creating FF linkmap for MSU file!\n");
}
romprops.fpga_features |= FEAT_MSU1;
@@ -152,6 +229,9 @@ int msu1_loop() {
int msu_res;
uint8_t cmd;

msu_last_sram_check = getticks();
msu_last_crc = calc_sram_crc(SRAM_SAVE_ADDR, romprops.ramsize_bytes);

msu_page1_start = 0x0000;
msu_page2_start = MSU_DATA_BUFSIZE / 2;

@@ -163,13 +243,15 @@ int msu1_loop() {
msu_reset(0x0);
ff_sd_offload=1;
sd_offload_tgt=2;
f_lseek(&msufile, 0L);
f_lseek(&msudata, 0L);
ff_sd_offload=1;
sd_offload_tgt=2;
f_read(&msufile, file_buf, MSU_DATA_BUFSIZE, &msu_data_bytes_read);
f_read(&msudata, file_buf, MSU_DATA_BUFSIZE, &msu_data_bytes_read);

prepare_audio_track(0, MSU_PCM_OFFSET_WAVEDATA);
prepare_data(0);
msu_data_usage = MSU_IDLE;

/* audio_start, data_start, 0, audio_ctrl[1:0], ctrl_start */
msu_res = SNES_RESET_NONE;
fpga_status_prev = fpga_status();
@@ -217,7 +299,10 @@ int msu1_loop() {
set_msu_addr(msu_addr);
sd_offload_tgt = 2;
ff_sd_offload = 1;
msu_res = f_read(&msufile, file_buf, MSU_DATA_BUFSIZE / 2, &msu_data_bytes_read);
msu_res = f_read(&msudata, file_buf, MSU_DATA_BUFSIZE / 2, &msu_data_bytes_read);
if(f_eof(&msudata)) {
msu_data_usage = MSU_IDLE;
}
DBG_MSU1 printf("data buffer refilled. res=%d page1=%08lx page2=%08lx\n", msu_res, msu_page1_start, msu_page2_start);
}

@@ -231,7 +316,7 @@ int msu1_loop() {
set_dac_addr(dac_addr);
sd_offload_tgt = 1;
ff_sd_offload = 1;
f_read(&file_handle, file_buf, MSU_DAC_BUFSIZE / 2, &msu_audio_bytes_read);
f_read(&msuaudio, file_buf, MSU_DAC_BUFSIZE / 2, &msu_audio_bytes_read);
}

if(fpga_status_now & MSU_FPGA_STATUS_AUDIO_START) {
@@ -254,7 +339,7 @@ int msu1_loop() {
if(fpga_status_now & MSU_FPGA_STATUS_CTRL_START) {
if(fpga_status_now & MSU_FPGA_STATUS_CTRL_RESUME_FLAG_BIT && !(fpga_status_now & MSU_FPGA_STATUS_CTRL_PLAY_FLAG_BIT)) {
resume_msu_track = msu_track;
resume_msu_offset = f_tell(&file_handle);
resume_msu_offset = f_tell(&msuaudio);
}

if(fpga_status_now & MSU_FPGA_STATUS_CTRL_REPEAT_FLAG_BIT) {
@@ -270,10 +355,12 @@ int msu1_loop() {
if(fpga_status_now & MSU_FPGA_STATUS_CTRL_PLAY_FLAG_BIT) {
DBG_MSU1 printf("PLAY!\n");
set_msu_status(MSU_SNES_STATUS_SET_AUDIO_PLAY);
msu_audio_usage = MSU_BUSY;
dac_play();
} else {
DBG_MSU1 printf("PAUSE!\n");
set_msu_status(MSU_SNES_STATUS_CLEAR_AUDIO_PLAY);
msu_audio_usage = MSU_IDLE;
dac_pause();
}
}
@@ -289,28 +376,37 @@ int msu1_loop() {
DBG_MSU1 printf("loop\n");
ff_sd_offload=1;
sd_offload_tgt=1;
f_lseek(&file_handle, MSU_PCM_OFFSET_WAVEDATA + msu_loop_point * 4);
f_lseek(&msuaudio, MSU_PCM_OFFSET_WAVEDATA + msu_loop_point * 4);
ff_sd_offload=1;
sd_offload_tgt=1;
DBG_MSU1 printf("---filling rest of buffer from loop point for %u bytes\n", (MSU_DAC_BUFSIZE / 2) - msu_audio_bytes_read);
f_read(&file_handle, file_buf, (MSU_DAC_BUFSIZE / 2) - msu_audio_bytes_read, &msu_audio_bytes_read);
f_read(&msuaudio, file_buf, (MSU_DAC_BUFSIZE / 2) - msu_audio_bytes_read, &msu_audio_bytes_read);
} else {
set_msu_status(MSU_SNES_STATUS_CLEAR_AUDIO_PLAY);
dac_pause();
msu_audio_usage = MSU_IDLE;
}
msu_audio_bytes_read = MSU_DAC_BUFSIZE;
}

/* check if we can sneak in an SRAM poll / save */
if(is_msu_free_to_save()) {
msu_savecheck(0);
}
}
dac_pause();
f_close(&file_handle);
f_close(&msuaudio);
msu_audio_usage = MSU_IDLE;
msu_data_usage = MSU_IDLE;
// TODO have FPGA automatically reset SRTC on detected reset
fpga_reset_srtc_state();
DBG_MSU1 printf("Reset ");
if(msu_res == SNES_RESET_LONG) {
f_close(&msufile);
f_close(&msudata);
DBG_MSU1 printf("to menu\n");
return 1;
}
save_during_msu_shortreset();
DBG_MSU1 printf("game\n");
return 0;
}

0 comments on commit 417b6c7

Please sign in to comment.
You can’t perform that action at this time.