summaryrefslogtreecommitdiff
path: root/apps/codecs/asap/apokeysnd.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/asap/apokeysnd.c')
-rw-r--r--apps/codecs/asap/apokeysnd.c537
1 files changed, 537 insertions, 0 deletions
diff --git a/apps/codecs/asap/apokeysnd.c b/apps/codecs/asap/apokeysnd.c
new file mode 100644
index 0000000000..d0a4b1bd91
--- /dev/null
+++ b/apps/codecs/asap/apokeysnd.c
@@ -0,0 +1,537 @@
1/*
2 * apokeysnd.c - another POKEY sound emulator
3 *
4 * Copyright (C) 2007-2008 Piotr Fusik
5 *
6 * This file is part of ASAP (Another Slight Atari Player),
7 * see http://asap.sourceforge.net
8 *
9 * ASAP is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License,
12 * or (at your option) any later version.
13 *
14 * ASAP is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 * See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ASAP; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#if !defined(JAVA) && !defined(CSHARP)
25#include <string.h>
26#endif
27
28#include "asap_internal.h"
29
30#define ULTRASOUND_CYCLES 112
31
32#define MUTE_FREQUENCY 1
33#define MUTE_INIT 2
34#define MUTE_USER 4
35
36CONST_LOOKUP(byte, poly4_lookup) =
37 { 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1 };
38CONST_LOOKUP(byte, poly5_lookup) =
39 { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1,
40 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1 };
41
42FILE_FUNC void init_state(PokeyState PTR pst)
43{
44 PST audctl = 0;
45 PST init = FALSE;
46 PST poly_index = 15 * 31 * 131071;
47 PST div_cycles = 28;
48 PST mute1 = MUTE_FREQUENCY | MUTE_USER;
49 PST mute2 = MUTE_FREQUENCY | MUTE_USER;
50 PST mute3 = MUTE_FREQUENCY | MUTE_USER;
51 PST mute4 = MUTE_FREQUENCY | MUTE_USER;
52 PST audf1 = 0;
53 PST audf2 = 0;
54 PST audf3 = 0;
55 PST audf4 = 0;
56 PST audc1 = 0;
57 PST audc2 = 0;
58 PST audc3 = 0;
59 PST audc4 = 0;
60 PST tick_cycle1 = NEVER;
61 PST tick_cycle2 = NEVER;
62 PST tick_cycle3 = NEVER;
63 PST tick_cycle4 = NEVER;
64 PST period_cycles1 = 28;
65 PST period_cycles2 = 28;
66 PST period_cycles3 = 28;
67 PST period_cycles4 = 28;
68 PST reload_cycles1 = 28;
69 PST reload_cycles3 = 28;
70 PST out1 = 0;
71 PST out2 = 0;
72 PST out3 = 0;
73 PST out4 = 0;
74 PST delta1 = 0;
75 PST delta2 = 0;
76 PST delta3 = 0;
77 PST delta4 = 0;
78 PST skctl = 3;
79 ZERO_ARRAY(PST delta_buffer);
80}
81
82ASAP_FUNC void PokeySound_Initialize(ASAP_State PTR ast)
83{
84 int i;
85 int reg;
86 reg = 0x1ff;
87 for (i = 0; i < 511; i++) {
88 reg = ((((reg >> 5) ^ reg) & 1) << 8) + (reg >> 1);
89 AST poly9_lookup[i] = (byte) reg;
90 }
91 reg = 0x1ffff;
92 for (i = 0; i < 16385; i++) {
93 reg = ((((reg >> 5) ^ reg) & 0xff) << 9) + (reg >> 8);
94 AST poly17_lookup[i] = (byte) (reg >> 1);
95 }
96 AST sample_offset = 0;
97 AST sample_index = 0;
98 AST samples = 0;
99 AST iir_acc_left = 0;
100 AST iir_acc_right = 0;
101 init_state(ADDRESSOF AST base_pokey);
102 init_state(ADDRESSOF AST extra_pokey);
103}
104
105#define CYCLE_TO_SAMPLE(cycle) (((cycle) * ASAP_SAMPLE_RATE + AST sample_offset) / ASAP_MAIN_CLOCK)
106
107#define DO_TICK(ch) \
108 if (PST init) { \
109 switch (PST audc##ch >> 4) { \
110 case 10: \
111 case 14: \
112 PST out##ch ^= 1; \
113 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta##ch = -PST delta##ch; \
114 break; \
115 default: \
116 break; \
117 } \
118 } \
119 else { \
120 int poly = cycle + PST poly_index - (ch - 1); \
121 int newout = PST out##ch; \
122 switch (PST audc##ch >> 4) { \
123 case 0: \
124 if (poly5_lookup[poly % 31] != 0) { \
125 if ((PST audctl & 0x80) != 0) \
126 newout = AST poly9_lookup[poly % 511] & 1; \
127 else { \
128 poly %= 131071; \
129 newout = (AST poly17_lookup[poly >> 3] >> (poly & 7)) & 1; \
130 } \
131 } \
132 break; \
133 case 2: \
134 case 6: \
135 newout ^= poly5_lookup[poly % 31]; \
136 break; \
137 case 4: \
138 if (poly5_lookup[poly % 31] != 0) \
139 newout = poly4_lookup[poly % 15]; \
140 break; \
141 case 8: \
142 if ((PST audctl & 0x80) != 0) \
143 newout = AST poly9_lookup[poly % 511] & 1; \
144 else { \
145 poly %= 131071; \
146 newout = (AST poly17_lookup[poly >> 3] >> (poly & 7)) & 1; \
147 } \
148 break; \
149 case 10: \
150 case 14: \
151 newout ^= 1; \
152 break; \
153 case 12: \
154 newout = poly4_lookup[poly % 15]; \
155 break; \
156 default: \
157 break; \
158 } \
159 if (newout != PST out##ch) { \
160 PST out##ch = newout; \
161 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta##ch = -PST delta##ch; \
162 } \
163 }
164
165FILE_FUNC void generate(ASAP_State PTR ast, PokeyState PTR pst, int current_cycle)
166{
167 for (;;) {
168 int cycle = current_cycle;
169 if (cycle > PST tick_cycle1)
170 cycle = PST tick_cycle1;
171 if (cycle > PST tick_cycle2)
172 cycle = PST tick_cycle2;
173 if (cycle > PST tick_cycle3)
174 cycle = PST tick_cycle3;
175 if (cycle > PST tick_cycle4)
176 cycle = PST tick_cycle4;
177 if (cycle == current_cycle)
178 break;
179 if (cycle == PST tick_cycle3) {
180 PST tick_cycle3 += PST period_cycles3;
181 if ((PST audctl & 4) != 0 && PST delta1 > 0 && PST mute1 == 0)
182 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta1 = -PST delta1;
183 DO_TICK(3);
184 }
185 if (cycle == PST tick_cycle4) {
186 PST tick_cycle4 += PST period_cycles4;
187 if ((PST audctl & 8) != 0)
188 PST tick_cycle3 = cycle + PST reload_cycles3;
189 if ((PST audctl & 2) != 0 && PST delta2 > 0 && PST mute2 == 0)
190 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta2 = -PST delta2;
191 DO_TICK(4);
192 }
193 if (cycle == PST tick_cycle1) {
194 PST tick_cycle1 += PST period_cycles1;
195 if ((PST skctl & 0x88) == 8)
196 PST tick_cycle2 = cycle + PST period_cycles2;
197 DO_TICK(1);
198 }
199 if (cycle == PST tick_cycle2) {
200 PST tick_cycle2 += PST period_cycles2;
201 if ((PST audctl & 0x10) != 0)
202 PST tick_cycle1 = cycle + PST reload_cycles1;
203 else if ((PST skctl & 8) != 0)
204 PST tick_cycle1 = cycle + PST period_cycles1;
205 DO_TICK(2);
206 }
207 }
208}
209
210#define MUTE_CHANNEL(ch, cond, mask) \
211 if (cond) { \
212 PST mute##ch |= mask; \
213 PST tick_cycle##ch = NEVER; \
214 } \
215 else { \
216 PST mute##ch &= ~mask; \
217 if (PST tick_cycle##ch == NEVER && PST mute##ch == 0) \
218 PST tick_cycle##ch = AST cycle; \
219 }
220
221#define DO_ULTRASOUND(ch) \
222 MUTE_CHANNEL(ch, PST period_cycles##ch <= ULTRASOUND_CYCLES && (PST audc##ch >> 4 == 10 || PST audc##ch >> 4 == 14), MUTE_FREQUENCY)
223
224#define DO_AUDC(ch) \
225 if (data == PST audc##ch) \
226 break; \
227 generate(ast, pst, AST cycle); \
228 PST audc##ch = data; \
229 if ((data & 0x10) != 0) { \
230 data &= 0xf; \
231 if ((PST mute##ch & MUTE_USER) == 0) \
232 PST delta_buffer[CYCLE_TO_SAMPLE(AST cycle)] \
233 += PST delta##ch > 0 ? data - PST delta##ch : data; \
234 PST delta##ch = data; \
235 } \
236 else { \
237 data &= 0xf; \
238 DO_ULTRASOUND(ch); \
239 if (PST delta##ch > 0) { \
240 if ((PST mute##ch & MUTE_USER) == 0) \
241 PST delta_buffer[CYCLE_TO_SAMPLE(AST cycle)] \
242 += data - PST delta##ch; \
243 PST delta##ch = data; \
244 } \
245 else \
246 PST delta##ch = -data; \
247 } \
248 break;
249
250#define DO_INIT(ch, cond) \
251 MUTE_CHANNEL(ch, PST init && cond, MUTE_INIT)
252
253ASAP_FUNC void PokeySound_PutByte(ASAP_State PTR ast, int addr, int data)
254{
255 PokeyState PTR pst = (addr & AST extra_pokey_mask) != 0
256 ? ADDRESSOF AST extra_pokey : ADDRESSOF AST base_pokey;
257 switch (addr & 0xf) {
258 case 0x00:
259 if (data == PST audf1)
260 break;
261 generate(ast, pst, AST cycle);
262 PST audf1 = data;
263 switch (PST audctl & 0x50) {
264 case 0x00:
265 PST period_cycles1 = PST div_cycles * (data + 1);
266 break;
267 case 0x10:
268 PST period_cycles2 = PST div_cycles * (data + 256 * PST audf2 + 1);
269 PST reload_cycles1 = PST div_cycles * (data + 1);
270 DO_ULTRASOUND(2);
271 break;
272 case 0x40:
273 PST period_cycles1 = data + 4;
274 break;
275 case 0x50:
276 PST period_cycles2 = data + 256 * PST audf2 + 7;
277 PST reload_cycles1 = data + 4;
278 DO_ULTRASOUND(2);
279 break;
280 }
281 DO_ULTRASOUND(1);
282 break;
283 case 0x01:
284 DO_AUDC(1)
285 case 0x02:
286 if (data == PST audf2)
287 break;
288 generate(ast, pst, AST cycle);
289 PST audf2 = data;
290 switch (PST audctl & 0x50) {
291 case 0x00:
292 case 0x40:
293 PST period_cycles2 = PST div_cycles * (data + 1);
294 break;
295 case 0x10:
296 PST period_cycles2 = PST div_cycles * (PST audf1 + 256 * data + 1);
297 break;
298 case 0x50:
299 PST period_cycles2 = PST audf1 + 256 * data + 7;
300 break;
301 }
302 DO_ULTRASOUND(2);
303 break;
304 case 0x03:
305 DO_AUDC(2)
306 case 0x04:
307 if (data == PST audf3)
308 break;
309 generate(ast, pst, AST cycle);
310 PST audf3 = data;
311 switch (PST audctl & 0x28) {
312 case 0x00:
313 PST period_cycles3 = PST div_cycles * (data + 1);
314 break;
315 case 0x08:
316 PST period_cycles4 = PST div_cycles * (data + 256 * PST audf4 + 1);
317 PST reload_cycles3 = PST div_cycles * (data + 1);
318 DO_ULTRASOUND(4);
319 break;
320 case 0x20:
321 PST period_cycles3 = data + 4;
322 break;
323 case 0x28:
324 PST period_cycles4 = data + 256 * PST audf4 + 7;
325 PST reload_cycles3 = data + 4;
326 DO_ULTRASOUND(4);
327 break;
328 }
329 DO_ULTRASOUND(3);
330 break;
331 case 0x05:
332 DO_AUDC(3)
333 case 0x06:
334 if (data == PST audf4)
335 break;
336 generate(ast, pst, AST cycle);
337 PST audf4 = data;
338 switch (PST audctl & 0x28) {
339 case 0x00:
340 case 0x20:
341 PST period_cycles4 = PST div_cycles * (data + 1);
342 break;
343 case 0x08:
344 PST period_cycles4 = PST div_cycles * (PST audf3 + 256 * data + 1);
345 break;
346 case 0x28:
347 PST period_cycles4 = PST audf3 + 256 * data + 7;
348 break;
349 }
350 DO_ULTRASOUND(4);
351 break;
352 case 0x07:
353 DO_AUDC(4)
354 case 0x08:
355 if (data == PST audctl)
356 break;
357 generate(ast, pst, AST cycle);
358 PST audctl = data;
359 PST div_cycles = ((data & 1) != 0) ? 114 : 28;
360 /* TODO: tick_cycles */
361 switch (data & 0x50) {
362 case 0x00:
363 PST period_cycles1 = PST div_cycles * (PST audf1 + 1);
364 PST period_cycles2 = PST div_cycles * (PST audf2 + 1);
365 break;
366 case 0x10:
367 PST period_cycles1 = PST div_cycles * 256;
368 PST period_cycles2 = PST div_cycles * (PST audf1 + 256 * PST audf2 + 1);
369 PST reload_cycles1 = PST div_cycles * (PST audf1 + 1);
370 break;
371 case 0x40:
372 PST period_cycles1 = PST audf1 + 4;
373 PST period_cycles2 = PST div_cycles * (PST audf2 + 1);
374 break;
375 case 0x50:
376 PST period_cycles1 = 256;
377 PST period_cycles2 = PST audf1 + 256 * PST audf2 + 7;
378 PST reload_cycles1 = PST audf1 + 4;
379 break;
380 }
381 DO_ULTRASOUND(1);
382 DO_ULTRASOUND(2);
383 switch (data & 0x28) {
384 case 0x00:
385 PST period_cycles3 = PST div_cycles * (PST audf3 + 1);
386 PST period_cycles4 = PST div_cycles * (PST audf4 + 1);
387 break;
388 case 0x08:
389 PST period_cycles3 = PST div_cycles * 256;
390 PST period_cycles4 = PST div_cycles * (PST audf3 + 256 * PST audf4 + 1);
391 PST reload_cycles3 = PST div_cycles * (PST audf3 + 1);
392 break;
393 case 0x20:
394 PST period_cycles3 = PST audf3 + 4;
395 PST period_cycles4 = PST div_cycles * (PST audf4 + 1);
396 break;
397 case 0x28:
398 PST period_cycles3 = 256;
399 PST period_cycles4 = PST audf3 + 256 * PST audf4 + 7;
400 PST reload_cycles3 = PST audf3 + 4;
401 break;
402 }
403 DO_ULTRASOUND(3);
404 DO_ULTRASOUND(4);
405 break;
406 case 0x09:
407 /* TODO: STIMER */
408 break;
409 case 0x0f:
410 PST skctl = data;
411 PST init = ((data & 3) == 0);
412 DO_INIT(1, (PST audctl & 0x40) == 0);
413 DO_INIT(2, (PST audctl & 0x50) != 0x50);
414 DO_INIT(3, (PST audctl & 0x20) == 0);
415 DO_INIT(4, (PST audctl & 0x28) != 0x28);
416 break;
417 default:
418 break;
419 }
420}
421
422ASAP_FUNC int PokeySound_GetRandom(ASAP_State PTR ast, int addr)
423{
424 PokeyState PTR pst = (addr & AST extra_pokey_mask) != 0
425 ? ADDRESSOF AST extra_pokey : ADDRESSOF AST base_pokey;
426 int i;
427 if (PST init)
428 return 0xff;
429 i = AST cycle + PST poly_index;
430 if ((PST audctl & 0x80) != 0)
431 return AST poly9_lookup[i % 511];
432 else {
433 int j;
434 i %= 131071;
435 j = i >> 3;
436 i &= 7;
437 return ((AST poly17_lookup[j] >> i) + (AST poly17_lookup[j + 1] << (8 - i))) & 0xff;
438 }
439}
440
441FILE_FUNC void end_frame(ASAP_State PTR ast, PokeyState PTR pst, int cycle_limit)
442{
443 int m;
444 generate(ast, pst, cycle_limit);
445 PST poly_index += cycle_limit;
446 m = ((PST audctl & 0x80) != 0) ? 15 * 31 * 511 : 15 * 31 * 131071;
447 if (PST poly_index >= 2 * m)
448 PST poly_index -= m;
449 if (PST tick_cycle1 != NEVER)
450 PST tick_cycle1 -= cycle_limit;
451 if (PST tick_cycle2 != NEVER)
452 PST tick_cycle2 -= cycle_limit;
453 if (PST tick_cycle3 != NEVER)
454 PST tick_cycle3 -= cycle_limit;
455 if (PST tick_cycle4 != NEVER)
456 PST tick_cycle4 -= cycle_limit;
457}
458
459ASAP_FUNC void PokeySound_StartFrame(ASAP_State PTR ast)
460{
461 ZERO_ARRAY(AST base_pokey.delta_buffer);
462 if (AST extra_pokey_mask != 0)
463 ZERO_ARRAY(AST extra_pokey.delta_buffer);
464}
465
466ASAP_FUNC void PokeySound_EndFrame(ASAP_State PTR ast, int current_cycle)
467{
468 end_frame(ast, ADDRESSOF AST base_pokey, current_cycle);
469 if (AST extra_pokey_mask != 0)
470 end_frame(ast, ADDRESSOF AST extra_pokey, current_cycle);
471 AST sample_offset += current_cycle * ASAP_SAMPLE_RATE;
472 AST sample_index = 0;
473 AST samples = AST sample_offset / ASAP_MAIN_CLOCK;
474 AST sample_offset %= ASAP_MAIN_CLOCK;
475}
476
477ASAP_FUNC int PokeySound_Generate(ASAP_State PTR ast, byte ARRAY buffer, int buffer_offset, int blocks, ASAP_SampleFormat format)
478{
479 int i = AST sample_index;
480 int samples = AST samples;
481 int acc_left = AST iir_acc_left;
482 int acc_right = AST iir_acc_right;
483 if (blocks < samples - i)
484 samples = i + blocks;
485 else
486 blocks = samples - i;
487 for (; i < samples; i++) {
488 int sample;
489 acc_left += (AST base_pokey.delta_buffer[i] << 20) - (acc_left * 3 >> 10);
490 sample = acc_left >> 10;
491#define STORE_SAMPLE \
492 if (sample < -32767) \
493 sample = -32767; \
494 else if (sample > 32767) \
495 sample = 32767; \
496 switch (format) { \
497 case ASAP_FORMAT_U8: \
498 buffer[buffer_offset++] = (byte) ((sample >> 8) + 128); \
499 break; \
500 case ASAP_FORMAT_S16_LE: \
501 buffer[buffer_offset++] = (byte) sample; \
502 buffer[buffer_offset++] = (byte) (sample >> 8); \
503 break; \
504 case ASAP_FORMAT_S16_BE: \
505 buffer[buffer_offset++] = (byte) (sample >> 8); \
506 buffer[buffer_offset++] = (byte) sample; \
507 break; \
508 }
509 STORE_SAMPLE;
510 if (AST extra_pokey_mask != 0) {
511 acc_right += (AST extra_pokey.delta_buffer[i] << 20) - (acc_right * 3 >> 10);
512 sample = acc_right >> 10;
513 STORE_SAMPLE;
514 }
515 }
516 if (i == AST samples) {
517 acc_left += AST base_pokey.delta_buffer[i] << 20;
518 acc_right += AST extra_pokey.delta_buffer[i] << 20;
519 }
520 AST sample_index = i;
521 AST iir_acc_left = acc_left;
522 AST iir_acc_right = acc_right;
523 return blocks;
524}
525
526ASAP_FUNC abool PokeySound_IsSilent(const PokeyState PTR pst)
527{
528 return ((PST audc1 | PST audc2 | PST audc3 | PST audc4) & 0xf) == 0;
529}
530
531ASAP_FUNC void PokeySound_Mute(const ASAP_State PTR ast, PokeyState PTR pst, int mask)
532{
533 MUTE_CHANNEL(1, (mask & 1) != 0, MUTE_USER);
534 MUTE_CHANNEL(2, (mask & 2) != 0, MUTE_USER);
535 MUTE_CHANNEL(3, (mask & 4) != 0, MUTE_USER);
536 MUTE_CHANNEL(4, (mask & 8) != 0, MUTE_USER);
537}