2013-09-08 11:41:01 +00:00
/* Mednafen - Multi-system Emulator
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/* 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
2014-12-17 03:53:41 +00:00
sure the real thing does it this way too , I think it at least runs the downsampler this way ) .
2013-09-08 11:41:01 +00:00
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 .
*/
/*
Update ( ) isn ' t called on Read and Writes for performance reasons , it ' s called with sufficient granularity from the event
system , though this will obviously need to change if we ever emulate the SPU with better precision than per - sample ( pair ) .
*/
# include "psx.h"
# include "cdc.h"
# include "spu.h"
2017-07-26 00:19:30 +00:00
# include <libretro.h>
2013-09-08 11:41:01 +00:00
2017-12-20 02:51:00 +00:00
# include "../state_helpers.h"
2014-06-15 04:40:33 +00:00
uint32_t IntermediateBufferPos ;
int16_t IntermediateBuffer [ 4096 ] [ 2 ] ;
2014-06-25 01:25:45 +00:00
2014-06-19 15:53:44 +00:00
static const int16 FIR_Table [ 256 ] [ 4 ] =
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
# include "spu_fir_table.inc"
2013-09-08 11:41:01 +00:00
} ;
PS_SPU : : PS_SPU ( )
{
2015-07-24 13:59:40 +00:00
IntermediateBufferPos = 0 ;
memset ( IntermediateBuffer , 0 , sizeof ( IntermediateBuffer ) ) ;
2014-06-19 15:53:44 +00:00
2013-09-08 11:41:01 +00:00
}
PS_SPU : : ~ PS_SPU ( )
{
}
void PS_SPU : : Power ( void )
{
2015-07-24 13:59:40 +00:00
clock_divider = 768 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
memset ( SPURAM , 0 , sizeof ( SPURAM ) ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
for ( int i = 0 ; i < 24 ; i + + )
{
memset ( Voices [ i ] . DecodeBuffer , 0 , sizeof ( Voices [ i ] . DecodeBuffer ) ) ;
Voices [ i ] . DecodeM2 = 0 ;
Voices [ i ] . DecodeM1 = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . DecodePlayDelay = 0 ;
Voices [ i ] . DecodeWritePos = 0 ;
Voices [ i ] . DecodeReadPos = 0 ;
Voices [ i ] . DecodeAvail = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . DecodeShift = 0 ;
Voices [ i ] . DecodeWeight = 0 ;
Voices [ i ] . DecodeFlags = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . IgnoreSampLA = false ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . Sweep [ 0 ] . Power ( ) ;
Voices [ i ] . Sweep [ 1 ] . Power ( ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . Pitch = 0 ;
Voices [ i ] . CurPhase = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . StartAddr = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . CurAddr = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . ADSRControl = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . LoopAddr = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
Voices [ i ] . PreLRSample = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
memset ( & Voices [ i ] . ADSR , 0 , sizeof ( SPU_ADSR ) ) ;
}
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
GlobalSweep [ 0 ] . Power ( ) ;
GlobalSweep [ 1 ] . Power ( ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
NoiseDivider = 0 ;
NoiseCounter = 0 ;
LFSR = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
FM_Mode = 0 ;
Noise_Mode = 0 ;
Reverb_Mode = 0 ;
ReverbWA = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
ReverbVol [ 0 ] = ReverbVol [ 1 ] = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
CDVol [ 0 ] = CDVol [ 1 ] = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
ExternVol [ 0 ] = ExternVol [ 1 ] = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
IRQAddr = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
RWAddr = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
SPUControl = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
VoiceOn = 0 ;
VoiceOff = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
BlockEnd = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
CWA = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
memset ( Regs , 0 , sizeof ( Regs ) ) ;
2021-04-05 15:51:46 +00:00
memset ( AuxRegs , 0 , sizeof ( AuxRegs ) ) ;
2015-07-24 13:59:40 +00:00
memset ( RDSB , 0 , sizeof ( RDSB ) ) ;
memset ( RUSB , 0 , sizeof ( RUSB ) ) ;
RvbResPos = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
ReverbCur = ReverbWA ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
IRQAsserted = false ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
static INLINE void CalcVCDelta ( const uint8 zs , uint8 speed , bool log_mode , bool dec_mode , bool inv_increment , int16 Current , int & increment , int & divinco )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
increment = ( 7 - ( speed & 0x3 ) ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( inv_increment )
increment = ~ increment ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
divinco = 32768 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( speed < 0x2C )
increment = ( unsigned ) increment < < ( ( 0x2F - speed ) > > 2 ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( speed > = 0x30 )
divinco > > = ( speed - 0x2C ) > > 2 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( log_mode )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
if ( dec_mode ) // Log decrement mode
increment = ( Current * increment ) > > 15 ;
else // Log increment mode
{
if ( ( Current & 0x7FFF ) > = 0x6000 )
{
if ( speed < 0x28 )
increment > > = 2 ;
else if ( speed > = 0x2C )
divinco > > = 2 ;
else // 0x28 ... 0x2B
{
increment > > = 1 ;
divinco > > = 1 ;
}
}
}
} // end if(log_mode)
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
if ( divinco = = 0 & & speed < zs ) //0x7F)
divinco = 1 ;
2013-09-08 11:41:01 +00:00
}
INLINE void SPU_Sweep : : Power ( void )
{
2015-07-24 13:59:40 +00:00
Control = 0 ;
Current = 0 ;
Divider = 0 ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
INLINE void SPU_Sweep : : WriteControl ( uint16 value )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
Control = value ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
INLINE int16 SPU_Sweep : : ReadVolume ( void )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
return ( ( int16 ) Current ) ;
2013-09-08 11:41:01 +00:00
}
2017-01-21 13:22:10 +00:00
void SPU_Sweep : : Clock ( )
2013-09-08 11:41:01 +00:00
{
2017-01-21 13:22:10 +00:00
const bool log_mode = ( bool ) ( Control & 0x4000 ) ;
const bool dec_mode = ( bool ) ( Control & 0x2000 ) ;
const bool inv_mode = ( bool ) ( Control & 0x1000 ) ;
const bool inv_increment = ( dec_mode ^ inv_mode ) | ( dec_mode & log_mode ) ;
const uint16 vc_cv_xor = ( inv_mode & ! ( dec_mode & log_mode ) ) ? 0xFFFF : 0x0000 ;
const uint16 TestInvert = inv_mode ? 0xFFFF : 0x0000 ;
int increment ;
int divinco ;
CalcVCDelta ( 0x7F , Control & 0x7F , log_mode , dec_mode , inv_increment , ( int16 ) ( Current ^ vc_cv_xor ) , increment , divinco ) ;
if ( ( dec_mode & ! ( inv_mode & log_mode ) ) & & ( ( Current & 0x8000 ) = = ( inv_mode ? 0x0000 : 0x8000 ) | | ( Current = = 0 ) ) )
2015-07-24 13:59:40 +00:00
{
2017-01-21 13:22:10 +00:00
//
// Not sure if this condition should stop the Divider adding or force the increment value to 0.
//
Current = 0 ;
2015-07-24 13:59:40 +00:00
}
2017-01-21 13:22:10 +00:00
else
2014-06-15 04:40:33 +00:00
{
2017-01-21 13:22:10 +00:00
Divider + = divinco ;
if ( Divider & 0x8000 )
2015-07-24 13:59:40 +00:00
{
2017-01-21 13:22:10 +00:00
Divider = 0 ;
2013-09-08 11:41:01 +00:00
2017-01-21 13:22:10 +00:00
if ( dec_mode | | ( ( Current ^ TestInvert ) ! = 0x7FFF ) )
2015-07-24 13:59:40 +00:00
{
2017-01-21 13:22:10 +00:00
uint16 PrevCurrent = Current ;
Current = Current + increment ;
2013-09-08 11:41:01 +00:00
2017-01-21 13:22:10 +00:00
if ( ! dec_mode & & ( ( Current ^ PrevCurrent ) & 0x8000 ) & & ( ( Current ^ TestInvert ) & 0x8000 ) )
Current = 0x7FFF ^ TestInvert ;
2015-07-24 13:59:40 +00:00
}
}
2014-06-15 04:40:33 +00:00
}
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
INLINE void SPU_Sweep : : WriteVolume ( int16 value )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
Current = value ;
2013-09-08 11:41:01 +00:00
}
2014-06-15 04:40:33 +00:00
2014-06-19 15:53:44 +00:00
//
// Take care not to trigger SPU IRQ for the next block before its decoding start.
//
void PS_SPU : : RunDecoder ( SPU_Voice * voice )
{
2015-07-24 13:59:40 +00:00
// 5 through 0xF appear to be 0 on the real thing.
static const int32 Weights [ 16 ] [ 2 ] =
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
// s-1 s-2
{ 0 , 0 } ,
{ 60 , 0 } ,
{ 115 , - 52 } ,
{ 98 , - 55 } ,
{ 122 , - 60 } ,
} ;
if ( voice - > DecodeAvail > = 11 )
2014-06-15 04:40:33 +00:00
{
2015-07-24 13:59:40 +00:00
if ( SPUControl & 0x40 )
{
unsigned test_addr = ( voice - > CurAddr - 1 ) & 0x3FFFF ;
if ( IRQAddr = = test_addr | | IRQAddr = = ( test_addr & 0x3FFF8 ) )
{
IRQAsserted = true ;
IRQ_Assert ( IRQ_SPU , IRQAsserted ) ;
}
}
return ;
2014-06-19 15:53:44 +00:00
}
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( ( voice - > CurAddr & 0x7 ) = = 0 )
2014-06-19 15:53:44 +00:00
{
2015-07-24 13:59:40 +00:00
// 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 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
BlockEnd | = 1 < < ( voice - Voices ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( ! ( voice - > DecodeFlags & 0x2 ) ) // Force enveloping to 0 if not "looping". TODO: Should we reset the ADSR divider counter too?
{
if ( ! ( Noise_Mode & ( 1 < < ( voice - Voices ) ) ) )
{
voice - > ADSR . Phase = ADSR_RELEASE ;
voice - > ADSR . EnvLevel = 0 ;
}
}
}
2014-06-15 04:40:33 +00:00
}
2015-07-24 13:59:40 +00:00
//for(int z = 0; z < 4; z++)
2014-12-17 03:53:41 +00:00
{
2015-07-24 13:59:40 +00:00
if ( SPUControl & 0x40 )
{
unsigned test_addr = voice - > CurAddr & 0x3FFFF ;
if ( IRQAddr = = test_addr | | IRQAddr = = ( test_addr & 0x3FFF8 ) )
{
IRQAsserted = true ;
IRQ_Assert ( IRQ_SPU , IRQAsserted ) ;
}
}
2014-12-17 03:53:41 +00:00
2015-07-24 13:59:40 +00:00
if ( ( voice - > CurAddr & 0x7 ) = = 0 )
{
const uint16 CV = SPURAM [ voice - > CurAddr ] ;
voice - > DecodeShift = CV & 0xF ;
voice - > DecodeWeight = ( CV > > 4 ) & 0xF ;
voice - > DecodeFlags = ( CV > > 8 ) & 0xFF ;
if ( voice - > DecodeFlags & 0x4 )
{
if ( ! voice - > IgnoreSampLA )
voice - > LoopAddr = voice - > CurAddr ;
}
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 ] ;
2014-12-17 03:53:41 +00:00
2015-07-24 13:59:40 +00:00
CV = SPURAM [ voice - > CurAddr ] ;
shift = voice - > DecodeShift ;
2014-12-17 03:53:41 +00:00
2015-07-24 13:59:40 +00:00
if ( MDFN_UNLIKELY ( shift > 12 ) )
{
shift = 8 ;
CV & = 0x8888 ;
}
coded = ( uint32 ) CV < < 12 ;
for ( int i = 0 ; i < 4 ; i + + )
{
int32 sample = ( int16 ) ( coded & 0xF000 ) > > shift ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
sample + = ( ( voice - > DecodeM2 * weight_m2 ) > > 6 ) ;
sample + = ( ( voice - > DecodeM1 * weight_m1 ) > > 6 ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
clamp ( & sample , - 32768 , 32767 ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
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 ;
}
2014-06-19 15:53:44 +00:00
}
2013-09-08 11:41:01 +00:00
}
void PS_SPU : : CacheEnvelope ( SPU_Voice * voice )
{
2015-07-24 13:59:40 +00:00
uint32_t raw = voice - > ADSRControl ;
SPU_ADSR * ADSR = & voice - > ADSR ;
int32_t Sl = ( raw > > 0 ) & 0x0F ;
int32_t Dr = ( raw > > 4 ) & 0x0F ;
int32_t Ar = ( raw > > 8 ) & 0x7F ;
int32_t Rr = ( raw > > 16 ) & 0x1F ;
int32_t Sr = ( raw > > 22 ) & 0x7F ;
ADSR - > AttackExp = ( bool ) ( raw & ( 1 < < 15 ) ) ;
ADSR - > ReleaseExp = ( bool ) ( raw & ( 1 < < 21 ) ) ;
ADSR - > SustainExp = ( bool ) ( raw & ( 1 < < 31 ) ) ;
ADSR - > SustainDec = ( bool ) ( raw & ( 1 < < 30 ) ) ;
ADSR - > AttackRate = Ar ;
ADSR - > DecayRate = Dr < < 2 ;
ADSR - > SustainRate = Sr ;
ADSR - > ReleaseRate = Rr < < 2 ;
ADSR - > SustainLevel = ( Sl + 1 ) < < 11 ;
2013-09-08 11:41:01 +00:00
}
void PS_SPU : : ResetEnvelope ( SPU_Voice * voice )
{
2015-07-24 13:59:40 +00:00
SPU_ADSR * ADSR = & voice - > ADSR ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
ADSR - > EnvLevel = 0 ;
ADSR - > Divider = 0 ;
ADSR - > Phase = ADSR_ATTACK ;
2013-09-08 11:41:01 +00:00
}
void PS_SPU : : ReleaseEnvelope ( SPU_Voice * voice )
{
2015-07-24 13:59:40 +00:00
SPU_ADSR * ADSR = & voice - > ADSR ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
ADSR - > Divider = 0 ;
ADSR - > Phase = ADSR_RELEASE ;
2013-09-08 11:41:01 +00:00
}
void PS_SPU : : RunEnvelope ( SPU_Voice * voice )
{
2015-07-24 13:59:40 +00:00
SPU_ADSR * ADSR = & voice - > ADSR ;
int increment ;
int divinco ;
int16 uoflow_reset ;
if ( ADSR - > Phase = = ADSR_ATTACK & & ADSR - > EnvLevel = = 0x7FFF )
ADSR - > Phase + + ;
//static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool decrement, bool inv_increment, int16 Current, int &increment, int &divinco)
switch ( ADSR - > Phase )
{
default : assert ( 0 ) ;
break ;
case ADSR_ATTACK :
CalcVCDelta ( 0x7F , ADSR - > AttackRate , ADSR - > AttackExp , false , false , ( int16 ) ADSR - > EnvLevel , increment , divinco ) ;
uoflow_reset = 0x7FFF ;
break ;
case ADSR_DECAY :
CalcVCDelta ( 0x1F < < 2 , ADSR - > DecayRate , true , true , true , ( int16 ) ADSR - > EnvLevel , increment , divinco ) ;
uoflow_reset = 0 ;
break ;
case ADSR_SUSTAIN :
CalcVCDelta ( 0x7F , ADSR - > SustainRate , ADSR - > SustainExp , ADSR - > SustainDec , ADSR - > SustainDec , ( int16 ) ADSR - > EnvLevel , increment , divinco ) ;
uoflow_reset = ADSR - > SustainDec ? 0 : 0x7FFF ;
break ;
case ADSR_RELEASE :
CalcVCDelta ( 0x1F < < 2 , ADSR - > ReleaseRate , ADSR - > ReleaseExp , true , true , ( int16 ) ADSR - > EnvLevel , increment , divinco ) ;
uoflow_reset = 0 ;
break ;
}
ADSR - > Divider + = divinco ;
if ( ADSR - > Divider & 0x8000 )
{
const uint16 prev_level = ADSR - > EnvLevel ;
ADSR - > Divider = 0 ;
ADSR - > EnvLevel + = increment ;
if ( ADSR - > Phase = = ADSR_ATTACK )
{
// If previous the upper bit was 0, but now it's 1, handle overflow.
if ( ( ( prev_level ^ ADSR - > EnvLevel ) & ADSR - > EnvLevel ) & 0x8000 )
ADSR - > EnvLevel = uoflow_reset ;
}
else
{
if ( ADSR - > EnvLevel & 0x8000 )
ADSR - > EnvLevel = uoflow_reset ;
}
if ( ADSR - > Phase = = ADSR_DECAY & & ( uint16 ) ADSR - > EnvLevel < ADSR - > SustainLevel )
ADSR - > Phase + + ;
}
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
INLINE void PS_SPU : : CheckIRQAddr ( uint32 addr )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
if ( SPUControl & 0x40 )
{
if ( IRQAddr ! = addr )
return ;
IRQAsserted = true ;
IRQ_Assert ( IRQ_SPU , IRQAsserted ) ;
}
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
INLINE void PS_SPU : : WriteSPURAM ( uint32 addr , uint16 value )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
CheckIRQAddr ( addr ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
SPURAM [ addr ] = value ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
INLINE uint16 PS_SPU : : ReadSPURAM ( uint32 addr )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
CheckIRQAddr ( addr ) ;
return ( SPURAM [ addr ] ) ;
2013-09-08 11:41:01 +00:00
}
2015-07-26 13:23:11 +00:00
static INLINE int16 ReverbSat ( int32 samp )
{
if ( samp > 32767 )
samp = 32767 ;
if ( samp < - 32768 )
samp = - 32768 ;
return ( samp ) ;
}
2021-03-21 17:42:00 +00:00
# define REVERB_NEG(samp) (((samp) == -32768) ? 0x7FFF : -(samp))
2015-07-26 13:23:11 +00:00
INLINE uint32 PS_SPU : : Get_Reverb_Offset ( uint32 in_offset )
{
uint32 offset = ReverbCur + ( in_offset & 0x3FFFF ) ;
offset + = ReverbWA & ( ( int32 ) ( offset < < 13 ) > > 31 ) ;
offset & = 0x3FFFF ;
return ( offset ) ;
}
int16 NO_INLINE PS_SPU : : RD_RVB ( uint16 raw_offs , int32 extra_offs )
{
return ReadSPURAM ( Get_Reverb_Offset ( ( raw_offs < < 2 ) + extra_offs ) ) ;
}
void NO_INLINE PS_SPU : : WR_RVB ( uint16 raw_offs , int16 sample )
{
2021-03-21 17:42:00 +00:00
if ( SPUControl & 0x80 )
WriteSPURAM ( Get_Reverb_Offset ( raw_offs < < 2 ) , sample ) ;
2015-07-26 13:23:11 +00:00
}
//
// Zeroes optimized out; middle removed too(it's 16384)
static const int16 ResampTable [ 20 ] =
{
- 1 , 2 , - 10 , 35 , - 103 , 266 , - 616 , 1332 , - 2960 , 10246 , 10246 , - 2960 , 1332 , - 616 , 266 , - 103 , 35 , - 10 , 2 , - 1 ,
} ;
static INLINE int32 Reverb4422 ( const int16 * src )
{
int32 out = 0 ; // 32-bits is adequate(it won't overflow)
for ( unsigned i = 0 ; i < 20 ; i + + )
out + = ResampTable [ i ] * src [ i * 2 ] ;
// Middle non-zero
out + = 0x4000 * src [ 19 ] ;
out > > = 15 ;
clamp ( & out , - 32768 , 32767 ) ;
return ( out ) ;
}
static INLINE int32 Reverb2244 ( const int16 * src )
{
unsigned i ;
int32_t out = 0 ; /* 32bits is adequate (it won't overflow) */
for ( i = 0 ; i < 20 ; i + + )
2021-03-21 17:42:00 +00:00
out + = ResampTable [ i ] * src [ i ] ;
2015-07-26 13:23:11 +00:00
out > > = 14 ;
clamp ( & out , - 32768 , 32767 ) ;
return out ;
}
static int32 IIASM ( const int16 IIR_ALPHA , const int16 insamp )
{
if ( MDFN_UNLIKELY ( IIR_ALPHA = = - 32768 ) )
{
if ( insamp = = - 32768 )
return 0 ;
return insamp * - 65536 ;
}
return insamp * ( 32768 - IIR_ALPHA ) ;
}
//
// Take care to thoroughly test the reverb resampling code when modifying anything that uses RvbResPos.
//
void PS_SPU : : RunReverb ( const int32 * in , int32 * out )
{
2017-01-21 11:57:16 +00:00
unsigned lr ;
int32 upsampled [ 2 ] ;
2015-07-26 13:23:11 +00:00
2017-01-21 11:57:16 +00:00
upsampled [ 0 ] = upsampled [ 1 ] = 0 ;
for ( lr = 0 ; lr < 2 ; lr + + )
2015-07-26 13:23:11 +00:00
{
RDSB [ lr ] [ RvbResPos | 0x00 ] = in [ lr ] ;
RDSB [ lr ] [ RvbResPos | 0x40 ] = in [ lr ] ; // So we don't have to &/bounds check in our MAC loop
}
if ( RvbResPos & 1 )
{
int32 downsampled [ 2 ] ;
for ( unsigned lr = 0 ; lr < 2 ; lr + + )
2021-03-21 17:42:00 +00:00
downsampled [ lr ] = Reverb4422 ( & RDSB [ lr ] [ ( RvbResPos - 38 ) & 0x3F ] ) ;
2015-07-26 13:23:11 +00:00
/* Run algorithm */
2021-03-21 17:42:00 +00:00
for ( unsigned lr = 0 ; lr < 2 ; lr + + )
2015-07-26 13:23:11 +00:00
{
2021-03-21 17:42:00 +00:00
const int16 IIR_INPUT_A = ReverbSat ( ( ( ( RD_RVB ( IIR_SRC_A [ lr ^ 0 ] ) * IIR_COEF ) > > 14 ) + ( ( downsampled [ lr ] * IN_COEF [ lr ] ) > > 14 ) ) > > 1 ) ;
const int16 IIR_INPUT_B = ReverbSat ( ( ( ( RD_RVB ( IIR_SRC_B [ lr ^ 1 ] ) * IIR_COEF ) > > 14 ) + ( ( downsampled [ lr ] * IN_COEF [ lr ] ) > > 14 ) ) > > 1 ) ;
const int16 IIR_A = ReverbSat ( ( ( ( IIR_INPUT_A * IIR_ALPHA ) > > 14 ) + ( IIASM ( IIR_ALPHA , RD_RVB ( IIR_DEST_A [ lr ] , - 1 ) ) > > 14 ) ) > > 1 ) ;
const int16 IIR_B = ReverbSat ( ( ( ( IIR_INPUT_B * IIR_ALPHA ) > > 14 ) + ( IIASM ( IIR_ALPHA , RD_RVB ( IIR_DEST_B [ lr ] , - 1 ) ) > > 14 ) ) > > 1 ) ;
WR_RVB ( IIR_DEST_A [ lr ] , IIR_A ) ;
WR_RVB ( IIR_DEST_B [ lr ] , IIR_B ) ;
const int32 ACC = ( ( RD_RVB ( ACC_SRC_A [ lr ] ) * ACC_COEF_A ) > > 14 ) +
( ( RD_RVB ( ACC_SRC_B [ lr ] ) * ACC_COEF_B ) > > 14 ) +
( ( RD_RVB ( ACC_SRC_C [ lr ] ) * ACC_COEF_C ) > > 14 ) +
( ( RD_RVB ( ACC_SRC_D [ lr ] ) * ACC_COEF_D ) > > 14 ) ;
const int16 FB_A = RD_RVB ( MIX_DEST_A [ lr ] - FB_SRC_A ) ;
const int16 FB_B = RD_RVB ( MIX_DEST_B [ lr ] - FB_SRC_B ) ;
const int16 MDA = ReverbSat ( ( ACC + ( ( FB_A * REVERB_NEG ( FB_ALPHA ) ) > > 14 ) ) > > 1 ) ;
const int16 MDB = ReverbSat ( FB_A + ( ( ( ( MDA * FB_ALPHA ) > > 14 ) + ( ( FB_B * REVERB_NEG ( FB_X ) ) > > 14 ) ) > > 1 ) ) ;
const int16 IVB = ReverbSat ( FB_B + ( ( MDB * FB_X ) > > 15 ) ) ;
WR_RVB ( MIX_DEST_A [ lr ] , MDA ) ;
WR_RVB ( MIX_DEST_B [ lr ] , MDB ) ;
#if 0
{
static uint32 sqcounter ;
RUSB [ lr ] [ ( RvbResPos > > 1 ) | 0x20 ] = RUSB [ lr ] [ RvbResPos > > 1 ] = ( ( sqcounter & 0xFF ) = = 0 ) ? 0x8000 : 0x0000 ; //((sqcounter & 0x80) ? 0x7000 : 0x9000);
sqcounter + = lr ;
}
# else
RUSB [ lr ] [ ( RvbResPos > > 1 ) | 0x20 ] = RUSB [ lr ] [ RvbResPos > > 1 ] = IVB ; // Output sample
# endif
2015-07-26 13:23:11 +00:00
}
ReverbCur = ( ReverbCur + 1 ) & 0x3FFFF ;
if ( ! ReverbCur )
ReverbCur = ReverbWA ;
for ( unsigned lr = 0 ; lr < 2 ; lr + + )
{
2021-03-21 17:42:00 +00:00
const int16 * src = & RUSB [ lr ] [ ( ( RvbResPos > > 1 ) - 19 ) & 0x1F ] ;
upsampled [ lr ] = Reverb2244 ( src ) ;
2015-07-26 13:23:11 +00:00
}
}
else
{
for ( unsigned lr = 0 ; lr < 2 ; lr + + )
{
2021-03-21 17:42:00 +00:00
const int16 * src = & RUSB [ lr ] [ ( ( RvbResPos > > 1 ) - 19 ) & 0x1F ] ;
upsampled [ lr ] = src [ 9 ] ; /* Reverb 2244 (Middle non-zero */
2015-07-26 13:23:11 +00:00
}
}
RvbResPos = ( RvbResPos + 1 ) & 0x3F ;
for ( unsigned lr = 0 ; lr < 2 ; lr + + )
out [ lr ] = upsampled [ lr ] ;
}
2013-09-08 11:41:01 +00:00
2014-06-22 15:44:52 +00:00
INLINE void PS_SPU : : RunNoise ( void )
{
const unsigned rf = ( ( SPUControl > > 8 ) & 0x3F ) ;
uint32 NoiseDividerInc = ( 2 < < ( rf > > 2 ) ) ;
uint32 NoiseCounterInc = 4 + ( rf & 0x3 ) ;
if ( rf > = 0x3C )
{
NoiseDividerInc = 0x8000 ;
NoiseCounterInc = 8 ;
}
NoiseDivider + = NoiseDividerInc ;
if ( NoiseDivider & 0x8000 )
{
NoiseDivider = 0 ;
NoiseCounter + = NoiseCounterInc ;
if ( NoiseCounter & 0x8 )
{
NoiseCounter & = 0x7 ;
LFSR = ( LFSR < < 1 ) | ( ( ( LFSR > > 15 ) ^ ( LFSR > > 12 ) ^ ( LFSR > > 11 ) ^ ( LFSR > > 10 ) ^ 1 ) & 1 ) ;
}
}
}
2014-06-19 15:53:44 +00:00
int32 PS_SPU : : UpdateFromCDC ( int32 clocks )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
//int32 clocks = timestamp - lastts;
int32 sample_clocks = 0 ;
//lastts = timestamp;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
clock_divider - = clocks ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
while ( clock_divider < = 0 )
{
clock_divider + = 768 ;
sample_clocks + + ;
}
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
while ( sample_clocks > 0 )
{
// xxx[0] = left, xxx[1] = right
// Accumulated sound output.
2017-01-21 11:57:16 +00:00
int32 accum [ 2 ] ;
2015-07-24 13:59:40 +00:00
// Accumulated sound output for reverb input
2017-01-21 11:57:16 +00:00
int32 accum_fv [ 2 ] ;
2015-07-24 13:59:40 +00:00
// Output of reverb processing.
2017-01-21 11:57:16 +00:00
int32 reverb [ 2 ] ;
2015-07-24 13:59:40 +00:00
// Final output.
2017-01-21 11:57:16 +00:00
int32 output [ 2 ] ;
accum [ 0 ] = accum [ 1 ] = 0 ;
accum_fv [ 0 ] = accum_fv [ 1 ] = 0 ;
reverb [ 0 ] = reverb [ 1 ] = 0 ;
output [ 0 ] = output [ 1 ] = 0 ;
2015-07-24 13:59:40 +00:00
const uint32 PhaseModCache = FM_Mode & ~ 1 ;
/*
* *
* * 0x1F801DAE Notes and Conjecture :
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * | 15 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 4 3 2 1 0 |
* * | ? | * 13 | ? | ba | * 10 | wrr | rdr | df | is | c |
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * c - Appears to be delayed copy of lower 6 bits from 0x1F801DAA .
* *
* * is - Interrupt asserted out status . ( apparently not instantaneous status though . . . )
* *
* * df - Related to ( c & 0x30 ) = = 0x20 or ( c & 0x30 ) = = 0x30 , at least .
* * 0 = DMA busy ( FIFO not empty when in DMA write mode ? ) ?
* * 1 = DMA ready ? Something to do with the FIFO ?
* *
* * rdr - Read ( DMA read ? ) Ready ?
* *
* * wrr - Write ( DMA write ? ) Ready ?
* *
* * * 10 - Unknown . Some sort of ( FIFO ? ) busy status ? ( BIOS tests for this bit in places )
* *
* * ba - Alternates between 0 and 1 , even when SPUControl bit15 is 0 ; might be related to CD audio and voice 1 and 3 writing to SPU RAM .
* *
* * * 13 - Unknown , was set to 1 when testing with an SPU delay system reg value of 0x200921E1 ( test result might not be reliable , re - run ) .
*/
SPUStatus = SPUControl & 0x3F ;
SPUStatus | = IRQAsserted ? 0x40 : 0x00 ;
if ( Regs [ 0xD6 ] = = 0x4 ) // TODO: Investigate more(case 0x2C in global regs r/w handler)
SPUStatus | = ( CWA & 0x100 ) ? 0x800 : 0x000 ;
for ( int voice_num = 0 ; voice_num < 24 ; voice_num + + )
{
SPU_Voice * voice = & Voices [ voice_num ] ;
int32 voice_pvs ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
voice - > PreLRSample = 0 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
//PSX_WARNING("[SPU] Voice %d CurPhase=%08x, pitch=%04x, CurAddr=%08x", voice_num, voice->CurPhase, voice->Pitch, voice->CurAddr);
2013-09-08 11:41:01 +00:00
2021-03-16 05:37:29 +00:00
if ( voice - > DecodePlayDelay )
{
voice - > IgnoreSampLA = false ;
}
2015-07-24 13:59:40 +00:00
//
// Decode new samples if necessary.
//
RunDecoder ( voice ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
//
//
//
int l , r ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( Noise_Mode & ( 1 < < voice_num ) )
voice_pvs = ( int16 ) LFSR ;
else
{
const int si = voice - > DecodeReadPos ;
const int pi = ( ( voice - > CurPhase & 0xFFF ) > > 4 ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
voice_pvs = ( ( voice - > DecodeBuffer [ ( si + 0 ) & 0x1F ] * FIR_Table [ pi ] [ 0 ] ) +
( voice - > DecodeBuffer [ ( si + 1 ) & 0x1F ] * FIR_Table [ pi ] [ 1 ] ) +
( voice - > DecodeBuffer [ ( si + 2 ) & 0x1F ] * FIR_Table [ pi ] [ 2 ] ) +
( voice - > DecodeBuffer [ ( si + 3 ) & 0x1F ] * FIR_Table [ pi ] [ 3 ] ) ) > > 15 ;
}
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
voice_pvs = ( voice_pvs * ( int16 ) voice - > ADSR . EnvLevel ) > > 15 ;
voice - > PreLRSample = voice_pvs ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( voice_num = = 1 | | voice_num = = 3 )
{
int index = voice_num > > 1 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
WriteSPURAM ( 0x400 | ( index * 0x200 ) | CWA , voice_pvs ) ;
}
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
l = ( voice_pvs * voice - > Sweep [ 0 ] . ReadVolume ( ) ) > > 15 ;
r = ( voice_pvs * voice - > Sweep [ 1 ] . ReadVolume ( ) ) > > 15 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
accum [ 0 ] + = l ;
accum [ 1 ] + = r ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( Reverb_Mode & ( 1 < < voice_num ) )
{
accum_fv [ 0 ] + = l ;
accum_fv [ 1 ] + = r ;
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
// Run sweep
for ( int lr = 0 ; lr < 2 ; lr + + )
2017-01-21 13:22:10 +00:00
{
if ( ( voice - > Sweep [ lr ] . Control & 0x8000 ) )
voice - > Sweep [ lr ] . Clock ( ) ;
else
voice - > Sweep [ lr ] . Current = ( voice - > Sweep [ lr ] . Control & 0x7FFF ) < < 1 ;
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
// Increment stuff
if ( ! voice - > DecodePlayDelay )
{
unsigned phase_inc ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
// Run enveloping
RunEnvelope ( voice ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( PhaseModCache & ( 1 < < voice_num ) )
{
// This old formula: phase_inc = (voice->Pitch * ((voice - 1)->PreLRSample + 0x8000)) >> 15;
// is incorrect, as it does not handle carrier pitches >= 0x8000 properly.
phase_inc = voice - > Pitch + ( ( ( int16 ) voice - > Pitch * ( ( voice - 1 ) - > PreLRSample ) ) > > 15 ) ;
}
else
phase_inc = voice - > Pitch ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( phase_inc > 0x3FFF )
phase_inc = 0x3FFF ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
{
const uint32 tmp_phase = voice - > CurPhase + phase_inc ;
const unsigned used = tmp_phase > > 12 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
voice - > CurPhase = tmp_phase & 0xFFF ;
voice - > DecodeAvail - = used ;
voice - > DecodeReadPos = ( voice - > DecodeReadPos + used ) & 0x1F ;
}
}
else
voice - > DecodePlayDelay - - ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( VoiceOff & ( 1U < < voice_num ) )
{
if ( voice - > ADSR . Phase ! = ADSR_RELEASE )
{
2021-03-16 05:37:29 +00:00
// TODO/FIXME:
// To fix all the missing notes in "Dragon Ball GT: Final Bout" music, !voice->DecodePlayDelay instead of
// voice->DecodePlayDelay < 3 is necessary, but that would cause the length of time for which the voice off is
// effectively ignored to be too long by about half a sample(rough test measurement). That, combined with current
// CPU and DMA emulation timing inaccuracies(execution generally too fast), creates a significant risk of regressions
// in other games, so be very conservative for now.
//
// Also, voice on should be ignored during the delay as well, but comprehensive tests are needed before implementing that
// due to some effects that appear to occur repeatedly during the delay on a PS1 but are currently only emulated as
// performed when the voice on is processed(e.g. curaddr = startaddr).
//
if ( voice - > DecodePlayDelay < 3 )
{
ReleaseEnvelope ( voice ) ;
}
2015-07-24 13:59:40 +00:00
}
}
if ( VoiceOn & ( 1U < < voice_num ) )
{
ResetEnvelope ( voice ) ;
voice - > DecodeFlags = 0 ;
voice - > DecodeWritePos = 0 ;
voice - > DecodeReadPos = 0 ;
voice - > DecodeAvail = 0 ;
voice - > DecodePlayDelay = 4 ;
BlockEnd & = ~ ( 1 < < voice_num ) ;
//
// Weight/filter previous value initialization:
//
voice - > DecodeM2 = 0 ;
voice - > DecodeM1 = 0 ;
voice - > CurPhase = 0 ;
voice - > CurAddr = voice - > StartAddr & ~ 0x7 ;
voice - > IgnoreSampLA = false ;
}
if ( ! ( SPUControl & 0x8000 ) )
{
voice - > ADSR . Phase = ADSR_RELEASE ;
voice - > ADSR . EnvLevel = 0 ;
}
}
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
VoiceOff = 0 ;
VoiceOn = 0 ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
// "Mute" control doesn't seem to affect CD audio(though CD audio reverb wasn't tested...)
// 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_fv [ 1 ] = 0 ;
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
// Get CD-DA
{
int32 cda_raw [ 2 ] ;
int32 cdav [ 2 ] ;
2019-07-14 20:05:54 +00:00
const unsigned freq = ( PSX_CDC - > AudioBuffer . ReadPos < PSX_CDC - > AudioBuffer . Size ) ? PSX_CDC - > AudioBuffer . Freq : 0 ;
2017-01-21 11:57:16 +00:00
cda_raw [ 0 ] = cda_raw [ 1 ] = 0 ;
2014-06-19 15:53:44 +00:00
2017-01-21 11:57:16 +00:00
if ( freq )
2019-07-14 20:05:54 +00:00
PSX_CDC - > GetCDAudio ( cda_raw , freq ) ; // PS_CDC::GetCDAudio() guarantees the variables passed by reference will be set to 0,
2015-07-24 13:59:40 +00:00
// and that their range shall be -32768 through 32767.
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
WriteSPURAM ( CWA | 0x000 , cda_raw [ 0 ] ) ;
WriteSPURAM ( CWA | 0x200 , cda_raw [ 1 ] ) ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
for ( unsigned i = 0 ; i < 2 ; i + + )
cdav [ i ] = ( cda_raw [ i ] * CDVol [ i ] ) > > 15 ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
if ( SPUControl & 0x0001 )
{
accum [ 0 ] + = cdav [ 0 ] ;
accum [ 1 ] + = cdav [ 1 ] ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
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 ] ;
}
}
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
CWA = ( CWA + 1 ) & 0x1FF ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
RunNoise ( ) ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
for ( unsigned lr = 0 ; lr < 2 ; lr + + )
clamp ( & accum_fv [ lr ] , - 32768 , 32767 ) ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
RunReverb ( accum_fv , reverb ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
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 ) ;
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
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 ;
2014-12-17 03:53:41 +00:00
2015-07-24 13:59:40 +00:00
IntermediateBufferPos + + ;
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
sample_clocks - - ;
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
// Clock global sweep
for ( unsigned lr = 0 ; lr < 2 ; lr + + )
2017-01-21 13:22:10 +00:00
{
if ( ( GlobalSweep [ lr ] . Control & 0x8000 ) )
GlobalSweep [ lr ] . Clock ( ) ;
else
GlobalSweep [ lr ] . Current = ( GlobalSweep [ lr ] . Control & 0x7FFF ) < < 1 ;
}
2015-07-24 13:59:40 +00:00
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
//assert(clock_divider < 768);
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
return clock_divider ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
void PS_SPU : : WriteDMA ( uint32 V )
2013-09-08 11:41:01 +00:00
{
2015-07-17 11:28:02 +00:00
WriteSPURAM ( RWAddr , V ) ;
RWAddr = ( RWAddr + 1 ) & 0x3FFFF ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
WriteSPURAM ( RWAddr , V > > 16 ) ;
RWAddr = ( RWAddr + 1 ) & 0x3FFFF ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
CheckIRQAddr ( RWAddr ) ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
uint32 PS_SPU : : ReadDMA ( void )
2013-09-08 11:41:01 +00:00
{
2015-07-17 11:28:02 +00:00
uint32 ret = ( uint16 ) ReadSPURAM ( RWAddr ) ;
RWAddr = ( RWAddr + 1 ) & 0x3FFFF ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
ret | = ( uint32 ) ( uint16 ) ReadSPURAM ( RWAddr ) < < 16 ;
RWAddr = ( RWAddr + 1 ) & 0x3FFFF ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
CheckIRQAddr ( RWAddr ) ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
return ( ret ) ;
2014-06-15 04:40:33 +00:00
}
2013-09-08 11:41:01 +00:00
2015-07-26 13:34:30 +00:00
void PS_SPU : : Write ( int32_t timestamp , uint32 A , uint16 V )
2014-06-19 15:53:44 +00:00
{
2015-07-17 11:28:02 +00:00
//if((A & 0x3FF) < 0x180)
// PSX_WARNING("[SPU] Write: %08x %04x", A, V);
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
A & = 0x3FF ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
if ( A > = 0x200 )
{
if ( A < 0x260 )
{
SPU_Voice * voice = & Voices [ ( A - 0x200 ) > > 2 ] ;
voice - > Sweep [ ( A & 2 ) > > 1 ] . WriteVolume ( V ) ;
}
else if ( A < 0x280 )
AuxRegs [ ( A & 0x1F ) > > 1 ] = V ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
return ;
}
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
if ( A < 0x180 )
{
SPU_Voice * voice = & Voices [ A > > 4 ] ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
switch ( A & 0xF )
{
case 0x00 :
case 0x02 :
voice - > Sweep [ ( A & 2 ) > > 1 ] . WriteControl ( V ) ;
break ;
case 0x04 :
voice - > Pitch = V ;
break ;
case 0x06 :
voice - > StartAddr = ( V < < 2 ) & 0x3FFFF ;
break ;
case 0x08 :
voice - > ADSRControl & = 0xFFFF0000 ;
voice - > ADSRControl | = V ;
CacheEnvelope ( voice ) ;
break ;
case 0x0A :
voice - > ADSRControl & = 0x0000FFFF ;
voice - > ADSRControl | = V < < 16 ;
CacheEnvelope ( voice ) ;
break ;
case 0x0C :
voice - > ADSR . EnvLevel = V ;
break ;
case 0x0E :
voice - > LoopAddr = ( V < < 2 ) & 0x3FFFF ;
voice - > IgnoreSampLA = true ;
break ;
}
}
else
{
switch ( A & 0x7F )
{
case 0x00 :
case 0x02 : GlobalSweep [ ( A & 2 ) > > 1 ] . WriteControl ( V ) ;
break ;
case 0x04 : ReverbVol [ 0 ] = ( int16 ) V ;
break ;
case 0x06 : ReverbVol [ 1 ] = ( int16 ) V ;
break ;
// Voice ON:
case 0x08 : VoiceOn & = 0xFFFF0000 ;
VoiceOn | = V < < 0 ;
break ;
case 0x0a : VoiceOn & = 0x0000FFFF ;
VoiceOn | = ( V & 0xFF ) < < 16 ;
break ;
// Voice OFF:
case 0x0c : VoiceOff & = 0xFFFF0000 ;
VoiceOff | = V < < 0 ;
break ;
case 0x0e : VoiceOff & = 0x0000FFFF ;
VoiceOff | = ( V & 0xFF ) < < 16 ;
break ;
case 0x10 : FM_Mode & = 0xFFFF0000 ;
FM_Mode | = V < < 0 ;
break ;
case 0x12 : FM_Mode & = 0x0000FFFF ;
FM_Mode | = ( V & 0xFF ) < < 16 ;
break ;
case 0x14 : Noise_Mode & = 0xFFFF0000 ;
Noise_Mode | = V < < 0 ;
break ;
case 0x16 : Noise_Mode & = 0x0000FFFF ;
Noise_Mode | = ( V & 0xFF ) < < 16 ;
break ;
case 0x18 : Reverb_Mode & = 0xFFFF0000 ;
Reverb_Mode | = V < < 0 ;
break ;
case 0x1A : Reverb_Mode & = 0x0000FFFF ;
Reverb_Mode | = ( V & 0xFF ) < < 16 ;
break ;
case 0x1C : BlockEnd & = 0xFFFF0000 ;
BlockEnd | = V < < 0 ;
break ;
case 0x1E : BlockEnd & = 0x0000FFFF ;
BlockEnd | = V < < 16 ;
break ;
case 0x22 : ReverbWA = ( V < < 2 ) & 0x3FFFF ;
ReverbCur = ReverbWA ;
//PSX_WARNING("[SPU] Reverb WA set: 0x%04x", V);
break ;
case 0x24 :
IRQAddr = ( V < < 2 ) & 0x3FFFF ;
CheckIRQAddr ( RWAddr ) ;
break ;
case 0x26 :
RWAddr = ( V < < 2 ) & 0x3FFFF ;
CheckIRQAddr ( RWAddr ) ;
break ;
case 0x28 : WriteSPURAM ( RWAddr , V ) ;
RWAddr = ( RWAddr + 1 ) & 0x3FFFF ;
CheckIRQAddr ( RWAddr ) ;
break ;
2021-11-18 00:05:31 +00:00
case 0x2A :
2015-07-17 11:28:02 +00:00
SPUControl = V ;
if ( ! ( V & 0x40 ) )
{
IRQAsserted = false ;
IRQ_Assert ( IRQ_SPU , IRQAsserted ) ;
}
CheckIRQAddr ( RWAddr ) ;
break ;
2020-05-26 17:33:19 +00:00
case 0x2C :
//PSX_WARNING("[SPU] Global reg 0x2c set: 0x%04x", V);
2015-07-17 11:28:02 +00:00
break ;
2019-12-25 22:02:47 +00:00
case 0x30 : CDVol [ 0 ] = ( int16_t ) V ;
2015-07-17 11:28:02 +00:00
break ;
2019-12-25 22:02:47 +00:00
case 0x32 : CDVol [ 1 ] = ( int16_t ) V ;
2015-07-17 11:28:02 +00:00
break ;
2019-12-25 22:02:47 +00:00
case 0x34 : ExternVol [ 0 ] = ( int16_t ) V ;
2015-07-17 11:28:02 +00:00
break ;
2019-12-25 22:02:47 +00:00
case 0x36 : ExternVol [ 1 ] = ( int16_t ) V ;
2015-07-17 11:28:02 +00:00
break ;
case 0x38 :
case 0x3A : GlobalSweep [ ( A & 2 ) > > 1 ] . WriteVolume ( V ) ;
break ;
}
}
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
Regs [ ( A & 0x1FF ) > > 1 ] = V ;
2014-06-19 15:53:44 +00:00
}
2013-09-08 11:41:01 +00:00
2015-07-26 13:34:30 +00:00
uint16 PS_SPU : : Read ( int32_t timestamp , uint32 A )
2014-06-15 04:40:33 +00:00
{
2015-07-17 11:28:02 +00:00
A & = 0x3FF ;
2013-09-08 11:41:01 +00:00
2015-07-17 11:28:02 +00:00
if ( A > = 0x200 )
{
if ( A < 0x260 )
{
SPU_Voice * voice = & Voices [ ( A - 0x200 ) > > 2 ] ;
return voice - > Sweep [ ( A & 2 ) > > 1 ] . ReadVolume ( ) ;
}
else if ( A < 0x280 )
return ( AuxRegs [ ( A & 0x1F ) > > 1 ] ) ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
return ( 0xFFFF ) ;
}
2014-06-19 15:53:44 +00:00
2013-09-08 11:41:01 +00:00
2015-07-17 11:28:02 +00:00
if ( A < 0x180 )
{
SPU_Voice * voice = & Voices [ A > > 4 ] ;
2013-09-08 11:41:01 +00:00
2015-07-17 11:28:02 +00:00
switch ( A & 0xF )
{
case 0x0C :
return ( voice - > ADSR . EnvLevel ) ;
case 0x0E :
return ( voice - > LoopAddr > > 2 ) ;
}
}
else
{
switch ( A & 0x7F )
{
case 0x1C :
return ( BlockEnd ) ;
case 0x1E :
return ( BlockEnd > > 16 ) ;
case 0x26 :
//PSX_WARNING("[SPU] RWADDR Read");
break ;
case 0x28 :
//PSX_WARNING("[SPU] SPURAM Read port(?) Read");
{
uint16 ret = ReadSPURAM ( RWAddr ) ;
RWAddr = ( RWAddr + 1 ) & 0x3FFFF ;
CheckIRQAddr ( RWAddr ) ;
return ( ret ) ;
}
case 0x2a :
return ( SPUControl ) ;
/* FIXME: What is this used for? */
case 0x3C :
//PSX_WARNING("[SPU] Read Unknown: %08x", A);
return ( 0 ) ;
case 0x38 :
case 0x3A :
return ( GlobalSweep [ ( A & 2 ) > > 1 ] . ReadVolume ( ) ) ;
}
}
2013-09-08 11:41:01 +00:00
2015-07-17 11:28:02 +00:00
return ( Regs [ ( A & 0x1FF ) > > 1 ] ) ;
2014-06-19 15:53:44 +00:00
}
2013-09-08 11:41:01 +00:00
2014-06-19 15:53:44 +00:00
int PS_SPU : : StateAction ( StateMem * sm , int load , int data_only )
{
2015-07-24 13:59:40 +00:00
SFORMAT StateRegs [ ] =
{
2014-06-19 15:53:44 +00:00
# define SFSWEEP(r) SFVAR((r).Control), \
2015-07-24 13:59:40 +00:00
SFVAR ( ( r ) . Current ) , \
SFVAR ( ( r ) . Divider )
2014-06-19 15:53:44 +00:00
# define SFVOICE(n) SFARRAY16(&Voices[n].DecodeBuffer[0], sizeof(Voices[n].DecodeBuffer) / sizeof(Voices[n].DecodeBuffer[0])), \
2015-07-24 13:59:40 +00:00
SFVAR ( Voices [ n ] . DecodeM2 ) , \
SFVAR ( Voices [ n ] . DecodeM1 ) , \
SFVAR ( Voices [ n ] . DecodePlayDelay ) , \
SFVAR ( Voices [ n ] . DecodeWritePos ) , \
SFVAR ( Voices [ n ] . DecodeReadPos ) , \
SFVAR ( Voices [ n ] . DecodeAvail ) , \
SFVAR ( Voices [ n ] . DecodeShift ) , \
SFVAR ( Voices [ n ] . DecodeWeight ) , \
SFVAR ( Voices [ n ] . DecodeFlags ) , \
SFVAR ( Voices [ n ] . IgnoreSampLA ) , \
\
SFSWEEP ( Voices [ n ] . Sweep [ 0 ] ) , \
SFSWEEP ( Voices [ n ] . Sweep [ 1 ] ) , \
\
SFVAR ( Voices [ n ] . Pitch ) , \
SFVAR ( Voices [ n ] . CurPhase ) , \
\
SFVAR ( Voices [ n ] . StartAddr ) , \
SFVAR ( Voices [ n ] . CurAddr ) , \
SFVAR ( Voices [ n ] . ADSRControl ) , \
SFVAR ( Voices [ n ] . LoopAddr ) , \
SFVAR ( Voices [ n ] . PreLRSample ) , \
\
SFVAR ( Voices [ n ] . ADSR . EnvLevel ) , \
SFVAR ( Voices [ n ] . ADSR . Divider ) , \
SFVAR ( Voices [ n ] . ADSR . Phase ) , \
\
SFVAR ( Voices [ n ] . ADSR . AttackExp ) , \
SFVAR ( Voices [ n ] . ADSR . SustainExp ) , \
SFVAR ( Voices [ n ] . ADSR . SustainDec ) , \
SFVAR ( Voices [ n ] . ADSR . ReleaseExp ) , \
\
SFVAR ( Voices [ n ] . ADSR . AttackRate ) , \
SFVAR ( Voices [ n ] . ADSR . DecayRate ) , \
SFVAR ( Voices [ n ] . ADSR . SustainRate ) , \
SFVAR ( Voices [ n ] . ADSR . ReleaseRate ) , \
\
SFVAR ( Voices [ n ] . ADSR . SustainLevel )
SFVOICE ( 0 ) ,
SFVOICE ( 1 ) ,
SFVOICE ( 2 ) ,
SFVOICE ( 3 ) ,
SFVOICE ( 4 ) ,
SFVOICE ( 5 ) ,
SFVOICE ( 6 ) ,
SFVOICE ( 7 ) ,
SFVOICE ( 8 ) ,
SFVOICE ( 9 ) ,
SFVOICE ( 10 ) ,
SFVOICE ( 11 ) ,
SFVOICE ( 12 ) ,
SFVOICE ( 13 ) ,
SFVOICE ( 14 ) ,
SFVOICE ( 15 ) ,
SFVOICE ( 16 ) ,
SFVOICE ( 17 ) ,
SFVOICE ( 18 ) ,
SFVOICE ( 19 ) ,
SFVOICE ( 20 ) ,
SFVOICE ( 21 ) ,
SFVOICE ( 22 ) ,
SFVOICE ( 23 ) ,
2014-06-19 15:53:44 +00:00
# undef SFVOICE
2015-07-24 13:59:40 +00:00
SFVAR ( NoiseDivider ) ,
SFVAR ( NoiseCounter ) ,
SFVAR ( LFSR ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( FM_Mode ) ,
SFVAR ( Noise_Mode ) ,
SFVAR ( Reverb_Mode ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( ReverbWA ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFSWEEP ( GlobalSweep [ 0 ] ) ,
SFSWEEP ( GlobalSweep [ 1 ] ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFARRAY32 ( ReverbVol , sizeof ( ReverbVol ) / sizeof ( ReverbVol [ 0 ] ) ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFARRAY32 ( CDVol , sizeof ( CDVol ) / sizeof ( CDVol [ 0 ] ) ) ,
SFARRAY32 ( ExternVol , sizeof ( ExternVol ) / sizeof ( ExternVol [ 0 ] ) ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( IRQAddr ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( RWAddr ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( SPUControl ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( VoiceOn ) ,
SFVAR ( VoiceOff ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( BlockEnd ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( CWA ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFARRAY16 ( Regs , sizeof ( Regs ) / sizeof ( Regs [ 0 ] ) ) ,
SFARRAY16 ( AuxRegs , sizeof ( AuxRegs ) / sizeof ( AuxRegs [ 0 ] ) ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFARRAY16 ( & RDSB [ 0 ] [ 0 ] , sizeof ( RDSB ) / sizeof ( RDSB [ 0 ] [ 0 ] ) ) ,
SFVAR ( RvbResPos ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFARRAY16 ( & RUSB [ 0 ] [ 0 ] , sizeof ( RUSB ) / sizeof ( RUSB [ 0 ] [ 0 ] ) ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( ReverbCur ) ,
SFVAR ( IRQAsserted ) ,
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
SFVAR ( clock_divider ) ,
SFARRAY16 ( SPURAM , 524288 / sizeof ( uint16 ) ) ,
SFEND
} ;
2013-09-08 11:41:01 +00:00
# undef SFSWEEP
2015-07-24 13:59:40 +00:00
int ret = 1 ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
ret & = MDFNSS_StateAction ( sm , load , data_only , StateRegs , " SPU " ) ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
if ( load )
{
for ( unsigned i = 0 ; i < 24 ; i + + )
{
Voices [ i ] . DecodeReadPos & = 0x1F ;
Voices [ i ] . DecodeWritePos & = 0x1F ;
2016-09-23 19:51:50 +00:00
Voices [ i ] . CurAddr & = 0x3FFFF ;
Voices [ i ] . StartAddr & = 0x3FFFF ;
Voices [ i ] . LoopAddr & = 0x3FFFF ;
2015-07-24 13:59:40 +00:00
}
2013-09-08 11:41:01 +00:00
2016-09-23 19:51:50 +00:00
if ( clock_divider < = 0 | | clock_divider > 768 )
clock_divider = 768 ;
RWAddr & = 0x3FFFF ;
CWA & = 0x1FF ;
ReverbWA & = 0x3FFFF ;
ReverbCur & = 0x3FFFF ;
2015-07-24 13:59:40 +00:00
RvbResPos & = 0x3F ;
2013-09-08 11:41:01 +00:00
2015-07-24 13:59:40 +00:00
IRQ_Assert ( IRQ_SPU , IRQAsserted ) ;
}
2014-06-19 15:53:44 +00:00
2015-07-24 13:59:40 +00:00
return ( ret ) ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
uint16 PS_SPU : : PeekSPURAM ( uint32 address )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
return ( SPURAM [ address & 0x3FFFF ] ) ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
void PS_SPU : : PokeSPURAM ( uint32 address , uint16 value )
2013-09-08 11:41:01 +00:00
{
2015-07-24 13:59:40 +00:00
SPURAM [ address & 0x3FFFF ] = value ;
2013-09-08 11:41:01 +00:00
}
2014-06-19 15:53:44 +00:00
uint32 PS_SPU : : GetRegister ( unsigned int which , char * special , const uint32 special_len )
2013-09-08 11:41:01 +00:00
{
2015-07-17 11:28:02 +00:00
if ( which > = 0x8000 )
{
unsigned int v = ( which - 0x8000 ) > > 8 ;
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
switch ( ( which & 0xFF ) | 0x8000 )
{
case GSREG_V0_VOL_CTRL_L :
return Regs [ v * 8 + 0x0 ] ;
case GSREG_V0_VOL_CTRL_R :
return Regs [ v * 8 + 0x1 ] ;
case GSREG_V0_VOL_L :
return Voices [ v ] . Sweep [ 0 ] . ReadVolume ( ) & 0xFFFF ;
case GSREG_V0_VOL_R :
return Voices [ v ] . Sweep [ 1 ] . ReadVolume ( ) & 0xFFFF ;
case GSREG_V0_PITCH :
return Voices [ v ] . Pitch ;
case GSREG_V0_STARTADDR :
return Voices [ v ] . StartAddr ;
case GSREG_V0_ADSR_CTRL :
return Voices [ v ] . ADSRControl ;
case GSREG_V0_ADSR_LEVEL :
return Voices [ v ] . ADSR . EnvLevel ;
case GSREG_V0_LOOP_ADDR :
return Voices [ v ] . LoopAddr ;
case GSREG_V0_READ_ADDR :
return Voices [ v ] . CurAddr ;
}
}
else if ( which > = 18 & & which < = 49 )
return ReverbRegs [ which - GSREG_FB_SRC_A ] ;
else switch ( which )
{
case GSREG_SPUCONTROL :
return SPUControl ;
case GSREG_FM_ON :
return FM_Mode ;
case GSREG_NOISE_ON :
return Noise_Mode ;
case GSREG_REVERB_ON :
return Reverb_Mode ;
case GSREG_CDVOL_L :
return ( uint16 ) CDVol [ 0 ] ;
case GSREG_CDVOL_R :
return ( uint16 ) CDVol [ 1 ] ;
case GSREG_MAINVOL_CTRL_L :
return Regs [ 0xC0 ] ;
case GSREG_MAINVOL_CTRL_R :
return Regs [ 0xC1 ] ;
case GSREG_MAINVOL_L :
return GlobalSweep [ 0 ] . ReadVolume ( ) & 0xFFFF ;
case GSREG_MAINVOL_R :
return GlobalSweep [ 1 ] . ReadVolume ( ) & 0xFFFF ;
case GSREG_RVBVOL_L :
return ( uint16 ) ReverbVol [ 0 ] ;
case GSREG_RVBVOL_R :
return ( uint16 ) ReverbVol [ 1 ] ;
case GSREG_RWADDR :
return RWAddr ;
case GSREG_IRQADDR :
return IRQAddr ;
case GSREG_REVERBWA :
return ReverbWA > > 2 ;
case GSREG_VOICEON :
return VoiceOn ;
case GSREG_VOICEOFF :
return VoiceOff ;
case GSREG_BLOCKEND :
return BlockEnd ;
}
2014-06-19 15:53:44 +00:00
2015-07-17 11:28:02 +00:00
return 0xDEADBEEF ;
2014-06-19 15:53:44 +00:00
}
2013-09-08 11:41:01 +00:00
2014-06-19 15:53:44 +00:00
void PS_SPU : : SetRegister ( unsigned int which , uint32 value )
{
2015-05-06 11:25:08 +00:00
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 ;
break ;
case GSREG_FM_ON :
FM_Mode = value & 0xFFFFFF ;
break ;
case GSREG_NOISE_ON :
Noise_Mode = value & 0xFFFFFF ;
break ;
case GSREG_REVERB_ON :
Reverb_Mode = value & 0xFFFFFF ;
break ;
case GSREG_CDVOL_L :
CDVol [ 0 ] = ( int16 ) value ;
break ;
case GSREG_CDVOL_R :
CDVol [ 1 ] = ( int16 ) value ;
break ;
case GSREG_MAINVOL_CTRL_L :
Regs [ 0xC0 ] = value ;
GlobalSweep [ 0 ] . WriteControl ( value ) ;
//GlobalSweep[0].Control = value;
break ;
case GSREG_MAINVOL_CTRL_R :
Regs [ 0xC1 ] = value ;
GlobalSweep [ 1 ] . WriteControl ( value ) ;
//GlobalSweep[1].Control = value;
break ;
case GSREG_MAINVOL_L :
GlobalSweep [ 0 ] . WriteVolume ( value ) ;
break ;
case GSREG_MAINVOL_R :
GlobalSweep [ 1 ] . WriteVolume ( value ) ;
break ;
case GSREG_RVBVOL_L :
ReverbVol [ 0 ] = ( int16 ) value ;
break ;
case GSREG_RVBVOL_R :
ReverbVol [ 1 ] = ( int16 ) value ;
break ;
case GSREG_RWADDR :
RWAddr = value & 0x3FFFF ;
break ;
case GSREG_IRQADDR :
IRQAddr = value & 0x3FFFC ;
break ;
//
// REVERB_WA
//
case GSREG_VOICEON :
VoiceOn = value & 0xFFFFFF ;
break ;
case GSREG_VOICEOFF :
VoiceOff = value & 0xFFFFFF ;
break ;
case GSREG_BLOCKEND :
BlockEnd = value & 0xFFFFFF ;
break ;
}
2014-06-15 04:40:33 +00:00
}