Save state support for 68K, Z80, sound board, DSB1, and MPEG playback.

This commit is contained in:
Bart Trzynadlowski 2011-08-09 18:16:06 +00:00
parent 07fd26003e
commit 52cd9d834b
6 changed files with 329 additions and 60 deletions

View file

@ -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

View file

@ -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;

View file

@ -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();

View file

@ -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.

View file

@ -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)

View file

@ -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)
};