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,
104     IO_DETACH_CART,
105     NULL,
106     0xde00, 0xdeff, 0xff,
107     0,
108     magicdesk_io1_store,
109     NULL,
110     magicdesk_io1_peek,
111     magicdesk_dump,
112     CARTRIDGE_MAGIC_DESK,
113     0,
114     0
115 };
116 
117 static io_source_list_t *magicdesk_list_item = NULL;
118 
119 static const export_resource_t export_res = {
120     CARTRIDGE_NAME_MAGIC_DESK, 0, 1, &magicdesk_device, NULL, CARTRIDGE_MAGIC_DESK
121 };
122 
123 /* ---------------------------------------------------------------------*/
124 
magicdesk_config_init(void)125 void magicdesk_config_init(void)
126 {
127     cart_config_changed_slotmain(0, 0, CMODE_READ);
128     magicdesk_io1_store((uint16_t)0xde00, 0);
129 }
130 
magicdesk_config_setup(uint8_t * rawcart)131 void magicdesk_config_setup(uint8_t *rawcart)
132 {
133     memcpy(roml_banks, rawcart, 0x2000 * MAXBANKS);
134     cart_config_changed_slotmain(0, 0, CMODE_READ);
135 }
136 
137 /* ---------------------------------------------------------------------*/
138 
magicdesk_common_attach(void)139 static int magicdesk_common_attach(void)
140 {
141     if (export_add(&export_res) < 0) {
142         return -1;
143     }
144     magicdesk_list_item = io_source_register(&magicdesk_device);
145     return 0;
146 }
147 
magicdesk_bin_attach(const char * filename,uint8_t * rawcart)148 int magicdesk_bin_attach(const char *filename, uint8_t *rawcart)
149 {
150     bankmask = 0x7f;
151     if (util_file_load(filename, rawcart, 0x100000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
152         bankmask = 0x3f;
153         if (util_file_load(filename, rawcart, 0x80000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
154             bankmask = 0x1f;
155             if (util_file_load(filename, rawcart, 0x40000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
156                 bankmask = 0x0f;
157                 if (util_file_load(filename, rawcart, 0x20000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
158                     bankmask = 0x07;
159                     if (util_file_load(filename, rawcart, 0x10000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
160                         bankmask = 0x03;
161                         if (util_file_load(filename, rawcart, 0x8000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
162                             return -1;
163                         }
164                     }
165                 }
166             }
167         }
168     }
169     return magicdesk_common_attach();
170 }
171 
magicdesk_crt_attach(FILE * fd,uint8_t * rawcart)172 int magicdesk_crt_attach(FILE *fd, uint8_t *rawcart)
173 {
174     crt_chip_header_t chip;
175     int lastbank = 0;
176 
177     while (1) {
178         if (crt_read_chip_header(&chip, fd)) {
179             break;
180         }
181         if ((chip.bank >= MAXBANKS) || ((chip.start != 0x8000) && (chip.start != 0xa000)) || (chip.size != 0x2000)) {
182             return -1;
183         }
184         if (crt_read_chip(rawcart, chip.bank << 13, &chip, fd)) {
185             return -1;
186         }
187         if (chip.bank > lastbank) {
188             lastbank = chip.bank;
189         }
190     }
191     if (lastbank >= 128) {
192         /* more than 128 banks does not work */
193         return -1;
194     } else if (lastbank >= 64) {
195         /* min 65, max 128 banks */
196         bankmask = 0x7f;
197     } else if (lastbank >= 32) {
198         /* min 33, max 64 banks */
199         bankmask = 0x3f;
200     } else if (lastbank >= 16) {
201         /* min 17, max 32 banks */
202         bankmask = 0x1f;
203     } else if (lastbank >= 8) {
204         /* min 9, max 16 banks */
205         bankmask = 0x0f;
206     } else if (lastbank >= 4) {
207         /* min 5, max 8 banks */
208         bankmask = 0x07;
209     } else {
210         /* max 4 banks */
211         bankmask = 0x03;
212     }
213     return magicdesk_common_attach();
214 }
215 
magicdesk_detach(void)216 void magicdesk_detach(void)
217 {
218     export_remove(&export_res);
219     io_source_unregister(magicdesk_list_item);
220     magicdesk_list_item = NULL;
221 }
222 
223 /* ---------------------------------------------------------------------*/
224 
225 #define CART_DUMP_VER_MAJOR   0
226 #define CART_DUMP_VER_MINOR   2
227 #define SNAP_MODULE_NAME  "CARTMAGICD"
228 
magicdesk_snapshot_write_module(snapshot_t * s)229 int magicdesk_snapshot_write_module(snapshot_t *s)
230 {
231     snapshot_module_t *m;
232 
233     m = snapshot_module_create(s, SNAP_MODULE_NAME,
234                                CART_DUMP_VER_MAJOR, CART_DUMP_VER_MINOR);
235     if (m == NULL) {
236         return -1;
237     }
238 
239     if (0
240         || (SMW_B(m, (uint8_t)regval) < 0)
241         || (SMW_B(m, (uint8_t)bankmask) < 0)
242         || (SMW_BA(m, roml_banks, 0x2000 * MAXBANKS) < 0)) {
243         snapshot_module_close(m);
244         return -1;
245     }
246 
247     snapshot_module_close(m);
248     return 0;
249 }
250 
magicdesk_snapshot_read_module(snapshot_t * s)251 int magicdesk_snapshot_read_module(snapshot_t *s)
252 {
253     uint8_t vmajor, vminor;
254     snapshot_module_t *m;
255 
256     m = snapshot_module_open(s, SNAP_MODULE_NAME, &vmajor, &vminor);
257     if (m == NULL) {
258         return -1;
259     }
260 
261     if ((vmajor != CART_DUMP_VER_MAJOR) || (vminor != CART_DUMP_VER_MINOR)) {
262         snapshot_module_close(m);
263         return -1;
264     }
265 
266     if (0
267         || (SMR_B(m, &regval) < 0)
268         || (SMR_B(m, &bankmask) < 0)
269         || (SMR_BA(m, roml_banks, 0x2000 * MAXBANKS) < 0)) {
270         snapshot_module_close(m);
271         return -1;
272     }
273 
274     snapshot_module_close(m);
275 
276     if (magicdesk_common_attach() == -1) {
277         return -1;
278     }
279     magicdesk_io1_store(0xde00, regval);
280     return 0;
281 }
282