1 /*
2 * expert.c - Cartridge handling, Expert cart.
3 *
4 * Written by
5 * Andreas Boose <viceteam@t-online.de>
6 * Nathan Huizinga <nathan.huizinga@chess.nl>
7 * Groepaz <groepaz@gmx.net>
8 *
9 * This file is part of VICE, the Versatile Commodore Emulator.
10 * See README for copyright notice.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 * 02111-1307 USA.
26 *
27 */
28
29 #include "vice.h"
30
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "archdep.h"
35 #define CARTRIDGE_INCLUDE_SLOT1_API
36 #include "c64cartsystem.h"
37 #undef CARTRIDGE_INCLUDE_SLOT1_API
38 #include "c64mem.h"
39 #include "cartio.h"
40 #include "cartridge.h"
41 #include "cmdline.h"
42 #include "crt.h"
43 #include "export.h"
44 #include "interrupt.h"
45 #include "lib.h"
46 #include "resources.h"
47 #include "snapshot.h"
48 #include "types.h"
49 #include "util.h"
50 #include "crt.h"
51
52 #define CARTRIDGE_INCLUDE_PRIVATE_API
53 #include "expert.h"
54 #undef CARTRIDGE_INCLUDE_PRIVATE_API
55
56 /*
57 FIXME: the following description is atleast inaccurate, if not plain wrong.
58
59 Trilogic Expert Cartridge
60
61 - one 8K RAM (!) bank
62 - IO1 seems to be connected to a flipflop, which makes a single bit "register".
63 wether it is decoded fully or mirrored over the full IO1 range is unknown
64 (the software only uses $de00).
65 - any access to io1 toggles the flipflop, which means enabling or disabling
66 the cartridge RAM, ie ROMH (exrom) (?)
67 - any access to io1 seems to disable write-access to the RAM
68
69 the cartridge has a 3 way switch:
70
71 PRG:
72
73 - NMI logic and registers are disabled
74 - RAM is mapped to 8000 (writeable) in 8k game mode
75
76 ON:
77
78 - after switching from OFF to ON
79 - NMI logic is active. the "freezer" can now be activated by either
80 pressing restore or the freezer button.
81 - Io1 "register" logic is disabled
82 - RAM not mapped
83
84 - after reset:
85 - RAM is mapped to 8000 (writeable) and E000 (read only) in ultimax mode
86 - Io1 "register" logic is enabled (?)
87
88 - on NMI (restore or freezer button pressed):
89 - RAM is mapped to 8000 (writeable) and E000 (read only) in ultimax mode
90 - Io1 "register" logic is enabled
91
92 OFF:
93
94 - NMI logic and registers are disabled
95 - RAM not mapped
96 - according to the documentation, the cartridge is disabled. however,
97 the NMI logic of the cart still seems to interfer somehow and makes
98 some program misbehave. the solution for this was to put an additional
99 switch at the NMI line of the cartridge port, which then allows to
100 completely disable the cartridge for real.
101
102 this misbehavior is NOT emulated
103
104 there also was an "upgrade" to the hardware at some point, called "EMS".
105 this pretty much was no more no less than a freezer button :=)
106
107 short instructions:
108
109 note: the state of the switch and the order of doing things is very important,
110 making a mistake here will almost certainly cause it not to work as
111 expected. also, since the software tracks its state internally to act
112 accordingly, saving it will not result in a runable image at any time.
113
114 preparing the expert ram (skip this if you use a crt file)
115
116 - switch to OFF
117 - reset
118 - switch to PRG
119 - load and run the install software
120 - switch to ON (as beeing told on screen)
121 - reset. this will usually start some menu/intro screen
122 - space to go to monitor
123
124 using the freezer / monitor
125
126 - "pp" or "p" clears memory (and will reset)
127 - (if you want to save a crt file for later use, do that now)
128 - switch to OFF
129 - reset
130 - load and run game
131 - switch to ON
132 - hit restore (or freezer button)
133 - z "name" to save backup
134 - r to restart running program
135 */
136
137 /*
138 notes from c64mem.c:
139 - ROML is enabled at memory configs 11, 15, 27, 31 and Ultimax.
140 - Allow writing at ROML at $8000-$9FFF.
141 - Allow ROML being visible independent of charen, hiram & loram
142 - Copy settings from "normal" operation mode into "ultimax" configuration.
143 */
144
145 /*
146 this sequence from expert 2.10 indicates that a full ROM is available at E000
147 when the cartridge is ON, HIROM is selected.
148
149 it also indicates that the de00 register is a toggle
150
151 .C:038b A9 37 LDA #$37
152 .C:038d 85 01 STA $01
153 .C:038f AD 00 DE LDA $DE00 toggle expert RAM on/off
154 .C:0392 AD BD FD LDA $FDBD fdbd is $00 in kernal rom
155 .C:0395 D0 F8 BNE $038F do again if expert RAM not off
156
157 Reset entry from expert 2.70:
158
159 .C:9fd1 78 SEI
160 .C:9fd2 D8 CLD
161 .C:9fd3 A2 FF LDX #$FF
162 .C:9fd5 9A TXS
163 .C:9fd6 E8 INX
164 .C:9fd7 8E F2 9F STX $9FF2 set to 0
165
166 .C:03b0 48 PHA
167 .C:03b1 A9 37 LDA #$37
168 .C:03b3 85 01 STA $01
169 .C:03b5 AD 00 DE LDA $DE00 toggle expert RAM on/off
170 .C:03b8 AD 00 E0 LDA $E000 is $85 in kernal
171 .C:03bb C9 85 CMP #$85
172 .C:03bd D0 F6 BNE $03B5
173 .C:03bf 68 PLA
174 .C:03c0 60 RTS
175
176 NMI entry from expert 2.70:
177
178 .C:9c00 2C F2 9F BIT $9FF2 get bit7 into N
179 .C:9c03 10 01 BPL $9C06 if N=0 branch
180 .C:9c05 40 RTI
181
182 .C:9c06 78 SEI
183 .C:9c07 8D 01 80 STA $8001
184 .C:9c0a A9 FF LDA #$FF
185 .C:9c0c 8D F2 9F STA $9FF2
186 */
187
188 /* #define DBGEXPERT */
189
190 #ifdef DBGEXPERT
191 #define DBG(x) printf x
192 const char * const expert_mode[3]={"off", "prg", "on"};
193 #else
194 #define DBG(x)
195 #endif
196
197 #define USEFAKEPRGMAPPING 1 /* emulate PRG mode as 8k game */
198
199 #if USEFAKEPRGMAPPING
200 #define EXPERT_PRG ((0 << 0) | (0 << 1)) /* 8k game */
201 #else
202 #define EXPERT_PRG ((1 << 0) | (1 << 1)) /* ultimax */
203 #endif
204 #define EXPERT_OFF ((0 << 0) | (1 << 1)) /* ram */
205 #define EXPERT_ON ((1 << 0) | (1 << 1)) /* ultimax */
206
207 static int cartmode = EXPERT_MODE_DEFAULT;
208 static int expert_enabled = 0;
209 static int expert_register_enabled = 0;
210 static int expert_ram_writeable = 0;
211 static int expert_ramh_enabled = 0; /* equals EXROM ? */
212
213 /* 8 KB RAM */
214 static uint8_t *expert_ram = NULL;
215
216 static char *expert_filename = NULL;
217 static int expert_filetype = 0;
218 static int expert_write_image = 0;
219
220 #define EXPERT_RAM_SIZE 8192
221
222 static const char STRING_EXPERT[] = CARTRIDGE_NAME_EXPERT;
223
224 static int expert_load_image(void);
225
226 /* ---------------------------------------------------------------------*/
227
228 uint8_t expert_io1_read(uint16_t addr);
229 uint8_t expert_io1_peek(uint16_t addr);
230 void expert_io1_store(uint16_t addr, uint8_t value);
231
232 static io_source_t expert_io1_device = {
233 CARTRIDGE_NAME_EXPERT, /* name of the device */
234 IO_DETACH_CART, /* use cartridge ID to detach the device when involved in a read-collision */
235 IO_DETACH_NO_RESOURCE, /* does not use a resource for detach */
236 0xde00, 0xde01, 0xff, /* range for the device, regs:$de00-$de01 */
237 0, /* read is never valid */
238 expert_io1_store, /* store function */
239 NULL, /* NO poke function */
240 expert_io1_read, /* read function */
241 expert_io1_peek, /* peek function */
242 NULL, /* TODO: device state information dump function */
243 CARTRIDGE_EXPERT, /* cartridge ID */
244 IO_PRIO_NORMAL, /* normal priority, device read needs to be checked for collisions */
245 0 /* insertion order, gets filled in by the registration function */
246 };
247
248 static const export_resource_t export_res = {
249 CARTRIDGE_NAME_EXPERT, 1, 1, &expert_io1_device, NULL, CARTRIDGE_EXPERT
250 };
251
252 static io_source_list_t *expert_io1_list_item = NULL;
253
254 /* ---------------------------------------------------------------------*/
expert_cart_enabled(void)255 int expert_cart_enabled(void)
256 {
257 if (expert_enabled) {
258 return 1;
259 }
260 return 0;
261 }
262
expert_mode_changed(int mode,void * param)263 static int expert_mode_changed(int mode, void *param)
264 {
265 switch (mode) {
266 case EXPERT_MODE_OFF:
267 case EXPERT_MODE_PRG:
268 case EXPERT_MODE_ON:
269 break;
270 default:
271 return -1;
272 }
273
274 cartmode = mode;
275 DBG(("EXPERT: expert_mode_changed cartmode: %d (%s)\n", cartmode, expert_mode[cartmode]));
276 if (expert_enabled) {
277 switch (mode) {
278 case EXPERT_MODE_PRG:
279 cart_config_changed_slot1(2, EXPERT_PRG, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
280 expert_register_enabled = 1;
281 expert_ramh_enabled = 0;
282 expert_ram_writeable = 1;
283 break;
284 case EXPERT_MODE_ON:
285 cart_config_changed_slot1(2, 2, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
286 expert_register_enabled = 0;
287 expert_ramh_enabled = 0;
288 expert_ram_writeable = 0;
289 break;
290 case EXPERT_MODE_OFF:
291 cart_config_changed_slot1(2, EXPERT_OFF, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
292 expert_register_enabled = 0;
293 expert_ramh_enabled = 0;
294 expert_ram_writeable = 0;
295 break;
296 }
297 }
298
299 return 0;
300 }
301
expert_activate(void)302 static int expert_activate(void)
303 {
304 if (expert_ram == NULL) {
305 expert_ram = lib_malloc(EXPERT_RAM_SIZE);
306 }
307
308 if (!util_check_null_string(expert_filename)) {
309 log_message(LOG_DEFAULT, "Reading Expert Cartridge image %s.", expert_filename);
310 if (expert_load_image() < 0) {
311 log_error(LOG_DEFAULT, "Reading Expert Cartridge image %s failed.", expert_filename);
312 /* only create a new file if no file exists, so we dont accidently overwrite any files */
313 expert_filetype = CARTRIDGE_FILETYPE_BIN;
314 if (!util_file_exists(expert_filename)) {
315 if (expert_flush_image() < 0) {
316 log_error(LOG_DEFAULT, "Creating Expert Cartridge image %s failed.", expert_filename);
317 return -1;
318 }
319 }
320 }
321 }
322
323 return 0;
324 }
325
expert_deactivate(void)326 static int expert_deactivate(void)
327 {
328 if (expert_ram == NULL) {
329 return 0;
330 }
331
332 if (!util_check_null_string(expert_filename)) {
333 if (expert_write_image) {
334 log_message(LOG_DEFAULT, "Writing Expert Cartridge image %s.", expert_filename);
335 if (expert_flush_image() < 0) {
336 log_error(LOG_DEFAULT, "Writing Expert Cartridge image %s failed.", expert_filename);
337 }
338 }
339 }
340
341 lib_free(expert_ram);
342 expert_ram = NULL;
343 return 0;
344 }
345
set_expert_enabled(int value,void * param)346 static int set_expert_enabled(int value, void *param)
347 {
348 int val = value ? 1 : 0;
349
350 DBG(("EXPERT: set enabled: %d:%d\n", expert_enabled, val));
351
352 if (expert_enabled && !val) {
353 DBG(("EXPERT: disable\n"));
354 if (expert_deactivate() < 0) {
355 return -1;
356 }
357 io_source_unregister(expert_io1_list_item);
358 expert_io1_list_item = NULL;
359 export_remove(&export_res);
360 expert_enabled = 0;
361 cart_power_off();
362 } else if (!expert_enabled && val) {
363 DBG(("EXPERT: enable\n"));
364 if (expert_activate() < 0) {
365 return -1;
366 }
367 expert_io1_list_item = io_source_register(&expert_io1_device);
368 if (export_add(&export_res) < 0) {
369 DBG(("EXPERT: set enabled: error\n"));
370 io_source_unregister(expert_io1_list_item);
371 expert_io1_list_item = NULL;
372 expert_enabled = 0;
373 return -1;
374 }
375 expert_enabled = 1;
376 resources_set_int("ExpertCartridgeMode", cartmode);
377 cart_power_off();
378 }
379
380 return 0;
381 }
382
set_expert_rw(int val,void * param)383 static int set_expert_rw(int val, void *param)
384 {
385 expert_write_image = val ? 1 : 0;
386
387 return 0;
388 }
389
390 /* TODO: make sure setting filename works under all conditions */
set_expert_filename(const char * name,void * param)391 static int set_expert_filename(const char *name, void *param)
392 {
393 if (expert_filename != NULL && name != NULL && strcmp(name, expert_filename) == 0) {
394 return 0;
395 }
396
397 if (name != NULL && *name != '\0') {
398 if (util_check_filename_access(name) < 0) {
399 return -1;
400 }
401 }
402
403 if (expert_enabled) {
404 expert_deactivate();
405 }
406 util_string_set(&expert_filename, name);
407 if (expert_enabled) {
408 expert_activate();
409 }
410
411 return 0;
412 }
413
414 /* ---------------------------------------------------------------------*/
415
expert_io1_store(uint16_t addr,uint8_t value)416 void expert_io1_store(uint16_t addr, uint8_t value)
417 {
418 DBG(("EXPERT: io1 wr %04x (%d)\n", addr, value));
419 if ((cartmode == EXPERT_MODE_ON) && (expert_register_enabled == 1)) {
420 cart_config_changed_slot1(2, 3, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
421 expert_ramh_enabled ^= 1;
422 expert_ram_writeable = 0; /* =0 ? */
423 DBG(("EXPERT: ON (regs: %d ramh: %d ramwrite: %d)\n", expert_register_enabled, expert_ramh_enabled, expert_ram_writeable));
424 }
425 }
426
expert_io1_read(uint16_t addr)427 uint8_t expert_io1_read(uint16_t addr)
428 {
429 expert_io1_device.io_source_valid = 0;
430 DBG(("EXPERT: io1 rd %04x (%d)\n", addr, expert_ramh_enabled));
431 if ((cartmode == EXPERT_MODE_ON) && (expert_register_enabled == 1)) {
432 cart_config_changed_slot1(2, 3, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
433 expert_ramh_enabled ^= 1;
434 expert_ram_writeable = 0; /* =0 ? */
435 DBG(("EXPERT: ON (regs: %d ramh: %d ramwrite: %d)\n", expert_register_enabled, expert_ramh_enabled, expert_ram_writeable));
436 }
437 return 0;
438 }
439
expert_io1_peek(uint16_t addr)440 uint8_t expert_io1_peek(uint16_t addr)
441 {
442 return 0;
443 }
444
445 /* ---------------------------------------------------------------------*/
446
expert_roml_read(uint16_t addr)447 uint8_t expert_roml_read(uint16_t addr)
448 {
449 /* DBG(("EXPERT: set expert_roml_read: %x\n", addr)); */
450 if (cartmode == EXPERT_MODE_PRG) {
451 return expert_ram[addr & 0x1fff];
452 } else if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
453 return expert_ram[addr & 0x1fff];
454 } else {
455 return ram_read(addr);
456 }
457 }
458
expert_roml_store(uint16_t addr,uint8_t value)459 void expert_roml_store(uint16_t addr, uint8_t value)
460 {
461 /* DBG(("EXPERT: set expert_roml_store: %x\n", addr)); */
462 if (expert_ram_writeable) {
463 if (cartmode == EXPERT_MODE_PRG) {
464 expert_ram[addr & 0x1fff] = value;
465 } else if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
466 expert_ram[addr & 0x1fff] = value;
467 } else {
468 /* mem_store_without_ultimax(addr, value); */
469 ram_store(addr, value);
470 }
471 } else {
472 ram_store(addr, value);
473 }
474 }
475
expert_romh_read(uint16_t addr)476 uint8_t expert_romh_read(uint16_t addr)
477 {
478 /* DBG(("EXPERT: set expert_romh_read: %x mode %d %02x %02x\n", addr, cartmode, expert_ram[0x1ffe], expert_ram[0x1fff])); */
479 if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
480 return expert_ram[addr & 0x1fff];
481 } else {
482 return mem_read_without_ultimax(addr);
483 }
484 }
485
expert_romh_phi1_read(uint16_t addr,uint8_t * value)486 int expert_romh_phi1_read(uint16_t addr, uint8_t *value)
487 {
488 if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
489 *value = expert_ram[addr & 0x1fff];
490 return CART_READ_VALID;
491 }
492 return CART_READ_C64MEM;
493 }
494
expert_romh_phi2_read(uint16_t addr,uint8_t * value)495 int expert_romh_phi2_read(uint16_t addr, uint8_t *value)
496 {
497 return expert_romh_phi1_read(addr, value);
498 }
499
expert_peek_mem(uint16_t addr,uint8_t * value)500 int expert_peek_mem(uint16_t addr, uint8_t *value)
501 {
502 if (cartmode == EXPERT_MODE_PRG) {
503 if ((addr >= 0x8000) && (addr <= 0x9fff)) {
504 *value = expert_ram[addr & 0x1fff];
505 return CART_READ_VALID;
506 }
507 /* return CART_READ_C64MEM; */
508 } else if (cartmode == EXPERT_MODE_ON) {
509 if ((addr >= 0x8000) && (addr <= 0x9fff)) {
510 *value = expert_ram[addr & 0x1fff];
511 return CART_READ_VALID;
512 } else if (addr >= 0xe000) {
513 if (expert_ramh_enabled) {
514 *value = expert_ram[addr & 0x1fff];
515 return CART_READ_VALID;
516 }
517 }
518 }
519 return CART_READ_THROUGH;
520 }
521
522 /* ---------------------------------------------------------------------*/
523
expert_freeze_allowed(void)524 int expert_freeze_allowed(void)
525 {
526 if (cartmode == EXPERT_MODE_ON) {
527 return 1;
528 }
529 return 0;
530 }
531
expert_freeze(void)532 void expert_freeze(void)
533 {
534 if (cartmode == EXPERT_MODE_ON) {
535 DBG(("EXPERT: freeze\n"));
536 /* DBG(("ram %02x %02x\n", expert_ram[0x1ffe], expert_ram[0x1fff])); */
537 cart_config_changed_slot1(2, EXPERT_ON, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
538 expert_register_enabled = 1;
539 expert_ram_writeable = 1;
540 expert_ramh_enabled = 1;
541 }
542 }
543
expert_ack_nmi(void)544 static void expert_ack_nmi(void)
545 {
546 if (cartmode == EXPERT_MODE_ON) {
547 DBG(("EXPERT:ack nmi\n"));
548 /* DBG(("ram %02x %02x\n", expert_ram[0x1ffe], expert_ram[0x1fff])); */
549 cart_config_changed_slot1(2, EXPERT_ON, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
550 expert_register_enabled = 1;
551 expert_ram_writeable = 1;
552 expert_ramh_enabled = 1;
553 }
554 }
555
expert_reset(void)556 void expert_reset(void)
557 {
558 DBG(("EXPERT: reset\n"));
559 if (cartmode == EXPERT_MODE_ON) {
560 expert_register_enabled = 1;
561 expert_ram_writeable = 1;
562 expert_ramh_enabled = 1;
563 cart_config_changed_slot1(2, 3, CMODE_READ | CMODE_PHI2_RAM);
564 } else if (cartmode == EXPERT_MODE_PRG) {
565 expert_register_enabled = 1;
566 expert_ram_writeable = 1;
567 expert_ramh_enabled = 1;
568 cart_config_changed_slot1(2, EXPERT_PRG, CMODE_READ);
569 } else {
570 expert_register_enabled = 0;
571 expert_ram_writeable = 0;
572 expert_ramh_enabled = 0;
573 cart_config_changed_slot1(2, EXPERT_OFF, CMODE_READ | CMODE_PHI2_RAM);
574 }
575 }
576
577 /* ---------------------------------------------------------------------*/
578
expert_mmu_translate(unsigned int addr,uint8_t ** base,int * start,int * limit)579 void expert_mmu_translate(unsigned int addr, uint8_t **base, int *start, int *limit)
580 {
581 switch (addr & 0xf000) {
582 case 0xf000:
583 case 0xe000:
584 if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
585 *base = expert_ram - 0xe000;
586 *start = 0xe000;
587 *limit = 0xfffd;
588 return;
589 }
590 break;
591 case 0x9000:
592 case 0x8000:
593 if ((cartmode == EXPERT_MODE_PRG)
594 || ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled)) {
595 *base = expert_ram - 0x8000;
596 *start = 0x8000;
597 *limit = 0x9ffd;
598 return;
599 }
600 break;
601 default:
602 break;
603 }
604 *base = NULL;
605 *start = 0;
606 *limit = 0;
607 }
608
expert_config_init(void)609 void expert_config_init(void)
610 {
611 if (expert_enabled) {
612 DBG(("EXPERT: config_init cartmode: %d\n", cartmode));
613 expert_reset();
614 interrupt_set_nmi_trap_func(maincpu_int_status, expert_ack_nmi);
615 }
616 }
617
expert_config_setup(uint8_t * rawcart)618 void expert_config_setup(uint8_t *rawcart)
619 {
620 memcpy(expert_ram, rawcart, EXPERT_RAM_SIZE);
621 /* DBG(("ram %02x %02x\n", expert_ram[0x1ffe], expert_ram[0x1fff])); */
622 }
623
624 /* ---------------------------------------------------------------------*/
625
expert_get_file_name(void)626 const char *expert_get_file_name(void)
627 {
628 return expert_filename;
629 }
630
expert_common_attach(void)631 static int expert_common_attach(void)
632 {
633 DBG(("EXPERT: common attach\n"));
634 if (resources_set_int("ExpertCartridgeEnabled", 1) < 0) {
635 return -1;
636 }
637 if (expert_enabled) {
638 /* Set default mode
639 here we want to load a previously saved image. we use ON as
640 default here.
641 */
642 resources_set_int("ExpertCartridgeMode", EXPERT_MODE_ON);
643 DBG(("EXPERT: common attach ok\n"));
644 return 0;
645 }
646 return -1;
647 }
648
expert_bin_load(const char * filename,uint8_t * rawcart)649 static int expert_bin_load(const char *filename, uint8_t *rawcart)
650 {
651 if (util_file_load(filename, rawcart, EXPERT_RAM_SIZE, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
652 return -1;
653 }
654 expert_filetype = CARTRIDGE_FILETYPE_BIN;
655 return 0;
656 }
657
expert_bin_attach(const char * filename,uint8_t * rawcart)658 int expert_bin_attach(const char *filename, uint8_t *rawcart)
659 {
660 if (expert_bin_load(filename, rawcart) < 0) {
661 return -1;
662 }
663 if (set_expert_filename(filename, NULL) < 0) {
664 return -1;
665 }
666 return expert_common_attach();
667 }
668
expert_bin_save(const char * filename)669 int expert_bin_save(const char *filename)
670 {
671 FILE *fd;
672
673 if (expert_ram == NULL) {
674 DBG(("EXPERT: ERROR expert_ram == NULL\n"));
675 return -1;
676 }
677
678 if (filename == NULL) {
679 return -1;
680 }
681
682 fd = fopen(filename, MODE_WRITE);
683
684 if (fd == NULL) {
685 return -1;
686 }
687
688 if (fwrite(expert_ram, 1, EXPERT_RAM_SIZE, fd) != EXPERT_RAM_SIZE) {
689 fclose(fd);
690 return -1;
691 }
692
693 fclose(fd);
694 return 0;
695 }
696
expert_crt_load(FILE * fd,uint8_t * rawcart)697 static int expert_crt_load(FILE *fd, uint8_t *rawcart)
698 {
699 crt_chip_header_t chip;
700
701 if (crt_read_chip_header(&chip, fd)) {
702 return -1;
703 }
704
705 if (chip.size != EXPERT_RAM_SIZE) {
706 return -1;
707 }
708
709 if (crt_read_chip(rawcart, 0, &chip, fd)) {
710 return -1;
711 }
712 expert_filetype = CARTRIDGE_FILETYPE_CRT;
713 return 0;
714 }
715
expert_crt_attach(FILE * fd,uint8_t * rawcart,const char * filename)716 int expert_crt_attach(FILE *fd, uint8_t *rawcart, const char *filename)
717 {
718 if (expert_crt_load(fd, rawcart) < 0) {
719 return -1;
720 }
721 if (set_expert_filename(filename, NULL) < 0) {
722 return -1;
723 }
724 return expert_common_attach();
725 }
726
expert_crt_save(const char * filename)727 int expert_crt_save(const char *filename)
728 {
729 FILE *fd;
730 crt_chip_header_t chip;
731
732 DBG(("EXPERT: save crt '%s'\n", filename));
733
734 if (expert_ram == NULL) {
735 DBG(("EXPERT: ERROR expert_ram == NULL\n"));
736 return -1;
737 }
738
739 fd = crt_create(filename, CARTRIDGE_EXPERT, 1, 0, STRING_EXPERT);
740
741 if (fd == NULL) {
742 return -1;
743 }
744
745 chip.type = 2; /* Chip type. (= FlashROM?) */
746 chip.bank = 0; /* Bank nr. (= 0) */
747 chip.start = 0x8000; /* Address. (= 0x8000) */
748 chip.size = EXPERT_RAM_SIZE; /* Length. (= 0x2000) */
749
750 /* Write CHIP packet data. */
751 if (crt_write_chip(expert_ram, &chip, fd)) {
752 fclose(fd);
753 return -1;
754 }
755
756 fclose(fd);
757 DBG(("EXPERT: ERROR save crt ok\n"));
758
759 return 0;
760 }
761
expert_load_image(void)762 static int expert_load_image(void)
763 {
764 int res = 0;
765 FILE *fd;
766
767 if (crt_getid(expert_filename) == CARTRIDGE_EXPERT) {
768 fd = fopen(expert_filename, MODE_READ);
769 res = expert_crt_load(fd, expert_ram);
770 fclose(fd);
771 } else {
772 res = expert_bin_load(expert_filename, expert_ram);
773 }
774 return res;
775 }
776
expert_flush_image(void)777 int expert_flush_image(void)
778 {
779 if (expert_filetype == CARTRIDGE_FILETYPE_BIN) {
780 return expert_bin_save(expert_filename);
781 } else if (expert_filetype == CARTRIDGE_FILETYPE_CRT) {
782 return expert_crt_save(expert_filename);
783 }
784 return -1;
785 }
786
expert_detach(void)787 void expert_detach(void)
788 {
789 resources_set_int("ExpertCartridgeEnabled", 0);
790 }
791
expert_enable(void)792 int expert_enable(void)
793 {
794 DBG(("EXPERT: enable\n"));
795 if (resources_set_int("ExpertCartridgeEnabled", 1) < 0) {
796 return -1;
797 }
798 return 0;
799 }
800
801
expert_disable(void)802 int expert_disable(void)
803 {
804 DBG(("EXPERT: disable\n"));
805 if (resources_set_int("ExpertCartridgeEnabled", 0) < 0) {
806 return -1;
807 }
808 return 0;
809 }
810
811
812
813 /* ---------------------------------------------------------------------*/
814
815 /* CARTEXPERT snapshot module format:
816
817 type | name | description
818 -------------------------------------
819 BYTE | mode | cartridge mode
820 BYTE | register enable | register enable flag
821 BYTE | RAM writable | RAM writable flag
822 BYTE | RAMH enable | RAMH enable flag
823 ARRAY | RAM | 8192 BYTES of RAM data
824 */
825
826 static const char snap_module_name[] = "CARTEXPERT";
827 #define SNAP_MAJOR 0
828 #define SNAP_MINOR 0
829
expert_snapshot_write_module(snapshot_t * s)830 int expert_snapshot_write_module(snapshot_t *s)
831 {
832 snapshot_module_t *m;
833
834 m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
835
836 if (m == NULL) {
837 return -1;
838 }
839
840 if (0
841 || (SMW_B(m, (uint8_t)cartmode) < 0)
842 || (SMW_B(m, (uint8_t)expert_register_enabled) < 0)
843 || (SMW_B(m, (uint8_t)expert_ram_writeable) < 0)
844 || (SMW_B(m, (uint8_t)expert_ramh_enabled) < 0)
845 || (SMW_BA(m, expert_ram, EXPERT_RAM_SIZE) < 0)) {
846 snapshot_module_close(m);
847 return -1;
848 }
849
850 return snapshot_module_close(m);
851 }
852
expert_snapshot_read_module(snapshot_t * s)853 int expert_snapshot_read_module(snapshot_t *s)
854 {
855 uint8_t vmajor, vminor;
856 snapshot_module_t *m;
857
858 m = snapshot_module_open(s, snap_module_name, &vmajor, &vminor);
859
860 if (m == NULL) {
861 return -1;
862 }
863
864 /* Do not accept versions higher than current */
865 if (snapshot_version_is_bigger(vmajor, vminor, SNAP_MAJOR, SNAP_MINOR)) {
866 snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
867 snapshot_module_close(m);
868 return -1;
869 }
870
871 expert_ram = lib_malloc(EXPERT_RAM_SIZE);
872
873 if (0
874 || (SMR_B_INT(m, &cartmode) < 0)
875 || (SMR_B_INT(m, &expert_register_enabled) < 0)
876 || (SMR_B_INT(m, &expert_ram_writeable) < 0)
877 || (SMR_B_INT(m, &expert_ramh_enabled) < 0)
878 || (SMR_BA(m, expert_ram, EXPERT_RAM_SIZE) < 0)) {
879 snapshot_module_close(m);
880 lib_free(expert_ram);
881 expert_ram = NULL;
882 return -1;
883 }
884
885 snapshot_module_close(m);
886
887 expert_filetype = 0;
888 expert_write_image = 0;
889 expert_enabled = 1;
890
891 /* FIXME: ugly code duplication to avoid cart_config_changed calls */
892 expert_io1_list_item = io_source_register(&expert_io1_device);
893
894 if (export_add(&export_res) < 0) {
895 lib_free(expert_ram);
896 expert_ram = NULL;
897 io_source_unregister(expert_io1_list_item);
898 expert_io1_list_item = NULL;
899 expert_enabled = 0;
900 return -1;
901 }
902
903 return 0;
904 }
905
906 /* ------------------------------------------------------------------------- */
907
908 static const cmdline_option_t cmdline_options[] =
909 {
910 { "-expert", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
911 NULL, NULL, "ExpertCartridgeEnabled", (resource_value_t)1,
912 NULL, "Enable the Expert Cartridge" },
913 { "+expert", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
914 NULL, NULL, "ExpertCartridgeEnabled", (resource_value_t)0,
915 NULL, "Disable the Expert Cartridge" },
916 { "-expertimagename", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
917 NULL, NULL, "Expertfilename", NULL,
918 "<Name>", "Set Expert Cartridge image name" },
919 { "-expertimagerw", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
920 NULL, NULL, "ExpertImageWrite", (resource_value_t)1,
921 NULL, "Allow writing to Expert Cartridge image" },
922 { "+expertimagerw", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
923 NULL, NULL, "ExpertImageWrite", (resource_value_t)0,
924 NULL, "Do not write to Expert Cartridge image" },
925 { "-expertmode", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
926 NULL, NULL, "ExpertCartridgeMode", NULL,
927 "<Mode>", "Set Expert Cartridge mode (0: Off, 1: Prg, 2: On)" },
928 CMDLINE_LIST_END
929 };
930
expert_cmdline_options_init(void)931 int expert_cmdline_options_init(void)
932 {
933 return cmdline_register_options(cmdline_options);
934 }
935
936 /* ------------------------------------------------------------------------- */
937
938 static const resource_string_t resources_string[] = {
939 { "Expertfilename", "", RES_EVENT_NO, NULL,
940 &expert_filename, set_expert_filename, NULL },
941 RESOURCE_STRING_LIST_END
942 };
943
944 static const resource_int_t resources_int[] = {
945 { "ExpertCartridgeEnabled", 0, RES_EVENT_STRICT, (resource_value_t)0,
946 &expert_enabled, set_expert_enabled, NULL },
947 { "ExpertCartridgeMode", EXPERT_MODE_DEFAULT, RES_EVENT_NO, NULL,
948 &cartmode, expert_mode_changed, NULL },
949 { "ExpertImageWrite", 0, RES_EVENT_STRICT, (resource_value_t)0,
950 &expert_write_image, set_expert_rw, NULL },
951 RESOURCE_INT_LIST_END
952 };
953
expert_resources_init(void)954 int expert_resources_init(void)
955 {
956 if (resources_register_string(resources_string) < 0) {
957 return -1;
958 }
959 return resources_register_int(resources_int);
960 }
961
expert_resources_shutdown(void)962 void expert_resources_shutdown(void)
963 {
964 lib_free(expert_filename);
965 expert_filename = NULL;
966 }
967