1 /*
2 * main65816cpu.c - Emulation of the main 65816 processor.
3 *
4 * Written by
5 * Marco van den Heuvel <blackystardust68@yahoo.com>
6 * Kajtar Zsolt <soci@c64.rulez.org>
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 "main65816cpu.h"
42 #include "mem.h"
43 #include "monitor.h"
44 #include "snapshot.h"
45 #include "traps.h"
46 #include "types.h"
47 #include "wdc65816.h"
48
49 #ifndef EXIT_FAILURE
50 #define EXIT_FAILURE 1
51 #endif
52
53 /* MACHINE_STUFF should define/undef
54
55 - NEED_REG_PC
56
57 */
58
59 /* ------------------------------------------------------------------------- */
60
61 #define NEED_REG_PC
62
63 /* ------------------------------------------------------------------------- */
64
65 /* Implement the hack to make opcode fetches faster. */
66 #define JUMP(addr) \
67 do { \
68 reg_pc = (unsigned int)(addr); \
69 if (reg_pbr != bank_bank || reg_pc >= (unsigned int)bank_limit || reg_pc < (unsigned int)bank_start) { \
70 mem_mmu_translate((unsigned int)(addr) | (reg_pbr << 16), &bank_base, &bank_start, &bank_limit); \
71 bank_bank = reg_pbr; \
72 } \
73 } while (0)
74
75 /* ------------------------------------------------------------------------- */
76
77 #ifndef STORE
78 #define STORE(addr, value) \
79 (*_mem_write_tab_ptr[(addr) >> 8])((uint16_t)(addr), (uint8_t)(value))
80 #endif
81
82 #ifndef LOAD
83 #define LOAD(addr) \
84 (*_mem_read_tab_ptr[(addr) >> 8])((uint16_t)(addr))
85 #endif
86
87 /* Those may be overridden by the machine stuff. Probably we want them in
88 the .def files, but if most of the machines do not use, we might keep it
89 here and only override it where needed. */
90
91 #ifndef DMA_FUNC
maincpu_generic_dma(void)92 static void maincpu_generic_dma(void)
93 {
94 /* Generic DMA hosts can be implemented here.
95 For example a very accurate REU emulation. */
96 }
97 #define DMA_FUNC maincpu_generic_dma()
98 #endif
99
100 #ifndef DMA_ON_RESET
101 #define DMA_ON_RESET
102 #endif
103
104 #ifndef CPU_ADDITIONAL_RESET
105 #define CPU_ADDITIONAL_RESET()
106 #endif
107
108 #ifndef CPU_ADDITIONAL_INIT
109 #define CPU_ADDITIONAL_INIT()
110 #endif
111
112 /* ------------------------------------------------------------------------- */
113
114 struct interrupt_cpu_status_s *maincpu_int_status = NULL;
115 #ifndef CYCLE_EXACT_ALARM
116 alarm_context_t *maincpu_alarm_context = NULL;
117 #endif
118 clk_guard_t *maincpu_clk_guard = NULL;
119 monitor_interface_t *maincpu_monitor_interface = NULL;
120
121 /* This flag is an obsolete optimization. It's always 0 for the 65816 CPU,
122 but has to be kept for the common code. */
123 int maincpu_rmw_flag = 0;
124
125 /* Global clock counter. */
126 CLOCK maincpu_clk = 0L;
127 /* if != 0, exit when this many cycles have been executed */
128 CLOCK maincpu_clk_limit = 0L;
129
130 /* Information about the last executed opcode. This is used to know the
131 number of write cycles in the last executed opcode and to delay interrupts
132 by one more cycle if necessary, as happens with conditional branch opcodes
133 when the branch is taken. */
134 unsigned int last_opcode_info;
135
136 /* Address of the last executed opcode. This is used by watchpoints. */
137 unsigned int last_opcode_addr;
138
139 /* Public copy of the CPU registers. As putting the registers into the
140 function makes it faster, you have to generate a `TRAP' interrupt to have
141 the values copied into this struct. */
142 WDC65816_regs_t maincpu_regs;
143
144 /* ------------------------------------------------------------------------- */
145
maincpu_monitor_interface_get(void)146 monitor_interface_t *maincpu_monitor_interface_get(void)
147 {
148 maincpu_monitor_interface->cpu_regs = NULL;
149 maincpu_monitor_interface->cpu_R65C02_regs = NULL;
150 maincpu_monitor_interface->cpu_65816_regs = &maincpu_regs;
151 maincpu_monitor_interface->dtv_cpu_regs = NULL;
152 maincpu_monitor_interface->z80_cpu_regs = NULL;
153 maincpu_monitor_interface->h6809_cpu_regs = NULL;
154
155 maincpu_monitor_interface->int_status = maincpu_int_status;
156
157 maincpu_monitor_interface->clk = &maincpu_clk;
158
159 maincpu_monitor_interface->current_bank = 0;
160 maincpu_monitor_interface->current_bank_index = 0;
161
162 maincpu_monitor_interface->mem_bank_list = mem_bank_list;
163 maincpu_monitor_interface->mem_bank_list_nos = mem_bank_list_nos;
164
165 maincpu_monitor_interface->mem_bank_from_name = mem_bank_from_name;
166 maincpu_monitor_interface->mem_bank_index_from_bank = mem_bank_index_from_bank;
167 maincpu_monitor_interface->mem_bank_flags_from_bank = mem_bank_flags_from_bank;
168
169 maincpu_monitor_interface->mem_bank_read = mem_bank_read;
170 maincpu_monitor_interface->mem_bank_peek = mem_bank_peek;
171 maincpu_monitor_interface->mem_bank_write = mem_bank_write;
172 maincpu_monitor_interface->mem_bank_poke = mem_bank_poke;
173
174 maincpu_monitor_interface->mem_ioreg_list_get = mem_ioreg_list_get;
175
176 maincpu_monitor_interface->toggle_watchpoints_func = mem_toggle_watchpoints;
177
178 maincpu_monitor_interface->set_bank_base = NULL;
179 maincpu_monitor_interface->get_line_cycle = machine_get_line_cycle;
180
181 return maincpu_monitor_interface;
182 }
183
184 /* ------------------------------------------------------------------------- */
185
maincpu_early_init(void)186 void maincpu_early_init(void)
187 {
188 maincpu_int_status = interrupt_cpu_status_new();
189 }
190
maincpu_init(void)191 void maincpu_init(void)
192 {
193 interrupt_cpu_status_init(maincpu_int_status, &last_opcode_info);
194
195 /* cpu specific additional init routine */
196 CPU_ADDITIONAL_INIT();
197 }
198
maincpu_shutdown(void)199 void maincpu_shutdown(void)
200 {
201 interrupt_cpu_status_destroy(maincpu_int_status);
202 }
203
cpu_reset(void)204 static void cpu_reset(void)
205 {
206 int preserve_monitor;
207
208 preserve_monitor = maincpu_int_status->global_pending_int & IK_MONITOR;
209
210 interrupt_cpu_status_reset(maincpu_int_status);
211
212 if (preserve_monitor) {
213 interrupt_monitor_trap_on(maincpu_int_status);
214 }
215
216 maincpu_clk = 6; /* # of clock cycles needed for RESET. */
217
218 /* CPU specific extra reset routine, currently only used
219 for 8502 fast mode refresh cycle. */
220 CPU_ADDITIONAL_RESET();
221
222 /* Do machine-specific initialization. */
223 machine_reset();
224 }
225
maincpu_reset(void)226 void maincpu_reset(void)
227 {
228 cpu_reset();
229 }
230
231 /* ------------------------------------------------------------------------- */
232
233 #ifdef NEED_REG_PC
234 unsigned int reg_pc;
235 #endif
236
237 static bool bank_base_ready = false;
238 static uint8_t *bank_base = NULL;
239 static int bank_start = 0;
240 static int bank_limit = 0;
241 static uint8_t bank_bank = 0;
242
maincpu_resync_limits(void)243 void maincpu_resync_limits(void)
244 {
245 if (bank_base_ready) {
246 mem_mmu_translate(reg_pc | (bank_bank << 16), &bank_base, &bank_start, &bank_limit);
247 }
248 }
249
maincpu_mainloop(void)250 void maincpu_mainloop(void)
251 {
252 /* Notice that using a struct for these would make it a lot slower (at
253 least, on gcc 2.7.2.x). */
254 union regs {
255 uint16_t reg_s;
256 uint8_t reg_q[2];
257 } regs65802;
258
259 #define reg_c regs65802.reg_s
260 #ifndef WORDS_BIGENDIAN
261 #define reg_a regs65802.reg_q[0]
262 #define reg_b regs65802.reg_q[1]
263 #else
264 #define reg_a regs65802.reg_q[1]
265 #define reg_b regs65802.reg_q[0]
266 #endif
267
268 uint16_t reg_x = 0;
269 uint16_t reg_y = 0;
270 uint8_t reg_pbr = 0;
271 uint8_t reg_dbr = 0;
272 uint16_t reg_dpr = 0;
273 uint8_t reg_p = 0;
274 uint16_t reg_sp = 0x100;
275 uint8_t flag_n = 0;
276 uint8_t flag_z = 0;
277 uint8_t reg_emul = 1;
278 int interrupt65816 = IK_RESET;
279 #ifndef NEED_REG_PC
280 unsigned int reg_pc;
281 #endif
282
283 /*
284 * Enable maincpu_resync_limits functionality .. in the old code
285 * this is where the local stack var had its address copied to
286 * the global.
287 */
288 bank_base_ready = true;
289
290 reg_c = 0;
291
292 machine_trigger_reset(MACHINE_RESET_MODE_SOFT);
293
294 while (1) {
295
296 #define CLK maincpu_clk
297 #define LAST_OPCODE_INFO last_opcode_info
298 #define LAST_OPCODE_ADDR last_opcode_addr
299 #define TRACEFLG debug.maincpu_traceflg
300
301 #define CPU_INT_STATUS maincpu_int_status
302
303 #define ALARM_CONTEXT maincpu_alarm_context
304
305 #define CHECK_PENDING_ALARM() \
306 (clk >= next_alarm_clk(maincpu_int_status))
307
308 #define CHECK_PENDING_INTERRUPT() \
309 check_pending_interrupt(maincpu_int_status)
310
311 #define TRAP(addr) \
312 maincpu_int_status->trap_func(addr);
313
314 #define ROM_TRAP_HANDLER() \
315 traps_handler()
316
317 #define JAM() \
318 do { \
319 unsigned int tmp; \
320 \
321 EXPORT_REGISTERS(); \
322 tmp = machine_jam(" " CPU_STR ": JAM at $%02x%04X ", reg_pbr, reg_pc); \
323 switch (tmp) { \
324 case JAM_RESET: \
325 DO_INTERRUPT(IK_RESET); \
326 break; \
327 case JAM_HARD_RESET: \
328 mem_powerup(); \
329 DO_INTERRUPT(IK_RESET); \
330 break; \
331 case JAM_MONITOR: \
332 monitor_startup(e_comp_space); \
333 IMPORT_REGISTERS(); \
334 break; \
335 default: \
336 STP(); \
337 } \
338 } while (0)
339
340 #define STP_65816() JAM()
341 #define WAI_65816() WAI()
342 #define COP_65816(value) COP()
343
344 #define CALLER e_comp_space
345
346 #define ROM_TRAP_ALLOWED() mem_rom_trap_allowed((uint16_t)reg_pc)
347
348 #define GLOBAL_REGS maincpu_regs
349
350 #include "65816core.c"
351
352 maincpu_int_status->num_dma_per_opcode = 0;
353
354 if (maincpu_clk_limit && (maincpu_clk > maincpu_clk_limit)) {
355 log_error(LOG_DEFAULT, "cycle limit reached.");
356 archdep_vice_exit(EXIT_FAILURE);
357 }
358 #if 0
359 if (CLK > 246171754)
360 debug.maincpu_traceflg = 1;
361 #endif
362 }
363 }
364
365 /* ------------------------------------------------------------------------- */
366
maincpu_set_pc(int pc)367 void maincpu_set_pc(int pc) {
368 WDC65816_REGS_SET_PC(&maincpu_regs, pc);
369 }
370
maincpu_set_a(int a)371 void maincpu_set_a(int a) {
372 WDC65816_REGS_SET_A(&maincpu_regs, a);
373 }
374
maincpu_set_x(int x)375 void maincpu_set_x(int x) {
376 WDC65816_REGS_SET_X(&maincpu_regs, x);
377 }
378
maincpu_set_y(int y)379 void maincpu_set_y(int y) {
380 WDC65816_REGS_SET_Y(&maincpu_regs, y);
381 }
382
maincpu_set_sign(int n)383 void maincpu_set_sign(int n) {
384 WDC65816_REGS_SET_SIGN(&maincpu_regs, n);
385 }
386
maincpu_set_zero(int z)387 void maincpu_set_zero(int z) {
388 WDC65816_REGS_SET_ZERO(&maincpu_regs, z);
389 }
390
maincpu_set_carry(int c)391 void maincpu_set_carry(int c) {
392 WDC65816_REGS_SET_CARRY(&maincpu_regs, c);
393 }
394
maincpu_set_interrupt(int i)395 void maincpu_set_interrupt(int i) {
396 WDC65816_REGS_SET_INTERRUPT(&maincpu_regs, i);
397 }
398
maincpu_get_pc(void)399 unsigned int maincpu_get_pc(void) {
400 return WDC65816_REGS_GET_PC(&maincpu_regs);
401 }
402
maincpu_get_a(void)403 unsigned int maincpu_get_a(void) {
404 return WDC65816_REGS_GET_A(&maincpu_regs);
405 }
406
maincpu_get_x(void)407 unsigned int maincpu_get_x(void) {
408 return WDC65816_REGS_GET_X(&maincpu_regs);
409 }
410
maincpu_get_y(void)411 unsigned int maincpu_get_y(void) {
412 return WDC65816_REGS_GET_Y(&maincpu_regs);
413 }
414
maincpu_get_sp(void)415 unsigned int maincpu_get_sp(void) {
416 return WDC65816_REGS_GET_SP(&maincpu_regs);
417 }
418
419 /* ------------------------------------------------------------------------- */
420
421 static char snap_module_name[] = "MAIN6565802CPU";
422 #define SNAP_MAJOR 1
423 #define SNAP_MINOR 1
424
maincpu_snapshot_write_module(snapshot_t * s)425 int maincpu_snapshot_write_module(snapshot_t *s)
426 {
427 snapshot_module_t *m;
428
429 m = snapshot_module_create(s, snap_module_name, ((uint8_t)SNAP_MAJOR),
430 ((uint8_t)SNAP_MINOR));
431 if (m == NULL)
432 return -1;
433
434 if (0
435 || SMW_DW(m, maincpu_clk) < 0
436 || SMW_B(m, (uint8_t)WDC65816_REGS_GET_A(&maincpu_regs)) < 0
437 || SMW_B(m, (uint8_t)WDC65816_REGS_GET_B(&maincpu_regs)) < 0
438 || SMW_W(m, (uint16_t)WDC65816_REGS_GET_X(&maincpu_regs)) < 0
439 || SMW_W(m, (uint16_t)WDC65816_REGS_GET_Y(&maincpu_regs)) < 0
440 || SMW_W(m, (uint16_t)WDC65816_REGS_GET_SP(&maincpu_regs)) < 0
441 || SMW_W(m, (uint16_t)WDC65816_REGS_GET_DPR(&maincpu_regs)) < 0
442 || SMW_B(m, (uint8_t)WDC65816_REGS_GET_PBR(&maincpu_regs)) < 0
443 || SMW_B(m, (uint8_t)WDC65816_REGS_GET_DBR(&maincpu_regs)) < 0
444 || SMW_B(m, (uint8_t)WDC65816_REGS_GET_EMUL(&maincpu_regs)) < 0
445 || SMW_W(m, (uint16_t)WDC65816_REGS_GET_PC(&maincpu_regs)) < 0
446 || SMW_B(m, (uint8_t)WDC65816_REGS_GET_STATUS(&maincpu_regs)) < 0
447 || SMW_DW(m, (uint8_t)last_opcode_info) < 0)
448 goto fail;
449
450 if (interrupt_write_snapshot(maincpu_int_status, m) < 0) {
451 goto fail;
452 }
453
454 if (interrupt_write_new_snapshot(maincpu_int_status, m) < 0) {
455 goto fail;
456 }
457
458 return snapshot_module_close(m);
459
460 fail:
461 if (m != NULL) {
462 snapshot_module_close(m);
463 }
464 return -1;
465 }
466
maincpu_snapshot_read_module(snapshot_t * s)467 int maincpu_snapshot_read_module(snapshot_t *s)
468 {
469 uint8_t a, b, pbr, dbr, emul, status;
470 uint16_t x, y, sp, pc, dpr;
471 uint8_t major, minor;
472 snapshot_module_t *m;
473
474 m = snapshot_module_open(s, snap_module_name, &major, &minor);
475 if (m == NULL) {
476 return -1;
477 }
478
479 /* XXX: Assumes `CLOCK' is the same size as a `DWORD'. */
480 if (0
481 || SMR_DW(m, &maincpu_clk) < 0
482 || SMR_B(m, &a) < 0
483 || SMR_B(m, &b) < 0
484 || SMR_W(m, &x) < 0
485 || SMR_W(m, &y) < 0
486 || SMR_W(m, &sp) < 0
487 || SMR_W(m, &dpr) < 0
488 || SMR_B(m, &pbr) < 0
489 || SMR_B(m, &dbr) < 0
490 || SMR_B(m, &emul) < 0
491 || SMR_W(m, &pc) < 0
492 || SMR_B(m, &status) < 0
493 || SMR_DW_UINT(m, &last_opcode_info) < 0)
494 goto fail;
495
496 WDC65816_REGS_SET_A(&maincpu_regs, a);
497 WDC65816_REGS_SET_B(&maincpu_regs, b);
498 WDC65816_REGS_SET_X(&maincpu_regs, x);
499 WDC65816_REGS_SET_Y(&maincpu_regs, y);
500 WDC65816_REGS_SET_SP(&maincpu_regs, sp);
501 WDC65816_REGS_SET_DPR(&maincpu_regs, dpr);
502 WDC65816_REGS_SET_PBR(&maincpu_regs, pbr);
503 WDC65816_REGS_SET_DBR(&maincpu_regs, dbr);
504 WDC65816_REGS_SET_EMUL(&maincpu_regs, emul);
505 WDC65816_REGS_SET_PC(&maincpu_regs, pc);
506 WDC65816_REGS_SET_STATUS(&maincpu_regs, status);
507
508 if (interrupt_read_snapshot(maincpu_int_status, m) < 0) {
509 goto fail;
510 }
511
512 if (interrupt_read_new_snapshot(maincpu_int_status, m) < 0) {
513 goto fail;
514 }
515
516 return snapshot_module_close(m);
517
518 fail:
519 if (m != NULL)
520 snapshot_module_close(m);
521 return -1;
522 }
523