]> Shamusworld >> Repos - apple2/blob - src/ay8910.cpp
4f8da2feef95c092a3119ef402b5ac22aba0d2ae
[apple2] / src / ay8910.cpp
1 /***************************************************************************
2
3   ay8910.cpp
4
5   Emulation of the AY-3-8910 / YM2149 sound chip.
6
7   Based on various code snippets by Ville Hallik, Michael Cuddy,
8   Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
9
10 ***************************************************************************/
11
12 // 
13 // From mame.txt (http://www.mame.net/readme.html)
14 // 
15 // VI. Reuse of Source Code
16 // --------------------------
17 //    This chapter might not apply to specific portions of MAME (e.g. CPU
18 //    emulators) which bear different copyright notices.
19 //    The source code cannot be used in a commercial product without the written
20 //    authorization of the authors. Use in non-commercial products is allowed, and
21 //    indeed encouraged.  If you use portions of the MAME source code in your
22 //    program, however, you must make the full source code freely available as
23 //    well.
24 //    Usage of the _information_ contained in the source code is free for any use.
25 //    However, given the amount of time and energy it took to collect this
26 //    information, if you find new information we would appreciate if you made it
27 //    freely available as well.
28 // 
29
30 // JLH: Commented out MAME specific crap
31
32 #include <string.h>                                                             // for memset()
33 #include "ay8910.h"
34
35 #define MAX_OUTPUT 0x7FFF
36
37 // See AY8910_set_clock() for definition of STEP
38 #define STEP 0x8000
39
40 struct AY8910
41 {
42         int Channel;
43         int SampleRate;
44 //      mem_read_handler PortAread;
45 //      mem_read_handler PortBread;
46 //      mem_write_handler PortAwrite;
47 //      mem_write_handler PortBwrite;
48         int register_latch;
49         unsigned char Regs[16];
50         int lastEnable;
51         unsigned int UpdateStep;
52         int PeriodA,PeriodB,PeriodC,PeriodN,PeriodE;
53         int CountA,CountB,CountC,CountN,CountE;
54         unsigned int VolA,VolB,VolC,VolE;
55         unsigned char EnvelopeA,EnvelopeB,EnvelopeC;
56         unsigned char OutputA,OutputB,OutputC,OutputN;
57         signed char CountEnv;
58         unsigned char Hold,Alternate,Attack,Holding;
59         int RNG;
60         unsigned int VolTable[32];
61 };
62
63 /* register id's */
64 #define AY_AFINE        (0)
65 #define AY_ACOARSE      (1)
66 #define AY_BFINE        (2)
67 #define AY_BCOARSE      (3)
68 #define AY_CFINE        (4)
69 #define AY_CCOARSE      (5)
70 #define AY_NOISEPER     (6)
71 #define AY_ENABLE       (7)
72 #define AY_AVOL         (8)
73 #define AY_BVOL         (9)
74 #define AY_CVOL         (10)
75 #define AY_EFINE        (11)
76 #define AY_ECOARSE      (12)
77 #define AY_ESHAPE       (13)
78
79 #define AY_PORTA        (14)
80 #define AY_PORTB        (15)
81
82
83 static struct AY8910 AYPSG[MAX_8910];           /* array of PSG's */
84
85
86 void _AYWriteReg(int n, int r, int v)
87 {
88         struct AY8910 *PSG = &AYPSG[n];
89         int old;
90
91         PSG->Regs[r] = v;
92
93         /* A note about the period of tones, noise and envelope: for speed reasons, *
94          * we count down from the period to 0, but careful studies of the chip      * 
95          * output prove that it instead counts up from 0 until the counter becomes  * 
96          * greater or equal to the period. This is an important difference when the * 
97          * program is rapidly changing the period to modulate the sound.            * 
98          * To compensate for the difference, when the period is changed we adjust   * 
99          * our internal counter.                                                    * 
100          * Also, note that period = 0 is the same as period = 1. This is mentioned  * 
101          * in the YM2203 data sheets. However, this does NOT apply to the Envelope  * 
102          * period. In that case, period = 0 is half as period = 1.                  */
103         switch (r)
104         {
105         case AY_AFINE:
106         case AY_ACOARSE:
107                 PSG->Regs[AY_ACOARSE] &= 0x0F;
108                 old = PSG->PeriodA;
109                 PSG->PeriodA = (PSG->Regs[AY_AFINE] + 256 * PSG->Regs[AY_ACOARSE]) * PSG->UpdateStep;
110
111                 if (PSG->PeriodA == 0) PSG->PeriodA = PSG->UpdateStep;
112
113                 PSG->CountA += PSG->PeriodA - old;
114
115                 if (PSG->CountA <= 0) PSG->CountA = 1;
116                 break;
117         case AY_BFINE:
118         case AY_BCOARSE:
119                 PSG->Regs[AY_BCOARSE] &= 0x0F;
120                 old = PSG->PeriodB;
121                 PSG->PeriodB = (PSG->Regs[AY_BFINE] + 256 * PSG->Regs[AY_BCOARSE]) * PSG->UpdateStep;
122
123                 if (PSG->PeriodB == 0) PSG->PeriodB = PSG->UpdateStep;
124
125                 PSG->CountB += PSG->PeriodB - old;
126
127                 if (PSG->CountB <= 0) PSG->CountB = 1;
128                 break;
129         case AY_CFINE:
130         case AY_CCOARSE:
131                 PSG->Regs[AY_CCOARSE] &= 0x0F;
132                 old = PSG->PeriodC;
133                 PSG->PeriodC = (PSG->Regs[AY_CFINE] + 256 * PSG->Regs[AY_CCOARSE]) * PSG->UpdateStep;
134
135                 if (PSG->PeriodC == 0) PSG->PeriodC = PSG->UpdateStep;
136
137                 PSG->CountC += PSG->PeriodC - old;
138
139                 if (PSG->CountC <= 0) PSG->CountC = 1;
140                 break;
141         case AY_NOISEPER:
142                 PSG->Regs[AY_NOISEPER] &= 0x1F;
143                 old = PSG->PeriodN;
144                 PSG->PeriodN = PSG->Regs[AY_NOISEPER] * PSG->UpdateStep;
145
146                 if (PSG->PeriodN == 0) PSG->PeriodN = PSG->UpdateStep;
147
148                 PSG->CountN += PSG->PeriodN - old;
149
150                 if (PSG->CountN <= 0) PSG->CountN = 1;
151                 break;
152         case AY_ENABLE:
153                 if ((PSG->lastEnable == -1) ||
154                     ((PSG->lastEnable & 0x40) != (PSG->Regs[AY_ENABLE] & 0x40)))
155                 {
156                         /* write out 0xff if port set to input */
157 //                      if (PSG->PortAwrite)
158 //                              (*PSG->PortAwrite)(0, (UINT8) ((PSG->Regs[AY_ENABLE] & 0x40) ? PSG->Regs[AY_PORTA] : 0xff));    // [TC: UINT8 cast]
159                 }
160
161                 if ((PSG->lastEnable == -1) ||
162                     ((PSG->lastEnable & 0x80) != (PSG->Regs[AY_ENABLE] & 0x80)))
163                 {
164                         /* write out 0xff if port set to input */
165 //                      if (PSG->PortBwrite)
166 //                              (*PSG->PortBwrite)(0, (UINT8) ((PSG->Regs[AY_ENABLE] & 0x80) ? PSG->Regs[AY_PORTB] : 0xff));    // [TC: UINT8 cast]
167                 }
168
169                 PSG->lastEnable = PSG->Regs[AY_ENABLE];
170                 break;
171         case AY_AVOL:
172                 PSG->Regs[AY_AVOL] &= 0x1F;
173                 PSG->EnvelopeA = PSG->Regs[AY_AVOL] & 0x10;
174                 PSG->VolA = PSG->EnvelopeA ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_AVOL] ? PSG->Regs[AY_AVOL]*2+1 : 0];
175                 break;
176         case AY_BVOL:
177                 PSG->Regs[AY_BVOL] &= 0x1F;
178                 PSG->EnvelopeB = PSG->Regs[AY_BVOL] & 0x10;
179                 PSG->VolB = PSG->EnvelopeB ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_BVOL] ? PSG->Regs[AY_BVOL]*2+1 : 0];
180                 break;
181         case AY_CVOL:
182                 PSG->Regs[AY_CVOL] &= 0x1F;
183                 PSG->EnvelopeC = PSG->Regs[AY_CVOL] & 0x10;
184                 PSG->VolC = PSG->EnvelopeC ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_CVOL] ? PSG->Regs[AY_CVOL]*2+1 : 0];
185                 break;
186         case AY_EFINE:
187         case AY_ECOARSE:
188                 old = PSG->PeriodE;
189                 PSG->PeriodE = ((PSG->Regs[AY_EFINE] + 256 * PSG->Regs[AY_ECOARSE])) * PSG->UpdateStep;
190
191                 if (PSG->PeriodE == 0) PSG->PeriodE = PSG->UpdateStep / 2;
192
193                 PSG->CountE += PSG->PeriodE - old;
194
195                 if (PSG->CountE <= 0) PSG->CountE = 1;
196                 break;
197         case AY_ESHAPE:
198                 /* envelope shapes:
199                 C AtAlH
200                 0 0 x x  \___
201
202                 0 1 x x  /___
203
204                 1 0 0 0  \\\\
205
206                 1 0 0 1  \___
207
208                 1 0 1 0  \/\/
209                           ___
210                 1 0 1 1  \
211
212                 1 1 0 0  ////
213                           ___
214                 1 1 0 1  /
215
216                 1 1 1 0  /\/\
217
218                 1 1 1 1  /___
219
220                 The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it
221                 has twice the steps, happening twice as fast. Since the end result is
222                 just a smoother curve, we always use the YM2149 behaviour.
223                 */
224                 PSG->Regs[AY_ESHAPE] &= 0x0F;
225                 PSG->Attack = (PSG->Regs[AY_ESHAPE] & 0x04) ? 0x1F : 0x00;
226
227                 if ((PSG->Regs[AY_ESHAPE] & 0x08) == 0)
228                 {
229                         /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */
230                         PSG->Hold = 1;
231                         PSG->Alternate = PSG->Attack;
232                 }
233                 else
234                 {
235                         PSG->Hold = PSG->Regs[AY_ESHAPE] & 0x01;
236                         PSG->Alternate = PSG->Regs[AY_ESHAPE] & 0x02;
237                 }
238
239                 PSG->CountE = PSG->PeriodE;
240                 PSG->CountEnv = 0x1F;
241                 PSG->Holding = 0;
242                 PSG->VolE = PSG->VolTable[PSG->CountEnv ^ PSG->Attack];
243
244                 if (PSG->EnvelopeA) PSG->VolA = PSG->VolE;
245                 if (PSG->EnvelopeB) PSG->VolB = PSG->VolE;
246                 if (PSG->EnvelopeC) PSG->VolC = PSG->VolE;
247                 break;
248         case AY_PORTA:
249                 if (PSG->Regs[AY_ENABLE] & 0x40)
250                 {
251 //                      if (PSG->PortAwrite)
252 //                              (*PSG->PortAwrite)(0, PSG->Regs[AY_PORTA]);
253 //                      else
254 //                              logerror("PC %04x: warning - write %02x to 8910 #%d Port A\n",activecpu_get_pc(),PSG->Regs[AY_PORTA],n);
255                 }
256                 else
257                 {
258 //                      logerror("warning: write to 8910 #%d Port A set as input - ignored\n",n);
259                 }
260                 break;
261         case AY_PORTB:
262                 if (PSG->Regs[AY_ENABLE] & 0x80)
263                 {
264 //                      if (PSG->PortBwrite)
265 //                              (*PSG->PortBwrite)(0, PSG->Regs[AY_PORTB]);
266 //                      else
267 //                              logerror("PC %04x: warning - write %02x to 8910 #%d Port B\n",activecpu_get_pc(),PSG->Regs[AY_PORTB],n);
268                 }
269                 else
270                 {
271 //                      logerror("warning: write to 8910 #%d Port B set as input - ignored\n",n);
272                 }
273                 break;
274         }
275 }
276
277
278 // /length/ is the number of samples we require
279 // NB. This should be called at twice the 6522 IRQ rate or (eg) 60Hz if no IRQ.
280 void AY8910Update(int chip, int16 ** buffer, int length)        // [TC: Removed static]
281 {
282         struct AY8910 * PSG = &AYPSG[chip];
283         INT16 * buf1, * buf2, * buf3;
284         int outn;
285
286         buf1 = buffer[0];
287         buf2 = buffer[1];
288         buf3 = buffer[2];
289
290         /* The 8910 has three outputs, each output is the mix of one of the three    *
291          * tone generators and of the (single) noise generator. The two are mixed    *
292          * BEFORE going into the DAC. The formula to mix each channel is:            *
293          * (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable).                        *
294          * Note that this means that if both tone and noise are disabled, the output *
295          * is 1, not 0, and can be modulated changing the volume.                    *
296          *                                                                           *
297          * If the channels are disabled, set their output to 1, and increase the     *
298          * counter, if necessary, so they will not be inverted during this update.   *
299          * Setting the output to 1 is necessary because a disabled channel is locked *
300          * into the ON state (see above); and it has no effect if the volume is 0.   *
301          * If the volume is 0, increase the counter, but don't touch the output.     */
302         if (PSG->Regs[AY_ENABLE] & 0x01)
303         {
304                 if (PSG->CountA <= length * STEP) PSG->CountA += length * STEP;
305                 PSG->OutputA = 1;
306         }
307         else if (PSG->Regs[AY_AVOL] == 0)
308         {
309                 /* note that I do count += length, NOT count = length + 1. You might think *
310                  * it's the same since the volume is 0, but doing the latter could cause   *
311                  * interferencies when the program is rapidly modulating the volume.       */
312                 if (PSG->CountA <= length * STEP) PSG->CountA += length * STEP;
313         }
314
315         if (PSG->Regs[AY_ENABLE] & 0x02)
316         {
317                 if (PSG->CountB <= length * STEP) PSG->CountB += length * STEP;
318                 PSG->OutputB = 1;
319         }
320         else if (PSG->Regs[AY_BVOL] == 0)
321         {
322                 if (PSG->CountB <= length * STEP) PSG->CountB += length * STEP;
323         }
324
325         if (PSG->Regs[AY_ENABLE] & 0x04)
326         {
327                 if (PSG->CountC <= length * STEP) PSG->CountC += length * STEP;
328                 PSG->OutputC = 1;
329         }
330         else if (PSG->Regs[AY_CVOL] == 0)
331         {
332                 if (PSG->CountC <= length * STEP) PSG->CountC += length * STEP;
333         }
334
335         /* for the noise channel we must not touch OutputN - it's also not necessary *
336          * since we use outn.                                                        */
337         if ((PSG->Regs[AY_ENABLE] & 0x38) == 0x38)      /* all off */
338                 if (PSG->CountN <= length * STEP) PSG->CountN += length * STEP;
339
340         outn = (PSG->OutputN | PSG->Regs[AY_ENABLE]);
341
342         /* buffering loop */
343         while (length)
344         {
345                 int vola, volb, volc;
346                 int left;
347
348                 /* vola, volb and volc keep track of how long each square wave stays *
349                  * in the 1 position during the sample period.                       */
350                 vola = volb = volc = 0;
351
352                 left = STEP;
353                 do
354                 {
355                         int nextevent;
356
357                         if (PSG->CountN < left) nextevent = PSG->CountN;
358                         else nextevent = left;
359
360                         if (outn & 0x08)
361                         {
362                                 if (PSG->OutputA) vola += PSG->CountA;
363
364                                 PSG->CountA -= nextevent;
365                                 /* PeriodA is the half period of the square wave. Here, in each    *
366                                  * loop I add PeriodA twice, so that at the end of the loop the    *
367                                  * square wave is in the same status (0 or 1) it was at the start. *
368                                  * vola is also incremented by PeriodA, since the wave has been 1  *
369                                  * exactly half of the time, regardless of the initial position.   *
370                                  * If we exit the loop in the middle, OutputA has to be inverted   *
371                                  * and vola incremented only if the exit status of the square      *
372                                  * wave is 1.                                                      */
373                                 while (PSG->CountA <= 0)
374                                 {
375                                         PSG->CountA += PSG->PeriodA;
376
377                                         if (PSG->CountA > 0)
378                                         {
379                                                 PSG->OutputA ^= 1;
380
381                                                 if (PSG->OutputA) vola += PSG->PeriodA;
382                                                 break;
383                                         }
384
385                                         PSG->CountA += PSG->PeriodA;
386                                         vola += PSG->PeriodA;
387                                 }
388
389                                 if (PSG->OutputA) vola -= PSG->CountA;
390                         }
391                         else
392                         {
393                                 PSG->CountA -= nextevent;
394                                 while (PSG->CountA <= 0)
395                                 {
396                                         PSG->CountA += PSG->PeriodA;
397
398                                         if (PSG->CountA > 0)
399                                         {
400                                                 PSG->OutputA ^= 1;
401                                                 break;
402                                         }
403
404                                         PSG->CountA += PSG->PeriodA;
405                                 }
406                         }
407
408                         if (outn & 0x10)
409                         {
410                                 if (PSG->OutputB) volb += PSG->CountB;
411
412                                 PSG->CountB -= nextevent;
413
414                                 while (PSG->CountB <= 0)
415                                 {
416                                         PSG->CountB += PSG->PeriodB;
417
418                                         if (PSG->CountB > 0)
419                                         {
420                                                 PSG->OutputB ^= 1;
421
422                                                 if (PSG->OutputB) volb += PSG->PeriodB;
423                                                 break;
424                                         }
425
426                                         PSG->CountB += PSG->PeriodB;
427                                         volb += PSG->PeriodB;
428                                 }
429
430                                 if (PSG->OutputB) volb -= PSG->CountB;
431                         }
432                         else
433                         {
434                                 PSG->CountB -= nextevent;
435
436                                 while (PSG->CountB <= 0)
437                                 {
438                                         PSG->CountB += PSG->PeriodB;
439
440                                         if (PSG->CountB > 0)
441                                         {
442                                                 PSG->OutputB ^= 1;
443                                                 break;
444                                         }
445
446                                         PSG->CountB += PSG->PeriodB;
447                                 }
448                         }
449
450                         if (outn & 0x20)
451                         {
452                                 if (PSG->OutputC) volc += PSG->CountC;
453
454                                 PSG->CountC -= nextevent;
455
456                                 while (PSG->CountC <= 0)
457                                 {
458                                         PSG->CountC += PSG->PeriodC;
459
460                                         if (PSG->CountC > 0)
461                                         {
462                                                 PSG->OutputC ^= 1;
463
464                                                 if (PSG->OutputC) volc += PSG->PeriodC;
465                                                 break;
466                                         }
467
468                                         PSG->CountC += PSG->PeriodC;
469                                         volc += PSG->PeriodC;
470                                 }
471
472                                 if (PSG->OutputC) volc -= PSG->CountC;
473                         }
474                         else
475                         {
476                                 PSG->CountC -= nextevent;
477
478                                 while (PSG->CountC <= 0)
479                                 {
480                                         PSG->CountC += PSG->PeriodC;
481
482                                         if (PSG->CountC > 0)
483                                         {
484                                                 PSG->OutputC ^= 1;
485                                                 break;
486                                         }
487
488                                         PSG->CountC += PSG->PeriodC;
489                                 }
490                         }
491
492                         PSG->CountN -= nextevent;
493
494                         if (PSG->CountN <= 0)
495                         {
496                                 /* Is noise output going to change? */
497                                 if ((PSG->RNG + 1) & 0x00002)   /* (bit0^bit1)? */
498                                 {
499                                         PSG->OutputN = ~PSG->OutputN;
500                                         outn = (PSG->OutputN | PSG->Regs[AY_ENABLE]);
501                                 }
502
503                                 /* The Random Number Generator of the 8910 is a 17-bit shift  *
504                                  * register. The input to the shift register is bit0 XOR bit3 *
505                                  * (bit0 is the output). This was verified on AY-3-8910 and   *
506                                  * YM2149 chips.                                              *
507                                  *                                                            *
508                                  * The following is a fast way to compute bit17 = bit0^bit3.  *
509                                  * Instead of doing all the logic operations, we only check   *
510                                  * bit0, relying on the fact that after three shifts of the   *
511                                  * register, what now is bit3 will become bit0, and will      *
512                                  * invert, if necessary, bit14, which previously was bit17.   */
513                                 if (PSG->RNG & 0x00001)
514                                         PSG->RNG ^= 0x24000; /* This version is called the "Galois configuration". */
515
516                                 PSG->RNG >>= 1;
517                                 PSG->CountN += PSG->PeriodN;
518                         }
519
520                         left -= nextevent;
521                 }
522                 while (left > 0);
523
524                 /* update envelope */
525                 if (PSG->Holding == 0)
526                 {
527                         PSG->CountE -= STEP;
528
529                         if (PSG->CountE <= 0)
530                         {
531                                 do
532                                 {
533                                         PSG->CountEnv--;
534                                         PSG->CountE += PSG->PeriodE;
535                                 }
536                                 while (PSG->CountE <= 0);
537
538                                 /* check envelope current position */
539                                 if (PSG->CountEnv < 0)
540                                 {
541                                         if (PSG->Hold)
542                                         {
543                                                 if (PSG->Alternate)
544                                                         PSG->Attack ^= 0x1F;
545
546                                                 PSG->Holding = 1;
547                                                 PSG->CountEnv = 0;
548                                         }
549                                         else
550                                         {
551                                                 /* if CountEnv has looped an odd number of times (usually 1), *
552                                                  * invert the output.                                         */
553                                                 if (PSG->Alternate && (PSG->CountEnv & 0x20))
554                                                         PSG->Attack ^= 0x1F;
555
556                                                 PSG->CountEnv &= 0x1F;
557                                         }
558                                 }
559
560                                 PSG->VolE = PSG->VolTable[PSG->CountEnv ^ PSG->Attack];
561                                 /* reload volume */
562                                 if (PSG->EnvelopeA) PSG->VolA = PSG->VolE;
563                                 if (PSG->EnvelopeB) PSG->VolB = PSG->VolE;
564                                 if (PSG->EnvelopeC) PSG->VolC = PSG->VolE;
565                         }
566                 }
567
568 #if 0
569                 *(buf1++) = (vola * PSG->VolA) / STEP;
570                 *(buf2++) = (volb * PSG->VolB) / STEP;
571                 *(buf3++) = (volc * PSG->VolC) / STEP;
572 #else   // [Tom's code...]
573                 // Output PCM wave [-32768...32767] instead of MAME's voltage level [0...32767]
574                 // - This allows for better s/w mixing
575
576                 if (PSG->VolA)
577                 {
578                         if (vola)
579                                 *(buf1++) = (vola * PSG->VolA) / STEP;
580                         else
581                                 *(buf1++) = -(int)PSG->VolA;
582                 }
583                 else
584                         *(buf1++) = 0;
585
586                 if (PSG->VolB)
587                 {
588                         if (volb)
589                                 *(buf2++) = (volb * PSG->VolB) / STEP;
590                         else
591                                 *(buf2++) = -(int)PSG->VolB;
592                 }
593                 else
594                         *(buf2++) = 0;
595
596                 if (PSG->VolC)
597                 {
598                         if (volc)
599                                 *(buf3++) = (volc * PSG->VolC) / STEP;
600                         else
601                                 *(buf3++) = -(int)PSG->VolC;
602                 }
603                 else
604                         *(buf3++) = 0;
605 #endif
606                 length--;
607         }
608 }
609
610
611 static void AY8910_set_clock(int chip, int clock)
612 {
613         struct AY8910 * PSG = &AYPSG[chip];
614
615         /* The step clock for the tone and noise generators is the chip clock    *
616          * divided by 8; for the envelope generator of the AY-3-8910, it is half *
617          * that much (clock/16), but the envelope of the YM2149 goes twice as    *
618          * fast, therefore again clock/8.                                        *
619          * Here we calculate the number of steps which happen during one sample  *
620          * at the given sample rate. No. of events = sample rate / (clock/8).    *
621          * STEP is a multiplier used to turn the fraction into a fixed point     *
622          * number.                                                               */
623         PSG->UpdateStep = (unsigned int)(((double)STEP * PSG->SampleRate * 8 + clock / 2) / clock);     // [TC: unsigned int cast]
624 }
625
626
627 static void build_mixer_table(int chip)
628 {
629         struct AY8910 * PSG = &AYPSG[chip];
630
631         /* calculate the volume->voltage conversion table                     */
632         /* The AY-3-8910 has 16 levels, in a logarithmic scale (3dB per step) */
633         /* The YM2149 still has 16 levels for the tone generators, but 32 for */
634         /* the envelope generator (1.5dB per step).                           */
635         double out = MAX_OUTPUT;
636
637         for(int i=31; i>0; i--)
638         {
639                 PSG->VolTable[i] = (unsigned int)(out + 0.5);   /* round to nearest */  // [TC: unsigned int cast]
640                 out /= 1.188502227;     /* = 10 ^ (1.5/20) = 1.5dB */
641         }
642
643         PSG->VolTable[0] = 0;
644 }
645
646
647 void AY8910_reset(int chip)
648 {
649         int i;
650         struct AY8910 * PSG = &AYPSG[chip];
651
652         PSG->register_latch = 0;
653         PSG->RNG = 1;
654         PSG->OutputA = 0;
655         PSG->OutputB = 0;
656         PSG->OutputC = 0;
657         PSG->OutputN = 0xFF;
658         PSG->lastEnable = -1;   /* force a write */
659
660         for(i=0; i<AY_PORTA; i++)
661                 _AYWriteReg(chip, i, 0);        /* AYWriteReg() uses the timer system; we cannot */
662                                                                         /* call it at this time because the timer system */
663                                                                         /* has not been initialized.                     */
664 }
665
666 // This stuff looks like Tom's code, so let's streamline and un-MSHungarianize this shit:
667 // [DONE]
668
669 void AY8910_InitAll(int clock, int sampleRate)
670 {
671         for(int chip=0; chip<MAX_8910; chip++)
672         {
673                 struct AY8910 * PSG = &AYPSG[chip];
674
675                 memset(PSG, 0, sizeof(struct AY8910));
676                 PSG->SampleRate = sampleRate;
677                 AY8910_set_clock(chip, clock);
678                 build_mixer_table(chip);
679         }
680 }
681
682 void AY8910_InitClock(int clock)
683 {
684         for(int chip=0; chip<MAX_8910; chip++)
685                 AY8910_set_clock(chip, clock);
686 }
687
688 uint8 * AY8910_GetRegsPtr(uint16 chipNum)
689 {
690         if (chipNum >= MAX_8910)
691                 return NULL;
692
693         return &AYPSG[chipNum].Regs[0];
694 }