1 /*
2  * comal80.c - Cartridge handling, Comal80 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 DEBUGCART */
28 
29 #include "vice.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #define CARTRIDGE_INCLUDE_SLOTMAIN_API
36 #include "c64cartsystem.h"
37 #undef CARTRIDGE_INCLUDE_SLOTMAIN_API
38 #include "c64mem.h"
39 #include "cartio.h"
40 #include "cartridge.h"
41 #include "comal80.h"
42 #include "export.h"
43 #include "monitor.h"
44 #include "snapshot.h"
45 #include "types.h"
46 #include "util.h"
47 #include "crt.h"
48 
49 #ifdef DEBUGCART
50 #define DBG(x) printf x
51 #else
52 #define DBG(x)
53 #endif
54 
55 /*
56     Comal80 Cartridge
57 
58     - 64K ROM (32K mapped to $8000 and 32K mapped to $A000)
59     - free socket for another 64K eprom
60 
61     The cart has 1 (write-only) bank control register which
62     is located at $DE00 and mirrored throughout the $DE00-$DEFF
63     range.
64 
65     bit 7   : exrom?
66     bit 6   : game?
67     bit 5   : unknown function (used by the software to disable the cartridge)
68     bit 4   : unused?
69     bit 3   : unused?
70     bit 2   : selects user eprom (bank MSB)
71     bit 0-1 : selects bank
72 */
73 
74 static int currregval = 0;
75 static int extrarom = 0;
76 
comal80_io1_store(uint16_t addr,uint8_t value)77 static void comal80_io1_store(uint16_t addr, uint8_t value)
78 {
79     int cmode, currbank;
80 
81     currregval = value & 0xc7;
82     currbank = value & 7;
83 
84     switch (value & 0xe0) {
85         case 0xe0:
86             cmode = CMODE_RAM;
87             break;
88         default:
89         case 0x80:
90             cmode = CMODE_16KGAME;
91             break;
92         case 0x40:
93             cmode = CMODE_8KGAME;
94             break;
95     }
96 #ifdef DEBUGCART
97     if ((value != 0x82) && (value != 0x83)) {
98         DBG(("COMAL80: IO1W %04x %02x mode: %d bank: %d\n", addr, value, cmode, currbank));
99     }
100 #endif
101     cart_config_changed_slotmain(0, (uint8_t)(cmode | (currbank << CMODE_BANK_SHIFT)), CMODE_READ);
102 }
103 
comal80_io1_peek(uint16_t addr)104 static uint8_t comal80_io1_peek(uint16_t addr)
105 {
106     return currregval;
107 }
108 
comal80_dump(void)109 static int comal80_dump(void)
110 {
111     mon_out("extra eprom is installed: %s\n", extrarom ? "yes" : "no");
112     mon_out("register value: $%02x\n", (unsigned int)currregval);
113     mon_out(" bank: %d/%d\n", currregval & 7, extrarom ? 8 : 4);
114     return 0;
115 }
116 
117 /* ---------------------------------------------------------------------*/
118 
119 static io_source_t comal80_device = {
120     CARTRIDGE_NAME_COMAL80, /* name of the device */
121     IO_DETACH_CART,         /* use cartridge ID to detach the device when involved in a read-collision */
122     IO_DETACH_NO_RESOURCE,  /* does not use a resource for detach */
123     0xde00, 0xdeff, 0xff,   /* range for the device, address is ignored, reg:$de00, mirrors:$de01-$deff */
124     0,                      /* read never valid, device is write only */
125     comal80_io1_store,      /* store function */
126     NULL,                   /* NO poke function */
127     NULL,                   /* NO read function */
128     comal80_io1_peek,       /* peek function */
129     comal80_dump,           /* device state information dump function */
130     CARTRIDGE_COMAL80,      /* cartridge ID */
131     IO_PRIO_NORMAL,         /* normal priority, device read needs to be checked for collisions */
132     0                       /* insertion order, gets filled in by the registration function */
133 };
134 
135 static io_source_list_t *comal80_list_item = NULL;
136 
137 static const export_resource_t export_res = {
138     CARTRIDGE_NAME_COMAL80, 1, 1, &comal80_device, NULL, CARTRIDGE_COMAL80
139 };
140 
141 /* ---------------------------------------------------------------------*/
142 
comal80_config_init(void)143 void comal80_config_init(void)
144 {
145     cart_config_changed_slotmain(CMODE_16KGAME, CMODE_16KGAME, CMODE_READ);
146     currregval = 0;
147 }
148 
comal80_config_setup(uint8_t * rawcart)149 void comal80_config_setup(uint8_t *rawcart)
150 {
151     memcpy(&roml_banks[0x0000], &rawcart[0x0000], 0x2000);
152     memcpy(&romh_banks[0x0000], &rawcart[0x2000], 0x2000);
153     memcpy(&roml_banks[0x2000], &rawcart[0x4000], 0x2000);
154     memcpy(&romh_banks[0x2000], &rawcart[0x6000], 0x2000);
155     memcpy(&roml_banks[0x4000], &rawcart[0x8000], 0x2000);
156     memcpy(&romh_banks[0x4000], &rawcart[0xa000], 0x2000);
157     memcpy(&roml_banks[0x6000], &rawcart[0xc000], 0x2000);
158     memcpy(&romh_banks[0x6000], &rawcart[0xe000], 0x2000);
159 
160     memset(&roml_banks[0x8000], 0xff, 0x8000);
161     memset(&romh_banks[0x8000], 0xff, 0x8000);
162 
163     if (extrarom) {
164         memcpy(&roml_banks[0x8000], &rawcart[0x10000], 0x2000);
165         memcpy(&romh_banks[0x8000], &rawcart[0x12000], 0x2000);
166         memcpy(&roml_banks[0xa000], &rawcart[0x14000], 0x2000);
167         memcpy(&romh_banks[0xa000], &rawcart[0x16000], 0x2000);
168         memcpy(&roml_banks[0xc000], &rawcart[0x18000], 0x2000);
169         memcpy(&romh_banks[0xc000], &rawcart[0x1a000], 0x2000);
170         memcpy(&roml_banks[0xe000], &rawcart[0x1c000], 0x2000);
171         memcpy(&romh_banks[0xe000], &rawcart[0x1e000], 0x2000);
172     }
173 
174     cart_config_changed_slotmain(CMODE_8KGAME, CMODE_8KGAME, CMODE_READ);
175 }
176 
177 /* ---------------------------------------------------------------------*/
comal80_common_attach(void)178 static int comal80_common_attach(void)
179 {
180     if (export_add(&export_res) < 0) {
181         return -1;
182     }
183     comal80_list_item = io_source_register(&comal80_device);
184     return 0;
185 }
186 
comal80_bin_attach(const char * filename,uint8_t * rawcart)187 int comal80_bin_attach(const char *filename, uint8_t *rawcart)
188 {
189     extrarom = 1;
190     if (util_file_load(filename, rawcart, 0x20000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
191         extrarom = 0;
192         if (util_file_load(filename, rawcart, 0x10000, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
193             return -1;
194         }
195     }
196     return comal80_common_attach();
197 }
198 
comal80_crt_attach(FILE * fd,uint8_t * rawcart)199 int comal80_crt_attach(FILE *fd, uint8_t *rawcart)
200 {
201     crt_chip_header_t chip;
202 
203     extrarom = 0;
204 
205     while (1) {
206         if (crt_read_chip_header(&chip, fd)) {
207             break;
208         }
209 
210         if (chip.start != 0x8000 || chip.size != 0x4000 || chip.bank > 7) {
211             return -1;
212         }
213 
214         if (crt_read_chip(rawcart, chip.bank << 14, &chip, fd)) {
215             return -1;
216         }
217 
218         if (chip.bank > 3) {
219             extrarom = 1;
220         }
221     }
222     return comal80_common_attach();
223 }
224 
comal80_detach(void)225 void comal80_detach(void)
226 {
227     export_remove(&export_res);
228     io_source_unregister(comal80_list_item);
229     comal80_list_item = NULL;
230 }
231 
232 /* ---------------------------------------------------------------------*/
233 
234 /* CARTCOMAL snapshot module format:
235 
236    type  | name     | description
237    ------------------------------
238    BYTE  | register | control register
239    BYTE  | extra rom| image contains extra eprom
240    ARRAY | ROML     | 32768 or 65536 BYTES of ROML data
241    ARRAY | ROMH     | 32768 or 65536 BYTES of ROMH data
242  */
243 
244 static const char snap_module_name[] = "CARTCOMAL";
245 #define SNAP_MAJOR   0
246 #define SNAP_MINOR   1
247 
comal80_snapshot_write_module(snapshot_t * s)248 int comal80_snapshot_write_module(snapshot_t *s)
249 {
250     snapshot_module_t *m;
251 
252     m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
253 
254     if (m == NULL) {
255         return -1;
256     }
257 
258     if (0
259         || (SMW_B(m, (uint8_t)currregval) < 0)
260         || (SMW_B(m, (uint8_t)extrarom) < 0)
261         || (SMW_BA(m, roml_banks, extrarom ? 0x10000 : 0x8000) < 0)
262         || (SMW_BA(m, romh_banks, extrarom ? 0x10000 : 0x8000) < 0)) {
263         snapshot_module_close(m);
264         return -1;
265     }
266 
267     return snapshot_module_close(m);
268 }
269 
comal80_snapshot_read_module(snapshot_t * s)270 int comal80_snapshot_read_module(snapshot_t *s)
271 {
272     uint8_t vmajor, vminor;
273     snapshot_module_t *m;
274 
275     m = snapshot_module_open(s, snap_module_name, &vmajor, &vminor);
276 
277     if (m == NULL) {
278         return -1;
279     }
280 
281     /* Do not accept versions higher than current */
282     if (snapshot_version_is_bigger(vmajor, vminor, SNAP_MAJOR, SNAP_MINOR)) {
283         snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
284         goto fail;
285     }
286 
287     if (0
288         || (SMR_B_INT(m, &currregval) < 0)
289         || (SMR_B_INT(m, &extrarom) < 0)
290         || (SMR_BA(m, roml_banks, extrarom ? 0x10000 : 0x8000) < 0)
291         || (SMR_BA(m, romh_banks, extrarom ? 0x10000 : 0x8000) < 0)) {
292         goto fail;
293     }
294 
295     snapshot_module_close(m);
296 
297     return comal80_common_attach();
298 
299 fail:
300     snapshot_module_close(m);
301     return -1;
302 }
303