Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/genplus-gx/core/sound/sn76489.c
2 views
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
25/04/07 Eke-Eke (Genesis Plus GX)
23
- Removed stereo GG support (unused)
24
- Made SN76489_Update outputs 16bits mono samples
25
- Replaced volume table with VGM plugin's one
26
27
05/01/09 Eke-Eke (Genesis Plus GX)
28
- Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746)
29
30
24/08/10 Eke-Eke (Genesis Plus GX)
31
- Removed multichip support (unused)
32
- Removed alternate volume table, panning & mute support (unused)
33
- Removed configurable Feedback and Shift Register Width (always use Sega ones)
34
- Added linear resampling using Blip Buffer (based on Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376)
35
36
01/09/12 Eke-Eke (Genesis Plus GX)
37
- Added generic Blip-Buffer support internally, using common Master Clock as timebase
38
- Re-added stereo GG support
39
- Re-added configurable Feedback and Shift Register Width
40
- Rewrote core with various optimizations
41
*/
42
43
#include "shared.h"
44
45
#define PSG_MCYCLES_RATIO (16 * 15)
46
47
/* Initial state of shift register */
48
#define NoiseInitialState 0x8000
49
50
/* Value below which PSG does not output */
51
/*#define PSG_CUTOFF 0x6*/
52
#define PSG_CUTOFF 0x1
53
54
/* original Texas Instruments TMS SN76489AN (rev. A) used in SG-1000, SC-3000H & SF-7000 computers */
55
#define FB_DISCRETE 0x0006
56
#define SRW_DISCRETE 15
57
58
/* SN76489AN clone integrated in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */
59
#define FB_SEGAVDP 0x0009
60
#define SRW_SEGAVDP 16
61
62
typedef struct
63
{
64
/* Configuration */
65
int PreAmp[4][2]; /* stereo channels pre-amplification ratio (%) */
66
int NoiseFeedback;
67
int SRWidth;
68
69
/* PSG registers: */
70
int Registers[8]; /* Tone, vol x4 */
71
int LatchedRegister;
72
int NoiseShiftRegister;
73
int NoiseFreq; /* Noise channel signal generator frequency */
74
75
/* Output calculation variables */
76
int ToneFreqVals[4]; /* Frequency register values (counters) */
77
int ToneFreqPos[4]; /* Frequency channel flip-flops */
78
int Channel[4][2]; /* current amplitude of each (stereo) channel */
79
int ChanOut[4][2]; /* current output value of each (stereo) channel */
80
81
/* Internal M-clock counter */
82
unsigned long clocks;
83
84
} SN76489_Context;
85
86
static const uint16 PSGVolumeValues[16] =
87
{
88
/* These values are taken from a real SMS2's output */
89
/*{892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, */
90
/* I can't remember why 892... :P some scaling I did at some point */
91
/* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1) */
92
1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0
93
};
94
95
SN76489_Context SN76489;
96
97
static blip_t* blip[2];
98
99
void SN76489_Init(blip_t* left, blip_t* right, int type)
100
{
101
int i;
102
103
blip[0] = left;
104
blip[1] = right;
105
106
for (i=0; i<4; i++)
107
{
108
SN76489.PreAmp[i][0] = 100;
109
SN76489.PreAmp[i][1] = 100;
110
}
111
112
if (type == SN_DISCRETE)
113
{
114
SN76489.NoiseFeedback = FB_DISCRETE;
115
SN76489.SRWidth = SRW_DISCRETE;
116
}
117
else
118
{
119
SN76489.NoiseFeedback = FB_SEGAVDP;
120
SN76489.SRWidth = SRW_SEGAVDP;
121
}
122
}
123
124
void SN76489_Reset()
125
{
126
int i;
127
128
for(i = 0; i <= 3; i++)
129
{
130
/* Initialise PSG state */
131
SN76489.Registers[2*i] = 1; /* tone freq=1 */
132
SN76489.Registers[2*i+1] = 0xf; /* vol=off */
133
134
/* Set counters to 0 */
135
SN76489.ToneFreqVals[i] = 0;
136
137
/* Set flip-flops to 1 */
138
SN76489.ToneFreqPos[i] = 1;
139
140
/* Clear stereo channels amplitude */
141
SN76489.Channel[i][0] = 0;
142
SN76489.Channel[i][1] = 0;
143
144
/* Clear stereo channel outputs in delta buffer */
145
SN76489.ChanOut[i][0] = 0;
146
SN76489.ChanOut[i][1] = 0;
147
}
148
149
/* Initialise latched register index */
150
SN76489.LatchedRegister = 0;
151
152
/* Initialise noise generator */
153
SN76489.NoiseShiftRegister=NoiseInitialState;
154
SN76489.NoiseFreq = 0x10;
155
156
/* Reset internal M-cycle counter */
157
SN76489.clocks = 0;
158
}
159
160
void *SN76489_GetContextPtr(void)
161
{
162
return (uint8 *)&SN76489;
163
}
164
165
int SN76489_GetContextSize(void)
166
{
167
return sizeof(SN76489_Context);
168
}
169
170
/* Updates tone amplitude in delta buffer. Call whenever amplitude might have changed. */
171
INLINE void UpdateToneAmplitude(int i, int time)
172
{
173
int delta;
174
175
/* left output */
176
delta = (SN76489.Channel[i][0] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][0];
177
if (delta != 0)
178
{
179
SN76489.ChanOut[i][0] += delta;
180
blip_add_delta_fast(blip[0], time, delta);
181
}
182
183
/* right output */
184
delta = (SN76489.Channel[i][1] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][1];
185
if (delta != 0)
186
{
187
SN76489.ChanOut[i][1] += delta;
188
blip_add_delta_fast(blip[1], time, delta);
189
}
190
}
191
192
/* Updates noise amplitude in delta buffer. Call whenever amplitude might have changed. */
193
INLINE void UpdateNoiseAmplitude(int time)
194
{
195
int delta;
196
197
/* left output */
198
delta = (SN76489.Channel[3][0] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][0];
199
if (delta != 0)
200
{
201
SN76489.ChanOut[3][0] += delta;
202
blip_add_delta_fast(blip[0], time, delta);
203
}
204
205
/* right output */
206
delta = (SN76489.Channel[3][1] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][1];
207
if (delta != 0)
208
{
209
SN76489.ChanOut[3][1] += delta;
210
blip_add_delta_fast(blip[1], time, delta);
211
}
212
}
213
214
/* Runs tone channel for clock_length clocks */
215
static void RunTone(int i, int clocks)
216
{
217
int time;
218
219
/* Update in case a register changed etc. */
220
UpdateToneAmplitude(i, SN76489.clocks);
221
222
/* Time of next transition */
223
time = SN76489.ToneFreqVals[i];
224
225
/* Process any transitions that occur within clocks we're running */
226
while (time < clocks)
227
{
228
if (SN76489.Registers[i*2]>PSG_CUTOFF) {
229
/* Flip the flip-flop */
230
SN76489.ToneFreqPos[i] = -SN76489.ToneFreqPos[i];
231
} else {
232
/* stuck value */
233
SN76489.ToneFreqPos[i] = 1;
234
}
235
UpdateToneAmplitude(i, time);
236
237
/* Advance to time of next transition */
238
time += SN76489.Registers[i*2] * PSG_MCYCLES_RATIO;
239
}
240
241
/* Update channel tone counter */
242
SN76489.ToneFreqVals[i] = time;
243
}
244
245
/* Runs noise channel for clock_length clocks */
246
static void RunNoise(int clocks)
247
{
248
int time;
249
250
/* Noise channel: match to tone2 if in slave mode */
251
int NoiseFreq = SN76489.NoiseFreq;
252
if (NoiseFreq == 0x80)
253
{
254
NoiseFreq = SN76489.Registers[2*2];
255
SN76489.ToneFreqVals[3] = SN76489.ToneFreqVals[2];
256
}
257
258
/* Update in case a register changed etc. */
259
UpdateNoiseAmplitude(SN76489.clocks);
260
261
/* Time of next transition */
262
time = SN76489.ToneFreqVals[3];
263
264
/* Process any transitions that occur within clocks we're running */
265
while (time < clocks)
266
{
267
/* Flip the flip-flop */
268
SN76489.ToneFreqPos[3] = -SN76489.ToneFreqPos[3];
269
if (SN76489.ToneFreqPos[3] == 1)
270
{
271
/* On the positive edge of the square wave (only once per cycle) */
272
int Feedback = SN76489.NoiseShiftRegister;
273
if ( SN76489.Registers[6] & 0x4 )
274
{
275
/* White noise */
276
/* Calculate parity of fed-back bits for feedback */
277
/* Do some optimised calculations for common (known) feedback values */
278
/* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */
279
/* since that's (one or more bits set) && (not all bits set) */
280
Feedback = ((Feedback & SN76489.NoiseFeedback) && ((Feedback & SN76489.NoiseFeedback) ^ SN76489.NoiseFeedback));
281
}
282
else /* Periodic noise */
283
Feedback = Feedback & 1;
284
285
SN76489.NoiseShiftRegister = (SN76489.NoiseShiftRegister >> 1) | (Feedback << (SN76489.SRWidth - 1));
286
UpdateNoiseAmplitude(time);
287
}
288
289
/* Advance to time of next transition */
290
time += NoiseFreq * PSG_MCYCLES_RATIO;
291
}
292
293
/* Update channel tone counter */
294
SN76489.ToneFreqVals[3] = time;
295
}
296
297
static void SN76489_RunUntil(unsigned int clocks)
298
{
299
int i;
300
301
/* Run noise first, since it might use current value of third tone frequency counter */
302
RunNoise(clocks);
303
304
/* Run tone channels */
305
for (i=0; i<3; ++i)
306
{
307
RunTone(i, clocks);
308
}
309
}
310
311
void SN76489_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo)
312
{
313
int i;
314
315
/* cycle-accurate Game Gear stereo */
316
if (clocks > SN76489.clocks)
317
{
318
/* Run chip until current timestamp */
319
SN76489_RunUntil(clocks);
320
321
/* Update internal M-cycle counter */
322
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
323
}
324
325
for (i=0; i<4; i++)
326
{
327
/* stereo channel pre-amplification */
328
SN76489.PreAmp[i][0] = preAmp * ((stereo >> (i + 4)) & 1);
329
SN76489.PreAmp[i][1] = preAmp * ((stereo >> (i + 0)) & 1);
330
331
/* noise channel boost */
332
if (i == 3)
333
{
334
SN76489.PreAmp[3][0] = SN76489.PreAmp[3][0] << boostNoise;
335
SN76489.PreAmp[3][1] = SN76489.PreAmp[3][1] << boostNoise;
336
}
337
338
/* update stereo channel amplitude */
339
SN76489.Channel[i][0]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][0]) / 100;
340
SN76489.Channel[i][1]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][1]) / 100;
341
}
342
}
343
344
void SN76489_Update(unsigned int clocks)
345
{
346
int i;
347
348
if (clocks > SN76489.clocks)
349
{
350
/* Run chip until current timestamp */
351
SN76489_RunUntil(clocks);
352
353
/* Update internal M-cycle counter */
354
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
355
}
356
357
/* Adjust internal M-cycle counter for next frame */
358
SN76489.clocks -= clocks;
359
360
/* Adjust channel time counters for new frame */
361
for (i=0; i<4; ++i)
362
{
363
SN76489.ToneFreqVals[i] -= clocks;
364
}
365
}
366
367
void SN76489_Write(unsigned int clocks, unsigned int data)
368
{
369
unsigned int index;
370
371
if (clocks > SN76489.clocks)
372
{
373
/* run chip until current timestamp */
374
SN76489_RunUntil(clocks);
375
376
/* update internal M-cycle counter */
377
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
378
}
379
380
if (data & 0x80)
381
{
382
/* latch byte %1 cc t dddd */
383
SN76489.LatchedRegister = index = (data >> 4) & 0x07;
384
}
385
else
386
{
387
/* restore latched register index */
388
index = SN76489.LatchedRegister;
389
}
390
391
switch (index)
392
{
393
case 0:
394
case 2:
395
case 4: /* Tone Channels frequency */
396
{
397
if (data & 0x80)
398
{
399
/* Data byte %1 cc t dddd */
400
SN76489.Registers[index] = (SN76489.Registers[index] & 0x3f0) | (data & 0xf);
401
}
402
else
403
{
404
/* Data byte %0 - dddddd */
405
SN76489.Registers[index] = (SN76489.Registers[index] & 0x00f) | ((data & 0x3f) << 4);
406
}
407
408
/* zero frequency behaves the same as a value of 1 */
409
if (SN76489.Registers[index] == 0)
410
{
411
SN76489.Registers[index] = 1;
412
}
413
break;
414
}
415
416
case 1:
417
case 3:
418
case 5: /* Tone Channels attenuation */
419
{
420
data &= 0x0f;
421
SN76489.Registers[index] = data;
422
data = PSGVolumeValues[data];
423
index >>= 1;
424
SN76489.Channel[index][0] = (data * SN76489.PreAmp[index][0]) / 100;
425
SN76489.Channel[index][1] = (data * SN76489.PreAmp[index][1]) / 100;
426
break;
427
}
428
429
case 6: /* Noise control */
430
{
431
SN76489.Registers[6] = data & 0x0f;
432
433
/* reset shift register */
434
SN76489.NoiseShiftRegister = NoiseInitialState;
435
436
/* set noise signal generator frequency */
437
SN76489.NoiseFreq = 0x10 << (data&0x3);
438
break;
439
}
440
441
case 7: /* Noise attenuation */
442
{
443
data &= 0x0f;
444
SN76489.Registers[7] = data;
445
data = PSGVolumeValues[data];
446
SN76489.Channel[3][0] = (data * SN76489.PreAmp[3][0]) / 100;
447
SN76489.Channel[3][1] = (data * SN76489.PreAmp[3][1]) / 100;
448
break;
449
}
450
}
451
}
452
453