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