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