1 /*
2  * magicdesk.c - Cartridge handling, Magic Desk cart.
3  *
4  * Written by
5  *  Marco van den Heuvel <blackystardust68@yahoo.com>
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 /* #define MAGICDESK_DEBUG */
28 
29 #include "vice.h"
30 
31 #include <stdio.h>
32 #include <string.h>
33 
34 #define CARTRIDGE_INCLUDE_SLOTMAIN_API
35 #include "c64cartsystem.h"
36 #undef CARTRIDGE_INCLUDE_SLOTMAIN_API
37 #include "c64mem.h"
38 #include "cartio.h"
39 #include "cartridge.h"
40 #include "export.h"
41 #include "magicdesk.h"
42 #include "monitor.h"
43 #include "snapshot.h"
44 #include "types.h"
45 #include "util.h"
46 #include "crt.h"
47 
48 #ifdef MAGICDESK_DEBUG
49 #define DBG(x) printf x
50 #else
51 #define DBG(x)
52 #endif
53 
54 /*
55     "Magic Desk" Cartridge
56 
57     - this cart comes in 3 sizes, 32Kb (4 banks), 64Kb (8 banks) and 128Kb (16 banks).
58       - supports "DDI Magic Cart" (32 banks, 256kb)
59       - supports "Magic Desk Clone" homebrew cart (64 banks, 512kb and 128banks, 1MB)
60 
61     - ROM is always mapped in at $8000-$9FFF (8k game).
62 
63     - 1 register at io1 / de00:
64 
65     bit 0-6   bank number
66     bit 7     exrom (1 = cart disabled)
67 */
68 
69 #define MAXBANKS 128
70 
71 static uint8_t regval = 0;
72 static uint8_t bankmask = 0x7f;
73 
magicdesk_io1_store(uint16_t addr,uint8_t value)74 static void magicdesk_io1_store(uint16_t addr, uint8_t value)
75 {
76     regval = value & (0x80 | bankmask);
77     cart_romlbank_set_slotmain(value & bankmask);
78     cart_set_port_game_slotmain(0);
79     if (value & 0x80) {
80         /* turn off cart ROM */
81         cart_set_port_exrom_slotmain(0);
82     } else {
83         cart_set_port_exrom_slotmain(1);
84     }
85     cart_port_config_changed_slotmain();
86     DBG(("MAGICDESK: Reg: %02x (Bank: %d of %d, %s)\n", regval, (regval & bankmask), bankmask + 1, (regval & 0x80) ? "disabled" : "enabled"));
87 }
88 
magicdesk_io1_peek(uint16_t addr)89 static uint8_t magicdesk_io1_peek(uint16_t addr)
90 {
91     return regval;
92 }
93 
magicdesk_dump(void)94 static int magicdesk_dump(void)
95 {
96     mon_out("Reg: %02x (Bank: %d of %d, %s)\n", regval, (regval & bankmask), bankmask + 1, (regval & 0x80) ? "disabled" : "enabled");
97     return 0;
98 }
99 
100 /* ---------------------------------------------------------------------*/
101 
102 static io_source_t magicdesk_device = {
103     CARTRIDGE_NAME_MAGIC_DESK, /* name of the device */
104     IO_DETACH_CART,            /* use cartridge ID to detach the device when involved in a read-collision */
105     IO_DETACH_NO_RESOURCE,     /* does not use a resource for detach */
106     0xde00, 0xdeff, 0xff,      /* range for the device, address is ignored, reg:$de00, mirrors:$de01-$deff */
107     0,                         /* read is never valid, reg is write only */
108     magicdesk_io1_store,       /* store function */
109     NULL,                      /* NO poke function */
110     NULL,                      /* read function */
111     magicdesk_io1_peek,        /* peek function */
112     magicdesk_dump,            /* device state information dump function */
113     CARTRIDGE_MAGIC_DESK,      /* cartridge ID */
114     IO_PRIO_NORMAL,            /* normal priority, device read needs to be checked for collisions */
115     0                          /* insertion order, gets filled in by the registration function */
116 };
117 
118 static io_source_list_t *magicdesk_list_item = NULL;
119 
120 static const export_resource_t export_res = {
121     CARTRIDGE_NAME_MAGIC_DESK, 0, 1, &magicdesk_device, NULL, CARTRIDGE_MAGIC_DESK
122 };
123 
124 /* ---------------------------------------------------------------------*/
125 
magicdesk_config_init(void)126 void magicdesk_config_init(void)
127 {
128     cart_config_changed_slotmain(0, 0, CMODE_READ);
129     magicdesk_io1_store((uint16_t)0xde00, 0);
130 }
131 
magicdesk_config_setup(uint8_t * rawcart)132 void magicdesk_config_setup(uint8_t *rawcart)
133 {
134     memcpy(roml_banks, rawcart, 0x2000 * MAXBANKS);
135     cart_config_changed_slotmain(0, 0, CMODE_READ);
136 }
137 
138 /* ---------------------------------------------------------------------*/
139 
magicdesk_common_attach(void)140 static int magicdesk_common_attach(void)
141 {
142     if (export_add(&export_res) < 0) {
143         return -1;
144     }
145     magicdesk_list_item = io_source_register(&magicdesk_device);
146     return 0;
147 }
148 
magicdesk_bin_attach(const char * filename,uint8_t * rawcart)149 int magicdesk_bin_attach(const char *filename, uint8_t *rawcart)
150 {
151     bankmask = 0x7f;
152     if (util_file_load(filename, rawcart, 0x100000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
153         bankmask = 0x3f;
154         if (util_file_load(filename, rawcart, 0x80000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
155             bankmask = 0x1f;
156             if (util_file_load(filename, rawcart, 0x40000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
157                 bankmask = 0x0f;
158                 if (util_file_load(filename, rawcart, 0x20000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
159                     bankmask = 0x07;
160                     if (util_file_load(filename, rawcart, 0x10000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
161                         bankmask = 0x03;
162                         if (util_file_load(filename, rawcart, 0x8000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
163                             return -1;
164                         }
165                     }
166                 }
167             }
168         }
169     }
170     return magicdesk_common_attach();
171 }
172 
magicdesk_crt_attach(FILE * fd,uint8_t * rawcart)173 int magicdesk_crt_attach(FILE *fd, uint8_t *rawcart)
174 {
175     crt_chip_header_t chip;
176     int lastbank = 0;
177 
178     while (1) {
179         if (crt_read_chip_header(&chip, fd)) {
180             break;
181         }
182         if ((chip.bank >= MAXBANKS) || ((chip.start != 0x8000) && (chip.start != 0xa000)) || (chip.size != 0x2000)) {
183             return -1;
184         }
185         if (crt_read_chip(rawcart, chip.bank << 13, &chip, fd)) {
186             return -1;
187         }
188         if (chip.bank > lastbank) {
189             lastbank = chip.bank;
190         }
191     }
192     if (lastbank >= 128) {
193         /* more than 128 banks does not work */
194         return -1;
195     } else if (lastbank >= 64) {
196         /* min 65, max 128 banks */
197         bankmask = 0x7f;
198     } else if (lastbank >= 32) {
199         /* min 33, max 64 banks */
200         bankmask = 0x3f;
201     } else if (lastbank >= 16) {
202         /* min 17, max 32 banks */
203         bankmask = 0x1f;
204     } else if (lastbank >= 8) {
205         /* min 9, max 16 banks */
206         bankmask = 0x0f;
207     } else if (lastbank >= 4) {
208         /* min 5, max 8 banks */
209         bankmask = 0x07;
210     } else {
211         /* max 4 banks */
212         bankmask = 0x03;
213     }
214     return magicdesk_common_attach();
215 }
216 
magicdesk_detach(void)217 void magicdesk_detach(void)
218 {
219     export_remove(&export_res);
220     io_source_unregister(magicdesk_list_item);
221     magicdesk_list_item = NULL;
222 }
223 
224 /* ---------------------------------------------------------------------*/
225 
226 #define CART_DUMP_VER_MAJOR   0
227 #define CART_DUMP_VER_MINOR   2
228 #define SNAP_MODULE_NAME  "CARTMAGICD"
229 
magicdesk_snapshot_write_module(snapshot_t * s)230 int magicdesk_snapshot_write_module(snapshot_t *s)
231 {
232     snapshot_module_t *m;
233 
234     m = snapshot_module_create(s, SNAP_MODULE_NAME,
235                                CART_DUMP_VER_MAJOR, CART_DUMP_VER_MINOR);
236     if (m == NULL) {
237         return -1;
238     }
239 
240     if (0
241         || (SMW_B(m, (uint8_t)regval) < 0)
242         || (SMW_B(m, (uint8_t)bankmask) < 0)
243         || (SMW_BA(m, roml_banks, 0x2000 * MAXBANKS) < 0)) {
244         snapshot_module_close(m);
245         return -1;
246     }
247 
248     snapshot_module_close(m);
249     return 0;
250 }
251 
magicdesk_snapshot_read_module(snapshot_t * s)252 int magicdesk_snapshot_read_module(snapshot_t *s)
253 {
254     uint8_t vmajor, vminor;
255     snapshot_module_t *m;
256 
257     m = snapshot_module_open(s, SNAP_MODULE_NAME, &vmajor, &vminor);
258     if (m == NULL) {
259         return -1;
260     }
261 
262     if ((vmajor != CART_DUMP_VER_MAJOR) || (vminor != CART_DUMP_VER_MINOR)) {
263         snapshot_module_close(m);
264         return -1;
265     }
266 
267     if (0
268         || (SMR_B(m, &regval) < 0)
269         || (SMR_B(m, &bankmask) < 0)
270         || (SMR_BA(m, roml_banks, 0x2000 * MAXBANKS) < 0)) {
271         snapshot_module_close(m);
272         return -1;
273     }
274 
275     snapshot_module_close(m);
276 
277     if (magicdesk_common_attach() == -1) {
278         return -1;
279     }
280     magicdesk_io1_store(0xde00, regval);
281     return 0;
282 }
283