mirror of https://github.com/snes9xgit/snes9x.git
Begin adding MSU-1 support. Data streaming is working in Windows, audio streaming is not.
This commit is contained in:
parent
09df5e1406
commit
3c0f6bfdee
80
apu/apu.cpp
80
apu/apu.cpp
|
@ -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)
|
||||
|
|
4
cpu.cpp
4
cpu.cpp
|
@ -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();
|
||||
}
|
||||
|
|
1
getset.h
1
getset.h
|
@ -199,6 +199,7 @@
|
|||
#include "obc1.h"
|
||||
#include "seta.h"
|
||||
#include "bsx.h"
|
||||
#include "msu1.h"
|
||||
|
||||
#define addCyclesInMemoryAccess \
|
||||
if (!CPU.InDMAorHDMA) \
|
||||
|
|
|
@ -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;
|
||||
|
|
15
memmap.cpp
15
memmap.cpp
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
8
ppu.cpp
8
ppu.cpp
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue