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