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