Another small bookmark.c revision, no functional change, saves bin size
[kugel-rb.git] / apps / codecs / libasap / asap.c
blobc79682c38bc2933e5ab1866e5ca64b4f43137b42
1 /*
2 * asap.c - ASAP engine
4 * Copyright (C) 2005-2010 Piotr Fusik
6 * This file is part of ASAP (Another Slight Atari Player),
7 * see http://asap.sourceforge.net
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.
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.
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
24 #include "asap_internal.h"
26 FUNC(int, ASAP_GetByte, (P(ASAP_State PTR, ast), P(int, addr)))
28 switch (addr & 0xff0f) {
29 case 0xd20a:
30 return PokeySound_GetRandom(ast, addr, ast _ cycle);
31 case 0xd20e:
32 if ((addr & ast _ extra_pokey_mask) != 0) {
33 /* interrupts in the extra POKEY not emulated at the moment */
34 return 0xff;
36 return ast _ irqst;
37 case 0xd20f:
38 /* just because some SAP files rely on this */
39 return 0xff;
40 case 0xd40b:
41 return ast _ scanline_number >> 1;
42 default:
43 return dGetByte(addr);
47 FUNC(void, ASAP_PutByte, (P(ASAP_State PTR, ast), P(int, addr), P(int, data)))
49 if ((addr >> 8) == 0xd2) {
50 if ((addr & (ast _ extra_pokey_mask + 0xf)) == 0xe) {
51 ast _ irqst |= data ^ 0xff;
52 #define SET_TIMER_IRQ(ch) \
53 if ((data & ast _ irqst & ch) != 0) { \
54 if (ast _ timer##ch##_cycle == NEVER) { \
55 V(int, t) = ast _ base_pokey.tick_cycle##ch; \
56 while (t < ast _ cycle) \
57 t += ast _ base_pokey.period_cycles##ch; \
58 ast _ timer##ch##_cycle = t; \
59 if (ast _ nearest_event_cycle > t) \
60 ast _ nearest_event_cycle = t; \
61 } \
62 } \
63 else \
64 ast _ timer##ch##_cycle = NEVER;
65 SET_TIMER_IRQ(1);
66 SET_TIMER_IRQ(2);
67 SET_TIMER_IRQ(4);
69 else
70 PokeySound_PutByte(ast, addr, data);
72 else if ((addr & 0xff0f) == 0xd40a) {
73 if (ast _ cycle <= ast _ next_scanline_cycle - 8)
74 ast _ cycle = ast _ next_scanline_cycle - 8;
75 else
76 ast _ cycle = ast _ next_scanline_cycle + 106;
78 else if ((addr & 0xff00) == ast _ module_info.covox_addr) {
79 V(PokeyState PTR, pst);
80 addr &= 3;
81 if (addr == 0 || addr == 3)
82 pst = ADDRESSOF ast _ base_pokey;
83 else
84 pst = ADDRESSOF ast _ extra_pokey;
85 pst _ delta_buffer[CYCLE_TO_SAMPLE(ast _ cycle)] += (data - UBYTE(ast _ covox[addr])) << DELTA_SHIFT_COVOX;
86 ast _ covox[addr] = CAST(byte) (data);
88 else if ((addr & 0xff1f) == 0xd01f) {
89 V(int, sample) = CYCLE_TO_SAMPLE(ast _ cycle);
90 V(int, delta);
91 data &= 8;
92 /* NOT data - ast _ consol; reverse to the POKEY sound */
93 delta = (ast _ consol - data) << DELTA_SHIFT_GTIA;
94 ast _ consol = data;
95 ast _ base_pokey.delta_buffer[sample] += delta;
96 ast _ extra_pokey.delta_buffer[sample] += delta;
98 else
99 dPutByte(addr, data);
102 #define UWORD(array, index) (UBYTE(array[index]) + (UBYTE(array[(index) + 1]) << 8))
104 #ifndef ASAP_ONLY_SAP
106 #ifndef JAVA
107 #include "players.h"
108 #endif
110 #define CMR_BASS_TABLE_OFFSET 0x70f
112 CONST_ARRAY(byte, cmr_bass_table)
113 0x5C, 0x56, 0x50, 0x4D, 0x47, 0x44, 0x41, 0x3E,
114 0x38, 0x35, CAST(byte) (0x88), 0x7F, 0x79, 0x73, 0x6C, 0x67,
115 0x60, 0x5A, 0x55, 0x51, 0x4C, 0x48, 0x43, 0x3F,
116 0x3D, 0x39, 0x34, 0x33, 0x30, 0x2D, 0x2A, 0x28,
117 0x25, 0x24, 0x21, 0x1F, 0x1E
118 END_CONST_ARRAY;
120 CONST_ARRAY(int, perframe2fastplay)
121 312, 312 / 2, 312 / 3, 312 / 4
122 END_CONST_ARRAY;
124 /* Loads native module (anything except SAP) and 6502 player routine. */
125 PRIVATE FUNC(abool, load_native, (
126 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
127 P(CONST BYTEARRAY, module), P(int, module_len), P(RESOURCE, player)))
129 V(int, player_last_byte);
130 V(int, music_last_byte);
131 V(int, block_len);
132 if ((UBYTE(module[0]) != 0xff || UBYTE(module[1]) != 0xff)
133 && (module[0] != 0 || module[1] != 0)) /* some CMC and clones start with zeros */
134 return FALSE;
135 module_info _ player = UWORD(player, 2);
136 player_last_byte = UWORD(player, 4);
137 module_info _ music = UWORD(module, 2);
138 if (module_info _ music <= player_last_byte)
139 return FALSE;
140 music_last_byte = UWORD(module, 4);
141 if (module_info _ music <= 0xd7ff && music_last_byte >= 0xd000)
142 return FALSE;
143 block_len = music_last_byte + 1 - module_info _ music;
144 if (6 + block_len != module_len) {
145 V(int, info_addr);
146 V(int, info_len);
147 if (module_info _ type != ASAP_TYPE_RMT || 11 + block_len > module_len)
148 return FALSE;
149 /* allow optional info for Raster Music Tracker */
150 info_addr = UWORD(module, 6 + block_len);
151 if (info_addr != module_info _ music + block_len)
152 return FALSE;
153 info_len = UWORD(module, 8 + block_len) + 1 - info_addr;
154 if (10 + block_len + info_len != module_len)
155 return FALSE;
157 if (ast != NULL) {
158 COPY_ARRAY(ast _ memory, module_info _ music, module, 6, block_len);
159 COPY_ARRAY(ast _ memory, module_info _ player, player, 6, player_last_byte + 1 - module_info _ player);
161 return TRUE;
164 PRIVATE FUNC(void, set_song_duration, (P(ASAP_ModuleInfo PTR, module_info), P(int, player_calls)))
166 module_info _ durations[module_info _ songs] = TO_INT(player_calls * module_info _ fastplay * 114000.0 / 1773447);
167 module_info _ songs++;
170 #define SEEN_THIS_CALL 1
171 #define SEEN_BEFORE 2
172 #define SEEN_REPEAT 3
174 PRIVATE FUNC(void, parse_cmc_song, (P(ASAP_ModuleInfo PTR, module_info), P(CONST BYTEARRAY, module), P(int, pos)))
176 V(int, tempo) = UBYTE(module[0x19]);
177 V(int, player_calls) = 0;
178 V(int, rep_start_pos) = 0;
179 V(int, rep_end_pos) = 0;
180 V(int, rep_times) = 0;
181 NEW_ARRAY(byte, seen, 0x55);
182 INIT_ARRAY(seen);
183 while (pos >= 0 && pos < 0x55) {
184 V(int, p1);
185 V(int, p2);
186 V(int, p3);
187 if (pos == rep_end_pos && rep_times > 0) {
188 for (p1 = 0; p1 < 0x55; p1++)
189 if (seen[p1] == SEEN_THIS_CALL || seen[p1] == SEEN_REPEAT)
190 seen[p1] = 0;
191 rep_times--;
192 pos = rep_start_pos;
194 if (seen[pos] != 0) {
195 if (seen[pos] != SEEN_THIS_CALL)
196 module_info _ loops[module_info _ songs] = TRUE;
197 break;
199 seen[pos] = SEEN_THIS_CALL;
200 p1 = UBYTE(module[0x206 + pos]);
201 p2 = UBYTE(module[0x25b + pos]);
202 p3 = UBYTE(module[0x2b0 + pos]);
203 if (p1 == 0xfe || p2 == 0xfe || p3 == 0xfe) {
204 pos++;
205 continue;
207 p1 >>= 4;
208 if (p1 == 8)
209 break;
210 if (p1 == 9) {
211 pos = p2;
212 continue;
214 if (p1 == 0xa) {
215 pos -= p2;
216 continue;
218 if (p1 == 0xb) {
219 pos += p2;
220 continue;
222 if (p1 == 0xc) {
223 tempo = p2;
224 pos++;
225 continue;
227 if (p1 == 0xd) {
228 pos++;
229 rep_start_pos = pos;
230 rep_end_pos = pos + p2;
231 rep_times = p3 - 1;
232 continue;
234 if (p1 == 0xe) {
235 module_info _ loops[module_info _ songs] = TRUE;
236 break;
238 p2 = rep_times > 0 ? SEEN_REPEAT : SEEN_BEFORE;
239 for (p1 = 0; p1 < 0x55; p1++)
240 if (seen[p1] == SEEN_THIS_CALL)
241 seen[p1] = CAST(byte) p2;
242 player_calls += tempo * (module_info _ type == ASAP_TYPE_CM3 ? 48 : 64);
243 pos++;
245 set_song_duration(module_info, player_calls);
248 PRIVATE FUNC(abool, parse_cmc, (
249 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
250 P(CONST BYTEARRAY, module), P(int, module_len), P(int, type), P(RESOURCE, player)))
252 V(int, last_pos);
253 V(int, pos);
254 if (module_len < 0x306)
255 return FALSE;
256 module_info _ type = type;
257 if (!load_native(ast, module_info, module, module_len, player))
258 return FALSE;
259 if (ast != NULL && type == ASAP_TYPE_CMR)
260 COPY_ARRAY(ast _ memory, 0x500 + CMR_BASS_TABLE_OFFSET, cmr_bass_table, 0, sizeof(cmr_bass_table));
261 last_pos = 0x54;
262 while (--last_pos >= 0) {
263 if (UBYTE(module[0x206 + last_pos]) < 0xb0
264 || UBYTE(module[0x25b + last_pos]) < 0x40
265 || UBYTE(module[0x2b0 + last_pos]) < 0x40)
266 break;
267 if (module_info _ channels == 2) {
268 if (UBYTE(module[0x306 + last_pos]) < 0xb0
269 || UBYTE(module[0x35b + last_pos]) < 0x40
270 || UBYTE(module[0x3b0 + last_pos]) < 0x40)
271 break;
274 module_info _ songs = 0;
275 parse_cmc_song(module_info, module, 0);
276 for (pos = 0; pos < last_pos && module_info _ songs < ASAP_SONGS_MAX; pos++)
277 if (UBYTE(module[0x206 + pos]) == 0x8f || UBYTE(module[0x206 + pos]) == 0xef)
278 parse_cmc_song(module_info, module, pos + 1);
279 return TRUE;
282 PRIVATE FUNC(abool, is_dlt_track_empty, (P(CONST BYTEARRAY, module), P(int, pos)))
284 return UBYTE(module[0x2006 + pos]) >= 0x43
285 && UBYTE(module[0x2106 + pos]) >= 0x40
286 && UBYTE(module[0x2206 + pos]) >= 0x40
287 && UBYTE(module[0x2306 + pos]) >= 0x40;
290 PRIVATE FUNC(abool, is_dlt_pattern_end, (P(CONST BYTEARRAY, module), P(int, pos), P(int, i)))
292 V(int, ch);
293 for (ch = 0; ch < 4; ch++) {
294 V(int, pattern) = UBYTE(module[0x2006 + (ch << 8) + pos]);
295 if (pattern < 64) {
296 V(int, offset) = 6 + (pattern << 7) + (i << 1);
297 if ((module[offset] & 0x80) == 0 && (module[offset + 1] & 0x80) != 0)
298 return TRUE;
301 return FALSE;
304 PRIVATE FUNC(void, parse_dlt_song, (
305 P(ASAP_ModuleInfo PTR, module_info), P(CONST BYTEARRAY, module),
306 P(BOOLARRAY, seen), P(int, pos)))
308 V(int, player_calls) = 0;
309 V(abool, loop) = FALSE;
310 V(int, tempo) = 6;
311 while (pos < 128 && !seen[pos] && is_dlt_track_empty(module, pos))
312 seen[pos++] = TRUE;
313 module_info _ song_pos[module_info _ songs] = CAST(byte) pos;
314 while (pos < 128) {
315 V(int, p1);
316 if (seen[pos]) {
317 loop = TRUE;
318 break;
320 seen[pos] = TRUE;
321 p1 = module[0x2006 + pos];
322 if (p1 == 0x40 || is_dlt_track_empty(module, pos))
323 break;
324 if (p1 == 0x41)
325 pos = UBYTE(module[0x2086 + pos]);
326 else if (p1 == 0x42)
327 tempo = UBYTE(module[0x2086 + pos++]);
328 else {
329 V(int, i);
330 for (i = 0; i < 64 && !is_dlt_pattern_end(module, pos, i); i++)
331 player_calls += tempo;
332 pos++;
335 if (player_calls > 0) {
336 module_info _ loops[module_info _ songs] = loop;
337 set_song_duration(module_info, player_calls);
341 PRIVATE FUNC(abool, parse_dlt, (
342 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
343 P(CONST BYTEARRAY, module), P(int, module_len)))
345 V(int, pos);
346 NEW_ARRAY(abool, seen, 128);
347 if (module_len == 0x2c06) {
348 if (ast != NULL)
349 ast _ memory[0x4c00] = 0;
351 else if (module_len != 0x2c07)
352 return FALSE;
353 module_info _ type = ASAP_TYPE_DLT;
354 if (!load_native(ast, module_info, module, module_len, GET_RESOURCE(dlt, obx))
355 || module_info _ music != 0x2000) {
356 return FALSE;
358 INIT_ARRAY(seen);
359 module_info _ songs = 0;
360 for (pos = 0; pos < 128 && module_info _ songs < ASAP_SONGS_MAX; pos++) {
361 if (!seen[pos])
362 parse_dlt_song(module_info, module, seen, pos);
364 return module_info _ songs > 0;
367 PRIVATE FUNC(void, parse_mpt_song, (
368 P(ASAP_ModuleInfo PTR, module_info), P(CONST BYTEARRAY, module),
369 P(BOOLARRAY, global_seen), P(int, song_len), P(int, pos)))
371 V(int, addr_to_offset) = UWORD(module, 2) - 6;
372 V(int, tempo) = UBYTE(module[0x1cf]);
373 V(int, player_calls) = 0;
374 NEW_ARRAY(byte, seen, 256);
375 NEW_ARRAY(int, pattern_offset, 4);
376 NEW_ARRAY(int, blank_rows, 4);
377 NEW_ARRAY(int, blank_rows_counter, 4);
378 INIT_ARRAY(seen);
379 INIT_ARRAY(blank_rows);
380 while (pos < song_len) {
381 V(int, i);
382 V(int, ch);
383 V(int, pattern_rows);
384 if (seen[pos] != 0) {
385 if (seen[pos] != SEEN_THIS_CALL)
386 module_info _ loops[module_info _ songs] = TRUE;
387 break;
389 seen[pos] = SEEN_THIS_CALL;
390 global_seen[pos] = TRUE;
391 i = UBYTE(module[0x1d0 + pos * 2]);
392 if (i == 0xff) {
393 pos = UBYTE(module[0x1d1 + pos * 2]);
394 continue;
396 for (ch = 3; ch >= 0; ch--) {
397 i = UBYTE(module[0x1c6 + ch]) + (UBYTE(module[0x1ca + ch]) << 8) - addr_to_offset;
398 i = UBYTE(module[i + pos * 2]);
399 if (i >= 0x40)
400 break;
401 i <<= 1;
402 i = UWORD(module, 0x46 + i);
403 pattern_offset[ch] = i == 0 ? 0 : i - addr_to_offset;
404 blank_rows_counter[ch] = 0;
406 if (ch >= 0)
407 break;
408 for (i = 0; i < song_len; i++)
409 if (seen[i] == SEEN_THIS_CALL)
410 seen[i] = SEEN_BEFORE;
411 for (pattern_rows = UBYTE(module[0x1ce]); --pattern_rows >= 0; ) {
412 for (ch = 3; ch >= 0; ch--) {
413 if (pattern_offset[ch] == 0 || --blank_rows_counter[ch] >= 0)
414 continue;
415 for (;;) {
416 i = UBYTE(module[pattern_offset[ch]++]);
417 if (i < 0x40 || i == 0xfe)
418 break;
419 if (i < 0x80)
420 continue;
421 if (i < 0xc0) {
422 blank_rows[ch] = i - 0x80;
423 continue;
425 if (i < 0xd0)
426 continue;
427 if (i < 0xe0) {
428 tempo = i - 0xcf;
429 continue;
431 pattern_rows = 0;
433 blank_rows_counter[ch] = blank_rows[ch];
435 player_calls += tempo;
437 pos++;
439 if (player_calls > 0)
440 set_song_duration(module_info, player_calls);
443 PRIVATE FUNC(abool, parse_mpt, (
444 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
445 P(CONST BYTEARRAY, module), P(int, module_len)))
447 V(int, track0_addr);
448 V(int, pos);
449 V(int, song_len);
450 /* seen[i] == TRUE if the track position i has been processed */
451 NEW_ARRAY(abool, global_seen, 256);
452 if (module_len < 0x1d0)
453 return FALSE;
454 module_info _ type = ASAP_TYPE_MPT;
455 if (!load_native(ast, module_info, module, module_len, GET_RESOURCE(mpt, obx)))
456 return FALSE;
457 track0_addr = UWORD(module, 2) + 0x1ca;
458 if (UBYTE(module[0x1c6]) + (UBYTE(module[0x1ca]) << 8) != track0_addr)
459 return FALSE;
460 /* Calculate the length of the first track. Address of the second track minus
461 address of the first track equals the length of the first track in bytes.
462 Divide by two to get number of track positions. */
463 song_len = (UBYTE(module[0x1c7]) + (UBYTE(module[0x1cb]) << 8) - track0_addr) >> 1;
464 if (song_len > 0xfe)
465 return FALSE;
466 INIT_ARRAY(global_seen);
467 module_info _ songs = 0;
468 for (pos = 0; pos < song_len && module_info _ songs < ASAP_SONGS_MAX; pos++) {
469 if (!global_seen[pos]) {
470 module_info _ song_pos[module_info _ songs] = CAST(byte) pos;
471 parse_mpt_song(module_info, module, global_seen, song_len, pos);
474 return module_info _ songs > 0;
477 CONST_ARRAY(byte, rmt_volume_silent)
478 16, 8, 4, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1
479 END_CONST_ARRAY;
481 PRIVATE FUNC(int, rmt_instrument_frames, (
482 P(CONST BYTEARRAY, module), P(int, instrument),
483 P(int, volume), P(int, volume_frame), P(abool, extra_pokey)))
485 V(int, addr_to_offset) = UWORD(module, 2) - 6;
486 V(int, per_frame) = module[0xc];
487 V(int, player_call);
488 V(int, player_calls);
489 V(int, index);
490 V(int, index_end);
491 V(int, index_loop);
492 V(int, volume_slide_depth);
493 V(int, volume_min);
494 V(int, volume_slide);
495 V(abool, silent_loop);
496 instrument = UWORD(module, 0xe) - addr_to_offset + (instrument << 1);
497 if (module[instrument + 1] == 0)
498 return 0;
499 instrument = UWORD(module, instrument) - addr_to_offset;
500 player_calls = player_call = volume_frame * per_frame;
501 index = UBYTE(module[instrument]) + 1 + player_call * 3;
502 index_end = UBYTE(module[instrument + 2]) + 3;
503 index_loop = UBYTE(module[instrument + 3]);
504 if (index_loop >= index_end)
505 return 0; /* error */
506 volume_slide_depth = UBYTE(module[instrument + 6]);
507 volume_min = UBYTE(module[instrument + 7]);
508 if (index >= index_end)
509 index = (index - index_end) % (index_end - index_loop) + index_loop;
510 else {
511 do {
512 V(int, vol) = module[instrument + index];
513 if (extra_pokey)
514 vol >>= 4;
515 if ((vol & 0xf) >= rmt_volume_silent[volume])
516 player_calls = player_call + 1;
517 player_call++;
518 index += 3;
519 } while (index < index_end);
521 if (volume_slide_depth == 0)
522 return player_calls / per_frame;
523 volume_slide = 128;
524 silent_loop = FALSE;
525 for (;;) {
526 V(int, vol);
527 if (index >= index_end) {
528 if (silent_loop)
529 break;
530 silent_loop = TRUE;
531 index = index_loop;
533 vol = module[instrument + index];
534 if (extra_pokey)
535 vol >>= 4;
536 if ((vol & 0xf) >= rmt_volume_silent[volume]) {
537 player_calls = player_call + 1;
538 silent_loop = FALSE;
540 player_call++;
541 index += 3;
542 volume_slide -= volume_slide_depth;
543 if (volume_slide < 0) {
544 volume_slide += 256;
545 if (--volume <= volume_min)
546 break;
549 return player_calls / per_frame;
552 PRIVATE FUNC(void, parse_rmt_song, (
553 P(ASAP_ModuleInfo PTR, module_info), P(CONST BYTEARRAY, module),
554 P(BOOLARRAY, global_seen), P(int, song_len), P(int, pos_shift), P(int, pos)))
556 V(int, ch);
557 V(int, addr_to_offset) = UWORD(module, 2) - 6;
558 V(int, tempo) = UBYTE(module[0xb]);
559 V(int, frames) = 0;
560 V(int, song_offset) = UWORD(module, 0x14) - addr_to_offset;
561 V(int, pattern_lo_offset) = UWORD(module, 0x10) - addr_to_offset;
562 V(int, pattern_hi_offset) = UWORD(module, 0x12) - addr_to_offset;
563 V(int, instrument_frames);
564 NEW_ARRAY(byte, seen, 256);
565 NEW_ARRAY(int, pattern_begin, 8);
566 NEW_ARRAY(int, pattern_offset, 8);
567 NEW_ARRAY(int, blank_rows, 8);
568 NEW_ARRAY(int, instrument_no, 8);
569 NEW_ARRAY(int, instrument_frame, 8);
570 NEW_ARRAY(int, volume_value, 8);
571 NEW_ARRAY(int, volume_frame, 8);
572 INIT_ARRAY(seen);
573 INIT_ARRAY(instrument_no);
574 INIT_ARRAY(instrument_frame);
575 INIT_ARRAY(volume_value);
576 INIT_ARRAY(volume_frame);
577 while (pos < song_len) {
578 V(int, i);
579 V(int, pattern_rows);
580 if (seen[pos] != 0) {
581 if (seen[pos] != SEEN_THIS_CALL)
582 module_info _ loops[module_info _ songs] = TRUE;
583 break;
585 seen[pos] = SEEN_THIS_CALL;
586 global_seen[pos] = TRUE;
587 if (UBYTE(module[song_offset + (pos << pos_shift)]) == 0xfe) {
588 pos = UBYTE(module[song_offset + (pos << pos_shift) + 1]);
589 continue;
591 for (ch = 0; ch < 1 << pos_shift; ch++) {
592 i = UBYTE(module[song_offset + (pos << pos_shift) + ch]);
593 if (i == 0xff)
594 blank_rows[ch] = 256;
595 else {
596 pattern_offset[ch] = pattern_begin[ch] = UBYTE(module[pattern_lo_offset + i])
597 + (UBYTE(module[pattern_hi_offset + i]) << 8) - addr_to_offset;
598 blank_rows[ch] = 0;
601 for (i = 0; i < song_len; i++)
602 if (seen[i] == SEEN_THIS_CALL)
603 seen[i] = SEEN_BEFORE;
604 for (pattern_rows = UBYTE(module[0xa]); --pattern_rows >= 0; ) {
605 for (ch = 0; ch < 1 << pos_shift; ch++) {
606 if (--blank_rows[ch] > 0)
607 continue;
608 for (;;) {
609 i = UBYTE(module[pattern_offset[ch]++]);
610 if ((i & 0x3f) < 62) {
611 i += UBYTE(module[pattern_offset[ch]++]) << 8;
612 if ((i & 0x3f) != 61) {
613 instrument_no[ch] = i >> 10;
614 instrument_frame[ch] = frames;
616 volume_value[ch] = (i >> 6) & 0xf;
617 volume_frame[ch] = frames;
618 break;
620 if (i == 62) {
621 blank_rows[ch] = UBYTE(module[pattern_offset[ch]++]);
622 break;
624 if ((i & 0x3f) == 62) {
625 blank_rows[ch] = i >> 6;
626 break;
628 if ((i & 0xbf) == 63) {
629 tempo = UBYTE(module[pattern_offset[ch]++]);
630 continue;
632 if (i == 0xbf) {
633 pattern_offset[ch] = pattern_begin[ch] + UBYTE(module[pattern_offset[ch]]);
634 continue;
636 /* assert(i == 0xff); */
637 pattern_rows = -1;
638 break;
640 if (pattern_rows < 0)
641 break;
643 if (pattern_rows >= 0)
644 frames += tempo;
646 pos++;
648 instrument_frames = 0;
649 for (ch = 0; ch < 1 << pos_shift; ch++) {
650 V(int, frame) = instrument_frame[ch];
651 frame += rmt_instrument_frames(module, instrument_no[ch], volume_value[ch], volume_frame[ch] - frame, ch >= 4);
652 if (instrument_frames < frame)
653 instrument_frames = frame;
655 if (frames > instrument_frames) {
656 if (frames - instrument_frames > 100)
657 module_info _ loops[module_info _ songs] = FALSE;
658 frames = instrument_frames;
660 if (frames > 0)
661 set_song_duration(module_info, frames);
664 PRIVATE FUNC(abool, parse_rmt, (
665 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
666 P(CONST BYTEARRAY, module), P(int, module_len)))
668 V(int, per_frame);
669 V(int, pos_shift);
670 V(int, song_len);
671 V(int, pos);
672 NEW_ARRAY(abool, global_seen, 256);
673 if (module_len < 0x30 || module[6] != CHARCODE('R') || module[7] != CHARCODE('M')
674 || module[8] != CHARCODE('T') || module[0xd] != 1)
675 return FALSE;
676 switch (CAST(char) module[9]) {
677 case CHARCODE('4'):
678 pos_shift = 2;
679 break;
680 case CHARCODE('8'):
681 module_info _ channels = 2;
682 pos_shift = 3;
683 break;
684 default:
685 return FALSE;
687 per_frame = module[0xc];
688 if (per_frame < 1 || per_frame > 4)
689 return FALSE;
690 module_info _ type = ASAP_TYPE_RMT;
691 if (!load_native(ast, module_info, module, module_len,
692 module_info _ channels == 2 ? GET_RESOURCE(rmt8, obx) : GET_RESOURCE(rmt4, obx)))
693 return FALSE;
694 song_len = UWORD(module, 4) + 1 - UWORD(module, 0x14);
695 if (pos_shift == 3 && (song_len & 4) != 0
696 && UBYTE(module[6 + UWORD(module, 4) - UWORD(module, 2) - 3]) == 0xfe)
697 song_len += 4;
698 song_len >>= pos_shift;
699 if (song_len >= 0x100)
700 return FALSE;
701 INIT_ARRAY(global_seen);
702 module_info _ songs = 0;
703 for (pos = 0; pos < song_len && module_info _ songs < ASAP_SONGS_MAX; pos++) {
704 if (!global_seen[pos]) {
705 module_info _ song_pos[module_info _ songs] = CAST(byte) pos;
706 parse_rmt_song(module_info, module, global_seen, song_len, pos_shift, pos);
709 /* must set fastplay after song durations calculations, so they assume 312 */
710 module_info _ fastplay = perframe2fastplay[per_frame - 1];
711 module_info _ player = 0x600;
712 return module_info _ songs > 0;
715 PRIVATE FUNC(void, parse_tmc_song, (
716 P(ASAP_ModuleInfo PTR, module_info), P(CONST BYTEARRAY, module), P(int, pos)))
718 V(int, addr_to_offset) = UWORD(module, 2) - 6;
719 V(int, tempo) = UBYTE(module[0x24]) + 1;
720 V(int, frames) = 0;
721 NEW_ARRAY(int, pattern_offset, 8);
722 NEW_ARRAY(int, blank_rows, 8);
723 while (UBYTE(module[0x1a6 + 15 + pos]) < 0x80) {
724 V(int, ch);
725 V(int, pattern_rows);
726 for (ch = 7; ch >= 0; ch--) {
727 V(int, pat) = UBYTE(module[0x1a6 + 15 + pos - 2 * ch]);
728 pattern_offset[ch] = UBYTE(module[0xa6 + pat]) + (UBYTE(module[0x126 + pat]) << 8) - addr_to_offset;
729 blank_rows[ch] = 0;
731 for (pattern_rows = 64; --pattern_rows >= 0; ) {
732 for (ch = 7; ch >= 0; ch--) {
733 if (--blank_rows[ch] >= 0)
734 continue;
735 for (;;) {
736 V(int, i) = UBYTE(module[pattern_offset[ch]++]);
737 if (i < 0x40) {
738 pattern_offset[ch]++;
739 break;
741 if (i == 0x40) {
742 i = UBYTE(module[pattern_offset[ch]++]);
743 if ((i & 0x7f) == 0)
744 pattern_rows = 0;
745 else
746 tempo = (i & 0x7f) + 1;
747 if (i >= 0x80)
748 pattern_offset[ch]++;
749 break;
751 if (i < 0x80) {
752 i = module[pattern_offset[ch]++] & 0x7f;
753 if (i == 0)
754 pattern_rows = 0;
755 else
756 tempo = i + 1;
757 pattern_offset[ch]++;
758 break;
760 if (i < 0xc0)
761 continue;
762 blank_rows[ch] = i - 0xbf;
763 break;
766 frames += tempo;
768 pos += 16;
770 if (UBYTE(module[0x1a6 + 14 + pos]) < 0x80)
771 module_info _ loops[module_info _ songs] = TRUE;
772 set_song_duration(module_info, frames);
775 PRIVATE FUNC(abool, parse_tmc, (
776 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
777 P(CONST BYTEARRAY, module), P(int, module_len)))
779 V(int, i);
780 V(int, last_pos);
781 if (module_len < 0x1d0)
782 return FALSE;
783 module_info _ type = ASAP_TYPE_TMC;
784 if (!load_native(ast, module_info, module, module_len, GET_RESOURCE(tmc, obx)))
785 return FALSE;
786 module_info _ channels = 2;
787 i = 0;
788 /* find first instrument */
789 while (module[0x66 + i] == 0) {
790 if (++i >= 64)
791 return FALSE; /* no instrument */
793 last_pos = (UBYTE(module[0x66 + i]) << 8) + UBYTE(module[0x26 + i])
794 - UWORD(module, 2) - 0x1b0;
795 if (0x1b5 + last_pos >= module_len)
796 return FALSE;
797 /* skip trailing jumps */
798 do {
799 if (last_pos <= 0)
800 return FALSE; /* no pattern to play */
801 last_pos -= 16;
802 } while (UBYTE(module[0x1b5 + last_pos]) >= 0x80);
803 module_info _ songs = 0;
804 parse_tmc_song(module_info, module, 0);
805 for (i = 0; i < last_pos && module_info _ songs < ASAP_SONGS_MAX; i += 16)
806 if (UBYTE(module[0x1b5 + i]) >= 0x80)
807 parse_tmc_song(module_info, module, i + 16);
808 /* must set fastplay after song durations calculations, so they assume 312 */
809 i = module[0x25];
810 if (i < 1 || i > 4)
811 return FALSE;
812 if (ast != NULL)
813 ast _ tmc_per_frame = module[0x25];
814 module_info _ fastplay = perframe2fastplay[i - 1];
815 return TRUE;
818 PRIVATE FUNC(void, parse_tm2_song, (
819 P(ASAP_ModuleInfo PTR, module_info), P(CONST BYTEARRAY, module), P(int, pos)))
821 V(int, addr_to_offset) = UWORD(module, 2) - 6;
822 V(int, tempo) = UBYTE(module[0x24]) + 1;
823 V(int, player_calls) = 0;
824 NEW_ARRAY(int, pattern_offset, 8);
825 NEW_ARRAY(int, blank_rows, 8);
826 for (;;) {
827 V(int, ch);
828 V(int, pattern_rows) = UBYTE(module[0x386 + 16 + pos]);
829 if (pattern_rows == 0)
830 break;
831 if (pattern_rows >= 0x80) {
832 module_info _ loops[module_info _ songs] = TRUE;
833 break;
835 for (ch = 7; ch >= 0; ch--) {
836 V(int, pat) = UBYTE(module[0x386 + 15 + pos - 2 * ch]);
837 pattern_offset[ch] = UBYTE(module[0x106 + pat]) + (UBYTE(module[0x206 + pat]) << 8) - addr_to_offset;
838 blank_rows[ch] = 0;
840 while (--pattern_rows >= 0) {
841 for (ch = 7; ch >= 0; ch--) {
842 if (--blank_rows[ch] >= 0)
843 continue;
844 for (;;) {
845 V(int, i) = UBYTE(module[pattern_offset[ch]++]);
846 if (i == 0) {
847 pattern_offset[ch]++;
848 break;
850 if (i < 0x40) {
851 if (UBYTE(module[pattern_offset[ch]++]) >= 0x80)
852 pattern_offset[ch]++;
853 break;
855 if (i < 0x80) {
856 pattern_offset[ch]++;
857 break;
859 if (i == 0x80) {
860 blank_rows[ch] = UBYTE(module[pattern_offset[ch]++]);
861 break;
863 if (i < 0xc0)
864 break;
865 if (i < 0xd0) {
866 tempo = i - 0xbf;
867 continue;
869 if (i < 0xe0) {
870 pattern_offset[ch]++;
871 break;
873 if (i < 0xf0) {
874 pattern_offset[ch] += 2;
875 break;
877 if (i < 0xff) {
878 blank_rows[ch] = i - 0xf0;
879 break;
881 blank_rows[ch] = 64;
882 break;
885 player_calls += tempo;
887 pos += 17;
889 set_song_duration(module_info, player_calls);
892 PRIVATE FUNC(abool, parse_tm2, (
893 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
894 P(CONST BYTEARRAY, module), P(int, module_len)))
896 V(int, i);
897 V(int, last_pos);
898 V(int, c);
899 if (module_len < 0x3a4)
900 return FALSE;
901 module_info _ type = ASAP_TYPE_TM2;
902 if (!load_native(ast, module_info, module, module_len, GET_RESOURCE(tm2, obx)))
903 return FALSE;
904 i = module[0x25];
905 if (i < 1 || i > 4)
906 return FALSE;
907 module_info _ fastplay = perframe2fastplay[i - 1];
908 module_info _ player = 0x500;
909 if (module[0x1f] != 0)
910 module_info _ channels = 2;
911 last_pos = 0xffff;
912 for (i = 0; i < 0x80; i++) {
913 V(int, instr_addr) = UBYTE(module[0x86 + i]) + (UBYTE(module[0x306 + i]) << 8);
914 if (instr_addr != 0 && instr_addr < last_pos)
915 last_pos = instr_addr;
917 for (i = 0; i < 0x100; i++) {
918 V(int, pattern_addr) = UBYTE(module[0x106 + i]) + (UBYTE(module[0x206 + i]) << 8);
919 if (pattern_addr != 0 && pattern_addr < last_pos)
920 last_pos = pattern_addr;
922 last_pos -= UWORD(module, 2) + 0x380;
923 if (0x386 + last_pos >= module_len)
924 return FALSE;
925 /* skip trailing stop/jump commands */
926 do {
927 if (last_pos <= 0)
928 return FALSE;
929 last_pos -= 17;
930 c = UBYTE(module[0x386 + 16 + last_pos]);
931 } while (c == 0 || c >= 0x80);
932 module_info _ songs = 0;
933 parse_tm2_song(module_info, module, 0);
934 for (i = 0; i < last_pos && module_info _ songs < ASAP_SONGS_MAX; i += 17) {
935 c = UBYTE(module[0x386 + 16 + i]);
936 if (c == 0 || c >= 0x80)
937 parse_tm2_song(module_info, module, i + 17);
939 return TRUE;
942 #endif /* ASAP_ONLY_SAP */
944 PRIVATE FUNC(abool, has_string_at, (P(CONST BYTEARRAY, module), P(int, module_index), P(STRING, s)))
946 V(int, i);
947 V(int, n) = strlen(s);
948 for (i = 0; i < n; i++)
949 if (module[module_index + i] != CHARCODEAT(s, i))
950 return FALSE;
951 return TRUE;
954 PRIVATE FUNC(STRING, parse_text, (P(OUT_STRING, dest), P(CONST BYTEARRAY, module), P(int, module_index)))
956 V(int, i);
957 if (module[module_index] != CHARCODE('"'))
958 return NULL;
959 if (has_string_at(module, module_index + 1, "<?>\""))
960 return dest;
961 for (i = 0; ; i++) {
962 V(int, c) = module[module_index + 1 + i];
963 if (c == CHARCODE('"'))
964 break;
965 if (c < 32 || c >= 127)
966 return NULL;
968 BYTES_TO_STRING(dest, module, module_index + 1, i);
969 return dest;
972 PRIVATE FUNC(int, parse_dec, (P(CONST BYTEARRAY, module), P(int, module_index), P(int, maxval)))
974 V(int, r);
975 if (module[module_index] == 0xd)
976 return -1;
977 for (r = 0;;) {
978 V(int, c) = module[module_index++];
979 if (c == 0xd)
980 break;
981 if (c < CHARCODE('0') || c > CHARCODE('9'))
982 return -1;
983 r = 10 * r + c - 48;
984 if (r > maxval)
985 return -1;
987 return r;
990 PRIVATE FUNC(int, parse_hex, (P(CONST BYTEARRAY, module), P(int, module_index)))
992 V(int, r);
993 if (module[module_index] == 0xd)
994 return -1;
995 for (r = 0;;) {
996 V(int, c) = module[module_index++];
997 if (c == 0xd)
998 break;
999 if (r > 0xfff)
1000 return -1;
1001 r <<= 4;
1002 if (c >= CHARCODE('0') && c <= CHARCODE('9'))
1003 r += c - CHARCODE('0');
1004 else if (c >= CHARCODE('A') && c <= CHARCODE('F'))
1005 r += c - CHARCODE('A') + 10;
1006 else if (c >= CHARCODE('a') && c <= CHARCODE('f'))
1007 r += c - CHARCODE('a') + 10;
1008 else
1009 return -1;
1011 return r;
1014 FUNC(int, ASAP_ParseDuration, (P(STRING, s)))
1016 V(int, i) = 0;
1017 V(int, r);
1018 V(int, d);
1019 V(int, n) = strlen(s);
1020 #define PARSE_DIGIT(maxdig, retifnot) \
1021 if (i >= n) \
1022 return retifnot; \
1023 d = CHARCODEAT(s, i) - 48; \
1024 if (d < 0 || d > maxdig) \
1025 return -1; \
1026 i++;
1028 PARSE_DIGIT(9, -1);
1029 r = d;
1030 if (i < n) {
1031 d = CHARCODEAT(s, i) - 48;
1032 if (d >= 0 && d <= 9) {
1033 i++;
1034 r = 10 * r + d;
1036 if (i < n && CHARAT(s, i) == ':') {
1037 i++;
1038 PARSE_DIGIT(5, -1);
1039 r = (6 * r + d) * 10;
1040 PARSE_DIGIT(9, -1);
1041 r += d;
1044 r *= 1000;
1045 if (i >= n)
1046 return r;
1047 if (CHARAT(s, i) != '.')
1048 return -1;
1049 i++;
1050 PARSE_DIGIT(9, -1);
1051 r += 100 * d;
1052 PARSE_DIGIT(9, r);
1053 r += 10 * d;
1054 PARSE_DIGIT(9, r);
1055 r += d;
1056 return r;
1059 PRIVATE FUNC(abool, parse_sap_header, (
1060 P(ASAP_ModuleInfo PTR, module_info), P(CONST BYTEARRAY, module), P(int, module_len)))
1062 V(int, module_index);
1063 V(int, type) = 0;
1064 V(int, duration_index) = 0;
1065 if (!has_string_at(module, 0, "SAP\r\n"))
1066 return FALSE;
1067 module_index = 5;
1068 while (UBYTE(module[module_index]) != 0xff) {
1069 if (module_index + 8 >= module_len)
1070 return FALSE;
1071 #define TAG_IS(s) has_string_at(module, module_index, s)
1072 #ifdef C
1073 #define SET_TEXT(v, i) if (parse_text(v, module, module_index + i) == NULL) return FALSE
1074 #else
1075 #define SET_TEXT(v, i) v = parse_text(v, module, module_index + i); if (v == NULL) return FALSE
1076 #endif
1077 #define SET_DEC(v, i, min, max) v = parse_dec(module, module_index + i, max); if (v < min) return FALSE
1078 #define SET_HEX(v, i) v = parse_hex(module, module_index + i)
1079 if (TAG_IS("AUTHOR ")) {
1080 SET_TEXT(module_info _ author, 7);
1082 else if (TAG_IS("NAME ")) {
1083 SET_TEXT(module_info _ name, 5);
1085 else if (TAG_IS("DATE ")) {
1086 SET_TEXT(module_info _ date, 5);
1088 else if (TAG_IS("SONGS ")) {
1089 SET_DEC(module_info _ songs, 6, 1, ASAP_SONGS_MAX);
1091 else if (TAG_IS("DEFSONG ")) {
1092 SET_DEC(module_info _ default_song, 8, 0, ASAP_SONGS_MAX - 1);
1094 else if (TAG_IS("STEREO\r"))
1095 module_info _ channels = 2;
1096 else if (TAG_IS("TIME ")) {
1097 V(int, i);
1098 #ifdef C
1099 char s[ASAP_DURATION_CHARS];
1100 #else
1101 V(STRING, s);
1102 #endif
1103 module_index += 5;
1104 for (i = 0; module[module_index + i] != 0xd; i++);
1105 if (i > 5 && has_string_at(module, module_index + i - 5, " LOOP")) {
1106 module_info _ loops[duration_index] = TRUE;
1107 i -= 5;
1109 #ifdef C
1110 if (i >= ASAP_DURATION_CHARS)
1111 return FALSE;
1112 #endif
1113 BYTES_TO_STRING(s, module, module_index, i);
1114 i = ASAP_ParseDuration(s);
1115 if (i < 0 || duration_index >= ASAP_SONGS_MAX)
1116 return FALSE;
1117 module_info _ durations[duration_index++] = i;
1119 else if (TAG_IS("TYPE "))
1120 type = module[module_index + 5];
1121 else if (TAG_IS("FASTPLAY ")) {
1122 SET_DEC(module_info _ fastplay, 9, 1, 312);
1124 else if (TAG_IS("MUSIC ")) {
1125 SET_HEX(module_info _ music, 6);
1127 else if (TAG_IS("INIT ")) {
1128 SET_HEX(module_info _ init, 5);
1130 else if (TAG_IS("PLAYER ")) {
1131 SET_HEX(module_info _ player, 7);
1133 else if (TAG_IS("COVOX ")) {
1134 SET_HEX(module_info _ covox_addr, 6);
1135 if (module_info _ covox_addr != 0xd600)
1136 return FALSE;
1137 module_info _ channels = 2;
1140 while (module[module_index++] != 0x0d) {
1141 if (module_index >= module_len)
1142 return FALSE;
1144 if (module[module_index++] != 0x0a)
1145 return FALSE;
1147 if (module_info _ default_song >= module_info _ songs)
1148 return FALSE;
1149 switch (type) {
1150 case CHARCODE('B'):
1151 if (module_info _ player < 0 || module_info _ init < 0)
1152 return FALSE;
1153 module_info _ type = ASAP_TYPE_SAP_B;
1154 break;
1155 case CHARCODE('C'):
1156 if (module_info _ player < 0 || module_info _ music < 0)
1157 return FALSE;
1158 module_info _ type = ASAP_TYPE_SAP_C;
1159 break;
1160 case CHARCODE('D'):
1161 if (module_info _ init < 0)
1162 return FALSE;
1163 module_info _ type = ASAP_TYPE_SAP_D;
1164 break;
1165 case CHARCODE('S'):
1166 if (module_info _ init < 0)
1167 return FALSE;
1168 module_info _ type = ASAP_TYPE_SAP_S;
1169 module_info _ fastplay = 78;
1170 break;
1171 default:
1172 return FALSE;
1174 if (UBYTE(module[module_index + 1]) != 0xff)
1175 return FALSE;
1176 module_info _ header_len = module_index;
1177 return TRUE;
1180 PRIVATE FUNC(abool, parse_sap, (
1181 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
1182 P(CONST BYTEARRAY, module), P(int, module_len)))
1184 V(int, module_index);
1185 if (!parse_sap_header(module_info, module, module_len))
1186 return FALSE;
1187 if (ast == NULL)
1188 return TRUE;
1189 ZERO_ARRAY(ast _ memory);
1190 module_index = module_info _ header_len + 2;
1191 while (module_index + 5 <= module_len) {
1192 V(int, start_addr) = UWORD(module, module_index);
1193 V(int, block_len) = UWORD(module, module_index + 2) + 1 - start_addr;
1194 if (block_len <= 0 || module_index + block_len > module_len)
1195 return FALSE;
1196 module_index += 4;
1197 COPY_ARRAY(ast _ memory, start_addr, module, module_index, block_len);
1198 module_index += block_len;
1199 if (module_index == module_len)
1200 return TRUE;
1201 if (module_index + 7 <= module_len
1202 && UBYTE(module[module_index]) == 0xff && UBYTE(module[module_index + 1]) == 0xff)
1203 module_index += 2;
1205 return FALSE;
1208 #define ASAP_EXT(c1, c2, c3) ((CHARCODE(c1) + (CHARCODE(c2) << 8) + (CHARCODE(c3) << 16)) | 0x202020)
1210 PRIVATE FUNC(int, get_packed_ext, (P(STRING, filename)))
1212 V(int, i) = strlen(filename);
1213 V(int, ext) = 0;
1214 while (--i > 0) {
1215 V(char, c) = CHARAT(filename, i);
1216 if (c <= ' ' || c > 'z')
1217 return 0;
1218 if (c == '.')
1219 return ext | 0x202020;
1220 ext = (ext << 8) + CHARCODE(c);
1222 return 0;
1225 PRIVATE FUNC(abool, is_our_ext, (P(int, ext)))
1227 switch (ext) {
1228 case ASAP_EXT('S', 'A', 'P'):
1229 #ifndef ASAP_ONLY_SAP
1230 case ASAP_EXT('C', 'M', 'C'):
1231 case ASAP_EXT('C', 'M', '3'):
1232 case ASAP_EXT('C', 'M', 'R'):
1233 case ASAP_EXT('C', 'M', 'S'):
1234 case ASAP_EXT('D', 'M', 'C'):
1235 case ASAP_EXT('D', 'L', 'T'):
1236 case ASAP_EXT('M', 'P', 'T'):
1237 case ASAP_EXT('M', 'P', 'D'):
1238 case ASAP_EXT('R', 'M', 'T'):
1239 case ASAP_EXT('T', 'M', 'C'):
1240 case ASAP_EXT('T', 'M', '8'):
1241 case ASAP_EXT('T', 'M', '2'):
1242 #endif
1243 return TRUE;
1244 default:
1245 return FALSE;
1249 FUNC(abool, ASAP_IsOurFile, (P(STRING, filename)))
1251 V(int, ext) = get_packed_ext(filename);
1252 return is_our_ext(ext);
1255 FUNC(abool, ASAP_IsOurExt, (P(STRING, ext)))
1257 return strlen(ext) == 3
1258 && is_our_ext(ASAP_EXT(CHARAT(ext, 0), CHARAT(ext, 1), CHARAT(ext, 2)));
1261 PRIVATE FUNC(abool, parse_file, (
1262 P(ASAP_State PTR, ast), P(ASAP_ModuleInfo PTR, module_info),
1263 P(STRING, filename), P(CONST BYTEARRAY, module), P(int, module_len)))
1265 V(int, i);
1266 V(int, len) = strlen(filename);
1267 V(int, basename) = 0;
1268 V(int, ext) = -1;
1269 for (i = 0; i < len; i++) {
1270 V(char, c) = CHARAT(filename, i);
1271 if (c == '/' || c == '\\') {
1272 basename = i + 1;
1273 ext = -1;
1275 else if (c == '.')
1276 ext = i;
1278 if (ext < 0)
1279 return FALSE;
1280 EMPTY_STRING(module_info _ author);
1281 SUBSTRING(module_info _ name, filename, basename, ext - basename);
1282 EMPTY_STRING(module_info _ date);
1283 module_info _ channels = 1;
1284 module_info _ songs = 1;
1285 module_info _ default_song = 0;
1286 for (i = 0; i < ASAP_SONGS_MAX; i++) {
1287 module_info _ durations[i] = -1;
1288 module_info _ loops[i] = FALSE;
1290 module_info _ fastplay = 312;
1291 module_info _ music = -1;
1292 module_info _ init = -1;
1293 module_info _ player = -1;
1294 module_info _ covox_addr = -1;
1295 switch (get_packed_ext(filename)) {
1296 case ASAP_EXT('S', 'A', 'P'):
1297 return parse_sap(ast, module_info, module, module_len);
1298 #ifndef ASAP_ONLY_SAP
1299 case ASAP_EXT('C', 'M', 'C'):
1300 return parse_cmc(ast, module_info, module, module_len, ASAP_TYPE_CMC, GET_RESOURCE(cmc, obx));
1301 case ASAP_EXT('C', 'M', '3'):
1302 return parse_cmc(ast, module_info, module, module_len, ASAP_TYPE_CM3, GET_RESOURCE(cm3, obx));
1303 case ASAP_EXT('C', 'M', 'R'):
1304 return parse_cmc(ast, module_info, module, module_len, ASAP_TYPE_CMR, GET_RESOURCE(cmc, obx));
1305 case ASAP_EXT('C', 'M', 'S'):
1306 module_info _ channels = 2;
1307 return parse_cmc(ast, module_info, module, module_len, ASAP_TYPE_CMS, GET_RESOURCE(cms, obx));
1308 case ASAP_EXT('D', 'M', 'C'):
1309 module_info _ fastplay = 156;
1310 return parse_cmc(ast, module_info, module, module_len, ASAP_TYPE_CMC, GET_RESOURCE(cmc, obx));
1311 case ASAP_EXT('D', 'L', 'T'):
1312 return parse_dlt(ast, module_info, module, module_len);
1313 case ASAP_EXT('M', 'P', 'T'):
1314 return parse_mpt(ast, module_info, module, module_len);
1315 case ASAP_EXT('M', 'P', 'D'):
1316 module_info _ fastplay = 156;
1317 return parse_mpt(ast, module_info, module, module_len);
1318 case ASAP_EXT('R', 'M', 'T'):
1319 return parse_rmt(ast, module_info, module, module_len);
1320 case ASAP_EXT('T', 'M', 'C'):
1321 case ASAP_EXT('T', 'M', '8'):
1322 return parse_tmc(ast, module_info, module, module_len);
1323 case ASAP_EXT('T', 'M', '2'):
1324 return parse_tm2(ast, module_info, module, module_len);
1325 #endif
1326 default:
1327 return FALSE;
1331 FUNC(abool, ASAP_GetModuleInfo, (
1332 P(ASAP_ModuleInfo PTR, module_info), P(STRING, filename),
1333 P(CONST BYTEARRAY, module), P(int, module_len)))
1335 return parse_file(NULL, module_info, filename, module, module_len);
1338 FUNC(abool, ASAP_Load, (
1339 P(ASAP_State PTR, ast), P(STRING, filename),
1340 P(CONST BYTEARRAY, module), P(int, module_len)))
1342 ast _ silence_cycles = 0;
1343 return parse_file(ast, ADDRESSOF ast _ module_info, filename, module, module_len);
1346 FUNC(void, ASAP_DetectSilence, (P(ASAP_State PTR, ast), P(int, seconds)))
1348 ast _ silence_cycles = seconds * ASAP_MAIN_CLOCK;
1351 PRIVATE FUNC(void, call_6502, (P(ASAP_State PTR, ast), P(int, addr), P(int, max_scanlines)))
1353 ast _ cpu_pc = addr;
1354 /* put a CIM at 0xd20a and a return address on stack */
1355 dPutByte(0xd20a, 0xd2);
1356 dPutByte(0x01fe, 0x09);
1357 dPutByte(0x01ff, 0xd2);
1358 ast _ cpu_s = 0xfd;
1359 Cpu_RunScanlines(ast, max_scanlines);
1362 /* 50 Atari frames for the initialization routine - some SAPs are self-extracting. */
1363 #define SCANLINES_FOR_INIT (50 * 312)
1365 PRIVATE FUNC(void, call_6502_init, (P(ASAP_State PTR, ast), P(int, addr), P(int, a), P(int, x), P(int, y)))
1367 ast _ cpu_a = a & 0xff;
1368 ast _ cpu_x = x & 0xff;
1369 ast _ cpu_y = y & 0xff;
1370 call_6502(ast, addr, SCANLINES_FOR_INIT);
1373 FUNC(void, ASAP_PlaySong, (P(ASAP_State PTR, ast), P(int, song), P(int, duration)))
1375 ast _ current_song = song;
1376 ast _ current_duration = duration;
1377 ast _ blocks_played = 0;
1378 ast _ silence_cycles_counter = ast _ silence_cycles;
1379 ast _ extra_pokey_mask = ast _ module_info.channels > 1 ? 0x10 : 0;
1380 ast _ consol = 8;
1381 ast _ covox[0] = CAST(byte) 0x80;
1382 ast _ covox[1] = CAST(byte) 0x80;
1383 ast _ covox[2] = CAST(byte) 0x80;
1384 ast _ covox[3] = CAST(byte) 0x80;
1385 PokeySound_Initialize(ast);
1386 ast _ cycle = 0;
1387 ast _ cpu_nz = 0;
1388 ast _ cpu_c = 0;
1389 ast _ cpu_vdi = 0;
1390 ast _ scanline_number = 0;
1391 ast _ next_scanline_cycle = 0;
1392 ast _ timer1_cycle = NEVER;
1393 ast _ timer2_cycle = NEVER;
1394 ast _ timer4_cycle = NEVER;
1395 ast _ irqst = 0xff;
1396 switch (ast _ module_info.type) {
1397 case ASAP_TYPE_SAP_B:
1398 call_6502_init(ast, ast _ module_info.init, song, 0, 0);
1399 break;
1400 case ASAP_TYPE_SAP_C:
1401 #ifndef ASAP_ONLY_SAP
1402 case ASAP_TYPE_CMC:
1403 case ASAP_TYPE_CM3:
1404 case ASAP_TYPE_CMR:
1405 case ASAP_TYPE_CMS:
1406 #endif
1407 call_6502_init(ast, ast _ module_info.player + 3, 0x70, ast _ module_info.music, ast _ module_info.music >> 8);
1408 call_6502_init(ast, ast _ module_info.player + 3, 0x00, song, 0);
1409 break;
1410 case ASAP_TYPE_SAP_D:
1411 case ASAP_TYPE_SAP_S:
1412 ast _ cpu_a = song;
1413 ast _ cpu_x = 0x00;
1414 ast _ cpu_y = 0x00;
1415 ast _ cpu_s = 0xff;
1416 ast _ cpu_pc = ast _ module_info.init;
1417 break;
1418 #ifndef ASAP_ONLY_SAP
1419 case ASAP_TYPE_DLT:
1420 call_6502_init(ast, ast _ module_info.player + 0x100, 0x00, 0x00, ast _ module_info.song_pos[song]);
1421 break;
1422 case ASAP_TYPE_MPT:
1423 call_6502_init(ast, ast _ module_info.player, 0x00, ast _ module_info.music >> 8, ast _ module_info.music);
1424 call_6502_init(ast, ast _ module_info.player, 0x02, ast _ module_info.song_pos[song], 0);
1425 break;
1426 case ASAP_TYPE_RMT:
1427 call_6502_init(ast, ast _ module_info.player, ast _ module_info.song_pos[song], ast _ module_info.music, ast _ module_info.music >> 8);
1428 break;
1429 case ASAP_TYPE_TMC:
1430 case ASAP_TYPE_TM2:
1431 call_6502_init(ast, ast _ module_info.player, 0x70, ast _ module_info.music >> 8, ast _ module_info.music);
1432 call_6502_init(ast, ast _ module_info.player, 0x00, song, 0);
1433 ast _ tmc_per_frame_counter = 1;
1434 break;
1435 #endif
1437 ASAP_MutePokeyChannels(ast, 0);
1440 FUNC(void, ASAP_MutePokeyChannels, (P(ASAP_State PTR, ast), P(int, mask)))
1442 PokeySound_Mute(ast, ADDRESSOF ast _ base_pokey, mask);
1443 PokeySound_Mute(ast, ADDRESSOF ast _ extra_pokey, mask >> 4);
1446 FUNC(abool, call_6502_player, (P(ASAP_State PTR, ast)))
1448 V(int, player) = ast _ module_info.player;
1449 PokeySound_StartFrame(ast);
1450 switch (ast _ module_info.type) {
1451 case ASAP_TYPE_SAP_B:
1452 call_6502(ast, player, ast _ module_info.fastplay);
1453 break;
1454 case ASAP_TYPE_SAP_C:
1455 #ifndef ASAP_ONLY_SAP
1456 case ASAP_TYPE_CMC:
1457 case ASAP_TYPE_CM3:
1458 case ASAP_TYPE_CMR:
1459 case ASAP_TYPE_CMS:
1460 #endif
1461 call_6502(ast, player + 6, ast _ module_info.fastplay);
1462 break;
1463 case ASAP_TYPE_SAP_D:
1464 if (player >= 0) {
1465 V(int, s)= ast _ cpu_s;
1466 #define PUSH_ON_6502_STACK(x) dPutByte(0x100 + s, x); s = (s - 1) & 0xff
1467 #define RETURN_FROM_PLAYER_ADDR 0xd200
1468 /* save 6502 state on 6502 stack */
1469 PUSH_ON_6502_STACK(ast _ cpu_pc >> 8);
1470 PUSH_ON_6502_STACK(ast _ cpu_pc & 0xff);
1471 PUSH_ON_6502_STACK(((ast _ cpu_nz | (ast _ cpu_nz >> 1)) & 0x80) + ast _ cpu_vdi + \
1472 ((ast _ cpu_nz & 0xff) == 0 ? Z_FLAG : 0) + ast _ cpu_c + 0x20);
1473 PUSH_ON_6502_STACK(ast _ cpu_a);
1474 PUSH_ON_6502_STACK(ast _ cpu_x);
1475 PUSH_ON_6502_STACK(ast _ cpu_y);
1476 /* RTS will jump to 6502 code that restores the state */
1477 PUSH_ON_6502_STACK((RETURN_FROM_PLAYER_ADDR - 1) >> 8);
1478 PUSH_ON_6502_STACK((RETURN_FROM_PLAYER_ADDR - 1) & 0xff);
1479 ast _ cpu_s = s;
1480 dPutByte(RETURN_FROM_PLAYER_ADDR, 0x68); /* PLA */
1481 dPutByte(RETURN_FROM_PLAYER_ADDR + 1, 0xa8); /* TAY */
1482 dPutByte(RETURN_FROM_PLAYER_ADDR + 2, 0x68); /* PLA */
1483 dPutByte(RETURN_FROM_PLAYER_ADDR + 3, 0xaa); /* TAX */
1484 dPutByte(RETURN_FROM_PLAYER_ADDR + 4, 0x68); /* PLA */
1485 dPutByte(RETURN_FROM_PLAYER_ADDR + 5, 0x40); /* RTI */
1486 ast _ cpu_pc = player;
1488 Cpu_RunScanlines(ast, ast _ module_info.fastplay);
1489 break;
1490 case ASAP_TYPE_SAP_S:
1491 Cpu_RunScanlines(ast, ast _ module_info.fastplay);
1493 V(int, i) = dGetByte(0x45) - 1;
1494 dPutByte(0x45, i);
1495 if (i == 0)
1496 dPutByte(0xb07b, dGetByte(0xb07b) + 1);
1498 break;
1499 #ifndef ASAP_ONLY_SAP
1500 case ASAP_TYPE_DLT:
1501 call_6502(ast, player + 0x103, ast _ module_info.fastplay);
1502 break;
1503 case ASAP_TYPE_MPT:
1504 case ASAP_TYPE_RMT:
1505 case ASAP_TYPE_TM2:
1506 call_6502(ast, player + 3, ast _ module_info.fastplay);
1507 break;
1508 case ASAP_TYPE_TMC:
1509 if (--ast _ tmc_per_frame_counter <= 0) {
1510 ast _ tmc_per_frame_counter = ast _ tmc_per_frame;
1511 call_6502(ast, player + 3, ast _ module_info.fastplay);
1513 else
1514 call_6502(ast, player + 6, ast _ module_info.fastplay);
1515 break;
1516 #endif
1518 PokeySound_EndFrame(ast, ast _ module_info.fastplay * 114);
1519 if (ast _ silence_cycles > 0) {
1520 if (PokeySound_IsSilent(ADDRESSOF ast _ base_pokey)
1521 && PokeySound_IsSilent(ADDRESSOF ast _ extra_pokey)) {
1522 ast _ silence_cycles_counter -= ast _ module_info.fastplay * 114;
1523 if (ast _ silence_cycles_counter <= 0)
1524 return FALSE;
1526 else
1527 ast _ silence_cycles_counter = ast _ silence_cycles;
1529 return TRUE;
1532 FUNC(int, ASAP_GetPosition, (P(CONST ASAP_State PTR, ast)))
1534 return ast _ blocks_played * 10 / (ASAP_SAMPLE_RATE / 100);
1537 FUNC(int, milliseconds_to_blocks, (P(int, milliseconds)))
1539 return milliseconds * (ASAP_SAMPLE_RATE / 100) / 10;
1542 #ifndef ACTIONSCRIPT
1544 FUNC(void, ASAP_Seek, (P(ASAP_State PTR, ast), P(int, position)))
1546 V(int, block) = milliseconds_to_blocks(position);
1547 if (block < ast _ blocks_played)
1548 ASAP_PlaySong(ast, ast _ current_song, ast _ current_duration);
1549 while (ast _ blocks_played + ast _ samples - ast _ sample_index < block) {
1550 ast _ blocks_played += ast _ samples - ast _ sample_index;
1551 call_6502_player(ast);
1553 ast _ sample_index += block - ast _ blocks_played;
1554 ast _ blocks_played = block;
1557 PRIVATE FUNC(void, serialize_int, (P(BYTEARRAY, buffer), P(int, offset), P(int, value)))
1559 buffer[offset] = TO_BYTE(value);
1560 buffer[offset + 1] = TO_BYTE(value >> 8);
1561 buffer[offset + 2] = TO_BYTE(value >> 16);
1562 buffer[offset + 3] = TO_BYTE(value >> 24);
1565 FUNC(void, ASAP_GetWavHeaderForPart, (
1566 P(CONST ASAP_State PTR, ast), P(BYTEARRAY, buffer),
1567 P(ASAP_SampleFormat, format), P(int, blocks)))
1569 V(int, use_16bit) = format != ASAP_FORMAT_U8 ? 1 : 0;
1570 V(int, block_size) = ast _ module_info.channels << use_16bit;
1571 V(int, bytes_per_second) = ASAP_SAMPLE_RATE * block_size;
1572 V(int, remaining_blocks) = milliseconds_to_blocks(ast _ current_duration) - ast _ blocks_played;
1573 V(int, n_bytes);
1574 if (blocks > remaining_blocks)
1575 blocks = remaining_blocks;
1576 n_bytes = blocks * block_size;
1577 buffer[0] = CAST(byte) CHARCODE('R');
1578 buffer[1] = CAST(byte) CHARCODE('I');
1579 buffer[2] = CAST(byte) CHARCODE('F');
1580 buffer[3] = CAST(byte) CHARCODE('F');
1581 serialize_int(buffer, 4, n_bytes + 36);
1582 buffer[8] = CAST(byte) CHARCODE('W');
1583 buffer[9] = CAST(byte) CHARCODE('A');
1584 buffer[10] = CAST(byte) CHARCODE('V');
1585 buffer[11] = CAST(byte) CHARCODE('E');
1586 buffer[12] = CAST(byte) CHARCODE('f');
1587 buffer[13] = CAST(byte) CHARCODE('m');
1588 buffer[14] = CAST(byte) CHARCODE('t');
1589 buffer[15] = CAST(byte) CHARCODE(' ');
1590 buffer[16] = 16;
1591 buffer[17] = 0;
1592 buffer[18] = 0;
1593 buffer[19] = 0;
1594 buffer[20] = 1;
1595 buffer[21] = 0;
1596 buffer[22] = CAST(byte) ast _ module_info.channels;
1597 buffer[23] = 0;
1598 serialize_int(buffer, 24, ASAP_SAMPLE_RATE);
1599 serialize_int(buffer, 28, bytes_per_second);
1600 buffer[32] = CAST(byte) block_size;
1601 buffer[33] = 0;
1602 buffer[34] = CAST(byte) (8 << use_16bit);
1603 buffer[35] = 0;
1604 buffer[36] = CAST(byte) CHARCODE('d');
1605 buffer[37] = CAST(byte) CHARCODE('a');
1606 buffer[38] = CAST(byte) CHARCODE('t');
1607 buffer[39] = CAST(byte) CHARCODE('a');
1608 serialize_int(buffer, 40, n_bytes);
1611 FUNC(void, ASAP_GetWavHeader, (
1612 P(CONST ASAP_State PTR, ast), P(BYTEARRAY, buffer), P(ASAP_SampleFormat, format)))
1614 V(int, remaining_blocks) = milliseconds_to_blocks(ast _ current_duration) - ast _ blocks_played;
1615 ASAP_GetWavHeaderForPart(ast, buffer, format, remaining_blocks);
1618 #endif /* ACTIONSCRIPT */
1620 PRIVATE FUNC(int, ASAP_GenerateAt, (P(ASAP_State PTR, ast), P(VOIDPTR, buffer), P(int, buffer_offset), P(int, buffer_len), P(ASAP_SampleFormat, format)))
1622 V(int, block_shift);
1623 V(int, buffer_blocks);
1624 V(int, block);
1625 if (ast _ silence_cycles > 0 && ast _ silence_cycles_counter <= 0)
1626 return 0;
1627 #ifdef ACTIONSCRIPT
1628 block_shift = 0;
1629 #else
1630 block_shift = (ast _ module_info.channels - 1) + (format != ASAP_FORMAT_U8 ? 1 : 0);
1631 #endif
1632 buffer_blocks = buffer_len >> block_shift;
1633 if (ast _ current_duration > 0) {
1634 V(int, total_blocks) = milliseconds_to_blocks(ast _ current_duration);
1635 if (buffer_blocks > total_blocks - ast _ blocks_played)
1636 buffer_blocks = total_blocks - ast _ blocks_played;
1638 block = 0;
1639 do {
1640 V(int, blocks) = PokeySound_Generate(ast, CAST(BYTEARRAY) buffer,
1641 buffer_offset + (block << block_shift), buffer_blocks - block, format);
1642 ast _ blocks_played += blocks;
1643 block += blocks;
1644 } while (block < buffer_blocks && call_6502_player(ast));
1645 return block << block_shift;
1648 FUNC(int, ASAP_Generate, (P(ASAP_State PTR, ast), P(VOIDPTR, buffer), P(int, buffer_len), P(ASAP_SampleFormat, format)))
1650 return ASAP_GenerateAt(ast, buffer, 0, buffer_len, format);
1653 #if defined(C) && !defined(ASAP_ONLY_SAP)
1655 abool ASAP_ChangeExt(char *filename, const char *ext)
1657 char *dest = NULL;
1658 while (*filename != '\0') {
1659 if (*filename == '/' || *filename == '\\')
1660 dest = NULL;
1661 else if (*filename == '.')
1662 dest = filename + 1;
1663 filename++;
1665 if (dest == NULL)
1666 return FALSE;
1667 strcpy(dest, ext);
1668 return TRUE;
1671 abool ASAP_CanSetModuleInfo(const char *filename)
1673 int ext = get_packed_ext(filename);
1674 return ext == ASAP_EXT('S', 'A', 'P');
1677 static byte *put_string(byte *dest, const char *str)
1679 while (*str != '\0')
1680 *dest++ = *str++;
1681 return dest;
1684 static byte *put_dec(byte *dest, int value)
1686 if (value >= 10) {
1687 dest = put_dec(dest, value / 10);
1688 value %= 10;
1690 *dest++ = '0' + value;
1691 return dest;
1694 static byte *put_text_tag(byte *dest, const char *tag, const char *value)
1696 dest = put_string(dest, tag);
1697 *dest++ = '"';
1698 if (*value == '\0')
1699 value = "<?>";
1700 while (*value != '\0') {
1701 if (*value < ' ' || *value > 'z' || *value == '"' || *value == '`')
1702 return NULL;
1703 *dest++ = *value++;
1705 *dest++ = '"';
1706 *dest++ = '\r';
1707 *dest++ = '\n';
1708 return dest;
1711 static byte *put_dec_tag(byte *dest, const char *tag, int value)
1713 dest = put_string(dest, tag);
1714 dest = put_dec(dest, value);
1715 *dest++ = '\r';
1716 *dest++ = '\n';
1717 return dest;
1720 static byte *put_hex_tag(byte *dest, const char *tag, int value)
1722 int i;
1723 if (value < 0)
1724 return dest;
1725 dest = put_string(dest, tag);
1726 for (i = 12; i >= 0; i -= 4) {
1727 int digit = (value >> i) & 0xf;
1728 *dest++ = (byte) (digit + (digit < 10 ? '0' : 'A' - 10));
1730 *dest++ = '\r';
1731 *dest++ = '\n';
1732 return dest;
1735 static byte *start_sap_header(byte *dest, const ASAP_ModuleInfo *module_info)
1737 dest = put_string(dest, "SAP\r\n");
1738 dest = put_text_tag(dest, "AUTHOR ", module_info->author);
1739 if (dest == NULL)
1740 return NULL;
1741 dest = put_text_tag(dest, "NAME ", module_info->name);
1742 if (dest == NULL)
1743 return NULL;
1744 dest = put_text_tag(dest, "DATE ", module_info->date);
1745 if (dest == NULL)
1746 return NULL;
1747 if (module_info->songs > 1) {
1748 dest = put_dec_tag(dest, "SONGS ", module_info->songs);
1749 if (module_info->default_song > 0)
1750 dest = put_dec_tag(dest, "DEFSONG ", module_info->default_song);
1752 if (module_info->channels > 1)
1753 dest = put_string(dest, "STEREO\r\n");
1754 return dest;
1757 static char *two_digits(char *s, int x)
1759 s[0] = '0' + x / 10;
1760 s[1] = '0' + x % 10;
1761 return s + 2;
1764 void ASAP_DurationToString(char *s, int duration)
1766 if (duration >= 0 && duration < 100 * 60 * 1000) {
1767 int seconds = duration / 1000;
1768 s = two_digits(s, seconds / 60);
1769 *s++ = ':';
1770 s = two_digits(s, seconds % 60);
1771 duration %= 1000;
1772 if (duration != 0) {
1773 *s++ = '.';
1774 s = two_digits(s, duration / 10);
1775 duration %= 10;
1776 if (duration != 0)
1777 *s++ = '0' + duration;
1780 *s = '\0';
1783 static byte *put_durations(byte *dest, const ASAP_ModuleInfo *module_info)
1785 int song;
1786 for (song = 0; song < module_info->songs; song++) {
1787 if (module_info->durations[song] < 0)
1788 break;
1789 dest = put_string(dest, "TIME ");
1790 ASAP_DurationToString((char *) dest, module_info->durations[song]);
1791 while (*dest != '\0')
1792 dest++;
1793 if (module_info->loops[song])
1794 dest = put_string(dest, " LOOP");
1795 *dest++ = '\r';
1796 *dest++ = '\n';
1798 return dest;
1801 static byte *put_sap_header(byte *dest, const ASAP_ModuleInfo *module_info, char type, int music, int init, int player)
1803 dest = start_sap_header(dest, module_info);
1804 if (dest == NULL)
1805 return NULL;
1806 dest = put_string(dest, "TYPE ");
1807 *dest++ = type;
1808 *dest++ = '\r';
1809 *dest++ = '\n';
1810 if (module_info->fastplay != 312)
1811 dest = put_dec_tag(dest, "FASTPLAY ", module_info->fastplay);
1812 dest = put_hex_tag(dest, "MUSIC ", music);
1813 dest = put_hex_tag(dest, "INIT ", init);
1814 dest = put_hex_tag(dest, "PLAYER ", player);
1815 dest = put_durations(dest, module_info);
1816 return dest;
1819 int ASAP_SetModuleInfo(const ASAP_ModuleInfo *module_info, const BYTEARRAY module, int module_len, BYTEARRAY out_module)
1821 byte *dest;
1822 int i;
1823 if (memcmp(module, "SAP\r\n", 5) != 0)
1824 return -1;
1825 dest = start_sap_header(out_module, module_info);
1826 if (dest == NULL)
1827 return -1;
1828 i = 5;
1829 while (i < module_len && module[i] != 0xff) {
1830 if (memcmp(module + i, "AUTHOR ", 7) == 0
1831 || memcmp(module + i, "NAME ", 5) == 0
1832 || memcmp(module + i, "DATE ", 5) == 0
1833 || memcmp(module + i, "SONGS ", 6) == 0
1834 || memcmp(module + i, "DEFSONG ", 8) == 0
1835 || memcmp(module + i, "STEREO\r", 7) == 0
1836 || memcmp(module + i, "TIME ", 5) == 0) {
1837 while (i < module_len && module[i++] != 0x0a);
1839 else {
1840 int b;
1841 do {
1842 b = module[i++];
1843 *dest++ = b;
1844 } while (i < module_len && b != 0x0a);
1847 dest = put_durations(dest, module_info);
1848 module_len -= i;
1849 memcpy(dest, module + i, module_len);
1850 dest += module_len;
1851 return dest - out_module;
1854 #define RMT_INIT 0x0c80
1855 #define TM2_INIT 0x1080
1857 const char *ASAP_CanConvert(
1858 const char *filename, const ASAP_ModuleInfo *module_info,
1859 const BYTEARRAY module, int module_len)
1861 (void) filename;
1862 switch (module_info->type) {
1863 case ASAP_TYPE_SAP_B:
1864 if ((module_info->init == 0x3fb || module_info->init == 0x3f9) && module_info->player == 0x503)
1865 return "dlt";
1866 if (module_info->init == 0x4f3 || module_info->init == 0xf4f3 || module_info->init == 0x4ef)
1867 return module_info->fastplay == 156 ? "mpd" : "mpt";
1868 if (module_info->init == RMT_INIT)
1869 return "rmt";
1870 if ((module_info->init == 0x4f5 || module_info->init == 0xf4f5 || module_info->init == 0x4f2)
1871 || ((module_info->init == 0x4e7 || module_info->init == 0xf4e7 || module_info->init == 0x4e4) && module_info->fastplay == 156)
1872 || ((module_info->init == 0x4e5 || module_info->init == 0xf4e5 || module_info->init == 0x4e2) && (module_info->fastplay == 104 || module_info->fastplay == 78)))
1873 return "tmc";
1874 if (module_info->init == TM2_INIT)
1875 return "tm2";
1876 break;
1877 case ASAP_TYPE_SAP_C:
1878 if (module_info->player == 0x500 || module_info->player == 0xf500) {
1879 if (module_info->fastplay == 156)
1880 return "dmc";
1881 if (module_info->channels > 1)
1882 return "cms";
1883 if (module[module_len - 170] == 0x1e)
1884 return "cmr";
1885 if (module[module_len - 909] == 0x30)
1886 return "cm3";
1887 return "cmc";
1889 break;
1890 case ASAP_TYPE_CMC:
1891 case ASAP_TYPE_CM3:
1892 case ASAP_TYPE_CMR:
1893 case ASAP_TYPE_CMS:
1894 case ASAP_TYPE_DLT:
1895 case ASAP_TYPE_MPT:
1896 case ASAP_TYPE_RMT:
1897 case ASAP_TYPE_TMC:
1898 case ASAP_TYPE_TM2:
1899 return "sap";
1900 default:
1901 break;
1903 return NULL;
1906 int ASAP_Convert(
1907 const char *filename, const ASAP_ModuleInfo *module_info,
1908 const BYTEARRAY module, int module_len, BYTEARRAY out_module)
1910 (void) filename;
1911 int out_len;
1912 byte *dest;
1913 int addr;
1914 int player;
1915 static const int tmc_player[4] = { 3, -9, -10, -10 };
1916 static const int tmc_init[4] = { -14, -16, -17, -17 };
1917 switch (module_info->type) {
1918 case ASAP_TYPE_SAP_B:
1919 case ASAP_TYPE_SAP_C:
1920 out_len = UWORD(module, module_info->header_len + 4) - UWORD(module, module_info->header_len + 2) + 7;
1921 if (out_len < 7 || module_info->header_len + out_len >= module_len)
1922 return -1;
1923 memcpy(out_module, module + module_info->header_len, out_len);
1924 return out_len;
1925 case ASAP_TYPE_CMC:
1926 case ASAP_TYPE_CM3:
1927 case ASAP_TYPE_CMR:
1928 case ASAP_TYPE_CMS:
1929 dest = put_sap_header(out_module, module_info, 'C', module_info->music, -1, module_info->player);
1930 if (dest == NULL)
1931 return -1;
1932 memcpy(dest, module, module_len);
1933 dest[0] = 0xff; /* some modules start with zeros */
1934 dest[1] = 0xff;
1935 dest += module_len;
1936 if (module_info->type == ASAP_TYPE_CM3) {
1937 memcpy(dest, cm3_obx + 2, sizeof(cm3_obx) - 2);
1938 dest += sizeof(cm3_obx) - 2;
1940 else if (module_info->type == ASAP_TYPE_CMS) {
1941 memcpy(dest, cms_obx + 2, sizeof(cms_obx) - 2);
1942 dest += sizeof(cms_obx) - 2;
1944 else {
1945 memcpy(dest, cmc_obx + 2, sizeof(cmc_obx) - 2);
1946 if (module_info->type == ASAP_TYPE_CMR)
1947 memcpy(dest + 4 + CMR_BASS_TABLE_OFFSET, cmr_bass_table, sizeof(cmr_bass_table));
1948 dest += sizeof(cmc_obx) - 2;
1950 return dest - out_module;
1951 case ASAP_TYPE_DLT:
1952 if (module_info->songs != 1) {
1953 addr = module_info->player - 7 - module_info->songs;
1954 dest = put_sap_header(out_module, module_info, 'B', -1, module_info->player - 7, module_info->player + 0x103);
1956 else {
1957 addr = module_info->player - 5;
1958 dest = put_sap_header(out_module, module_info, 'B', -1, addr, module_info->player + 0x103);
1960 if (dest == NULL)
1961 return -1;
1962 memcpy(dest, module, module_len);
1963 if (module_len == 0x2c06) {
1964 dest[4] = 0;
1965 dest[5] = 0x4c;
1966 dest[0x2c06] = 0;
1968 dest += 0x2c07;
1969 *dest++ = (byte) addr;
1970 *dest++ = (byte) (addr >> 8);
1971 *dest++ = dlt_obx[4];
1972 *dest++ = dlt_obx[5];
1973 if (module_info->songs != 1) {
1974 memcpy(dest, module_info->song_pos, module_info->songs);
1975 dest += module_info->songs;
1976 *dest++ = 0xaa; /* tax */
1977 *dest++ = 0xbc; /* ldy song2pos,x */
1978 *dest++ = (byte) addr;
1979 *dest++ = (byte) (addr >> 8);
1981 else {
1982 *dest++ = 0xa0; /* ldy #0 */
1983 *dest++ = 0;
1985 *dest++ = 0x4c; /* jmp init */
1986 *dest++ = (byte) module_info->player;
1987 *dest++ = (byte) ((module_info->player >> 8) + 1);
1988 memcpy(dest, dlt_obx + 6, sizeof(dlt_obx) - 6);
1989 dest += sizeof(dlt_obx) - 6;
1990 return dest - out_module;
1991 case ASAP_TYPE_MPT:
1992 if (module_info->songs != 1) {
1993 addr = module_info->player - 17 - module_info->songs;
1994 dest = put_sap_header(out_module, module_info, 'B', -1, module_info->player - 17, module_info->player + 3);
1996 else {
1997 addr = module_info->player - 13;
1998 dest = put_sap_header(out_module, module_info, 'B', -1, addr, module_info->player + 3);
2000 if (dest == NULL)
2001 return -1;
2002 memcpy(dest, module, module_len);
2003 dest += module_len;
2004 *dest++ = (byte) addr;
2005 *dest++ = (byte) (addr >> 8);
2006 *dest++ = mpt_obx[4];
2007 *dest++ = mpt_obx[5];
2008 if (module_info->songs != 1) {
2009 memcpy(dest, module_info->song_pos, module_info->songs);
2010 dest += module_info->songs;
2011 *dest++ = 0x48; /* pha */
2013 *dest++ = 0xa0; /* ldy #<music */
2014 *dest++ = (byte) module_info->music;
2015 *dest++ = 0xa2; /* ldx #>music */
2016 *dest++ = (byte) (module_info->music >> 8);
2017 *dest++ = 0xa9; /* lda #0 */
2018 *dest++ = 0;
2019 *dest++ = 0x20; /* jsr player */
2020 *dest++ = (byte) module_info->player;
2021 *dest++ = (byte) (module_info->player >> 8);
2022 if (module_info->songs != 1) {
2023 *dest++ = 0x68; /* pla */
2024 *dest++ = 0xa8; /* tay */
2025 *dest++ = 0xbe; /* ldx song2pos,y */
2026 *dest++ = (byte) addr;
2027 *dest++ = (byte) (addr >> 8);
2029 else {
2030 *dest++ = 0xa2; /* ldx #0 */
2031 *dest++ = 0;
2033 *dest++ = 0xa9; /* lda #2 */
2034 *dest++ = 2;
2035 memcpy(dest, mpt_obx + 6, sizeof(mpt_obx) - 6);
2036 dest += sizeof(mpt_obx) - 6;
2037 return dest - out_module;
2038 case ASAP_TYPE_RMT:
2039 dest = put_sap_header(out_module, module_info, 'B', -1, RMT_INIT, module_info->player + 3);
2040 if (dest == NULL)
2041 return -1;
2042 memcpy(dest, module, module_len);
2043 dest += module_len;
2044 *dest++ = (byte) RMT_INIT;
2045 *dest++ = (byte) (RMT_INIT >> 8);
2046 if (module_info->songs != 1) {
2047 addr = RMT_INIT + 10 + module_info->songs;
2048 *dest++ = (byte) addr;
2049 *dest++ = (byte) (addr >> 8);
2050 *dest++ = 0xa8; /* tay */
2051 *dest++ = 0xb9; /* lda song2pos,y */
2052 *dest++ = (byte) (RMT_INIT + 11);
2053 *dest++ = (byte) ((RMT_INIT + 11) >> 8);
2055 else {
2056 *dest++ = (byte) (RMT_INIT + 8);
2057 *dest++ = (byte) ((RMT_INIT + 8) >> 8);
2058 *dest++ = 0xa9; /* lda #0 */
2059 *dest++ = 0;
2061 *dest++ = 0xa2; /* ldx #<music */
2062 *dest++ = (byte) module_info->music;
2063 *dest++ = 0xa0; /* ldy #>music */
2064 *dest++ = (byte) (module_info->music >> 8);
2065 *dest++ = 0x4c; /* jmp player */
2066 *dest++ = (byte) module_info->player;
2067 *dest++ = (byte) (module_info->player >> 8);
2068 if (module_info->songs != 1) {
2069 memcpy(dest, module_info->song_pos, module_info->songs);
2070 dest += module_info->songs;
2072 if (module_info->channels == 1) {
2073 memcpy(dest, rmt4_obx + 2, sizeof(rmt4_obx) - 2);
2074 dest += sizeof(rmt4_obx) - 2;
2076 else {
2077 memcpy(dest, rmt8_obx + 2, sizeof(rmt8_obx) - 2);
2078 dest += sizeof(rmt8_obx) - 2;
2080 return dest - out_module;
2081 case ASAP_TYPE_TMC:
2082 player = module_info->player + tmc_player[module[0x25] - 1];
2083 addr = player + tmc_init[module[0x25] - 1];
2084 if (module_info->songs != 1)
2085 addr -= 3;
2086 dest = put_sap_header(out_module, module_info, 'B', -1, addr, player);
2087 if (dest == NULL)
2088 return -1;
2089 memcpy(dest, module, module_len);
2090 dest += module_len;
2091 *dest++ = (byte) addr;
2092 *dest++ = (byte) (addr >> 8);
2093 *dest++ = tmc_obx[4];
2094 *dest++ = tmc_obx[5];
2095 if (module_info->songs != 1)
2096 *dest++ = 0x48; /* pha */
2097 *dest++ = 0xa0; /* ldy #<music */
2098 *dest++ = (byte) module_info->music;
2099 *dest++ = 0xa2; /* ldx #>music */
2100 *dest++ = (byte) (module_info->music >> 8);
2101 *dest++ = 0xa9; /* lda #$70 */
2102 *dest++ = 0x70;
2103 *dest++ = 0x20; /* jsr player */
2104 *dest++ = (byte) module_info->player;
2105 *dest++ = (byte) (module_info->player >> 8);
2106 if (module_info->songs != 1) {
2107 *dest++ = 0x68; /* pla */
2108 *dest++ = 0xaa; /* tax */
2109 *dest++ = 0xa9; /* lda #0 */
2110 *dest++ = 0;
2112 else {
2113 *dest++ = 0xa9; /* lda #$60 */
2114 *dest++ = 0x60;
2116 switch (module[0x25]) {
2117 case 2:
2118 *dest++ = 0x06; /* asl 0 */
2119 *dest++ = 0;
2120 *dest++ = 0x4c; /* jmp player */
2121 *dest++ = (byte) module_info->player;
2122 *dest++ = (byte) (module_info->player >> 8);
2123 *dest++ = 0xa5; /* lda 0 */
2124 *dest++ = 0;
2125 *dest++ = 0xe6; /* inc 0 */
2126 *dest++ = 0;
2127 *dest++ = 0x4a; /* lsr @ */
2128 *dest++ = 0x90; /* bcc player+3 */
2129 *dest++ = 5;
2130 *dest++ = 0xb0; /* bcs player+6 */
2131 *dest++ = 6;
2132 break;
2133 case 3:
2134 case 4:
2135 *dest++ = 0xa0; /* ldy #1 */
2136 *dest++ = 1;
2137 *dest++ = 0x84; /* sty 0 */
2138 *dest++ = 0;
2139 *dest++ = 0xd0; /* bne player */
2140 *dest++ = 10;
2141 *dest++ = 0xc6; /* dec 0 */
2142 *dest++ = 0;
2143 *dest++ = 0xd0; /* bne player+6 */
2144 *dest++ = 12;
2145 *dest++ = 0xa0; /* ldy #3 */
2146 *dest++ = module[0x25];
2147 *dest++ = 0x84; /* sty 0 */
2148 *dest++ = 0;
2149 *dest++ = 0xd0; /* bne player+3 */
2150 *dest++ = 3;
2151 break;
2152 default:
2153 break;
2155 memcpy(dest, tmc_obx + 6, sizeof(tmc_obx) - 6);
2156 dest += sizeof(tmc_obx) - 6;
2157 return dest - out_module;
2158 case ASAP_TYPE_TM2:
2159 dest = put_sap_header(out_module, module_info, 'B', -1, TM2_INIT, module_info->player + 3);
2160 if (dest == NULL)
2161 return -1;
2162 memcpy(dest, module, module_len);
2163 dest += module_len;
2164 *dest++ = (byte) TM2_INIT;
2165 *dest++ = (byte) (TM2_INIT >> 8);
2166 if (module_info->songs != 1) {
2167 *dest++ = (byte) (TM2_INIT + 16);
2168 *dest++ = (byte) ((TM2_INIT + 16) >> 8);
2169 *dest++ = 0x48; /* pha */
2171 else {
2172 *dest++ = (byte) (TM2_INIT + 14);
2173 *dest++ = (byte) ((TM2_INIT + 14) >> 8);
2175 *dest++ = 0xa0; /* ldy #<music */
2176 *dest++ = (byte) module_info->music;
2177 *dest++ = 0xa2; /* ldx #>music */
2178 *dest++ = (byte) (module_info->music >> 8);
2179 *dest++ = 0xa9; /* lda #$70 */
2180 *dest++ = 0x70;
2181 *dest++ = 0x20; /* jsr player */
2182 *dest++ = (byte) module_info->player;
2183 *dest++ = (byte) (module_info->player >> 8);
2184 if (module_info->songs != 1) {
2185 *dest++ = 0x68; /* pla */
2186 *dest++ = 0xaa; /* tax */
2187 *dest++ = 0xa9; /* lda #0 */
2188 *dest++ = 0;
2190 else {
2191 *dest++ = 0xa9; /* lda #0 */
2192 *dest++ = 0;
2193 *dest++ = 0xaa; /* tax */
2195 *dest++ = 0x4c; /* jmp player */
2196 *dest++ = (byte) module_info->player;
2197 *dest++ = (byte) (module_info->player >> 8);
2198 memcpy(dest, tm2_obx + 2, sizeof(tm2_obx) - 2);
2199 dest += sizeof(tm2_obx) - 2;
2200 return dest - out_module;
2201 default:
2202 return -1;
2206 #endif /* defined(C) && !defined(ASAP_ONLY_SAP) */