nothing much

This commit is contained in:
zeromus 2020-04-09 16:03:43 -04:00
parent b8accdfd75
commit e2389e25d7
1 changed files with 321 additions and 316 deletions

View File

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