1 /*
2  * rrnetmk3.c - Cartridge handling, RR-Net MK3 cart.
3  *
4  * Written by
5  *  Groepaz <groepaz@gmx.net>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #ifdef HAVE_RAWNET
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include "archdep.h"
36 #include "c64cart.h" /* for export_t */
37 #define CARTRIDGE_INCLUDE_SLOTMAIN_API
38 #include "c64cartsystem.h"
39 #undef CARTRIDGE_INCLUDE_SLOTMAIN_API
40 #include "c64mem.h"
41 #include "cartio.h"
42 #include "cartridge.h"
43 #include "cmdline.h"
44 #include "crt.h"
45 #include "cs8900io.h"
46 #include "export.h"
47 #include "lib.h"
48 #include "log.h"
49 #include "machine.h"
50 #include "mem.h"
51 #include "monitor.h"
52 #include "resources.h"
53 #include "snapshot.h"
54 #include "types.h"
55 #include "util.h"
56 
57 #define CARTRIDGE_INCLUDE_PRIVATE_API
58 #include "rrnetmk3.h"
59 #undef CARTRIDGE_INCLUDE_PRIVATE_API
60 
61 /*
62     RR-Net MK3 (Individual Computers)
63 
64     - 8K ROM (EEPROM, writeable in flash mode)
65     - CS8900a in de00-de0f range (similar to other - clockport - devices)
66 
67     - one "register":
68       - a write to $de80 enables the ROM
69       - a write to $de88 disables the ROM
70 */
71 
72 /* #define RRNETMK3DEBUG */
73 
74 #ifdef RRNETMK3DEBUG
75 #define LOG(_x_) log_debug _x_
76 #else
77 #define LOG(_x_)
78 #endif
79 
80 /* RRNETMK3 enable */
81 static int rrnetmk3_enabled;
82 
83 /* RRNETMK3 bios writable */
84 static int rrnetmk3_bios_write;
85 
86 /* Bios file name */
87 static char *rrnetmk3_bios_filename = NULL;
88 
89 /* BIOS changed flag */
90 static int rrnetmk3_bios_changed = 0;
91 
92 static int rrnetmk3_hw_flashjumper = 0; /* status of the flash jumper */
93 
94 static uint8_t rrnetmk3_biossel = 0; /* 0 = ROM is mapped */
95 
96 static log_t rrnetmk3_log = LOG_ERR;
97 
98 static uint8_t rrnetmk3_bios[0x2002];
99 static int rrnetmk3_bios_offset = 0;
100 static int rrnetmk3_bios_type = 0;
101 
102 static const char STRING_RRNETMK3[] = CARTRIDGE_NAME_RRNETMK3;
103 
104 /* ---------------------------------------------------------------------*/
105 
106 /* some prototypes are needed */
107 static void rrnetmk3_io1_store(uint16_t addr, uint8_t value);
108 static uint8_t rrnetmk3_io1_peek(uint16_t addr);
109 static int rrnetmk3_dump(void);
110 
111 static uint8_t rrnetmk3_cs8900_read(uint16_t io_address);
112 static uint8_t rrnetmk3_cs8900_peek(uint16_t io_address);
113 static void rrnetmk3_cs8900_store(uint16_t io_address, uint8_t byte);
114 static int rrnetmk3_cs8900_dump(void);
115 
116 static io_source_t rrnetmk3_io1_device = {
117     CARTRIDGE_NAME_RRNETMK3, /* name of the device */
118     IO_DETACH_RESOURCE,      /* use resource to detach the device when involved in a read-collision */
119     "RRNETMK3",              /* resource to set to '0' */
120     0xde80, 0xde88, 0xff,    /* range for the device, regs:$de80/$de88 */
121     0,                       /* read is never valid, regs are write only */
122     rrnetmk3_io1_store,      /* store function */
123     NULL,                    /* NO poke function */
124     NULL,                    /* NO read function */
125     rrnetmk3_io1_peek,       /* peek function */
126     rrnetmk3_dump,           /* device state information dump function */
127     CARTRIDGE_RRNETMK3,      /* cartridge ID */
128     IO_PRIO_NORMAL,          /* normal priority, device read needs to be checked for collisions */
129     0                        /* insertion order, gets filled in by the registration function */
130 };
131 
132 static io_source_t rrnetmk3_cs8900_io1_device = {
133     CARTRIDGE_NAME_RRNETMK3, /* name of the device */
134     IO_DETACH_RESOURCE,      /* use resource to detach the device when involved in a read-collision */
135     "RRNETMK3",              /* resource to set to '0' */
136     0xde02, 0xde0f, 0x0f,    /* range for the device, regs:$de02-$de0f */
137     0,                       /* read validity is determined by the device upon a read */
138     rrnetmk3_cs8900_store,   /* store function */
139     NULL,                    /* NO poke function */
140     rrnetmk3_cs8900_read,    /* read function */
141     rrnetmk3_cs8900_peek,    /* peek function */
142     rrnetmk3_cs8900_dump,    /* device state information dump function */
143     CARTRIDGE_RRNETMK3,      /* cartridge ID */
144     IO_PRIO_NORMAL,          /* normal priority, device read needs to be checked for collisions */
145     0                        /* insertion order, gets filled in by the registration function */
146 };
147 
148 static io_source_list_t *rrnetmk3_io1_list_item = NULL;
149 static io_source_list_t *rrnetmk3_cs8900_list_item = NULL;
150 
151 static const export_resource_t export_res = {
152     CARTRIDGE_NAME_RRNETMK3, 0, 1, &rrnetmk3_io1_device, NULL, CARTRIDGE_RRNETMK3
153 };
154 
155 /* ---------------------------------------------------------------------*/
156 
157 /* Resets the card */
rrnetmk3_reset(void)158 void rrnetmk3_reset(void)
159 {
160     rrnetmk3_biossel = rrnetmk3_hw_flashjumper; /* disable bios at reset when flash jumper is set */
161     if (rrnetmk3_enabled) {
162         cs8900io_reset();
163     }
164     cart_config_changed_slotmain(CMODE_RAM, (uint8_t)(rrnetmk3_biossel ? CMODE_RAM : CMODE_8KGAME), CMODE_READ);
165 }
166 
set_rrnetmk3_flashjumper(int val,void * param)167 static int set_rrnetmk3_flashjumper(int val, void *param)
168 {
169     rrnetmk3_hw_flashjumper = val ? 1 : 0;
170     LOG(("RRNETMK3 Flashjumper: %d", rrnetmk3_hw_flashjumper));
171     return 0;
172 }
173 
set_rrnetmk3_bios_write(int val,void * param)174 static int set_rrnetmk3_bios_write(int val, void *param)
175 {
176     rrnetmk3_bios_write = val ? 1 : 0;
177     return 0;
178 }
179 
180 /* ---------------------------------------------------------------------*/
181 
rrnetmk3_mmu_translate(unsigned int addr,uint8_t ** base,int * start,int * limit)182 int rrnetmk3_mmu_translate(unsigned int addr, uint8_t **base, int *start, int *limit)
183 {
184     if (!rrnetmk3_biossel) {
185         switch (addr & 0xf000) {
186             case 0x9000:
187             case 0x8000:
188                 *base = &rrnetmk3_bios[rrnetmk3_bios_offset] - 0x8000;
189                 *start = 0x8000;
190                 *limit = 0x9ffd;
191                 return CART_READ_VALID;
192             default:
193                 break;
194         }
195     }
196     return CART_READ_THROUGH;
197 }
198 
rrnetmk3_config_init(void)199 void rrnetmk3_config_init(void)
200 {
201     LOG(("RRNETMK3 rrnetmk3_config_init"));
202     rrnetmk3_biossel = rrnetmk3_hw_flashjumper; /* disable bios at reset when flash jumper is set */
203     cart_config_changed_slotmain(CMODE_RAM, (uint8_t)(rrnetmk3_biossel ? CMODE_RAM : CMODE_8KGAME), CMODE_READ);
204 }
205 
rrnetmk3_io1_store(uint16_t addr,uint8_t value)206 static void rrnetmk3_io1_store(uint16_t addr, uint8_t value)
207 {
208     LOG(("RRNETMK3: IO1 ST %04x %02x", addr, value));
209     switch (addr) {
210         case 0x80:      /* ROM_ENABLE */
211             rrnetmk3_biossel = 0;
212             cart_config_changed_slotmain(CMODE_RAM, CMODE_8KGAME, CMODE_READ);
213             break;
214         case 0x88:      /* ROM_DISABLE */
215             rrnetmk3_biossel = 1;
216             cart_config_changed_slotmain(CMODE_RAM, CMODE_RAM, CMODE_READ);
217             break;
218         default:      /* Not for us */
219             return;
220     }
221 }
222 
rrnetmk3_io1_peek(uint16_t addr)223 static uint8_t rrnetmk3_io1_peek(uint16_t addr)
224 {
225     switch (addr) {
226         case 0x80:      /* ROM_ENABLE */
227             return rrnetmk3_biossel;
228         case 0x88:      /* ROM_DISABLE */
229             return rrnetmk3_biossel;
230     }
231     return 0;
232 }
233 
234 /* ---------------------------------------------------------------------*/
235 
rrnetmk3_cs8900_read(uint16_t address)236 static uint8_t rrnetmk3_cs8900_read(uint16_t address)
237 {
238     if (address < 0x02) {
239         rrnetmk3_cs8900_io1_device.io_source_valid = 0;
240         return 0;
241     }
242     rrnetmk3_cs8900_io1_device.io_source_valid = 1;
243     if (address > 0x0b) {
244         return rrnetmk3_bios[(0x1ff0 + address) + rrnetmk3_bios_offset];
245     }
246     address ^= 0x08;
247     return cs8900io_read(address);
248 }
249 
rrnetmk3_cs8900_peek(uint16_t address)250 static uint8_t rrnetmk3_cs8900_peek(uint16_t address)
251 {
252     if (address < 0x02) {
253         return 0;
254     }
255     if (address > 0x0b) {
256         return rrnetmk3_bios[(0x1ff0 + address) + rrnetmk3_bios_offset];
257     }
258     address ^= 0x08;
259 
260     return cs8900io_read(address);
261 }
262 
rrnetmk3_cs8900_store(uint16_t address,uint8_t byte)263 static void rrnetmk3_cs8900_store(uint16_t address, uint8_t byte)
264 {
265     if (address < 0x02) {
266         return;
267     }
268     address ^= 0x08;
269 
270     cs8900io_store(address, byte);
271 }
272 
rrnetmk3_cs8900_dump(void)273 static int rrnetmk3_cs8900_dump(void)
274 {
275     cs8900io_dump();
276     return 0;
277 }
278 
279 /* ---------------------------------------------------------------------*/
280 
rrnetmk3_dump(void)281 static int rrnetmk3_dump(void)
282 {
283     mon_out("Flashmode jumper is %s.\n", rrnetmk3_hw_flashjumper ? "set" : "not set");
284     mon_out("ROM is %s.\n", rrnetmk3_biossel ? "not enabled" : "enabled");
285 
286     return 0;
287 }
288 
289 /* ---------------------------------------------------------------------*/
290 
rrnetmk3_roml_read(uint16_t addr)291 int rrnetmk3_roml_read(uint16_t addr)
292 {
293     if (!rrnetmk3_biossel) {
294         return rrnetmk3_bios[(addr & 0x1fff) + rrnetmk3_bios_offset];
295     }
296     return mem_ram[addr];
297 }
298 
rrnetmk3_peek_mem(export_t * ex,uint16_t addr,uint8_t * value)299 int rrnetmk3_peek_mem(export_t *ex, uint16_t addr, uint8_t *value)
300 {
301     if ((addr >= 0x8000) && (addr <= 0x9fff)) {
302         if (!rrnetmk3_biossel) {
303             *value = rrnetmk3_bios[(addr & 0x1fff) + rrnetmk3_bios_offset];
304             return CART_READ_VALID;
305         }
306     }
307     return CART_READ_THROUGH;
308 }
309 
rrnetmk3_roml_store(uint16_t addr,uint8_t byte)310 int rrnetmk3_roml_store(uint16_t addr, uint8_t byte)
311 {
312     if (!rrnetmk3_biossel) {
313         if (rrnetmk3_hw_flashjumper) {
314             LOG(("RRNETMK3 Flash w %04x %02x", addr, byte));
315             if (rrnetmk3_bios[(addr & 0x1fff) + rrnetmk3_bios_offset] != byte) {
316                 rrnetmk3_bios[(addr & 0x1fff) + rrnetmk3_bios_offset] = byte;
317                 rrnetmk3_bios_changed = 1;
318             }
319         }
320         return 1;
321     }
322     return 0;
323 }
324 
325 /* ---------------------------------------------------------------------*/
326 
327 static const resource_int_t resources_int[] = {
328     { "RRNETMK3_flashjumper", 0, RES_EVENT_NO, NULL,
329       &rrnetmk3_hw_flashjumper, set_rrnetmk3_flashjumper, NULL },
330     { "RRNETMK3_bios_write", 0, RES_EVENT_NO, NULL,
331       &rrnetmk3_bios_write, set_rrnetmk3_bios_write, NULL },
332     RESOURCE_INT_LIST_END
333 };
334 
rrnetmk3_resources_init(void)335 int rrnetmk3_resources_init(void)
336 {
337     return resources_register_int(resources_int);
338 }
339 
rrnetmk3_resources_shutdown(void)340 void rrnetmk3_resources_shutdown(void)
341 {
342 }
343 
344 /* ------------------------------------------------------------------------- */
345 
346 static const cmdline_option_t cmdline_options[] =
347 {
348     { "-rrnetmk3bioswrite", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
349       NULL, NULL, "RRNETMK3_bios_write", (resource_value_t)1,
350       NULL, "Save the RRNETMK3 bios when changed" },
351     { "+rrnetmk3bioswrite", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
352       NULL, NULL, "RRNETMK3_bios_write", (resource_value_t)0,
353       NULL, "Do not save the RRNETMK3 bios when changed" },
354     { "-rrnetmk3flash", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
355       NULL, NULL, "RRNETMK3_flashjumper", (resource_value_t)1,
356       NULL, "Set the RRNETMK3 Flash Jumper" },
357     { "+rrnetmk3flash", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
358       NULL, NULL, "RRNETMK3_flashjumper", (resource_value_t)0,
359       NULL, "Remove the RRNETMK3 Flash Jumper" },
360     CMDLINE_LIST_END
361 };
362 
rrnetmk3_cmdline_options_init(void)363 int rrnetmk3_cmdline_options_init(void)
364 {
365     return cmdline_register_options(cmdline_options);
366 }
367 
368 /* ------------------------------------------------------------------------- */
369 
rrnetmk3_init(void)370 void rrnetmk3_init(void)
371 {
372     rrnetmk3_log = log_open("RRNETMK3");
373     cs8900io_init();
374 }
375 
rrnetmk3_config_setup(uint8_t * rawcart)376 void rrnetmk3_config_setup(uint8_t *rawcart)
377 {
378     memcpy(rrnetmk3_bios, rawcart, 0x2000 + rrnetmk3_bios_offset);
379 }
380 
rrnetmk3_common_attach(void)381 static int rrnetmk3_common_attach(void)
382 {
383     LOG(("RRNETMK3: rrnetmk3_common_attach '%s'", rrnetmk3_bios_filename));
384     cart_power_off();
385     /* if the param is == NULL, then we should actually set the resource */
386     if (export_add(&export_res) < 0) {
387         LOG(("RRNETMK3: export did not register"));
388         return -1;
389     } else {
390         LOG(("RRNETMK3: export registered"));
391         if (cs8900io_enable(CARTRIDGE_NAME_RRNETMK3) < 0) {
392             return -1;
393         }
394         rrnetmk3_bios_changed = 0;
395         rrnetmk3_enabled = 1;
396         cart_set_port_exrom_slotmain(1);
397         cart_port_config_changed_slotmain();
398         rrnetmk3_io1_list_item = io_source_register(&rrnetmk3_io1_device);
399         rrnetmk3_cs8900_list_item = io_source_register(&rrnetmk3_cs8900_io1_device);
400         rrnetmk3_reset();
401     }
402     return 0;
403 }
404 
rrnetmk3_bin_save(const char * filename)405 int rrnetmk3_bin_save(const char *filename)
406 {
407     FILE *fd;
408     size_t ret;
409 
410     if (filename == NULL) {
411         return -1;
412     }
413 
414     fd = fopen(filename, MODE_WRITE);
415     if (fd == NULL) {
416         return -1;
417     }
418 
419     ret = fwrite(rrnetmk3_bios, 1, 0x2000 + rrnetmk3_bios_offset, fd);
420     fclose(fd);
421     if (ret != (0x2000 + rrnetmk3_bios_offset)) {
422         return -1;
423     }
424     rrnetmk3_bios_changed = 0;
425     return 0;
426 }
427 
rrnetmk3_crt_save(const char * filename)428 int rrnetmk3_crt_save(const char *filename)
429 {
430     FILE *fd;
431     crt_chip_header_t chip;
432 
433     fd = crt_create(filename, CARTRIDGE_RRNETMK3, 1, 0, STRING_RRNETMK3);
434 
435     if (fd == NULL) {
436         return -1;
437     }
438 
439     chip.type = 2;
440     chip.size = 0x2000;
441     chip.start = 0x8000;
442     chip.bank = 0;
443 
444     if (crt_write_chip(rrnetmk3_bios, &chip, fd)) {
445         fclose(fd);
446         return -1;
447     }
448 
449     fclose(fd);
450     return 0;
451 }
452 
rrnetmk3_bin_attach(const char * filename,uint8_t * rawcart)453 int rrnetmk3_bin_attach(const char *filename, uint8_t *rawcart)
454 {
455     int amount_read = 0;
456     FILE *fd;
457 
458     fd = fopen(filename, MODE_READ);
459     if (!fd) {
460         return -1;
461     }
462 
463     amount_read = (int)fread(rawcart, 1, 0x2002, fd);
464     fclose(fd);
465 
466     if (amount_read != 0x2000 && amount_read != 0x2002) {
467         return -1;
468     }
469 
470     rrnetmk3_bios_offset = amount_read & 3;
471     rrnetmk3_bios_type = CARTRIDGE_FILETYPE_BIN;
472     rrnetmk3_bios_filename = lib_strdup(filename);
473     return rrnetmk3_common_attach();
474 }
475 
rrnetmk3_crt_attach(FILE * fd,uint8_t * rawcart,const char * filename)476 int rrnetmk3_crt_attach(FILE *fd, uint8_t *rawcart, const char *filename)
477 {
478     crt_chip_header_t chip;
479 
480     if (crt_read_chip_header(&chip, fd)) {
481         return -1;
482     }
483 
484     if (chip.bank > 1 || chip.size != 0x2000) {
485         return -1;
486     }
487 
488     if (crt_read_chip(rawcart, 0, &chip, fd)) {
489         return -1;
490     }
491 
492     rrnetmk3_bios_offset = 0;
493     rrnetmk3_bios_type = CARTRIDGE_FILETYPE_CRT;
494     rrnetmk3_bios_filename = lib_strdup(filename);
495     return rrnetmk3_common_attach();
496 }
497 
rrnetmk3_flush_image(void)498 int rrnetmk3_flush_image(void)
499 {
500     if (rrnetmk3_bios_type == CARTRIDGE_FILETYPE_BIN) {
501         return rrnetmk3_bin_save(rrnetmk3_bios_filename);
502     } else if (rrnetmk3_bios_type == CARTRIDGE_FILETYPE_CRT) {
503         return rrnetmk3_crt_save(rrnetmk3_bios_filename);
504     }
505     return -1;
506 }
507 
rrnetmk3_detach(void)508 void rrnetmk3_detach(void)
509 {
510     /* flush_image */
511     if (rrnetmk3_bios_changed && rrnetmk3_bios_write) {
512         rrnetmk3_flush_image();
513     }
514     cart_power_off();
515     export_remove(&export_res);
516 #ifdef HAVE_FTE
517     cs8900io_disable();
518 #endif
519     rrnetmk3_enabled = 0;
520     cart_set_port_exrom_slotmain(0);
521     cart_port_config_changed_slotmain();
522     io_source_unregister(rrnetmk3_io1_list_item);
523     rrnetmk3_io1_list_item = NULL;
524     io_source_unregister(rrnetmk3_cs8900_list_item);
525     rrnetmk3_cs8900_list_item = NULL;
526     lib_free(rrnetmk3_bios_filename);
527     rrnetmk3_bios_filename = NULL;
528 }
529 
530 /* ---------------------------------------------------------------------*/
531 /*    snapshot support functions                                             */
532 
533 #define CART_DUMP_VER_MAJOR   0
534 #define CART_DUMP_VER_MINOR   0
535 #define SNAP_MODULE_NAME  "CARTRRNETMK3"
536 
537 /* FIXME: implement snapshot support */
rrnetmk3_snapshot_write_module(snapshot_t * s)538 int rrnetmk3_snapshot_write_module(snapshot_t *s)
539 {
540     snapshot_module_t *m;
541 
542     m = snapshot_module_create(s, SNAP_MODULE_NAME,
543                                CART_DUMP_VER_MAJOR, CART_DUMP_VER_MINOR);
544     if (m == NULL) {
545         return -1;
546     }
547 
548     snapshot_set_error(SNAPSHOT_MODULE_NOT_IMPLEMENTED);
549     return -1;
550 #if 0
551     if (0) {
552         snapshot_module_close(m);
553         return -1;
554     }
555 
556     snapshot_module_close(m);
557     return 0;
558 #endif
559 }
560 
rrnetmk3_snapshot_read_module(snapshot_t * s)561 int rrnetmk3_snapshot_read_module(snapshot_t *s)
562 {
563     return -1;
564 #if 0
565     uint8_t vmajor, vminor;
566     snapshot_module_t *m;
567 
568     m = snapshot_module_open(s, SNAP_MODULE_NAME, &vmajor, &vminor);
569     if (m == NULL) {
570         return -1;
571     }
572 
573     if ((vmajor != CART_DUMP_VER_MAJOR) || (vminor != CART_DUMP_VER_MINOR)) {
574         snapshot_module_close(m);
575         return -1;
576     }
577 
578     if (0) {
579         snapshot_module_close(m);
580         return -1;
581     }
582 
583     snapshot_module_close(m);
584 
585     if (rrnetmk3_common_attach() < 0) {
586         return -1;
587     }
588     return 0;
589 #endif
590 }
591 
592 #endif
593 
594