1 /*
2 * psid.c - PSID file handling.
3 *
4 * Written by
5 * Dag Lem <resid@nimrod.no>
6 *
7 * This file is part of VICE, the Versatile Commodore Emulator.
8 * See README for copyright notice.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 * 02111-1307 USA.
24 *
25 */
26
27 #include "vice.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "archdep.h"
34 #include "c64mem.h"
35 #include "c64rom.h"
36 #include "c64-resources.h"
37 #include "charset.h"
38 #include "cmdline.h"
39 #include "lib.h"
40 #include "log.h"
41 #include "machine.h"
42 #include "patchrom.h"
43 #include "psid.h"
44 #include "resources.h"
45 #include "types.h"
46 #include "uiapi.h"
47 #include "vsidui.h"
48 #include "vsync.h"
49 #include "zfile.h"
50
51 static int mus_load_file(const char* filename, int ispsid);
52
53 static log_t vlog = LOG_ERR;
54
55 typedef struct psid_s {
56 /* PSID data */
57 uint8_t is_rsid;
58 uint16_t version;
59 uint16_t data_offset;
60 uint16_t load_addr;
61 uint16_t init_addr;
62 uint16_t play_addr;
63 uint16_t songs;
64 uint16_t start_song;
65 uint32_t speed;
66 /* psid v3 allows all 32 bytes to be used with no zero termination */
67 uint8_t name[32 + 1];
68 uint8_t author[32 + 1];
69 uint8_t copyright[32 + 1];
70 uint16_t flags;
71 uint8_t start_page;
72 uint8_t max_pages;
73 uint16_t reserved;
74 uint16_t data_size;
75 uint8_t data[65536];
76
77 /* Non-PSID data */
78 uint32_t frames_played;
79 uint16_t load_last_addr;
80 } psid_t;
81
82 #define PSID_V1_DATA_OFFSET 0x76
83 #define PSID_V2_DATA_OFFSET 0x7c
84
85
86 static psid_t* psid = NULL;
87 static int psid_tune = 0; /* currently selected tune, 0: default 1: first, 2: second, etc */
88 static int keepenv = 0;
89
90 static int firstfile = 0;
91 static int psid_tune_cmdline = 0;
92
93 struct kernal_s {
94 const char *name;
95 int rev;
96 };
97
98 static struct kernal_s kernal_match[] = {
99 { "1", C64_KERNAL_REV1 },
100 { "2", C64_KERNAL_REV2 },
101 { "3", C64_KERNAL_REV3 },
102 { "67", C64_KERNAL_SX64 },
103 { "sx", C64_KERNAL_SX64 },
104 { "100", C64_KERNAL_4064 },
105 { "4064", C64_KERNAL_4064 },
106 { NULL, C64_KERNAL_UNKNOWN }
107 };
108
set_kernal_revision(const char * param,void * extra_param)109 static int set_kernal_revision(const char *param, void *extra_param)
110 {
111 uint16_t sum; /* ROM checksum */
112 int id; /* ROM identification number */
113 int rev = C64_KERNAL_UNKNOWN;
114 int i = 0;
115
116 if (!param) {
117 return -1;
118 }
119
120 do {
121 if (strcmp(kernal_match[i].name, param) == 0) {
122 rev = kernal_match[i].rev;
123 }
124 i++;
125 } while ((rev == C64_KERNAL_UNKNOWN) && (kernal_match[i].name != NULL));
126
127 if(!c64rom_isloaded()) {
128 kernal_revision = rev;
129 return 0;
130 }
131
132 if (c64rom_get_kernal_chksum_id(&sum, &id) < 0) {
133 id = C64_KERNAL_UNKNOWN;
134 kernal_revision = id;
135 } else {
136 if (patch_rom_idx(rev) >= 0) {
137 kernal_revision = rev;
138 } else {
139 kernal_revision = id;
140 }
141 }
142 return 0;
143 }
144
145
set_keepenv(int val,void * param)146 static int set_keepenv(int val, void *param)
147 {
148 keepenv = val ? 1 : 0;
149 return 0;
150 }
151
152 static const resource_int_t resources_int[] = {
153 { "PSIDKeepEnv", 0, RES_EVENT_NO, NULL,
154 &keepenv, set_keepenv, NULL },
155 { "PSIDTune", 0, RES_EVENT_NO, NULL,
156 &psid_tune, psid_ui_set_tune, NULL },
157 RESOURCE_INT_LIST_END
158 };
159
psid_resources_init(void)160 int psid_resources_init(void)
161 {
162 return resources_register_int(resources_int);
163 }
164
cmdline_keepenv(const char * param,void * extra_param)165 static int cmdline_keepenv(const char *param, void *extra_param)
166 {
167 keepenv = 1;
168 return 0;
169 }
170
cmdline_psid_tune(const char * param,void * extra_param)171 static int cmdline_psid_tune(const char *param, void *extra_param)
172 {
173 psid_tune_cmdline = atoi(param);
174 if (psid_tune_cmdline < 0) {
175 psid_tune_cmdline = 0;
176 }
177 return 0;
178 }
179
180 static const cmdline_option_t cmdline_options[] =
181 {
182 /* The Video Standard options are copied from the machine files. */
183 { "-pal", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
184 NULL, NULL, "MachineVideoStandard", (resource_value_t)MACHINE_SYNC_PAL,
185 NULL, "Use PAL sync factor" },
186 { "-ntsc", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
187 NULL, NULL, "MachineVideoStandard", (resource_value_t)MACHINE_SYNC_NTSC,
188 NULL, "Use NTSC sync factor" },
189 { "-ntscold", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
190 NULL, NULL, "MachineVideoStandard", (resource_value_t)MACHINE_SYNC_NTSCOLD,
191 NULL, "Use old NTSC sync factor" },
192 { "-paln", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
193 NULL, NULL, "MachineVideoStandard", (resource_value_t)MACHINE_SYNC_PALN,
194 NULL, "Use PAL-N sync factor" },
195 { "-keepenv", CALL_FUNCTION, CMDLINE_ATTRIB_NONE,
196 cmdline_keepenv, NULL, NULL, NULL,
197 NULL, "Override PSID settings for Video standard and SID model" },
198 { "-tune", CALL_FUNCTION, CMDLINE_ATTRIB_NEED_ARGS,
199 cmdline_psid_tune, NULL, NULL, NULL,
200 "<number>", "Specify PSID tune <number>" },
201 { "-kernal", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
202 NULL, NULL, "KernalName", NULL,
203 "<Name>", "Specify name of Kernal ROM image" },
204 { "-basic", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
205 NULL, NULL, "BasicName", NULL,
206 "<Name>", "Specify name of BASIC ROM image" },
207 { "-chargen", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
208 NULL, NULL, "ChargenName", NULL,
209 "<Name>", "Specify name of character generator ROM image" },
210 { "-kernalrev", CALL_FUNCTION, CMDLINE_ATTRIB_NONE,
211 set_kernal_revision, NULL, NULL, NULL,
212 "<Revision>", "Patch the Kernal ROM to the specified <revision> (1: rev. 1, 2: rev. 2, 3: rev. 3, 67/sx: sx64, 100/4064: 4064)" },
213 CMDLINE_LIST_END
214 };
215
psid_cmdline_options_init(void)216 int psid_cmdline_options_init(void)
217 {
218 return cmdline_register_options(cmdline_options);
219 }
220
psid_extract_word(uint8_t ** buf)221 static uint16_t psid_extract_word(uint8_t **buf)
222 {
223 uint16_t word = (*buf)[0] << 8 | (*buf)[1];
224 *buf += 2;
225 return word;
226 }
227
psid_load_file(const char * filename)228 int psid_load_file(const char* filename)
229 {
230 FILE* f;
231 uint8_t buf[PSID_V2_DATA_OFFSET + 2];
232 uint8_t *ptr = buf;
233 unsigned int length;
234
235 /* HACK: the selected tune number is handled by the "PSIDtune" resource, which
236 * is actually saved in the ini file, and thus loaded and restored at
237 * startup. however, we do not want that. instead we want the default
238 * tune of the respective .sid file to be used, or the explicit tune
239 * number given on commandline (if any).
240 */
241 if (!firstfile) {
242 if (psid_tune_cmdline) {
243 psid_tune = psid_tune_cmdline;
244 } else {
245 psid_tune = 0;
246 }
247 firstfile = 1;
248 }
249
250 if (vlog == LOG_ERR) {
251 vlog = log_open("Vsid");
252 }
253
254 if (!(f = zfile_fopen(filename, MODE_READ))) {
255 return -1;
256 }
257
258 lib_free(psid);
259 psid = lib_calloc(sizeof(psid_t), 1);
260
261 if (fread(ptr, 1, 6, f) != 6 || (memcmp(ptr, "PSID", 4) != 0 && memcmp(ptr, "RSID", 4) != 0)) {
262 goto fail;
263 }
264 psid->is_rsid = (ptr[0] == 'R');
265
266 ptr += 4;
267 psid->version = psid_extract_word(&ptr);
268
269 if (psid->version < 1 || psid->version > 4) {
270 log_error(vlog, "Unknown PSID version number: %d.", (int)psid->version);
271 goto fail;
272 }
273 log_message(vlog, "PSID version number: %d.", (int)psid->version);
274
275 length = (unsigned int)((psid->version == 1 ? PSID_V1_DATA_OFFSET : PSID_V2_DATA_OFFSET) - 6);
276
277 if (fread(ptr, 1, length, f) != length) {
278 log_error(vlog, "Reading PSID header.");
279 goto fail;
280 }
281
282 psid->data_offset = psid_extract_word(&ptr);
283 psid->load_addr = psid_extract_word(&ptr);
284 psid->init_addr = psid_extract_word(&ptr);
285 psid->play_addr = psid_extract_word(&ptr);
286 psid->songs = psid_extract_word(&ptr);
287 psid->start_song = psid_extract_word(&ptr);
288 psid->speed = psid_extract_word(&ptr) << 16;
289 psid->speed |= psid_extract_word(&ptr);
290 psid->frames_played = 0;
291 memcpy(psid->name, ptr, 32);
292 psid->name[32] = '\0';
293 ptr += 32;
294 memcpy(psid->author, ptr, 32);
295 psid->author[32] = '\0';
296 ptr += 32;
297 memcpy(psid->copyright, ptr, 32);
298 psid->copyright[32] = '\0';
299 ptr += 32;
300 if (psid->version >= 2) {
301 psid->flags = psid_extract_word(&ptr);
302 psid->start_page = *ptr++;
303 psid->max_pages = *ptr++;
304 psid->reserved = psid_extract_word(&ptr);
305 } else {
306 psid->flags = 0;
307 psid->start_page = 0;
308 psid->max_pages = 0;
309 psid->reserved = 0;
310 }
311
312 if ((psid->start_song < 1) || (psid->start_song > psid->songs)) {
313 log_error(vlog, "Default tune out of range (%d of %d ?), using 1 instead.", psid->start_song, psid->songs);
314 psid->start_song = 1;
315 }
316
317 vsid_ui_display_nr_of_tunes(psid->songs);
318 vsid_ui_set_default_tune(psid->start_song);
319
320 /* Check for SIDPLAYER MUS files. */
321 if (psid->flags & 0x01) {
322 zfile_fclose(f);
323 return mus_load_file(filename, 1);
324 }
325
326 /* Zero load address => the load address is stored in the
327 first two bytes of the binary C64 data. */
328 if (psid->load_addr == 0) {
329 if (fread(ptr, 1, 2, f) != 2) {
330 log_error(vlog, "Reading PSID load address.");
331 goto fail;
332 }
333 psid->load_addr = ptr[0] | ptr[1] << 8;
334 }
335
336 /* Zero init address => use load address. */
337 if (psid->init_addr == 0) {
338 psid->init_addr = psid->load_addr;
339 }
340
341 /* Read binary C64 data. */
342 psid->data_size = (uint16_t)fread(psid->data, 1, sizeof(psid->data), f);
343 psid->load_last_addr = (psid->load_addr + psid->data_size - 1);
344
345 if (ferror(f)) {
346 log_error(vlog, "Reading PSID data.");
347 goto fail;
348 }
349
350 if (!feof(f)) {
351 log_error(vlog, "More than 64K PSID data.");
352 goto fail;
353 }
354
355 /* Relocation setup. */
356 if (psid->start_page == 0x00) {
357 /* Start and end pages. */
358 int startp = psid->load_addr >> 8;
359 int endp = psid->load_last_addr >> 8;
360
361 /* Used memory ranges. */
362 unsigned int used[] = {
363 0x00,
364 0x03,
365 0xa0,
366 0xbf,
367 0xd0,
368 0xff,
369 0x00,
370 0x00
371 }; /* calculated below */
372
373 unsigned int pages[256];
374 unsigned int last_page = 0;
375 unsigned int i, page, tmp;
376
377 log_message(vlog, "No PSID freepages set, recalculating...");
378
379 /* finish initialization */
380 used[6] = startp; used[7] = endp;
381
382 /* Mark used pages in table. */
383 memset(pages, 0, sizeof(pages));
384 for (i = 0; i < sizeof(used) / sizeof(*used); i += 2) {
385 for (page = used[i]; page <= used[i + 1]; page++) {
386 pages[page] = 1;
387 }
388 }
389
390 /* Find largest free range. */
391 psid->max_pages = 0x00;
392 for (page = 0; page < sizeof(pages) / sizeof(*pages); page++) {
393 if (!pages[page]) {
394 continue;
395 }
396 tmp = page - last_page;
397 if (tmp > psid->max_pages) {
398 psid->start_page = last_page;
399 psid->max_pages = tmp;
400 }
401 last_page = page + 1;
402 }
403
404 if (psid->max_pages == 0x00) {
405 psid->start_page = 0xff;
406 }
407 }
408
409 if (psid->start_page == 0xff || psid->max_pages < 2) {
410 log_error(vlog, "No space for driver.");
411 goto fail;
412 }
413
414 zfile_fclose(f);
415
416 return 0;
417
418 fail:
419 zfile_fclose(f);
420 lib_free(psid);
421 psid = NULL;
422
423 /* if the file wasnt in PSID format, try MUS/STR */
424 if (memcmp(ptr, "PSID", 4) != 0 && memcmp(ptr, "RSID", 4) != 0) {
425 return mus_load_file(filename, 0);
426 }
427
428 return -1;
429 }
430
psid_shutdown(void)431 void psid_shutdown(void)
432 {
433 lib_free(psid);
434 psid = NULL;
435 }
436
437 /* Use CBM80 vector to start PSID driver. This is a simple method to
438 transfer control to the PSID driver while running in a pure C64
439 environment. */
psid_set_cbm80(uint16_t vec,uint16_t addr)440 static int psid_set_cbm80(uint16_t vec, uint16_t addr)
441 {
442 unsigned int i;
443 uint8_t cbm80[] = { 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc2, 0xcd, 0x38, 0x30 };
444
445 cbm80[0] = vec & 0xff;
446 cbm80[1] = vec >> 8;
447
448 for (i = 0; i < sizeof(cbm80); i++) {
449 /* make backup of original content at 0x8000 */
450 ram_store((uint16_t)(addr + i), ram_read((uint16_t)(0x8000 + i)));
451 /* copy header */
452 ram_store((uint16_t)(0x8000 + i), cbm80[i]);
453 }
454
455 return i;
456 }
457
psid_init_tune(int install_driver_hook)458 void psid_init_tune(int install_driver_hook)
459 {
460 int start_song = psid_tune;
461 int sync, sid_model;
462 int i;
463 uint16_t reloc_addr;
464 uint16_t addr;
465 int speedbit;
466 char* irq;
467 char irq_str[20];
468 const char csidflag[4][8] = { "UNKNOWN", "6581", "8580", "ANY"};
469
470 if (!psid) {
471 return;
472 }
473
474 psid->frames_played = 0;
475
476 reloc_addr = psid->start_page << 8;
477
478 log_message(vlog, "Driver=$%04X, Image=$%04X-$%04X, Init=$%04X, Play=$%04X", reloc_addr, psid->load_addr, psid->load_addr + psid->data_size - 1, psid->init_addr, psid->play_addr);
479
480 /* PAL/NTSC. */
481 resources_get_int("MachineVideoStandard", &sync);
482
483 /* MOS6581/MOS8580. */
484 resources_get_int("SidModel", &sid_model);
485
486 /* Check tune number. */
487 /* printf("start_song: %d psid->start_song %d\n", start_song, psid->start_song); */
488
489 if (start_song == 0) {
490 start_song = psid->start_song;
491 } else if (start_song < 1 || start_song > psid->songs) {
492 log_warning(vlog, "Tune out of range.");
493 start_song = psid->start_song;
494 }
495
496 /* Check for PlaySID specific file. */
497 if (psid->flags & 0x02 && !psid->is_rsid) {
498 log_warning(vlog, "Image is PlaySID specific - trying anyway.");
499 }
500
501 /* Check tune speed. */
502 speedbit = 1;
503 for (i = 1; i < start_song && i < 32; i++) {
504 speedbit <<= 1;
505 }
506
507 irq = psid->speed & speedbit ? "CIA 1" : "VICII";
508
509 if (psid->play_addr) {
510 strcpy(irq_str, irq);
511 } else {
512 sprintf(irq_str, "custom (%s ?)", irq);
513 }
514
515 if (console_mode) {
516 log_message(vlog, " Title: %s", (char *) psid->name);
517 log_message(vlog, " Author: %s", (char *) psid->author);
518 log_message(vlog, "Released: %s", (char *) psid->copyright);
519 log_message(vlog, "Using %s sync", sync == MACHINE_SYNC_PAL ? "PAL" : "NTSC");
520 log_message(vlog, "SID model: %s", csidflag[(psid->flags >> 4) & 3]);
521 log_message(vlog, "Using %s interrupt", irq_str);
522 log_message(vlog, "Playing tune %d out of %d (default=%d)", start_song, psid->songs, psid->start_song);
523 } else {
524 if (machine_class == VICE_MACHINE_VSID) {
525 char * driver_info_text;
526 driver_info_text = lib_msprintf("Driver=$%04X, Image=$%04X-$%04X, Init=$%04X, Play=$%04X", reloc_addr, psid->load_addr,
527 psid->load_addr + psid->data_size - 1, psid->init_addr, psid->play_addr);
528 vsid_ui_setdrv(driver_info_text);
529 vsid_ui_set_driver_addr(reloc_addr);
530 vsid_ui_set_load_addr(psid->load_addr);
531 vsid_ui_set_init_addr(psid->init_addr);
532 vsid_ui_set_play_addr(psid->play_addr);
533 vsid_ui_set_data_size(psid->data_size);
534 lib_free(driver_info_text);
535 }
536 vsid_ui_display_name((char *)(psid->name));
537 vsid_ui_display_author((char *)(psid->author));
538 vsid_ui_display_copyright((char *)(psid->copyright));
539
540 vsid_ui_display_sync(sync);
541 vsid_ui_display_sid_model(sid_model);
542 vsid_ui_display_irqtype(irq_str);
543 vsid_ui_display_tune_nr(start_song);
544 vsid_ui_display_time(0);
545 }
546
547 /* Store parameters for PSID player. */
548 if (install_driver_hook) {
549 /* Skip JMP. */
550 addr = reloc_addr + 3 + 9;
551
552 /* CBM80 reset vector. */
553 addr += psid_set_cbm80((uint16_t)(reloc_addr + 9), addr);
554
555 ram_store(addr, (uint8_t)(start_song));
556 }
557
558 /* put song number into address 780/1/2 (A/X/Y) for use by BASIC tunes */
559 ram_store(780, (uint8_t)(start_song - 1));
560 ram_store(781, (uint8_t)(start_song - 1));
561 ram_store(782, (uint8_t)(start_song - 1));
562 /* force flag in c64 memory, many sids reads it and must be set AFTER the sid flag is read */
563 ram_store((uint16_t)(0x02a6), (uint8_t)(sync == MACHINE_SYNC_NTSC ? 0 : 1));
564 }
565
psid_basic_rsid_to_autostart(uint16_t * address,uint8_t ** data,uint16_t * length)566 int psid_basic_rsid_to_autostart(uint16_t *address, uint8_t **data, uint16_t *length)
567 {
568 if (psid && psid->is_rsid && psid->flags & 0x02) {
569 *address = psid->load_addr;
570 *data = psid->data;
571 *length = psid->data_size;
572 return 1;
573 }
574 return 0;
575 }
576
577 /* called from machine_play_psid */
psid_set_tune(int tune)578 void psid_set_tune(int tune)
579 {
580 if (tune == -1) {
581 psid_tune = 0;
582 lib_free(psid);
583 psid = NULL;
584 } else {
585 psid_tune = tune;
586 }
587 }
588
589 /* used for setting the PSIDtune resource */
psid_ui_set_tune(int tune,void * param)590 int psid_ui_set_tune(int tune, void *param)
591 {
592 psid_tune = (tune == -1) ? 0 : tune;
593
594 psid_set_tune(psid_tune);
595 vsync_suspend_speed_eval();
596 machine_trigger_reset(MACHINE_RESET_MODE_SOFT);
597
598 return 0;
599 }
600
psid_tunes(int * default_tune)601 int psid_tunes(int* default_tune)
602 {
603 *default_tune = psid ? psid->start_song : 0;
604
605 return psid ? psid->songs : 0;
606 }
607
psid_init_driver(void)608 void psid_init_driver(void)
609 {
610 uint8_t psid_driver[] = {
611 #include "psiddrv.h"
612 };
613 char *psid_reloc = (char *)psid_driver;
614 int psid_size;
615
616 uint16_t reloc_addr;
617 uint16_t addr;
618 int i;
619 int sync;
620 int sid2loc, sid3loc;
621
622 if (!psid) {
623 return;
624 }
625
626 /* C64 PAL/NTSC flag. */
627 resources_get_int("MachineVideoStandard", &sync);
628 if (!keepenv) {
629 switch ((psid->flags >> 2) & 0x03) {
630 case 0x01:
631 sync = MACHINE_SYNC_PAL;
632 resources_set_int("MachineVideoStandard", sync);
633 break;
634 case 0x02:
635 sync = MACHINE_SYNC_NTSC;
636 resources_set_int("MachineVideoStandard", sync);
637 break;
638 default:
639 /* Keep settings (00 = unknown, 11 = any) */
640 break;
641 }
642 }
643
644 /* Stereo SID specification support from Wilfred Bos.
645 * Top byte of reserved holds the middle nybbles of
646 * the 2nd chip address. */
647 resources_set_int("SidStereo", 0);
648 if (psid->version >= 3) {
649 sid2loc = 0xd000 | ((psid->reserved >> 4) & 0x0ff0);
650 log_message(vlog, "2nd SID at $%04x", sid2loc);
651 if (((sid2loc >= 0xd420 && sid2loc < 0xd800) || sid2loc >= 0xde00)
652 && (sid2loc & 0x10) == 0) {
653 resources_set_int("SidStereo", 1);
654 resources_set_int("SidStereoAddressStart", sid2loc);
655 }
656 sid3loc = 0xd000 | ((psid->reserved << 4) & 0x0ff0);
657 if (sid3loc != 0xd000) {
658 log_message(vlog, "3rd SID at $%04x", sid3loc);
659 if (((sid3loc >= 0xd420 && sid3loc < 0xd800) || sid3loc >= 0xde00)
660 && (sid3loc & 0x10) == 0) {
661 resources_set_int("SidStereo", 2);
662 resources_set_int("SidTripleAddressStart", sid3loc);
663 }
664 }
665 }
666
667 /* MOS6581/MOS8580 flag. */
668 if (!keepenv) {
669 switch ((psid->flags >> 4) & 0x03) {
670 case 0x01:
671 resources_set_int("SidModel", 0);
672 break;
673 case 0x02:
674 resources_set_int("SidModel", 1);
675 break;
676 default:
677 /* Keep settings (00 = unknown, 11 = any) */
678 break;
679 }
680 /* FIXME: second chip model is ignored,
681 * but it is stored at (flags >> 6) & 3. */
682 }
683
684 /* Clear low memory to minimize the damage of PSIDs doing bad reads. */
685 for (addr = 0; addr < 0x0800; addr++) {
686 ram_store(addr, (uint8_t)0x00);
687 }
688
689 /* Relocation of C64 PSID driver code. */
690 reloc_addr = psid->start_page << 8;
691 psid_size = sizeof(psid_driver);
692 log_message(vlog, "PSID free pages: $%04x-$%04x", reloc_addr, (reloc_addr + (psid->max_pages << 8)) -1);
693
694 if (!reloc65((char **)&psid_reloc, &psid_size, reloc_addr)) {
695 log_error(vlog, "Relocation.");
696 psid_set_tune(-1);
697 return;
698 }
699
700 for (i = 0; i < psid_size; i++) {
701 ram_store((uint16_t)(reloc_addr + i), psid_reloc[i]);
702 }
703
704 /* Store binary C64 data. */
705 for (i = 0; i < psid->data_size; i++) {
706 ram_store((uint16_t)(psid->load_addr + i), psid->data[i]);
707 }
708
709 /* Skip JMP and CBM80 reset vector. */
710 addr = reloc_addr + 3 + 9 + 9;
711
712 /* Store parameters for PSID player. */
713 ram_store(addr++, (uint8_t)(0));
714 ram_store(addr++, (uint8_t)(psid->songs));
715 ram_store(addr++, (uint8_t)(psid->load_addr & 0xff));
716 ram_store(addr++, (uint8_t)(psid->load_addr >> 8));
717 ram_store(addr++, (uint8_t)(psid->init_addr & 0xff));
718 ram_store(addr++, (uint8_t)(psid->init_addr >> 8));
719 ram_store(addr++, (uint8_t)(psid->play_addr & 0xff));
720 ram_store(addr++, (uint8_t)(psid->play_addr >> 8));
721 ram_store(addr++, (uint8_t)(psid->speed & 0xff));
722 ram_store(addr++, (uint8_t)((psid->speed >> 8) & 0xff));
723 ram_store(addr++, (uint8_t)((psid->speed >> 16) & 0xff));
724 ram_store(addr++, (uint8_t)(psid->speed >> 24));
725 ram_store(addr++, (uint8_t)((int)sync == MACHINE_SYNC_PAL ? 1 : 0));
726 ram_store(addr++, (uint8_t)(psid->load_last_addr & 0xff));
727 ram_store(addr++, (uint8_t)(psid->load_last_addr >> 8));
728 }
729
psid_increment_frames(void)730 unsigned int psid_increment_frames(void)
731 {
732 if (!psid) {
733 return 0;
734 }
735
736 (psid->frames_played)++;
737
738 return (unsigned int)(psid->frames_played);
739 }
740
741 /******************************************************************************
742 * compute sidplayer (.mus/.str) support
743 *
744 * to minimize code duplication and to simplify the integration with the rest
745 * of the code, the sidplayer data is simply converted into PSID like format
746 * at load time. heavily inspired by the respective code in libsidplay2.
747 ******************************************************************************/
748
749 #define MUS_HLT_CMD 0x014F
750
751 #define MUS_IMAGE_START 0x0900
752 #define MUS_DATA_ADDR 0x0900
753 #define MUS_DATA2_ADDR 0x6900
754 #define MUS_DATA_MAXLEN 0x6000
755
756 #define MUS_DRIVER_ADDR 0xe000
757 #define MUS_DRIVER2_ADDR 0xf000
758
759 #define MUS_SID1_BASE_ADDR 0xd400
760 #define MUS_SID2_BASE_ADDR 0xd500
761
762 #include "musdrv.h"
763
mus_install(void)764 static void mus_install(void)
765 {
766 uint16_t dest;
767 /* Install MUS player #1. */
768 dest = ((mus_driver[1] << 8) | mus_driver[0]) - MUS_IMAGE_START;
769 memcpy(psid->data + dest, mus_driver + 2, sizeof(mus_driver) - 2);
770 /* Point player #1 to data #1. */
771 psid->data[dest + 0xc6e] = MUS_DATA_ADDR & 0xFF;
772 psid->data[dest + 0xc70] = MUS_DATA_ADDR >> 8;
773
774 /* Install MUS player #2. It doesnt hurt to do it also for mono tunes. */
775 dest = ((mus_stereo_driver[1] << 8) | mus_stereo_driver[0]) - MUS_IMAGE_START;
776 memcpy(psid->data + dest, mus_stereo_driver + 2, sizeof(mus_stereo_driver) - 2);
777 /* Point player #2 to data #2. */
778 psid->data[dest + 0xc6e] = MUS_DATA2_ADDR & 0xFF;
779 psid->data[dest + 0xc70] = MUS_DATA2_ADDR >> 8;
780 }
781
mus_check(const unsigned char * buf)782 static int mus_check(const unsigned char *buf)
783 {
784 unsigned int voice1Index, voice2Index, voice3Index;
785
786 /* Skip 3x length entry. */
787 voice1Index = ((buf[1] << 8) | buf[0]) + (3 * 2);
788 voice2Index = voice1Index + ((buf[3] << 8) | buf[2]);
789 voice3Index = voice2Index + ((buf[5] << 8) | buf[4]);
790 return (((buf[voice1Index - 2] << 8) | buf[voice1Index - 1]) == MUS_HLT_CMD)
791 && (((buf[voice2Index - 2] << 8) | buf[voice2Index - 1]) == MUS_HLT_CMD)
792 && (((buf[voice3Index - 2] << 8) | buf[voice3Index - 1]) == MUS_HLT_CMD);
793 }
794
795 /* check for graphic characters */
ispetchar(unsigned char c)796 static int ispetchar(unsigned char c)
797 {
798 if ((c >= 32) && (c <= 93)) {
799 return 1;
800 }
801 return 0;
802 }
803
copystring(unsigned char * d,const unsigned char * s)804 static const unsigned char *copystring(unsigned char *d, const unsigned char *s)
805 {
806 unsigned char *end = d + 32;
807 int n = 0;
808 /* skip leading spaces and special characters */
809 while (((*s == 0x20) || !ispetchar(*s)) && (*s != 0x00)) {
810 ++s;
811 }
812 /* copy until end of line, omit special characters */
813 while ((*s != 0x0d) && (*s != 0x00)) {
814 if (ispetchar(*s)) {
815 *d++ = *s;
816 n = (*s == 0x20);
817 } else {
818 /* for special chars, insert one space */
819 if (!n) {
820 *d++ = 0x20;
821 n = 1;
822 }
823 }
824 /* if max len of destination is reached, skip until end of source */
825 if (d == end) {
826 while ((*s != 0x0d) && (*s != 0x00)) {
827 ++s;
828 }
829 break;
830 }
831 ++s;
832 }
833 *d = 0;
834 charset_petconvstring(d, 1);
835 return s + 1;
836 }
837
mus_extract_credits(const unsigned char * buf,int datalen)838 static void mus_extract_credits(const unsigned char *buf, int datalen)
839 {
840 const unsigned char *end;
841 int n;
842
843 /* get offset behind note data */
844 n = ((buf[1] << 8) | buf[0]) + (3 * 2);
845 n += ((buf[3] << 8) | buf[2]);
846 n += ((buf[5] << 8) | buf[4]);
847
848 end = buf + datalen;
849 buf += n;
850
851 psid->name[0] = psid->author[0] = psid->copyright[0] = 0;
852
853 if (buf < end) {
854 buf = copystring(psid->name, buf);
855 }
856 if (buf < end) {
857 buf = copystring(psid->author, buf);
858 }
859 if (buf < end) {
860 buf = copystring(psid->copyright, buf);
861 }
862 }
863
mus_load_file(const char * filename,int ispsid)864 static int mus_load_file(const char* filename, int ispsid)
865 {
866 char *strname;
867 FILE *f;
868 int n, stereo = 0;
869 int mus_datalen;
870
871 if (!(f = zfile_fopen(filename, MODE_READ))) {
872 return -1;
873 }
874
875 if (!ispsid) {
876 lib_free(psid);
877 psid = lib_calloc(sizeof(psid_t), 1);
878 }
879
880 /* skip header & original load address */
881 fseek(f, psid->data_offset + (psid->load_addr ? 0 : 2), SEEK_SET);
882 /* read .mus data */
883 mus_datalen = fread(psid->data, 1, 0xffff - MUS_DATA_MAXLEN, f);
884
885 if (!mus_check(psid->data)) {
886 log_error(vlog, "not a valid .mus file.");
887 goto fail;
888 }
889 zfile_fclose(f);
890
891 /* read additional stereo (.str) data if available */
892 /* FIXME: the psid file format specification does not tell how to handle
893 stereo sidplayer tunes when they are in psid format */
894 if (!ispsid) {
895 strname = lib_stralloc(filename);
896 n = strlen(strname) - 4;
897 strcpy(strname + n, ".str");
898
899 if ((f = zfile_fopen(strname, MODE_READ))) {
900 fseek(f, 2, SEEK_SET); /* skip original load address */
901 if (fread(psid->data + (MUS_DATA2_ADDR - MUS_IMAGE_START), 1, 0xffff - MUS_DATA_MAXLEN, f) < (3 * 2)) {
902 goto fail;
903 }
904 zfile_fclose(f);
905 stereo = 1;
906 }
907 lib_free(strname);
908 /* only extract credits if this is NOT a psid file */
909 mus_extract_credits(psid->data, mus_datalen);
910 }
911
912 mus_install();
913
914 psid->version = 3; /* v3 so we get stereo support */
915 psid->flags = 2 << 2; /* NTSC */
916 psid->start_page = 0x04;
917 psid->max_pages = (MUS_DATA_ADDR - 0x0400) >> 8;
918
919 psid->load_addr = MUS_IMAGE_START;
920 psid->data_size = 0x10000 - MUS_IMAGE_START;
921
922 psid->songs = 1;
923 psid->start_song = 1;
924 psid->speed = 1; /* play at 60Hz */
925
926 if (stereo) {
927 /* Player #1 + #2. */
928 psid->reserved = (MUS_SID2_BASE_ADDR << 4) & 0xff00; /* second SID at 0xd500 */
929 psid->init_addr = 0xfc90;
930 psid->play_addr = 0xfc96;
931 } else {
932 /* Player #1. */
933 psid->init_addr = 0xec60;
934 psid->play_addr = 0xec80;
935 }
936
937 return 0;
938
939 fail:
940 zfile_fclose(f);
941 lib_free(psid);
942 psid = NULL;
943 return -1;
944 }
945