diff options
Diffstat (limited to 'lib/rbcodec/codecs/cRSID/C64/SID.c')
-rw-r--r-- | lib/rbcodec/codecs/cRSID/C64/SID.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/cRSID/C64/SID.c b/lib/rbcodec/codecs/cRSID/C64/SID.c new file mode 100644 index 0000000000..22b07c15da --- /dev/null +++ b/lib/rbcodec/codecs/cRSID/C64/SID.c | |||
@@ -0,0 +1,276 @@ | |||
1 | |||
2 | //cRSID SID emulation engine | ||
3 | |||
4 | |||
5 | void cRSID_createSIDchip (cRSID_C64instance* C64, cRSID_SIDinstance* SID, unsigned short model, unsigned short baseaddress) { | ||
6 | SID->C64 = C64; | ||
7 | SID->ChipModel = model; | ||
8 | if( baseaddress>=0xD400 && (baseaddress<0xD800 || (0xDE00<=baseaddress && baseaddress<=0xDFE0)) ) { //check valid address, avoid Color-RAM | ||
9 | SID->BaseAddress = baseaddress; SID->BasePtr = &C64->IObankWR[baseaddress]; | ||
10 | } | ||
11 | else { SID->BaseAddress=0x0000; SID->BasePtr = NULL; } | ||
12 | cRSID_initSIDchip(SID); | ||
13 | } | ||
14 | |||
15 | |||
16 | void cRSID_initSIDchip (cRSID_SIDinstance* SID) { | ||
17 | static unsigned char Channel; | ||
18 | for (Channel = 0; Channel < 21; Channel+=7) { | ||
19 | SID->ADSRstate[Channel] = 0; SID->RateCounter[Channel] = 0; | ||
20 | SID->EnvelopeCounter[Channel] = 0; SID->ExponentCounter[Channel] = 0; | ||
21 | SID->PhaseAccu[Channel] = 0; SID->PrevPhaseAccu[Channel] = 0; | ||
22 | SID->NoiseLFSR[Channel] = 0x7FFFFF; | ||
23 | SID->PrevWavGenOut[Channel] = 0; SID->PrevWavData[Channel] = 0; | ||
24 | } | ||
25 | SID->SyncSourceMSBrise = 0; SID->RingSourceMSB = 0; | ||
26 | SID->PrevLowPass = SID->PrevBandPass = SID->PrevVolume = 0; | ||
27 | } | ||
28 | |||
29 | |||
30 | void cRSID_emulateADSRs (cRSID_SIDinstance *SID, char cycles) { | ||
31 | |||
32 | enum ADSRstateBits { GATE_BITVAL=0x01, ATTACK_BITVAL=0x80, DECAYSUSTAIN_BITVAL=0x40, HOLDZEROn_BITVAL=0x10 }; | ||
33 | |||
34 | static const short ADSRprescalePeriods[16] = { | ||
35 | 9, 32, 63, 95, 149, 220, 267, 313, 392, 977, 1954, 3126, 3907, 11720, 19532, 31251 | ||
36 | }; | ||
37 | static const unsigned char ADSRexponentPeriods[256] = { | ||
38 | 1, 30, 30, 30, 30, 30, 30, 16, 16, 16, 16, 16, 16, 16, 16, | ||
39 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, //pos0:1 pos6:30 pos14:16 pos26:8 | ||
40 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, | ||
41 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, //pos54:4 //pos93:2 | ||
42 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
43 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
44 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
45 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
46 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 | ||
47 | }; | ||
48 | |||
49 | static unsigned char Channel, PrevGate, AD, SR; | ||
50 | static unsigned short PrescalePeriod; | ||
51 | static unsigned char *ChannelPtr, *ADSRstatePtr, *EnvelopeCounterPtr, *ExponentCounterPtr; | ||
52 | static unsigned short *RateCounterPtr; | ||
53 | |||
54 | |||
55 | for (Channel=0; Channel<21; Channel+=7) { | ||
56 | |||
57 | ChannelPtr=&SID->BasePtr[Channel]; AD=ChannelPtr[5]; SR=ChannelPtr[6]; | ||
58 | ADSRstatePtr = &(SID->ADSRstate[Channel]); | ||
59 | RateCounterPtr = &(SID->RateCounter[Channel]); | ||
60 | EnvelopeCounterPtr = &(SID->EnvelopeCounter[Channel]); | ||
61 | ExponentCounterPtr = &(SID->ExponentCounter[Channel]); | ||
62 | |||
63 | PrevGate = (*ADSRstatePtr & GATE_BITVAL); | ||
64 | if ( PrevGate != (ChannelPtr[4] & GATE_BITVAL) ) { //gatebit-change? | ||
65 | if (PrevGate) *ADSRstatePtr &= ~ (GATE_BITVAL | ATTACK_BITVAL | DECAYSUSTAIN_BITVAL); //falling edge | ||
66 | else *ADSRstatePtr = (GATE_BITVAL | ATTACK_BITVAL | DECAYSUSTAIN_BITVAL | HOLDZEROn_BITVAL); //rising edge | ||
67 | } | ||
68 | |||
69 | if (*ADSRstatePtr & ATTACK_BITVAL) PrescalePeriod = ADSRprescalePeriods[ AD >> 4 ]; | ||
70 | else if (*ADSRstatePtr & DECAYSUSTAIN_BITVAL) PrescalePeriod = ADSRprescalePeriods[ AD & 0x0F ]; | ||
71 | else PrescalePeriod = ADSRprescalePeriods[ SR & 0x0F ]; | ||
72 | |||
73 | *RateCounterPtr += cycles; if (*RateCounterPtr >= 0x8000) *RateCounterPtr -= 0x8000; //*RateCounterPtr &= 0x7FFF; //can wrap around (ADSR delay-bug: short 1st frame) | ||
74 | |||
75 | if (PrescalePeriod <= *RateCounterPtr && *RateCounterPtr < PrescalePeriod+cycles) { //ratecounter shot (matches rateperiod) (in genuine SID ratecounter is LFSR) | ||
76 | *RateCounterPtr -= PrescalePeriod; //reset rate-counter on period-match | ||
77 | if ( (*ADSRstatePtr & ATTACK_BITVAL) || ++(*ExponentCounterPtr) == ADSRexponentPeriods[*EnvelopeCounterPtr] ) { | ||
78 | *ExponentCounterPtr = 0; | ||
79 | if (*ADSRstatePtr & HOLDZEROn_BITVAL) { | ||
80 | if (*ADSRstatePtr & ATTACK_BITVAL) { | ||
81 | ++(*EnvelopeCounterPtr); | ||
82 | if (*EnvelopeCounterPtr==0xFF) *ADSRstatePtr &= ~ATTACK_BITVAL; | ||
83 | } | ||
84 | else if ( !(*ADSRstatePtr & DECAYSUSTAIN_BITVAL) || *EnvelopeCounterPtr != (SR&0xF0)+(SR>>4) ) { | ||
85 | --(*EnvelopeCounterPtr); //resid adds 1 cycle delay, we omit that mechanism here | ||
86 | if (*EnvelopeCounterPtr==0) *ADSRstatePtr &= ~HOLDZEROn_BITVAL; | ||
87 | }}}}} | ||
88 | |||
89 | } | ||
90 | |||
91 | |||
92 | |||
93 | int cRSID_emulateWaves (cRSID_SIDinstance *SID) { | ||
94 | |||
95 | enum SIDspecs { CHANNELS=3+1, VOLUME_MAX=0xF, D418_DIGI_VOLUME=2 }; //digi-channel is counted too | ||
96 | enum WaveFormBits { NOISE_BITVAL=0x80, PULSE_BITVAL=0x40, SAW_BITVAL=0x20, TRI_BITVAL=0x10 }; | ||
97 | enum ControlBits { TEST_BITVAL=0x08, RING_BITVAL=0x04, SYNC_BITVAL=0x02, GATE_BITVAL=0x01 }; | ||
98 | enum FilterBits { OFF3_BITVAL=0x80, HIGHPASS_BITVAL=0x40, BANDPASS_BITVAL=0x20, LOWPASS_BITVAL=0x10 }; | ||
99 | |||
100 | #include "SID.h" | ||
101 | static const unsigned char FilterSwitchVal[] = {1,1,1,1,1,1,1,2,2,2,2,2,2,2,4}; | ||
102 | |||
103 | static char MainVolume; | ||
104 | static unsigned char Channel, WF, TestBit, Envelope, FilterSwitchReso, VolumeBand; | ||
105 | static unsigned int Utmp, PhaseAccuStep, MSB, WavGenOut, PW; | ||
106 | static int Tmp, Feedback, Steepness, PulsePeak; | ||
107 | static int FilterInput, Cutoff, Resonance, FilterOutput, NonFilted, Output; | ||
108 | static unsigned char *ChannelPtr; | ||
109 | static int *PhaseAccuPtr; | ||
110 | |||
111 | |||
112 | inline unsigned short combinedWF( const unsigned char* WFarray, unsigned short oscval) { | ||
113 | static unsigned char Pitch; | ||
114 | static unsigned short Filt; | ||
115 | if (SID->ChipModel==6581 && WFarray!=PulseTriangle) oscval &= 0x7FFF; | ||
116 | Pitch = ChannelPtr[1] ? ChannelPtr[1] : 1; //avoid division by zero | ||
117 | Filt = 0x7777 + (0x8888/Pitch); | ||
118 | SID->PrevWavData[Channel] = ( WFarray[oscval>>4]*Filt + SID->PrevWavData[Channel]*(0xFFFF-Filt) ) >> 16; | ||
119 | return SID->PrevWavData[Channel] << 8; | ||
120 | } | ||
121 | |||
122 | |||
123 | FilterInput = NonFilted = 0; | ||
124 | FilterSwitchReso = SID->BasePtr[0x17]; VolumeBand=SID->BasePtr[0x18]; | ||
125 | |||
126 | |||
127 | //Waveform-generator //(phase accumulator and waveform-selector) | ||
128 | |||
129 | |||
130 | for (Channel=0; Channel<21; Channel+=7) { | ||
131 | ChannelPtr=&(SID->BasePtr[Channel]); | ||
132 | |||
133 | WF = ChannelPtr[4]; TestBit = ( (WF & TEST_BITVAL) != 0 ); | ||
134 | PhaseAccuPtr = &(SID->PhaseAccu[Channel]); | ||
135 | |||
136 | |||
137 | PhaseAccuStep = ( (ChannelPtr[1]<<8) + ChannelPtr[0] ) * SID->C64->SampleClockRatio; | ||
138 | if (TestBit || ((WF & SYNC_BITVAL) && SID->SyncSourceMSBrise)) *PhaseAccuPtr = 0; | ||
139 | else { //stepping phase-accumulator (oscillator) | ||
140 | *PhaseAccuPtr += PhaseAccuStep; | ||
141 | if (*PhaseAccuPtr >= 0x10000000) *PhaseAccuPtr -= 0x10000000; | ||
142 | } | ||
143 | *PhaseAccuPtr &= 0xFFFFFFF; | ||
144 | MSB = *PhaseAccuPtr & 0x8000000; | ||
145 | SID->SyncSourceMSBrise = (MSB > (SID->PrevPhaseAccu[Channel] & 0x8000000)) ? 1 : 0; | ||
146 | |||
147 | |||
148 | if (WF & NOISE_BITVAL) { //noise waveform | ||
149 | Tmp = SID->NoiseLFSR[Channel]; //clock LFSR all time if clockrate exceeds observable at given samplerate (last term): | ||
150 | if ( ((*PhaseAccuPtr & 0x1000000) != (SID->PrevPhaseAccu[Channel] & 0x1000000)) || PhaseAccuStep >= 0x1000000 ) { | ||
151 | Feedback = ( (Tmp & 0x400000) ^ ((Tmp & 0x20000) << 5) ) != 0; | ||
152 | Tmp = ( (Tmp << 1) | Feedback|TestBit ) & 0x7FFFFF; //TEST-bit turns all bits in noise LFSR to 1 (on real SID slowly, in approx. 8000 microseconds ~ 300 samples) | ||
153 | SID->NoiseLFSR[Channel] = Tmp; | ||
154 | } //we simply zero output when other waveform is mixed with noise. On real SID LFSR continuously gets filled by zero and locks up. ($C1 waveform with pw<8 can keep it for a while.) | ||
155 | WavGenOut = (WF & 0x70) ? 0 : ((Tmp & 0x100000) >> 5) | ((Tmp & 0x40000) >> 4) | ((Tmp & 0x4000) >> 1) | ((Tmp & 0x800) << 1) | ||
156 | | ((Tmp & 0x200) << 2) | ((Tmp & 0x20) << 5) | ((Tmp & 0x04) << 7) | ((Tmp & 0x01) << 8); | ||
157 | } | ||
158 | |||
159 | else if (WF & PULSE_BITVAL) { //simple pulse | ||
160 | PW = ( ((ChannelPtr[3]&0xF) << 8) + ChannelPtr[2] ) << 4; //PW=0000..FFF0 from SID-register | ||
161 | Utmp = (int) (PhaseAccuStep >> 13); if (0 < PW && PW < Utmp) PW = Utmp; //Too thin pulsewidth? Correct... | ||
162 | Utmp ^= 0xFFFF; if (PW > Utmp) PW = Utmp; //Too thin pulsewidth? Correct it to a value representable at the current samplerate | ||
163 | Utmp = *PhaseAccuPtr >> 12; | ||
164 | |||
165 | if ( (WF&0xF0) == PULSE_BITVAL ) { //simple pulse, most often used waveform, make it sound as clean as possible (by making it trapezoid) | ||
166 | Steepness = (PhaseAccuStep>=4096) ? 0xFFFFFFF/PhaseAccuStep : 0xFFFF; //rising/falling-edge steepness (add/sub at samples) | ||
167 | if (TestBit) WavGenOut = 0xFFFF; | ||
168 | else if (Utmp<PW) { //rising edge (interpolation) | ||
169 | PulsePeak = (0xFFFF-PW) * Steepness; //very thin pulses don't make a full swing between 0 and max but make a little spike | ||
170 | if (PulsePeak>0xFFFF) PulsePeak=0xFFFF; //but adequately thick trapezoid pulses reach the maximum level | ||
171 | Tmp = PulsePeak - (PW-Utmp)*Steepness; //draw the slope from the peak | ||
172 | WavGenOut = (Tmp<0)? 0:Tmp; //but stop at 0-level | ||
173 | } | ||
174 | else { //falling edge (interpolation) | ||
175 | PulsePeak = PW*Steepness; //very thin pulses don't make a full swing between 0 and max but make a little spike | ||
176 | if (PulsePeak>0xFFFF) PulsePeak=0xFFFF; //adequately thick trapezoid pulses reach the maximum level | ||
177 | Tmp = (0xFFFF-Utmp)*Steepness - PulsePeak; //draw the slope from the peak | ||
178 | WavGenOut = (Tmp>=0)? 0xFFFF:Tmp; //but stop at max-level | ||
179 | }} | ||
180 | |||
181 | else { //combined pulse | ||
182 | WavGenOut = (Utmp >= PW || TestBit) ? 0xFFFF:0; | ||
183 | if (WF & TRI_BITVAL) { | ||
184 | if (WF & SAW_BITVAL) { //pulse+saw+triangle (waveform nearly identical to tri+saw) | ||
185 | if (WavGenOut) WavGenOut = combinedWF( PulseSawTriangle, Utmp); | ||
186 | } | ||
187 | else { //pulse+triangle | ||
188 | Tmp = *PhaseAccuPtr ^ ( (WF&RING_BITVAL)? SID->RingSourceMSB : 0 ); | ||
189 | if (WavGenOut) WavGenOut = combinedWF( PulseTriangle, Tmp >> 12); | ||
190 | }} | ||
191 | else if (WF & SAW_BITVAL) { //pulse+saw | ||
192 | if(WavGenOut) WavGenOut = combinedWF( PulseSawtooth, Utmp); | ||
193 | }} | ||
194 | } | ||
195 | |||
196 | else if (WF & SAW_BITVAL) { //sawtooth | ||
197 | WavGenOut = *PhaseAccuPtr >> 12; //saw (this row would be enough for simple but aliased-at-high-pitch saw) | ||
198 | if (WF & TRI_BITVAL) WavGenOut = combinedWF( SawTriangle, WavGenOut); //saw+triangle | ||
199 | else { //simple cleaned (bandlimited) saw | ||
200 | Steepness = (PhaseAccuStep>>4)/288; if(Steepness==0) Steepness=1; //avoid division by zero in next steps | ||
201 | WavGenOut += (WavGenOut * Steepness) >> 16; //1st half (rising edge) of asymmetric triangle-like saw waveform | ||
202 | if (WavGenOut>0xFFFF) WavGenOut = 0xFFFF - ( ((WavGenOut-0x10000)<<16) / Steepness ); //2nd half (falling edge, reciprocal steepness) | ||
203 | }} | ||
204 | |||
205 | else if (WF & TRI_BITVAL) { //triangle (this waveform has no harsh edges, so it doesn't suffer from strong aliasing at high pitches) | ||
206 | Tmp = *PhaseAccuPtr ^ ( WF&RING_BITVAL? SID->RingSourceMSB : 0 ); | ||
207 | WavGenOut = ( Tmp ^ (Tmp&0x8000000? 0xFFFFFFF:0) ) >> 11; | ||
208 | } | ||
209 | |||
210 | |||
211 | WavGenOut &= 0xFFFF; | ||
212 | if (WF&0xF0) SID->PrevWavGenOut[Channel] = WavGenOut; //emulate waveform 00 floating wave-DAC (utilized by SounDemon digis) | ||
213 | else WavGenOut = SID->PrevWavGenOut[Channel]; //(on real SID waveform00 decays, we just simply keep the value to avoid clicks) | ||
214 | SID->PrevPhaseAccu[Channel] = *PhaseAccuPtr; | ||
215 | SID->RingSourceMSB = MSB; | ||
216 | |||
217 | //routing the channel signal to either the filter or the unfiltered master output depending on filter-switch SID-registers | ||
218 | Envelope = SID->ChipModel==8580 ? SID->EnvelopeCounter[Channel] : ADSR_DAC_6581[SID->EnvelopeCounter[Channel]]; | ||
219 | if (FilterSwitchReso & FilterSwitchVal[Channel]) { | ||
220 | FilterInput += ( ((int)WavGenOut-0x8000) * Envelope ) >> 8; | ||
221 | } | ||
222 | else if ( Channel!=14 || !(VolumeBand & OFF3_BITVAL) ) { | ||
223 | NonFilted += ( ((int)WavGenOut-0x8000) * Envelope ) >> 8; | ||
224 | } | ||
225 | |||
226 | } | ||
227 | //update readable SID1-registers (some SID tunes might use 3rd channel ENV3/OSC3 value as control) | ||
228 | SID->C64->IObankRD[SID->BaseAddress+0x1B] = WavGenOut>>8; //OSC3, ENV3 (some players rely on it, unfortunately even for timing) | ||
229 | SID->C64->IObankRD[SID->BaseAddress+0x1C] = SID->EnvelopeCounter[14]; //Envelope | ||
230 | |||
231 | |||
232 | //Filter | ||
233 | |||
234 | |||
235 | Cutoff = (SID->BasePtr[0x16] << 3) + (SID->BasePtr[0x15] & 7); | ||
236 | Resonance = FilterSwitchReso >> 4; | ||
237 | if (SID->ChipModel == 8580) { | ||
238 | Cutoff = CutoffMul8580_44100Hz[Cutoff]; | ||
239 | Resonance = Resonances8580[Resonance]; | ||
240 | } | ||
241 | else { //6581 | ||
242 | Cutoff += (FilterInput*105)>>16; if (Cutoff>0x7FF) Cutoff=0x7FF; else if (Cutoff<0) Cutoff=0; //MOSFET-VCR control-voltage-modulation | ||
243 | Cutoff = CutoffMul6581_44100Hz[Cutoff]; //(resistance-modulation aka 6581 filter distortion) emulation | ||
244 | Resonance = Resonances6581[Resonance]; | ||
245 | } | ||
246 | |||
247 | FilterOutput=0; | ||
248 | Tmp = FilterInput + ((SID->PrevBandPass * Resonance)>>12) + SID->PrevLowPass; | ||
249 | if (VolumeBand & HIGHPASS_BITVAL) FilterOutput -= Tmp; | ||
250 | Tmp = SID->PrevBandPass - ( (Tmp * Cutoff) >> 12 ); | ||
251 | SID->PrevBandPass = Tmp; | ||
252 | if (VolumeBand & BANDPASS_BITVAL) FilterOutput -= Tmp; | ||
253 | Tmp = SID->PrevLowPass + ( (Tmp * Cutoff) >> 12 ); | ||
254 | SID->PrevLowPass = Tmp; | ||
255 | if (VolumeBand & LOWPASS_BITVAL) FilterOutput += Tmp; | ||
256 | |||
257 | |||
258 | //Output stage | ||
259 | //For $D418 volume-register digi playback: an AC / DC separation for $D418 value at low (20Hz or so) cutoff-frequency, | ||
260 | //sending AC (highpass) value to a 4th 'digi' channel mixed to the master output, and set ONLY the DC (lowpass) value to the volume-control. | ||
261 | //This solved 2 issues: Thanks to the lowpass filtering of the volume-control, SID tunes where digi is played together with normal SID channels, | ||
262 | //won't sound distorted anymore, and the volume-clicks disappear when setting SID-volume. (This is useful for fade-in/out tunes like Hades Nebula, where clicking ruins the intro.) | ||
263 | if (SID->C64->RealSIDmode) { | ||
264 | Tmp = (signed int) ( (VolumeBand&0xF) << 12 ); | ||
265 | NonFilted += (Tmp - SID->PrevVolume) * D418_DIGI_VOLUME; //highpass is digi, adding it to output must be before digifilter-code | ||
266 | SID->PrevVolume += (Tmp - SID->PrevVolume) >> 10; //arithmetic shift amount determines digi lowpass-frequency | ||
267 | MainVolume = SID->PrevVolume >> 12; //lowpass is main volume | ||
268 | } | ||
269 | else MainVolume = VolumeBand & 0xF; | ||
270 | |||
271 | Output = ((NonFilted+FilterOutput) * MainVolume) / ( (CHANNELS*VOLUME_MAX) + SID->C64->Attenuation ); | ||
272 | |||
273 | return Output; // master output | ||
274 | |||
275 | } | ||
276 | |||