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 char *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,
234 IO_DETACH_CART,
235 NULL,
236 0xde00, 0xde01, 0xff,
237 0, /* read is never valid */
238 expert_io1_store,
239 expert_io1_read,
240 expert_io1_peek,
241 NULL, /* TODO: dump */
242 CARTRIDGE_EXPERT,
243 0,
244 0
245 };
246
247 static const export_resource_t export_res = {
248 CARTRIDGE_NAME_EXPERT, 1, 1, &expert_io1_device, NULL, CARTRIDGE_EXPERT
249 };
250
251 static io_source_list_t *expert_io1_list_item = NULL;
252
253 /* ---------------------------------------------------------------------*/
expert_cart_enabled(void)254 int expert_cart_enabled(void)
255 {
256 if (expert_enabled) {
257 return 1;
258 }
259 return 0;
260 }
261
expert_mode_changed(int mode,void * param)262 static int expert_mode_changed(int mode, void *param)
263 {
264 switch (mode) {
265 case EXPERT_MODE_OFF:
266 case EXPERT_MODE_PRG:
267 case EXPERT_MODE_ON:
268 break;
269 default:
270 return -1;
271 }
272
273 cartmode = mode;
274 DBG(("EXPERT: expert_mode_changed cartmode: %d (%s)\n", cartmode, expert_mode[cartmode]));
275 if (expert_enabled) {
276 switch (mode) {
277 case EXPERT_MODE_PRG:
278 cart_config_changed_slot1(2, EXPERT_PRG, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
279 expert_register_enabled = 1;
280 expert_ramh_enabled = 0;
281 expert_ram_writeable = 1;
282 break;
283 case EXPERT_MODE_ON:
284 cart_config_changed_slot1(2, 2, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
285 expert_register_enabled = 0;
286 expert_ramh_enabled = 0;
287 expert_ram_writeable = 0;
288 break;
289 case EXPERT_MODE_OFF:
290 cart_config_changed_slot1(2, EXPERT_OFF, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
291 expert_register_enabled = 0;
292 expert_ramh_enabled = 0;
293 expert_ram_writeable = 0;
294 break;
295 }
296 }
297
298 return 0;
299 }
300
expert_activate(void)301 static int expert_activate(void)
302 {
303 if (expert_ram == NULL) {
304 expert_ram = lib_malloc(EXPERT_RAM_SIZE);
305 }
306
307 if (!util_check_null_string(expert_filename)) {
308 log_message(LOG_DEFAULT, "Reading Expert Cartridge image %s.", expert_filename);
309 if (expert_load_image() < 0) {
310 log_error(LOG_DEFAULT, "Reading Expert Cartridge image %s failed.", expert_filename);
311 /* only create a new file if no file exists, so we dont accidently overwrite any files */
312 expert_filetype = CARTRIDGE_FILETYPE_BIN;
313 if (!util_file_exists(expert_filename)) {
314 if (expert_flush_image() < 0) {
315 log_error(LOG_DEFAULT, "Creating Expert Cartridge image %s failed.", expert_filename);
316 return -1;
317 }
318 }
319 }
320 }
321
322 return 0;
323 }
324
expert_deactivate(void)325 static int expert_deactivate(void)
326 {
327 if (expert_ram == NULL) {
328 return 0;
329 }
330
331 if (!util_check_null_string(expert_filename)) {
332 if (expert_write_image) {
333 log_message(LOG_DEFAULT, "Writing Expert Cartridge image %s.", expert_filename);
334 if (expert_flush_image() < 0) {
335 log_error(LOG_DEFAULT, "Writing Expert Cartridge image %s failed.", expert_filename);
336 }
337 }
338 }
339
340 lib_free(expert_ram);
341 expert_ram = NULL;
342 return 0;
343 }
344
set_expert_enabled(int value,void * param)345 static int set_expert_enabled(int value, void *param)
346 {
347 int val = value ? 1 : 0;
348
349 DBG(("EXPERT: set enabled: %d:%d\n", expert_enabled, val));
350
351 if (expert_enabled && !val) {
352 DBG(("EXPERT: disable\n"));
353 if (expert_deactivate() < 0) {
354 return -1;
355 }
356 io_source_unregister(expert_io1_list_item);
357 expert_io1_list_item = NULL;
358 export_remove(&export_res);
359 expert_enabled = 0;
360 cart_power_off();
361 } else if (!expert_enabled && val) {
362 DBG(("EXPERT: enable\n"));
363 if (expert_activate() < 0) {
364 return -1;
365 }
366 expert_io1_list_item = io_source_register(&expert_io1_device);
367 if (export_add(&export_res) < 0) {
368 DBG(("EXPERT: set enabled: error\n"));
369 io_source_unregister(expert_io1_list_item);
370 expert_io1_list_item = NULL;
371 expert_enabled = 0;
372 return -1;
373 }
374 expert_enabled = 1;
375 resources_set_int("ExpertCartridgeMode", cartmode);
376 cart_power_off();
377 }
378
379 return 0;
380 }
381
set_expert_rw(int val,void * param)382 static int set_expert_rw(int val, void *param)
383 {
384 expert_write_image = val ? 1 : 0;
385
386 return 0;
387 }
388
389 /* TODO: make sure setting filename works under all conditions */
set_expert_filename(const char * name,void * param)390 static int set_expert_filename(const char *name, void *param)
391 {
392 if (expert_filename != NULL && name != NULL && strcmp(name, expert_filename) == 0) {
393 return 0;
394 }
395
396 if (name != NULL && *name != '\0') {
397 if (util_check_filename_access(name) < 0) {
398 return -1;
399 }
400 }
401
402 if (expert_enabled) {
403 expert_deactivate();
404 }
405 util_string_set(&expert_filename, name);
406 if (expert_enabled) {
407 expert_activate();
408 }
409
410 return 0;
411 }
412
413 /* ---------------------------------------------------------------------*/
414
expert_io1_store(uint16_t addr,uint8_t value)415 void expert_io1_store(uint16_t addr, uint8_t value)
416 {
417 DBG(("EXPERT: io1 wr %04x (%d)\n", addr, value));
418 if ((cartmode == EXPERT_MODE_ON) && (expert_register_enabled == 1)) {
419 cart_config_changed_slot1(2, 3, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
420 expert_ramh_enabled ^= 1;
421 expert_ram_writeable = 0; /* =0 ? */
422 DBG(("EXPERT: ON (regs: %d ramh: %d ramwrite: %d)\n", expert_register_enabled, expert_ramh_enabled, expert_ram_writeable));
423 }
424 }
425
expert_io1_read(uint16_t addr)426 uint8_t expert_io1_read(uint16_t addr)
427 {
428 expert_io1_device.io_source_valid = 0;
429 DBG(("EXPERT: io1 rd %04x (%d)\n", addr, expert_ramh_enabled));
430 if ((cartmode == EXPERT_MODE_ON) && (expert_register_enabled == 1)) {
431 cart_config_changed_slot1(2, 3, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
432 expert_ramh_enabled ^= 1;
433 expert_ram_writeable = 0; /* =0 ? */
434 DBG(("EXPERT: ON (regs: %d ramh: %d ramwrite: %d)\n", expert_register_enabled, expert_ramh_enabled, expert_ram_writeable));
435 }
436 return 0;
437 }
438
expert_io1_peek(uint16_t addr)439 uint8_t expert_io1_peek(uint16_t addr)
440 {
441 return 0;
442 }
443
444 /* ---------------------------------------------------------------------*/
445
expert_roml_read(uint16_t addr)446 uint8_t expert_roml_read(uint16_t addr)
447 {
448 /* DBG(("EXPERT: set expert_roml_read: %x\n", addr)); */
449 if (cartmode == EXPERT_MODE_PRG) {
450 return expert_ram[addr & 0x1fff];
451 } else if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
452 return expert_ram[addr & 0x1fff];
453 } else {
454 return ram_read(addr);
455 }
456 }
457
expert_roml_store(uint16_t addr,uint8_t value)458 void expert_roml_store(uint16_t addr, uint8_t value)
459 {
460 /* DBG(("EXPERT: set expert_roml_store: %x\n", addr)); */
461 if (expert_ram_writeable) {
462 if (cartmode == EXPERT_MODE_PRG) {
463 expert_ram[addr & 0x1fff] = value;
464 } else if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
465 expert_ram[addr & 0x1fff] = value;
466 } else {
467 /* mem_store_without_ultimax(addr, value); */
468 ram_store(addr, value);
469 }
470 } else {
471 ram_store(addr, value);
472 }
473 }
474
expert_romh_read(uint16_t addr)475 uint8_t expert_romh_read(uint16_t addr)
476 {
477 /* DBG(("EXPERT: set expert_romh_read: %x mode %d %02x %02x\n", addr, cartmode, expert_ram[0x1ffe], expert_ram[0x1fff])); */
478 if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
479 return expert_ram[addr & 0x1fff];
480 } else {
481 return mem_read_without_ultimax(addr);
482 }
483 }
484
expert_romh_phi1_read(uint16_t addr,uint8_t * value)485 int expert_romh_phi1_read(uint16_t addr, uint8_t *value)
486 {
487 if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
488 *value = expert_ram[addr & 0x1fff];
489 return CART_READ_VALID;
490 }
491 return CART_READ_C64MEM;
492 }
493
expert_romh_phi2_read(uint16_t addr,uint8_t * value)494 int expert_romh_phi2_read(uint16_t addr, uint8_t *value)
495 {
496 return expert_romh_phi1_read(addr, value);
497 }
498
expert_peek_mem(uint16_t addr,uint8_t * value)499 int expert_peek_mem(uint16_t addr, uint8_t *value)
500 {
501 if (cartmode == EXPERT_MODE_PRG) {
502 if ((addr >= 0x8000) && (addr <= 0x9fff)) {
503 *value = expert_ram[addr & 0x1fff];
504 return CART_READ_VALID;
505 }
506 /* return CART_READ_C64MEM; */
507 } else if (cartmode == EXPERT_MODE_ON) {
508 if ((addr >= 0x8000) && (addr <= 0x9fff)) {
509 *value = expert_ram[addr & 0x1fff];
510 return CART_READ_VALID;
511 } else if (addr >= 0xe000) {
512 if (expert_ramh_enabled) {
513 *value = expert_ram[addr & 0x1fff];
514 return CART_READ_VALID;
515 }
516 }
517 }
518 return CART_READ_THROUGH;
519 }
520
521 /* ---------------------------------------------------------------------*/
522
expert_freeze_allowed(void)523 int expert_freeze_allowed(void)
524 {
525 if (cartmode == EXPERT_MODE_ON) {
526 return 1;
527 }
528 return 0;
529 }
530
expert_freeze(void)531 void expert_freeze(void)
532 {
533 if (cartmode == EXPERT_MODE_ON) {
534 DBG(("EXPERT: freeze\n"));
535 /* DBG(("ram %02x %02x\n", expert_ram[0x1ffe], expert_ram[0x1fff])); */
536 cart_config_changed_slot1(2, EXPERT_ON, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
537 expert_register_enabled = 1;
538 expert_ram_writeable = 1;
539 expert_ramh_enabled = 1;
540 }
541 }
542
expert_ack_nmi(void)543 static void expert_ack_nmi(void)
544 {
545 if (cartmode == EXPERT_MODE_ON) {
546 DBG(("EXPERT:ack nmi\n"));
547 /* DBG(("ram %02x %02x\n", expert_ram[0x1ffe], expert_ram[0x1fff])); */
548 cart_config_changed_slot1(2, EXPERT_ON, CMODE_READ | CMODE_RELEASE_FREEZE | CMODE_PHI2_RAM);
549 expert_register_enabled = 1;
550 expert_ram_writeable = 1;
551 expert_ramh_enabled = 1;
552 }
553 }
554
expert_reset(void)555 void expert_reset(void)
556 {
557 DBG(("EXPERT: reset\n"));
558 if (cartmode == EXPERT_MODE_ON) {
559 expert_register_enabled = 1;
560 expert_ram_writeable = 1;
561 expert_ramh_enabled = 1;
562 cart_config_changed_slot1(2, 3, CMODE_READ | CMODE_PHI2_RAM);
563 } else if (cartmode == EXPERT_MODE_PRG) {
564 expert_register_enabled = 1;
565 expert_ram_writeable = 1;
566 expert_ramh_enabled = 1;
567 cart_config_changed_slot1(2, EXPERT_PRG, CMODE_READ);
568 } else {
569 expert_register_enabled = 0;
570 expert_ram_writeable = 0;
571 expert_ramh_enabled = 0;
572 cart_config_changed_slot1(2, EXPERT_OFF, CMODE_READ | CMODE_PHI2_RAM);
573 }
574 }
575
576 /* ---------------------------------------------------------------------*/
577
expert_mmu_translate(unsigned int addr,uint8_t ** base,int * start,int * limit)578 void expert_mmu_translate(unsigned int addr, uint8_t **base, int *start, int *limit)
579 {
580 switch (addr & 0xf000) {
581 case 0xf000:
582 case 0xe000:
583 if ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled) {
584 *base = expert_ram - 0xe000;
585 *start = 0xe000;
586 *limit = 0xfffd;
587 return;
588 }
589 break;
590 case 0x9000:
591 case 0x8000:
592 if ((cartmode == EXPERT_MODE_PRG)
593 || ((cartmode == EXPERT_MODE_ON) && expert_ramh_enabled)) {
594 *base = expert_ram - 0x8000;
595 *start = 0x8000;
596 *limit = 0x9ffd;
597 return;
598 }
599 break;
600 default:
601 break;
602 }
603 *base = NULL;
604 *start = 0;
605 *limit = 0;
606 }
607
expert_config_init(void)608 void expert_config_init(void)
609 {
610 if (expert_enabled) {
611 DBG(("EXPERT: config_init cartmode: %d\n", cartmode));
612 expert_reset();
613 interrupt_set_nmi_trap_func(maincpu_int_status, expert_ack_nmi);
614 }
615 }
616
expert_config_setup(uint8_t * rawcart)617 void expert_config_setup(uint8_t *rawcart)
618 {
619 memcpy(expert_ram, rawcart, EXPERT_RAM_SIZE);
620 /* DBG(("ram %02x %02x\n", expert_ram[0x1ffe], expert_ram[0x1fff])); */
621 }
622
623 /* ---------------------------------------------------------------------*/
624
expert_get_file_name(void)625 const char *expert_get_file_name(void)
626 {
627 return expert_filename;
628 }
629
expert_common_attach(void)630 static int expert_common_attach(void)
631 {
632 DBG(("EXPERT: common attach\n"));
633 if (resources_set_int("ExpertCartridgeEnabled", 1) < 0) {
634 return -1;
635 }
636 if (expert_enabled) {
637 /* Set default mode
638 here we want to load a previously saved image. we use ON as
639 default here.
640 */
641 resources_set_int("ExpertCartridgeMode", EXPERT_MODE_ON);
642 DBG(("EXPERT: common attach ok\n"));
643 return 0;
644 }
645 return -1;
646 }
647
expert_bin_load(const char * filename,uint8_t * rawcart)648 static int expert_bin_load(const char *filename, uint8_t *rawcart)
649 {
650 if (util_file_load(filename, rawcart, EXPERT_RAM_SIZE, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
651 return -1;
652 }
653 expert_filetype = CARTRIDGE_FILETYPE_BIN;
654 return 0;
655 }
656
expert_bin_attach(const char * filename,uint8_t * rawcart)657 int expert_bin_attach(const char *filename, uint8_t *rawcart)
658 {
659 if (expert_bin_load(filename, rawcart) < 0) {
660 return -1;
661 }
662 if (set_expert_filename(filename, NULL) < 0) {
663 return -1;
664 }
665 return expert_common_attach();
666 }
667
expert_bin_save(const char * filename)668 int expert_bin_save(const char *filename)
669 {
670 FILE *fd;
671
672 if (expert_ram == NULL) {
673 DBG(("EXPERT: ERROR expert_ram == NULL\n"));
674 return -1;
675 }
676
677 if (filename == NULL) {
678 return -1;
679 }
680
681 fd = fopen(filename, MODE_WRITE);
682
683 if (fd == NULL) {
684 return -1;
685 }
686
687 if (fwrite(expert_ram, 1, EXPERT_RAM_SIZE, fd) != EXPERT_RAM_SIZE) {
688 fclose(fd);
689 return -1;
690 }
691
692 fclose(fd);
693 return 0;
694 }
695
expert_crt_load(FILE * fd,uint8_t * rawcart)696 static int expert_crt_load(FILE *fd, uint8_t *rawcart)
697 {
698 crt_chip_header_t chip;
699
700 if (crt_read_chip_header(&chip, fd)) {
701 return -1;
702 }
703
704 if (chip.size != EXPERT_RAM_SIZE) {
705 return -1;
706 }
707
708 if (crt_read_chip(rawcart, 0, &chip, fd)) {
709 return -1;
710 }
711 expert_filetype = CARTRIDGE_FILETYPE_CRT;
712 return 0;
713 }
714
expert_crt_attach(FILE * fd,uint8_t * rawcart,const char * filename)715 int expert_crt_attach(FILE *fd, uint8_t *rawcart, const char *filename)
716 {
717 if (expert_crt_load(fd, rawcart) < 0) {
718 return -1;
719 }
720 if (set_expert_filename(filename, NULL) < 0) {
721 return -1;
722 }
723 return expert_common_attach();
724 }
725
expert_crt_save(const char * filename)726 int expert_crt_save(const char *filename)
727 {
728 FILE *fd;
729 crt_chip_header_t chip;
730
731 DBG(("EXPERT: save crt '%s'\n", filename));
732
733 if (expert_ram == NULL) {
734 DBG(("EXPERT: ERROR expert_ram == NULL\n"));
735 return -1;
736 }
737
738 fd = crt_create(filename, CARTRIDGE_EXPERT, 1, 0, STRING_EXPERT);
739
740 if (fd == NULL) {
741 return -1;
742 }
743
744 chip.type = 2; /* Chip type. (= FlashROM?) */
745 chip.bank = 0; /* Bank nr. (= 0) */
746 chip.start = 0x8000; /* Address. (= 0x8000) */
747 chip.size = EXPERT_RAM_SIZE; /* Length. (= 0x2000) */
748
749 /* Write CHIP packet data. */
750 if (crt_write_chip(expert_ram, &chip, fd)) {
751 fclose(fd);
752 return -1;
753 }
754
755 fclose(fd);
756 DBG(("EXPERT: ERROR save crt ok\n"));
757
758 return 0;
759 }
760
expert_load_image(void)761 static int expert_load_image(void)
762 {
763 int res = 0;
764 FILE *fd;
765
766 if (crt_getid(expert_filename) == CARTRIDGE_EXPERT) {
767 fd = fopen(expert_filename, MODE_READ);
768 res = expert_crt_load(fd, expert_ram);
769 fclose(fd);
770 } else {
771 res = expert_bin_load(expert_filename, expert_ram);
772 }
773 return res;
774 }
775
expert_flush_image(void)776 int expert_flush_image(void)
777 {
778 if (expert_filetype == CARTRIDGE_FILETYPE_BIN) {
779 return expert_bin_save(expert_filename);
780 } else if (expert_filetype == CARTRIDGE_FILETYPE_CRT) {
781 return expert_crt_save(expert_filename);
782 }
783 return -1;
784 }
785
expert_detach(void)786 void expert_detach(void)
787 {
788 resources_set_int("ExpertCartridgeEnabled", 0);
789 }
790
expert_enable(void)791 int expert_enable(void)
792 {
793 DBG(("EXPERT: enable\n"));
794 if (resources_set_int("ExpertCartridgeEnabled", 1) < 0) {
795 return -1;
796 }
797 return 0;
798 }
799
800
expert_disable(void)801 int expert_disable(void)
802 {
803 DBG(("EXPERT: disable\n"));
804 if (resources_set_int("ExpertCartridgeEnabled", 0) < 0) {
805 return -1;
806 }
807 return 0;
808 }
809
810
811
812 /* ---------------------------------------------------------------------*/
813
814 /* CARTEXPERT snapshot module format:
815
816 type | name | description
817 -------------------------------------
818 BYTE | mode | cartridge mode
819 BYTE | register enable | register enable flag
820 BYTE | RAM writable | RAM writable flag
821 BYTE | RAMH enable | RAMH enable flag
822 ARRAY | RAM | 8192 BYTES of RAM data
823 */
824
825 static char snap_module_name[] = "CARTEXPERT";
826 #define SNAP_MAJOR 0
827 #define SNAP_MINOR 0
828
expert_snapshot_write_module(snapshot_t * s)829 int expert_snapshot_write_module(snapshot_t *s)
830 {
831 snapshot_module_t *m;
832
833 m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
834
835 if (m == NULL) {
836 return -1;
837 }
838
839 if (0
840 || (SMW_B(m, (uint8_t)cartmode) < 0)
841 || (SMW_B(m, (uint8_t)expert_register_enabled) < 0)
842 || (SMW_B(m, (uint8_t)expert_ram_writeable) < 0)
843 || (SMW_B(m, (uint8_t)expert_ramh_enabled) < 0)
844 || (SMW_BA(m, expert_ram, EXPERT_RAM_SIZE) < 0)) {
845 snapshot_module_close(m);
846 return -1;
847 }
848
849 return snapshot_module_close(m);
850 }
851
expert_snapshot_read_module(snapshot_t * s)852 int expert_snapshot_read_module(snapshot_t *s)
853 {
854 uint8_t vmajor, vminor;
855 snapshot_module_t *m;
856
857 m = snapshot_module_open(s, snap_module_name, &vmajor, &vminor);
858
859 if (m == NULL) {
860 return -1;
861 }
862
863 /* Do not accept versions higher than current */
864 if (vmajor > SNAP_MAJOR || vminor > SNAP_MINOR) {
865 snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
866 snapshot_module_close(m);
867 return -1;
868 }
869
870 expert_ram = lib_malloc(EXPERT_RAM_SIZE);
871
872 if (0
873 || (SMR_B_INT(m, &cartmode) < 0)
874 || (SMR_B_INT(m, &expert_register_enabled) < 0)
875 || (SMR_B_INT(m, &expert_ram_writeable) < 0)
876 || (SMR_B_INT(m, &expert_ramh_enabled) < 0)
877 || (SMR_BA(m, expert_ram, EXPERT_RAM_SIZE) < 0)) {
878 snapshot_module_close(m);
879 lib_free(expert_ram);
880 expert_ram = NULL;
881 return -1;
882 }
883
884 snapshot_module_close(m);
885
886 expert_filetype = 0;
887 expert_write_image = 0;
888 expert_enabled = 1;
889
890 /* FIXME: ugly code duplication to avoid cart_config_changed calls */
891 expert_io1_list_item = io_source_register(&expert_io1_device);
892
893 if (export_add(&export_res) < 0) {
894 lib_free(expert_ram);
895 expert_ram = NULL;
896 io_source_unregister(expert_io1_list_item);
897 expert_io1_list_item = NULL;
898 expert_enabled = 0;
899 return -1;
900 }
901
902 return 0;
903 }
904
905 /* ------------------------------------------------------------------------- */
906
907 static const cmdline_option_t cmdline_options[] =
908 {
909 { "-expert", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
910 NULL, NULL, "ExpertCartridgeEnabled", (resource_value_t)1,
911 NULL, "Enable the Expert Cartridge" },
912 { "+expert", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
913 NULL, NULL, "ExpertCartridgeEnabled", (resource_value_t)0,
914 NULL, "Disable the Expert Cartridge" },
915 { "-expertimagename", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
916 NULL, NULL, "Expertfilename", NULL,
917 "<Name>", "Set Expert Cartridge image name" },
918 { "-expertimagerw", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
919 NULL, NULL, "ExpertImageWrite", (resource_value_t)1,
920 NULL, "Allow writing to Expert Cartridge image" },
921 { "+expertimagerw", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
922 NULL, NULL, "ExpertImageWrite", (resource_value_t)0,
923 NULL, "Do not write to Expert Cartridge image" },
924 { "-expertmode", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
925 NULL, NULL, "ExpertCartridgeMode", NULL,
926 "<Mode>", "Set Expert Cartridge mode (0: Off, 1: Prg, 2: On)" },
927 CMDLINE_LIST_END
928 };
929
expert_cmdline_options_init(void)930 int expert_cmdline_options_init(void)
931 {
932 return cmdline_register_options(cmdline_options);
933 }
934
935 /* ------------------------------------------------------------------------- */
936
937 static const resource_string_t resources_string[] = {
938 { "Expertfilename", "", RES_EVENT_NO, NULL,
939 &expert_filename, set_expert_filename, NULL },
940 RESOURCE_STRING_LIST_END
941 };
942
943 static const resource_int_t resources_int[] = {
944 { "ExpertCartridgeEnabled", 0, RES_EVENT_STRICT, (resource_value_t)0,
945 &expert_enabled, set_expert_enabled, NULL },
946 { "ExpertCartridgeMode", EXPERT_MODE_DEFAULT, RES_EVENT_NO, NULL,
947 &cartmode, expert_mode_changed, NULL },
948 { "ExpertImageWrite", 0, RES_EVENT_STRICT, (resource_value_t)0,
949 &expert_write_image, set_expert_rw, NULL },
950 RESOURCE_INT_LIST_END
951 };
952
expert_resources_init(void)953 int expert_resources_init(void)
954 {
955 if (resources_register_string(resources_string) < 0) {
956 return -1;
957 }
958 return resources_register_int(resources_int);
959 }
960
expert_resources_shutdown(void)961 void expert_resources_shutdown(void)
962 {
963 lib_free(expert_filename);
964 expert_filename = NULL;
965 }
966