1 /*
2  * petmemsnapshot.c - PET memory snapshot handling.
3  *
4  * Written by
5  *  Ettore Perazzoli <ettore@comm2000.it>
6  *  Andre Fachat <fachat@physik.tu-chemnitz.de>
7  *  Andreas Boose <viceteam@t-online.de>
8  *
9  * This file is part of VICE, the Versatile Commodore Emulator.
10  * See README for copyright notice.
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25  *  02111-1307  USA.
26  *
27  */
28 
29 /*
30  * FIXME: the rom_*_loaded flag stuff is not clear enough.
31  *
32  */
33 
34 #include "vice.h"
35 
36 #include <stdio.h>
37 
38 #include "autostart.h"
39 #include "kbdbuf.h"
40 #include "log.h"
41 #include "mem.h"
42 #include "pet.h"
43 #include "petmem.h"
44 #include "petmemsnapshot.h"
45 #include "petrom.h"
46 #include "pets.h"
47 #include "resources.h"
48 #include "snapshot.h"
49 #include "tape.h"
50 #include "types.h"
51 #include "machine.h"
52 
53 static log_t pet_snapshot_log = LOG_ERR;
54 
55 /*
56  * PET memory dump should be 4-32k or 128k, depending on the config, as RAM.
57  * Plus 64k expansion RAM (8096 or SuperPET) if necessary. Also there
58  * is the 1/2k video RAM as "VRAM".
59  * In this prototype we save the full ram......
60  */
61 
62 static const char module_ram_name[] = "PETMEM";
63 #define PETMEM_DUMP_VER_MAJOR   1
64 #define PETMEM_DUMP_VER_MINOR   3
65 
66 /*
67  * UBYTE        CONFIG          Bits 0-3: 0 = 40 col PET without CRTC
68  *                                        1 = 40 col PET with CRTC
69  *                                        2 = 80 col PET (with CRTC)
70  *                                        3 = SuperPET
71  *                                        4 = 8096
72  *                                        5 = 8296
73  *                              Bit 6: 1= RAM at $9***
74  *                              Bit 7: 1= RAM at $A***
75  *
76  * UBYTE        KEYBOARD        0 = UK business
77  *                              1 = graphics
78  *
79  * UBYTE        MEMSIZE         memory size of low 32k in k (4,8,16,32)
80  *
81  * UBYTE        CONF8X96        8x96 configuration register
82  * UBYTE        SUPERPET        SuperPET config:
83  *                              Bit 0: spet_ramen,  1= RAM enabled
84  *                                  1: spet_ramwp,  1= RAM write protected
85  *                                  2: spet_ctrlwp, 1= CTRL reg write prot.
86  *                                  3: spet_diag,   0= diag active
87  *                                  4-7: spet_bank, RAM block in use
88  *
89  * ARRAY        RAM             4-32k RAM (not 8296, dep. on MEMSIZE)
90  * ARRAY        VRAM            2/4k RAM (not 8296, dep in CONFIG)
91  * ARRAY        EXTRAM          64k (SuperPET and 8096 only)
92  * ARRAY        RAM             128k RAM (8296 only)
93  *
94  *                              Added in format V1.1, should be part of
95  *                              KEYBOARD in later versions.
96  *
97  * BYTE         POSITIONAL      bit 0=0 = symbolic keyboard mapping
98  *                                   =1 = positional keyboard mapping
99  *
100  *                              Added in format V1.2
101  * BYTE         EOIBLANK        bit 0=0: EOI does not blank screen
102  *                                   =1: EOI does blank screen
103  *
104  *                              Added in format V1.3
105  * WORD         CPU_SWITCH      6502 / 6809 / PROG
106  * BYTE         VAL             6702 state information
107  * BYTE         PREVODD
108  * BYTE         WANTODD
109  * WORD[8]      SHIFT
110  */
111 
mem_write_ram_snapshot_module(snapshot_t * s)112 static int mem_write_ram_snapshot_module(snapshot_t *s)
113 {
114     snapshot_module_t *m;
115     uint8_t config, rconf, memsize, conf8x96, superpet, superpet2;
116     int kbdindex;
117     int i;
118 
119     memsize = petres.ramSize;
120     if (memsize > 32) {
121         memsize = 32;
122     }
123 
124     if (!petres.crtc) {
125         config = 0;
126     } else {
127         config = petres.videoSize == 0x400 ? 1 : 2;
128     }
129 
130     if (petres.map) {
131         config = petres.map + 3;
132     } else {
133         if (petres.superpet) {
134             config = 3;
135         }
136     }
137 
138     rconf = (petres.ramsel9 ? 0x40 : 0) | (petres.ramselA ? 0x80 : 0);
139 
140     conf8x96 = petmem_map_reg;
141 
142     superpet = (spet_ramen ? 1 : 0)
143                | (spet_ramwp ? 2 : 0)
144                | (spet_ctrlwp ? 4 : 0)
145                | (spet_diag ? 8 : 0)
146                | ((spet_bank << 4) & 0xf0);
147 
148     m = snapshot_module_create(s, module_ram_name,
149                                PETMEM_DUMP_VER_MAJOR, PETMEM_DUMP_VER_MINOR);
150     if (m == NULL) {
151         return -1;
152     }
153     SMW_B(m, (uint8_t)(config | rconf));
154 
155     resources_get_int("KeymapIndex", &kbdindex);
156     SMW_B(m, (uint8_t)(kbdindex >> 1));
157 
158     SMW_B(m, memsize);
159     SMW_B(m, conf8x96);
160     SMW_B(m, superpet);
161 
162     if (config != 5) {
163         SMW_BA(m, mem_ram, memsize << 10);
164 
165         SMW_BA(m, mem_ram + 0x8000, (config < 2) ? 0x400 : 0x800);
166 
167         if (config == 3 || config == 4) {
168             SMW_BA(m, mem_ram + 0x10000, 0x10000);
169         }
170     } else {    /* 8296 */
171         SMW_BA(m, mem_ram, 0x20000);
172     }
173 
174     /* V1.1 */
175     SMW_B(m, (uint8_t)(kbdindex & 1));
176     /* V1.2 */
177     SMW_B(m, (uint8_t)(petres.eoiblank ? 1 : 0));
178     /* V1.3 */
179     SMW_W(m, (uint16_t)petres.superpet_cpu_switch);
180     SMW_B(m, (uint8_t)dongle6702.val);
181     SMW_B(m, (uint8_t)dongle6702.prevodd);
182     SMW_B(m, (uint8_t)dongle6702.wantodd);
183     for (i = 0; i < 8; i++) {
184         SMW_W(m, (uint16_t)dongle6702.shift[i]);
185     }
186     /* Extra SuperPET2 byte; more state of $EFFC */
187     superpet2 = spet_bank & 0x10;
188     if (spet_firq_disabled) {
189         superpet2 |= 0x20;
190     }
191     if (spet_flat_mode) {
192         superpet2 |= 0x40;
193     }
194     SMW_B(m, superpet2);
195 
196     snapshot_module_close(m);
197 
198     return 0;
199 }
200 
mem_read_ram_snapshot_module(snapshot_t * s)201 static int mem_read_ram_snapshot_module(snapshot_t *s)
202 {
203     uint8_t vmajor, vminor;
204     snapshot_module_t *m;
205     uint8_t config, rconf, byte, memsize, conf8x96, superpet;
206     petinfo_t peti = {
207         32, 0x0800, 1, 80, 0, 0, 0, 0, 0, 0, 0,
208         NULL, NULL, NULL, NULL, NULL, NULL, NULL, { NULL }
209     };
210     int old6809mode;
211     int spetbank = 0;
212 
213     m = snapshot_module_open(s, module_ram_name, &vmajor, &vminor);
214     if (m == NULL) {
215         return -1;
216     }
217 
218     if (vmajor != PETMEM_DUMP_VER_MAJOR) {
219         log_error(pet_snapshot_log,
220                   "Cannot load PET RAM module with major version %d",
221                   vmajor);
222         snapshot_module_close(m);
223         return -1;
224     }
225 
226     old6809mode = petres.superpet &&
227                   petres.superpet_cpu_switch == SUPERPET_CPU_6809;
228 
229     SMR_B(m, &config);
230 
231     SMR_B(m, &byte);
232     peti.kbd_type = byte;
233 
234     SMR_B(m, &memsize);
235     SMR_B(m, &conf8x96);
236     SMR_B(m, &superpet);
237 
238     rconf = config & 0xc0;
239     config &= 0x0f;
240 
241     peti.ramSize = memsize;
242     peti.crtc = 1;
243     peti.IOSize = 0x800;
244     peti.video = 80;
245     peti.superpet = 0;
246 
247     switch (config) {
248         case 0:         /* 40 cols w/o CRTC */
249             peti.crtc = 0;
250             peti.video = 40;
251             break;
252         case 1:         /* 40 cols w/ CRTC */
253             peti.video = 40;
254             break;
255         case 2:         /* 80 cols (w/ CRTC) */
256             break;
257         case 3:         /* SuperPET */
258             spet_ramen = superpet & 1;
259             spet_ramwp = superpet & 2;
260             spet_ctrlwp = superpet & 4;
261             spet_diag = superpet & 8;
262             spetbank = (superpet >> 4) & 0x0f;
263             peti.superpet = 1;
264             break;
265         case 4:         /* 8096 */
266             peti.ramSize = 96;
267             break;
268         case 5:         /* 8296 */
269             peti.ramSize = 128;
270             break;
271     }
272 
273     peti.ramsel9 = (rconf & 0x40) ? 1 : 0;
274     peti.ramselA = (rconf & 0x80) ? 1 : 0;
275 
276     petmem_set_conf_info(&peti);  /* set resources and config accordingly */
277     petmem_map_reg = conf8x96;
278 
279     mem_initialize_memory();
280 
281     pet_crtc_set_screen();
282 
283     if (config != 5) {
284         SMR_BA(m, mem_ram, memsize << 10);
285 
286         SMR_BA(m, mem_ram + 0x8000, (config < 2) ? 0x400 : 0x800);
287 
288         if (config == 3 || config == 4) {
289             SMR_BA(m, mem_ram + 0x10000, 0x10000);
290         }
291     } else {    /* 8296 */
292         SMR_BA(m, mem_ram, 0x20000);
293     }
294 
295     if (vminor > 0) {
296         int kindex;
297         SMR_B(m, &byte);
298         resources_get_int("KeymapIndex", &kindex);
299         resources_set_int("KeymapIndex", (kindex & ~1) | (byte & 1));
300     }
301     if (vminor > 1) {
302         SMR_B(m, &byte);
303         resources_set_int("EoiBlank", byte & 1);
304     }
305     if (vminor > 2) {
306         int new6809mode, i;
307         uint8_t b;
308         uint16_t w;
309 
310         SMR_W(m, &w); petres.superpet_cpu_switch = w;
311         SMR_B(m, &b); dongle6702.val = b;
312         SMR_B(m, &b); dongle6702.prevodd = b;
313         SMR_B(m, &b); dongle6702.wantodd = b;
314 
315         for (i = 0; i < 8; i++) {
316             SMR_W(m, &w);
317             dongle6702.shift[i] = w;
318         }
319 
320         /* Extra superpet2 bits */
321         b = 0;  /* when not present in file */
322         SMR_B(m, &b);
323         spetbank |= (b & 0x10);
324         spet_firq_disabled = (b & 0x20);
325         spet_flat_mode = (b & 0x40);
326 
327         /*
328          * TODO: make the CPU switch if needed, WITHOUT a reset!
329          * (A real-world CPU switch toggle always implies a reset)
330          * If the user loads a dump file running in the other mode,
331          * she may need to reset (to get to the correct CPU),
332          * then reload the dump again.
333          */
334         new6809mode = petres.superpet &&
335                       petres.superpet_cpu_switch == SUPERPET_CPU_6809;
336         if (new6809mode != old6809mode) {
337             log_error(pet_snapshot_log,
338                       "Snapshot for different CPU. Re-load the snapshot.");
339             machine_trigger_reset(MACHINE_RESET_MODE_HARD);
340             return -1;
341         }
342         /* set banked or flat memory mapping */
343         mem_initialize_memory_6809();
344     }
345 
346     /* spet_bank_4k = spetbank << 12; */
347     set_spet_bank(spetbank);
348 
349     snapshot_module_close(m);
350 
351     return 0;
352 }
353 
354 static const char module_rom_name[] = "PETROM";
355 #define PETROM_DUMP_VER_MAJOR   1
356 #define PETROM_DUMP_VER_MINOR   1
357 
358 /*
359  * UBYTE        CONFIG          Bit 0: 1= $9*** ROM included
360  *                                  1: 1= $a*** ROM included
361  *                                  2: 1= $b*** ROM included
362  *                                  3: 1= $e900-$efff ROM included
363  *                                  4: 1= $9000-$ffff 6809 ROM
364  *                                        and upper half CHARGEN ROM included
365  *
366  * ARRAY        KERNAL          4k KERNAL ROM image $f000-$ffff
367  * ARRAY        EDITOR          2k EDITOR ROM image $e000-$e800
368  * ARRAY        CHARGEN         2k CHARGEN ROM image
369  * ARRAY        ROM9            4k $9*** ROM (if CONFIG & 1)
370  * ARRAY        ROMA            4k $A*** ROM (if CONFIG & 2)
371  * ARRAY        ROMB            4k $B*** ROM (if CONFIG & 4)
372  * ARRAY        ROMC            4k $C*** ROM
373  * ARRAY        ROMD            4k $D*** ROM
374  * ARRAY        ROME9           7 blocks $e900-$efff ROM (if CONFIG & 8)
375  *                              Added in format V1.1:
376  * ARRAY        ROM6809         24k $A000-$FFFF ROM   (if CONFIG & 16)
377  * ARRAY        CHARGEN(2)      upper half of CHARGEN (if CONFIG & 16)
378  *
379  */
380 
mem_write_rom_snapshot_module(snapshot_t * s,int save_roms)381 static int mem_write_rom_snapshot_module(snapshot_t *s, int save_roms)
382 {
383     snapshot_module_t *m;
384     uint8_t config;
385     int i, trapfl;
386 
387     if (!save_roms) {
388         return 0;
389     }
390 
391     m = snapshot_module_create(s, module_rom_name,
392                                PETROM_DUMP_VER_MAJOR, PETROM_DUMP_VER_MINOR);
393     if (m == NULL) {
394         return -1;
395     }
396 
397     /* disable traps before saving the ROM */
398     resources_get_int("VirtualDevices", &trapfl);
399     resources_set_int("VirtualDevices", 0);
400     petrom_unpatch_2001();
401 
402     config = (petrom_9_loaded ? 1 : 0)
403              | (petrom_A_loaded ? 2 : 0)
404              | (petrom_B_loaded ? 4 : 0)
405              | ((petres.ramSize == 128) ? 8 : 0)
406              | (petres.superpet ? 16 : 0);
407 
408     SMW_B(m, config);
409 
410     {
411         SMW_BA(m, mem_rom + 0x7000, 0x1000);
412         SMW_BA(m, mem_rom + 0x6000, 0x0800);
413 
414         /* pick relevant data from chargen ROM */
415         for (i = 0; i < 128; i++) {
416             SMW_BA(m, mem_chargen_rom + i * 16, 8);
417         }
418         for (i = 0; i < 128; i++) {
419             SMW_BA(m, mem_chargen_rom + 0x1000 + i * 16, 8);
420         }
421 
422         if (config & 1) {
423             SMW_BA(m, mem_rom + 0x1000, 0x1000);
424         }
425         if (config & 2) {
426             SMW_BA(m, mem_rom + 0x2000, 0x1000);
427         }
428         if (config & 4) {
429             SMW_BA(m, mem_rom + 0x3000, 0x1000);
430         }
431 
432         SMW_BA(m, mem_rom + 0x4000, 0x2000);
433 
434         if (config & 8) {
435             SMW_BA(m, mem_rom + 0x6900, 0x0700);
436         }
437 
438         if (config & 16) {
439             SMW_BA(m, mem_6809rom, PET_6809_ROMSIZE);
440 
441             /* pick relevant data from upper half of chargen ROM */
442             for (i = 0; i < 128; i++) {
443                 SMW_BA(m, mem_chargen_rom + 0x2000 + i * 16, 8);
444             }
445             for (i = 0; i < 128; i++) {
446                 SMW_BA(m, mem_chargen_rom + 0x3000 + i * 16, 8);
447             }
448         }
449     }
450 
451     /* enable traps again when necessary */
452     resources_set_int("VirtualDevices", trapfl);
453     petrom_patch_2001();
454 
455     snapshot_module_close(m);
456 
457     return 0;
458 }
459 
mem_read_rom_snapshot_module(snapshot_t * s)460 static int mem_read_rom_snapshot_module(snapshot_t *s)
461 {
462     uint8_t vmajor, vminor;
463     snapshot_module_t *m;
464     uint8_t config;
465     int trapfl, new_iosize;
466 
467     m = snapshot_module_open(s, module_rom_name, &vmajor, &vminor);
468     if (m == NULL) {
469         return 0;       /* optional */
470     }
471     if (vmajor != PETROM_DUMP_VER_MAJOR) {
472         log_error(pet_snapshot_log,
473                   "Cannot load PET ROM module with major version %d",
474                   vmajor);
475         snapshot_module_close(m);
476         return -1;
477     }
478 
479     /* disable traps before loading the ROM */
480     resources_get_int("VirtualDevices", &trapfl);
481     resources_set_int("VirtualDevices", 0);
482     petrom_unpatch_2001();
483 
484     config = (petrom_9_loaded ? 1 : 0)
485              | (petrom_A_loaded ? 2 : 0)
486              | (petrom_B_loaded ? 4 : 0)
487              | ((petres.pet2k || petres.ramSize == 128) ? 8 : 0);
488 
489     SMR_B(m, &config);
490 
491     /* De-initialize kbd-buf, autostart and tape stuff here before
492        loading the new ROMs. These depend on addresses defined in the
493        rom - they might be different in the loaded ROM. */
494     kbdbuf_init(0, 0, 0, 0);
495     autostart_init(0, 0);
496     tape_deinstall();
497 
498     petrom_9_loaded = config & 1;
499     petrom_A_loaded = config & 2;
500     petrom_B_loaded = config & 4;
501 
502     if (config & 8) {
503         new_iosize = 0x100;
504     } else {
505         new_iosize = 0x800;
506     }
507     if (new_iosize != petres.IOSize) {
508         petres.IOSize = new_iosize;
509         mem_initialize_memory();
510     }
511 
512     {
513         /* kernal $f000-$ffff */
514         SMR_BA(m, mem_rom + 0x7000, 0x1000);
515         /* editor $e000-$e7ff */
516         SMR_BA(m, mem_rom + 0x6000, 0x0800);
517 
518         /* chargen ROM */
519         resources_set_int("Basic1Chars", 0);
520         SMR_BA(m, mem_chargen_rom, 0x0800);
521 
522         /* $9000-$9fff */
523         if (config & 1) {
524             SMR_BA(m, mem_rom + 0x1000, 0x1000);
525         }
526         /* $a000-$afff */
527         if (config & 2) {
528             SMR_BA(m, mem_rom + 0x2000, 0x1000);
529         }
530         /* $b000-$bfff */
531         if (config & 4) {
532             SMR_BA(m, mem_rom + 0x3000, 0x1000);
533         }
534 
535         /* $c000-$dfff */
536         SMR_BA(m, mem_rom + 0x4000, 0x2000);
537 
538         /* $e900-$efff editor extension */
539         if (config & 8) {
540             SMR_BA(m, mem_rom + 0x6900, 0x0700);
541         }
542 
543         /* 6809 ROMs */
544         if (config & 16) {
545             SMR_BA(m, mem_6809rom, PET_6809_ROMSIZE);
546             SMR_BA(m, mem_chargen_rom + 0x0800, 0x0800);
547         }
548 
549         petrom_convert_chargen(mem_chargen_rom);
550     }
551 
552     log_warning(pet_snapshot_log, "Dumped Romset files and saved settings will "
553                 "represent\nthe state before loading the snapshot!");
554 
555     petres.rompatch = 0;
556 
557     petrom_get_kernal_checksum();
558     petrom_get_editor_checksum();
559     petrom_checksum();
560 
561     petrom_patch_2001();
562 
563     /* enable traps again when necessary */
564     resources_set_int("VirtualDevices", trapfl);
565 
566     snapshot_module_close(m);
567 
568     return 0;
569 }
570 
pet_snapshot_write_module(snapshot_t * s,int save_roms)571 int pet_snapshot_write_module(snapshot_t *s, int save_roms)
572 {
573     if (mem_write_ram_snapshot_module(s) < 0
574         || mem_write_rom_snapshot_module(s, save_roms) < 0) {
575         return -1;
576     }
577     return 0;
578 }
579 
pet_snapshot_read_module(snapshot_t * s)580 int pet_snapshot_read_module(snapshot_t *s)
581 {
582     if (mem_read_ram_snapshot_module(s) < 0
583         || mem_read_rom_snapshot_module(s) < 0) {
584         return -1;
585     }
586     return 0;
587 }
588