summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/cRSID/C64/SID.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/cRSID/C64/SID.c')
-rw-r--r--lib/rbcodec/codecs/cRSID/C64/SID.c276
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
5void 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
16void 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
30void 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
93int 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