nothing much
This commit is contained in:
parent
b8accdfd75
commit
e2389e25d7
|
@ -1,69 +1,74 @@
|
|||
/******************************************************************************/
|
||||
/* Mednafen Sony PS1 Emulation Module */
|
||||
/******************************************************************************/
|
||||
/* spu.cpp:
|
||||
** Copyright (C) 2011-2016 Mednafen Team
|
||||
**
|
||||
** This program 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 Software Foundation; either version 2
|
||||
** of the License, or (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma GCC optimize ("unroll-loops")
|
||||
|
||||
/* TODO:
|
||||
Note to self: Emulating the SPU at more timing accuracy than sample, and emulating the whole SPU RAM write port FIFO thing and hypothetical periodic FIFO commit to
|
||||
SPU RAM(maybe every 32 CPU cycles, with exceptions?) will likely necessitate a much more timing-accurate CPU core, and emulation of the SPU delay register(or at least the
|
||||
effects of the standard value written to it), to avoid game glitches. Probably more trouble than it's worth....
|
||||
|
||||
SPU IRQ emulation isn't totally correct, behavior is kind of complex; run more tests on PS1.
|
||||
|
||||
Test reverb upsampler on the real thing.
|
||||
|
||||
Alter reverb algorithm to process in the pattern of L,R,L,R,L,R on each input sample, instead of doing both L and R on every 2 input samples(make
|
||||
sure the real thing does it this way too, I think it at least runs the downsampler this way).
|
||||
|
||||
Alter reverb algorithm to perform saturation more often, as occurs on the real thing.
|
||||
|
||||
See if sample flag & 0x8 does anything weird, like suppressing the program-readable block end flag setting.
|
||||
|
||||
Determine the actual purpose of global register 0x2C(is it REALLY an address multiplier? And if so, does it affect the reverb offsets too?)
|
||||
|
||||
For ADSR and volume sweep, should the divider be reset to 0 on &0x8000 == true, or should the upper bit be cleared?
|
||||
|
||||
Should shift occur on all stages of ADPCM sample decoding, or only at the end?
|
||||
|
||||
On the real thing, there's some kind of weirdness with ADSR when you voice on when attack_rate(raw) = 0x7F; the envelope level register is repeatedly
|
||||
reset to 0, which you can see by manual writes to the envelope level register. Normally in the attack phase when attack_rate = 0x7F, enveloping is effectively stuck/paused such that the value you write is sticky and won't be replaced or reset. Note that after you voice on, you can write a new attack_rate < 0x7F, and enveloping will work "normally" again shortly afterwards. You can even write an attack_rate of 0x7F at that point to pause enveloping clocking. I doubt any games rely on this, but it's something to keep in mind if we ever need greater insight as to how the SPU functions at a low-level in order to emulate it at cycle granularity rather than sample granularity, and it may not be a bad idea to investigate this oddity further and emulate it in the future regardless.
|
||||
|
||||
Voice 1 and 3 waveform output writes to SPURAM might not be correct(noted due to problems reading this area of SPU RAM on the real thing
|
||||
based on my expectations of how this should work).
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes:
|
||||
All addresses(for 16-bit access, at least) within the SPU address space appear to be fully read/write as if they were RAM, though
|
||||
values at some addresses(like the envelope current value) will be "overwritten" by the sound processing at certain times.
|
||||
|
||||
32-bit and 8-bit reads act as if it were RAM(not tested with all addresses, but a few, assuming the rest are the same), but 8-bit writes
|
||||
to odd addresses appear to be ignored, and 8-bit writes to even addresses are treated as 16-bit writes(most likely, but, need to code custom assembly to
|
||||
fully test the upper 8 bits). NOTE: the preceding information doesn't necessarily cover accesses with side effects, they still need to be tested; and it
|
||||
of course covers reads/writes from the point of view of software running on the CPU.
|
||||
|
||||
It doesn't appear to be possible to enable FM on the first channel/voice(channel/voice 0).
|
||||
|
||||
Lower bit of channel start address appears to be masked out to 0(such that ADPCM block decoding is always 8 16-bit units, 16 bytes, aligned), as far as
|
||||
block-decoding and flag-set program-readable loop address go.
|
||||
/******************************************************************************/
|
||||
/* Mednafen Sony PS1 Emulation Module */
|
||||
/******************************************************************************/
|
||||
/* spu.cpp:
|
||||
** Copyright (C) 2011-2016 Mednafen Team
|
||||
**
|
||||
** This program 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 Software Foundation; either version 2
|
||||
** of the License, or (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma GCC optimize ("unroll-loops")
|
||||
|
||||
/* TODO:
|
||||
Note to self: Emulating the SPU at more timing accuracy than sample, and emulating the whole SPU RAM write port FIFO thing and hypothetical periodic FIFO commit to
|
||||
SPU RAM(maybe every 32 CPU cycles, with exceptions?) will likely necessitate a much more timing-accurate CPU core, and emulation of the SPU delay register(or at least the
|
||||
effects of the standard value written to it), to avoid game glitches. Probably more trouble than it's worth....
|
||||
|
||||
SPU IRQ emulation isn't totally correct, behavior is kind of complex; run more tests on PS1.
|
||||
|
||||
Test reverb upsampler on the real thing.
|
||||
|
||||
Alter reverb algorithm to process in the pattern of L,R,L,R,L,R on each input sample, instead of doing both L and R on every 2 input samples(make
|
||||
sure the real thing does it this way too, I think it at least runs the downsampler this way).
|
||||
|
||||
Alter reverb algorithm to perform saturation more often, as occurs on the real thing.
|
||||
|
||||
See if sample flag & 0x8 does anything weird, like suppressing the program-readable block end flag setting.
|
||||
|
||||
Determine the actual purpose of global register 0x2C(is it REALLY an address multiplier? And if so, does it affect the reverb offsets too?)
|
||||
|
||||
For ADSR and volume sweep, should the divider be reset to 0 on &0x8000 == true, or should the upper bit be cleared?
|
||||
|
||||
Should shift occur on all stages of ADPCM sample decoding, or only at the end?
|
||||
|
||||
On the real thing, there's some kind of weirdness with ADSR when you voice on when attack_rate(raw) = 0x7F; the envelope level register is repeatedly
|
||||
reset to 0, which you can see by manual writes to the envelope level register. Normally in the attack phase when attack_rate = 0x7F, enveloping is
|
||||
effectively stuck/paused such that the value you write is sticky and won't be replaced or reset. Note that after you voice on, you can write a new
|
||||
attack_rate < 0x7F, and enveloping will work "normally" again shortly afterwards. You can even write an attack_rate of 0x7F at that point to pause
|
||||
enveloping clocking. I doubt any games rely on this, but it's something to keep in mind if we ever need greater insight as to how the SPU functions
|
||||
at a low-level in order to emulate it at cycle granularity rather than sample granularity, and it may not be a bad idea to investigate this oddity
|
||||
further and emulate it in the future regardless.
|
||||
|
||||
Voice 1 and 3 waveform output writes to SPURAM might not be correct(noted due to problems reading this area of SPU RAM on the real thing
|
||||
based on my expectations of how this should work).
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes:
|
||||
All addresses(for 16-bit access, at least) within the SPU address space appear to be fully read/write as if they were RAM, though
|
||||
values at some addresses(like the envelope current value) will be "overwritten" by the sound processing at certain times.
|
||||
|
||||
32-bit and 8-bit reads act as if it were RAM(not tested with all addresses, but a few, assuming the rest are the same), but 8-bit writes
|
||||
to odd addresses appear to be ignored, and 8-bit writes to even addresses are treated as 16-bit writes(most likely, but, need to code custom assembly to
|
||||
fully test the upper 8 bits). NOTE: the preceding information doesn't necessarily cover accesses with side effects, they still need to be tested; and it
|
||||
of course covers reads/writes from the point of view of software running on the CPU.
|
||||
|
||||
It doesn't appear to be possible to enable FM on the first channel/voice(channel/voice 0).
|
||||
|
||||
Lower bit of channel start address appears to be masked out to 0(such that ADPCM block decoding is always 8 16-bit units, 16 bytes, aligned), as far as
|
||||
block-decoding and flag-set program-readable loop address go.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -98,89 +103,89 @@ PS_SPU::~PS_SPU()
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
void PS_SPU::Power(void)
|
||||
{
|
||||
clock_divider = 768;
|
||||
|
||||
memset(SPURAM, 0, sizeof(SPURAM));
|
||||
|
||||
for(int i = 0; i < 24; i++)
|
||||
{
|
||||
memset(Voices[i].DecodeBuffer, 0, sizeof(Voices[i].DecodeBuffer));
|
||||
Voices[i].DecodeM2 = 0;
|
||||
Voices[i].DecodeM1 = 0;
|
||||
|
||||
Voices[i].DecodePlayDelay = 0;
|
||||
Voices[i].DecodeWritePos = 0;
|
||||
Voices[i].DecodeReadPos = 0;
|
||||
Voices[i].DecodeAvail = 0;
|
||||
|
||||
Voices[i].DecodeShift = 0;
|
||||
Voices[i].DecodeWeight = 0;
|
||||
Voices[i].DecodeFlags = 0;
|
||||
|
||||
Voices[i].IgnoreSampLA = false;
|
||||
|
||||
Voices[i].Sweep[0].Power();
|
||||
Voices[i].Sweep[1].Power();
|
||||
|
||||
Voices[i].Pitch = 0;
|
||||
Voices[i].CurPhase = 0;
|
||||
|
||||
Voices[i].StartAddr = 0;
|
||||
|
||||
Voices[i].CurAddr = 0;
|
||||
|
||||
Voices[i].ADSRControl = 0;
|
||||
|
||||
Voices[i].LoopAddr = 0;
|
||||
|
||||
Voices[i].PreLRSample = 0;
|
||||
|
||||
memset(&Voices[i].ADSR, 0, sizeof(SPU_ADSR));
|
||||
}
|
||||
|
||||
GlobalSweep[0].Power();
|
||||
GlobalSweep[1].Power();
|
||||
|
||||
NoiseDivider = 0;
|
||||
NoiseCounter = 0;
|
||||
LFSR = 0;
|
||||
|
||||
FM_Mode = 0;
|
||||
Noise_Mode = 0;
|
||||
Reverb_Mode = 0;
|
||||
ReverbWA = 0;
|
||||
|
||||
ReverbVol[0] = ReverbVol[1] = 0;
|
||||
|
||||
CDVol[0] = CDVol[1] = 0;
|
||||
|
||||
ExternVol[0] = ExternVol[1] = 0;
|
||||
|
||||
IRQAddr = 0;
|
||||
|
||||
RWAddr = 0;
|
||||
|
||||
SPUControl = 0;
|
||||
|
||||
VoiceOn = 0;
|
||||
VoiceOff = 0;
|
||||
|
||||
BlockEnd = 0;
|
||||
|
||||
CWA = 0;
|
||||
|
||||
memset(Regs, 0, sizeof(Regs));
|
||||
|
||||
memset(RDSB, 0, sizeof(RDSB));
|
||||
memset(RUSB, 0, sizeof(RUSB));
|
||||
RvbResPos = 0;
|
||||
|
||||
ReverbCur = ReverbWA;
|
||||
|
||||
IRQAsserted = false;
|
||||
|
||||
void PS_SPU::Power(void)
|
||||
{
|
||||
clock_divider = 768;
|
||||
|
||||
memset(SPURAM, 0, sizeof(SPURAM));
|
||||
|
||||
for(int i = 0; i < 24; i++)
|
||||
{
|
||||
memset(Voices[i].DecodeBuffer, 0, sizeof(Voices[i].DecodeBuffer));
|
||||
Voices[i].DecodeM2 = 0;
|
||||
Voices[i].DecodeM1 = 0;
|
||||
|
||||
Voices[i].DecodePlayDelay = 0;
|
||||
Voices[i].DecodeWritePos = 0;
|
||||
Voices[i].DecodeReadPos = 0;
|
||||
Voices[i].DecodeAvail = 0;
|
||||
|
||||
Voices[i].DecodeShift = 0;
|
||||
Voices[i].DecodeWeight = 0;
|
||||
Voices[i].DecodeFlags = 0;
|
||||
|
||||
Voices[i].IgnoreSampLA = false;
|
||||
|
||||
Voices[i].Sweep[0].Power();
|
||||
Voices[i].Sweep[1].Power();
|
||||
|
||||
Voices[i].Pitch = 0;
|
||||
Voices[i].CurPhase = 0;
|
||||
|
||||
Voices[i].StartAddr = 0;
|
||||
|
||||
Voices[i].CurAddr = 0;
|
||||
|
||||
Voices[i].ADSRControl = 0;
|
||||
|
||||
Voices[i].LoopAddr = 0;
|
||||
|
||||
Voices[i].PreLRSample = 0;
|
||||
|
||||
memset(&Voices[i].ADSR, 0, sizeof(SPU_ADSR));
|
||||
}
|
||||
|
||||
GlobalSweep[0].Power();
|
||||
GlobalSweep[1].Power();
|
||||
|
||||
NoiseDivider = 0;
|
||||
NoiseCounter = 0;
|
||||
LFSR = 0;
|
||||
|
||||
FM_Mode = 0;
|
||||
Noise_Mode = 0;
|
||||
Reverb_Mode = 0;
|
||||
ReverbWA = 0;
|
||||
|
||||
ReverbVol[0] = ReverbVol[1] = 0;
|
||||
|
||||
CDVol[0] = CDVol[1] = 0;
|
||||
|
||||
ExternVol[0] = ExternVol[1] = 0;
|
||||
|
||||
IRQAddr = 0;
|
||||
|
||||
RWAddr = 0;
|
||||
|
||||
SPUControl = 0;
|
||||
|
||||
VoiceOn = 0;
|
||||
VoiceOff = 0;
|
||||
|
||||
BlockEnd = 0;
|
||||
|
||||
CWA = 0;
|
||||
|
||||
memset(Regs, 0, sizeof(Regs));
|
||||
|
||||
memset(RDSB, 0, sizeof(RDSB));
|
||||
memset(RUSB, 0, sizeof(RUSB));
|
||||
RvbResPos = 0;
|
||||
|
||||
ReverbCur = ReverbWA;
|
||||
|
||||
IRQAsserted = false;
|
||||
}
|
||||
|
||||
static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool dec_mode, bool inv_increment, int16 Current, int &increment, int &divinco)
|
||||
|
@ -192,8 +197,8 @@ static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool
|
|||
|
||||
divinco = 32768;
|
||||
|
||||
if(speed < 0x2C)
|
||||
increment = (unsigned)increment << ((0x2F - speed) >> 2);
|
||||
if(speed < 0x2C)
|
||||
increment = (unsigned)increment << ((0x2F - speed) >> 2);
|
||||
|
||||
if(speed >= 0x30)
|
||||
divinco >>= (speed - 0x2C) >> 2;
|
||||
|
@ -330,20 +335,20 @@ void PS_SPU::RunDecoder(SPU_Voice *voice)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if((voice->CurAddr & 0x7) == 0)
|
||||
{
|
||||
//
|
||||
// Handle delayed flags from the previously-decoded block.
|
||||
//
|
||||
// NOTE: The timing of setting the BlockEnd bit here, and forcing ADSR envelope volume to 0, is a bit late. (And I'm not sure if it should be done once
|
||||
// per decoded block, or more than once, but that's probably not something games would rely on, but we should test it anyway).
|
||||
//
|
||||
// Correctish timing can be achieved by moving this block of code up above voice->DecodeAvail >= 11, and sticking it inside an: if(voice->DecodeAvail <= 12),
|
||||
// though more tests are needed on the ADPCM decoding process as a whole before we should actually make such a change. Additionally, we'd probably
|
||||
// have to separate the CurAddr = LoopAddr logic, so we don't generate spurious early SPU IRQs.
|
||||
//
|
||||
if(voice->DecodeFlags & 0x1)
|
||||
|
||||
if((voice->CurAddr & 0x7) == 0)
|
||||
{
|
||||
//
|
||||
// Handle delayed flags from the previously-decoded block.
|
||||
//
|
||||
// NOTE: The timing of setting the BlockEnd bit here, and forcing ADSR envelope volume to 0, is a bit late. (And I'm not sure if it should be done once
|
||||
// per decoded block, or more than once, but that's probably not something games would rely on, but we should test it anyway).
|
||||
//
|
||||
// Correctish timing can be achieved by moving this block of code up above voice->DecodeAvail >= 11, and sticking it inside an: if(voice->DecodeAvail <= 12),
|
||||
// though more tests are needed on the ADPCM decoding process as a whole before we should actually make such a change. Additionally, we'd probably
|
||||
// have to separate the CurAddr = LoopAddr logic, so we don't generate spurious early SPU IRQs.
|
||||
//
|
||||
if(voice->DecodeFlags & 0x1)
|
||||
{
|
||||
voice->CurAddr = voice->LoopAddr & ~0x7;
|
||||
|
||||
|
@ -398,50 +403,50 @@ void PS_SPU::RunDecoder(SPU_Voice *voice)
|
|||
voice->CurAddr = (voice->CurAddr + 1) & 0x3FFFF;
|
||||
}
|
||||
|
||||
//
|
||||
// Don't else this block; we need to ALWAYS decode 4 samples per call to RunDecoder() if DecodeAvail < 11, or else sample playback
|
||||
// at higher rates will fail horribly.
|
||||
//
|
||||
{
|
||||
const int32 weight_m1 = Weights[voice->DecodeWeight][0];
|
||||
const int32 weight_m2 = Weights[voice->DecodeWeight][1];
|
||||
uint16 CV;
|
||||
unsigned shift;
|
||||
uint32 coded;
|
||||
int16 *tb = &voice->DecodeBuffer[voice->DecodeWritePos];
|
||||
|
||||
CV = SPURAM[voice->CurAddr];
|
||||
shift = voice->DecodeShift;
|
||||
|
||||
if(MDFN_UNLIKELY(shift > 12))
|
||||
{
|
||||
//PSX_DBG(PSX_DBG_FLOOD, "[SPU] Buggy/Illegal ADPCM block shift value on voice %u: %u\n", (unsigned)(voice - Voices), shift);
|
||||
|
||||
shift = 8;
|
||||
CV &= 0x8888;
|
||||
}
|
||||
|
||||
coded = (uint32)CV << 12;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
int32 sample = (int16)(coded & 0xF000) >> shift;
|
||||
|
||||
sample += ((voice->DecodeM2 * weight_m2) >> 6);
|
||||
sample += ((voice->DecodeM1 * weight_m1) >> 6);
|
||||
|
||||
clamp(&sample, -32768, 32767);
|
||||
|
||||
tb[i] = sample;
|
||||
voice->DecodeM2 = voice->DecodeM1;
|
||||
voice->DecodeM1 = sample;
|
||||
coded >>= 4;
|
||||
}
|
||||
voice->DecodeWritePos = (voice->DecodeWritePos + 4) & 0x1F;
|
||||
voice->DecodeAvail += 4;
|
||||
voice->CurAddr = (voice->CurAddr + 1) & 0x3FFFF;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Don't else this block; we need to ALWAYS decode 4 samples per call to RunDecoder() if DecodeAvail < 11, or else sample playback
|
||||
// at higher rates will fail horribly.
|
||||
//
|
||||
{
|
||||
const int32 weight_m1 = Weights[voice->DecodeWeight][0];
|
||||
const int32 weight_m2 = Weights[voice->DecodeWeight][1];
|
||||
uint16 CV;
|
||||
unsigned shift;
|
||||
uint32 coded;
|
||||
int16 *tb = &voice->DecodeBuffer[voice->DecodeWritePos];
|
||||
|
||||
CV = SPURAM[voice->CurAddr];
|
||||
shift = voice->DecodeShift;
|
||||
|
||||
if(MDFN_UNLIKELY(shift > 12))
|
||||
{
|
||||
//PSX_DBG(PSX_DBG_FLOOD, "[SPU] Buggy/Illegal ADPCM block shift value on voice %u: %u\n", (unsigned)(voice - Voices), shift);
|
||||
|
||||
shift = 8;
|
||||
CV &= 0x8888;
|
||||
}
|
||||
|
||||
coded = (uint32)CV << 12;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
int32 sample = (int16)(coded & 0xF000) >> shift;
|
||||
|
||||
sample += ((voice->DecodeM2 * weight_m2) >> 6);
|
||||
sample += ((voice->DecodeM1 * weight_m1) >> 6);
|
||||
|
||||
clamp(&sample, -32768, 32767);
|
||||
|
||||
tb[i] = sample;
|
||||
voice->DecodeM2 = voice->DecodeM1;
|
||||
voice->DecodeM1 = sample;
|
||||
coded >>= 4;
|
||||
}
|
||||
voice->DecodeWritePos = (voice->DecodeWritePos + 4) & 0x1F;
|
||||
voice->DecodeAvail += 4;
|
||||
voice->CurAddr = (voice->CurAddr + 1) & 0x3FFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PS_SPU::CacheEnvelope(SPU_Voice *voice)
|
||||
|
@ -617,20 +622,20 @@ int32 PS_SPU::UpdateFromCDC(int32 clocks)
|
|||
sample_clocks++;
|
||||
}
|
||||
|
||||
while(sample_clocks > 0)
|
||||
{
|
||||
// xxx[0] = left, xxx[1] = right
|
||||
|
||||
// Accumulated sound output.
|
||||
int32 accum[2] = { 0, 0 };
|
||||
|
||||
// Accumulated sound output for reverb input
|
||||
int32 accum_fv[2] = { 0, 0 };
|
||||
|
||||
// Output of reverb processing.
|
||||
int32 reverb[2] = { 0, 0 };
|
||||
|
||||
// Final output.
|
||||
while(sample_clocks > 0)
|
||||
{
|
||||
// xxx[0] = left, xxx[1] = right
|
||||
|
||||
// Accumulated sound output.
|
||||
int32 accum[2] = { 0, 0 };
|
||||
|
||||
// Accumulated sound output for reverb input
|
||||
int32 accum_fv[2] = { 0, 0 };
|
||||
|
||||
// Output of reverb processing.
|
||||
int32 reverb[2] = { 0, 0 };
|
||||
|
||||
// Final output.
|
||||
int32 output[2] = { 0, 0 };
|
||||
|
||||
const uint32 PhaseModCache = FM_Mode & ~ 1;
|
||||
|
@ -713,14 +718,14 @@ while(sample_clocks > 0)
|
|||
l = (voice_pvs * voice->Sweep[0].ReadVolume()) >> 15;
|
||||
r = (voice_pvs * voice->Sweep[1].ReadVolume()) >> 15;
|
||||
|
||||
accum[0] += l;
|
||||
accum[1] += r;
|
||||
|
||||
if(Reverb_Mode & (1 << voice_num))
|
||||
{
|
||||
accum_fv[0] += l;
|
||||
accum[0] += l;
|
||||
accum[1] += r;
|
||||
|
||||
if(Reverb_Mode & (1 << voice_num))
|
||||
{
|
||||
accum_fv[0] += l;
|
||||
accum_fv[1] += r;
|
||||
}
|
||||
}
|
||||
|
||||
// Run sweep
|
||||
for(int lr = 0; lr < 2; lr++)
|
||||
|
@ -805,9 +810,9 @@ while(sample_clocks > 0)
|
|||
// TODO: If we add sub-sample timing accuracy, see if it's checked for every channel at different times, or just once.
|
||||
if(!(SPUControl & 0x4000))
|
||||
{
|
||||
accum[0] = 0;
|
||||
accum[1] = 0;
|
||||
accum_fv[0] = 0;
|
||||
accum[0] = 0;
|
||||
accum[1] = 0;
|
||||
accum_fv[0] = 0;
|
||||
accum_fv[1] = 0;
|
||||
}
|
||||
|
||||
|
@ -825,55 +830,55 @@ while(sample_clocks > 0)
|
|||
for(unsigned i = 0; i < 2; i++)
|
||||
cdav[i] = (cda_raw[i] * CDVol[i]) >> 15;
|
||||
|
||||
if(SPUControl & 0x0001)
|
||||
{
|
||||
accum[0] += cdav[0];
|
||||
accum[1] += cdav[1];
|
||||
|
||||
if(SPUControl & 0x0004) // TODO: Test this bit(and see if it is really dependent on bit0)
|
||||
{
|
||||
accum_fv[0] += cdav[0];
|
||||
accum_fv[1] += cdav[1];
|
||||
}
|
||||
}
|
||||
if(SPUControl & 0x0001)
|
||||
{
|
||||
accum[0] += cdav[0];
|
||||
accum[1] += cdav[1];
|
||||
|
||||
if(SPUControl & 0x0004) // TODO: Test this bit(and see if it is really dependent on bit0)
|
||||
{
|
||||
accum_fv[0] += cdav[0];
|
||||
accum_fv[1] += cdav[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CWA = (CWA + 1) & 0x1FF;
|
||||
|
||||
RunNoise();
|
||||
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
clamp(&accum_fv[lr], -32768, 32767);
|
||||
|
||||
RunReverb(accum_fv, reverb);
|
||||
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
{
|
||||
accum[lr] += ((reverb[lr] * ReverbVol[lr]) >> 15);
|
||||
clamp(&accum[lr], -32768, 32767);
|
||||
output[lr] = (accum[lr] * GlobalSweep[lr].ReadVolume()) >> 15;
|
||||
clamp(&output[lr], -32768, 32767);
|
||||
}
|
||||
|
||||
if(IntermediateBufferPos < 4096) // Overflow might occur in some debugger use cases.
|
||||
{
|
||||
// 75%, for some (resampling) headroom.
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
IntermediateBuffer[IntermediateBufferPos][lr] = (output[lr] * 3 + 2) >> 2;
|
||||
|
||||
IntermediateBufferPos++;
|
||||
}
|
||||
|
||||
sample_clocks--;
|
||||
|
||||
// Clock global sweep
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
GlobalSweep[lr].Clock();
|
||||
}
|
||||
|
||||
//assert(clock_divider < 768);
|
||||
|
||||
return clock_divider;
|
||||
RunNoise();
|
||||
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
clamp(&accum_fv[lr], -32768, 32767);
|
||||
|
||||
RunReverb(accum_fv, reverb);
|
||||
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
{
|
||||
accum[lr] += ((reverb[lr] * ReverbVol[lr]) >> 15);
|
||||
clamp(&accum[lr], -32768, 32767);
|
||||
output[lr] = (accum[lr] * GlobalSweep[lr].ReadVolume()) >> 15;
|
||||
clamp(&output[lr], -32768, 32767);
|
||||
}
|
||||
|
||||
if(IntermediateBufferPos < 4096) // Overflow might occur in some debugger use cases.
|
||||
{
|
||||
// 75%, for some (resampling) headroom.
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
IntermediateBuffer[IntermediateBufferPos][lr] = (output[lr] * 3 + 2) >> 2;
|
||||
|
||||
IntermediateBufferPos++;
|
||||
}
|
||||
|
||||
sample_clocks--;
|
||||
|
||||
// Clock global sweep
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
GlobalSweep[lr].Clock();
|
||||
}
|
||||
|
||||
//assert(clock_divider < 768);
|
||||
|
||||
return clock_divider;
|
||||
}
|
||||
|
||||
void PS_SPU::WriteDMA(uint32 V)
|
||||
|
@ -1067,16 +1072,16 @@ void PS_SPU::Write(pscpu_timestamp_t timestamp, uint32 A, uint16 V)
|
|||
case 0x2C: PSX_WARNING("[SPU] Global reg 0x2c set: 0x%04x", V);
|
||||
break;
|
||||
|
||||
case 0x30: CDVol[0] = (int16)V;
|
||||
break;
|
||||
|
||||
case 0x32: CDVol[1] = (int16)V;
|
||||
break;
|
||||
|
||||
case 0x34: ExternVol[0] = (int16)V;
|
||||
break;
|
||||
|
||||
case 0x36: ExternVol[1] = (int16)V;
|
||||
case 0x30: CDVol[0] = (int16)V;
|
||||
break;
|
||||
|
||||
case 0x32: CDVol[1] = (int16)V;
|
||||
break;
|
||||
|
||||
case 0x34: ExternVol[0] = (int16)V;
|
||||
break;
|
||||
|
||||
case 0x36: ExternVol[1] = (int16)V;
|
||||
break;
|
||||
|
||||
case 0x38:
|
||||
|
@ -1241,26 +1246,26 @@ SYNCFUNC(PS_SPU)
|
|||
|
||||
if(isReader)
|
||||
{
|
||||
for(unsigned i = 0; i < 24; i++)
|
||||
{
|
||||
Voices[i].DecodeReadPos &= 0x1F;
|
||||
Voices[i].DecodeWritePos &= 0x1F;
|
||||
Voices[i].CurAddr &= 0x3FFFF;
|
||||
Voices[i].StartAddr &= 0x3FFFF;
|
||||
Voices[i].LoopAddr &= 0x3FFFF;
|
||||
}
|
||||
|
||||
if(clock_divider <= 0 || clock_divider > 768)
|
||||
clock_divider = 768;
|
||||
|
||||
RWAddr &= 0x3FFFF;
|
||||
CWA &= 0x1FF;
|
||||
|
||||
ReverbWA &= 0x3FFFF;
|
||||
ReverbCur &= 0x3FFFF;
|
||||
|
||||
RvbResPos &= 0x3F;
|
||||
|
||||
for(unsigned i = 0; i < 24; i++)
|
||||
{
|
||||
Voices[i].DecodeReadPos &= 0x1F;
|
||||
Voices[i].DecodeWritePos &= 0x1F;
|
||||
Voices[i].CurAddr &= 0x3FFFF;
|
||||
Voices[i].StartAddr &= 0x3FFFF;
|
||||
Voices[i].LoopAddr &= 0x3FFFF;
|
||||
}
|
||||
|
||||
if(clock_divider <= 0 || clock_divider > 768)
|
||||
clock_divider = 768;
|
||||
|
||||
RWAddr &= 0x3FFFF;
|
||||
CWA &= 0x1FF;
|
||||
|
||||
ReverbWA &= 0x3FFFF;
|
||||
ReverbCur &= 0x3FFFF;
|
||||
|
||||
RvbResPos &= 0x3F;
|
||||
|
||||
IRQ_Assert(IRQ_SPU, IRQAsserted);
|
||||
}
|
||||
}
|
||||
|
@ -1326,9 +1331,9 @@ uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 speci
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if(which >= GSREG_FB_SRC_A && which <= GSREG_IN_COEF_R)
|
||||
{
|
||||
ret = ReverbRegs[which - GSREG_FB_SRC_A];
|
||||
else if(which >= GSREG_FB_SRC_A && which <= GSREG_IN_COEF_R)
|
||||
{
|
||||
ret = ReverbRegs[which - GSREG_FB_SRC_A];
|
||||
}
|
||||
else switch(which)
|
||||
{
|
||||
|
@ -1410,11 +1415,11 @@ uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 speci
|
|||
|
||||
void PS_SPU::SetRegister(unsigned int which, uint32 value)
|
||||
{
|
||||
if(which >= GSREG_FB_SRC_A && which <= GSREG_IN_COEF_R)
|
||||
{
|
||||
ReverbRegs[which - GSREG_FB_SRC_A] = value;
|
||||
}
|
||||
else switch(which)
|
||||
if(which >= GSREG_FB_SRC_A && which <= GSREG_IN_COEF_R)
|
||||
{
|
||||
ReverbRegs[which - GSREG_FB_SRC_A] = value;
|
||||
}
|
||||
else switch(which)
|
||||
{
|
||||
case GSREG_SPUCONTROL:
|
||||
SPUControl = value;
|
||||
|
|
Loading…
Reference in New Issue