1 /*
2  * maincpu.c - Emulation of the main 6510 processor.
3  *
4  * Written by
5  *  Ettore Perazzoli <ettore@comm2000.it>
6  *  Andreas Boose <viceteam@t-online.de>
7  *
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #include "6510core.h"
34 #include "alarm.h"
35 #include "archdep.h"
36 #include "clkguard.h"
37 #include "debug.h"
38 #include "interrupt.h"
39 #include "log.h"
40 #include "machine.h"
41 #include "maincpu.h"
42 #include "mainlock.h"
43 #include "mem.h"
44 #include "monitor.h"
45 #ifdef C64DTV
46 #include "mos6510dtv.h"
47 #else
48 #include "mos6510.h"
49 #endif
50 #include "h6809regs.h"
51 #include "snapshot.h"
52 #include "traps.h"
53 #include "types.h"
54 
55 #ifndef EXIT_FAILURE
56 #define EXIT_FAILURE 1
57 #endif
58 
59 /* MACHINE_STUFF should define/undef
60 
61  - NEED_REG_PC
62 
63  The following are optional:
64 
65  - PAGE_ZERO
66  - PAGE_ONE
67  - STORE_IND
68  - LOAD_IND
69 
70 */
71 
72 /* ------------------------------------------------------------------------- */
73 
74 #define NEED_REG_PC
75 
76 /* ------------------------------------------------------------------------- */
77 
78 /* Implement the hack to make opcode fetches faster.  */
79 #define JUMP(addr)                                                                         \
80     do {                                                                                   \
81         reg_pc = (unsigned int)(addr);                                                     \
82         if (reg_pc >= (unsigned int)bank_limit || reg_pc < (unsigned int)bank_start) {     \
83             mem_mmu_translate((unsigned int)(addr), &bank_base, &bank_start, &bank_limit); \
84         }                                                                                  \
85     } while (0)
86 
87 /* ------------------------------------------------------------------------- */
88 
89 #ifdef FEATURE_CPUMEMHISTORY
90 #ifndef C64DTV /* FIXME: fix DTV and remove this */
91 
92 /* map access functions to memmap hooks */
93 #ifndef STORE
94 #define STORE(addr, value) \
95     memmap_mem_store(addr, value)
96 #endif
97 
98 #ifndef LOAD
99 #define LOAD(addr) \
100     memmap_mem_read(addr)
101 #endif
102 
103 #ifndef STORE_ZERO
104 #define STORE_ZERO(addr, value) \
105     memmap_mem_store((addr) & 0xff, value)
106 #endif
107 
108 #ifndef LOAD_ZERO
109 #define LOAD_ZERO(addr) \
110     memmap_mem_read((addr) & 0xff)
111 #endif
112 
113 #ifndef STORE_DUMMY
114 #define STORE_DUMMY(addr, value) \
115     memmap_mem_store_dummy(addr, value)
116 #endif
117 
118 #ifndef LOAD_DUMMY
119 #define LOAD_DUMMY(addr) \
120     memmap_mem_read_dummy(addr)
121 #endif
122 
123 #ifndef STORE_ZERO_DUMMY
124 #define STORE_ZERO_DUMMY(addr, value) \
125     memmap_mem_store_dummy((addr) & 0xff, value)
126 #endif
127 
128 #ifndef LOAD_ZERO_DUMMY
129 #define LOAD_ZERO_DUMMY(addr) \
130     memmap_mem_read_dummy((addr) & 0xff)
131 #endif
132 
133 #endif /* C64DTV */
134 #endif /* FEATURE_CPUMEMHISTORY */
135 
136 #ifndef STORE
137 #define STORE(addr, value) \
138     (*_mem_write_tab_ptr[(addr) >> 8])((uint16_t)(addr), (uint8_t)(value))
139 #endif
140 
141 #ifndef LOAD
142 #define LOAD(addr) \
143     (*_mem_read_tab_ptr[(addr) >> 8])((uint16_t)(addr))
144 #endif
145 
146 #ifndef STORE_ZERO
147 #define STORE_ZERO(addr, value) \
148     (*_mem_write_tab_ptr[0])((uint16_t)(addr), (uint8_t)(value))
149 #endif
150 
151 #ifndef LOAD_ZERO
152 #define LOAD_ZERO(addr) \
153     (*_mem_read_tab_ptr[0])((uint16_t)(addr))
154 #endif
155 
156 #define LOAD_ADDR(addr) \
157     (LOAD(addr) | (LOAD((addr) + 1) << 8))
158 
159 #define LOAD_ZERO_ADDR(addr) \
160     (LOAD_ZERO(addr) | (LOAD_ZERO((addr) + 1) << 8))
161 
162 #ifndef STORE_DUMMY
163 #define STORE_DUMMY(addr, value) \
164     (*_mem_write_tab_ptr_dummy[(addr) >> 8])((uint16_t)(addr), (uint8_t)(value))
165 #endif
166 
167 #ifndef LOAD_DUMMY
168 #define LOAD_DUMMY(addr) \
169     (*_mem_read_tab_ptr_dummy[(addr) >> 8])((uint16_t)(addr))
170 #endif
171 
172 #ifndef STORE_ZERO_DUMMY
173 #define STORE_ZERO_DUMMY(addr, value) \
174     (*_mem_write_tab_ptr_dummy[0])((uint16_t)(addr), (uint8_t)(value))
175 #endif
176 
177 #ifndef LOAD_ZERO_DUMMY
178 #define LOAD_ZERO_DUMMY(addr) \
179     (*_mem_read_tab_ptr_dummy[0])((uint16_t)(addr))
180 #endif
181 
182 #define LOAD_ADDR_DUMMY(addr) \
183     (LOAD_DUMMY(addr) | (LOAD_DUMMY((addr) + 1) << 8))
184 
185 #define LOAD_ZERO_ADDR_DUMMY(addr) \
186     (LOAD_ZERO_DUMMY(addr) | (LOAD_ZERO_DUMMY((addr) + 1) << 8))
187 
188 /* Those may be overridden by the machine stuff.  Probably we want them in
189    the .def files, but if most of the machines do not use, we might keep it
190    here and only override it where needed.  */
191 #ifndef PAGE_ZERO
192 #define PAGE_ZERO mem_ram
193 #endif
194 
195 #ifndef PAGE_ONE
196 #define PAGE_ONE (mem_ram + 0x100)
197 #endif
198 
199 #ifndef STORE_IND
200 #define STORE_IND(addr, value) STORE((addr), (value))
201 #endif
202 
203 #ifndef LOAD_IND
204 #define LOAD_IND(addr) LOAD((addr))
205 #endif
206 
207 #ifndef DMA_FUNC
maincpu_generic_dma(void)208 static void maincpu_generic_dma(void)
209 {
210     /* Generic DMA hosts can be implemented here.
211        For example a very accurate REU emulation. */
212 }
213 #define DMA_FUNC maincpu_generic_dma()
214 #endif
215 
216 #ifndef DMA_ON_RESET
217 #define DMA_ON_RESET
218 #endif
219 
220 #ifndef CPU_ADDITIONAL_RESET
221 #define CPU_ADDITIONAL_RESET()
222 #endif
223 
224 #ifndef CPU_ADDITIONAL_INIT
225 #define CPU_ADDITIONAL_INIT()
226 #endif
227 
228 /* ------------------------------------------------------------------------- */
229 
230 struct interrupt_cpu_status_s *maincpu_int_status = NULL;
231 #ifndef CYCLE_EXACT_ALARM
232 alarm_context_t *maincpu_alarm_context = NULL;
233 #endif
234 clk_guard_t *maincpu_clk_guard = NULL;
235 monitor_interface_t *maincpu_monitor_interface = NULL;
236 
237 /* Global clock counter.  */
238 CLOCK maincpu_clk = 0L;
239 /* if != 0, exit when this many cycles have been executed */
240 CLOCK maincpu_clk_limit = 0L;
241 
242 /* This is flag is set to 1 each time a Read-Modify-Write instructions that
243    accesses memory is executed.  We can emulate the RMW behaviour of the 6510
244    this way.  VERY important notice: Always assign 1 for true, 0 for false!
245    Some functions depend on this to do some optimization.  */
246 int maincpu_rmw_flag = 0;
247 
248 /* Information about the last executed opcode.  This is used to know the
249    number of write cycles in the last executed opcode and to delay interrupts
250    by one more cycle if necessary, as happens with conditional branch opcodes
251    when the branch is taken.  */
252 unsigned int last_opcode_info;
253 
254 /* Address of the last executed opcode. This is used by watchpoints. */
255 unsigned int last_opcode_addr;
256 
257 /* Number of write cycles for each 6510 opcode.  */
258 const CLOCK maincpu_opcode_write_cycles[] = {
259             /* 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F */
260     /* $00 */  3, 0, 0, 2, 0, 0, 2, 2, 1, 0, 0, 0, 0, 0, 2, 2, /* $00 */
261     /* $10 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $10 */
262     /* $20 */  2, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $20 */
263     /* $30 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $30 */
264     /* $40 */  0, 0, 0, 2, 0, 0, 2, 2, 1, 0, 0, 0, 0, 0, 2, 2, /* $40 */
265     /* $50 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $50 */
266     /* $60 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $60 */
267     /* $70 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $70 */
268     /* $80 */  0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, /* $80 */
269     /* $90 */  0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, /* $90 */
270     /* $A0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* $A0 */
271     /* $B0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* $B0 */
272     /* $C0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $C0 */
273     /* $D0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $D0 */
274     /* $E0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $E0 */
275     /* $F0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2  /* $F0 */
276             /* 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F */
277 };
278 
279 /* Public copy of the CPU registers.  As putting the registers into the
280    function makes it faster, you have to generate a `TRAP' interrupt to have
281    the values copied into this struct.  */
282 #ifdef C64DTV
283 mos6510dtv_regs_t maincpu_regs;
284 #else
285 mos6510_regs_t maincpu_regs;
286 #endif
287 
288 /* ------------------------------------------------------------------------- */
289 
maincpu_monitor_interface_get(void)290 monitor_interface_t *maincpu_monitor_interface_get(void)
291 {
292 #ifdef C64DTV
293     maincpu_monitor_interface->cpu_regs = NULL;
294     maincpu_monitor_interface->dtv_cpu_regs = &maincpu_regs;
295 #else
296     maincpu_monitor_interface->cpu_regs = &maincpu_regs;
297     maincpu_monitor_interface->cpu_R65C02_regs = NULL;
298     maincpu_monitor_interface->dtv_cpu_regs = NULL;
299 #endif
300 
301 #ifdef HAVE_Z80_REGS
302     maincpu_monitor_interface->z80_cpu_regs = &z80_regs;
303 #else
304     maincpu_monitor_interface->z80_cpu_regs = NULL;
305 #endif
306 #ifdef HAVE_6809_REGS
307     maincpu_monitor_interface->h6809_cpu_regs = &h6809_regs;
308 #else
309     maincpu_monitor_interface->h6809_cpu_regs = NULL;
310 #endif
311 
312     maincpu_monitor_interface->int_status = maincpu_int_status;
313 
314     maincpu_monitor_interface->clk = &maincpu_clk;
315 
316     maincpu_monitor_interface->current_bank = 0;
317     maincpu_monitor_interface->current_bank_index = 0;
318 
319     maincpu_monitor_interface->mem_bank_list = mem_bank_list;
320     maincpu_monitor_interface->mem_bank_list_nos = mem_bank_list_nos;
321 
322     maincpu_monitor_interface->mem_bank_from_name = mem_bank_from_name;
323     maincpu_monitor_interface->mem_bank_index_from_bank = mem_bank_index_from_bank;
324     maincpu_monitor_interface->mem_bank_flags_from_bank = mem_bank_flags_from_bank;
325 
326     maincpu_monitor_interface->mem_bank_read = mem_bank_read;
327     maincpu_monitor_interface->mem_bank_peek = mem_bank_peek;
328     maincpu_monitor_interface->mem_bank_write = mem_bank_write;
329     maincpu_monitor_interface->mem_bank_poke = mem_bank_poke;
330 
331     maincpu_monitor_interface->mem_ioreg_list_get = mem_ioreg_list_get;
332 
333     maincpu_monitor_interface->toggle_watchpoints_func = mem_toggle_watchpoints;
334 
335     maincpu_monitor_interface->set_bank_base = NULL;
336     maincpu_monitor_interface->get_line_cycle = machine_get_line_cycle;
337 
338     return maincpu_monitor_interface;
339 }
340 
341 /* ------------------------------------------------------------------------- */
342 
maincpu_early_init(void)343 void maincpu_early_init(void)
344 {
345     maincpu_int_status = interrupt_cpu_status_new();
346 }
347 
maincpu_init(void)348 void maincpu_init(void)
349 {
350     interrupt_cpu_status_init(maincpu_int_status, &last_opcode_info);
351 
352     /* cpu specifix additional init routine */
353     CPU_ADDITIONAL_INIT();
354 }
355 
maincpu_shutdown(void)356 void maincpu_shutdown(void)
357 {
358     interrupt_cpu_status_destroy(maincpu_int_status);
359 }
360 
cpu_reset(void)361 static void cpu_reset(void)
362 {
363     int preserve_monitor;
364 
365     preserve_monitor = maincpu_int_status->global_pending_int & IK_MONITOR;
366 
367     interrupt_cpu_status_reset(maincpu_int_status);
368 
369     if (preserve_monitor) {
370         interrupt_monitor_trap_on(maincpu_int_status);
371     }
372 
373     maincpu_clk = 6; /* # of clock cycles needed for RESET.  */
374 
375     /* CPU specific extra reset routine, currently only used
376        for 8502 fast mode refresh cycle. */
377     CPU_ADDITIONAL_RESET();
378 
379     /* Do machine-specific initialization.  */
380     machine_reset();
381 }
382 
maincpu_reset(void)383 void maincpu_reset(void)
384 {
385     cpu_reset();
386 }
387 
388 /* ------------------------------------------------------------------------- */
389 
390 /* Return nonzero if a pending NMI should be dispatched now.  This takes
391    account for the internal delays of the 6510, but does not actually check
392    the status of the NMI line.  */
interrupt_check_nmi_delay(interrupt_cpu_status_t * cs,CLOCK cpu_clk)393 inline static int interrupt_check_nmi_delay(interrupt_cpu_status_t *cs,
394                                             CLOCK cpu_clk)
395 {
396     CLOCK nmi_clk = cs->nmi_clk + INTERRUPT_DELAY;
397 
398     /* BRK (0x00) delays the NMI by one opcode.  */
399     /* TODO DO_INTERRUPT sets last opcode to 0: can NMI occur right after IRQ? */
400     if (OPINFO_NUMBER(*cs->last_opcode_info_ptr) == 0x00) {
401         return 0;
402     }
403 
404     /* Branch instructions delay IRQs and NMI by one cycle if branch
405        is taken with no page boundary crossing.  */
406     if (OPINFO_DELAYS_INTERRUPT(*cs->last_opcode_info_ptr)) {
407         nmi_clk++;
408     }
409 
410     if (cpu_clk >= nmi_clk) {
411         return 1;
412     }
413 
414     return 0;
415 }
416 
417 /* Return nonzero if a pending IRQ should be dispatched now.  This takes
418    account for the internal delays of the 6510, but does not actually check
419    the status of the IRQ line.  */
interrupt_check_irq_delay(interrupt_cpu_status_t * cs,CLOCK cpu_clk)420 inline static int interrupt_check_irq_delay(interrupt_cpu_status_t *cs,
421                                             CLOCK cpu_clk)
422 {
423     CLOCK irq_clk = cs->irq_clk + INTERRUPT_DELAY;
424 
425     /* Branch instructions delay IRQs and NMI by one cycle if branch
426        is taken with no page boundary crossing.  */
427     if (OPINFO_DELAYS_INTERRUPT(*cs->last_opcode_info_ptr)) {
428         irq_clk++;
429     }
430 
431     /* If an opcode changes the I flag from 1 to 0, the 6510 needs
432        one more opcode before it triggers the IRQ routine.  */
433     if (cpu_clk >= irq_clk) {
434         if (!OPINFO_ENABLES_IRQ(*cs->last_opcode_info_ptr)) {
435             return 1;
436         } else {
437             cs->global_pending_int |= IK_IRQPEND;
438         }
439     }
440     return 0;
441 }
442 
443 /* ------------------------------------------------------------------------- */
444 
445 #ifdef NEED_REG_PC
446 /* FIXME: this should really be uint16_t, but it breaks things (eg trap17.prg) */
447 unsigned int reg_pc;
448 #endif
449 
450 static bool bank_base_ready = false;
451 static uint8_t *bank_base = NULL;
452 static int bank_start = 0;
453 static int bank_limit = 0;
454 
maincpu_resync_limits(void)455 void maincpu_resync_limits(void)
456 {
457     if (bank_base_ready) {
458         mem_mmu_translate(reg_pc, &bank_base, &bank_start, &bank_limit);
459     }
460 }
461 
maincpu_mainloop(void)462 void maincpu_mainloop(void)
463 {
464 #ifndef C64DTV
465     /* Notice that using a struct for these would make it a lot slower (at
466        least, on gcc 2.7.2.x).  */
467     uint8_t reg_a = 0;
468     uint8_t reg_x = 0;
469     uint8_t reg_y = 0;
470 #else
471     int reg_a_read_idx = 0;
472     int reg_a_write_idx = 0;
473     int reg_x_idx = 2;
474     int reg_y_idx = 1;
475 
476 #define reg_a_write(c)                      \
477     do {                                    \
478         dtv_registers[reg_a_write_idx] = c; \
479         if (reg_a_write_idx >= 3) {         \
480             maincpu_resync_limits();        \
481         }                                   \
482     } while (0);
483 #define reg_a_read dtv_registers[reg_a_read_idx]
484 #define reg_x_write(c)                \
485     do {                              \
486         dtv_registers[reg_x_idx] = c; \
487         if (reg_x_idx >= 3) {         \
488             maincpu_resync_limits();  \
489         }                             \
490     } while (0);
491 
492 #define reg_x_read dtv_registers[reg_x_idx]
493 #define reg_y_write(c)                \
494     do {                              \
495         dtv_registers[reg_y_idx] = c; \
496         if (reg_y_idx >= 3) {         \
497             maincpu_resync_limits();  \
498         }                             \
499     } while (0);
500 #define reg_y_read dtv_registers[reg_y_idx]
501 #endif
502     uint8_t reg_p = 0;
503     uint8_t reg_sp = 0;
504     uint8_t flag_n = 0;
505     uint8_t flag_z = 0;
506 #ifndef NEED_REG_PC
507     unsigned int reg_pc;
508 #endif
509 
510     /*
511      * Enable maincpu_resync_limits functionality .. in the old code
512      * this is where the local stack var had its address copied to
513      * the global.
514      */
515     bank_base_ready = true;
516 
517     machine_trigger_reset(MACHINE_RESET_MODE_SOFT);
518 
519     while (1) {
520 #define CLK maincpu_clk
521 #define RMW_FLAG maincpu_rmw_flag
522 #define LAST_OPCODE_INFO last_opcode_info
523 #define LAST_OPCODE_ADDR last_opcode_addr
524 #define TRACEFLG debug.maincpu_traceflg
525 
526 #define CPU_INT_STATUS maincpu_int_status
527 
528 #define ALARM_CONTEXT maincpu_alarm_context
529 
530 #define CHECK_PENDING_ALARM() (clk >= next_alarm_clk(maincpu_int_status))
531 
532 #define CHECK_PENDING_INTERRUPT() check_pending_interrupt(maincpu_int_status)
533 
534 #define TRAP(addr) maincpu_int_status->trap_func(addr);
535 
536 #define ROM_TRAP_HANDLER() traps_handler()
537 
538 #define JAM()                                                         \
539     do {                                                              \
540         unsigned int tmp;                                             \
541                                                                       \
542         EXPORT_REGISTERS();                                           \
543         tmp = machine_jam("   " CPU_STR ": JAM at $%04X   ", reg_pc); \
544         switch (tmp) {                                                \
545             case JAM_RESET:                                           \
546                 DO_INTERRUPT(IK_RESET);                               \
547                 break;                                                \
548             case JAM_HARD_RESET:                                      \
549                 mem_powerup();                                        \
550                 DO_INTERRUPT(IK_RESET);                               \
551                 break;                                                \
552             case JAM_MONITOR:                                         \
553                 monitor_startup(e_comp_space);                        \
554                 IMPORT_REGISTERS();                                   \
555                 break;                                                \
556             default:                                                  \
557                 CLK++;                                                \
558         }                                                             \
559     } while (0)
560 
561 #define CALLER e_comp_space
562 
563 #define ROM_TRAP_ALLOWED() mem_rom_trap_allowed((uint16_t)reg_pc)
564 
565 #define GLOBAL_REGS maincpu_regs
566 
567 #include "6510core.c"
568 
569         maincpu_int_status->num_dma_per_opcode = 0;
570 
571         if (maincpu_clk_limit && (maincpu_clk > maincpu_clk_limit)) {
572             log_error(LOG_DEFAULT, "cycle limit reached.");
573             archdep_vice_exit(1);
574         }
575 #if 0
576         if (CLK > 246171754) {
577             debug.maincpu_traceflg = 1;
578         }
579 #endif
580     }
581 }
582 
583 /* ------------------------------------------------------------------------- */
584 
maincpu_set_pc(int pc)585 void maincpu_set_pc(int pc) {
586 #ifdef C64DTV
587     MOS6510DTV_REGS_SET_PC(&maincpu_regs, pc);
588 #else
589     MOS6510_REGS_SET_PC(&maincpu_regs, pc);
590 #endif
591 }
592 
maincpu_set_a(int a)593 void maincpu_set_a(int a) {
594 #ifdef C64DTV
595     MOS6510DTV_REGS_SET_A(&maincpu_regs, a);
596 #else
597     MOS6510_REGS_SET_A(&maincpu_regs, a);
598 #endif
599 }
600 
maincpu_set_x(int x)601 void maincpu_set_x(int x) {
602 #ifdef C64DTV
603     MOS6510DTV_REGS_SET_X(&maincpu_regs, x);
604 #else
605     MOS6510_REGS_SET_X(&maincpu_regs, x);
606 #endif
607 }
608 
maincpu_set_y(int y)609 void maincpu_set_y(int y) {
610 #ifdef C64DTV
611     MOS6510DTV_REGS_SET_Y(&maincpu_regs, y);
612 #else
613     MOS6510_REGS_SET_Y(&maincpu_regs, y);
614 #endif
615 }
616 
maincpu_set_sign(int n)617 void maincpu_set_sign(int n) {
618 #ifdef C64DTV
619     MOS6510DTV_REGS_SET_SIGN(&maincpu_regs, n);
620 #else
621     MOS6510_REGS_SET_SIGN(&maincpu_regs, n);
622 #endif
623 }
624 
maincpu_set_zero(int z)625 void maincpu_set_zero(int z) {
626 #ifdef C64DTV
627     MOS6510DTV_REGS_SET_ZERO(&maincpu_regs, z);
628 #else
629     MOS6510_REGS_SET_ZERO(&maincpu_regs, z);
630 #endif
631 }
632 
maincpu_set_carry(int c)633 void maincpu_set_carry(int c) {
634 #ifdef C64DTV
635     MOS6510DTV_REGS_SET_CARRY(&maincpu_regs, c);
636 #else
637     MOS6510_REGS_SET_CARRY(&maincpu_regs, c);
638 #endif
639 }
640 
maincpu_set_interrupt(int i)641 void maincpu_set_interrupt(int i) {
642 #ifdef C64DTV
643     MOS6510DTV_REGS_SET_INTERRUPT(&maincpu_regs, i);
644 #else
645     MOS6510_REGS_SET_INTERRUPT(&maincpu_regs, i);
646 #endif
647 }
648 
maincpu_get_pc(void)649 unsigned int maincpu_get_pc(void) {
650 #ifdef C64DTV
651     return MOS6510DTV_REGS_GET_PC(&maincpu_regs);
652 #else
653     return MOS6510_REGS_GET_PC(&maincpu_regs);
654 #endif
655 }
656 
maincpu_get_a(void)657 unsigned int maincpu_get_a(void) {
658 #ifdef C64DTV
659     return MOS6510DTV_REGS_GET_A(&maincpu_regs);
660 #else
661     return MOS6510_REGS_GET_A(&maincpu_regs);
662 #endif
663 }
664 
maincpu_get_x(void)665 unsigned int maincpu_get_x(void) {
666 #ifdef C64DTV
667     return MOS6510DTV_REGS_GET_X(&maincpu_regs);
668 #else
669     return MOS6510_REGS_GET_X(&maincpu_regs);
670 #endif
671 }
672 
maincpu_get_y(void)673 unsigned int maincpu_get_y(void) {
674 #ifdef C64DTV
675     return MOS6510DTV_REGS_GET_Y(&maincpu_regs);
676 #else
677     return MOS6510_REGS_GET_Y(&maincpu_regs);
678 #endif
679 }
680 
maincpu_get_sp(void)681 unsigned int maincpu_get_sp(void) {
682 #ifdef C64DTV
683     return MOS6510DTV_REGS_GET_SP(&maincpu_regs);
684 #else
685     return MOS6510_REGS_GET_SP(&maincpu_regs);
686 #endif
687 }
688 
689 /* ------------------------------------------------------------------------- */
690 
691 static char snap_module_name[] = "MAINCPU";
692 #define SNAP_MAJOR 1
693 #define SNAP_MINOR 1
694 
maincpu_snapshot_write_module(snapshot_t * s)695 int maincpu_snapshot_write_module(snapshot_t *s)
696 {
697     snapshot_module_t *m;
698 
699     m = snapshot_module_create(s, snap_module_name, ((uint8_t)SNAP_MAJOR),
700                                ((uint8_t)SNAP_MINOR));
701     if (m == NULL) {
702         return -1;
703     }
704 
705 #ifdef C64DTV
706     if (SMW_DW(m, maincpu_clk) < 0
707             || SMW_B(m, MOS6510DTV_REGS_GET_A(&maincpu_regs)) < 0
708             || SMW_B(m, MOS6510DTV_REGS_GET_X(&maincpu_regs)) < 0
709             || SMW_B(m, MOS6510DTV_REGS_GET_Y(&maincpu_regs)) < 0
710             || SMW_B(m, MOS6510DTV_REGS_GET_SP(&maincpu_regs)) < 0
711             || SMW_W(m, (uint16_t)MOS6510DTV_REGS_GET_PC(&maincpu_regs)) < 0
712             || SMW_B(m, (uint8_t)MOS6510DTV_REGS_GET_STATUS(&maincpu_regs)) < 0
713             || SMW_B(m, MOS6510DTV_REGS_GET_R3(&maincpu_regs)) < 0
714             || SMW_B(m, MOS6510DTV_REGS_GET_R4(&maincpu_regs)) < 0
715             || SMW_B(m, MOS6510DTV_REGS_GET_R5(&maincpu_regs)) < 0
716             || SMW_B(m, MOS6510DTV_REGS_GET_R6(&maincpu_regs)) < 0
717             || SMW_B(m, MOS6510DTV_REGS_GET_R7(&maincpu_regs)) < 0
718             || SMW_B(m, MOS6510DTV_REGS_GET_R8(&maincpu_regs)) < 0
719             || SMW_B(m, MOS6510DTV_REGS_GET_R9(&maincpu_regs)) < 0
720             || SMW_B(m, MOS6510DTV_REGS_GET_R10(&maincpu_regs)) < 0
721             || SMW_B(m, MOS6510DTV_REGS_GET_R11(&maincpu_regs)) < 0
722             || SMW_B(m, MOS6510DTV_REGS_GET_R12(&maincpu_regs)) < 0
723             || SMW_B(m, MOS6510DTV_REGS_GET_R13(&maincpu_regs)) < 0
724             || SMW_B(m, MOS6510DTV_REGS_GET_R14(&maincpu_regs)) < 0
725             || SMW_B(m, MOS6510DTV_REGS_GET_R15(&maincpu_regs)) < 0
726             || SMW_B(m, MOS6510DTV_REGS_GET_ACM(&maincpu_regs)) < 0
727             || SMW_B(m, MOS6510DTV_REGS_GET_YXM(&maincpu_regs)) < 0
728             || SMW_BA(m, burst_cache, 4) < 0
729             || SMW_W(m, burst_addr) < 0
730             || SMW_DW(m, dtvclockneg) < 0
731             || SMW_DW(m, (uint32_t)last_opcode_info) < 0) {
732         goto fail;
733     }
734 #else
735     if (SMW_DW(m, maincpu_clk) < 0
736             || SMW_B(m, MOS6510_REGS_GET_A(&maincpu_regs)) < 0
737             || SMW_B(m, MOS6510_REGS_GET_X(&maincpu_regs)) < 0
738             || SMW_B(m, MOS6510_REGS_GET_Y(&maincpu_regs)) < 0
739             || SMW_B(m, MOS6510_REGS_GET_SP(&maincpu_regs)) < 0
740             || SMW_W(m, (uint16_t)MOS6510_REGS_GET_PC(&maincpu_regs)) < 0
741             || SMW_B(m, (uint8_t)MOS6510_REGS_GET_STATUS(&maincpu_regs)) < 0
742             || SMW_DW(m, (uint32_t)last_opcode_info) < 0) {
743         goto fail;
744     }
745 #endif
746 
747     if (interrupt_write_snapshot(maincpu_int_status, m) < 0) {
748         goto fail;
749     }
750 
751     if (interrupt_write_new_snapshot(maincpu_int_status, m) < 0) {
752         goto fail;
753     }
754 
755     return snapshot_module_close(m);
756 
757 fail:
758     if (m != NULL) {
759         snapshot_module_close(m);
760     }
761     return -1;
762 }
763 
maincpu_snapshot_read_module(snapshot_t * s)764 int maincpu_snapshot_read_module(snapshot_t *s)
765 {
766     uint8_t a, x, y, sp, status;
767 #ifdef C64DTV
768     uint8_t r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, acm, yxm;
769 #endif
770     uint16_t pc;
771     uint8_t major, minor;
772     snapshot_module_t *m;
773 
774     m = snapshot_module_open(s, snap_module_name, &major, &minor);
775     if (m == NULL) {
776         return -1;
777     }
778 
779     /* FIXME: This is a mighty kludge to prevent VIC-II from stealing the
780        wrong number of cycles.  */
781     maincpu_rmw_flag = 0;
782 
783     /* XXX: Assumes `CLOCK' is the same size as a `DWORD'.  */
784     if (SMR_DW(m, &maincpu_clk) < 0
785             || SMR_B(m, &a) < 0
786             || SMR_B(m, &x) < 0
787             || SMR_B(m, &y) < 0
788             || SMR_B(m, &sp) < 0
789             || SMR_W(m, &pc) < 0
790             || SMR_B(m, &status) < 0
791 #ifdef C64DTV
792             || SMR_B(m, &r3) < 0
793             || SMR_B(m, &r4) < 0
794             || SMR_B(m, &r5) < 0
795             || SMR_B(m, &r6) < 0
796             || SMR_B(m, &r7) < 0
797             || SMR_B(m, &r8) < 0
798             || SMR_B(m, &r9) < 0
799             || SMR_B(m, &r10) < 0
800             || SMR_B(m, &r11) < 0
801             || SMR_B(m, &r12) < 0
802             || SMR_B(m, &r13) < 0
803             || SMR_B(m, &r14) < 0
804             || SMR_B(m, &r15) < 0
805             || SMR_B(m, &acm) < 0
806             || SMR_B(m, &yxm) < 0
807             || SMR_BA(m, burst_cache, 4) < 0
808             || SMR_W(m, &burst_addr) < 0
809             || SMR_DW_INT(m, &dtvclockneg) < 0
810 #endif
811             || SMR_DW_UINT(m, &last_opcode_info) < 0) {
812         goto fail;
813     }
814 
815 #ifdef C64DTV
816     MOS6510DTV_REGS_SET_A(&maincpu_regs, a);
817     MOS6510DTV_REGS_SET_X(&maincpu_regs, x);
818     MOS6510DTV_REGS_SET_Y(&maincpu_regs, y);
819     MOS6510DTV_REGS_SET_SP(&maincpu_regs, sp);
820     MOS6510DTV_REGS_SET_PC(&maincpu_regs, pc);
821     MOS6510DTV_REGS_SET_STATUS(&maincpu_regs, status);
822     MOS6510DTV_REGS_SET_R3(&maincpu_regs, r3);
823     MOS6510DTV_REGS_SET_R4(&maincpu_regs, r4);
824     MOS6510DTV_REGS_SET_R5(&maincpu_regs, r5);
825     MOS6510DTV_REGS_SET_R6(&maincpu_regs, r6);
826     MOS6510DTV_REGS_SET_R7(&maincpu_regs, r7);
827     MOS6510DTV_REGS_SET_R8(&maincpu_regs, r8);
828     MOS6510DTV_REGS_SET_R9(&maincpu_regs, r9);
829     MOS6510DTV_REGS_SET_R10(&maincpu_regs, r10);
830     MOS6510DTV_REGS_SET_R11(&maincpu_regs, r11);
831     MOS6510DTV_REGS_SET_R12(&maincpu_regs, r12);
832     MOS6510DTV_REGS_SET_R13(&maincpu_regs, r13);
833     MOS6510DTV_REGS_SET_R14(&maincpu_regs, r14);
834     MOS6510DTV_REGS_SET_R15(&maincpu_regs, r15);
835     MOS6510DTV_REGS_SET_ACM(&maincpu_regs, acm);
836     MOS6510DTV_REGS_SET_YXM(&maincpu_regs, yxm);
837 #else
838     MOS6510_REGS_SET_A(&maincpu_regs, a);
839     MOS6510_REGS_SET_X(&maincpu_regs, x);
840     MOS6510_REGS_SET_Y(&maincpu_regs, y);
841     MOS6510_REGS_SET_SP(&maincpu_regs, sp);
842     MOS6510_REGS_SET_PC(&maincpu_regs, pc);
843     MOS6510_REGS_SET_STATUS(&maincpu_regs, status);
844 #endif
845 
846     if (interrupt_read_snapshot(maincpu_int_status, m) < 0) {
847         goto fail;
848     }
849 
850     if (interrupt_read_new_snapshot(maincpu_int_status, m) < 0) {
851         goto fail;
852     }
853 
854     return snapshot_module_close(m);
855 
856 fail:
857     if (m != NULL) {
858         snapshot_module_close(m);
859     }
860     return -1;
861 }
862