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