1 /*
2  * megacart.c -- VIC20 Mega-Cart emulation.
3  *
4  * Written by
5  *  Daniel Kahlin <daniel@kahlin.net>
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 "archdep.h"
34 #include "cartio.h"
35 #include "cartridge.h"
36 #include "cmdline.h"
37 #include "export.h"
38 #include "lib.h"
39 #include "log.h"
40 #include "machine.h"
41 #include "megacart.h"
42 #include "mem.h"
43 #include "monitor.h"
44 #include "resources.h"
45 #include "snapshot.h"
46 #include "types.h"
47 #include "util.h"
48 #include "vic20cart.h"
49 #include "vic20cartmem.h"
50 #include "vic20mem.h"
51 #include "zfile.h"
52 
53 /* ------------------------------------------------------------------------- */
54 
55 /*
56  * Cartridge RAM
57  *
58  * Mapping
59  *      RAM                 VIC20
60  *   0x0000 - 0x1fff  ->  0xa000 - 0xbfff
61  *   0x2000 - 0x7fff  ->  0x2000 - 0x7fff
62  *
63  * (by reasoning around minimal decoding, may be different on actual HW)
64  */
65 #define CART_RAM_SIZE 0x8000
66 static uint8_t *cart_ram = NULL;
67 
68 /*
69  * Cartridge NvRAM
70  *
71  * Mapping
72  *      NvRAM                 VIC20
73  *   0x0400 - 0x0fff  ->  0x0400 - 0x0fff
74  *   0x1800 - 0x1fff  ->  0x9800 - 0x9fff
75  *
76  * (by reasoning around minimal decoding, may be different on actual HW)
77  */
78 #define CART_NVRAM_SIZE 0x2000
79 static uint8_t *cart_nvram = NULL;
80 
81 /*
82  * Cartridge ROM
83  *
84  * Mapping
85  *      ROM
86  *   0x000000 - 0x0fffff  ->  Low ROM: banks 0x00-0x7f
87  *   0x100000 - 0x1fffff  ->  High ROM: banks 0x00-0x7f
88  *
89  */
90 #define CART_ROM_SIZE 0x200000
91 static uint8_t *cart_rom = NULL;
92 
93 /* Cartridge States */
94 static enum { BUTTON_RESET, SOFTWARE_RESET } reset_mode = BUTTON_RESET;
95 static int oe_flop = 0;
96 static int nvram_en_flop = 0;
97 static uint8_t bank_low_reg = 0;
98 static uint8_t bank_high_reg = 0;
99 
100 /* Resource variables */
101 static char *nvram_filename = NULL;
102 static int nvram_writeback = 0;
103 
104 static log_t megacart_log = LOG_ERR;
105 
106 /* ------------------------------------------------------------------------- */
107 
108 /* helper pointers */
109 static uint8_t *cart_rom_low;
110 static uint8_t *cart_rom_high;
111 
112 /* ------------------------------------------------------------------------- */
113 
114 /* Some prototypes are needed */
115 static uint8_t megacart_io2_read(uint16_t addr);
116 static void megacart_io2_store(uint16_t addr, uint8_t value);
117 static uint8_t megacart_io3_read(uint16_t addr);
118 static uint8_t megacart_io3_peek(uint16_t addr);
119 static void megacart_io3_store(uint16_t addr, uint8_t value);
120 static int megacart_mon_dump(void);
121 
122 static io_source_t megacart_io2_device = {
123     CARTRIDGE_VIC20_NAME_MEGACART,
124     IO_DETACH_CART,
125     NULL,
126     0x9800, 0x9bff, 0x3ff,
127     0,
128     megacart_io2_store,
129     megacart_io2_read,
130     NULL, /* TODO: peek */
131     megacart_mon_dump,
132     CARTRIDGE_VIC20_MEGACART,
133     0,
134     0
135 };
136 
137 static io_source_t megacart_io3_device = {
138     CARTRIDGE_VIC20_NAME_MEGACART,
139     IO_DETACH_CART,
140     NULL,
141     0x9c00, 0x9fff, 0x3ff,
142     0,
143     megacart_io3_store,
144     megacart_io3_read,
145     megacart_io3_peek,
146     megacart_mon_dump,
147     CARTRIDGE_VIC20_MEGACART,
148     0,
149     0
150 };
151 
152 static io_source_list_t *megacart_io2_list_item = NULL;
153 static io_source_list_t *megacart_io3_list_item = NULL;
154 
155 static const export_resource_t export_res = {
156     CARTRIDGE_VIC20_NAME_MEGACART, 0, 0, &megacart_io2_device, &megacart_io3_device, CARTRIDGE_VIC20_MEGACART
157 };
158 
159 /* ------------------------------------------------------------------------- */
160 
161 /* read 0x0400-0x0fff */
megacart_ram123_read(uint16_t addr)162 uint8_t megacart_ram123_read(uint16_t addr)
163 {
164     if (nvram_en_flop) {
165         return cart_nvram[addr & 0x1fff];
166     } else {
167         return vic20_v_bus_last_data;
168     }
169 }
170 
171 /* store 0x0400-0x0fff */
megacart_ram123_store(uint16_t addr,uint8_t value)172 void megacart_ram123_store(uint16_t addr, uint8_t value)
173 {
174     if (nvram_en_flop) {
175         cart_nvram[addr & 0x1fff] = value;
176     }
177 }
178 
179 /* read 0x2000-0x7fff */
megacart_blk123_read(uint16_t addr)180 uint8_t megacart_blk123_read(uint16_t addr)
181 {
182     uint8_t bank_low;
183     uint8_t bank_high;
184     int ram_low_en;
185     int ram_high_en;
186 
187     /* get bank registers */
188     bank_low = (oe_flop) ? bank_low_reg : 0x7f;
189     bank_high = (oe_flop) ? bank_high_reg : 0x7f;
190 
191     /* determine flags from bank registers. */
192     ram_low_en = (bank_low & 0x80) ? 1 : 0;
193     ram_high_en = (bank_high & 0x80) ? 1 : 0;
194 
195     if (!ram_low_en) {
196         return cart_rom_low[(addr & 0x1fff) | (bank_low * 0x2000)];
197     } else {
198         if (ram_high_en) {
199             return cart_ram[addr];
200         }
201     }
202 
203     return vic20_cpu_last_data;
204 }
205 
206 /* store 0x2000-0x7fff */
megacart_blk123_store(uint16_t addr,uint8_t value)207 void megacart_blk123_store(uint16_t addr, uint8_t value)
208 {
209     uint8_t bank_low;
210     uint8_t bank_high;
211     int ram_low_en;
212     int ram_high_en;
213     int ram_wp;
214 
215     /* get bank registers */
216     bank_low = (oe_flop) ? bank_low_reg : 0x7f;
217     bank_high = (oe_flop) ? bank_high_reg : 0x7f;
218 
219     /* determine flags from bank registers. */
220     ram_low_en = (bank_low & 0x80) ? 1 : 0;
221     ram_high_en = (bank_high & 0x80) ? 1 : 0;
222     ram_wp = (bank_high & 0x40) ? 0 : 1;
223 
224     if (!ram_wp && (ram_low_en && ram_high_en)) {
225         cart_ram[addr] = value;
226     }
227 }
228 
229 /* read 0xa000-0xbfff */
megacart_blk5_read(uint16_t addr)230 uint8_t megacart_blk5_read(uint16_t addr)
231 {
232     uint8_t bank_low;
233     uint8_t bank_high;
234     int ram_low_en;
235     int ram_high_en;
236 
237     /* get bank registers */
238     bank_low = (oe_flop) ? bank_low_reg : 0x7f;
239     bank_high = (oe_flop) ? bank_high_reg : 0x7f;
240 
241     /* determine flags from bank registers. */
242     ram_low_en = (bank_low & 0x80) ? 1 : 0;
243     ram_high_en = (bank_high & 0x80) ? 1 : 0;
244 
245     if (!ram_high_en) {
246         return cart_rom_high[(addr & 0x1fff) | (bank_high * 0x2000)];
247     } else {
248         if (!ram_low_en) {
249             return cart_rom_low[(addr & 0x1fff) | (bank_low * 0x2000)];
250         }
251     }
252 
253     return cart_ram[addr & 0x1fff];
254 }
255 
256 /* store 0xa000-0xbfff */
megacart_blk5_store(uint16_t addr,uint8_t value)257 void megacart_blk5_store(uint16_t addr, uint8_t value)
258 {
259     uint8_t bank_low;
260     uint8_t bank_high;
261     int ram_low_en;
262     int ram_high_en;
263     int ram_wp;
264 
265     /* get bank registers */
266     bank_low = (oe_flop) ? bank_low_reg : 0x7f;
267     bank_high = (oe_flop) ? bank_high_reg : 0x7f;
268 
269     /* determine flags from bank registers. */
270     ram_low_en = (bank_low & 0x80) ? 1 : 0;
271     ram_high_en = (bank_high & 0x80) ? 1 : 0;
272     ram_wp = (bank_high & 0x40) ? 0 : 1;
273 
274     if (!ram_wp && (ram_low_en && ram_high_en)) {
275         cart_ram[addr & 0x1fff] = value;
276     }
277 }
278 
279 /* read 0x9800-0x9bff */
megacart_io2_read(uint16_t addr)280 static uint8_t megacart_io2_read(uint16_t addr)
281 {
282     uint8_t value;
283 
284     if (nvram_en_flop) {
285         megacart_io2_device.io_source_valid = 1;
286         value = cart_nvram[addr & 0x1fff];
287     } else {
288         megacart_io2_device.io_source_valid = 0;
289         value = vic20_cpu_last_data;
290     }
291     return value;
292 }
293 
294 /* store 0x9800-0x9bff */
megacart_io2_store(uint16_t addr,uint8_t value)295 static void megacart_io2_store(uint16_t addr, uint8_t value)
296 {
297     if (nvram_en_flop) {
298         cart_nvram[addr & 0x1fff] = value;
299     }
300 }
301 
302 /* read 0x9c00-0x9fff */
megacart_io3_read(uint16_t addr)303 static uint8_t megacart_io3_read(uint16_t addr)
304 {
305     uint8_t value;
306 
307     if (nvram_en_flop) {
308         megacart_io3_device.io_source_valid = 1;
309         value = cart_nvram[addr & 0x1fff];
310     } else {
311         megacart_io3_device.io_source_valid = 0;
312         value = vic20_cpu_last_data;
313     }
314     return value;
315 }
316 
megacart_io3_peek(uint16_t addr)317 static uint8_t megacart_io3_peek(uint16_t addr)
318 {
319     if ((addr & 0x180) == 0x080) { /* $9c80 */
320         return bank_high_reg;
321     }
322 
323     if ((addr & 0x180) == 0x100) { /* $9d00 */
324         return bank_low_reg;
325     }
326 
327     return cart_nvram[addr & 0x1fff];
328 }
329 
330 /* store 0x9c00-0x9fff */
megacart_io3_store(uint16_t addr,uint8_t value)331 static void megacart_io3_store(uint16_t addr, uint8_t value)
332 {
333     if (nvram_en_flop) {
334         cart_nvram[addr & 0x1fff] = value;
335     }
336 
337     if ((addr & 0x180) == 0x080) { /* $9c80 */
338         bank_high_reg = value;
339     }
340 
341     if ((addr & 0x180) == 0x100) { /* $9d00 */
342         bank_low_reg = value;
343     }
344 
345     if ((addr & 0x180) == 0x180) { /* $9d80 */
346         nvram_en_flop = (value & 0x1) ? 0 : 1;
347         bank_high_reg = value;
348         bank_low_reg = value;
349     }
350 
351     if ((addr & 0x200) == 0x200) { /* $9e00 */
352         /* perform reset */
353         reset_mode = SOFTWARE_RESET;
354         machine_trigger_reset(MACHINE_RESET_MODE_SOFT);
355     }
356 }
357 
358 /* ------------------------------------------------------------------------- */
359 
megacart_init(void)360 void megacart_init(void)
361 {
362     if (megacart_log == LOG_ERR) {
363         megacart_log = log_open(CARTRIDGE_VIC20_NAME_MEGACART);
364     }
365 
366     reset_mode = BUTTON_RESET;
367     oe_flop = 0;
368     nvram_en_flop = 0;
369 }
370 
megacart_reset(void)371 void megacart_reset(void)
372 {
373     if (reset_mode == SOFTWARE_RESET) {
374         oe_flop = !oe_flop;
375     } else {
376         oe_flop = 0;
377     }
378     reset_mode = BUTTON_RESET;
379 }
380 
megacart_config_setup(uint8_t * rawcart)381 void megacart_config_setup(uint8_t *rawcart)
382 {
383 }
384 
zfile_load(const char * filename,uint8_t * dest,size_t size)385 static int zfile_load(const char *filename, uint8_t *dest, size_t size)
386 {
387     FILE *fd;
388 
389     fd = zfile_fopen(filename, MODE_READ);
390     if (!fd) {
391         return -1;
392     }
393     if (util_file_length(fd) != size) {
394         zfile_fclose(fd);
395         return -1;
396     }
397     if (fread(dest, size, 1, fd) < 1) {
398         zfile_fclose(fd);
399         return -1;
400     }
401     zfile_fclose(fd);
402     return 0;
403 }
404 
try_nvram_load(const char * filename)405 static int try_nvram_load(const char *filename)
406 {
407     if (cart_nvram && filename && *filename != '\0') {
408         if (zfile_load(filename, cart_nvram, (size_t)CART_NVRAM_SIZE) < 0) {
409             log_message(megacart_log, "Failed to read NvRAM image `%s'!", filename);
410             return -1;
411         } else {
412             log_message(megacart_log, "Read NvRAM image `%s'.", filename);
413         }
414     }
415 
416     return 0;
417 }
418 
try_nvram_save(const char * filename)419 static int try_nvram_save(const char *filename)
420 {
421     int ret = 0;
422     if (cart_nvram && filename && *filename != '\0') {
423         FILE *fd;
424         fd = fopen(filename, "wb");
425         if (fd) {
426             if (fwrite(cart_nvram, (size_t)CART_NVRAM_SIZE, 1, fd) > 0) {
427                 log_message(megacart_log, "Wrote back NvRAM image `%s'.", filename);
428             } else {
429                 ret = -1;
430             }
431             fclose(fd);
432         } else {
433             ret = -1;
434         }
435         if (ret == -1) {
436             log_message(megacart_log, "Failed to write back NvRAM image `%s'!", filename);
437         }
438     }
439 
440     return ret;
441 }
442 
megacart_bin_attach(const char * filename)443 int megacart_bin_attach(const char *filename)
444 {
445     if (!cart_ram) {
446         cart_ram = lib_malloc(CART_RAM_SIZE);
447     }
448     if (!cart_nvram) {
449         cart_nvram = lib_malloc(CART_NVRAM_SIZE);
450     }
451     if (!cart_rom) {
452         cart_rom = lib_malloc(CART_ROM_SIZE);
453     }
454 
455     if (zfile_load(filename, cart_rom, (size_t)CART_ROM_SIZE) < 0) {
456         megacart_detach();
457         return -1;
458     }
459 
460     if (export_add(&export_res) < 0) {
461         return -1;
462     }
463 
464     try_nvram_load(nvram_filename);
465 
466     cart_rom_low = cart_rom;
467     cart_rom_high = cart_rom + 0x100000;
468 
469     mem_cart_blocks = VIC_CART_RAM123 |
470                       VIC_CART_BLK1 | VIC_CART_BLK2 | VIC_CART_BLK3 | VIC_CART_BLK5 |
471                       VIC_CART_IO2 | VIC_CART_IO3;
472     mem_initialize_memory();
473 
474     megacart_io2_list_item = io_source_register(&megacart_io2_device);
475     megacart_io3_list_item = io_source_register(&megacart_io3_device);
476 
477     return 0;
478 }
479 
megacart_detach(void)480 void megacart_detach(void)
481 {
482     /* try to write back NvRAM contents if write back is enabled
483        and cartridge is not from a snapshot */
484     if (nvram_writeback && !cartridge_is_from_snapshot) {
485         try_nvram_save(nvram_filename);
486     }
487 
488     mem_cart_blocks = 0;
489     mem_initialize_memory();
490     lib_free(cart_ram);
491     lib_free(cart_nvram);
492     lib_free(cart_rom);
493     cart_ram = NULL;
494     cart_nvram = NULL;
495     cart_rom = NULL;
496 
497     export_remove(&export_res);
498 
499     if (megacart_io2_list_item != NULL) {
500         io_source_unregister(megacart_io2_list_item);
501         megacart_io2_list_item = NULL;
502     }
503 
504     if (megacart_io3_list_item != NULL) {
505         io_source_unregister(megacart_io3_list_item);
506         megacart_io3_list_item = NULL;
507     }
508 }
509 
510 /* ------------------------------------------------------------------------- */
511 
set_nvram_filename(const char * name,void * param)512 static int set_nvram_filename(const char *name, void *param)
513 {
514     if (nvram_filename && name && strcmp(name, nvram_filename) == 0) {
515         return 0;
516     }
517 
518     /* try to write back NvRAM contents to the old file if write back is enabled
519        and NvRAM wasn't from a snapshot */
520     if (nvram_writeback && !cartridge_is_from_snapshot) {
521         try_nvram_save(nvram_filename);
522     }
523 
524     util_string_set(&nvram_filename, name);
525 
526     try_nvram_load(nvram_filename);
527     return 0;
528 }
529 
530 static const resource_string_t resources_string[] = {
531     { "MegaCartNvRAMfilename", "", RES_EVENT_NO, NULL,
532       &nvram_filename, set_nvram_filename, NULL },
533     RESOURCE_STRING_LIST_END
534 };
535 
set_nvram_writeback(int val,void * param)536 static int set_nvram_writeback(int val, void *param)
537 {
538     nvram_writeback = val ? 1 : 0;
539 
540     return 0;
541 }
542 
543 static const resource_int_t resources_int[] = {
544     { "MegaCartNvRAMWriteBack", 0, RES_EVENT_STRICT, (resource_value_t)0,
545       &nvram_writeback, set_nvram_writeback, NULL },
546     RESOURCE_INT_LIST_END
547 };
548 
megacart_resources_init(void)549 int megacart_resources_init(void)
550 {
551     if (resources_register_string(resources_string) < 0) {
552         return -1;
553     }
554 
555     return resources_register_int(resources_int);
556 }
557 
megacart_resources_shutdown(void)558 void megacart_resources_shutdown(void)
559 {
560     lib_free(nvram_filename);
561     nvram_filename = NULL;
562 }
563 
564 static const cmdline_option_t cmdline_options[] =
565 {
566     { "-mcnvramfile", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
567       NULL, NULL, "MegaCartNvRAMfilename", NULL,
568       "<Name>", "Set Mega-Cart NvRAM filename" },
569     { "-mcnvramwriteback", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
570       NULL, NULL, "MegaCartNvRAMWriteBack", (resource_value_t)1,
571       NULL, "Enable Mega-Cart NvRAM writeback" },
572     { "+mcnvramwriteback", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
573       NULL, NULL, "MegaCartNvRAMWriteBack", (resource_value_t)0,
574       NULL, "Disable Mega-Cart NvRAM writeback" },
575     CMDLINE_LIST_END
576 };
577 
megacart_cmdline_options_init(void)578 int megacart_cmdline_options_init(void)
579 {
580     return cmdline_register_options(cmdline_options);
581 }
582 
583 /* ------------------------------------------------------------------------- */
584 
585 #define VIC20CART_DUMP_VER_MAJOR   2
586 #define VIC20CART_DUMP_VER_MINOR   0
587 #define SNAP_MODULE_NAME  "MEGACART"
588 
megacart_snapshot_write_module(snapshot_t * s)589 int megacart_snapshot_write_module(snapshot_t *s)
590 {
591     snapshot_module_t *m;
592 
593     m = snapshot_module_create(s, SNAP_MODULE_NAME, VIC20CART_DUMP_VER_MAJOR, VIC20CART_DUMP_VER_MINOR);
594     if (m == NULL) {
595         return -1;
596     }
597 
598     if (0
599         || (SMW_B(m, bank_low_reg) < 0)
600         || (SMW_B(m, bank_high_reg) < 0)
601         || (SMW_B(m, (uint8_t)oe_flop) < 0)
602         || (SMW_B(m, (uint8_t)nvram_en_flop) < 0)
603         || (SMW_BA(m, cart_ram, CART_RAM_SIZE) < 0)
604         || (SMW_BA(m, cart_rom, CART_ROM_SIZE) < 0)
605         || (SMW_BA(m, cart_nvram, CART_NVRAM_SIZE) < 0)) {
606         snapshot_module_close(m);
607         return -1;
608     }
609 
610     snapshot_module_close(m);
611     return 0;
612 }
613 
megacart_snapshot_read_module(snapshot_t * s)614 int megacart_snapshot_read_module(snapshot_t *s)
615 {
616     uint8_t vmajor, vminor;
617     snapshot_module_t *m;
618 
619     m = snapshot_module_open(s, SNAP_MODULE_NAME, &vmajor, &vminor);
620     if (m == NULL) {
621         return -1;
622     }
623 
624     if (vmajor != VIC20CART_DUMP_VER_MAJOR) {
625         snapshot_module_close(m);
626         return -1;
627     }
628 
629     if (!cart_ram) {
630         cart_ram = lib_malloc(CART_RAM_SIZE);
631     }
632     if (!cart_nvram) {
633         cart_nvram = lib_malloc(CART_NVRAM_SIZE);
634     }
635     if (!cart_rom) {
636         cart_rom = lib_malloc(CART_ROM_SIZE);
637     }
638 
639     if (0
640         || (SMR_B(m, &bank_low_reg) < 0)
641         || (SMR_B(m, &bank_high_reg) < 0)
642         || (SMR_B_INT(m, &oe_flop) < 0)
643         || (SMR_B_INT(m, &nvram_en_flop) < 0)
644         || (SMR_BA(m, cart_ram, CART_RAM_SIZE) < 0)
645         || (SMR_BA(m, cart_rom, CART_ROM_SIZE) < 0)
646         || (SMR_BA(m, cart_nvram, CART_NVRAM_SIZE) < 0)) {
647         snapshot_module_close(m);
648         lib_free(cart_ram);
649         lib_free(cart_nvram);
650         lib_free(cart_rom);
651         cart_ram = NULL;
652         cart_nvram = NULL;
653         cart_rom = NULL;
654         return -1;
655     }
656 
657     snapshot_module_close(m);
658 
659     cart_rom_low = cart_rom;
660     cart_rom_high = cart_rom + 0x100000;
661 
662     reset_mode = BUTTON_RESET;
663 
664     mem_cart_blocks = VIC_CART_RAM123 |
665                       VIC_CART_BLK1 | VIC_CART_BLK2 | VIC_CART_BLK3 | VIC_CART_BLK5 |
666                       VIC_CART_IO2 | VIC_CART_IO3;
667     mem_initialize_memory();
668 
669     return 0;
670 }
671 
672 /* ------------------------------------------------------------------------- */
673 
megacart_mon_dump(void)674 static int megacart_mon_dump(void)
675 {
676     uint8_t bank_low;
677     uint8_t bank_high;
678     int ram_low_en;
679     int ram_high_en;
680     int ram_wp;
681 
682     /* get bank registers */
683     bank_low = (oe_flop) ? bank_low_reg : 0x7f;
684     bank_high = (oe_flop) ? bank_high_reg : 0x7f;
685 
686     /* determine flags from bank registers. */
687     ram_low_en = (bank_low & 0x80) ? 1 : 0;
688     ram_high_en = (bank_high & 0x80) ? 1 : 0;
689     ram_wp = (bank_high & 0x40) ? 0 : 1;
690 
691     mon_out("Registers: Bank low $%02x, high $%02x\n", bank_low_reg, bank_high_reg);
692     mon_out("NvRAM flop: %i, OE flop: %i\n", nvram_en_flop, oe_flop);
693     mon_out("RAM123: %s\n", nvram_en_flop ? "NvRAM" : "off");
694 
695     mon_out("BLKn: ");
696     if (!ram_low_en) {
697         mon_out("ROM bank $%02x (offset $%06x)\n", bank_low, bank_low * 0x2000);
698     } else {
699         if (ram_high_en) {
700             mon_out("RAM %s\n", ram_wp ? "(write protected)" : "");
701         } else {
702             mon_out("off\n");
703         }
704     }
705 
706     mon_out("BLK5: ");
707     if (!ram_high_en) {
708         mon_out("ROM bank $%02x (offset $%06x)\n", bank_high, bank_high * 0x2000 + 0x100000);
709     } else {
710         if (!ram_low_en) {
711             mon_out("ROM bank $%02x (offset $%06x)\n", bank_low, bank_low * 0x2000);
712         } else {
713             mon_out("RAM %s\n", ram_wp ? "(write protected)" : "");
714         }
715     }
716 
717     return 0;
718 }
719