1 /***************************************************************************
2
3 sn76496.c
4
5 Routines to emulate the Texas Instruments SN76489 / SN76496 programmable
6 tone /noise generator. Also known as (or at least compatible with) TMS9919.
7
8 Noise emulation is not accurate due to lack of documentation. The noise
9 generator uses a shift register with a XOR-feedback network, but the exact
10 layout is unknown. It can be set for either period or white noise; again,
11 the details are unknown.
12
13 ***************************************************************************/
14
15 #include "driver.h"
16
17
18 #define MAX_OUTPUT 0x7fff
19
20 #define STEP 0x10000
21
22
23 /* Formulas for noise generator */
24 /* bit0 = output */
25
26 /* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */
27 #define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */
28
29 /* noise feedback for periodic noise mode */
30 //#define FB_PNOISE 0x10000 /* 16bit rorate */
31 #define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */
32
33 /*
34 0x08000 is definitely wrong. The Master System conversion of Marble Madness
35 uses periodic noise as a baseline. With a 15-bit rotate, the bassline is
36 out of tune.
37 The 16-bit rotate has been confirmed against a real PAL Sega Master System 2.
38 Hope that helps the System E stuff, more news on the PSG as and when!
39 */
40
41 /* noise generator start preset (for periodic noise) */
42 #define NG_PRESET 0x0f35
43
44
45 struct SN76496
46 {
47 int Channel;
48 int SampleRate;
49 unsigned int UpdateStep;
50 int VolTable[16]; /* volume table */
51 int Register[8]; /* registers */
52 int LastRegister; /* last register written */
53 int Volume[4]; /* volume of voice 0-2 and noise */
54 unsigned int RNG; /* noise generator */
55 int NoiseFB; /* noise feedback mask */
56 int Period[4];
57 int Count[4];
58 int Output[4];
59 };
60
61
62 static struct SN76496 sn[MAX_76496];
63
64
65
SN76496Write(int chip,int data)66 static void SN76496Write(int chip,int data)
67 {
68 struct SN76496 *R = &sn[chip];
69
70
71 /* update the output buffer before changing the registers */
72 stream_update(R->Channel,0);
73
74 if (data & 0x80)
75 {
76 int r = (data & 0x70) >> 4;
77 int c = r/2;
78
79 R->LastRegister = r;
80 R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
81 switch (r)
82 {
83 case 0: /* tone 0 : frequency */
84 case 2: /* tone 1 : frequency */
85 case 4: /* tone 2 : frequency */
86 R->Period[c] = R->UpdateStep * R->Register[r];
87 if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
88 if (r == 4)
89 {
90 /* update noise shift frequency */
91 if ((R->Register[6] & 0x03) == 0x03)
92 R->Period[3] = 2 * R->Period[2];
93 }
94 break;
95 case 1: /* tone 0 : volume */
96 case 3: /* tone 1 : volume */
97 case 5: /* tone 2 : volume */
98 case 7: /* noise : volume */
99 R->Volume[c] = R->VolTable[data & 0x0f];
100 break;
101 case 6: /* noise : frequency, mode */
102 {
103 int n = R->Register[6];
104 R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
105 n &= 3;
106 /* N/512,N/1024,N/2048,Tone #3 output */
107 R->Period[3] = (n == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+n));
108
109 /* reset noise shifter */
110 R->RNG = NG_PRESET;
111 R->Output[3] = R->RNG & 1;
112 }
113 break;
114 }
115 }
116 else
117 {
118 int r = R->LastRegister;
119 int c = r/2;
120
121 switch (r)
122 {
123 case 0: /* tone 0 : frequency */
124 case 2: /* tone 1 : frequency */
125 case 4: /* tone 2 : frequency */
126 R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
127 R->Period[c] = R->UpdateStep * R->Register[r];
128 if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
129 if (r == 4)
130 {
131 /* update noise shift frequency */
132 if ((R->Register[6] & 0x03) == 0x03)
133 R->Period[3] = 2 * R->Period[2];
134 }
135 break;
136 }
137 }
138 }
139
140
WRITE_HANDLER(SN76496_0_w)141 WRITE_HANDLER( SN76496_0_w ) { SN76496Write(0,data); }
WRITE_HANDLER(SN76496_1_w)142 WRITE_HANDLER( SN76496_1_w ) { SN76496Write(1,data); }
WRITE_HANDLER(SN76496_2_w)143 WRITE_HANDLER( SN76496_2_w ) { SN76496Write(2,data); }
WRITE_HANDLER(SN76496_3_w)144 WRITE_HANDLER( SN76496_3_w ) { SN76496Write(3,data); }
145
146
147
SN76496Update(int chip,INT16 * buffer,int length)148 static void SN76496Update(int chip,INT16 *buffer,int length)
149 {
150 int i;
151 struct SN76496 *R = &sn[chip];
152
153
154 /* If the volume is 0, increase the counter */
155 for (i = 0;i < 4;i++)
156 {
157 if (R->Volume[i] == 0)
158 {
159 /* note that I do count += length, NOT count = length + 1. You might think */
160 /* it's the same since the volume is 0, but doing the latter could cause */
161 /* interferencies when the program is rapidly modulating the volume. */
162 if (R->Count[i] <= length*STEP) R->Count[i] += length*STEP;
163 }
164 }
165
166 while (length > 0)
167 {
168 int vol[4];
169 unsigned int out;
170 int left;
171
172
173 /* vol[] keeps track of how long each square wave stays */
174 /* in the 1 position during the sample period. */
175 vol[0] = vol[1] = vol[2] = vol[3] = 0;
176
177 for (i = 0;i < 3;i++)
178 {
179 if (R->Output[i]) vol[i] += R->Count[i];
180 R->Count[i] -= STEP;
181 /* Period[i] is the half period of the square wave. Here, in each */
182 /* loop I add Period[i] twice, so that at the end of the loop the */
183 /* square wave is in the same status (0 or 1) it was at the start. */
184 /* vol[i] is also incremented by Period[i], since the wave has been 1 */
185 /* exactly half of the time, regardless of the initial position. */
186 /* If we exit the loop in the middle, Output[i] has to be inverted */
187 /* and vol[i] incremented only if the exit status of the square */
188 /* wave is 1. */
189 while (R->Count[i] <= 0)
190 {
191 R->Count[i] += R->Period[i];
192 if (R->Count[i] > 0)
193 {
194 R->Output[i] ^= 1;
195 if (R->Output[i]) vol[i] += R->Period[i];
196 break;
197 }
198 R->Count[i] += R->Period[i];
199 vol[i] += R->Period[i];
200 }
201 if (R->Output[i]) vol[i] -= R->Count[i];
202 }
203
204 left = STEP;
205 do
206 {
207 int nextevent;
208
209
210 if (R->Count[3] < left) nextevent = R->Count[3];
211 else nextevent = left;
212
213 if (R->Output[3]) vol[3] += R->Count[3];
214 R->Count[3] -= nextevent;
215 if (R->Count[3] <= 0)
216 {
217 if (R->RNG & 1) R->RNG ^= R->NoiseFB;
218 R->RNG >>= 1;
219 R->Output[3] = R->RNG & 1;
220 R->Count[3] += R->Period[3];
221 if (R->Output[3]) vol[3] += R->Period[3];
222 }
223 if (R->Output[3]) vol[3] -= R->Count[3];
224
225 left -= nextevent;
226 } while (left > 0);
227
228 out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] +
229 vol[2] * R->Volume[2] + vol[3] * R->Volume[3];
230
231 if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP;
232
233 *(buffer++) = out / STEP;
234
235 length--;
236 }
237 }
238
239
240
SN76496_set_clock(int chip,int clock)241 static void SN76496_set_clock(int chip,int clock)
242 {
243 struct SN76496 *R = &sn[chip];
244
245
246 /* the base clock for the tone generators is the chip clock divided by 16; */
247 /* for the noise generator, it is clock / 256. */
248 /* Here we calculate the number of steps which happen during one sample */
249 /* at the given sample rate. No. of events = sample rate / (clock/16). */
250 /* STEP is a multiplier used to turn the fraction into a fixed point */
251 /* number. */
252 R->UpdateStep = ((double)STEP * R->SampleRate * 16) / clock;
253 }
254
255
256
SN76496_set_gain(int chip,int gain)257 static void SN76496_set_gain(int chip,int gain)
258 {
259 struct SN76496 *R = &sn[chip];
260 int i;
261 double out;
262
263
264 gain &= 0xff;
265
266 /* increase max output basing on gain (0.2 dB per step) */
267 out = MAX_OUTPUT / 3;
268 while (gain-- > 0)
269 out *= 1.023292992; /* = (10 ^ (0.2/20)) */
270
271 /* build volume table (2dB per step) */
272 for (i = 0;i < 15;i++)
273 {
274 /* limit volume to avoid clipping */
275 if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3;
276 else R->VolTable[i] = out;
277
278 out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
279 }
280 R->VolTable[15] = 0;
281 }
282
283
284
SN76496_init(const struct MachineSound * msound,int chip,int clock,int volume,int sample_rate)285 static int SN76496_init(const struct MachineSound *msound,int chip,int clock,int volume,int sample_rate)
286 {
287 int i;
288 struct SN76496 *R = &sn[chip];
289 char name[40];
290
291
292 sprintf(name,"SN76496 #%d",chip);
293 R->Channel = stream_init(name,volume,sample_rate,chip,SN76496Update);
294
295 if (R->Channel == -1)
296 return 1;
297
298 R->SampleRate = sample_rate;
299 SN76496_set_clock(chip,clock);
300
301 for (i = 0;i < 4;i++) R->Volume[i] = 0;
302
303 R->LastRegister = 0;
304 for (i = 0;i < 8;i+=2)
305 {
306 R->Register[i] = 0;
307 R->Register[i + 1] = 0x0f; /* volume = 0 */
308 }
309
310 for (i = 0;i < 4;i++)
311 {
312 R->Output[i] = 0;
313 R->Period[i] = R->Count[i] = R->UpdateStep;
314 }
315 R->RNG = NG_PRESET;
316 R->Output[3] = R->RNG & 1;
317
318 return 0;
319 }
320
321
322
SN76496_sh_start(const struct MachineSound * msound)323 int SN76496_sh_start(const struct MachineSound *msound)
324 {
325 int chip;
326 const struct SN76496interface *intf = msound->sound_interface;
327
328
329 for (chip = 0;chip < intf->num;chip++)
330 {
331 if (SN76496_init(msound,chip,intf->baseclock[chip],intf->volume[chip] & 0xff,Machine->sample_rate) != 0)
332 return 1;
333
334 SN76496_set_gain(chip,(intf->volume[chip] >> 8) & 0xff);
335 }
336 return 0;
337 }
338