1 /*
2  * plus256k.c - +256K EXPANSION emulation.
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 #include "vice.h"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "c64cart.h"
34 #include "c64mem.h"
35 #include "cartio.h"
36 #include "cartridge.h"
37 #include "cmdline.h"
38 #include "lib.h"
39 #include "log.h"
40 #include "machine.h"
41 #include "mem.h"
42 #include "monitor.h"
43 #include "plus256k.h"
44 #include "resources.h"
45 #include "reu.h"
46 #include "snapshot.h"
47 #include "types.h"
48 #include "uiapi.h"
49 #include "util.h"
50 #include "vicii.h"
51 #include "vicii-mem.h"
52 
53 /* PLUS256K registers */
54 static uint8_t plus256k_reg = 0;
55 
56 static log_t plus256k_log = LOG_ERR;
57 
58 static int plus256k_activate(void);
59 static int plus256k_deactivate(void);
60 
61 int plus256k_enabled = 0;
62 
63 static int plus256k_video_bank = 0;
64 static int plus256k_low_bank = 0;
65 static int plus256k_high_bank = 0;
66 static int plus256k_protected = 0;
67 
68 /* Filename of the +256K image.  */
69 static char *plus256k_filename = NULL;
70 
71 uint8_t *plus256k_ram = NULL;
72 
73 /* ------------------------------------------------------------------------- */
74 
plus256k_peek(uint16_t addr)75 static uint8_t plus256k_peek(uint16_t addr)
76 {
77     return plus256k_reg;
78 }
79 
plus256k_ff_read(uint16_t addr)80 static uint8_t plus256k_ff_read(uint16_t addr)
81 {
82     return 0xff;
83 }
84 
plus256k_dump(void)85 static int plus256k_dump(void)
86 {
87     mon_out("$0000-$0FFF bank: %d\n", plus256k_low_bank);
88     mon_out("$1000-$FFFF bank: %d\n", plus256k_high_bank);
89     mon_out("VICII-bank : %d\n", plus256k_video_bank);
90     mon_out("Register protection: %s\n", (plus256k_protected) ? "on" : "off");
91     return 0;
92 }
93 
plus256k_vicii_store(uint16_t addr,uint8_t value)94 static void plus256k_vicii_store(uint16_t addr, uint8_t value)
95 {
96     int new_bank;
97 
98     if (plus256k_protected == 0) {
99         plus256k_reg = value;
100         plus256k_high_bank = (value & 0xc0) >> 6;
101         plus256k_low_bank = value & 3;
102         plus256k_protected = (value & 0x10) >> 4;
103         new_bank = (value & 0xc) >> 2;
104         if (new_bank != plus256k_video_bank) {
105             vicii_set_ram_base(plus256k_ram + (new_bank * 0x10000));
106             plus256k_video_bank = new_bank;
107         }
108     }
109 }
110 
111 /* When the +256K device is active, this device is used instead of the default VICII device */
112 static io_source_t vicii_d000_device = {
113     "VIC-II",              /* name of the device */
114     IO_DETACH_NEVER,       /* chip is never involved in collisions, so no detach */
115     IO_DETACH_NO_RESOURCE, /* does not use a resource for detach */
116     0xd000, 0xd0ff, 0x3f,  /* range for the device, regs:$d000-$d03f, mirrors:$d040-$d0ff */
117     1,                     /* read is always valid */
118     vicii_store,           /* store function */
119     NULL,                  /* NO poke function */
120     vicii_read,            /* read function */
121     vicii_peek,            /* peek function */
122     vicii_dump,            /* device state information dump function */
123     0,                     /* dummy (not a cartridge) */
124     IO_PRIO_HIGH,          /* high priority, device is never involved in collisions */
125     0                      /* insertion order, gets filled in by the registration function */
126 };
127 
128 static io_source_t vicii_d100_device = {
129     "+256K",              /* name of the device */
130     IO_DETACH_RESOURCE,   /* use resource to detach the device when involved in a read-collision */
131     "PLUS256K",           /* resource to set to '0' */
132     0xd100, 0xd1ff, 0x00, /* range for the device, address is ignored, reg:$d100, mirrors:$d101-$d1ff */
133     1,                    /* read is always valid */
134     plus256k_vicii_store, /* store function */
135     NULL,                 /* NO poke function */
136     plus256k_ff_read,     /* read function */
137     plus256k_peek,        /* peek function */
138     plus256k_dump,        /* device state information dump function */
139     CARTRIDGE_PLUS256K,   /* cartridge ID */
140     IO_PRIO_NORMAL,       /* normal priority, device read needs to be checked for collisions */
141     0                     /* insertion order, gets filled in by the registration function */
142 };
143 
144 static io_source_list_t *vicii_d000_list_item = NULL;
145 static io_source_list_t *vicii_d100_list_item = NULL;
146 
set_plus256k_enabled(int value,int disable_reset)147 int set_plus256k_enabled(int value, int disable_reset)
148 {
149     int val = value ? 1 : 0;
150 
151     if (val == plus256k_enabled) {
152         return 0;
153     }
154 
155     if (!val) {
156         if (plus256k_deactivate() < 0) {
157             return -1;
158         }
159         if (!disable_reset) {
160             machine_trigger_reset(MACHINE_RESET_MODE_HARD);
161         }
162         plus256k_enabled = 0;
163         return 0;
164     } else {
165         if (plus256k_activate() < 0) {
166             return -1;
167         }
168         if (!disable_reset) {
169             machine_trigger_reset(MACHINE_RESET_MODE_HARD);
170         }
171         plus256k_enabled = 1;
172         return 0;
173     }
174 }
175 
set_plus256k_filename(const char * name,void * param)176 static int set_plus256k_filename(const char *name, void *param)
177 {
178     if (plus256k_filename != NULL && name != NULL && strcmp(name, plus256k_filename) == 0) {
179         return 0;
180     }
181 
182     if (name != NULL && *name != '\0') {
183         if (util_check_filename_access(name) < 0) {
184             return -1;
185         }
186     }
187 
188     if (plus256k_enabled) {
189         plus256k_deactivate();
190         util_string_set(&plus256k_filename, name);
191         plus256k_activate();
192     } else {
193         util_string_set(&plus256k_filename, name);
194     }
195 
196     return 0;
197 }
198 
199 static const resource_string_t resources_string[] = {
200     { "PLUS256Kfilename", "", RES_EVENT_NO, NULL,
201       &plus256k_filename, set_plus256k_filename, NULL },
202     RESOURCE_STRING_LIST_END
203 };
204 
plus256k_resources_init(void)205 int plus256k_resources_init(void)
206 {
207     return resources_register_string(resources_string);
208 }
209 
plus256k_resources_shutdown(void)210 void plus256k_resources_shutdown(void)
211 {
212     lib_free(plus256k_filename);
213 }
214 
215 /* ------------------------------------------------------------------------- */
216 
217 static const cmdline_option_t cmdline_options[] =
218 {
219     { "-plus256kimage", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
220       NULL, NULL, "PLUS256Kfilename", NULL,
221       "<Name>", "Specify name of PLUS256K image" },
222     CMDLINE_LIST_END
223 };
224 
plus256k_cmdline_options_init(void)225 int plus256k_cmdline_options_init(void)
226 {
227     return cmdline_register_options(cmdline_options);
228 }
229 
230 /* ------------------------------------------------------------------------- */
231 
plus256k_init(void)232 void plus256k_init(void)
233 {
234     plus256k_log = log_open("PLUS256K");
235 }
236 
plus256k_reset(void)237 void plus256k_reset(void)
238 {
239     plus256k_reg = 0;
240     plus256k_video_bank = 0;
241     plus256k_low_bank = 0;
242     plus256k_high_bank = 0;
243     plus256k_protected = 0;
244     if (plus256k_enabled) {
245         vicii_set_ram_base(plus256k_ram);
246     }
247 }
248 
plus256k_activate(void)249 static int plus256k_activate(void)
250 {
251     plus256k_ram = lib_realloc((void *)plus256k_ram, (size_t)0x40000);
252 
253     log_message(plus256k_log, "PLUS256K hack installed.");
254 
255     if (!util_check_null_string(plus256k_filename)) {
256         if (util_file_load(plus256k_filename, plus256k_ram, (size_t)0x40000, UTIL_FILE_LOAD_RAW) < 0) {
257             log_message(plus256k_log, "Reading PLUS256K image %s failed.", plus256k_filename);
258             if (util_file_save(plus256k_filename, plus256k_ram, 0x40000) < 0) {
259                 log_message(plus256k_log, "Creating PLUS256K image %s failed.", plus256k_filename);
260                 return -1;
261             } else {
262                 log_message(plus256k_log, "Creating PLUS256K image %s.", plus256k_filename);
263             }
264         }
265         log_message(plus256k_log, "Reading PLUS256K image %s.", plus256k_filename);
266     }
267     plus256k_reset();
268     c64io_vicii_deinit();
269     vicii_d000_list_item = io_source_register(&vicii_d000_device);
270     vicii_d100_list_item = io_source_register(&vicii_d100_device);
271     return 0;
272 }
273 
plus256k_deactivate(void)274 static int plus256k_deactivate(void)
275 {
276     if (!util_check_null_string(plus256k_filename)) {
277         if (util_file_save(plus256k_filename, plus256k_ram, 0x40000) < 0) {
278             log_message(plus256k_log, "Writing PLUS256K image %s failed.", plus256k_filename);
279             return -1;
280         }
281         log_message(plus256k_log, "Writing PLUS256K image %s.", plus256k_filename);
282     }
283     vicii_set_ram_base(mem_ram);
284     lib_free(plus256k_ram);
285     plus256k_ram = NULL;
286 
287     if (vicii_d000_list_item != NULL) {
288         io_source_unregister(vicii_d000_list_item);
289         vicii_d000_list_item = NULL;
290     }
291 
292     if (vicii_d100_list_item != NULL) {
293         io_source_unregister(vicii_d100_list_item);
294         vicii_d100_list_item = NULL;
295     }
296     c64io_vicii_reinit();
297     return 0;
298 }
299 
plus256k_shutdown(void)300 void plus256k_shutdown(void)
301 {
302     if (plus256k_enabled) {
303         plus256k_deactivate();
304     }
305 }
306 
307 /* ------------------------------------------------------------------------- */
308 
plus256k_ram_low_store(uint16_t addr,uint8_t value)309 void plus256k_ram_low_store(uint16_t addr, uint8_t value)
310 {
311     plus256k_ram[(plus256k_low_bank << 16) + addr] = value;
312 }
313 
plus256k_ram_high_store(uint16_t addr,uint8_t value)314 void plus256k_ram_high_store(uint16_t addr, uint8_t value)
315 {
316     plus256k_ram[(plus256k_high_bank << 16) + addr] = value;
317     if (addr == 0xff00) {
318         reu_dma(-1);
319     }
320 }
321 
plus256k_ram_inject(uint16_t addr,uint8_t value)322 void plus256k_ram_inject(uint16_t addr, uint8_t value)
323 {
324     if (addr < 0x1000) {
325         plus256k_ram_low_store(addr, value);
326     } else {
327         plus256k_ram_high_store(addr, value);
328     }
329 }
330 
plus256k_ram_low_read(uint16_t addr)331 uint8_t plus256k_ram_low_read(uint16_t addr)
332 {
333     return plus256k_ram[(plus256k_low_bank << 16) + addr];
334 }
335 
plus256k_ram_high_read(uint16_t addr)336 uint8_t plus256k_ram_high_read(uint16_t addr)
337 {
338     return plus256k_ram[(plus256k_high_bank * 0x10000) + addr];
339 }
340 
341 /* ------------------------------------------------------------------------- */
342 
343 /* PLUS256K snapshot module format:
344 
345    type  | name          | description
346    -----------------------------------
347    BYTE  | register      | register
348    BYTE  | video bank    | current video bank
349    BYTE  | low bank      | current low bank
350    BYTE  | high bank     | current high bank
351    BYTE  | write protect | write protect flag
352    ARRAY | RAM           | 262144 BYTES of RAM data
353 
354    Note: for some reason this snapshot module revision started at 0.1, so there never was a 0.0
355  */
356 
357 static char snap_module_name[] = "PLUS256K";
358 #define SNAP_MAJOR   0
359 #define SNAP_MINOR   1
360 
plus256k_snapshot_write(struct snapshot_s * s)361 int plus256k_snapshot_write(struct snapshot_s *s)
362 {
363     snapshot_module_t *m;
364 
365     m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
366 
367     if (m == NULL) {
368         return -1;
369     }
370 
371     if (0
372         || SMW_B (m, plus256k_reg) < 0
373         || SMW_B (m, (uint8_t)plus256k_video_bank) < 0
374         || SMW_B (m, (uint8_t)plus256k_low_bank) < 0
375         || SMW_B (m, (uint8_t)plus256k_high_bank) < 0
376         || SMW_B (m, (uint8_t)plus256k_protected) < 0
377         || SMW_BA(m, plus256k_ram, 0x40000) < 0) {
378         snapshot_module_close(m);
379         return -1;
380     }
381 
382     return snapshot_module_close(m);
383 }
384 
plus256k_snapshot_read(struct snapshot_s * s)385 int plus256k_snapshot_read(struct snapshot_s *s)
386 {
387     snapshot_module_t *m;
388     uint8_t vmajor, vminor;
389 
390     m = snapshot_module_open(s, snap_module_name, &vmajor, &vminor);
391 
392     if (m == NULL) {
393         return -1;
394     }
395 
396     /* Do not accept versions higher than current */
397     if (snapshot_version_is_bigger(vmajor, vminor, SNAP_MAJOR, SNAP_MINOR)) {
398         snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
399         goto fail;
400     }
401 
402     /* enable plus256k, without reset */
403     set_plus256k_enabled(1, 1);
404 
405     /* overwrite registers */
406     if (0
407         || SMR_B(m, &plus256k_reg) < 0
408         || SMR_B_INT(m, &plus256k_video_bank) < 0
409         || SMR_B_INT(m, &plus256k_low_bank) < 0
410         || SMR_B_INT(m, &plus256k_high_bank) < 0
411         || SMR_B_INT(m, &plus256k_protected) < 0
412         || SMR_BA(m, plus256k_ram, 0x40000) < 0) {
413         goto fail;
414     }
415 
416     return snapshot_module_close(m);
417 
418 fail:
419     if (m != NULL) {
420         snapshot_module_close(m);
421     }
422 
423     /* disable plus256k, without reset */
424     set_plus256k_enabled(0, 1);
425 
426     return -1;
427 }
428