mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-04-10 19:15:14 +00:00
DSB2 IRQ 2 now fires at 1KHz rather than once per frame, improving
music timing in Daytona USA 2 and Sega Rally 2. DSB1 CPU timing increased from 1MHz to 4MHz, improving music fade timing in Scud Race. Thanks to gm_matthew for these discoveries.
This commit is contained in:
parent
e3374256ff
commit
46b1de2238
|
@ -1,12 +1,13 @@
|
||||||
/**
|
/**
|
||||||
** Supermodel
|
** Supermodel
|
||||||
** A Sega Model 3 Arcade Emulator.
|
** A Sega Model 3 Arcade Emulator.
|
||||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
** Copyright 2011-2021 Bart Trzynadlowski, Nik Henson, Ian Curtis,
|
||||||
|
** Harry Tuttle, and Spindizzi
|
||||||
**
|
**
|
||||||
** This file is part of Supermodel.
|
** This file is part of Supermodel.
|
||||||
**
|
**
|
||||||
** Supermodel is free software: you can redistribute it and/or modify it under
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
** the terms of the GNU General Public License as published by the Free
|
** the terms of the GNU General Public License as published by the Free
|
||||||
** Software Foundation, either version 3 of the License, or (at your option)
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
** any later version.
|
** any later version.
|
||||||
**
|
**
|
||||||
|
@ -18,12 +19,12 @@
|
||||||
** You should have received a copy of the GNU General Public License along
|
** You should have received a copy of the GNU General Public License along
|
||||||
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DSB.cpp
|
* DSB.cpp
|
||||||
*
|
*
|
||||||
* Sega Digital Sound Board (MPEG audio). Implementation of the CDSB1 and CDSB2
|
* Sega Digital Sound Board (MPEG audio). Implementation of the CDSB1 and CDSB2
|
||||||
* classes. Based on code donated by R. Belmont. Many Bothans died to bring us
|
* classes. Based on code donated by R. Belmont. Many Bothans died to bring us
|
||||||
* this emulation.
|
* this emulation.
|
||||||
*
|
*
|
||||||
* TODO List
|
* TODO List
|
||||||
|
@ -43,61 +44,61 @@
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
Resampler
|
Resampler
|
||||||
|
|
||||||
MPEG Layer 2 audio can be 32, 44.1, or 48 KHz. Here, an up-sampling algorithm
|
MPEG Layer 2 audio can be 32, 44.1, or 48 KHz. Here, an up-sampling algorithm
|
||||||
is provided, which should work for any frequency less than 44.1 KHz and an
|
is provided, which should work for any frequency less than 44.1 KHz and an
|
||||||
output frequency of 44.1 KHz. Down-sampling is not yet implemented, but would
|
output frequency of 44.1 KHz. Down-sampling is not yet implemented, but would
|
||||||
work in a similar fashion. The chief difference is that the input index would
|
work in a similar fashion. The chief difference is that the input index would
|
||||||
sometimes advance by more than one for a single output sample and the
|
sometimes advance by more than one for a single output sample and the
|
||||||
fractions, nFrac and pFrac, would sometimes exceed 1.0.
|
fractions, nFrac and pFrac, would sometimes exceed 1.0.
|
||||||
|
|
||||||
Up-Sampling Description
|
Up-Sampling Description
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Linear interpolation is used to up-sample. Not as accurate as the Shannon
|
Linear interpolation is used to up-sample. Not as accurate as the Shannon
|
||||||
reconstruction equation but it seems to work quite well.
|
reconstruction equation but it seems to work quite well.
|
||||||
|
|
||||||
1. Linear Interpolation
|
1. Linear Interpolation
|
||||||
|
|
||||||
Input samples for a given frame (here, this means 1/60Hz, not to be confused
|
Input samples for a given frame (here, this means 1/60Hz, not to be confused
|
||||||
with an MPEG frame, which is shorter) are numbered 0 ... L-1 (L samples in
|
with an MPEG frame, which is shorter) are numbered 0 ... L-1 (L samples in
|
||||||
total). Output samples are 0 ... M-1.
|
total). Output samples are 0 ... M-1.
|
||||||
|
|
||||||
For two adjacent input samples at times p ("previous") and n ("next"), in[p]
|
For two adjacent input samples at times p ("previous") and n ("next"), in[p]
|
||||||
and in[n], and output out[t] at time t, linear interpolation yields:
|
and in[n], and output out[t] at time t, linear interpolation yields:
|
||||||
|
|
||||||
out[t] = (n-t)/(n-p) * in[p] + (t-p)/(n-p) * in[n]
|
out[t] = (n-t)/(n-p) * in[p] + (t-p)/(n-p) * in[n]
|
||||||
|
|
||||||
Note that (n-p) = 1/fin (fin being the input sampling frequency).
|
Note that (n-p) = 1/fin (fin being the input sampling frequency).
|
||||||
|
|
||||||
Let pFrac = (n-t)/(n-p) and nFrac = (t-p)/(n-p). As t moves from p to n, pFrac
|
Let pFrac = (n-t)/(n-p) and nFrac = (t-p)/(n-p). As t moves from p to n, pFrac
|
||||||
moves from 1 to 0 and nFrac from 0 to 1, as we expect.
|
moves from 1 to 0 and nFrac from 0 to 1, as we expect.
|
||||||
|
|
||||||
If we proceed one output sample at a time, we must add the time difference
|
If we proceed one output sample at a time, we must add the time difference
|
||||||
between output samples, 1/fout, to t. Call this delta_t. If we divide delta_t
|
between output samples, 1/fout, to t. Call this delta_t. If we divide delta_t
|
||||||
by (n-p), we can add it directly to nFrac and subtract from pFrac. Therefore:
|
by (n-p), we can add it directly to nFrac and subtract from pFrac. Therefore:
|
||||||
|
|
||||||
delta = (1/fout)/(n-p) = fin/fout
|
delta = (1/fout)/(n-p) = fin/fout
|
||||||
|
|
||||||
What happens when nFrac exceeds 1.0 or pFrac goes below 0.0? That can't
|
What happens when nFrac exceeds 1.0 or pFrac goes below 0.0? That can't
|
||||||
be allowed to happen -- it means that we've actually moved along the line into
|
be allowed to happen -- it means that we've actually moved along the line into
|
||||||
the region between the next set of samples. We use pFrac < 0 as the condition
|
the region between the next set of samples. We use pFrac < 0 as the condition
|
||||||
to update the input samples.
|
to update the input samples.
|
||||||
|
|
||||||
It so happens that when fin < fout, pFrac and nFrac will never exceed 1.0. So
|
It so happens that when fin < fout, pFrac and nFrac will never exceed 1.0. So
|
||||||
there is no need to check or mask the fixed point values when using them to
|
there is no need to check or mask the fixed point values when using them to
|
||||||
interpolate samples.
|
interpolate samples.
|
||||||
|
|
||||||
2. Input Buffer Overflows
|
2. Input Buffer Overflows
|
||||||
|
|
||||||
For some low sampling rates, particularly those that are a factor of 2 or 4
|
For some low sampling rates, particularly those that are a factor of 2 or 4
|
||||||
smaller, it is possible that the very last sample or two needed from the input
|
smaller, it is possible that the very last sample or two needed from the input
|
||||||
stream will be beyond the end. Fetching two extra samples (which can introduce
|
stream will be beyond the end. Fetching two extra samples (which can introduce
|
||||||
an update lag of two samples -- imperceptible and inconsequential) fixes this,
|
an update lag of two samples -- imperceptible and inconsequential) fixes this,
|
||||||
and so we do it.
|
and so we do it.
|
||||||
|
|
||||||
3. Continuity Between Frames
|
3. Continuity Between Frames
|
||||||
|
|
||||||
The very last output sample will typically sit somewhere between two input
|
The very last output sample will typically sit somewhere between two input
|
||||||
samples. It is wrong to start the next frame by assuming everything is lined
|
samples. It is wrong to start the next frame by assuming everything is lined
|
||||||
up again. The first sample of the next frame will often have to be interpol-
|
up again. The first sample of the next frame will often have to be interpol-
|
||||||
|
@ -105,17 +106,17 @@
|
||||||
to see how many input samples remain unprocessed when up-sampling is finished,
|
to see how many input samples remain unprocessed when up-sampling is finished,
|
||||||
and then copy those to the beginning of the buffer. We then return the number
|
and then copy those to the beginning of the buffer. We then return the number
|
||||||
of samples so that the buffer update function will know to skip them.
|
of samples so that the buffer update function will know to skip them.
|
||||||
|
|
||||||
We also must maintain the state of pFrac and nFrac to resume interpolation
|
We also must maintain the state of pFrac and nFrac to resume interpolation
|
||||||
correctly. Therefore, these variables are persistent.
|
correctly. Therefore, these variables are persistent.
|
||||||
|
|
||||||
4. Fixed Point Arithmetic
|
4. Fixed Point Arithmetic
|
||||||
|
|
||||||
Fixed point arithmetic is used to track fractions. For such numbers, the low
|
Fixed point arithmetic is used to track fractions. For such numbers, the low
|
||||||
8 bits represent a fraction (0x100 would be 1.0, 0x080 would be 0.5, etc.)
|
8 bits represent a fraction (0x100 would be 1.0, 0x080 would be 0.5, etc.)
|
||||||
and the upper bits are the integral portion.
|
and the upper bits are the integral portion.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
void CDSBResampler::Reset(void)
|
void CDSBResampler::Reset(void)
|
||||||
{
|
{
|
||||||
// Initial state of fractions (24.8 fixed point)
|
// Initial state of fractions (24.8 fixed point)
|
||||||
|
@ -142,48 +143,48 @@ int CDSBResampler::UpSampleAndMix(INT16 *outL, INT16 *outR, INT16 *inL, INT16 *i
|
||||||
int inIdx = 0;
|
int inIdx = 0;
|
||||||
INT32 leftSample, rightSample, leftSoundSample, rightSoundSample;
|
INT32 leftSample, rightSample, leftSoundSample, rightSoundSample;
|
||||||
INT32 v[2], musicVol, soundVol;
|
INT32 v[2], musicVol, soundVol;
|
||||||
|
|
||||||
// Obtain program volume settings and convert to 24.8 fixed point (0-200 -> 0x00-0x200)
|
// Obtain program volume settings and convert to 24.8 fixed point (0-200 -> 0x00-0x200)
|
||||||
musicVol = m_config["MusicVolume"].ValueAs<int>();
|
musicVol = m_config["MusicVolume"].ValueAs<int>();
|
||||||
soundVol = m_config["SoundVolume"].ValueAs<int>();
|
soundVol = m_config["SoundVolume"].ValueAs<int>();
|
||||||
musicVol = (INT32) ((float) 0x100 * (float) musicVol / 100.0f);
|
musicVol = (INT32) ((float) 0x100 * (float) musicVol / 100.0f);
|
||||||
soundVol = (INT32) ((float) 0x100 * (float) soundVol / 100.0f);
|
soundVol = (INT32) ((float) 0x100 * (float) soundVol / 100.0f);
|
||||||
|
|
||||||
// Scale volume from 0x00-0xFF -> 0x00-0x100 (24.8 fixed point)
|
// Scale volume from 0x00-0xFF -> 0x00-0x100 (24.8 fixed point)
|
||||||
v[0] = (INT16) ((float) 0x100 * (float) volumeL / 255.0f);
|
v[0] = (INT16) ((float) 0x100 * (float) volumeL / 255.0f);
|
||||||
v[1] = (INT16) ((float) 0x100 * (float) volumeR / 255.0f);
|
v[1] = (INT16) ((float) 0x100 * (float) volumeR / 255.0f);
|
||||||
|
|
||||||
// Up-sample and mix!
|
// Up-sample and mix!
|
||||||
while (outIdx < sizeOut)
|
while (outIdx < sizeOut)
|
||||||
{
|
{
|
||||||
// nFrac, pFrac will never exceed 1.0 (0x100) (only true if delta does not exceed 1)
|
// 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
|
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
|
rightSample = ((int)inR[inIdx]*pFrac+(int)inR[inIdx+1]*nFrac) >> 8; // right channel
|
||||||
|
|
||||||
// Apply DSB volume and then overall music volume setting
|
// Apply DSB volume and then overall music volume setting
|
||||||
leftSample = (leftSample*v[0]*musicVol) >> 16; // multiplied by two 24.8 numbers, shift back by 16
|
leftSample = (leftSample*v[0]*musicVol) >> 16; // multiplied by two 24.8 numbers, shift back by 16
|
||||||
rightSample = (rightSample*v[0]*musicVol) >> 16;
|
rightSample = (rightSample*v[0]*musicVol) >> 16;
|
||||||
|
|
||||||
// Apply sound volume setting
|
// Apply sound volume setting
|
||||||
leftSoundSample = (outL[outIdx]*soundVol) >> 8;
|
leftSoundSample = (outL[outIdx]*soundVol) >> 8;
|
||||||
rightSoundSample = (outR[outIdx]*soundVol) >> 8;
|
rightSoundSample = (outR[outIdx]*soundVol) >> 8;
|
||||||
|
|
||||||
// Mix and output
|
// Mix and output
|
||||||
outL[outIdx] = MixAndClip(leftSoundSample, leftSample);
|
outL[outIdx] = MixAndClip(leftSoundSample, leftSample);
|
||||||
outR[outIdx] = MixAndClip(rightSoundSample, rightSample);
|
outR[outIdx] = MixAndClip(rightSoundSample, rightSample);
|
||||||
outIdx++;
|
outIdx++;
|
||||||
|
|
||||||
// Time step
|
// Time step
|
||||||
pFrac -= delta;
|
pFrac -= delta;
|
||||||
nFrac += delta;
|
nFrac += delta;
|
||||||
|
|
||||||
// Time to move to next samples?
|
// Time to move to next samples?
|
||||||
if (pFrac <= 0) // when pFrac becomes 0, advance samples, reset pFrac to 1
|
if (pFrac <= 0) // when pFrac becomes 0, advance samples, reset pFrac to 1
|
||||||
{
|
{
|
||||||
pFrac += (1<<8);
|
pFrac += (1<<8);
|
||||||
nFrac -= (1<<8);
|
nFrac -= (1<<8);
|
||||||
inIdx++; // advance samples (for upsampling only; downsampling may advance by more than one -- add delta every loop iteration)
|
inIdx++; // advance samples (for upsampling only; downsampling may advance by more than one -- add delta every loop iteration)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +211,7 @@ UINT8 CDSB1::Read8(UINT32 addr)
|
||||||
// ROM: 0x0000-0x7FFF
|
// ROM: 0x0000-0x7FFF
|
||||||
if (addr < 0x8000)
|
if (addr < 0x8000)
|
||||||
return progROM[addr];
|
return progROM[addr];
|
||||||
|
|
||||||
// RAM: 0x8000-0xFFFF
|
// RAM: 0x8000-0xFFFF
|
||||||
return ram[addr&0x7FFF];
|
return ram[addr&0x7FFF];
|
||||||
}
|
}
|
||||||
|
@ -259,17 +260,17 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE2: // MPEG start, high byte
|
case 0xE2: // MPEG start, high byte
|
||||||
startLatch &= 0x00FFFF;
|
startLatch &= 0x00FFFF;
|
||||||
startLatch |= ((UINT32)data) << 16;
|
startLatch |= ((UINT32)data) << 16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE3: // MPEG start, middle byte
|
case 0xE3: // MPEG start, middle byte
|
||||||
startLatch &= 0xFF00FF;
|
startLatch &= 0xFF00FF;
|
||||||
startLatch |= ((UINT32)data) << 8;
|
startLatch |= ((UINT32)data) << 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE4: // MPEG start, low byte
|
case 0xE4: // MPEG start, low byte
|
||||||
startLatch &= 0xFFFF00;
|
startLatch &= 0xFFFF00;
|
||||||
startLatch |= data;
|
startLatch |= data;
|
||||||
|
@ -297,19 +298,19 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data)
|
||||||
MpegDec::UpdateMemory(&mpegROM[usingLoopStart], usingLoopEnd, true);
|
MpegDec::UpdateMemory(&mpegROM[usingLoopStart], usingLoopEnd, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE5: // MPEG end, high byte
|
case 0xE5: // MPEG end, high byte
|
||||||
endLatch &= 0x00FFFF;
|
endLatch &= 0x00FFFF;
|
||||||
endLatch |= ((UINT32)data) << 16;
|
endLatch |= ((UINT32)data) << 16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE6: // MPEG end, middle byte
|
case 0xE6: // MPEG end, middle byte
|
||||||
endLatch &= 0xFF00FF;
|
endLatch &= 0xFF00FF;
|
||||||
endLatch |= ((UINT32)data) << 8;
|
endLatch |= ((UINT32)data) << 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE7: // MPEG end, low byte
|
case 0xE7: // MPEG end, low byte
|
||||||
endLatch &= 0xFFFF00;
|
endLatch &= 0xFFFF00;
|
||||||
endLatch |= data;
|
endLatch |= data;
|
||||||
|
@ -327,20 +328,20 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data)
|
||||||
MpegDec::UpdateMemory(&mpegROM[usingLoopStart], usingLoopEnd, true);
|
MpegDec::UpdateMemory(&mpegROM[usingLoopStart], usingLoopEnd, true);
|
||||||
//printf("loopEnd = %08X\n", loopEnd);
|
//printf("loopEnd = %08X\n", loopEnd);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE8: // MPEG volume
|
case 0xE8: // MPEG volume
|
||||||
volume = 0x7F-data;
|
volume = 0x7F-data;
|
||||||
//printf("Set Volume: %02X\n", volume);
|
//printf("Set Volume: %02X\n", volume);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xE9: // MPEG stereo
|
case 0xE9: // MPEG stereo
|
||||||
stereo = data;
|
stereo = data;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xF0: // command echo back
|
case 0xF0: // command echo back
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//printf("Z80 Port %02X=%08X\n", addr, data);
|
//printf("Z80 Port %02X=%08X\n", addr, data);
|
||||||
break;
|
break;
|
||||||
|
@ -350,24 +351,24 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data)
|
||||||
UINT8 CDSB1::IORead8(UINT32 addr)
|
UINT8 CDSB1::IORead8(UINT32 addr)
|
||||||
{
|
{
|
||||||
int progress;
|
int progress;
|
||||||
|
|
||||||
switch ((addr&0xFF))
|
switch ((addr&0xFF))
|
||||||
{
|
{
|
||||||
case 0xE2: // MPEG position, high byte
|
case 0xE2: // MPEG position, high byte
|
||||||
progress = MpegDec::GetPosition();
|
progress = MpegDec::GetPosition();
|
||||||
progress += mpegStart; // byte address currently playing
|
progress += mpegStart; // byte address currently playing
|
||||||
return (progress>>16)&0xFF;
|
return (progress>>16)&0xFF;
|
||||||
|
|
||||||
case 0xE3: // MPEG position, middle byte
|
case 0xE3: // MPEG position, middle byte
|
||||||
progress = MpegDec::GetPosition();
|
progress = MpegDec::GetPosition();
|
||||||
progress += mpegStart;
|
progress += mpegStart;
|
||||||
return (progress>>8)&0xFF;
|
return (progress>>8)&0xFF;
|
||||||
|
|
||||||
case 0xE4: // MPEG position, low byte
|
case 0xE4: // MPEG position, low byte
|
||||||
progress = MpegDec::GetPosition();
|
progress = MpegDec::GetPosition();
|
||||||
progress += mpegStart;
|
progress += mpegStart;
|
||||||
return progress&0xFF;
|
return progress&0xFF;
|
||||||
|
|
||||||
case 0xF0: // Latch
|
case 0xF0: // Latch
|
||||||
UINT8 d;
|
UINT8 d;
|
||||||
d = fifo[fifoIdxR]; // retrieve next command byte
|
d = fifo[fifoIdxR]; // retrieve next command byte
|
||||||
|
@ -376,16 +377,16 @@ UINT8 CDSB1::IORead8(UINT32 addr)
|
||||||
fifoIdxR++;
|
fifoIdxR++;
|
||||||
fifoIdxR &= 127;
|
fifoIdxR &= 127;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fifoIdxR == fifoIdxW) // FIFO empty?
|
if (fifoIdxR == fifoIdxW) // FIFO empty?
|
||||||
status &= ~2; // yes, indicate no commands left
|
status &= ~2; // yes, indicate no commands left
|
||||||
else
|
else
|
||||||
status |= 2;
|
status |= 2;
|
||||||
|
|
||||||
Z80.SetINT(false); // clear IRQ
|
Z80.SetINT(false); // clear IRQ
|
||||||
//printf("Z80: INT cleared, read from FIFO\n");
|
//printf("Z80: INT cleared, read from FIFO\n");
|
||||||
return d;
|
return d;
|
||||||
|
|
||||||
case 0xF1: // Status
|
case 0xF1: // Status
|
||||||
/*
|
/*
|
||||||
* Bit 0: Must be 1 for most games.
|
* Bit 0: Must be 1 for most games.
|
||||||
|
@ -394,7 +395,7 @@ UINT8 CDSB1::IORead8(UINT32 addr)
|
||||||
*/
|
*/
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf("Z80 Port Read %02X\n", addr);
|
//printf("Z80 Port Read %02X\n", addr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -402,7 +403,7 @@ UINT8 CDSB1::IORead8(UINT32 addr)
|
||||||
static int Z80IRQCallback(CZ80 *Z80)
|
static int Z80IRQCallback(CZ80 *Z80)
|
||||||
{
|
{
|
||||||
return 0x38;
|
return 0x38;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDSB1::SendCommand(UINT8 data)
|
void CDSB1::SendCommand(UINT8 data)
|
||||||
{
|
{
|
||||||
|
@ -415,7 +416,7 @@ void CDSB1::SendCommand(UINT8 data)
|
||||||
fifo[fifoIdxW++] = data;
|
fifo[fifoIdxW++] = data;
|
||||||
fifoIdxW &= 127;
|
fifoIdxW &= 127;
|
||||||
//printf("Write FIFO: %02X\n", data);
|
//printf("Write FIFO: %02X\n", data);
|
||||||
|
|
||||||
// Have we caught up to the read pointer?
|
// Have we caught up to the read pointer?
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (fifoIdxW == fifoIdxR)
|
if (fifoIdxW == fifoIdxR)
|
||||||
|
@ -427,7 +428,7 @@ void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR)
|
||||||
{
|
{
|
||||||
int cycles;
|
int cycles;
|
||||||
UINT8 v;
|
UINT8 v;
|
||||||
|
|
||||||
if (!m_config["EmulateDSB"].ValueAs<bool>())
|
if (!m_config["EmulateDSB"].ValueAs<bool>())
|
||||||
{
|
{
|
||||||
// DSB code applies SCSP volume, too, so we must still mix
|
// DSB code applies SCSP volume, too, so we must still mix
|
||||||
|
@ -436,23 +437,23 @@ void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR)
|
||||||
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, 0, 0, 44100/60, 32000/60+2, 44100, 32000);
|
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, 0, 0, 44100/60, 32000/60+2, 44100, 32000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// While FIFO not empty, fire interrupts, run for up to one frame
|
// While FIFO not empty, fire interrupts, run for up to one frame
|
||||||
for (cycles = (4000000/60)/4; (cycles > 0) && (fifoIdxR != fifoIdxW); )
|
for (cycles = (4000000/60); (cycles > 0) && (fifoIdxR != fifoIdxW); )
|
||||||
{
|
{
|
||||||
Z80.SetINT(true); // fire an IRQ to indicate pending command
|
Z80.SetINT(true); // fire an IRQ to indicate pending command
|
||||||
//printf("Z80 INT fired\n");
|
//printf("Z80 INT fired\n");
|
||||||
cycles -= Z80.Run(500);
|
cycles -= Z80.Run(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run remaining cycles
|
// Run remaining cycles
|
||||||
Z80.Run(cycles);
|
Z80.Run(cycles);
|
||||||
|
|
||||||
//printf("VOLUME=%02X STEREO=%02X\n", volume, stereo);
|
//printf("VOLUME=%02X STEREO=%02X\n", volume, stereo);
|
||||||
|
|
||||||
// Convert volume from 0x00-0x7F -> 0x00-0xFF
|
// Convert volume from 0x00-0x7F -> 0x00-0xFF
|
||||||
v = (UINT8) ((float) 255.0f * (float) volume /127.0f);
|
v = (UINT8) ((float) 255.0f * (float) volume /127.0f);
|
||||||
|
|
||||||
// Decode MPEG for this frame
|
// Decode MPEG for this frame
|
||||||
MpegDec::DecodeAudio(&mpegL[retainedSamples], &mpegR[retainedSamples], 32000 / 60 - retainedSamples + 2);
|
MpegDec::DecodeAudio(&mpegL[retainedSamples], &mpegR[retainedSamples], 32000 / 60 - retainedSamples + 2);
|
||||||
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, v, v, 44100/60, 32000/60+2, 44100, 32000);
|
retainedSamples = Resampler.UpSampleAndMix(audioL, audioR, mpegL, mpegR, v, v, 44100/60, 32000/60+2, 44100, 32000);
|
||||||
|
@ -463,15 +464,15 @@ void CDSB1::Reset(void)
|
||||||
MpegDec::Stop();
|
MpegDec::Stop();
|
||||||
Resampler.Reset();
|
Resampler.Reset();
|
||||||
retainedSamples = 0;
|
retainedSamples = 0;
|
||||||
|
|
||||||
memset(fifo, 0, sizeof(fifo));
|
memset(fifo, 0, sizeof(fifo));
|
||||||
fifoIdxW = fifoIdxR = 0;
|
fifoIdxW = fifoIdxR = 0;
|
||||||
|
|
||||||
status = 1;
|
status = 1;
|
||||||
mpegState = 0; // why doesn't RB ever init this?
|
mpegState = 0; // why doesn't RB ever init this?
|
||||||
volume = 0x7F; // full volume
|
volume = 0x7F; // full volume
|
||||||
usingLoopStart = 0;
|
usingLoopStart = 0;
|
||||||
|
|
||||||
// Even if DSB emulation is disabled, must reset to establish valid Z80 state
|
// Even if DSB emulation is disabled, must reset to establish valid Z80 state
|
||||||
Z80.Reset();
|
Z80.Reset();
|
||||||
DebugLog("DSB1 Reset\n");
|
DebugLog("DSB1 Reset\n");
|
||||||
|
@ -481,9 +482,9 @@ void CDSB1::SaveState(CBlockFile *StateFile)
|
||||||
{
|
{
|
||||||
UINT32 playOffset, endOffset;
|
UINT32 playOffset, endOffset;
|
||||||
UINT8 isPlaying;
|
UINT8 isPlaying;
|
||||||
|
|
||||||
StateFile->NewBlock("DSB1", __FILE__);
|
StateFile->NewBlock("DSB1", __FILE__);
|
||||||
|
|
||||||
// MPEG playback state
|
// MPEG playback state
|
||||||
isPlaying = (UINT8)MpegDec::IsLoaded();
|
isPlaying = (UINT8)MpegDec::IsLoaded();
|
||||||
playOffset = (UINT32)MpegDec::GetPosition();
|
playOffset = (UINT32)MpegDec::GetPosition();
|
||||||
|
@ -496,7 +497,7 @@ void CDSB1::SaveState(CBlockFile *StateFile)
|
||||||
StateFile->Write(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
StateFile->Write(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
||||||
StateFile->Write(&usingLoopStart, sizeof(usingLoopStart));
|
StateFile->Write(&usingLoopStart, sizeof(usingLoopStart));
|
||||||
StateFile->Write(&usingLoopEnd, sizeof(usingLoopEnd));
|
StateFile->Write(&usingLoopEnd, sizeof(usingLoopEnd));
|
||||||
|
|
||||||
// MPEG board state
|
// MPEG board state
|
||||||
StateFile->Write(ram, 0x8000);
|
StateFile->Write(ram, 0x8000);
|
||||||
StateFile->Write(fifo, sizeof(fifo));
|
StateFile->Write(fifo, sizeof(fifo));
|
||||||
|
@ -511,7 +512,7 @@ void CDSB1::SaveState(CBlockFile *StateFile)
|
||||||
StateFile->Write(&cmdLatch, sizeof(cmdLatch));
|
StateFile->Write(&cmdLatch, sizeof(cmdLatch));
|
||||||
StateFile->Write(&volume, sizeof(volume));
|
StateFile->Write(&volume, sizeof(volume));
|
||||||
StateFile->Write(&stereo, sizeof(stereo));
|
StateFile->Write(&stereo, sizeof(stereo));
|
||||||
|
|
||||||
// Z80 CPU state
|
// Z80 CPU state
|
||||||
Z80.SaveState(StateFile, "DSB1 Z80");
|
Z80.SaveState(StateFile, "DSB1 Z80");
|
||||||
}
|
}
|
||||||
|
@ -520,13 +521,13 @@ void CDSB1::LoadState(CBlockFile *StateFile)
|
||||||
{
|
{
|
||||||
UINT32 playOffset, endOffset;
|
UINT32 playOffset, endOffset;
|
||||||
UINT8 isPlaying;
|
UINT8 isPlaying;
|
||||||
|
|
||||||
if (OKAY != StateFile->FindBlock("DSB1"))
|
if (OKAY != StateFile->FindBlock("DSB1"))
|
||||||
{
|
{
|
||||||
ErrorLog("Unable to load Digital Sound Board state. Save state file is corrupt.");
|
ErrorLog("Unable to load Digital Sound Board state. Save state file is corrupt.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateFile->Read(&isPlaying, sizeof(isPlaying));
|
StateFile->Read(&isPlaying, sizeof(isPlaying));
|
||||||
StateFile->Read(&playOffset, sizeof(playOffset));
|
StateFile->Read(&playOffset, sizeof(playOffset));
|
||||||
StateFile->Read(&endOffset, sizeof(endOffset));
|
StateFile->Read(&endOffset, sizeof(endOffset));
|
||||||
|
@ -547,9 +548,9 @@ void CDSB1::LoadState(CBlockFile *StateFile)
|
||||||
StateFile->Read(&cmdLatch, sizeof(cmdLatch));
|
StateFile->Read(&cmdLatch, sizeof(cmdLatch));
|
||||||
StateFile->Read(&volume, sizeof(volume));
|
StateFile->Read(&volume, sizeof(volume));
|
||||||
StateFile->Read(&stereo, sizeof(stereo));
|
StateFile->Read(&stereo, sizeof(stereo));
|
||||||
|
|
||||||
Z80.LoadState(StateFile, "DSB1 Z80");
|
Z80.LoadState(StateFile, "DSB1 Z80");
|
||||||
|
|
||||||
// Restart MPEG audio at the appropriate position
|
// Restart MPEG audio at the appropriate position
|
||||||
if (isPlaying)
|
if (isPlaying)
|
||||||
{
|
{
|
||||||
|
@ -575,27 +576,27 @@ void CDSB1::LoadState(CBlockFile *StateFile)
|
||||||
bool CDSB1::Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr)
|
bool CDSB1::Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr)
|
||||||
{
|
{
|
||||||
float memSizeMB = (float)DSB1_MEMORY_POOL_SIZE/(float)0x100000;
|
float memSizeMB = (float)DSB1_MEMORY_POOL_SIZE/(float)0x100000;
|
||||||
|
|
||||||
// Receive ROM
|
// Receive ROM
|
||||||
progROM = progROMPtr;
|
progROM = progROMPtr;
|
||||||
mpegROM = mpegROMPtr;
|
mpegROM = mpegROMPtr;
|
||||||
|
|
||||||
// Allocate memory pool
|
// Allocate memory pool
|
||||||
memoryPool = new(std::nothrow) UINT8[DSB1_MEMORY_POOL_SIZE];
|
memoryPool = new(std::nothrow) UINT8[DSB1_MEMORY_POOL_SIZE];
|
||||||
if (NULL == memoryPool)
|
if (NULL == memoryPool)
|
||||||
return ErrorLog("Insufficient memory for DSB1 board (needs %1.1f MB).", memSizeMB);
|
return ErrorLog("Insufficient memory for DSB1 board (needs %1.1f MB).", memSizeMB);
|
||||||
memset(memoryPool, 0, DSB1_MEMORY_POOL_SIZE);
|
memset(memoryPool, 0, DSB1_MEMORY_POOL_SIZE);
|
||||||
|
|
||||||
// Set up memory pointers
|
// Set up memory pointers
|
||||||
ram = &memoryPool[DSB1_OFFSET_RAM];
|
ram = &memoryPool[DSB1_OFFSET_RAM];
|
||||||
mpegL = (INT16 *) &memoryPool[DSB1_OFFSET_MPEG_LEFT];
|
mpegL = (INT16 *) &memoryPool[DSB1_OFFSET_MPEG_LEFT];
|
||||||
mpegR = (INT16 *) &memoryPool[DSB1_OFFSET_MPEG_RIGHT];
|
mpegR = (INT16 *) &memoryPool[DSB1_OFFSET_MPEG_RIGHT];
|
||||||
|
|
||||||
// Initialize Z80 CPU
|
// Initialize Z80 CPU
|
||||||
Z80.Init(this, Z80IRQCallback);
|
Z80.Init(this, Z80IRQCallback);
|
||||||
|
|
||||||
retainedSamples = 0;
|
retainedSamples = 0;
|
||||||
|
|
||||||
return OKAY;
|
return OKAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,24 +622,24 @@ CDSB1::CDSB1(const Util::Config::Node &config)
|
||||||
mpegState = 0;
|
mpegState = 0;
|
||||||
loopStart = 0;
|
loopStart = 0;
|
||||||
loopEnd = 0;
|
loopEnd = 0;
|
||||||
|
|
||||||
DebugLog("Built DSB1 Board\n");
|
DebugLog("Built DSB1 Board\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
CDSB1::~CDSB1(void)
|
CDSB1::~CDSB1(void)
|
||||||
{
|
{
|
||||||
if (memoryPool != NULL)
|
if (memoryPool != NULL)
|
||||||
{
|
{
|
||||||
delete [] memoryPool;
|
delete [] memoryPool;
|
||||||
memoryPool = NULL;
|
memoryPool = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
progROM = NULL;
|
progROM = NULL;
|
||||||
mpegROM = NULL;
|
mpegROM = NULL;
|
||||||
ram = NULL;
|
ram = NULL;
|
||||||
mpegL = NULL;
|
mpegL = NULL;
|
||||||
mpegR = NULL;
|
mpegR = NULL;
|
||||||
|
|
||||||
DebugLog("Destroyed DSB1 Board\n");
|
DebugLog("Destroyed DSB1 Board\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,7 +649,7 @@ CDSB1::~CDSB1(void)
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
// MPEG state machine
|
// MPEG state machine
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
ST_IDLE = 0,
|
ST_IDLE = 0,
|
||||||
ST_GOT14, // start/loop addr
|
ST_GOT14, // start/loop addr
|
||||||
|
@ -672,7 +673,7 @@ enum
|
||||||
ST_GOTB6
|
ST_GOTB6
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *stateName[] =
|
static const char *stateName[] =
|
||||||
{
|
{
|
||||||
"idle",
|
"idle",
|
||||||
"st_got_14",
|
"st_got_14",
|
||||||
|
@ -717,7 +718,7 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
||||||
|
|
||||||
MpegDec::SetMemory(&mpegROM[mpegStart], mpegEnd - mpegStart, false);
|
MpegDec::SetMemory(&mpegROM[mpegStart], mpegEnd - mpegStart, false);
|
||||||
|
|
||||||
mpegState = ST_IDLE;
|
mpegState = ST_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (byte == 0x84 || byte == 0x85)
|
else if (byte == 0x84 || byte == 0x85)
|
||||||
|
@ -766,7 +767,7 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ST_GOT24:
|
case ST_GOT24:
|
||||||
mpegEnd &= 0x00FFFF;
|
mpegEnd &= 0x00FFFF;
|
||||||
mpegEnd |= (byte<<16);
|
mpegEnd |= (byte<<16);
|
||||||
mpegState++;
|
mpegState++;
|
||||||
break;
|
break;
|
||||||
|
@ -782,12 +783,12 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
||||||
stereo = StereoMode::Stereo;
|
stereo = StereoMode::Stereo;
|
||||||
mpegState = ST_IDLE;
|
mpegState = ST_IDLE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_GOTA0:
|
case ST_GOTA0:
|
||||||
stereo = (byte != 0x00) ? StereoMode::MonoLeft : StereoMode::Stereo;
|
stereo = (byte != 0x00) ? StereoMode::MonoLeft : StereoMode::Stereo;
|
||||||
mpegState = ST_IDLE;
|
mpegState = ST_IDLE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_GOTA4: // dayto2pe plays advertise tune from this state by writing 0x75
|
case ST_GOTA4: // dayto2pe plays advertise tune from this state by writing 0x75
|
||||||
mpegState = ST_IDLE;
|
mpegState = ST_IDLE;
|
||||||
if (byte == 0x75)
|
if (byte == 0x75)
|
||||||
|
@ -818,7 +819,7 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte)
|
||||||
case ST_GOTB5:
|
case ST_GOTB5:
|
||||||
mpegState = ST_IDLE;
|
mpegState = ST_IDLE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Speaker Volume:
|
* Speaker Volume:
|
||||||
*
|
*
|
||||||
|
@ -868,7 +869,7 @@ UINT8 CDSB2::Read8(UINT32 addr)
|
||||||
if (addr < (128*1024))
|
if (addr < (128*1024))
|
||||||
return progROM[addr^1];
|
return progROM[addr^1];
|
||||||
|
|
||||||
if (addr == 0xc00001)
|
if (addr == 0xc00001)
|
||||||
{
|
{
|
||||||
return cmdLatch;
|
return cmdLatch;
|
||||||
}
|
}
|
||||||
|
@ -911,7 +912,7 @@ UINT16 CDSB2::Read16(UINT32 addr)
|
||||||
UINT32 CDSB2::Read32(UINT32 addr)
|
UINT32 CDSB2::Read32(UINT32 addr)
|
||||||
{
|
{
|
||||||
UINT32 hi, lo;
|
UINT32 hi, lo;
|
||||||
|
|
||||||
if (addr < (128*1024))
|
if (addr < (128*1024))
|
||||||
{
|
{
|
||||||
hi = *(UINT16 *) &progROM[addr];
|
hi = *(UINT16 *) &progROM[addr];
|
||||||
|
@ -937,10 +938,10 @@ void CDSB2::Write8(UINT32 addr, UINT8 data)
|
||||||
ram[addr^1] = data;
|
ram[addr^1] = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr == 0xd00001) return;
|
if (addr == 0xd00001) return;
|
||||||
|
|
||||||
if (addr == 0xe00003)
|
if (addr == 0xe00003)
|
||||||
{
|
{
|
||||||
WriteMPEGFIFO(data);
|
WriteMPEGFIFO(data);
|
||||||
return;
|
return;
|
||||||
|
@ -983,14 +984,14 @@ void CDSB2::SendCommand(UINT8 data)
|
||||||
fifo[fifoIdxW++] = data;
|
fifo[fifoIdxW++] = data;
|
||||||
fifoIdxW &= 127;
|
fifoIdxW &= 127;
|
||||||
//printf("Write FIFO: %02X\n", data);
|
//printf("Write FIFO: %02X\n", data);
|
||||||
|
|
||||||
// Have we caught up to the read pointer?
|
// Have we caught up to the read pointer?
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (fifoIdxW == fifoIdxR)
|
if (fifoIdxW == fifoIdxR)
|
||||||
printf("DSB2 FIFO overflow!\n");
|
printf("DSB2 FIFO overflow!\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
|
void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
|
||||||
{
|
{
|
||||||
|
@ -1005,28 +1006,43 @@ void CDSB2::RunFrame(INT16 *audioL, INT16 *audioR)
|
||||||
|
|
||||||
M68KSetContext(&M68K);
|
M68KSetContext(&M68K);
|
||||||
//printf("DSB2 run frame PC=%06X\n", M68KGetPC());
|
//printf("DSB2 run frame PC=%06X\n", M68KGetPC());
|
||||||
|
|
||||||
// While FIFO not empty...
|
// While FIFO not empty...
|
||||||
while (fifoIdxR != fifoIdxW)
|
while (fifoIdxR != fifoIdxW)
|
||||||
{
|
{
|
||||||
cmdLatch = fifo[fifoIdxR]; // retrieve next command byte
|
cmdLatch = fifo[fifoIdxR]; // retrieve next command byte
|
||||||
fifoIdxR++;
|
fifoIdxR++;
|
||||||
fifoIdxR &= 127;
|
fifoIdxR &= 127;
|
||||||
|
|
||||||
M68KSetIRQ(1); // indicate pending command
|
M68KSetIRQ(1); // indicate pending command
|
||||||
//printf("68K INT fired\n");
|
//printf("68K INT fired\n");
|
||||||
M68KRun(500);
|
m_totalCyclesElapsed += M68KRun(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gm_matthew made the interesting discovery that IRQ2 may in fact be a timer interrupt
|
||||||
|
// rather than a per-frame interrupt.For Daytona 2 and Sega Rally 2, assuming a value
|
||||||
|
// of 1KHz fixes music fade outs and some timing issues. It is very likely this is a
|
||||||
|
// configurable timer and we should be on the look-out for games which appear to use
|
||||||
|
// different values. It is equally likely that all games share a similar code base and
|
||||||
|
// use 1KHz as the timer rate.
|
||||||
|
while (m_totalCyclesElapsed < m_nextFrameEndCycles)
|
||||||
|
{
|
||||||
|
if (m_totalCyclesElapsed >= m_nextTimerInterruptCycles)
|
||||||
|
{
|
||||||
|
// Fire timer interrupt and schedule next one
|
||||||
|
M68KSetIRQ(2);
|
||||||
|
m_nextTimerInterruptCycles = (m_totalCyclesElapsed + k_timerPeriod) - (m_totalCyclesElapsed + k_timerPeriod) % k_timerPeriod;
|
||||||
|
}
|
||||||
|
int cyclesToRun = (std::min)(m_nextTimerInterruptCycles, m_nextFrameEndCycles) - m_totalCyclesElapsed;
|
||||||
|
m_totalCyclesElapsed += M68KRun(cyclesToRun);
|
||||||
|
}
|
||||||
|
m_nextFrameEndCycles = (m_totalCyclesElapsed + k_framePeriod) - (m_totalCyclesElapsed + k_framePeriod) % k_framePeriod;
|
||||||
|
|
||||||
// Per-frame interrupt
|
|
||||||
M68KSetIRQ(2);
|
|
||||||
M68KRun(4000000/60);
|
|
||||||
|
|
||||||
M68KGetContext(&M68K);
|
M68KGetContext(&M68K);
|
||||||
|
|
||||||
// Decode MPEG for this frame
|
// Decode MPEG for this frame
|
||||||
MpegDec::DecodeAudio(&mpegL[retainedSamples], &mpegR[retainedSamples], 32000 / 60 - retainedSamples + 2);
|
MpegDec::DecodeAudio(&mpegL[retainedSamples], &mpegR[retainedSamples], 32000 / 60 - retainedSamples + 2);
|
||||||
|
|
||||||
INT16 *leftChannelSource = nullptr;
|
INT16 *leftChannelSource = nullptr;
|
||||||
INT16 *rightChannelSource = nullptr;
|
INT16 *rightChannelSource = nullptr;
|
||||||
UINT8 volL=0, volR=0;
|
UINT8 volL=0, volR=0;
|
||||||
|
@ -1062,10 +1078,10 @@ void CDSB2::Reset(void)
|
||||||
MpegDec::Stop();
|
MpegDec::Stop();
|
||||||
Resampler.Reset();
|
Resampler.Reset();
|
||||||
retainedSamples = 0;
|
retainedSamples = 0;
|
||||||
|
|
||||||
memset(fifo, 0, sizeof(fifo));
|
memset(fifo, 0, sizeof(fifo));
|
||||||
fifoIdxW = fifoIdxR = 0;
|
fifoIdxW = fifoIdxR = 0;
|
||||||
|
|
||||||
mpegState = ST_IDLE;
|
mpegState = ST_IDLE;
|
||||||
mpegStart = 0;
|
mpegStart = 0;
|
||||||
mpegEnd = 0;
|
mpegEnd = 0;
|
||||||
|
@ -1073,13 +1089,17 @@ void CDSB2::Reset(void)
|
||||||
volume[0] = 0xFF; // set to max volume in case we miss the volume commands
|
volume[0] = 0xFF; // set to max volume in case we miss the volume commands
|
||||||
volume[1] = 0xFF;
|
volume[1] = 0xFF;
|
||||||
stereo = StereoMode::Stereo;
|
stereo = StereoMode::Stereo;
|
||||||
|
|
||||||
// Even if DSB emulation is disabled, must reset to establish valid Z80 state
|
// Even if DSB emulation is disabled, must reset to establish valid Z80 state
|
||||||
M68KSetContext(&M68K);
|
M68KSetContext(&M68K);
|
||||||
M68KReset();
|
M68KReset();
|
||||||
//printf("DSB2 PC=%06X\n", M68KGetPC());
|
//printf("DSB2 PC=%06X\n", M68KGetPC());
|
||||||
M68KGetContext(&M68K);
|
M68KGetContext(&M68K);
|
||||||
|
|
||||||
|
m_totalCyclesElapsed = 0;
|
||||||
|
m_nextFrameEndCycles = k_framePeriod;
|
||||||
|
m_nextTimerInterruptCycles = k_timerPeriod;
|
||||||
|
|
||||||
DebugLog("DSB2 Reset\n");
|
DebugLog("DSB2 Reset\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1087,9 +1107,9 @@ void CDSB2::SaveState(CBlockFile *StateFile)
|
||||||
{
|
{
|
||||||
UINT32 playOffset, endOffset;
|
UINT32 playOffset, endOffset;
|
||||||
UINT8 isPlaying;
|
UINT8 isPlaying;
|
||||||
|
|
||||||
StateFile->NewBlock("DSB2", __FILE__);
|
StateFile->NewBlock("DSB2", __FILE__);
|
||||||
|
|
||||||
// MPEG playback state
|
// MPEG playback state
|
||||||
isPlaying = (UINT8)MpegDec::IsLoaded();
|
isPlaying = (UINT8)MpegDec::IsLoaded();
|
||||||
playOffset = (UINT32)MpegDec::GetPosition();
|
playOffset = (UINT32)MpegDec::GetPosition();
|
||||||
|
@ -1102,7 +1122,7 @@ void CDSB2::SaveState(CBlockFile *StateFile)
|
||||||
StateFile->Write(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
StateFile->Write(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
||||||
StateFile->Write(&usingLoopStart, sizeof(usingLoopStart));
|
StateFile->Write(&usingLoopStart, sizeof(usingLoopStart));
|
||||||
StateFile->Write(&usingLoopEnd, sizeof(usingLoopEnd));
|
StateFile->Write(&usingLoopEnd, sizeof(usingLoopEnd));
|
||||||
|
|
||||||
// MPEG board state
|
// MPEG board state
|
||||||
StateFile->Write(ram, 0x20000);
|
StateFile->Write(ram, 0x20000);
|
||||||
StateFile->Write(fifo, sizeof(fifo));
|
StateFile->Write(fifo, sizeof(fifo));
|
||||||
|
@ -1115,11 +1135,11 @@ void CDSB2::SaveState(CBlockFile *StateFile)
|
||||||
StateFile->Write(&playing, sizeof(playing));
|
StateFile->Write(&playing, sizeof(playing));
|
||||||
StateFile->Write(volume, sizeof(volume));
|
StateFile->Write(volume, sizeof(volume));
|
||||||
StateFile->Write(&stereo, sizeof(stereo));
|
StateFile->Write(&stereo, sizeof(stereo));
|
||||||
|
|
||||||
// 68K CPU state
|
// 68K CPU state
|
||||||
M68KSetContext(&M68K);
|
M68KSetContext(&M68K);
|
||||||
M68KSaveState(StateFile, "DSB2 68K");
|
M68KSaveState(StateFile, "DSB2 68K");
|
||||||
|
|
||||||
//DEBUG
|
//DEBUG
|
||||||
//printf("DSB2 PC=%06X\n", M68KGetPC());
|
//printf("DSB2 PC=%06X\n", M68KGetPC());
|
||||||
//printf("mpegStart=%X, mpegEnd=%X\n", mpegStart, mpegEnd);
|
//printf("mpegStart=%X, mpegEnd=%X\n", mpegStart, mpegEnd);
|
||||||
|
@ -1131,13 +1151,13 @@ void CDSB2::LoadState(CBlockFile *StateFile)
|
||||||
{
|
{
|
||||||
UINT32 playOffset, endOffset;
|
UINT32 playOffset, endOffset;
|
||||||
UINT8 isPlaying;
|
UINT8 isPlaying;
|
||||||
|
|
||||||
if (OKAY != StateFile->FindBlock("DSB2"))
|
if (OKAY != StateFile->FindBlock("DSB2"))
|
||||||
{
|
{
|
||||||
ErrorLog("Unable to load Digital Sound Board state. Save state file is corrupt.");
|
ErrorLog("Unable to load Digital Sound Board state. Save state file is corrupt.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateFile->Read(&isPlaying, sizeof(isPlaying));
|
StateFile->Read(&isPlaying, sizeof(isPlaying));
|
||||||
StateFile->Read(&playOffset, sizeof(playOffset));
|
StateFile->Read(&playOffset, sizeof(playOffset));
|
||||||
StateFile->Read(&endOffset, sizeof(endOffset));
|
StateFile->Read(&endOffset, sizeof(endOffset));
|
||||||
|
@ -1145,7 +1165,7 @@ void CDSB2::LoadState(CBlockFile *StateFile)
|
||||||
StateFile->Read(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
StateFile->Read(&usingMPEGEnd, sizeof(usingMPEGEnd));
|
||||||
StateFile->Read(&usingLoopStart, sizeof(usingLoopStart));
|
StateFile->Read(&usingLoopStart, sizeof(usingLoopStart));
|
||||||
StateFile->Read(&usingLoopEnd, sizeof(usingLoopEnd));
|
StateFile->Read(&usingLoopEnd, sizeof(usingLoopEnd));
|
||||||
|
|
||||||
StateFile->Read(ram, 0x20000);
|
StateFile->Read(ram, 0x20000);
|
||||||
StateFile->Read(fifo, sizeof(fifo));
|
StateFile->Read(fifo, sizeof(fifo));
|
||||||
StateFile->Read(&fifoIdxR, sizeof(fifoIdxR));
|
StateFile->Read(&fifoIdxR, sizeof(fifoIdxR));
|
||||||
|
@ -1157,11 +1177,17 @@ void CDSB2::LoadState(CBlockFile *StateFile)
|
||||||
StateFile->Read(&playing, sizeof(playing));
|
StateFile->Read(&playing, sizeof(playing));
|
||||||
StateFile->Read(volume, sizeof(volume));
|
StateFile->Read(volume, sizeof(volume));
|
||||||
StateFile->Read(&stereo, sizeof(stereo));
|
StateFile->Read(&stereo, sizeof(stereo));
|
||||||
|
|
||||||
M68KSetContext(&M68K);
|
M68KSetContext(&M68K);
|
||||||
M68KLoadState(StateFile, "DSB2 68K");
|
M68KLoadState(StateFile, "DSB2 68K");
|
||||||
M68KGetContext(&M68K);
|
M68KGetContext(&M68K);
|
||||||
|
|
||||||
|
// Technically these should be saved/restored rather than being reset but that would mean
|
||||||
|
// the save state format has to be modified and the difference would be imperceptible anyway
|
||||||
|
m_totalCyclesElapsed = 0;
|
||||||
|
m_nextFrameEndCycles = k_framePeriod;
|
||||||
|
m_nextTimerInterruptCycles = k_timerPeriod;
|
||||||
|
|
||||||
// Restart MPEG audio at the appropriate position
|
// Restart MPEG audio at the appropriate position
|
||||||
if (isPlaying)
|
if (isPlaying)
|
||||||
{
|
{
|
||||||
|
@ -1176,13 +1202,13 @@ void CDSB2::LoadState(CBlockFile *StateFile)
|
||||||
else {
|
else {
|
||||||
MpegDec::Stop();
|
MpegDec::Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
//DEBUG
|
//DEBUG
|
||||||
//printf("DSB2 PC=%06X\n", M68KGetPC());
|
//printf("DSB2 PC=%06X\n", M68KGetPC());
|
||||||
//printf("mpegStart=%X, mpegEnd=%X\n", mpegStart, mpegEnd);
|
//printf("mpegStart=%X, mpegEnd=%X\n", mpegStart, mpegEnd);
|
||||||
//printf("usingMPEGStart=%X, usingMPEGEnd=%X\n", usingMPEGStart, usingMPEGEnd);
|
//printf("usingMPEGStart=%X, usingMPEGEnd=%X\n", usingMPEGStart, usingMPEGEnd);
|
||||||
//printf("usingLoopStart=%X, usingLoopEnd=%X\n", usingLoopStart, usingLoopEnd);
|
//printf("usingLoopStart=%X, usingLoopEnd=%X\n", usingLoopStart, usingLoopEnd);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Offsets of memory regions within DSB2's pool
|
// Offsets of memory regions within DSB2's pool
|
||||||
|
@ -1194,22 +1220,22 @@ void CDSB2::LoadState(CBlockFile *StateFile)
|
||||||
bool CDSB2::Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr)
|
bool CDSB2::Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr)
|
||||||
{
|
{
|
||||||
float memSizeMB = (float)DSB2_MEMORY_POOL_SIZE/(float)0x100000;
|
float memSizeMB = (float)DSB2_MEMORY_POOL_SIZE/(float)0x100000;
|
||||||
|
|
||||||
// Receive ROM
|
// Receive ROM
|
||||||
progROM = progROMPtr;
|
progROM = progROMPtr;
|
||||||
mpegROM = mpegROMPtr;
|
mpegROM = mpegROMPtr;
|
||||||
|
|
||||||
// Allocate memory pool
|
// Allocate memory pool
|
||||||
memoryPool = new(std::nothrow) UINT8[DSB2_MEMORY_POOL_SIZE];
|
memoryPool = new(std::nothrow) UINT8[DSB2_MEMORY_POOL_SIZE];
|
||||||
if (NULL == memoryPool)
|
if (NULL == memoryPool)
|
||||||
return ErrorLog("Insufficient memory for DSB2 board (needs %1.1f MB).", memSizeMB);
|
return ErrorLog("Insufficient memory for DSB2 board (needs %1.1f MB).", memSizeMB);
|
||||||
memset(memoryPool, 0, DSB2_MEMORY_POOL_SIZE);
|
memset(memoryPool, 0, DSB2_MEMORY_POOL_SIZE);
|
||||||
|
|
||||||
// Set up memory pointers
|
// Set up memory pointers
|
||||||
ram = &memoryPool[DSB2_OFFSET_RAM];
|
ram = &memoryPool[DSB2_OFFSET_RAM];
|
||||||
mpegL = (INT16 *) &memoryPool[DSB2_OFFSET_MPEG_LEFT];
|
mpegL = (INT16 *) &memoryPool[DSB2_OFFSET_MPEG_LEFT];
|
||||||
mpegR = (INT16 *) &memoryPool[DSB2_OFFSET_MPEG_RIGHT];
|
mpegR = (INT16 *) &memoryPool[DSB2_OFFSET_MPEG_RIGHT];
|
||||||
|
|
||||||
// Initialize 68K CPU
|
// Initialize 68K CPU
|
||||||
M68KSetContext(&M68K);
|
M68KSetContext(&M68K);
|
||||||
M68KInit();
|
M68KInit();
|
||||||
|
@ -1218,7 +1244,7 @@ bool CDSB2::Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr)
|
||||||
M68KGetContext(&M68K);
|
M68KGetContext(&M68K);
|
||||||
|
|
||||||
retainedSamples = 0;
|
retainedSamples = 0;
|
||||||
|
|
||||||
return OKAY;
|
return OKAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1248,18 +1274,18 @@ CDSB2::CDSB2(const Util::Config::Node &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
CDSB2::~CDSB2(void)
|
CDSB2::~CDSB2(void)
|
||||||
{
|
{
|
||||||
if (memoryPool != NULL)
|
if (memoryPool != NULL)
|
||||||
{
|
{
|
||||||
delete [] memoryPool;
|
delete [] memoryPool;
|
||||||
memoryPool = NULL;
|
memoryPool = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
progROM = NULL;
|
progROM = NULL;
|
||||||
mpegROM = NULL;
|
mpegROM = NULL;
|
||||||
ram = NULL;
|
ram = NULL;
|
||||||
mpegL = NULL;
|
mpegL = NULL;
|
||||||
mpegR = NULL;
|
mpegR = NULL;
|
||||||
|
|
||||||
DebugLog("Destroyed DSB2 Board\n");
|
DebugLog("Destroyed DSB2 Board\n");
|
||||||
}
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
/**
|
/**
|
||||||
** Supermodel
|
** Supermodel
|
||||||
** A Sega Model 3 Arcade Emulator.
|
** A Sega Model 3 Arcade Emulator.
|
||||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
** Copyright 2011-2021 Bart Trzynadlowski, Nik Henson, Ian Curtis,
|
||||||
|
** Harry Tuttle, and Spindizzi
|
||||||
**
|
**
|
||||||
** This file is part of Supermodel.
|
** This file is part of Supermodel.
|
||||||
**
|
**
|
||||||
** Supermodel is free software: you can redistribute it and/or modify it under
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
** the terms of the GNU General Public License as published by the Free
|
** the terms of the GNU General Public License as published by the Free
|
||||||
** Software Foundation, either version 3 of the License, or (at your option)
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
** any later version.
|
** any later version.
|
||||||
**
|
**
|
||||||
|
@ -18,10 +19,10 @@
|
||||||
** You should have received a copy of the GNU General Public License along
|
** You should have received a copy of the GNU General Public License along
|
||||||
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DSB.h
|
* DSB.h
|
||||||
*
|
*
|
||||||
* Header file for the Sega Digital Sound Board (Type 1 and 2) devices. CDSB1
|
* Header file for the Sega Digital Sound Board (Type 1 and 2) devices. CDSB1
|
||||||
* is an implementation of the Z80-based DSB Type 1, and CDSB2 is the 68K-based
|
* is an implementation of the Z80-based DSB Type 1, and CDSB2 is the 68K-based
|
||||||
* Type 2 board. Only one may be active at a time because they rely on non-
|
* Type 2 board. Only one may be active at a time because they rely on non-
|
||||||
|
@ -38,7 +39,7 @@
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
Resampling
|
Resampling
|
||||||
|
|
||||||
Used internally by the DSB's MPEG code. If this becomes sufficiently generic,
|
Used internally by the DSB's MPEG code. If this becomes sufficiently generic,
|
||||||
it can be moved to Sound/. Not intended for general use for now.
|
it can be moved to Sound/. Not intended for general use for now.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
@ -47,8 +48,8 @@
|
||||||
* CDSBResampler:
|
* CDSBResampler:
|
||||||
*
|
*
|
||||||
* Frame-by-frame resampler. Resamples one single frame of audio and maintains
|
* Frame-by-frame resampler. Resamples one single frame of audio and maintains
|
||||||
* continuity between frames by copying unprocessed input samples to the
|
* continuity between frames by copying unprocessed input samples to the
|
||||||
* beginning of the buffer and retaining the internal interpolation state.
|
* beginning of the buffer and retaining the internal interpolation state.
|
||||||
*
|
*
|
||||||
* See DSB.cpp for a detailed description of how this works.
|
* See DSB.cpp for a detailed description of how this works.
|
||||||
*
|
*
|
||||||
|
@ -56,9 +57,9 @@
|
||||||
* Reset(). Whether the resampler will otherwise behave correctly and stay
|
* Reset(). Whether the resampler will otherwise behave correctly and stay
|
||||||
* within array bounds has not been verified.
|
* within array bounds has not been verified.
|
||||||
*
|
*
|
||||||
* Designed for use at 60 Hz, for input frequencies of 11.025, 22.05, 16, and
|
* Designed for use at 60 Hz, for input frequencies of 11.025, 22.05, 16, and
|
||||||
* 32 KHz and 44.1 KHz output frequencies. Theoretically, it should be able to
|
* 32 KHz and 44.1 KHz output frequencies. Theoretically, it should be able to
|
||||||
* operate on most output frequencies and input frequencies that are simply
|
* operate on most output frequencies and input frequencies that are simply
|
||||||
* lower, but it has not been extensively verified.
|
* lower, but it has not been extensively verified.
|
||||||
*/
|
*/
|
||||||
class CDSBResampler
|
class CDSBResampler
|
||||||
|
@ -86,17 +87,17 @@ private:
|
||||||
*
|
*
|
||||||
* Abstract base class defining the common interface for both DSB board types.
|
* Abstract base class defining the common interface for both DSB board types.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CDSB: public IBus
|
class CDSB: public IBus
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
* SendCommand(data):
|
* SendCommand(data):
|
||||||
*
|
*
|
||||||
* Send a MIDI command to the DSB board.
|
* Send a MIDI command to the DSB board.
|
||||||
*/
|
*/
|
||||||
virtual void SendCommand(UINT8 data) = 0;
|
virtual void SendCommand(UINT8 data) = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RunFrame(audioL, audioR):
|
* RunFrame(audioL, audioR):
|
||||||
*
|
*
|
||||||
|
@ -108,14 +109,14 @@ public:
|
||||||
* audioR Right audio channel.
|
* audioR Right audio channel.
|
||||||
*/
|
*/
|
||||||
virtual void RunFrame(INT16 *audioL, INT16 *audioR) = 0;
|
virtual void RunFrame(INT16 *audioL, INT16 *audioR) = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset(void):
|
* Reset(void):
|
||||||
*
|
*
|
||||||
* Resets the DSB. Must be called prior to RunFrame().
|
* Resets the DSB. Must be called prior to RunFrame().
|
||||||
*/
|
*/
|
||||||
virtual void Reset(void) = 0;
|
virtual void Reset(void) = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SaveState(SaveState):
|
* SaveState(SaveState):
|
||||||
*
|
*
|
||||||
|
@ -135,7 +136,7 @@ public:
|
||||||
* SaveState Block file to load state information from.
|
* SaveState Block file to load state information from.
|
||||||
*/
|
*/
|
||||||
virtual void LoadState(CBlockFile *SaveState) = 0;
|
virtual void LoadState(CBlockFile *SaveState) = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init(progROMPtr, mpegROMPtr):
|
* Init(progROMPtr, mpegROMPtr):
|
||||||
*
|
*
|
||||||
|
@ -146,10 +147,10 @@ public:
|
||||||
* mpegROMPtr MPEG data ROM.
|
* mpegROMPtr MPEG data ROM.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* OKAY if successful, otherwise FAIL.
|
* OKAY if successful, otherwise FAIL.
|
||||||
*/
|
*/
|
||||||
virtual bool Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr) = 0;
|
virtual bool Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr) = 0;
|
||||||
|
|
||||||
virtual ~CDSB()
|
virtual ~CDSB()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -157,8 +158,8 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
DSB Classes
|
DSB Classes
|
||||||
|
|
||||||
DSB1 and DSB2 hardware. The base class, CDSB, should ideally be dynamically
|
DSB1 and DSB2 hardware. The base class, CDSB, should ideally be dynamically
|
||||||
allocated using one of these. See CDSB for descriptions of member functions.
|
allocated using one of these. See CDSB for descriptions of member functions.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
@ -166,7 +167,7 @@ public:
|
||||||
/*
|
/*
|
||||||
* CDSB1:
|
* CDSB1:
|
||||||
*
|
*
|
||||||
* Sega Digital Sound Board Type 1: Z80 plus custom gate array for MPEG
|
* Sega Digital Sound Board Type 1: Z80 plus custom gate array for MPEG
|
||||||
* decoding.
|
* decoding.
|
||||||
*/
|
*/
|
||||||
class CDSB1: public CDSB
|
class CDSB1: public CDSB
|
||||||
|
@ -177,7 +178,7 @@ public:
|
||||||
void IOWrite8(UINT32 addr, UINT8 data);
|
void IOWrite8(UINT32 addr, UINT8 data);
|
||||||
UINT8 Read8(UINT32 addr);
|
UINT8 Read8(UINT32 addr);
|
||||||
void Write8(UINT32 addr, UINT8 data);
|
void Write8(UINT32 addr, UINT8 data);
|
||||||
|
|
||||||
// DSB interface (see CDSB definition)
|
// DSB interface (see CDSB definition)
|
||||||
void SendCommand(UINT8 data);
|
void SendCommand(UINT8 data);
|
||||||
void RunFrame(INT16 *audioL, INT16 *audioR);
|
void RunFrame(INT16 *audioL, INT16 *audioR);
|
||||||
|
@ -185,48 +186,48 @@ public:
|
||||||
void SaveState(CBlockFile *StateFile);
|
void SaveState(CBlockFile *StateFile);
|
||||||
void LoadState(CBlockFile *StateFile);
|
void LoadState(CBlockFile *StateFile);
|
||||||
bool Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr);
|
bool Init(const UINT8 *progROMPtr, const UINT8 *mpegROMPtr);
|
||||||
|
|
||||||
// Returns a reference to the Z80 CPU
|
// Returns a reference to the Z80 CPU
|
||||||
CZ80 *GetZ80(void);
|
CZ80 *GetZ80(void);
|
||||||
|
|
||||||
// Constructor and destructor
|
// Constructor and destructor
|
||||||
CDSB1(const Util::Config::Node &config);
|
CDSB1(const Util::Config::Node &config);
|
||||||
~CDSB1(void);
|
~CDSB1(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Util::Config::Node &m_config;
|
const Util::Config::Node &m_config;
|
||||||
|
|
||||||
// Resampler
|
// Resampler
|
||||||
CDSBResampler Resampler;
|
CDSBResampler Resampler;
|
||||||
int retainedSamples; // how many MPEG samples carried over from previous frame
|
int retainedSamples; // how many MPEG samples carried over from previous frame
|
||||||
|
|
||||||
// MPEG decode buffers (48KHz, 1/60th second + 2 extra padding samples)
|
// MPEG decode buffers (48KHz, 1/60th second + 2 extra padding samples)
|
||||||
INT16 *mpegL, *mpegR;
|
INT16 *mpegL, *mpegR;
|
||||||
|
|
||||||
// DSB memory
|
// DSB memory
|
||||||
const UINT8 *progROM; // Z80 program ROM (passed in from parent object)
|
const UINT8 *progROM; // Z80 program ROM (passed in from parent object)
|
||||||
const UINT8 *mpegROM; // MPEG music ROM
|
const UINT8 *mpegROM; // MPEG music ROM
|
||||||
UINT8 *memoryPool; // all memory allocated here
|
UINT8 *memoryPool; // all memory allocated here
|
||||||
UINT8 *ram; // Z80 RAM
|
UINT8 *ram; // Z80 RAM
|
||||||
|
|
||||||
// Command FIFO
|
// Command FIFO
|
||||||
UINT8 fifo[128];
|
UINT8 fifo[128];
|
||||||
int fifoIdxR; // read position
|
int fifoIdxR; // read position
|
||||||
int fifoIdxW; // write position
|
int fifoIdxW; // write position
|
||||||
|
|
||||||
// MPEG playback variables
|
// MPEG playback variables
|
||||||
int mpegStart;
|
int mpegStart;
|
||||||
int mpegEnd;
|
int mpegEnd;
|
||||||
int mpegState;
|
int mpegState;
|
||||||
int loopStart;
|
int loopStart;
|
||||||
int loopEnd;
|
int loopEnd;
|
||||||
|
|
||||||
// Settings of currently playing stream (may not match the playback register variables above)
|
// 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 usingLoopStart; // what was last set by MPEG_SetLoop() or MPEG_PlayMemory()
|
||||||
UINT32 usingLoopEnd;
|
UINT32 usingLoopEnd;
|
||||||
UINT32 usingMPEGStart; // what was last set by MPEG_PlayMemory()
|
UINT32 usingMPEGStart; // what was last set by MPEG_PlayMemory()
|
||||||
UINT32 usingMPEGEnd;
|
UINT32 usingMPEGEnd;
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
UINT32 startLatch; // MPEG start address latch
|
UINT32 startLatch; // MPEG start address latch
|
||||||
UINT32 endLatch; // MPEG end address latch
|
UINT32 endLatch; // MPEG end address latch
|
||||||
|
@ -234,7 +235,7 @@ private:
|
||||||
UINT8 cmdLatch;
|
UINT8 cmdLatch;
|
||||||
UINT8 volume; // 0x00-0x7F
|
UINT8 volume; // 0x00-0x7F
|
||||||
UINT8 stereo;
|
UINT8 stereo;
|
||||||
|
|
||||||
// Z80 CPU
|
// Z80 CPU
|
||||||
CZ80 Z80;
|
CZ80 Z80;
|
||||||
};
|
};
|
||||||
|
@ -254,7 +255,7 @@ public:
|
||||||
void Write8(UINT32 addr, UINT8 data);
|
void Write8(UINT32 addr, UINT8 data);
|
||||||
void Write16(UINT32 addr, UINT16 data);
|
void Write16(UINT32 addr, UINT16 data);
|
||||||
void Write32(UINT32 addr, UINT32 data);
|
void Write32(UINT32 addr, UINT32 data);
|
||||||
|
|
||||||
// DSB interface (see definition of CDSB)
|
// DSB interface (see definition of CDSB)
|
||||||
void SendCommand(UINT8 data);
|
void SendCommand(UINT8 data);
|
||||||
void RunFrame(INT16 *audioL, INT16 *audioR);
|
void RunFrame(INT16 *audioL, INT16 *audioR);
|
||||||
|
@ -269,20 +270,20 @@ public:
|
||||||
// Constructor and destructor
|
// Constructor and destructor
|
||||||
CDSB2(const Util::Config::Node &config);
|
CDSB2(const Util::Config::Node &config);
|
||||||
~CDSB2(void);
|
~CDSB2(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Util::Config::Node &m_config;
|
const Util::Config::Node &m_config;
|
||||||
|
|
||||||
// Private helper functions
|
// Private helper functions
|
||||||
void WriteMPEGFIFO(UINT8 byte);
|
void WriteMPEGFIFO(UINT8 byte);
|
||||||
|
|
||||||
// Resampler
|
// Resampler
|
||||||
CDSBResampler Resampler;
|
CDSBResampler Resampler;
|
||||||
int retainedSamples; // how many MPEG samples carried over from previous frame
|
int retainedSamples; // how many MPEG samples carried over from previous frame
|
||||||
|
|
||||||
// MPEG decode buffers (48KHz, 1/60th second + 2 extra padding samples)
|
// MPEG decode buffers (48KHz, 1/60th second + 2 extra padding samples)
|
||||||
INT16 *mpegL, *mpegR;
|
INT16 *mpegL, *mpegR;
|
||||||
|
|
||||||
// Stereo mode (do not change values because they are used in save states!)
|
// Stereo mode (do not change values because they are used in save states!)
|
||||||
enum class StereoMode: uint8_t
|
enum class StereoMode: uint8_t
|
||||||
{
|
{
|
||||||
|
@ -290,7 +291,7 @@ private:
|
||||||
MonoLeft = 1, // mono, using left stream as source data
|
MonoLeft = 1, // mono, using left stream as source data
|
||||||
MonoRight = 2 // mono, using right stream as source data
|
MonoRight = 2 // mono, using right stream as source data
|
||||||
};
|
};
|
||||||
|
|
||||||
// DSB memory
|
// DSB memory
|
||||||
const UINT8 *progROM; // 68K program ROM (passed in from parent object)
|
const UINT8 *progROM; // 68K program ROM (passed in from parent object)
|
||||||
const UINT8 *mpegROM; // MPEG music ROM
|
const UINT8 *mpegROM; // MPEG music ROM
|
||||||
|
@ -301,22 +302,27 @@ private:
|
||||||
UINT8 fifo[128];
|
UINT8 fifo[128];
|
||||||
int fifoIdxR; // read position
|
int fifoIdxR; // read position
|
||||||
int fifoIdxW; // write position
|
int fifoIdxW; // write position
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
int cmdLatch;
|
int cmdLatch;
|
||||||
int mpegState;
|
int mpegState;
|
||||||
int mpegStart, mpegEnd, playing;
|
int mpegStart, mpegEnd, playing;
|
||||||
UINT8 volume[2]; // left, right volume (0x00-0xFF)
|
UINT8 volume[2]; // left, right volume (0x00-0xFF)
|
||||||
StereoMode stereo;
|
StereoMode stereo;
|
||||||
|
|
||||||
// Settings of currently playing stream (may not match the playback register variables above)
|
// 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 usingLoopStart; // what was last set by MPEG_SetLoop() or MPEG_PlayMemory()
|
||||||
UINT32 usingLoopEnd;
|
UINT32 usingLoopEnd;
|
||||||
UINT32 usingMPEGStart; // what was last set by MPEG_PlayMemory()
|
UINT32 usingMPEGStart; // what was last set by MPEG_PlayMemory()
|
||||||
UINT32 usingMPEGEnd;
|
UINT32 usingMPEGEnd;
|
||||||
|
|
||||||
// M68K CPU
|
// M68K CPU
|
||||||
M68KCtx M68K;
|
M68KCtx M68K;
|
||||||
|
static const int k_framePeriod = 4000000/60;
|
||||||
|
static const int k_timerPeriod = 4000000/1000; // 1KHz timer
|
||||||
|
int m_totalCyclesElapsed;
|
||||||
|
int m_nextFrameEndCycles;
|
||||||
|
int m_nextTimerInterruptCycles;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue