mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-02-16 17:35:39 +00:00
Save state support for 68K, Z80, sound board, DSB1, and MPEG playback.
This commit is contained in:
parent
07fd26003e
commit
52cd9d834b
|
@ -1,3 +1,5 @@
|
|||
//TODO: MPEG_IsPlaying() -- must be saved to save state file so we don't continue playing when loading a silent state
|
||||
//TODO: amp can print error messages -- change them to Supermodel error messages
|
||||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
|
@ -28,7 +30,8 @@
|
|||
*
|
||||
* TODO List
|
||||
* ---------
|
||||
* - Music looping on DSB2 does not work in Daytona 2 (will play next track).
|
||||
* - Volume fade out in Daytona 2 is much too slow. Probably caused by 68K
|
||||
* timing or interrupts.
|
||||
* - Check actual MPEG sample rate. So far, all games seem to use 32 KHz, which
|
||||
* may be a hardware requirement, but if other sampling rates are allowable,
|
||||
* the code here will fail (it is hard coded for 32 KHz).
|
||||
|
@ -132,20 +135,30 @@ static inline INT16 MixAndClip(INT32 a, INT32 b)
|
|||
}
|
||||
|
||||
// Mixes audio and returns number of samples copied back to start of buffer (ie. offset at which new samples should be written)
|
||||
int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *inR, int sizeOut, int sizeIn, int outRate, int inRate)
|
||||
int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *inR, UINT8 volumeL, UINT8 volumeR, int sizeOut, int sizeIn, int outRate, int inRate)
|
||||
{
|
||||
int delta = (inRate<<8)/outRate; // (1/fout)/(1/fin)=fin/fout, 24.8 fixed point
|
||||
int outIdx = 0;
|
||||
int inIdx = 0;
|
||||
INT16 leftSample, rightSample;
|
||||
INT16 v[2];
|
||||
|
||||
// Scale volume from 0x00-0xFF -> 0x00-0x100 (24.8 fixed point)
|
||||
v[0] = (INT16) ((float) 0x100 * (float) volumeL / 255.0f);
|
||||
v[1] = (INT16) ((float) 0x100 * (float) volumeR / 255.0f);
|
||||
|
||||
// Up-sample and mix!
|
||||
while (outIdx < sizeOut)
|
||||
{
|
||||
// nFrac, pFrac will never exceed 1.0 (0x100) (only true if delta does not exceed 1)
|
||||
leftSample = ((int)inL[inIdx]*pFrac+(int)inL[inIdx+1]*nFrac) >> 8; // left channel
|
||||
rightSample = ((int)inR[inIdx]*pFrac+(int)inR[inIdx+1]*nFrac) >> 8; // right channel
|
||||
|
||||
// Apply volume
|
||||
leftSample = (leftSample*v[0]) >> 8;
|
||||
rightSample = (rightSample*v[0]) >> 8;
|
||||
|
||||
// Mix and output
|
||||
outL[outIdx] = MixAndClip(outL[outIdx], leftSample);
|
||||
outR[outIdx] = MixAndClip(outR[outIdx], rightSample);
|
||||
outIdx++;
|
||||
|
@ -213,15 +226,23 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data)
|
|||
|
||||
if (data == 1) // play without loop
|
||||
{
|
||||
//printf("====> Playing %06X (mpegEnd=%06X)\n", mpegStart, mpegEnd);
|
||||
MPEG_SetLoop(NULL, 0);
|
||||
//printf("====> Playing %06X\n", mpegStart);
|
||||
usingLoopStart = 0; // save the settings of the MPEG currently playing
|
||||
usingLoopEnd = 0;
|
||||
usingMPEGStart = mpegStart;
|
||||
usingMPEGEnd = mpegEnd;
|
||||
MPEG_PlayMemory((const char *) &mpegROM[mpegStart], mpegEnd-mpegStart);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data == 2) // play with loop
|
||||
if (data == 2) // play with loop (NOTE: I don't think this actually loops b/c MPEG_PlayMemory() clears lstart)
|
||||
{
|
||||
//printf("====> Playing %06X\n", mpegStart);
|
||||
//printf("====> Playing w/ loop %06X (mpegEnd=%06X, loopStart=%06X, loopEnd=%06X)\n", mpegStart, mpegEnd, loopStart, loopEnd);
|
||||
usingLoopStart = 0; // MPEG_PlayMemory() clears these settings and Z80 will set them up later
|
||||
usingLoopEnd = 0;
|
||||
usingMPEGStart = mpegStart;
|
||||
usingMPEGEnd = mpegEnd;
|
||||
MPEG_PlayMemory((const char *) &mpegROM[mpegStart], mpegEnd-mpegStart);
|
||||
return;
|
||||
}
|
||||
|
@ -253,11 +274,15 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data)
|
|||
// SWA: if loop end is zero, it means "keep previous end marker"
|
||||
if (loopEnd == 0)
|
||||
{
|
||||
MPEG_SetLoop((const char *) &mpegROM[loopStart], mpegEnd-loopStart);
|
||||
usingLoopStart = loopStart;
|
||||
usingLoopEnd = mpegEnd-loopStart;
|
||||
MPEG_SetLoop((const char *) &mpegROM[usingLoopStart], usingLoopEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
MPEG_SetLoop((const char *) &mpegROM[loopStart], loopEnd-loopStart);
|
||||
usingLoopStart = loopStart;
|
||||
usingLoopEnd = loopEnd-loopStart;
|
||||
MPEG_SetLoop((const char *) &mpegROM[usingLoopStart], usingLoopEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,12 +311,15 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data)
|
|||
{
|
||||
loopEnd = endLatch;
|
||||
//printf("loopEnd = %08X\n", loopEnd);
|
||||
MPEG_SetLoop((const char *) &mpegROM[loopStart], loopEnd-loopStart);
|
||||
usingLoopStart = loopStart;
|
||||
usingLoopEnd = loopEnd-loopStart;
|
||||
MPEG_SetLoop((const char *) &mpegROM[usingLoopStart], usingLoopEnd);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE8: // MPEG volume
|
||||
volume = data;
|
||||
volume = 0x7F-data;
|
||||
//printf("Set Volume: %02X\n", volume);
|
||||
break;
|
||||
|
||||
case 0xE9: // MPEG stereo
|
||||
|
@ -383,7 +411,8 @@ void CDSB1::SendCommand(UINT8 data)
|
|||
void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR)
|
||||
{
|
||||
#ifdef SUPERMODEL_SOUND
|
||||
int cycles;
|
||||
int cycles;
|
||||
UINT8 v;
|
||||
|
||||
// While FIFO not empty, fire interrupts, run for up to one frame
|
||||
for (cycles = (4000000/60)/4; (cycles > 0) && (fifoIdxR != fifoIdxW); )
|
||||
|
@ -398,10 +427,13 @@ void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR)
|
|||
|
||||
//printf("VOLUME=%02X STEREO=%02X\n", volume, stereo);
|
||||
|
||||
// Convert volume from 0x00-0x7F -> 0x00-0xFF
|
||||
v = (UINT8) ((float) 255.0f * (float) volume /127.0f);
|
||||
|
||||
// Decode MPEG for this frame
|
||||
INT16 *mpegFill[2] = { &mpegL[retainedSamples], &mpegR[retainedSamples] };
|
||||
MPEG_Decode(mpegFill, 32000/60-retainedSamples+2);
|
||||
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, 44100/60, 32000/60+2, 44100, 32000);
|
||||
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, v, v, 44100/60, 32000/60+2, 44100, 32000);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@ -417,11 +449,93 @@ void CDSB1::Reset(void)
|
|||
|
||||
status = 1;
|
||||
mpegState = 0; // why doesn't RB ever init this?
|
||||
volume = 0x7F; // full volume
|
||||
usingLoopStart = 0;
|
||||
|
||||
Z80.Reset();
|
||||
DebugLog("DSB1 Reset\n");
|
||||
}
|
||||
|
||||
void CDSB1::SaveState(CBlockFile *StateFile)
|
||||
{
|
||||
UINT32 mpegPos;
|
||||
UINT8 isPlaying;
|
||||
|
||||
StateFile->NewBlock("DSB1", __FILE__);
|
||||
|
||||
// MPEG playback state
|
||||
isPlaying = (UINT8) MPEG_IsPlaying();
|
||||
mpegPos = MPEG_GetProgress();
|
||||
StateFile->Write(&isPlaying, sizeof(isPlaying));
|
||||
StateFile->Write(&mpegPos, sizeof(mpegPos));
|
||||
StateFile->Write(&usingMPEGStart, sizeof(usingMPEGStart));
|
||||
StateFile->Write(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
||||
StateFile->Write(&usingLoopStart, sizeof(usingLoopStart));
|
||||
StateFile->Write(&usingLoopEnd, sizeof(usingLoopEnd));
|
||||
|
||||
// MPEG board state
|
||||
StateFile->Write(ram, 0x8000);
|
||||
StateFile->Write(fifo, sizeof(fifo));
|
||||
StateFile->Write(&fifoIdxR, sizeof(fifoIdxR));
|
||||
StateFile->Write(&fifoIdxW, sizeof(fifoIdxW));
|
||||
StateFile->Write(&mpegStart, sizeof(mpegStart));
|
||||
StateFile->Write(&mpegEnd, sizeof(mpegEnd));
|
||||
StateFile->Write(&mpegState, sizeof(mpegState));
|
||||
StateFile->Write(&loopStart, sizeof(loopStart));
|
||||
StateFile->Write(&loopEnd, sizeof(loopEnd));
|
||||
StateFile->Write(&status, sizeof(status));
|
||||
StateFile->Write(&cmdLatch, sizeof(cmdLatch));
|
||||
StateFile->Write(&volume, sizeof(volume));
|
||||
StateFile->Write(&stereo, sizeof(stereo));
|
||||
|
||||
// Z80 CPU state
|
||||
Z80.SaveState(StateFile, "DSB1 Z80");
|
||||
}
|
||||
|
||||
void CDSB1::LoadState(CBlockFile *StateFile)
|
||||
{
|
||||
UINT32 mpegPos;
|
||||
UINT8 isPlaying;
|
||||
|
||||
if (OKAY != StateFile->FindBlock("DSB1"))
|
||||
{
|
||||
ErrorLog("Unable to load Digital Sound Board state. Save state file is corrupted.");
|
||||
return;
|
||||
}
|
||||
|
||||
StateFile->Read(&isPlaying, sizeof(isPlaying));
|
||||
StateFile->Read(&mpegPos, sizeof(mpegPos));
|
||||
StateFile->Read(&usingMPEGStart, sizeof(usingMPEGStart));
|
||||
StateFile->Read(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
||||
StateFile->Read(&usingLoopStart, sizeof(usingLoopStart));
|
||||
StateFile->Read(&usingLoopEnd, sizeof(usingLoopEnd));
|
||||
StateFile->Read(ram, 0x8000);
|
||||
StateFile->Read(fifo, sizeof(fifo));
|
||||
StateFile->Read(&fifoIdxR, sizeof(fifoIdxR));
|
||||
StateFile->Read(&fifoIdxW, sizeof(fifoIdxW));
|
||||
StateFile->Read(&mpegStart, sizeof(mpegStart));
|
||||
StateFile->Read(&mpegEnd, sizeof(mpegEnd));
|
||||
StateFile->Read(&mpegState, sizeof(mpegState));
|
||||
StateFile->Read(&loopStart, sizeof(loopStart));
|
||||
StateFile->Read(&loopEnd, sizeof(loopEnd));
|
||||
StateFile->Read(&status, sizeof(status));
|
||||
StateFile->Read(&cmdLatch, sizeof(cmdLatch));
|
||||
StateFile->Read(&volume, sizeof(volume));
|
||||
StateFile->Read(&stereo, sizeof(stereo));
|
||||
|
||||
Z80.LoadState(StateFile, "DSB1 Z80");
|
||||
|
||||
// Restart MPEG audio at the appropriate position
|
||||
if (isPlaying)
|
||||
{
|
||||
MPEG_PlayMemory((const char *) &mpegROM[usingMPEGStart], usingMPEGEnd-usingMPEGStart);
|
||||
MPEG_SetLoop((const char *) &mpegROM[usingLoopStart], usingLoopEnd);
|
||||
MPEG_SetOffset(mpegPos);
|
||||
}
|
||||
else
|
||||
MPEG_StopPlaying();
|
||||
}
|
||||
|
||||
// Offsets of memory regions within DSB2's pool
|
||||
#define DSB1_OFFSET_RAM 0 // 32KB Z80 RAM
|
||||
#define DSB1_OFFSET_MPEG_LEFT 0x8000 // 1604 bytes (48 KHz max., 1/60th second, 2 extra = 2*(48000/60+2)) left MPEG buffer
|
||||
|
@ -507,17 +621,22 @@ enum
|
|||
ST_GOT74,
|
||||
ST_GOTA0,
|
||||
ST_GOTA1,
|
||||
ST_GOTA3,
|
||||
ST_GOTA4,
|
||||
ST_GOTA5,
|
||||
ST_GOTA7,
|
||||
ST_GOTB0,
|
||||
ST_GOTB1,
|
||||
ST_GOTB2,
|
||||
ST_GOTB4,
|
||||
ST_GOTB5,
|
||||
ST_GOTB6
|
||||
};
|
||||
|
||||
static const char *stateName[] =
|
||||
{
|
||||
"idle",
|
||||
"st_got_14",
|
||||
"st_14_0",
|
||||
"st_14_1",
|
||||
"st_got24",
|
||||
|
@ -526,12 +645,16 @@ static const char *stateName[] =
|
|||
"st_got74",
|
||||
"st_gota0",
|
||||
"st_gota1",
|
||||
"st_gota3",
|
||||
"st_gota4",
|
||||
"st_gota5",
|
||||
"st_gota7",
|
||||
"st_gotb0",
|
||||
"st_gotb1",
|
||||
"st_gotb2",
|
||||
"st_gotb4",
|
||||
"st_gotb5"
|
||||
"st_gotb5",
|
||||
"st_gotb6"
|
||||
};
|
||||
|
||||
void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
||||
|
@ -561,13 +684,17 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
|||
|
||||
else if (byte == 0xa0) mpegState = ST_GOTA0;
|
||||
else if (byte == 0xa1) mpegState = ST_GOTA1;
|
||||
else if (byte == 0xA3) mpegState = ST_GOTA3; // volume (front right?)
|
||||
else if (byte == 0xa4) mpegState = ST_GOTA4;
|
||||
else if (byte == 0xa5) mpegState = ST_GOTA5;
|
||||
else if (byte == 0xA7) mpegState = ST_GOTA7; // volume (rear right?)
|
||||
|
||||
else if (byte == 0xb0) mpegState = ST_GOTB0;
|
||||
else if (byte == 0xb1) mpegState = ST_GOTB1;
|
||||
else if (byte == 0xB2) mpegState = ST_GOTB2; // volume (front left?)
|
||||
else if (byte == 0xb4) mpegState = ST_GOTB4;
|
||||
else if (byte == 0xb5) mpegState = ST_GOTB5;
|
||||
else if (byte == 0xB6) mpegState = ST_GOTB6; // volume (rear left?)
|
||||
|
||||
break;
|
||||
|
||||
|
@ -614,16 +741,13 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
|||
// mixer_set_stereo_pan(0, MIXER_PAN_RIGHT, MIXER_PAN_LEFT);
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
case ST_GOTA0:
|
||||
// case ST_GOTA0:
|
||||
// ch 0 mono
|
||||
// mixer_set_stereo_volume(0, 0, 255);
|
||||
// printf("ch 0 mono\n");
|
||||
// mixer_set_stereo_pan(0, MIXER_PAN_CENTER, MIXER_PAN_CENTER);
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
case ST_GOTA1:
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
// break;
|
||||
case ST_GOTA4: // dayto2pe plays advertise tune from this state by writing 0x75
|
||||
mpegState = ST_IDLE;
|
||||
if (byte == 0x75)
|
||||
|
@ -636,9 +760,6 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
|||
case ST_GOTA5:
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
case ST_GOTB0:
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
case ST_GOTB1:
|
||||
// ch 1 mono
|
||||
// printf("ch 1 mono\n");
|
||||
|
@ -652,6 +773,35 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
|||
case ST_GOTB5:
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
|
||||
/*
|
||||
* Speaker Volume:
|
||||
*
|
||||
* Daytona 2 uses B6, A7 for rear speakers; B2, A3 for front speakers.
|
||||
*
|
||||
* Star Wars Trilogy uses B0, A1 but B4, A4, B1, A1, B5, and A5 may be
|
||||
* volume/speaker related.
|
||||
*
|
||||
* Sega Rally 2 uses B0, A0 and B4, A4 when setting all four but B0, A1
|
||||
* when only setting two (in-game)
|
||||
*/
|
||||
case ST_GOTB6: // rear left(?) volume
|
||||
case ST_GOTB0: // left volume
|
||||
volume[0] = byte;
|
||||
printf("Set L Volume: %02X\n", byte);
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
case ST_GOTA7: // rear right(?) volume
|
||||
case ST_GOTA1: // right volume
|
||||
case ST_GOTA0:
|
||||
volume[1] = byte;
|
||||
printf("Set R Volume: %02X\n", byte);
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
case ST_GOTB2:
|
||||
case ST_GOTA3:
|
||||
mpegState = ST_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -812,7 +962,7 @@ void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
|
|||
// Decode MPEG for this frame
|
||||
INT16 *mpegFill[2] = { &mpegL[retainedSamples], &mpegR[retainedSamples] };
|
||||
MPEG_Decode(mpegFill, 32000/60-retainedSamples+2);
|
||||
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, 44100/60, 32000/60+2, 44100, 32000);
|
||||
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, volume[0], volume[1], 44100/60, 32000/60+2, 44100, 32000);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@ -830,6 +980,8 @@ void CDSB2::Reset(void)
|
|||
mpegStart = 0;
|
||||
mpegEnd = 0;
|
||||
playing = 0;
|
||||
volume[0] = 0xFF; // set to max volume in case we miss the volume commands
|
||||
volume[1] = 0xFF;
|
||||
|
||||
M68KSetContext(&M68K);
|
||||
M68KReset();
|
||||
|
@ -838,6 +990,14 @@ void CDSB2::Reset(void)
|
|||
DebugLog("DSB2 Reset\n");
|
||||
}
|
||||
|
||||
void CDSB2::SaveState(CBlockFile *StateFile)
|
||||
{
|
||||
}
|
||||
|
||||
void CDSB2::LoadState(CBlockFile *StateFile)
|
||||
{
|
||||
}
|
||||
|
||||
// Offsets of memory regions within DSB2's pool
|
||||
#define DSB2_OFFSET_RAM 0 // 128KB 68K RAM
|
||||
#define DSB2_OFFSET_MPEG_LEFT 0x20000 // 1604 bytes (48 KHz max., 1/60th second, 2 extra = 2*(48000/60+2)) left MPEG buffer
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
class CDSBResampler
|
||||
{
|
||||
public:
|
||||
int UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *inR, int sizeOut, int sizeIn, int outRate, int inRate);
|
||||
int UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *inR, UINT8 volumeL, UINT8 volumeR, int sizeOut, int sizeIn, int outRate, int inRate);
|
||||
void Reset(void);
|
||||
private:
|
||||
int nFrac;
|
||||
|
@ -110,6 +110,26 @@ public:
|
|||
*/
|
||||
virtual void Reset(void) = 0;
|
||||
|
||||
/*
|
||||
* SaveState(SaveState):
|
||||
*
|
||||
* Saves an image of the current device state.
|
||||
*
|
||||
* Parameters:
|
||||
* SaveState Block file to save state information to.
|
||||
*/
|
||||
virtual void SaveState(CBlockFile *SaveState) = 0;
|
||||
|
||||
/*
|
||||
* LoadState(SaveState):
|
||||
*
|
||||
* Loads and a state image.
|
||||
*
|
||||
* Parameters:
|
||||
* SaveState Block file to load state information from.
|
||||
*/
|
||||
virtual void LoadState(CBlockFile *SaveState) = 0;
|
||||
|
||||
/*
|
||||
* Init(progROMPtr, mpegROMPtr):
|
||||
*
|
||||
|
@ -152,6 +172,8 @@ public:
|
|||
void SendCommand(UINT8 data);
|
||||
void RunFrame(INT16 *audioL, INT16 *audioR);
|
||||
void Reset(void);
|
||||
void SaveState(CBlockFile *StateFile);
|
||||
void LoadState(CBlockFile *StateFile);
|
||||
BOOL Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr);
|
||||
|
||||
// Constructor and destructor
|
||||
|
@ -184,12 +206,18 @@ private:
|
|||
int loopStart;
|
||||
int loopEnd;
|
||||
|
||||
// Settings of currently playing stream (may not match the playback register variables above)
|
||||
UINT32 usingLoopStart; // what was last set by MPEG_SetLoop() or MPEG_PlayMemory()
|
||||
UINT32 usingLoopEnd;
|
||||
UINT32 usingMPEGStart; // what was last set by MPEG_PlayMemory()
|
||||
UINT32 usingMPEGEnd;
|
||||
|
||||
// Registers
|
||||
UINT32 startLatch; // MPEG start address latch
|
||||
UINT32 endLatch; // MPEG end address latch
|
||||
UINT8 status;
|
||||
UINT8 cmdLatch;
|
||||
UINT8 volume;
|
||||
UINT8 volume; // 0x00-0x7F
|
||||
UINT8 stereo;
|
||||
|
||||
// Z80 CPU
|
||||
|
@ -216,6 +244,8 @@ public:
|
|||
void SendCommand(UINT8 data);
|
||||
void RunFrame(INT16 *audioL, INT16 *audioR);
|
||||
void Reset(void);
|
||||
void SaveState(CBlockFile *StateFile);
|
||||
void LoadState(CBlockFile *StateFile);
|
||||
BOOL Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr);
|
||||
|
||||
// Constructor and destructor
|
||||
|
@ -245,9 +275,10 @@ private:
|
|||
int fifoIdxW; // write position
|
||||
|
||||
// Registers
|
||||
int cmdLatch;
|
||||
int mpegState;
|
||||
int mpegStart, mpegEnd, playing;
|
||||
int cmdLatch;
|
||||
int mpegState;
|
||||
int mpegStart, mpegEnd, playing;
|
||||
UINT8 volume[2]; // left, right volume (0x00-0xFF)
|
||||
|
||||
// M68K CPU
|
||||
M68KCtx M68K;
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
//TODO: Update save state file format (must output) MIDI control port; will no longer be compatible with 0.1a save states
|
||||
//TODO: Star Wars expects bit 0x80 to be set when reading from MIDI control port. This should be made explicit! Lostwsga may behave similarly
|
||||
|
||||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
|
@ -812,7 +809,8 @@ UINT8 CModel3::Read8(UINT32 addr)
|
|||
|
||||
// Sound Board
|
||||
case 0x08:
|
||||
printf("PPC: Read8 %08X\n", addr);
|
||||
if ((addr&0xF) == 4) // MIDI control port
|
||||
return 0xFF; // one of these bits (0x80?) indicates "ready"
|
||||
break;
|
||||
|
||||
// System registers
|
||||
|
@ -889,7 +887,7 @@ UINT16 CModel3::Read16(UINT32 addr)
|
|||
|
||||
// Sound Board
|
||||
case 0x08:
|
||||
printf("PPC: Read16 %08X\n", addr);
|
||||
//printf("PPC: Read16 %08X\n", addr);
|
||||
break;
|
||||
|
||||
// MPC105
|
||||
|
@ -981,7 +979,7 @@ UINT32 CModel3::Read32(UINT32 addr)
|
|||
|
||||
// Sound Board
|
||||
case 0x08:
|
||||
printf("PPC: Read32 %08X\n", addr);
|
||||
//printf("PPC: Read32 %08X\n", addr);
|
||||
break;
|
||||
|
||||
// Backup RAM
|
||||
|
@ -1126,7 +1124,7 @@ void CModel3::Write8(UINT32 addr, UINT8 data)
|
|||
|
||||
// Sound Board
|
||||
case 0x08:
|
||||
printf("PPC: %08X=%02X * (PC=%08X, LR=%08X)\n", addr, data, ppc_get_pc(), ppc_get_lr());
|
||||
//printf("PPC: %08X=%02X * (PC=%08X, LR=%08X)\n", addr, data, ppc_get_pc(), ppc_get_lr());
|
||||
if ((addr&0xF) == 0) // MIDI data port
|
||||
SoundBoard.WriteMIDIPort(data);
|
||||
else if ((addr&0xF) == 4) // MIDI control port
|
||||
|
@ -1207,7 +1205,7 @@ void CModel3::Write16(UINT32 addr, UINT16 data)
|
|||
{
|
||||
// Sound Board
|
||||
case 0x08:
|
||||
printf("PPC: %08X=%04X\n", addr, data);
|
||||
//printf("%08X=%04X\n", addr, data);
|
||||
break;
|
||||
|
||||
// Backup RAM
|
||||
|
@ -1245,9 +1243,6 @@ void CModel3::Write16(UINT32 addr, UINT16 data)
|
|||
|
||||
void CModel3::Write32(UINT32 addr, UINT32 data)
|
||||
{
|
||||
//if (addr==0x100060 || addr==0x100180)
|
||||
// printf(" %08X=%08X\n", addr, data);
|
||||
|
||||
if ((addr&3))
|
||||
{
|
||||
Write16(addr+0,data>>16);
|
||||
|
@ -1316,7 +1311,7 @@ void CModel3::Write32(UINT32 addr, UINT32 data)
|
|||
|
||||
// Sound Board
|
||||
case 0x08:
|
||||
printf("PPC: %08X=%08X\n", addr, data);
|
||||
//printf("PPC: %08X=%08X\n", addr, data);
|
||||
break;
|
||||
|
||||
// Backup RAM
|
||||
|
@ -1470,6 +1465,11 @@ UINT8 CModel3::Read8(UINT32 addr)
|
|||
return GPU.ReadDMARegister8(addr&0xFF);
|
||||
else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040)))
|
||||
return ReadInputs(addr&0x3F);
|
||||
else if (((addr>=0xF0080000) && (addr<=0xF0080007)) || ((addr>=0xFE080000) && (addr<=0xFE080007)))
|
||||
{
|
||||
if ((addr&0xF) == 4) // MIDI control port
|
||||
return 0xFF;
|
||||
}
|
||||
else if (((addr>=0xF00C0000) && (addr<0xF00DFFFF)) || ((addr>=0xFE0C0000) && (addr<0xFE0DFFFF)))
|
||||
return backupRAM[(addr&0x1FFFF)^3];
|
||||
else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040)))
|
||||
|
@ -1791,6 +1791,7 @@ void CModel3::SaveState(CBlockFile *SaveState)
|
|||
SaveState->Write(ram, 0x800000);
|
||||
SaveState->Write(backupRAM, 0x20000);
|
||||
SaveState->Write(securityRAM, 0x20000);
|
||||
SaveState->Write(&midiCtrlPort, sizeof(midiCtrlPort));
|
||||
|
||||
// All devices...
|
||||
ppc_save_state(SaveState);
|
||||
|
@ -1800,6 +1801,7 @@ void CModel3::SaveState(CBlockFile *SaveState)
|
|||
EEPROM.SaveState(SaveState);
|
||||
TileGen.SaveState(SaveState);
|
||||
GPU.SaveState(SaveState);
|
||||
SoundBoard.SaveState(SaveState); // also saves DSB state
|
||||
}
|
||||
|
||||
void CModel3::LoadState(CBlockFile *SaveState)
|
||||
|
@ -1822,6 +1824,7 @@ void CModel3::LoadState(CBlockFile *SaveState)
|
|||
SaveState->Read(ram, 0x800000);
|
||||
SaveState->Read(backupRAM, 0x20000);
|
||||
SaveState->Read(securityRAM, 0x20000);
|
||||
SaveState->Read(&midiCtrlPort, sizeof(midiCtrlPort));
|
||||
|
||||
// All devices...
|
||||
GPU.LoadState(SaveState);
|
||||
|
@ -1831,6 +1834,7 @@ void CModel3::LoadState(CBlockFile *SaveState)
|
|||
PCIBridge.LoadState(SaveState);
|
||||
IRQ.LoadState(SaveState);
|
||||
ppc_load_state(SaveState);
|
||||
SoundBoard.LoadState(SaveState);
|
||||
}
|
||||
|
||||
void CModel3::SaveNVRAM(CBlockFile *NVRAM)
|
||||
|
@ -2375,7 +2379,7 @@ void CModel3::Patch(void)
|
|||
{
|
||||
*(UINT32 *) &crom[0xF6DD0] = 0x60000000; // from MAME
|
||||
|
||||
//*(UINT32 *) &crom[0xF1128] = 0x60000000;
|
||||
//*(UINT32 *) &crom[0xF1128] = 0x60000000; // these bypass required delay loops and break game timing
|
||||
//*(UINT32 *) &crom[0xF10E0] = 0x60000000;
|
||||
}
|
||||
else if (!strcmp(Game->id, "eca") || !strcmp(Game->id, "ecax"))
|
||||
|
@ -2707,15 +2711,15 @@ CModel3::CModel3(void)
|
|||
|
||||
CModel3::~CModel3(void)
|
||||
{
|
||||
// Dump some files first
|
||||
//#if 0
|
||||
// Debug: dump some files
|
||||
#if 0
|
||||
Dump("ram", ram, 0x800000, TRUE, FALSE);
|
||||
//Dump("vrom", vrom, 0x4000000, TRUE, FALSE);
|
||||
Dump("crom", crom, 0x800000, TRUE, FALSE);
|
||||
//Dump("bankedCrom", &crom[0x800000], 0x7000000, TRUE, FALSE);
|
||||
//Dump("soundROM", soundROM, 0x80000, FALSE, TRUE);
|
||||
//Dump("sampleROM", sampleROM, 0x800000, FALSE, TRUE);
|
||||
//#endif
|
||||
#endif
|
||||
|
||||
// Stop all threads
|
||||
StopThreads();
|
||||
|
|
|
@ -121,7 +121,8 @@ public:
|
|||
/*
|
||||
* SaveState(SaveState):
|
||||
*
|
||||
* Saves an image of the current state.
|
||||
* Saves an image of the current state. Must never be called while emulator
|
||||
* is running (inside RunFrame()).
|
||||
*
|
||||
* Parameters:
|
||||
* SaveState Block file to save state information to.
|
||||
|
@ -131,7 +132,10 @@ public:
|
|||
/*
|
||||
* LoadState(SaveState):
|
||||
*
|
||||
* Loads and resumes execution from a state image.
|
||||
* Loads and resumes execution from a state image. Modifies data that may
|
||||
* be used by multiple threads -- use with caution and ensure threads are
|
||||
* not accessing data that will be touched. Must never be called while
|
||||
* emulator is running (inside RunFrame()).
|
||||
*
|
||||
* Parameters:
|
||||
* SaveState Block file to load state information from.
|
||||
|
|
|
@ -65,9 +65,23 @@ static FILE *soundFP;
|
|||
|
||||
|
||||
/******************************************************************************
|
||||
68K Access Handlers
|
||||
68K Address Space Handlers
|
||||
******************************************************************************/
|
||||
|
||||
void CSoundBoard::UpdateROMBanks(void)
|
||||
{
|
||||
if ((ctrlReg&0x10))
|
||||
{
|
||||
sampleBankLo = &sampleROM[0xA00000];
|
||||
sampleBankHi = &sampleROM[0xE00000];
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleBankLo = &sampleROM[0x200000];
|
||||
sampleBankHi = &sampleROM[0x600000];
|
||||
}
|
||||
}
|
||||
|
||||
UINT8 CSoundBoard::Read8(UINT32 a)
|
||||
{
|
||||
switch ((a>>20)&0xF)
|
||||
|
@ -228,16 +242,8 @@ void CSoundBoard::Write8(unsigned int a,unsigned char d)
|
|||
default:
|
||||
if (a == 0x400001)
|
||||
{
|
||||
if ((d&0x10))
|
||||
{
|
||||
sampleBankLo = &sampleROM[0xA00000];
|
||||
sampleBankHi = &sampleROM[0xE00000];
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleBankLo = &sampleROM[0x200000];
|
||||
sampleBankHi = &sampleROM[0x600000];
|
||||
}
|
||||
ctrlReg = d;
|
||||
UpdateROMBanks();
|
||||
}
|
||||
else
|
||||
printf("68K: Unknown write8 %06X=%02X\n", a, d);
|
||||
|
@ -359,6 +365,7 @@ void CSoundBoard::RunFrame(void)
|
|||
M68KSetContext(&M68K);
|
||||
SCSP_Update();
|
||||
M68KGetContext(&M68K);
|
||||
//memset(audioL, 0, 44100/60*sizeof(INT16));memset(audioR, 0, 44100/60*sizeof(INT16)); // clear, I want DSB only
|
||||
|
||||
// Run DSB and mix with existing audio
|
||||
if (NULL != DSB)
|
||||
|
@ -384,8 +391,8 @@ void CSoundBoard::RunFrame(void)
|
|||
void CSoundBoard::Reset(void)
|
||||
{
|
||||
memcpy(ram1, soundROM, 16); // copy 68K vector table
|
||||
sampleBankLo = &sampleROM[0x200000]; // default banks
|
||||
sampleBankHi = &sampleROM[0x600000];
|
||||
ctrlReg = 0; // set default banks
|
||||
UpdateROMBanks();
|
||||
M68KSetContext(&M68K);
|
||||
M68KReset();
|
||||
M68KGetContext(&M68K);
|
||||
|
@ -394,6 +401,43 @@ void CSoundBoard::Reset(void)
|
|||
DebugLog("Sound Board Reset\n");
|
||||
}
|
||||
|
||||
void CSoundBoard::SaveState(CBlockFile *SaveState)
|
||||
{
|
||||
SaveState->NewBlock("Sound Board", __FILE__);
|
||||
SaveState->Write(ram1, 0x100000);
|
||||
SaveState->Write(ram2, 0x100000);
|
||||
SaveState->Write(&ctrlReg, sizeof(ctrlReg));
|
||||
|
||||
// All other devices...
|
||||
M68KSetContext(&M68K);
|
||||
M68KSaveState(SaveState, "Sound Board 68K");
|
||||
SCSP_SaveState(SaveState);
|
||||
if (NULL != DSB)
|
||||
DSB->SaveState(SaveState);
|
||||
}
|
||||
|
||||
void CSoundBoard::LoadState(CBlockFile *SaveState)
|
||||
{
|
||||
if (OKAY != SaveState->FindBlock("Sound Board"))
|
||||
{
|
||||
ErrorLog("Unable to load sound board state. Save state file is corrupted.");
|
||||
return;
|
||||
}
|
||||
|
||||
SaveState->Read(ram1, 0x100000);
|
||||
SaveState->Read(ram2, 0x100000);
|
||||
SaveState->Read(&ctrlReg, sizeof(ctrlReg));
|
||||
UpdateROMBanks();
|
||||
|
||||
// All other devices
|
||||
M68KSetContext(&M68K); // so we don't lose callback pointers when copying context back
|
||||
M68KLoadState(SaveState, "Sound Board 68K");
|
||||
M68KGetContext(&M68K);
|
||||
SCSP_LoadState(SaveState);
|
||||
if (NULL != DSB)
|
||||
DSB->LoadState(SaveState);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Configuration, Initialization, and Shutdown
|
||||
|
@ -419,9 +463,9 @@ BOOL CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr)
|
|||
// Receive sound ROMs
|
||||
soundROM = soundROMPtr;
|
||||
sampleROM = sampleROMPtr;
|
||||
sampleBankLo = &sampleROM[0x200000];
|
||||
sampleBankHi = &sampleROM[0x600000];
|
||||
|
||||
ctrlReg = 0;
|
||||
UpdateROMBanks();
|
||||
|
||||
// Allocate all memory for RAM
|
||||
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
|
||||
if (NULL == memoryPool)
|
||||
|
|
|
@ -84,6 +84,26 @@ public:
|
|||
*/
|
||||
void WriteMIDIPort(UINT8 data);
|
||||
|
||||
/*
|
||||
* SaveState(SaveState):
|
||||
*
|
||||
* Saves an image of the current device state.
|
||||
*
|
||||
* Parameters:
|
||||
* SaveState Block file to save state information to.
|
||||
*/
|
||||
void SaveState(CBlockFile *SaveState);
|
||||
|
||||
/*
|
||||
* LoadState(SaveState):
|
||||
*
|
||||
* Loads and a state image.
|
||||
*
|
||||
* Parameters:
|
||||
* SaveState Block file to load state information from.
|
||||
*/
|
||||
void LoadState(CBlockFile *SaveState);
|
||||
|
||||
/*
|
||||
* RunFrame(void):
|
||||
*
|
||||
|
@ -135,6 +155,9 @@ public:
|
|||
~CSoundBoard(void);
|
||||
|
||||
private:
|
||||
// Private helper functions
|
||||
void UpdateROMBanks(void);
|
||||
|
||||
// Digital Sound Board
|
||||
CDSB *DSB;
|
||||
|
||||
|
@ -149,6 +172,9 @@ private:
|
|||
UINT8 *memoryPool; // single allocated region for all sound board RAM
|
||||
UINT8 *ram1, *ram2; // SCSP1 and SCSP2 RAM
|
||||
|
||||
// Registers
|
||||
UINT8 ctrlReg; // control register: ROM banking
|
||||
|
||||
// Audio
|
||||
INT16 *audioL, *audioR; // left and right audio channels (1/60th second, 44.1 KHz)
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue