]> Shamusworld >> Repos - apple2/blob - src/ay8910.cpp
3e5df25803c84c6eca455797bcf06cb425cc5dd0
[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 "ay8910.h"
33 #include <string.h>                                                             // for memset()
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)
112                         PSG->PeriodA = PSG->UpdateStep;
113
114                 PSG->CountA += PSG->PeriodA - old;
115
116                 if (PSG->CountA <= 0)
117                         PSG->CountA = 1;
118                 break;
119         case AY_BFINE:
120         case AY_BCOARSE:
121                 PSG->Regs[AY_BCOARSE] &= 0x0F;
122                 old = PSG->PeriodB;
123                 PSG->PeriodB = (PSG->Regs[AY_BFINE] + 256 * PSG->Regs[AY_BCOARSE]) * PSG->UpdateStep;
124
125                 if (PSG->PeriodB == 0)
126                         PSG->PeriodB = PSG->UpdateStep;
127
128                 PSG->CountB += PSG->PeriodB - old;
129
130                 if (PSG->CountB <= 0)
131                         PSG->CountB = 1;
132                 break;
133         case AY_CFINE:
134         case AY_CCOARSE:
135                 PSG->Regs[AY_CCOARSE] &= 0x0F;
136                 old = PSG->PeriodC;
137                 PSG->PeriodC = (PSG->Regs[AY_CFINE] + 256 * PSG->Regs[AY_CCOARSE]) * PSG->UpdateStep;
138
139                 if (PSG->PeriodC == 0)
140                         PSG->PeriodC = PSG->UpdateStep;
141
142                 PSG->CountC += PSG->PeriodC - old;
143
144                 if (PSG->CountC <= 0)
145                         PSG->CountC = 1;
146                 break;
147         case AY_NOISEPER:
148                 PSG->Regs[AY_NOISEPER] &= 0x1F;
149                 old = PSG->PeriodN;
150                 PSG->PeriodN = PSG->Regs[AY_NOISEPER] * PSG->UpdateStep;
151
152                 if (PSG->PeriodN == 0)
153                         PSG->PeriodN = PSG->UpdateStep;
154
155                 PSG->CountN += PSG->PeriodN - old;
156
157                 if (PSG->CountN <= 0)
158                         PSG->CountN = 1;
159                 break;
160         case AY_ENABLE:
161                 if ((PSG->lastEnable == -1) ||
162                     ((PSG->lastEnable & 0x40) != (PSG->Regs[AY_ENABLE] & 0x40)))
163                 {
164                         /* write out 0xff if port set to input */
165 //                      if (PSG->PortAwrite)
166 //                              (*PSG->PortAwrite)(0, (UINT8) ((PSG->Regs[AY_ENABLE] & 0x40) ? PSG->Regs[AY_PORTA] : 0xff));    // [TC: UINT8 cast]
167                 }
168
169                 if ((PSG->lastEnable == -1) ||
170                     ((PSG->lastEnable & 0x80) != (PSG->Regs[AY_ENABLE] & 0x80)))
171                 {
172                         /* write out 0xff if port set to input */
173 //                      if (PSG->PortBwrite)
174 //                              (*PSG->PortBwrite)(0, (UINT8) ((PSG->Regs[AY_ENABLE] & 0x80) ? PSG->Regs[AY_PORTB] : 0xff));    // [TC: UINT8 cast]
175                 }
176
177                 PSG->lastEnable = PSG->Regs[AY_ENABLE];
178                 break;
179         case AY_AVOL:
180                 PSG->Regs[AY_AVOL] &= 0x1F;
181                 PSG->EnvelopeA = PSG->Regs[AY_AVOL] & 0x10;
182                 PSG->VolA = PSG->EnvelopeA ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_AVOL] ? PSG->Regs[AY_AVOL]*2+1 : 0];
183                 break;
184         case AY_BVOL:
185                 PSG->Regs[AY_BVOL] &= 0x1F;
186                 PSG->EnvelopeB = PSG->Regs[AY_BVOL] & 0x10;
187                 PSG->VolB = PSG->EnvelopeB ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_BVOL] ? PSG->Regs[AY_BVOL]*2+1 : 0];
188                 break;
189         case AY_CVOL:
190                 PSG->Regs[AY_CVOL] &= 0x1F;
191                 PSG->EnvelopeC = PSG->Regs[AY_CVOL] & 0x10;
192                 PSG->VolC = PSG->EnvelopeC ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_CVOL] ? PSG->Regs[AY_CVOL]*2+1 : 0];
193                 break;
194         case AY_EFINE:
195         case AY_ECOARSE:
196                 old = PSG->PeriodE;
197                 PSG->PeriodE = ((PSG->Regs[AY_EFINE] + 256 * PSG->Regs[AY_ECOARSE])) * PSG->UpdateStep;
198
199                 if (PSG->PeriodE == 0)
200                         PSG->PeriodE = PSG->UpdateStep / 2;
201
202                 PSG->CountE += PSG->PeriodE - old;
203
204                 if (PSG->CountE <= 0)
205                         PSG->CountE = 1;
206                 break;
207         case AY_ESHAPE:
208                 /* envelope shapes:
209                 C AtAlH
210                 0 0 x x  \___
211
212                 0 1 x x  /___
213
214                 1 0 0 0  \\\\
215
216                 1 0 0 1  \___
217
218                 1 0 1 0  \/\/
219                           ___
220                 1 0 1 1  \
221
222                 1 1 0 0  ////
223                           ___
224                 1 1 0 1  /
225
226                 1 1 1 0  /\/\
227
228                 1 1 1 1  /___
229
230                 The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it
231                 has twice the steps, happening twice as fast. Since the end result is
232                 just a smoother curve, we always use the YM2149 behaviour.
233                 */
234                 PSG->Regs[AY_ESHAPE] &= 0x0F;
235                 PSG->Attack = (PSG->Regs[AY_ESHAPE] & 0x04) ? 0x1F : 0x00;
236
237                 if ((PSG->Regs[AY_ESHAPE] & 0x08) == 0)
238                 {
239                         /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */
240                         PSG->Hold = 1;
241                         PSG->Alternate = PSG->Attack;
242                 }
243                 else
244                 {
245                         PSG->Hold = PSG->Regs[AY_ESHAPE] & 0x01;
246                         PSG->Alternate = PSG->Regs[AY_ESHAPE] & 0x02;
247                 }
248
249                 PSG->CountE = PSG->PeriodE;
250                 PSG->CountEnv = 0x1F;
251                 PSG->Holding = 0;
252                 PSG->VolE = PSG->VolTable[PSG->CountEnv ^ PSG->Attack];
253
254                 if (PSG->EnvelopeA)
255                         PSG->VolA = PSG->VolE;
256
257                 if (PSG->EnvelopeB)
258                         PSG->VolB = PSG->VolE;
259
260                 if (PSG->EnvelopeC)
261                         PSG->VolC = PSG->VolE;
262                 break;
263         case AY_PORTA:
264                 if (PSG->Regs[AY_ENABLE] & 0x40)
265                 {
266 //                      if (PSG->PortAwrite)
267 //                              (*PSG->PortAwrite)(0, PSG->Regs[AY_PORTA]);
268 //                      else
269 //                              logerror("PC %04x: warning - write %02x to 8910 #%d Port A\n",activecpu_get_pc(),PSG->Regs[AY_PORTA],n);
270                 }
271                 else
272                 {
273 //                      logerror("warning: write to 8910 #%d Port A set as input - ignored\n",n);
274                 }
275                 break;
276         case AY_PORTB:
277                 if (PSG->Regs[AY_ENABLE] & 0x80)
278                 {
279 //                      if (PSG->PortBwrite)
280 //                              (*PSG->PortBwrite)(0, PSG->Regs[AY_PORTB]);
281 //                      else
282 //                              logerror("PC %04x: warning - write %02x to 8910 #%d Port B\n",activecpu_get_pc(),PSG->Regs[AY_PORTB],n);
283                 }
284                 else
285                 {
286 //                      logerror("warning: write to 8910 #%d Port B set as input - ignored\n",n);
287                 }
288                 break;
289         }
290 }
291
292
293 // /length/ is the number of samples we require
294 // NB. This should be called at twice the 6522 IRQ rate or (eg) 60Hz if no IRQ.
295 void AY8910Update(int chip, int16_t ** buffer, int length)      // [TC: Removed static]
296 {
297         struct AY8910 * PSG = &AYPSG[chip];
298         int16_t * buf1, * buf2, * buf3;
299         int outn;
300
301         buf1 = buffer[0];
302         buf2 = buffer[1];
303         buf3 = buffer[2];
304
305         /* The 8910 has three outputs, each output is the mix of one of the three    *
306          * tone generators and of the (single) noise generator. The two are mixed    *
307          * BEFORE going into the DAC. The formula to mix each channel is:            *
308          * (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable).                        *
309          * Note that this means that if both tone and noise are disabled, the output *
310          * is 1, not 0, and can be modulated changing the volume.                    *
311          *                                                                           *
312          * If the channels are disabled, set their output to 1, and increase the     *
313          * counter, if necessary, so they will not be inverted during this update.   *
314          * Setting the output to 1 is necessary because a disabled channel is locked *
315          * into the ON state (see above); and it has no effect if the volume is 0.   *
316          * If the volume is 0, increase the counter, but don't touch the output.     */
317         if (PSG->Regs[AY_ENABLE] & 0x01)
318         {
319                 if (PSG->CountA <= length * STEP)
320                         PSG->CountA += length * STEP;
321
322                 PSG->OutputA = 1;
323         }
324         else if (PSG->Regs[AY_AVOL] == 0)
325         {
326                 /* note that I do count += length, NOT count = length + 1. You might think *
327                  * it's the same since the volume is 0, but doing the latter could cause   *
328                  * interferencies when the program is rapidly modulating the volume.       */
329                 if (PSG->CountA <= length * STEP)
330                         PSG->CountA += length * STEP;
331         }
332
333         if (PSG->Regs[AY_ENABLE] & 0x02)
334         {
335                 if (PSG->CountB <= length * STEP)
336                         PSG->CountB += length * STEP;
337
338                 PSG->OutputB = 1;
339         }
340         else if (PSG->Regs[AY_BVOL] == 0)
341         {
342                 if (PSG->CountB <= length * STEP)
343                         PSG->CountB += length * STEP;
344         }
345
346         if (PSG->Regs[AY_ENABLE] & 0x04)
347         {
348                 if (PSG->CountC <= length * STEP)
349                         PSG->CountC += length * STEP;
350
351                 PSG->OutputC = 1;
352         }
353         else if (PSG->Regs[AY_CVOL] == 0)
354         {
355                 if (PSG->CountC <= length * STEP)
356                         PSG->CountC += length * STEP;
357         }
358
359         /* for the noise channel we must not touch OutputN - it's also not necessary *
360          * since we use outn.                                                        */
361         if ((PSG->Regs[AY_ENABLE] & 0x38) == 0x38)      /* all off */
362                 if (PSG->CountN <= length * STEP)
363                         PSG->CountN += length * STEP;
364
365         outn = (PSG->OutputN | PSG->Regs[AY_ENABLE]);
366
367         /* buffering loop */
368         while (length)
369         {
370                 int vola, volb, volc;
371                 int left;
372
373                 /* vola, volb and volc keep track of how long each square wave stays *
374                  * in the 1 position during the sample period.                       */
375                 vola = volb = volc = 0;
376
377                 left = STEP;
378                 do
379                 {
380                         int nextevent;
381
382                         if (PSG->CountN < left)
383                                 nextevent = PSG->CountN;
384                         else
385                                 nextevent = left;
386
387                         if (outn & 0x08)
388                         {
389                                 if (PSG->OutputA)
390                                         vola += PSG->CountA;
391
392                                 PSG->CountA -= nextevent;
393                                 /* PeriodA is the half period of the square wave. Here, in each    *
394                                  * loop I add PeriodA twice, so that at the end of the loop the    *
395                                  * square wave is in the same status (0 or 1) it was at the start. *
396                                  * vola is also incremented by PeriodA, since the wave has been 1  *
397                                  * exactly half of the time, regardless of the initial position.   *
398                                  * If we exit the loop in the middle, OutputA has to be inverted   *
399                                  * and vola incremented only if the exit status of the square      *
400                                  * wave is 1.                                                      */
401                                 while (PSG->CountA <= 0)
402                                 {
403                                         PSG->CountA += PSG->PeriodA;
404
405                                         if (PSG->CountA > 0)
406                                         {
407                                                 PSG->OutputA ^= 1;
408
409                                                 if (PSG->OutputA)
410                                                         vola += PSG->PeriodA;
411                                                 break;
412                                         }
413
414                                         PSG->CountA += PSG->PeriodA;
415                                         vola += PSG->PeriodA;
416                                 }
417
418                                 if (PSG->OutputA)
419                                         vola -= PSG->CountA;
420                         }
421                         else
422                         {
423                                 PSG->CountA -= nextevent;
424                                 while (PSG->CountA <= 0)
425                                 {
426                                         PSG->CountA += PSG->PeriodA;
427
428                                         if (PSG->CountA > 0)
429                                         {
430                                                 PSG->OutputA ^= 1;
431                                                 break;
432                                         }
433
434                                         PSG->CountA += PSG->PeriodA;
435                                 }
436                         }
437
438                         if (outn & 0x10)
439                         {
440                                 if (PSG->OutputB)
441                                         volb += PSG->CountB;
442
443                                 PSG->CountB -= nextevent;
444
445                                 while (PSG->CountB <= 0)
446                                 {
447                                         PSG->CountB += PSG->PeriodB;
448
449                                         if (PSG->CountB > 0)
450                                         {
451                                                 PSG->OutputB ^= 1;
452
453                                                 if (PSG->OutputB)
454                                                         volb += PSG->PeriodB;
455                                                 break;
456                                         }
457
458                                         PSG->CountB += PSG->PeriodB;
459                                         volb += PSG->PeriodB;
460                                 }
461
462                                 if (PSG->OutputB)
463                                         volb -= PSG->CountB;
464                         }
465                         else
466                         {
467                                 PSG->CountB -= nextevent;
468
469                                 while (PSG->CountB <= 0)
470                                 {
471                                         PSG->CountB += PSG->PeriodB;
472
473                                         if (PSG->CountB > 0)
474                                         {
475                                                 PSG->OutputB ^= 1;
476                                                 break;
477                                         }
478
479                                         PSG->CountB += PSG->PeriodB;
480                                 }
481                         }
482
483                         if (outn & 0x20)
484                         {
485                                 if (PSG->OutputC)
486                                         volc += PSG->CountC;
487
488                                 PSG->CountC -= nextevent;
489
490                                 while (PSG->CountC <= 0)
491                                 {
492                                         PSG->CountC += PSG->PeriodC;
493
494                                         if (PSG->CountC > 0)
495                                         {
496                                                 PSG->OutputC ^= 1;
497
498                                                 if (PSG->OutputC)
499                                                         volc += PSG->PeriodC;
500                                                 break;
501                                         }
502
503                                         PSG->CountC += PSG->PeriodC;
504                                         volc += PSG->PeriodC;
505                                 }
506
507                                 if (PSG->OutputC)
508                                         volc -= PSG->CountC;
509                         }
510                         else
511                         {
512                                 PSG->CountC -= nextevent;
513
514                                 while (PSG->CountC <= 0)
515                                 {
516                                         PSG->CountC += PSG->PeriodC;
517
518                                         if (PSG->CountC > 0)
519                                         {
520                                                 PSG->OutputC ^= 1;
521                                                 break;
522                                         }
523
524                                         PSG->CountC += PSG->PeriodC;
525                                 }
526                         }
527
528                         PSG->CountN -= nextevent;
529
530                         if (PSG->CountN <= 0)
531                         {
532                                 /* Is noise output going to change? */
533                                 if ((PSG->RNG + 1) & 0x00002)   /* (bit0^bit1)? */
534                                 {
535                                         PSG->OutputN = ~PSG->OutputN;
536                                         outn = (PSG->OutputN | PSG->Regs[AY_ENABLE]);
537                                 }
538
539                                 /* The Random Number Generator of the 8910 is a 17-bit shift  *
540                                  * register. The input to the shift register is bit0 XOR bit3 *
541                                  * (bit0 is the output). This was verified on AY-3-8910 and   *
542                                  * YM2149 chips.                                              *
543                                  *                                                            *
544                                  * The following is a fast way to compute bit17 = bit0^bit3.  *
545                                  * Instead of doing all the logic operations, we only check   *
546                                  * bit0, relying on the fact that after three shifts of the   *
547                                  * register, what now is bit3 will become bit0, and will      *
548                                  * invert, if necessary, bit14, which previously was bit17.   */
549                                 if (PSG->RNG & 0x00001)
550                                         PSG->RNG ^= 0x24000; /* This version is called the "Galois configuration". */
551
552                                 PSG->RNG >>= 1;
553                                 PSG->CountN += PSG->PeriodN;
554                         }
555
556                         left -= nextevent;
557                 }
558                 while (left > 0);
559
560                 /* update envelope */
561                 if (PSG->Holding == 0)
562                 {
563                         PSG->CountE -= STEP;
564
565                         if (PSG->CountE <= 0)
566                         {
567                                 do
568                                 {
569                                         PSG->CountEnv--;
570                                         PSG->CountE += PSG->PeriodE;
571                                 }
572                                 while (PSG->CountE <= 0);
573
574                                 /* check envelope current position */
575                                 if (PSG->CountEnv < 0)
576                                 {
577                                         if (PSG->Hold)
578                                         {
579                                                 if (PSG->Alternate)
580                                                         PSG->Attack ^= 0x1F;
581
582                                                 PSG->Holding = 1;
583                                                 PSG->CountEnv = 0;
584                                         }
585                                         else
586                                         {
587                                                 /* if CountEnv has looped an odd number of times (usually 1), *
588                                                  * invert the output.                                         */
589                                                 if (PSG->Alternate && (PSG->CountEnv & 0x20))
590                                                         PSG->Attack ^= 0x1F;
591
592                                                 PSG->CountEnv &= 0x1F;
593                                         }
594                                 }
595
596                                 PSG->VolE = PSG->VolTable[PSG->CountEnv ^ PSG->Attack];
597                                 /* reload volume */
598                                 if (PSG->EnvelopeA)
599                                         PSG->VolA = PSG->VolE;
600
601                                 if (PSG->EnvelopeB)
602                                         PSG->VolB = PSG->VolE;
603
604                                 if (PSG->EnvelopeC)
605                                         PSG->VolC = PSG->VolE;
606                         }
607                 }
608
609 #if 0
610                 *(buf1++) = (vola * PSG->VolA) / STEP;
611                 *(buf2++) = (volb * PSG->VolB) / STEP;
612                 *(buf3++) = (volc * PSG->VolC) / STEP;
613 #else   // [Tom's code...]
614                 // Output PCM wave [-32768...32767] instead of MAME's voltage level [0...32767]
615                 // - This allows for better s/w mixing
616
617                 if (PSG->VolA)
618                 {
619                         if (vola)
620                                 *(buf1++) = (vola * PSG->VolA) / STEP;
621                         else
622                                 *(buf1++) = -(int)PSG->VolA;
623                 }
624                 else
625                         *(buf1++) = 0;
626
627                 if (PSG->VolB)
628                 {
629                         if (volb)
630                                 *(buf2++) = (volb * PSG->VolB) / STEP;
631                         else
632                                 *(buf2++) = -(int)PSG->VolB;
633                 }
634                 else
635                         *(buf2++) = 0;
636
637                 if (PSG->VolC)
638                 {
639                         if (volc)
640                                 *(buf3++) = (volc * PSG->VolC) / STEP;
641                         else
642                                 *(buf3++) = -(int)PSG->VolC;
643                 }
644                 else
645                         *(buf3++) = 0;
646 #endif
647                 length--;
648         }
649 }
650
651
652 static void AY8910_set_clock(int chip, int clock)
653 {
654         struct AY8910 * PSG = &AYPSG[chip];
655
656         /* The step clock for the tone and noise generators is the chip clock    *
657          * divided by 8; for the envelope generator of the AY-3-8910, it is half *
658          * that much (clock/16), but the envelope of the YM2149 goes twice as    *
659          * fast, therefore again clock/8.                                        *
660          * Here we calculate the number of steps which happen during one sample  *
661          * at the given sample rate. No. of events = sample rate / (clock/8).    *
662          * STEP is a multiplier used to turn the fraction into a fixed point     *
663          * number.                                                               */
664         PSG->UpdateStep = (unsigned int)(((double)STEP * PSG->SampleRate * 8 + clock / 2) / clock);     // [TC: unsigned int cast]
665 }
666
667
668 static void build_mixer_table(int chip)
669 {
670         struct AY8910 * PSG = &AYPSG[chip];
671
672         /* calculate the volume->voltage conversion table                     */
673         /* The AY-3-8910 has 16 levels, in a logarithmic scale (3dB per step) */
674         /* The YM2149 still has 16 levels for the tone generators, but 32 for */
675         /* the envelope generator (1.5dB per step).                           */
676         double out = MAX_OUTPUT;
677
678         for(int i=31; i>0; i--)
679         {
680                 PSG->VolTable[i] = (unsigned int)(out + 0.5);   /* round to nearest */  // [TC: unsigned int cast]
681                 out /= 1.188502227;     /* = 10 ^ (1.5/20) = 1.5dB */
682         }
683
684         PSG->VolTable[0] = 0;
685 }
686
687
688 void AY8910_reset(int chip)
689 {
690         int i;
691         struct AY8910 * PSG = &AYPSG[chip];
692
693         PSG->register_latch = 0;
694         PSG->RNG = 1;
695         PSG->OutputA = 0;
696         PSG->OutputB = 0;
697         PSG->OutputC = 0;
698         PSG->OutputN = 0xFF;
699         PSG->lastEnable = -1;   /* force a write */
700
701         for(i=0; i<AY_PORTA; i++)
702                 _AYWriteReg(chip, i, 0);        /* AYWriteReg() uses the timer system; we cannot */
703                                                                         /* call it at this time because the timer system */
704                                                                         /* has not been initialized.                     */
705 }
706
707 // This stuff looks like Tom's code, so let's streamline and un-MSHungarianize this shit:
708 // [DONE]
709
710 void AY8910_InitAll(int clock, int sampleRate)
711 {
712         for(int chip=0; chip<MAX_8910; chip++)
713         {
714                 struct AY8910 * PSG = &AYPSG[chip];
715
716                 memset(PSG, 0, sizeof(struct AY8910));
717                 PSG->SampleRate = sampleRate;
718                 AY8910_set_clock(chip, clock);
719                 build_mixer_table(chip);
720         }
721 }
722
723 void AY8910_InitClock(int clock)
724 {
725         for(int chip=0; chip<MAX_8910; chip++)
726                 AY8910_set_clock(chip, clock);
727 }
728
729 uint8_t * AY8910_GetRegsPtr(uint16_t chipNum)
730 {
731         if (chipNum >= MAX_8910)
732                 return NULL;
733
734         return &AYPSG[chipNum].Regs[0];
735 }