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