diff options
Diffstat (limited to 'src/emu/cpu/g65816/g65816.c')
-rw-r--r-- | src/emu/cpu/g65816/g65816.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/emu/cpu/g65816/g65816.c b/src/emu/cpu/g65816/g65816.c new file mode 100644 index 00000000000..4503eb91cac --- /dev/null +++ b/src/emu/cpu/g65816/g65816.c @@ -0,0 +1,484 @@ +/* ======================================================================== */ +/* =============================== COPYRIGHT ============================== */ +/* ======================================================================== */ +/* + +G65C816 CPU Emulator V0.93 + +Copyright (c) 2000 Karl Stenerud +All rights reserved. + +Permission is granted to use this source code for non-commercial purposes. +To use this code for commercial purposes, you must get permission from the +author (Karl Stenerud) at karl@higashiyama-unet.ocn.ne.jp. + + +*/ +/* ======================================================================== */ +/* ================================= NOTES ================================ */ +/* ======================================================================== */ +/* + +Changes: + 0.94 (2007-06-14): + Zsolt Vasvari + - Removed unneccessary checks from MVP and MVN + + 0.93 (2003-07-05): + Angelo Salese <lordkale@libero.it> + - Fixed the BCD conversion when using the Decimal Flag in ADC and SBC. + - Removed the two conversion tables for ADC and SBC as they aren't + needed anymore. + + 0.92 (2000-05-28): + Lee Hammerton <lee-hammerton@hammerhead.ltd.uk> + - Fixed debugger bug that caused D to be misrepresented. + - Fixed MVN and MVP (they were reversed) + + 0.91 (2000-05-22): + Lee Hammerton <lee-hammerton@hammerhead.ltd.uk> + - Fixed reset vector fetch to be little endian + - Fixed disassembler call bug + - Fixed C flag in SBC (should be inverted before operation) + - Fixed JSR to stack PC-1 and RTS to pull PC and add 1 + + Karl Stenerud <karl@higashiyama-unet.ocn.ne.jp> + - Added correct timing for absolute indexed operations + - SBC: fixed corruption of interim values + + 0.90 (2000-05-17): + Karl Stenerud <karl@higashiyama-unet.ocn.ne.jp> + - first public release + + +Note on timings: + - For instructions that write to memory (ASL, ASR, LSL, ROL, ROR, DEC, + INC, STA, STZ), the absolute indexed addressing mode takes 1 extra + cycle to complete. + - The spec says fc (JMP axi) is 6 cyles, but elsewhere says 8 cycles + (which is what it should be) + + +TODO general: + - WAI will not stop if RDY is held high. + + - RDY internally held low when WAI executed and returned to hi when RES, + ABORT, NMI, or IRQ asserted. + + - ABORT will terminate WAI instruction but wil not restart the processor + + - If interrupt occurs after ABORT of WAI, processor returns to WAI + instruction. + + - Add one cycle when indexing across page boundary and E=1 except for STA + and STZ instructions. + + - Add 1 cycle if branch is taken. In Emulation (E= 1 ) mode only --add 1 + cycle if the branch is taken and crosses a page boundary. + + - Add 1 cycle in Emulation mode (E=1) for (dir),y; abs,x; and abs,y + addressing modes. + +*/ +/* ======================================================================== */ +/* ================================= DATA ================================= */ +/* ======================================================================== */ + +#include "g65816cm.h" + +/* Our CPU structure */ +g65816i_cpu_struct g65816i_cpu = {0}; + +int g65816_ICount = 0; + +/* Temporary Variables */ +uint g65816i_source; +uint g65816i_destination; + +static int g65816_readop(UINT32 offset, int size, UINT64 *value) +{ + *value = g65816_read_8_immediate(offset); + + return 1; +} + +extern void (*g65816i_opcodes_M0X0[])(void); +extern uint g65816i_get_reg_M0X0(int regnum); +extern void g65816i_set_reg_M0X0(int regnum, uint val); +extern void g65816i_set_line_M0X0(int line, int state); +extern int g65816i_execute_M0X0(int cycles); + +extern void (*g65816i_opcodes_M0X1[])(void); +extern uint g65816i_get_reg_M0X1(int regnum); +extern void g65816i_set_reg_M0X1(int regnum, uint val); +extern void g65816i_set_line_M0X1(int line, int state); +extern int g65816i_execute_M0X1(int cycles); + +extern void (*g65816i_opcodes_M1X0[])(void); +extern uint g65816i_get_reg_M1X0(int regnum); +extern void g65816i_set_reg_M1X0(int regnum, uint val); +extern void g65816i_set_line_M1X0(int line, int state); +extern int g65816i_execute_M1X0(int cycles); + +extern void (*g65816i_opcodes_M1X1[])(void); +extern uint g65816i_get_reg_M1X1(int regnum); +extern void g65816i_set_reg_M1X1(int regnum, uint val); +extern void g65816i_set_line_M1X1(int line, int state); +extern int g65816i_execute_M1X1(int cycles); + +extern void (*g65816i_opcodes_E[])(void); +extern uint g65816i_get_reg_E(int regnum); +extern void g65816i_set_reg_E(int regnum, uint val); +extern void g65816i_set_line_E(int line, int state); +extern int g65816i_execute_E(int cycles); + +void (**g65816i_opcodes[5])(void) = +{ + g65816i_opcodes_M0X0, + g65816i_opcodes_M0X1, + g65816i_opcodes_M1X0, + g65816i_opcodes_M1X1, + g65816i_opcodes_E +}; + +uint (*g65816i_get_reg[5])(int regnum) = +{ + g65816i_get_reg_M0X0, + g65816i_get_reg_M0X1, + g65816i_get_reg_M1X0, + g65816i_get_reg_M1X1, + g65816i_get_reg_E +}; + +void (*g65816i_set_reg[5])(int regnum, uint val) = +{ + g65816i_set_reg_M0X0, + g65816i_set_reg_M0X1, + g65816i_set_reg_M1X0, + g65816i_set_reg_M1X1, + g65816i_set_reg_E +}; + +void (*g65816i_set_line[5])(int line, int state) = +{ + g65816i_set_line_M0X0, + g65816i_set_line_M0X1, + g65816i_set_line_M1X0, + g65816i_set_line_M1X1, + g65816i_set_line_E +}; + +int (*g65816i_execute[5])(int cycles) = +{ + g65816i_execute_M0X0, + g65816i_execute_M0X1, + g65816i_execute_M1X0, + g65816i_execute_M1X1, + g65816i_execute_E +}; + +/* ======================================================================== */ +/* ================================= API ================================== */ +/* ======================================================================== */ + + +static void g65816_reset(void) +{ + /* Start the CPU */ + CPU_STOPPED = 0; + + /* Put into emulation mode */ + REGISTER_D = 0; + REGISTER_PB = 0; + REGISTER_DB = 0; + REGISTER_S = (REGISTER_S & 0xff) | 0x100; + REGISTER_X &= 0xff; + REGISTER_Y &= 0xff; + if(!FLAG_M) + { + REGISTER_B = REGISTER_A & 0xff00; + REGISTER_A &= 0xff; + } + FLAG_E = EFLAG_SET; + FLAG_M = MFLAG_SET; + FLAG_X = XFLAG_SET; + + /* Clear D and set I */ + FLAG_D = DFLAG_CLEAR; + FLAG_I = IFLAG_SET; + + /* Clear all pending interrupts (should we really do this?) */ + LINE_IRQ = 0; + LINE_NMI = 0; + IRQ_DELAY = 0; + + /* Set the function tables to emulation mode */ + g65816i_set_execution_mode(EXECUTION_MODE_E); + + /* 6502 expects these, but its not in the 65816 spec */ + FLAG_Z = ZFLAG_CLEAR; + REGISTER_S = 0x1ff; + + /* Fetch the reset vector */ + REGISTER_PC = g65816_read_8(VECTOR_RESET) | (g65816_read_8(VECTOR_RESET+1)<<8); + g65816i_jumping(REGISTER_PB | REGISTER_PC); +} + +/* Exit and clean up */ +static void g65816_exit(void) +{ + /* nothing to do yet */ +} + +/* Execute some instructions */ +static int g65816_execute(int cycles) +{ + return FTABLE_EXECUTE(cycles); +} + + +/* Get the current CPU context */ +static void g65816_get_context(void *dst_context) +{ + if(dst_context) + *(g65816i_cpu_struct*)dst_context = g65816i_cpu; +} + +/* Set the current CPU context */ +static void g65816_set_context(void *src_context) +{ + if(src_context) + { + g65816i_cpu = *(g65816i_cpu_struct*)src_context; + g65816i_jumping(REGISTER_PB | REGISTER_PC); + } +} + +/* Get the current Program Counter */ +static unsigned g65816_get_pc(void) +{ + return REGISTER_PB | REGISTER_PC; +} + +/* Set the Program Counter */ +static void g65816_set_pc(unsigned val) +{ + REGISTER_PC = MAKE_UINT_16(val); + REGISTER_PB = (val >> 16) & 0xFF; + g65816_jumping(REGISTER_PB | REGISTER_PC); +} + +/* Get the current Stack Pointer */ +static unsigned g65816_get_sp(void) +{ + return REGISTER_S; +} + +/* Set the Stack Pointer */ +static void g65816_set_sp(unsigned val) +{ + REGISTER_S = FLAG_E ? MAKE_UINT_8(val) | 0x100 : MAKE_UINT_16(val); +} + +/* Get a register */ +static unsigned g65816_get_reg(int regnum) +{ + /* Set the function tables to emulation mode if the FTABLE is NULL */ + if( FTABLE_GET_REG == NULL ) + g65816i_set_execution_mode(EXECUTION_MODE_E); + + return FTABLE_GET_REG(regnum); +} + +/* Set a register */ +static void g65816_set_reg(int regnum, unsigned value) +{ + FTABLE_SET_REG(regnum, value); +} + +/* Set an interrupt line */ +static void g65816_set_irq_line(int line, int state) +{ + FTABLE_SET_LINE(line, state); +} + +/* Set the callback that is called when servicing an interrupt */ +static void g65816_set_irq_callback(int (*callback)(int)) +{ + INT_ACK = callback; +} + + +/* Disassemble an instruction */ +#ifdef MAME_DEBUG +#include "g65816ds.h" + +static offs_t g65816_dasm(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram) +{ + return g65816_disassemble(buffer, (pc & 0x00ffff), (pc & 0xff0000) >> 16, oprom, FLAG_M, FLAG_X); +} +#endif /* MAME_DEBUG */ + + + +static void g65816_init(int index, int clock, const void *config, int (*irqcallback)(int)) +{ + g65816_set_irq_callback(irqcallback); +} + + +/************************************************************************** + * Generic set_info + **************************************************************************/ + +static void g65816_set_info(UINT32 state, cpuinfo *info) +{ + switch (state) + { + /* --- the following bits of info are set as 64-bit signed integers --- */ + case CPUINFO_INT_INPUT_STATE + G65816_LINE_IRQ: g65816_set_irq_line(G65816_LINE_IRQ, info->i); break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_NMI: g65816_set_irq_line(G65816_LINE_NMI, info->i); break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_ABORT: g65816_set_irq_line(G65816_LINE_ABORT, info->i); break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_SO: g65816_set_irq_line(G65816_LINE_SO, info->i); break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_RDY: g65816_set_irq_line(G65816_LINE_RDY, info->i); break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_RESET: g65816_set_irq_line(G65816_LINE_RESET, info->i); break; + + case CPUINFO_INT_PC: g65816_set_pc(info->i); break; + case CPUINFO_INT_SP: g65816_set_sp(info->i); break; + + case CPUINFO_INT_REGISTER + G65816_PC: g65816_set_reg(G65816_PC, info->i); break; + case CPUINFO_INT_REGISTER + G65816_S: g65816_set_reg(G65816_S, info->i); break; + case CPUINFO_INT_REGISTER + G65816_P: g65816_set_reg(G65816_P, info->i); break; + case CPUINFO_INT_REGISTER + G65816_A: g65816_set_reg(G65816_A, info->i); break; + case CPUINFO_INT_REGISTER + G65816_X: g65816_set_reg(G65816_X, info->i); break; + case CPUINFO_INT_REGISTER + G65816_Y: g65816_set_reg(G65816_Y, info->i); break; + case CPUINFO_INT_REGISTER + G65816_PB: g65816_set_reg(G65816_PB, info->i); break; + case CPUINFO_INT_REGISTER + G65816_DB: g65816_set_reg(G65816_DB, info->i); break; + case CPUINFO_INT_REGISTER + G65816_D: g65816_set_reg(G65816_D, info->i); break; + case CPUINFO_INT_REGISTER + G65816_E: g65816_set_reg(G65816_E, info->i); break; + case CPUINFO_INT_REGISTER + G65816_NMI_STATE: g65816_set_reg(G65816_NMI_STATE, info->i); break; + case CPUINFO_INT_REGISTER + G65816_IRQ_STATE: g65816_set_reg(G65816_IRQ_STATE, info->i); break; + + /* --- the following bits of info are set as pointers to data or functions --- */ + case CPUINFO_PTR_G65816_READVECTOR_CALLBACK: READ_VECTOR = (read8_handler) info->f; break; + } +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +void g65816_get_info(UINT32 state, cpuinfo *info) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case CPUINFO_INT_CONTEXT_SIZE: info->i = sizeof(g65816i_cpu); break; + case CPUINFO_INT_INPUT_LINES: info->i = 1; break; + case CPUINFO_INT_DEFAULT_IRQ_VECTOR: info->i = 0; break; + case CPUINFO_INT_ENDIANNESS: info->i = CPU_IS_BE; break; + case CPUINFO_INT_CLOCK_DIVIDER: info->i = 1; break; + case CPUINFO_INT_MIN_INSTRUCTION_BYTES: info->i = 1; break; + case CPUINFO_INT_MAX_INSTRUCTION_BYTES: info->i = 4; break; + case CPUINFO_INT_MIN_CYCLES: info->i = 1; break; + case CPUINFO_INT_MAX_CYCLES: info->i = 20; /* rough guess */ break; + + case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 8; break; + case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 24; break; + case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = 0; break; + case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_DATA: info->i = 0; break; + case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_DATA: info->i = 0; break; + case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_DATA: info->i = 0; break; + case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_IO: info->i = 0; break; + case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_IO: info->i = 0; break; + case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_IO: info->i = 0; break; + + case CPUINFO_INT_INPUT_STATE + G65816_LINE_IRQ: info->i = LINE_IRQ; break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_NMI: info->i = LINE_NMI; break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_ABORT: info->i = 0; break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_SO: info->i = 0; break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_RDY: info->i = 0; break; + case CPUINFO_INT_INPUT_STATE + G65816_LINE_RESET: info->i = 0; break; + + case CPUINFO_INT_PREVIOUSPC: /* not supported */ break; + + case CPUINFO_INT_PC: info->i = g65816_get_pc(); break; + case CPUINFO_INT_SP: info->i = g65816_get_sp(); break; + + case CPUINFO_INT_REGISTER + G65816_PC: info->i = g65816_get_pc(); break; + case CPUINFO_INT_REGISTER + G65816_S: info->i = g65816_get_reg(G65816_S); break; + case CPUINFO_INT_REGISTER + G65816_P: info->i = g65816_get_reg(G65816_P); break; + case CPUINFO_INT_REGISTER + G65816_A: info->i = g65816_get_reg(G65816_A); break; + case CPUINFO_INT_REGISTER + G65816_X: info->i = g65816_get_reg(G65816_X); break; + case CPUINFO_INT_REGISTER + G65816_Y: info->i = g65816_get_reg(G65816_Y); break; + case CPUINFO_INT_REGISTER + G65816_PB: info->i = g65816_get_reg(G65816_PB); break; + case CPUINFO_INT_REGISTER + G65816_DB: info->i = g65816_get_reg(G65816_DB); break; + case CPUINFO_INT_REGISTER + G65816_D: info->i = g65816_get_reg(G65816_D); break; + case CPUINFO_INT_REGISTER + G65816_E: info->i = g65816_get_reg(G65816_E); break; + case CPUINFO_INT_REGISTER + G65816_NMI_STATE: info->i = g65816_get_reg(G65816_NMI_STATE); break; + case CPUINFO_INT_REGISTER + G65816_IRQ_STATE: info->i = g65816_get_reg(G65816_IRQ_STATE); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case CPUINFO_PTR_SET_INFO: info->setinfo = g65816_set_info; break; + case CPUINFO_PTR_GET_CONTEXT: info->getcontext = g65816_get_context; break; + case CPUINFO_PTR_SET_CONTEXT: info->setcontext = g65816_set_context; break; + case CPUINFO_PTR_INIT: info->init = g65816_init; break; + case CPUINFO_PTR_RESET: info->reset = g65816_reset; break; + case CPUINFO_PTR_EXIT: info->exit = g65816_exit; break; + case CPUINFO_PTR_EXECUTE: info->execute = g65816_execute; break; + case CPUINFO_PTR_BURN: info->burn = NULL; break; +#ifdef MAME_DEBUG + case CPUINFO_PTR_DISASSEMBLE: info->disassemble = g65816_dasm; break; +#endif /* MAME_DEBUG */ + case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &g65816_ICount; break; + case CPUINFO_PTR_G65816_READVECTOR_CALLBACK: info->f = (genf *) READ_VECTOR; break; + + case CPUINFO_PTR_READOP: info->readop = g65816_readop; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case CPUINFO_STR_NAME: strcpy(info->s, "G65C816"); break; + case CPUINFO_STR_CORE_FAMILY: strcpy(info->s, "6500"); break; + case CPUINFO_STR_CORE_VERSION: strcpy(info->s, "0.94"); break; + case CPUINFO_STR_CORE_FILE: strcpy(info->s, __FILE__); break; + case CPUINFO_STR_CORE_CREDITS: strcpy(info->s, "Copyright (c) 2000 Karl Stenerud, all rights reserved."); break; + + case CPUINFO_STR_FLAGS: + sprintf(info->s, "%c%c%c%c%c%c%c%c", + g65816i_cpu.flag_n & NFLAG_SET ? 'N':'.', + g65816i_cpu.flag_v & VFLAG_SET ? 'V':'.', + g65816i_cpu.flag_m & MFLAG_SET ? 'M':'.', + g65816i_cpu.flag_x & XFLAG_SET ? 'X':'.', + g65816i_cpu.flag_d & DFLAG_SET ? 'D':'.', + g65816i_cpu.flag_i & IFLAG_SET ? 'I':'.', + g65816i_cpu.flag_z == 0 ? 'Z':'.', + g65816i_cpu.flag_c & CFLAG_SET ? 'C':'.'); + break; + + case CPUINFO_STR_REGISTER + G65816_PC: sprintf(info->s, "PC:%06X", g65816_get_pc()); break; + case CPUINFO_STR_REGISTER + G65816_PB: sprintf(info->s, "PB:%02X", g65816i_cpu.pb>>16); break; + case CPUINFO_STR_REGISTER + G65816_DB: sprintf(info->s, "DB:%02X", g65816i_cpu.db>>16); break; + case CPUINFO_STR_REGISTER + G65816_D: sprintf(info->s, "D:%04X", g65816i_cpu.d); break; + case CPUINFO_STR_REGISTER + G65816_S: sprintf(info->s, "S:%04X", g65816i_cpu.s); break; + case CPUINFO_STR_REGISTER + G65816_P: sprintf(info->s, "P:%02X", + (g65816i_cpu.flag_n&0x80) | + ((g65816i_cpu.flag_v>>1)&0x40) | + g65816i_cpu.flag_m | + g65816i_cpu.flag_x | + g65816i_cpu.flag_d | + g65816i_cpu.flag_i | + ((!g65816i_cpu.flag_z)<<1) | + ((g65816i_cpu.flag_c>>8)&1)); break; + case CPUINFO_STR_REGISTER + G65816_E: sprintf(info->s, "E:%d", g65816i_cpu.flag_e); break; + case CPUINFO_STR_REGISTER + G65816_A: sprintf(info->s, "A:%04X", g65816i_cpu.a | g65816i_cpu.b); break; + case CPUINFO_STR_REGISTER + G65816_X: sprintf(info->s, "X:%04X", g65816i_cpu.x); break; + case CPUINFO_STR_REGISTER + G65816_Y: sprintf(info->s, "Y:%04X", g65816i_cpu.y); break; + case CPUINFO_STR_REGISTER + G65816_NMI_STATE: sprintf(info->s, "NMI:%X", g65816i_cpu.line_nmi); break; + case CPUINFO_STR_REGISTER + G65816_IRQ_STATE: sprintf(info->s, "IRQ:%X", g65816i_cpu.line_irq); break; + } +} + +/* ======================================================================== */ +/* ============================== END OF FILE ============================= */ +/* ======================================================================== */ |