1 /*
2 SN76489 emulation
3 by Maxim in 2001 and 2002
4 converted from my original Delphi implementation
5
6 I'm a C newbie so I'm sure there are loads of stupid things
7 in here which I'll come back to some day and redo
8
9 Includes:
10 - Super-high quality tone channel "oversampling" by calculating fractional positions on transitions
11 - Noise output pattern reverse engineered from actual SMS output
12 - Volume levels taken from actual SMS output
13
14 07/08/04 Charles MacDonald
15 Modified for use with SMS Plus:
16 - Added support for multiple PSG chips.
17 - Added reset/config/update routines.
18 - Added context management routines.
19 - Removed SN76489_GetValues().
20 - Removed some unused variables.
21 */
22
23 #include <stdlib.h> // malloc/free
24 #include <float.h> // for FLT_MIN
25 #include <string.h> // for memcpy
26 #include "mamedef.h"
27 #include "sn76489.h"
28 #include "panning.h"
29
30 #define NoiseInitialState 0x8000 /* Initial state of shift register */
31 #define PSG_CUTOFF 0x6 /* Value below which PSG does not output */
32
33 static const int PSGVolumeValues[16] = {
34 /* // These values are taken from a real SMS2's output
35 {892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, // I can't remember why 892... :P some scaling I did at some point
36 // these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1)
37 1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0*/
38 // The MAME core uses 0x2000 as maximum volume (0x1000 for bipolar output)
39 4096, 3254, 2584, 2053, 1631, 1295, 1029, 817, 649, 516, 410, 325, 258, 205, 163, 0
40 };
41
42 /*static SN76489_Context SN76489[MAX_SN76489];*/
43 static SN76489_Context* LastChipInit = NULL;
44 //static unsigned short int FNumLimit;
45
46
SN76489_Init(int PSGClockValue,int SamplingRate)47 SN76489_Context* SN76489_Init( int PSGClockValue, int SamplingRate)
48 {
49 int i;
50 SN76489_Context* chip = (SN76489_Context*)malloc(sizeof(SN76489_Context));
51 if(chip)
52 {
53 chip->dClock=(float)(PSGClockValue & 0x7FFFFFF)/16/SamplingRate;
54
55 SN76489_SetMute(chip, MUTE_ALLON);
56 SN76489_Config(chip, /*MUTE_ALLON,*/ FB_SEGAVDP, SRW_SEGAVDP, 1);
57
58 for( i = 0; i <= 3; i++ )
59 centre_panning(chip->panning[i]);
60 //SN76489_Reset(chip);
61
62 if ((PSGClockValue & 0x80000000) && LastChipInit != NULL)
63 {
64 // Activate special NeoGeoPocket Mode
65 LastChipInit->NgpFlags = 0x80 | 0x00;
66 chip->NgpFlags = 0x80 | 0x01;
67 chip->NgpChip2 = LastChipInit;
68 LastChipInit->NgpChip2 = chip;
69 LastChipInit = NULL;
70 }
71 else
72 {
73 chip->NgpFlags = 0x00;
74 chip->NgpChip2 = NULL;
75 LastChipInit = chip;
76 }
77 }
78 return chip;
79 }
80
SN76489_Reset(SN76489_Context * chip)81 void SN76489_Reset(SN76489_Context* chip)
82 {
83 int i;
84
85 chip->PSGStereo = 0xFF;
86
87 for( i = 0; i <= 3; i++ )
88 {
89 /* Initialise PSG state */
90 chip->Registers[2*i] = 1; /* tone freq=1 */
91 chip->Registers[2*i+1] = 0xf; /* vol=off */
92 chip->NoiseFreq = 0x10;
93
94 /* Set counters to 0 */
95 chip->ToneFreqVals[i] = 0;
96
97 /* Set flip-flops to 1 */
98 chip->ToneFreqPos[i] = 1;
99
100 /* Set intermediate positions to do-not-use value */
101 chip->IntermediatePos[i] = FLT_MIN;
102
103 /* Set panning to centre */
104 //centre_panning( chip->panning[i] );
105 }
106
107 chip->LatchedRegister = 0;
108
109 /* Initialise noise generator */
110 chip->NoiseShiftRegister = NoiseInitialState;
111
112 /* Zero clock */
113 chip->Clock = 0;
114 }
115
SN76489_Shutdown(SN76489_Context * chip)116 void SN76489_Shutdown(SN76489_Context* chip)
117 {
118 free(chip);
119 }
120
SN76489_Config(SN76489_Context * chip,int feedback,int sr_width,int boost_noise)121 void SN76489_Config(SN76489_Context* chip, /*int mute,*/ int feedback, int sr_width, int boost_noise)
122 {
123 //chip->Mute = mute;
124 chip->WhiteNoiseFeedback = feedback;
125 chip->SRWidth = sr_width;
126 }
127
128 /*
129 void SN76489_SetContext(int which, uint8 *data)
130 {
131 memcpy( &SN76489[which], data, sizeof(SN76489_Context) );
132 }
133
134 void SN76489_GetContext(int which, uint8 *data)
135 {
136 memcpy( data, &SN76489[which], sizeof(SN76489_Context) );
137 }
138
139 uint8 *SN76489_GetContextPtr(int which)
140 {
141 return (uint8 *)&SN76489[which];
142 }
143
144 int SN76489_GetContextSize(void)
145 {
146 return sizeof(SN76489_Context);
147 }
148 */
SN76489_Write(SN76489_Context * chip,int data)149 void SN76489_Write(SN76489_Context* chip, int data)
150 {
151 if ( data & 0x80 )
152 {
153 /* Latch/data byte %1 cc t dddd */
154 chip->LatchedRegister = ( data >> 4 ) & 0x07;
155 chip->Registers[chip->LatchedRegister] =
156 ( chip->Registers[chip->LatchedRegister] & 0x3f0 ) /* zero low 4 bits */
157 | ( data & 0xf ); /* and replace with data */
158 } else {
159 /* Data byte %0 - dddddd */
160 if ( !( chip->LatchedRegister % 2 ) && ( chip->LatchedRegister < 5 ) )
161 /* Tone register */
162 chip->Registers[chip->LatchedRegister] =
163 ( chip->Registers[chip->LatchedRegister] & 0x00f) /* zero high 6 bits */
164 | ( ( data & 0x3f ) << 4 ); /* and replace with data */
165 else
166 /* Other register */
167 chip->Registers[chip->LatchedRegister]=data&0x0f; /* Replace with data */
168 }
169 switch (chip->LatchedRegister) {
170 case 0:
171 case 2:
172 case 4: /* Tone channels */
173 if ( chip->Registers[chip->LatchedRegister] == 0 )
174 chip->Registers[chip->LatchedRegister] = 1; /* Zero frequency changed to 1 to avoid div/0 */
175 break;
176 case 6: /* Noise */
177 chip->NoiseShiftRegister = NoiseInitialState; /* reset shift register */
178 chip->NoiseFreq = 0x10 << ( chip->Registers[6] & 0x3 ); /* set noise signal generator frequency */
179 break;
180 }
181 }
182
SN76489_GGStereoWrite(SN76489_Context * chip,int data)183 void SN76489_GGStereoWrite(SN76489_Context* chip, int data)
184 {
185 chip->PSGStereo=data;
186 }
187
188 //void SN76489_Update(SN76489_Context* chip, INT16 **buffer, int length)
SN76489_Update(SN76489_Context * chip,INT32 ** buffer,int length)189 void SN76489_Update(SN76489_Context* chip, INT32 **buffer, int length)
190 {
191 int i, j;
192 int NGPMode;
193 SN76489_Context* chip2;
194 SN76489_Context* chip_t;
195 SN76489_Context* chip_n;
196
197 NGPMode = (chip->NgpFlags >> 7) & 0x01;
198 if (! NGPMode)
199 {
200 chip2 = NULL;
201 chip_t = chip_n = chip;
202 }
203 else
204 {
205 chip2 = (SN76489_Context*)chip->NgpChip2;
206 if (! (chip->NgpFlags & 0x01))
207 {
208 chip_t = chip;
209 chip_n = chip2;
210 }
211 else
212 {
213 chip_t = chip2;
214 chip_n = chip;
215 }
216 }
217
218 for( j = 0; j < length; j++ )
219 {
220 /* Tone channels */
221 for ( i = 0; i <= 2; ++i )
222 if ( (chip_t->Mute >> i) & 1 )
223 {
224 if ( chip_t->IntermediatePos[i] != FLT_MIN )
225 /* Intermediate position (antialiasing) */
226 chip->Channels[i] = (short)( PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->IntermediatePos[i] );
227 else
228 /* Flat (no antialiasing needed) */
229 chip->Channels[i]= PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->ToneFreqPos[i];
230 }
231 else
232 /* Muted channel */
233 chip->Channels[i] = 0;
234
235 /* Noise channel */
236 if ( (chip_t->Mute >> 3) & 1 )
237 {
238 //chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * ( chip_n->NoiseShiftRegister & 0x1 ) * 2; /* double noise volume */
239 // Now the noise is bipolar, too. -Valley Bell
240 chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * (( chip_n->NoiseShiftRegister & 0x1 ) * 2 - 1);
241 // due to the way the white noise works here, it seems twice as loud as it should be
242 if (chip->Registers[6] & 0x4 )
243 chip->Channels[3] >>= 1;
244 }
245 else
246 chip->Channels[i] = 0;
247
248 // Build stereo result into buffer
249 buffer[0][j] = 0;
250 buffer[1][j] = 0;
251 if (! chip->NgpFlags)
252 {
253 // For all 4 channels
254 for ( i = 0; i <= 3; ++i )
255 {
256 if ( ( ( chip->PSGStereo >> i ) & 0x11 ) == 0x11 )
257 {
258 // no GG stereo for this channel
259 if ( chip->panning[i][0] == 1.0f )
260 {
261 buffer[0][j] += chip->Channels[i]; // left
262 buffer[1][j] += chip->Channels[i]; // right
263 }
264 else
265 {
266 buffer[0][j] += (INT32)( chip->panning[i][0] * chip->Channels[i] ); // left
267 buffer[1][j] += (INT32)( chip->panning[i][1] * chip->Channels[i] ); // right
268 }
269 }
270 else
271 {
272 // GG stereo overrides panning
273 buffer[0][j] += ( chip->PSGStereo >> (i+4) & 0x1 ) * chip->Channels[i]; // left
274 buffer[1][j] += ( chip->PSGStereo >> i & 0x1 ) * chip->Channels[i]; // right
275 }
276 }
277 }
278 else
279 {
280 if (! (chip->NgpFlags & 0x01))
281 {
282 // For all 3 tone channels
283 for (i = 0; i < 3; i ++)
284 {
285 buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip ->Channels[i]; // left
286 buffer[1][j] += (chip->PSGStereo >> i & 0x1 ) * chip2->Channels[i]; // right
287 }
288 }
289 else
290 {
291 // noise channel
292 i = 3;
293 buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip2->Channels[i]; // left
294 buffer[1][j] += (chip->PSGStereo >> i & 0x1 ) * chip ->Channels[i]; // right
295 }
296 }
297
298 /* Increment clock by 1 sample length */
299 chip->Clock += chip->dClock;
300 chip->NumClocksForSample = (int)chip->Clock; /* truncate */
301 chip->Clock -= chip->NumClocksForSample; /* remove integer part */
302
303 /* Decrement tone channel counters */
304 for ( i = 0; i <= 2; ++i )
305 chip->ToneFreqVals[i] -= chip->NumClocksForSample;
306
307 /* Noise channel: match to tone2 or decrement its counter */
308 if ( chip->NoiseFreq == 0x80 )
309 chip->ToneFreqVals[3] = chip->ToneFreqVals[2];
310 else
311 chip->ToneFreqVals[3] -= chip->NumClocksForSample;
312
313 /* Tone channels: */
314 for ( i = 0; i <= 2; ++i ) {
315 if ( chip->ToneFreqVals[i] <= 0 ) { /* If the counter gets below 0... */
316 if (chip->Registers[i*2]>=PSG_CUTOFF) {
317 /* For tone-generating values, calculate how much of the sample is + and how much is - */
318 /* This is optimised into an even more confusing state than it was in the first place... */
319 chip->IntermediatePos[i] = ( chip->NumClocksForSample - chip->Clock + 2 * chip->ToneFreqVals[i] ) * chip->ToneFreqPos[i] / ( chip->NumClocksForSample + chip->Clock );
320 /* Flip the flip-flop */
321 chip->ToneFreqPos[i] = -chip->ToneFreqPos[i];
322 } else {
323 /* stuck value */
324 chip->ToneFreqPos[i] = 1;
325 chip->IntermediatePos[i] = FLT_MIN;
326 }
327 chip->ToneFreqVals[i] += chip->Registers[i*2] * ( chip->NumClocksForSample / chip->Registers[i*2] + 1 );
328 }
329 else
330 /* signal no antialiasing needed */
331 chip->IntermediatePos[i] = FLT_MIN;
332 }
333
334 /* Noise channel */
335 if ( chip->ToneFreqVals[3] <= 0 ) {
336 /* If the counter gets below 0... */
337 /* Flip the flip-flop */
338 chip->ToneFreqPos[3] = -chip->ToneFreqPos[3];
339 if (chip->NoiseFreq != 0x80)
340 /* If not matching tone2, decrement counter */
341 chip->ToneFreqVals[3] += chip->NoiseFreq * ( chip->NumClocksForSample / chip->NoiseFreq + 1 );
342 if (chip->ToneFreqPos[3] == 1) {
343 /* On the positive edge of the square wave (only once per cycle) */
344 int Feedback;
345 if ( chip->Registers[6] & 0x4 ) {
346 /* White noise */
347 /* Calculate parity of fed-back bits for feedback */
348 switch (chip->WhiteNoiseFeedback) {
349 /* Do some optimised calculations for common (known) feedback values */
350 case 0x0003: /* SC-3000, BBC %00000011 */
351 case 0x0009: /* SMS, GG, MD %00001001 */
352 /* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */
353 /* since that's (one or more bits set) && (not all bits set) */
354 Feedback = ( ( chip->NoiseShiftRegister & chip->WhiteNoiseFeedback )
355 && ( (chip->NoiseShiftRegister & chip->WhiteNoiseFeedback ) ^ chip->WhiteNoiseFeedback ) );
356 break;
357 default:
358 /* Default handler for all other feedback values */
359 /* XOR fold bits into the final bit */
360 Feedback = chip->NoiseShiftRegister & chip->WhiteNoiseFeedback;
361 Feedback ^= Feedback >> 8;
362 Feedback ^= Feedback >> 4;
363 Feedback ^= Feedback >> 2;
364 Feedback ^= Feedback >> 1;
365 Feedback &= 1;
366 break;
367 }
368 } else /* Periodic noise */
369 Feedback=chip->NoiseShiftRegister&1;
370
371 chip->NoiseShiftRegister=(chip->NoiseShiftRegister>>1) | (Feedback << (chip->SRWidth-1));
372 }
373 }
374 }
375 }
376
377 /*void SN76489_UpdateOne(SN76489_Context* chip, int *l, int *r)
378 {
379 INT16 tl,tr;
380 INT16 *buff[2] = { &tl, &tr };
381 SN76489_Update( chip, buff, 1 );
382 *l = tl;
383 *r = tr;
384 }*/
385
386
387 /*int SN76489_GetMute(SN76489_Context* chip)
388 {
389 return chip->Mute;
390 }*/
391
SN76489_SetMute(SN76489_Context * chip,int val)392 void SN76489_SetMute(SN76489_Context* chip, int val)
393 {
394 chip->Mute=val;
395 }
396
SN76489_SetPanning(SN76489_Context * chip,int ch0,int ch1,int ch2,int ch3)397 void SN76489_SetPanning(SN76489_Context* chip, int ch0, int ch1, int ch2, int ch3)
398 {
399 calc_panning( chip->panning[0], ch0 );
400 calc_panning( chip->panning[1], ch1 );
401 calc_panning( chip->panning[2], ch2 );
402 calc_panning( chip->panning[3], ch3 );
403 }
404