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