Begin adding MSU-1 support. Data streaming is working in Windows, audio streaming is not.

This commit is contained in:
qwertymodo 2016-11-03 11:40:47 -07:00
parent 09df5e1406
commit 3c0f6bfdee
9 changed files with 347 additions and 3 deletions

View File

@ -190,6 +190,7 @@
#include <math.h>
#include "snes9x.h"
#include "apu.h"
#include "msu1.h"
#include "snapshot.h"
#include "display.h"
#include "hermite_resampler.h"
@ -239,6 +240,13 @@ namespace spc
static uint32 ratio_denominator = APU_DENOMINATOR_NTSC;
}
namespace msu
{
static int buffer_size;
static uint8 *landing_buffer = NULL;
static Resampler *resampler = NULL;
}
static void EightBitize (uint8 *, int);
static void DeStereo (uint8 *, int);
static void ReverseStereo (uint8 *, int);
@ -311,6 +319,9 @@ bool8 S9xMixSamples (uint8 *buffer, int sample_count)
memset(dest, 0, sample_count << 1);
spc::resampler->clear();
if(Settings.MSU1)
msu::resampler->clear();
return (FALSE);
}
else
@ -320,6 +331,17 @@ bool8 S9xMixSamples (uint8 *buffer, int sample_count)
spc::resampler->read((short *) dest, sample_count);
if (spc::lag == spc::lag_master)
spc::lag = 0;
if (Settings.MSU1)
{
if (msu::resampler->avail() >= sample_count)
{
uint8 *msu_sample = new uint8[sizeof(dest)];
msu::resampler->read((short *)msu_sample, sample_count);
for(uint32 i = 0; i < sizeof(dest); ++i)
dest[i] += msu_sample[i];
}
}
}
else
{
@ -369,6 +391,11 @@ void S9xFinalizeSamples (void)
if (Settings.SoundSync && !Settings.TurboMode)
return;
}
if (Settings.MSU1 && !msu::resampler->push((short *) msu::landing_buffer, S9xMSU1Samples()))
{
}
}
if (!Settings.SoundSync || Settings.TurboMode || Settings.Mute)
@ -380,6 +407,9 @@ void S9xFinalizeSamples (void)
spc::sound_in_sync = FALSE;
SNES::dsp.spc_dsp.set_output((SNES::SPC_DSP::sample_t *) spc::landing_buffer, spc::buffer_size);
if (Settings.MSU1)
S9xMSU1SetOutput((int16 *)msu::landing_buffer, msu::buffer_size);
}
void S9xLandSamples (void)
@ -393,6 +423,8 @@ void S9xLandSamples (void)
void S9xClearSamples (void)
{
spc::resampler->clear();
if (Settings.MSU1)
msu::resampler->clear();
spc::lag = spc::lag_master;
}
@ -419,6 +451,12 @@ static void UpdatePlaybackRate (void)
double time_ratio = (double) Settings.SoundInputRate * spc::timing_hack_numerator / (Settings.SoundPlaybackRate * spc::timing_hack_denominator);
spc::resampler->time_ratio(time_ratio);
if (Settings.MSU1)
{
time_ratio = 44100.0 / Settings.SoundPlaybackRate;
msu::resampler->time_ratio(time_ratio);
}
}
bool8 S9xInitSound (int buffer_ms, int lag_ms)
@ -442,6 +480,8 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms)
spc::buffer_size <<= 1;
if (Settings.SixteenBitSound)
spc::buffer_size <<= 1;
if (Settings.MSU1)
msu::buffer_size = buffer_ms * 44100 / 1000;
printf("Sound buffer size: %d (%d samples)\n", spc::buffer_size, sample_count);
@ -450,6 +490,11 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms)
spc::landing_buffer = new uint8[spc::buffer_size * 2];
if (!spc::landing_buffer)
return (FALSE);
if (msu::landing_buffer)
delete[] msu::landing_buffer;
msu::landing_buffer = new uint8[msu::buffer_size * 2];
if (!msu::landing_buffer)
return (FALSE);
/* The resampler and spc unit use samples (16-bit short) as
arguments. Use 2x in the resampler for buffer leveling with SoundSync */
@ -465,6 +510,20 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms)
else
spc::resampler->resize(spc::buffer_size >> (Settings.SoundSync ? 0 : 1));
if (!msu::resampler)
{
msu::resampler = new HermiteResampler(msu::buffer_size);
if (!msu::resampler)
{
delete[] msu::landing_buffer;
return (FALSE);
}
}
else
msu::resampler->resize(msu::buffer_size);
SNES::dsp.spc_dsp.set_output ((SNES::SPC_DSP::sample_t *) spc::landing_buffer, spc::buffer_size);
UpdatePlaybackRate();
@ -503,6 +562,7 @@ bool8 S9xInitAPU (void)
spc::landing_buffer = NULL;
spc::shrink_buffer = NULL;
spc::resampler = NULL;
msu::resampler = NULL;
return (TRUE);
}
@ -526,6 +586,18 @@ void S9xDeinitAPU (void)
delete[] spc::shrink_buffer;
spc::shrink_buffer = NULL;
}
if (msu::resampler)
{
delete msu::resampler;
msu::resampler = NULL;
}
if (msu::landing_buffer)
{
delete[] msu::landing_buffer;
msu::landing_buffer = NULL;
}
}
static inline int S9xAPUGetClock (int32 cpucycles)
@ -572,6 +644,8 @@ void S9xAPUEndScanline (void)
S9xAPUExecute();
SNES::dsp.synchronize();
S9xMSU1Execute();
if (SNES::dsp.spc_dsp.sample_count() >= APU_MINIMUM_SAMPLE_BLOCK || !spc::sound_in_sync)
S9xLandSamples();
}
@ -603,6 +677,9 @@ void S9xResetAPU (void)
SNES::dsp.spc_dsp.set_spc_snapshot_callback(SPCSnapshotCallback);
spc::resampler->clear();
if (Settings.MSU1)
msu::resampler->clear();
}
void S9xSoftResetAPU (void)
@ -615,6 +692,9 @@ void S9xSoftResetAPU (void)
SNES::dsp.spc_dsp.set_output ((SNES::SPC_DSP::sample_t *) spc::landing_buffer, spc::buffer_size >> 1);
spc::resampler->clear();
if (Settings.MSU1)
msu::resampler->clear();
}
void S9xAPUSaveState (uint8 *block)

View File

@ -312,6 +312,8 @@ void S9xReset (void)
S9xResetOBC1();
if (Settings.SRTC)
S9xResetSRTC();
if (Settings.MSU1)
S9xMSU1Init();
S9xInitCheatData();
}
@ -346,6 +348,8 @@ void S9xSoftReset (void)
S9xResetOBC1();
if (Settings.SRTC)
S9xResetSRTC();
if (Settings.MSU1)
S9xMSU1Init();
S9xInitCheatData();
}

View File

@ -199,6 +199,7 @@
#include "obc1.h"
#include "seta.h"
#include "bsx.h"
#include "msu1.h"
#define addCyclesInMemoryAccess \
if (!CPU.InDMAorHDMA) \

View File

@ -232,6 +232,7 @@ struct SSPC7110Snapshot s7snap;
struct SSRTCSnapshot srtcsnap;
struct SRTCData RTCData;
struct SBSX BSX;
struct SMSU1 MSU1;
struct SMulti Multi;
struct SSettings Settings;
struct SSNESGameFixes SNESGameFixes;

View File

@ -1270,6 +1270,12 @@ static bool8 is_GNEXT_Add_On (const uint8 *data, uint32 size)
return (FALSE);
}
static bool8 MsuRomExists (void)
{
struct stat buf;
return (stat(S9xGetFilename(".msu", ROMFILENAME_DIR), &buf) == 0);
}
int CMemory::ScoreHiROM (bool8 skip_header, int32 romoff)
{
uint8 *buf = ROM + 0xff00 + romoff + (skip_header ? 0x200 : 0);
@ -2360,6 +2366,7 @@ void CMemory::InitROM (void)
Settings.SETA = 0;
Settings.SRTC = FALSE;
Settings.BS = FALSE;
Settings.MSU1 = FALSE;
SuperFX.nRomBanks = CalculatedSize >> 15;
@ -2534,6 +2541,9 @@ void CMemory::InitROM (void)
break;
}
// MSU1
Settings.MSU1 = MsuRomExists();
//// Map memory and calculate checksum
Map_Initialize();
@ -3486,7 +3496,7 @@ const char * CMemory::KartContents (void)
static char str[64];
static const char *contents[3] = { "ROM", "ROM+RAM", "ROM+RAM+BAT" };
char chip[16];
char chip[20];
if (ROMType == 0 && !Settings.BS)
return ("ROM");
@ -3532,6 +3542,9 @@ const char * CMemory::KartContents (void)
else
strcpy(chip, "");
if (Settings.MSU1)
sprintf(chip + strlen(chip), "+MSU-1");
sprintf(str, "%s%s", contents[(ROMType & 0xf) % 3], chip);
return (str);

207
msu1.cpp Normal file
View File

@ -0,0 +1,207 @@
#include "snes9x.h"
#include "display.h"
#include "msu1.h"
#include <fstream>
std::ifstream dataFile, audioFile;
uint32 dataPos, audioPos, audioResumePos, audioLoopPos;
uint16 audioTrack, audioResumeTrack;
char fName[64];
// Sample buffer
int16 *bufPos, *bufBegin, *bufEnd;
void S9xMSU1Init(void)
{
MSU1.MSU1_STATUS = 0;
MSU1.MSU1_SEEK = 0;
MSU1.MSU1_TRACK = 0;
MSU1.MSU1_VOLUME = 0;
MSU1.MSU1_CONTROL = 0;
dataPos = 0;
audioPos = 0;
audioResumePos = 0;
audioTrack = 0;
audioResumeTrack = 0;
bufPos = 0;
bufBegin = 0;
bufEnd = 0;
if (dataFile.is_open())
dataFile.close();
if (audioFile.is_open())
audioFile.close();
dataFile.open(S9xGetFilename(".msu", ROMFILENAME_DIR), std::ios::in | std::ios::binary);
}
void S9xMSU1Execute(void)
{
static long long hitcount = 0;
//return; // Dummy out for now because it's broken
while ((bufPos < bufEnd) && (MSU1.MSU1_STATUS & AudioPlaying))
{
hitcount++;
if (audioFile.good())
{
audioPos += 2;
int16 sample = 0;
((uint8 *)&sample)[0] = audioFile.get();
((uint8 *)&sample)[1] = audioFile.get();
sample = (double)sample * (double)MSU1.MSU1_VOLUME / 255.0;
*bufPos = ((uint8 *)&sample)[0];
*(bufPos + 1) = ((uint8 *)&sample)[1];
bufPos += 2;
if (audioFile.eof())
{
if (MSU1.MSU1_STATUS & AudioRepeating)
{
audioPos = audioLoopPos;
audioFile.seekg(audioPos);
}
else
{
MSU1.MSU1_STATUS &= ~(AudioPlaying | AudioRepeating);
audioFile.seekg(8);
return;
}
}
}
else
{
MSU1.MSU1_STATUS &= ~AudioPlaying;
}
}
}
uint8 S9xMSU1ReadPort(int port)
{
switch (port)
{
case 0:
return MSU1.MSU1_STATUS;
case 1:
if (MSU1.MSU1_STATUS & DataBusy)
return 0;
if (dataFile.fail() || dataFile.bad() || dataFile.eof())
return 0;
return dataFile.get();
case 2:
return 'S';
case 3:
return '-';
case 4:
return 'M';
case 5:
return 'S';
case 6:
return 'U';
case 7:
return '1';
}
}
void S9xMSU1WritePort(int port, uint8 byte)
{
switch (port)
{
case 0:
((uint8 *)(&MSU1.MSU1_SEEK))[0] = byte;
break;
case 1:
((uint8 *)(&MSU1.MSU1_SEEK))[1] = byte;
break;
case 2:
((uint8 *)(&MSU1.MSU1_SEEK))[2] = byte;
break;
case 3:
((uint8 *)(&MSU1.MSU1_SEEK))[3] = byte;
dataPos = MSU1.MSU1_SEEK;
if(dataFile.good())
dataFile.seekg(dataPos);
break;
case 4:
((uint8 *)(&MSU1.MSU1_TRACK))[0] = byte;
break;
case 5:
((uint8 *)(&MSU1.MSU1_TRACK))[1] = byte;
audioTrack = MSU1.MSU1_TRACK;
if (audioFile.is_open())
audioFile.close();
// This is an ugly hack... need to see if there's a better way to get the base name without extension
sprintf(fName, "%s", S9xGetFilename(".msu", ROMFILENAME_DIR));
fName[strlen(fName) - 4] = '\0';
sprintf(fName, "%s-%d.pcm", fName, audioTrack);
audioFile.open(fName);
if (audioFile.is_open() && audioFile.good())
{
MSU1.MSU1_STATUS |= AudioError;
if (audioFile.get() != 'M')
break;
if (audioFile.get() != 'S')
break;
if (audioFile.get() != 'U')
break;
if (audioFile.get() != '1')
break;
((uint8 *)(&audioLoopPos))[0] = audioFile.get();
((uint8 *)(&audioLoopPos))[1] = audioFile.get();
((uint8 *)(&audioLoopPos))[2] = audioFile.get();
((uint8 *)(&audioLoopPos))[3] = audioFile.get();
audioLoopPos += 8;
MSU1.MSU1_STATUS &= ~AudioPlaying;
MSU1.MSU1_STATUS &= ~AudioRepeating;
if (audioTrack == audioResumeTrack)
{
audioPos = audioResumePos;
audioResumeTrack = 0xFFFF;
audioResumePos = 0;
}
else
audioPos = 8;
audioFile.seekg(audioPos);
MSU1.MSU1_STATUS &= ~AudioError;
}
else
MSU1.MSU1_STATUS |= AudioError;
break;
case 6:
MSU1.MSU1_VOLUME = byte;
break;
case 7:
if (MSU1.MSU1_STATUS & (AudioBusy | AudioError))
break;
MSU1.MSU1_STATUS = (MSU1.MSU1_STATUS & ~0x30) | ((byte & 0x03) << 4);
if ((byte & 0x05) == 0x05)
audioResumeTrack = audioTrack;
break;
}
}
uint16 S9xMSU1Samples(void)
{
return bufPos - bufBegin;
}
void S9xMSU1SetOutput(int16 * out, int size)
{
bufPos = bufBegin = out;
bufEnd = out + size;
}

33
msu1.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef _MSU1_H_
#define _MSU1_H_
#include "snes9x.h"
struct SMSU1
{
uint8 MSU1_STATUS;
uint32 MSU1_SEEK;
uint16 MSU1_TRACK;
uint8 MSU1_VOLUME;
uint8 MSU1_CONTROL;
};
enum SMSU1_FLAG : uint8 {
Revision = 0x02, //max: 0x07
AudioResume = 0x04,
AudioError = 0x08,
AudioPlaying = 0x10,
AudioRepeating = 0x20,
AudioBusy = 0x40,
DataBusy = 0x80,
};
extern struct SMSU1 MSU1;
void S9xMSU1Init(void);
void S9xMSU1Execute(void);
uint8 S9xMSU1ReadPort(int port);
void S9xMSU1WritePort(int port, uint8 byte);
uint16 S9xMSU1Samples(void);
void S9xMSU1SetOutput(int16 *out, int size);
#endif

View File

@ -344,6 +344,9 @@ void S9xSetPPU (uint8 Byte, uint16 Address)
S9xTraceFormattedMessage("--- HDMA PPU %04X -> %02X", Address, Byte);
#endif
if (Settings.MSU1 && (Address & 0xfff8) == 0x2000) // MSU-1
S9xMSU1WritePort(Address & 7, Byte);
else
if ((Address & 0xffc0) == 0x2140) // APUIO0, APUIO1, APUIO2, APUIO3
// write_port will run the APU until given clock before writing value
S9xAPUWritePort(Address & 3, Byte);
@ -1095,7 +1098,9 @@ void S9xSetPPU (uint8 Byte, uint16 Address)
uint8 S9xGetPPU (uint16 Address)
{
// MAP_PPU: $2000-$3FFF
if (Settings.MSU1 && (Address & 0xfff8) == 0x2000)
return (S9xMSU1ReadPort(Address & 7));
else
if (Address < 0x2100)
return (OpenBus);
@ -1125,7 +1130,6 @@ uint8 S9xGetPPU (uint16 Address)
if ((Address & 0xffc0) == 0x2140) // APUIO0, APUIO1, APUIO2, APUIO3
// read_port will run the APU until given APU time before reading value
return (S9xAPUReadPort(Address & 3));
else
if (Address <= 0x2183)
{
uint8 byte;

View File

@ -377,6 +377,7 @@ struct SSettings
bool8 BS;
bool8 BSXItself;
bool8 BSXBootup;
bool8 MSU1;
bool8 MouseMaster;
bool8 SuperScopeMaster;
bool8 JustifierMaster;