Here's more detail about how it works. Look carefully at SoundBoard.cpp:
- Code: Select all
// SCSP callback for running the 68K
int SCSP68KRunCallback(int numCycles)
{
return M68KRun(numCycles) - numCycles;
}
...
bool CSoundBoard::RunFrame(void)
{
// Run sound board first to generate SCSP audio
if (m_config["EmulateSound"].ValueAs<bool>())
{
M68KSetContext(&M68K);
SCSP_Update();
M68KGetContext(&M68K);
}
else
{
memset(audioL, 0, 44100/60*sizeof(INT16));
memset(audioR, 0, 44100/60*sizeof(INT16));
}
// Run DSB and mix with existing audio
if (NULL != DSB)
DSB->RunFrame(audioL, audioR);
ElSemi's SCSP emulator makes this even grosser than it needs to be. It actually runs the 68K emulation (!) from inside SCSP_Update() by calling a callback (SCSP68KRunCallback).
Beginning at CSoundBoard::RunFrame(), you can see that we first set the global 68K context (all the 68K emulation state variables: registers and memory pointers). Then we run SCSP_Update(), which will in turn run the 68K. I think it runs the 68K itself in order to generate IRQs at specific times (the timing of which is broken, but never mind for now).
After it has emulated one frame, it copies back the context using M68KGetContext() and then calls the DSB RunFrame function, which for DSB2 will be CDSB2::RunFrame():
- Code: Select all
void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
{
if (!m_config["EmulateDSB"].ValueAs<bool>())
{
// DSB code applies SCSP volume, too, so we must still mix
memset(mpegL, 0, (32000/60+2)*sizeof(INT16));
memset(mpegR, 0, (32000/60+2)*sizeof(INT16));
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, volume[0], volume[1], 44100/60, 32000/60+2, 44100, 32000);
return;
}
M68KSetContext(&M68K);
//printf("DSB2 run frame PC=%06X\n", M68KGetPC());
// While FIFO not empty...
while (fifoIdxR != fifoIdxW)
{
cmdLatch = fifo[fifoIdxR]; // retrieve next command byte
fifoIdxR++;
fifoIdxR &= 127;
M68KSetIRQ(1); // indicate pending command
//printf("68K INT fired\n");
M68KRun(500);
}
// Per-frame interrupt
M68KSetIRQ(2);
M68KRun(4000000/60);
M68KGetContext(&M68K);
// 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, volume[0], volume[1], 44100/60, 32000/60+2, 44100, 32000);
}
Here you can see that the DSB2's context is copied in, the 68K is executed, and then the context is read back.