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