From 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 8 Nov 2015 12:56:12 +0100 Subject: Rename *.c -> *.cpp in our source (nw) --- src/devices/cpu/e0c6200/e0c6200.c | 837 -------------------------------- src/devices/cpu/e0c6200/e0c6200.cpp | 837 ++++++++++++++++++++++++++++++++ src/devices/cpu/e0c6200/e0c6200d.c | 708 --------------------------- src/devices/cpu/e0c6200/e0c6200d.cpp | 708 +++++++++++++++++++++++++++ src/devices/cpu/e0c6200/e0c6s46.c | 893 ----------------------------------- src/devices/cpu/e0c6200/e0c6s46.cpp | 893 +++++++++++++++++++++++++++++++++++ 6 files changed, 2438 insertions(+), 2438 deletions(-) delete mode 100644 src/devices/cpu/e0c6200/e0c6200.c create mode 100644 src/devices/cpu/e0c6200/e0c6200.cpp delete mode 100644 src/devices/cpu/e0c6200/e0c6200d.c create mode 100644 src/devices/cpu/e0c6200/e0c6200d.cpp delete mode 100644 src/devices/cpu/e0c6200/e0c6s46.c create mode 100644 src/devices/cpu/e0c6200/e0c6s46.cpp (limited to 'src/devices/cpu/e0c6200') diff --git a/src/devices/cpu/e0c6200/e0c6200.c b/src/devices/cpu/e0c6200/e0c6200.c deleted file mode 100644 index e0b6a2eb8d2..00000000000 --- a/src/devices/cpu/e0c6200/e0c6200.c +++ /dev/null @@ -1,837 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:hap -/* - - Seiko Epson E0C6200 CPU core and E0C62 MCU family - - References: - - 1998 MF297-06a E0C6200/E0C6200A Core CPU Manual - - 1998 MF1049-01a E0C6S46 Technical Manual - - E0C6200 is a CPU core used as the basis of many chips, it is not standalone. - Seiko Epson often changed prefixes of their device names. Depending on when, - the E0C6200 is known as SMC6200, E0C6200, S1C6200. - - TODO: - - RLC is part of the r,q opcodes and requires that r == q, what happens otherwise? - - documentation is conflicting on whether or not the zero flag is set on RLC/RRC - -*/ - -#include "e0c6200.h" -#include "debugger.h" - -#include "e0c6200op.inc" - - -// disasm -void e0c6200_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) -{ - switch (entry.index()) - { - case STATE_GENFLAGS: - strprintf(str, "%c%c%c%c", - (m_f & I_FLAG) ? 'I':'i', - (m_f & D_FLAG) ? 'D':'d', - (m_f & Z_FLAG) ? 'Z':'z', - (m_f & C_FLAG) ? 'C':'c' - ); - break; - - default: break; - } -} - -offs_t e0c6200_cpu_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) -{ - extern CPU_DISASSEMBLE(e0c6200); - return CPU_DISASSEMBLE_NAME(e0c6200)(this, buffer, pc, oprom, opram, options); -} - - - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -enum -{ - E0C6200_PC=1, E0C6200_A, E0C6200_B, - E0C6200_XP, E0C6200_XH, E0C6200_XL, - E0C6200_YP, E0C6200_YH, E0C6200_YL, - E0C6200_SP -}; - -void e0c6200_cpu_device::device_start() -{ - m_program = &space(AS_PROGRAM); - m_data = &space(AS_DATA); - - // zerofill - m_op = 0; - m_prev_op = 0; - m_irq_vector = 0; - m_irq_id = 0; - m_possible_irq = false; - m_halt = m_sleep = false; - m_pc = 0; - m_prev_pc = 0; - m_npc = 0; - m_jpc = 0; - - m_a = 0; - m_b = 0; - m_xp = m_xh = m_xl = 0; - m_yp = m_yh = m_yl = 0; - m_sp = 0; - m_f = 0; - - // register for savestates - save_item(NAME(m_op)); - save_item(NAME(m_prev_op)); - save_item(NAME(m_irq_vector)); - save_item(NAME(m_irq_id)); - save_item(NAME(m_possible_irq)); - save_item(NAME(m_halt)); - save_item(NAME(m_sleep)); - save_item(NAME(m_pc)); - save_item(NAME(m_prev_pc)); - save_item(NAME(m_npc)); - save_item(NAME(m_jpc)); - save_item(NAME(m_a)); - save_item(NAME(m_b)); - save_item(NAME(m_xp)); - save_item(NAME(m_xh)); - save_item(NAME(m_xl)); - save_item(NAME(m_yp)); - save_item(NAME(m_yh)); - save_item(NAME(m_yl)); - save_item(NAME(m_sp)); - save_item(NAME(m_f)); - - // register state for debugger - state_add(E0C6200_PC, "PC", m_pc).formatstr("%04X"); - state_add(E0C6200_A, "A", m_a).formatstr("%01X"); - state_add(E0C6200_B, "B", m_b).formatstr("%01X"); - state_add(E0C6200_XP, "XP", m_xp).formatstr("%01X"); - state_add(E0C6200_XH, "XH", m_xh).formatstr("%01X"); - state_add(E0C6200_XL, "XL", m_xl).formatstr("%01X"); - state_add(E0C6200_YP, "YP", m_yp).formatstr("%01X"); - state_add(E0C6200_YH, "YH", m_yh).formatstr("%01X"); - state_add(E0C6200_YL, "YL", m_yl).formatstr("%01X"); - state_add(E0C6200_SP, "SP", m_sp).formatstr("%02X"); - - state_add(STATE_GENPC, "curpc", m_pc).formatstr("%04X").noshow(); - state_add(STATE_GENFLAGS, "GENFLAGS", m_f).formatstr("%4s").noshow(); - - m_icountptr = &m_icount; -} - - - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void e0c6200_cpu_device::device_reset() -{ - m_halt = m_sleep = false; - m_op = 0xfff; // nop - m_pc = 0x100; - m_f &= 3; // decimal flag is 0 on 6200A, undefined on 6200 -} - - - -//------------------------------------------------- -// execute loop -//------------------------------------------------- - -void e0c6200_cpu_device::do_interrupt() -{ - // interrupt handling takes 13* cycles, plus 1 extra if cpu was halted - // *: 12.5 on E0C6200A, does the cpu osc source change polarity or something? - m_icount -= 13; - if (m_halt) - m_icount--; - - m_halt = m_sleep = false; - push_pc(); - m_f &= ~I_FLAG; - - // page 1 of the current bank - m_pc = (m_pc & 0x1000) | 0x100 | m_irq_vector; - - standard_irq_callback(m_irq_id); -} - -void e0c6200_cpu_device::execute_run() -{ - while (m_icount > 0) - { - // check/handle interrupt, but not right after EI or in the middle of a longjump - if (m_possible_irq && (m_op & 0xfe0) != 0xe40 && (m_op & 0xff8) != 0xf48) - { - m_possible_irq = false; - if (m_f & I_FLAG && check_interrupt()) - { - do_interrupt(); - if (m_icount <= 0) - break; - } - } - - // core cpu not running (peripherals still work) - if (m_halt || m_sleep) - { - m_icount = 0; - break; - } - - // remember previous state, prepare pset-longjump - m_prev_op = m_op; - m_prev_pc = m_pc; - m_jpc = ((m_prev_op & 0xfe0) == 0xe40) ? m_npc : (m_prev_pc & 0x1f00); - - // fetch next opcode - debugger_instruction_hook(this, m_pc); - m_op = m_program->read_word(m_pc << 1) & 0xfff; - m_pc = (m_pc & 0x1000) | ((m_pc + 1) & 0x0fff); - - // minimal opcode time is 5 clock cycles, opcodes take 5, 7, or 12 clock cycles - m_icount -= 5; - - // handle opcode - execute_one(); - } -} - - - -//------------------------------------------------- -// execute one -//------------------------------------------------- - -void e0c6200_cpu_device::execute_one() -{ - // legend: - // X = --.XH.XL 8-bit - // Y = --.YH.YL 8-bit - // IX = XP.XH.XL 12-bit index register - // IY = YP.YH.YL 12-bit index register - // MX = data memory at IX - // MY = data memory at IY - // Mn = data memory at 0-F, via 4-bit immediate param - // r/q = 2-bit param directing to A/B/MX/MY - // i = 4-bit immediate param - // e = 8-bit immediate param - // s = 8-bit immediate branch destination - - switch (m_op & 0xf00) - { - // JP s: jump unconditional - case 0x000: - do_branch(); - break; - - // JP C,s: jump if carry - case 0x200: - do_branch(m_f & C_FLAG); - break; - - // JP NC,s: jump if no carry - case 0x300: - do_branch(~m_f & C_FLAG); - break; - - // JP Z,s: jump if zero - case 0x600: - do_branch(m_f & Z_FLAG); - break; - - // JP NZ,s: jump if not zero - case 0x700: - do_branch(~m_f & Z_FLAG); - break; - - // CALL s: call unconditional (on current bank) - case 0x400: - m_icount -= 2; - push_pc(); - m_pc = (m_pc & 0x1000) | (m_jpc & 0x0f00) | (m_op & 0xff); - break; - - // CALZ s: call zero page (on current bank) - case 0x500: - m_icount -= 2; - push_pc(); - m_pc = (m_pc & 0x1000) | (m_op & 0xff); - break; - - // RETD e: return from subroutine, then LBPX MX,e - case 0x100: - m_icount -= 7; - pop_pc(); - // fall through! - - // LBPX MX,e: load memory with 8-bit immediate data, increment X by 2 - case 0x900: - write_mx(m_op & 0xf); inc_x(); - write_mx(m_op >> 4 & 0xf); inc_x(); - break; - - // LD X,e: load X with 8-bit immediate data - case 0xb00: - m_xh = m_op >> 4 & 0xf; - m_xl = m_op & 0xf; - break; - - // LD Y,e: load Y with 8-bit immediate data - case 0x800: - m_yh = m_op >> 4 & 0xf; - m_yl = m_op & 0xf; - break; - - - default: - switch (m_op) - { - // LD r,q: load register with register - case 0xec0: /* m_a = m_a; */ break; - case 0xec1: m_a = m_b; break; - case 0xec2: m_a = read_mx(); break; - case 0xec3: m_a = read_my(); break; - case 0xec4: m_b = m_a; break; - case 0xec5: /* m_b = m_b; */ break; - case 0xec6: m_b = read_mx(); break; - case 0xec7: m_b = read_my(); break; - case 0xec8: write_mx(m_a); break; - case 0xec9: write_mx(m_b); break; - case 0xeca: write_mx(read_mx()); break; - case 0xecb: write_mx(read_my()); break; - case 0xecc: write_my(m_a); break; - case 0xecd: write_my(m_b); break; - case 0xece: write_my(read_mx()); break; - case 0xecf: write_my(read_my()); break; - - // LDPX r,q: LD r,q, then increment X - case 0xee0: /* m_a = m_a; */ inc_x(); break; - case 0xee1: m_a = m_b; inc_x(); break; - case 0xee2: m_a = read_mx(); inc_x(); break; - case 0xee3: m_a = read_my(); inc_x(); break; - case 0xee4: m_b = m_a; inc_x(); break; - case 0xee5: /* m_b = m_b; */ inc_x(); break; - case 0xee6: m_b = read_mx(); inc_x(); break; - case 0xee7: m_b = read_my(); inc_x(); break; - case 0xee8: write_mx(m_a); inc_x(); break; - case 0xee9: write_mx(m_b); inc_x(); break; - case 0xeea: write_mx(read_mx()); inc_x(); break; - case 0xeeb: write_mx(read_my()); inc_x(); break; - case 0xeec: write_my(m_a); inc_x(); break; - case 0xeed: write_my(m_b); inc_x(); break; - case 0xeee: write_my(read_mx()); inc_x(); break; - case 0xeef: write_my(read_my()); inc_x(); break; - - // LDPY r,q: LD r,q, then increment Y - case 0xef0: /* m_a = m_a; */ inc_y(); break; - case 0xef1: m_a = m_b; inc_y(); break; - case 0xef2: m_a = read_mx(); inc_y(); break; - case 0xef3: m_a = read_my(); inc_y(); break; - case 0xef4: m_b = m_a; inc_y(); break; - case 0xef5: /* m_b = m_b; */ inc_y(); break; - case 0xef6: m_b = read_mx(); inc_y(); break; - case 0xef7: m_b = read_my(); inc_y(); break; - case 0xef8: write_mx(m_a); inc_y(); break; - case 0xef9: write_mx(m_b); inc_y(); break; - case 0xefa: write_mx(read_mx()); inc_y(); break; - case 0xefb: write_mx(read_my()); inc_y(); break; - case 0xefc: write_my(m_a); inc_y(); break; - case 0xefd: write_my(m_b); inc_y(); break; - case 0xefe: write_my(read_mx()); inc_y(); break; - case 0xeff: write_my(read_my()); inc_y(); break; - - // LD Xphl/Yphl,r: load IX/IY with register - case 0xe80: m_xp = m_a; break; - case 0xe81: m_xp = m_b; break; - case 0xe82: m_xp = read_mx(); break; - case 0xe83: m_xp = read_my(); break; - case 0xe84: m_xh = m_a; break; - case 0xe85: m_xh = m_b; break; - case 0xe86: m_xh = read_mx(); break; - case 0xe87: m_xh = read_my(); break; - case 0xe88: m_xl = m_a; break; - case 0xe89: m_xl = m_b; break; - case 0xe8a: m_xl = read_mx(); break; - case 0xe8b: m_xl = read_my(); break; - case 0xe90: m_yp = m_a; break; - case 0xe91: m_yp = m_b; break; - case 0xe92: m_yp = read_mx(); break; - case 0xe93: m_yp = read_my(); break; - case 0xe94: m_yh = m_a; break; - case 0xe95: m_yh = m_b; break; - case 0xe96: m_yh = read_mx(); break; - case 0xe97: m_yh = read_my(); break; - case 0xe98: m_yl = m_a; break; - case 0xe99: m_yl = m_b; break; - case 0xe9a: m_yl = read_mx(); break; - case 0xe9b: m_yl = read_my(); break; - - // LD r,Xphl/Yphl: load register with IX/IY - case 0xea0: m_a = m_xp; break; - case 0xea1: m_b = m_xp; break; - case 0xea2: write_mx(m_xp); break; - case 0xea3: write_my(m_xp); break; - case 0xea4: m_a = m_xh; break; - case 0xea5: m_b = m_xh; break; - case 0xea6: write_mx(m_xh); break; - case 0xea7: write_my(m_xh); break; - case 0xea8: m_a = m_xl; break; - case 0xea9: m_b = m_xl; break; - case 0xeaa: write_mx(m_xl); break; - case 0xeab: write_my(m_xl); break; - case 0xeb0: m_a = m_yp; break; - case 0xeb1: m_b = m_yp; break; - case 0xeb2: write_mx(m_yp); break; - case 0xeb3: write_my(m_yp); break; - case 0xeb4: m_a = m_yh; break; - case 0xeb5: m_b = m_yh; break; - case 0xeb6: write_mx(m_yh); break; - case 0xeb7: write_my(m_yh); break; - case 0xeb8: m_a = m_yl; break; - case 0xeb9: m_b = m_yl; break; - case 0xeba: write_mx(m_yl); break; - case 0xebb: write_my(m_yl); break; - - // LD SPhl,r: load stackpointer with register - case 0xfe0: m_sp = (m_sp & 0xf0) | m_a; break; - case 0xfe1: m_sp = (m_sp & 0xf0) | m_b; break; - case 0xfe2: m_sp = (m_sp & 0xf0) | read_mx(); break; - case 0xfe3: m_sp = (m_sp & 0xf0) | read_my(); break; - case 0xff0: m_sp = (m_sp & 0x0f) | m_a << 4; break; - case 0xff1: m_sp = (m_sp & 0x0f) | m_b << 4; break; - case 0xff2: m_sp = (m_sp & 0x0f) | read_mx() << 4; break; - case 0xff3: m_sp = (m_sp & 0x0f) | read_my() << 4; break; - - // LD r,SPhl: load register with stackpointer - case 0xfe4: m_a = m_sp >> 4 & 0xf; break; - case 0xfe5: m_b = m_sp >> 4 & 0xf; break; - case 0xfe6: write_mx(m_sp >> 4 & 0xf); break; - case 0xfe7: write_my(m_sp >> 4 & 0xf); break; - case 0xff4: m_a = m_sp & 0xf; break; - case 0xff5: m_b = m_sp & 0xf; break; - case 0xff6: write_mx(m_sp & 0xf); break; - case 0xff7: write_my(m_sp & 0xf); break; - - // ADD r,q: add register to register (flags: C, Z) - case 0xa80: m_a = op_add(m_a, m_a, D_FLAG); break; - case 0xa81: m_a = op_add(m_a, m_b, D_FLAG); break; - case 0xa82: m_a = op_add(m_a, read_mx(), D_FLAG); break; - case 0xa83: m_a = op_add(m_a, read_my(), D_FLAG); break; - case 0xa84: m_b = op_add(m_b, m_a, D_FLAG); break; - case 0xa85: m_b = op_add(m_b, m_b, D_FLAG); break; - case 0xa86: m_b = op_add(m_b, read_mx(), D_FLAG); break; - case 0xa87: m_b = op_add(m_b, read_my(), D_FLAG); break; - case 0xa88: write_mx(op_add(read_mx(), m_a, D_FLAG)); break; - case 0xa89: write_mx(op_add(read_mx(), m_b, D_FLAG)); break; - case 0xa8a: write_mx(op_add(read_mx(), read_mx(), D_FLAG)); break; - case 0xa8b: write_mx(op_add(read_mx(), read_my(), D_FLAG)); break; - case 0xa8c: write_my(op_add(read_my(), m_a, D_FLAG)); break; - case 0xa8d: write_my(op_add(read_my(), m_b, D_FLAG)); break; - case 0xa8e: write_my(op_add(read_my(), read_mx(), D_FLAG)); break; - case 0xa8f: write_my(op_add(read_my(), read_my(), D_FLAG)); break; - - // ADC r,q: add with carry register to register (flags: C, Z) - case 0xa90: m_a = op_adc(m_a, m_a, D_FLAG); break; - case 0xa91: m_a = op_adc(m_a, m_b, D_FLAG); break; - case 0xa92: m_a = op_adc(m_a, read_mx(), D_FLAG); break; - case 0xa93: m_a = op_adc(m_a, read_my(), D_FLAG); break; - case 0xa94: m_b = op_adc(m_b, m_a, D_FLAG); break; - case 0xa95: m_b = op_adc(m_b, m_b, D_FLAG); break; - case 0xa96: m_b = op_adc(m_b, read_mx(), D_FLAG); break; - case 0xa97: m_b = op_adc(m_b, read_my(), D_FLAG); break; - case 0xa98: write_mx(op_adc(read_mx(), m_a, D_FLAG)); break; - case 0xa99: write_mx(op_adc(read_mx(), m_b, D_FLAG)); break; - case 0xa9a: write_mx(op_adc(read_mx(), read_mx(), D_FLAG)); break; - case 0xa9b: write_mx(op_adc(read_mx(), read_my(), D_FLAG)); break; - case 0xa9c: write_my(op_adc(read_my(), m_a, D_FLAG)); break; - case 0xa9d: write_my(op_adc(read_my(), m_b, D_FLAG)); break; - case 0xa9e: write_my(op_adc(read_my(), read_mx(), D_FLAG)); break; - case 0xa9f: write_my(op_adc(read_my(), read_my(), D_FLAG)); break; - - // ACPX MX,r: ADC MX,r, then increment X (flags: C, Z) - case 0xf28: write_mx(op_adc(read_mx(), m_a, D_FLAG)); inc_x(); break; - case 0xf29: write_mx(op_adc(read_mx(), m_b, D_FLAG)); inc_x(); break; - case 0xf2a: write_mx(op_adc(read_mx(), read_mx(), D_FLAG)); inc_x(); break; - case 0xf2b: write_mx(op_adc(read_mx(), read_my(), D_FLAG)); inc_x(); break; - - // ACPY MY,r: ADC MY,r, then increment Y (flags: C, Z) - case 0xf2c: write_my(op_adc(read_my(), m_a, D_FLAG)); inc_y(); break; - case 0xf2d: write_my(op_adc(read_my(), m_b, D_FLAG)); inc_y(); break; - case 0xf2e: write_my(op_adc(read_my(), read_mx(), D_FLAG)); inc_y(); break; - case 0xf2f: write_my(op_adc(read_my(), read_my(), D_FLAG)); inc_y(); break; - - // SUB r,q: subtract register from register (flags: C, Z) - case 0xaa0: m_a = op_sub(m_a, m_a, D_FLAG); break; - case 0xaa1: m_a = op_sub(m_a, m_b, D_FLAG); break; - case 0xaa2: m_a = op_sub(m_a, read_mx(), D_FLAG); break; - case 0xaa3: m_a = op_sub(m_a, read_my(), D_FLAG); break; - case 0xaa4: m_b = op_sub(m_b, m_a, D_FLAG); break; - case 0xaa5: m_b = op_sub(m_b, m_b, D_FLAG); break; - case 0xaa6: m_b = op_sub(m_b, read_mx(), D_FLAG); break; - case 0xaa7: m_b = op_sub(m_b, read_my(), D_FLAG); break; - case 0xaa8: write_mx(op_sub(read_mx(), m_a, D_FLAG)); break; - case 0xaa9: write_mx(op_sub(read_mx(), m_b, D_FLAG)); break; - case 0xaaa: write_mx(op_sub(read_mx(), read_mx(), D_FLAG)); break; - case 0xaab: write_mx(op_sub(read_mx(), read_my(), D_FLAG)); break; - case 0xaac: write_my(op_sub(read_my(), m_a, D_FLAG)); break; - case 0xaad: write_my(op_sub(read_my(), m_b, D_FLAG)); break; - case 0xaae: write_my(op_sub(read_my(), read_mx(), D_FLAG)); break; - case 0xaaf: write_my(op_sub(read_my(), read_my(), D_FLAG)); break; - - // SBC r,q: subtract with carry register from register (flags: C, Z) - case 0xab0: m_a = op_sbc(m_a, m_a, D_FLAG); break; - case 0xab1: m_a = op_sbc(m_a, m_b, D_FLAG); break; - case 0xab2: m_a = op_sbc(m_a, read_mx(), D_FLAG); break; - case 0xab3: m_a = op_sbc(m_a, read_my(), D_FLAG); break; - case 0xab4: m_b = op_sbc(m_b, m_a, D_FLAG); break; - case 0xab5: m_b = op_sbc(m_b, m_b, D_FLAG); break; - case 0xab6: m_b = op_sbc(m_b, read_mx(), D_FLAG); break; - case 0xab7: m_b = op_sbc(m_b, read_my(), D_FLAG); break; - case 0xab8: write_mx(op_sbc(read_mx(), m_a, D_FLAG)); break; - case 0xab9: write_mx(op_sbc(read_mx(), m_b, D_FLAG)); break; - case 0xaba: write_mx(op_sbc(read_mx(), read_mx(), D_FLAG)); break; - case 0xabb: write_mx(op_sbc(read_mx(), read_my(), D_FLAG)); break; - case 0xabc: write_my(op_sbc(read_my(), m_a, D_FLAG)); break; - case 0xabd: write_my(op_sbc(read_my(), m_b, D_FLAG)); break; - case 0xabe: write_my(op_sbc(read_my(), read_mx(), D_FLAG)); break; - case 0xabf: write_my(op_sbc(read_my(), read_my(), D_FLAG)); break; - - // SCPX MX,r: SBC MX,r, then increment X (flags: C, Z) - case 0xf38: write_mx(op_sbc(read_mx(), m_a, D_FLAG)); inc_x(); break; - case 0xf39: write_mx(op_sbc(read_mx(), m_b, D_FLAG)); inc_x(); break; - case 0xf3a: write_mx(op_sbc(read_mx(), read_mx(), D_FLAG)); inc_x(); break; - case 0xf3b: write_mx(op_sbc(read_mx(), read_my(), D_FLAG)); inc_x(); break; - - // SCPY MY,r: SBC MY,r, then increment Y (flags: C, Z) - case 0xf3c: write_my(op_sbc(read_my(), m_a, D_FLAG)); inc_y(); break; - case 0xf3d: write_my(op_sbc(read_my(), m_b, D_FLAG)); inc_y(); break; - case 0xf3e: write_my(op_sbc(read_my(), read_mx(), D_FLAG)); inc_y(); break; - case 0xf3f: write_my(op_sbc(read_my(), read_my(), D_FLAG)); inc_y(); break; - - // CP r,q: compare: SUB r,q, but discard result (flags: C, Z, no D flag) - case 0xf00: op_sub(m_a, m_a); break; - case 0xf01: op_sub(m_a, m_b); break; - case 0xf02: op_sub(m_a, read_mx()); break; - case 0xf03: op_sub(m_a, read_my()); break; - case 0xf04: op_sub(m_b, m_a); break; - case 0xf05: op_sub(m_b, m_b); break; - case 0xf06: op_sub(m_b, read_mx()); break; - case 0xf07: op_sub(m_b, read_my()); break; - case 0xf08: op_sub(read_mx(), m_a); break; - case 0xf09: op_sub(read_mx(), m_b); break; - case 0xf0a: op_sub(read_mx(), read_mx()); break; - case 0xf0b: op_sub(read_mx(), read_my()); break; - case 0xf0c: op_sub(read_my(), m_a); break; - case 0xf0d: op_sub(read_my(), m_b); break; - case 0xf0e: op_sub(read_my(), read_mx()); break; - case 0xf0f: op_sub(read_my(), read_my()); break; - - // AND r,q: logical AND register with register (flags: Z) - case 0xac0: m_a = op_and(m_a, m_a); break; - case 0xac1: m_a = op_and(m_a, m_b); break; - case 0xac2: m_a = op_and(m_a, read_mx()); break; - case 0xac3: m_a = op_and(m_a, read_my()); break; - case 0xac4: m_b = op_and(m_b, m_a); break; - case 0xac5: m_b = op_and(m_b, m_b); break; - case 0xac6: m_b = op_and(m_b, read_mx()); break; - case 0xac7: m_b = op_and(m_b, read_my()); break; - case 0xac8: write_mx(op_and(read_mx(), m_a)); break; - case 0xac9: write_mx(op_and(read_mx(), m_b)); break; - case 0xaca: write_mx(op_and(read_mx(), read_mx())); break; - case 0xacb: write_mx(op_and(read_mx(), read_my())); break; - case 0xacc: write_my(op_and(read_my(), m_a)); break; - case 0xacd: write_my(op_and(read_my(), m_b)); break; - case 0xace: write_my(op_and(read_my(), read_mx())); break; - case 0xacf: write_my(op_and(read_my(), read_my())); break; - - // FAN r,q: flag-check: AND r,q, but discard result (flags: Z) - case 0xf10: op_and(m_a, m_a); break; - case 0xf11: op_and(m_a, m_b); break; - case 0xf12: op_and(m_a, read_mx()); break; - case 0xf13: op_and(m_a, read_my()); break; - case 0xf14: op_and(m_b, m_a); break; - case 0xf15: op_and(m_b, m_b); break; - case 0xf16: op_and(m_b, read_mx()); break; - case 0xf17: op_and(m_b, read_my()); break; - case 0xf18: op_and(read_mx(), m_a); break; - case 0xf19: op_and(read_mx(), m_b); break; - case 0xf1a: op_and(read_mx(), read_mx()); break; - case 0xf1b: op_and(read_mx(), read_my()); break; - case 0xf1c: op_and(read_my(), m_a); break; - case 0xf1d: op_and(read_my(), m_b); break; - case 0xf1e: op_and(read_my(), read_mx()); break; - case 0xf1f: op_and(read_my(), read_my()); break; - - // OR r,q: logical OR register with register (flags: Z) - case 0xad0: m_a = op_or(m_a, m_a); break; - case 0xad1: m_a = op_or(m_a, m_b); break; - case 0xad2: m_a = op_or(m_a, read_mx()); break; - case 0xad3: m_a = op_or(m_a, read_my()); break; - case 0xad4: m_b = op_or(m_b, m_a); break; - case 0xad5: m_b = op_or(m_b, m_b); break; - case 0xad6: m_b = op_or(m_b, read_mx()); break; - case 0xad7: m_b = op_or(m_b, read_my()); break; - case 0xad8: write_mx(op_or(read_mx(), m_a)); break; - case 0xad9: write_mx(op_or(read_mx(), m_b)); break; - case 0xada: write_mx(op_or(read_mx(), read_mx())); break; - case 0xadb: write_mx(op_or(read_mx(), read_my())); break; - case 0xadc: write_my(op_or(read_my(), m_a)); break; - case 0xadd: write_my(op_or(read_my(), m_b)); break; - case 0xade: write_my(op_or(read_my(), read_mx())); break; - case 0xadf: write_my(op_or(read_my(), read_my())); break; - - // XOR r,q: exclusive-OR register with register (flags: Z) - case 0xae0: m_a = op_xor(m_a, m_a); break; - case 0xae1: m_a = op_xor(m_a, m_b); break; - case 0xae2: m_a = op_xor(m_a, read_mx()); break; - case 0xae3: m_a = op_xor(m_a, read_my()); break; - case 0xae4: m_b = op_xor(m_b, m_a); break; - case 0xae5: m_b = op_xor(m_b, m_b); break; - case 0xae6: m_b = op_xor(m_b, read_mx()); break; - case 0xae7: m_b = op_xor(m_b, read_my()); break; - case 0xae8: write_mx(op_xor(read_mx(), m_a)); break; - case 0xae9: write_mx(op_xor(read_mx(), m_b)); break; - case 0xaea: write_mx(op_xor(read_mx(), read_mx())); break; - case 0xaeb: write_mx(op_xor(read_mx(), read_my())); break; - case 0xaec: write_my(op_xor(read_my(), m_a)); break; - case 0xaed: write_my(op_xor(read_my(), m_b)); break; - case 0xaee: write_my(op_xor(read_my(), read_mx())); break; - case 0xaef: write_my(op_xor(read_my(), read_my())); break; - - // RLC r(,r): rotate register left through carry (flags: C, Z) - case 0xaf0: m_a = op_rlc(m_a); break; - case 0xaf5: m_b = op_rlc(m_b); break; - case 0xafa: read_mx(); write_mx(op_rlc(read_mx())); break; - case 0xaff: read_my(); write_my(op_rlc(read_my())); break; - - // RRC r: rotate register right through carry (flags: C, Z) - case 0xe8c: m_a = op_rrc(m_a); break; - case 0xe8d: m_b = op_rrc(m_b); break; - case 0xe8e: write_mx(op_rrc(read_mx())); break; - case 0xe8f: write_my(op_rrc(read_my())); break; - - // INC SP: increment stackpointer - case 0xfdb: - m_sp++; - break; - - // DEC SP: decrement stackpointer - case 0xfcb: - m_sp--; - break; - - // PUSH r/Xphl/Yphl/F: push register to stack - case 0xfc0: push(m_a); break; - case 0xfc1: push(m_b); break; - case 0xfc2: push(read_mx()); break; - case 0xfc3: push(read_my()); break; - case 0xfc4: push(m_xp); break; - case 0xfc5: push(m_xh); break; - case 0xfc6: push(m_xl); break; - case 0xfc7: push(m_yp); break; - case 0xfc8: push(m_yh); break; - case 0xfc9: push(m_yl); break; - case 0xfca: push(m_f); break; - - // POP r/Xphl/Yphl/F: pop value from stack - case 0xfd0: m_a = pop(); break; - case 0xfd1: m_b = pop(); break; - case 0xfd2: write_mx(pop()); break; - case 0xfd3: write_my(pop()); break; - case 0xfd4: m_xp = pop(); break; - case 0xfd5: m_xh = pop(); break; - case 0xfd6: m_xl = pop(); break; - case 0xfd7: m_yp = pop(); break; - case 0xfd8: m_yh = pop(); break; - case 0xfd9: m_yl = pop(); break; - case 0xfda: m_f = pop(); m_possible_irq = true; break; - - // RETS: return from subroutine, then skip next instruction - case 0xfde: - m_icount -= 7; - pop_pc(); - m_pc = (m_pc & 0x1000) | ((m_pc + 1) & 0x0fff); - break; - - // RET: return from subroutine - case 0xfdf: - m_icount -= 2; - pop_pc(); - break; - - // JPBA: jump indirect using registers A and B - case 0xfe8: - m_pc = m_jpc | m_b << 4 | m_a; - break; - - // HALT: halt (stop cpu core clock) - case 0xff8: - m_halt = true; - break; - - // SLP: sleep (stop source oscillation) - case 0xff9: - m_sleep = true; - break; - - // NOP5: no operation (5 clock cycles) - case 0xffb: - break; - - // NOP7: no operation (7 clock cycles) - case 0xfff: - m_icount -= 2; - break; - - - default: - switch (m_op & 0xff0) - { - // LD r,i: load register with 4-bit immediate data - case 0xe00: m_a = m_op & 0xf; break; - case 0xe10: m_b = m_op & 0xf; break; - case 0xe20: write_mx(m_op & 0xf); break; - case 0xe30: write_my(m_op & 0xf); break; - - // LDPX MX,i: LD MX,i, then increment X - case 0xe60: - write_mx(m_op & 0xf); inc_x(); - break; - - // LDPY MY,i: LD MY,i, then increment Y - case 0xe70: - write_my(m_op & 0xf); inc_y(); - break; - - // LD A,Mn: load A with memory - case 0xfa0: - m_a = read_mn(); - break; - - // LD B,Mn: load B with memory - case 0xfb0: - m_b = read_mn(); - break; - - // LD Mn,A: load memory with A - case 0xf80: - write_mn(m_a); - break; - - // LD Mn,B: load memory with B - case 0xf90: - write_mn(m_b); - break; - - // INC Mn: increment memory (flags: C, Z) - case 0xf60: - write_mn(op_inc(read_mn())); - break; - - // DEC Mn: decrement memory (flags: C, Z) - case 0xf70: - write_mn(op_dec(read_mn())); - break; - - // ADD r,i: add 4-bit immediate data to register (flags: C, Z) - case 0xc00: m_a = op_add(m_a, m_op & 0xf, D_FLAG); break; - case 0xc10: m_b = op_add(m_b, m_op & 0xf, D_FLAG); break; - case 0xc20: write_mx(op_add(read_mx(), m_op & 0xf, D_FLAG)); break; - case 0xc30: write_my(op_add(read_my(), m_op & 0xf, D_FLAG)); break; - - // ADC r,i: add with carry 4-bit immediate data to register (flags: C, Z) - case 0xc40: m_a = op_adc(m_a, m_op & 0xf, D_FLAG); break; - case 0xc50: m_b = op_adc(m_b, m_op & 0xf, D_FLAG); break; - case 0xc60: write_mx(op_adc(read_mx(), m_op & 0xf, D_FLAG)); break; - case 0xc70: write_my(op_adc(read_my(), m_op & 0xf, D_FLAG)); break; - - // ADC Xhl/Yhl,i: add with carry 4-bit immediate data to X/Y (flags: C, Z, no D flag) - case 0xa00: m_xh = op_adc(m_xh, m_op & 0xf); break; - case 0xa10: m_xl = op_adc(m_xl, m_op & 0xf); break; - case 0xa20: m_yh = op_adc(m_yh, m_op & 0xf); break; - case 0xa30: m_yl = op_adc(m_yl, m_op & 0xf); break; - - // SBC r,i: subtract with carry 4-bit immediate data from register (flags: C, Z) - case 0xd40: m_a = op_sbc(m_a, m_op & 0xf, D_FLAG); break; - case 0xd50: m_b = op_sbc(m_b, m_op & 0xf, D_FLAG); break; - case 0xd60: write_mx(op_sbc(read_mx(), m_op & 0xf, D_FLAG)); break; - case 0xd70: write_my(op_sbc(read_my(), m_op & 0xf, D_FLAG)); break; - - // CP r,i: compare: SUB r,i, but discard result (flags: C, Z, no D flag) - case 0xdc0: op_sub(m_a, m_op & 0xf); break; - case 0xdd0: op_sub(m_b, m_op & 0xf); break; - case 0xde0: op_sub(read_mx(), m_op & 0xf); break; - case 0xdf0: op_sub(read_my(), m_op & 0xf); break; - - // CP XH,i: compare: SUB Xhl/Yhl,i, but discard result (flags: C, Z, no D flag) - case 0xa40: op_sub(m_xh, m_op & 0xf); break; - case 0xa50: op_sub(m_xl, m_op & 0xf); break; - case 0xa60: op_sub(m_yh, m_op & 0xf); break; - case 0xa70: op_sub(m_yl, m_op & 0xf); break; - - // AND r,i: logical AND register with 4-bit immediate data (flags: Z) - case 0xc80: m_a = op_and(m_a, m_op & 0xf); break; - case 0xc90: m_b = op_and(m_b, m_op & 0xf); break; - case 0xca0: write_mx(op_and(read_mx(), m_op & 0xf)); break; - case 0xcb0: write_my(op_and(read_my(), m_op & 0xf)); break; - - // FAN r,i: flag-check: AND r,i, but discard result (flags: Z) - case 0xd80: op_and(m_a, m_op & 0xf); break; - case 0xd90: op_and(m_b, m_op & 0xf); break; - case 0xda0: op_and(read_mx(), m_op & 0xf); break; - case 0xdb0: op_and(read_my(), m_op & 0xf); break; - - // OR r,i: logical OR register with 4-bit immediate data (flags: Z) - case 0xcc0: m_a = op_or(m_a, m_op & 0xf); break; - case 0xcd0: m_b = op_or(m_b, m_op & 0xf); break; - case 0xce0: write_mx(op_or(read_mx(), m_op & 0xf)); break; - case 0xcf0: write_my(op_or(read_my(), m_op & 0xf)); break; - - // XOR r,i: exclusive-OR register with 4-bit immediate data (flags: Z) - case 0xd00: m_a = op_xor(m_a, m_op & 0xf); break; - case 0xd10: m_b = op_xor(m_b, m_op & 0xf); break; - case 0xd20: write_mx(op_xor(read_mx(), m_op & 0xf)); break; - case 0xd30: write_my(op_xor(read_my(), m_op & 0xf)); break; - - // SET F,i: set flag(s), this includes opcodes SCF, SZF, SDF, EI - case 0xf40: - m_icount -= 2; - m_f |= (m_op & 0xf); - m_possible_irq = true; - break; - - // RST F,i: reset flag(s), this includes opcodes RCF, RZF, RDF, DI - case 0xf50: - m_icount -= 2; - m_f &= (m_op & 0xf); - break; - - // PSET p: page set, used to set page/bank before a jump instruction - case 0xe40: case 0xe50: - m_npc = m_op << 8 & 0x1f00; - break; - - - // illegal opcode - default: - logerror("%s unknown opcode $%03X at $%04X\n", tag(), m_op, m_prev_pc); - break; - - } // 0xff0 - break; - - } // 0xfff - break; - - } // 0xf00 (big switch) -} diff --git a/src/devices/cpu/e0c6200/e0c6200.cpp b/src/devices/cpu/e0c6200/e0c6200.cpp new file mode 100644 index 00000000000..e0b6a2eb8d2 --- /dev/null +++ b/src/devices/cpu/e0c6200/e0c6200.cpp @@ -0,0 +1,837 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Seiko Epson E0C6200 CPU core and E0C62 MCU family + + References: + - 1998 MF297-06a E0C6200/E0C6200A Core CPU Manual + - 1998 MF1049-01a E0C6S46 Technical Manual + + E0C6200 is a CPU core used as the basis of many chips, it is not standalone. + Seiko Epson often changed prefixes of their device names. Depending on when, + the E0C6200 is known as SMC6200, E0C6200, S1C6200. + + TODO: + - RLC is part of the r,q opcodes and requires that r == q, what happens otherwise? + - documentation is conflicting on whether or not the zero flag is set on RLC/RRC + +*/ + +#include "e0c6200.h" +#include "debugger.h" + +#include "e0c6200op.inc" + + +// disasm +void e0c6200_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) +{ + switch (entry.index()) + { + case STATE_GENFLAGS: + strprintf(str, "%c%c%c%c", + (m_f & I_FLAG) ? 'I':'i', + (m_f & D_FLAG) ? 'D':'d', + (m_f & Z_FLAG) ? 'Z':'z', + (m_f & C_FLAG) ? 'C':'c' + ); + break; + + default: break; + } +} + +offs_t e0c6200_cpu_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) +{ + extern CPU_DISASSEMBLE(e0c6200); + return CPU_DISASSEMBLE_NAME(e0c6200)(this, buffer, pc, oprom, opram, options); +} + + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +enum +{ + E0C6200_PC=1, E0C6200_A, E0C6200_B, + E0C6200_XP, E0C6200_XH, E0C6200_XL, + E0C6200_YP, E0C6200_YH, E0C6200_YL, + E0C6200_SP +}; + +void e0c6200_cpu_device::device_start() +{ + m_program = &space(AS_PROGRAM); + m_data = &space(AS_DATA); + + // zerofill + m_op = 0; + m_prev_op = 0; + m_irq_vector = 0; + m_irq_id = 0; + m_possible_irq = false; + m_halt = m_sleep = false; + m_pc = 0; + m_prev_pc = 0; + m_npc = 0; + m_jpc = 0; + + m_a = 0; + m_b = 0; + m_xp = m_xh = m_xl = 0; + m_yp = m_yh = m_yl = 0; + m_sp = 0; + m_f = 0; + + // register for savestates + save_item(NAME(m_op)); + save_item(NAME(m_prev_op)); + save_item(NAME(m_irq_vector)); + save_item(NAME(m_irq_id)); + save_item(NAME(m_possible_irq)); + save_item(NAME(m_halt)); + save_item(NAME(m_sleep)); + save_item(NAME(m_pc)); + save_item(NAME(m_prev_pc)); + save_item(NAME(m_npc)); + save_item(NAME(m_jpc)); + save_item(NAME(m_a)); + save_item(NAME(m_b)); + save_item(NAME(m_xp)); + save_item(NAME(m_xh)); + save_item(NAME(m_xl)); + save_item(NAME(m_yp)); + save_item(NAME(m_yh)); + save_item(NAME(m_yl)); + save_item(NAME(m_sp)); + save_item(NAME(m_f)); + + // register state for debugger + state_add(E0C6200_PC, "PC", m_pc).formatstr("%04X"); + state_add(E0C6200_A, "A", m_a).formatstr("%01X"); + state_add(E0C6200_B, "B", m_b).formatstr("%01X"); + state_add(E0C6200_XP, "XP", m_xp).formatstr("%01X"); + state_add(E0C6200_XH, "XH", m_xh).formatstr("%01X"); + state_add(E0C6200_XL, "XL", m_xl).formatstr("%01X"); + state_add(E0C6200_YP, "YP", m_yp).formatstr("%01X"); + state_add(E0C6200_YH, "YH", m_yh).formatstr("%01X"); + state_add(E0C6200_YL, "YL", m_yl).formatstr("%01X"); + state_add(E0C6200_SP, "SP", m_sp).formatstr("%02X"); + + state_add(STATE_GENPC, "curpc", m_pc).formatstr("%04X").noshow(); + state_add(STATE_GENFLAGS, "GENFLAGS", m_f).formatstr("%4s").noshow(); + + m_icountptr = &m_icount; +} + + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void e0c6200_cpu_device::device_reset() +{ + m_halt = m_sleep = false; + m_op = 0xfff; // nop + m_pc = 0x100; + m_f &= 3; // decimal flag is 0 on 6200A, undefined on 6200 +} + + + +//------------------------------------------------- +// execute loop +//------------------------------------------------- + +void e0c6200_cpu_device::do_interrupt() +{ + // interrupt handling takes 13* cycles, plus 1 extra if cpu was halted + // *: 12.5 on E0C6200A, does the cpu osc source change polarity or something? + m_icount -= 13; + if (m_halt) + m_icount--; + + m_halt = m_sleep = false; + push_pc(); + m_f &= ~I_FLAG; + + // page 1 of the current bank + m_pc = (m_pc & 0x1000) | 0x100 | m_irq_vector; + + standard_irq_callback(m_irq_id); +} + +void e0c6200_cpu_device::execute_run() +{ + while (m_icount > 0) + { + // check/handle interrupt, but not right after EI or in the middle of a longjump + if (m_possible_irq && (m_op & 0xfe0) != 0xe40 && (m_op & 0xff8) != 0xf48) + { + m_possible_irq = false; + if (m_f & I_FLAG && check_interrupt()) + { + do_interrupt(); + if (m_icount <= 0) + break; + } + } + + // core cpu not running (peripherals still work) + if (m_halt || m_sleep) + { + m_icount = 0; + break; + } + + // remember previous state, prepare pset-longjump + m_prev_op = m_op; + m_prev_pc = m_pc; + m_jpc = ((m_prev_op & 0xfe0) == 0xe40) ? m_npc : (m_prev_pc & 0x1f00); + + // fetch next opcode + debugger_instruction_hook(this, m_pc); + m_op = m_program->read_word(m_pc << 1) & 0xfff; + m_pc = (m_pc & 0x1000) | ((m_pc + 1) & 0x0fff); + + // minimal opcode time is 5 clock cycles, opcodes take 5, 7, or 12 clock cycles + m_icount -= 5; + + // handle opcode + execute_one(); + } +} + + + +//------------------------------------------------- +// execute one +//------------------------------------------------- + +void e0c6200_cpu_device::execute_one() +{ + // legend: + // X = --.XH.XL 8-bit + // Y = --.YH.YL 8-bit + // IX = XP.XH.XL 12-bit index register + // IY = YP.YH.YL 12-bit index register + // MX = data memory at IX + // MY = data memory at IY + // Mn = data memory at 0-F, via 4-bit immediate param + // r/q = 2-bit param directing to A/B/MX/MY + // i = 4-bit immediate param + // e = 8-bit immediate param + // s = 8-bit immediate branch destination + + switch (m_op & 0xf00) + { + // JP s: jump unconditional + case 0x000: + do_branch(); + break; + + // JP C,s: jump if carry + case 0x200: + do_branch(m_f & C_FLAG); + break; + + // JP NC,s: jump if no carry + case 0x300: + do_branch(~m_f & C_FLAG); + break; + + // JP Z,s: jump if zero + case 0x600: + do_branch(m_f & Z_FLAG); + break; + + // JP NZ,s: jump if not zero + case 0x700: + do_branch(~m_f & Z_FLAG); + break; + + // CALL s: call unconditional (on current bank) + case 0x400: + m_icount -= 2; + push_pc(); + m_pc = (m_pc & 0x1000) | (m_jpc & 0x0f00) | (m_op & 0xff); + break; + + // CALZ s: call zero page (on current bank) + case 0x500: + m_icount -= 2; + push_pc(); + m_pc = (m_pc & 0x1000) | (m_op & 0xff); + break; + + // RETD e: return from subroutine, then LBPX MX,e + case 0x100: + m_icount -= 7; + pop_pc(); + // fall through! + + // LBPX MX,e: load memory with 8-bit immediate data, increment X by 2 + case 0x900: + write_mx(m_op & 0xf); inc_x(); + write_mx(m_op >> 4 & 0xf); inc_x(); + break; + + // LD X,e: load X with 8-bit immediate data + case 0xb00: + m_xh = m_op >> 4 & 0xf; + m_xl = m_op & 0xf; + break; + + // LD Y,e: load Y with 8-bit immediate data + case 0x800: + m_yh = m_op >> 4 & 0xf; + m_yl = m_op & 0xf; + break; + + + default: + switch (m_op) + { + // LD r,q: load register with register + case 0xec0: /* m_a = m_a; */ break; + case 0xec1: m_a = m_b; break; + case 0xec2: m_a = read_mx(); break; + case 0xec3: m_a = read_my(); break; + case 0xec4: m_b = m_a; break; + case 0xec5: /* m_b = m_b; */ break; + case 0xec6: m_b = read_mx(); break; + case 0xec7: m_b = read_my(); break; + case 0xec8: write_mx(m_a); break; + case 0xec9: write_mx(m_b); break; + case 0xeca: write_mx(read_mx()); break; + case 0xecb: write_mx(read_my()); break; + case 0xecc: write_my(m_a); break; + case 0xecd: write_my(m_b); break; + case 0xece: write_my(read_mx()); break; + case 0xecf: write_my(read_my()); break; + + // LDPX r,q: LD r,q, then increment X + case 0xee0: /* m_a = m_a; */ inc_x(); break; + case 0xee1: m_a = m_b; inc_x(); break; + case 0xee2: m_a = read_mx(); inc_x(); break; + case 0xee3: m_a = read_my(); inc_x(); break; + case 0xee4: m_b = m_a; inc_x(); break; + case 0xee5: /* m_b = m_b; */ inc_x(); break; + case 0xee6: m_b = read_mx(); inc_x(); break; + case 0xee7: m_b = read_my(); inc_x(); break; + case 0xee8: write_mx(m_a); inc_x(); break; + case 0xee9: write_mx(m_b); inc_x(); break; + case 0xeea: write_mx(read_mx()); inc_x(); break; + case 0xeeb: write_mx(read_my()); inc_x(); break; + case 0xeec: write_my(m_a); inc_x(); break; + case 0xeed: write_my(m_b); inc_x(); break; + case 0xeee: write_my(read_mx()); inc_x(); break; + case 0xeef: write_my(read_my()); inc_x(); break; + + // LDPY r,q: LD r,q, then increment Y + case 0xef0: /* m_a = m_a; */ inc_y(); break; + case 0xef1: m_a = m_b; inc_y(); break; + case 0xef2: m_a = read_mx(); inc_y(); break; + case 0xef3: m_a = read_my(); inc_y(); break; + case 0xef4: m_b = m_a; inc_y(); break; + case 0xef5: /* m_b = m_b; */ inc_y(); break; + case 0xef6: m_b = read_mx(); inc_y(); break; + case 0xef7: m_b = read_my(); inc_y(); break; + case 0xef8: write_mx(m_a); inc_y(); break; + case 0xef9: write_mx(m_b); inc_y(); break; + case 0xefa: write_mx(read_mx()); inc_y(); break; + case 0xefb: write_mx(read_my()); inc_y(); break; + case 0xefc: write_my(m_a); inc_y(); break; + case 0xefd: write_my(m_b); inc_y(); break; + case 0xefe: write_my(read_mx()); inc_y(); break; + case 0xeff: write_my(read_my()); inc_y(); break; + + // LD Xphl/Yphl,r: load IX/IY with register + case 0xe80: m_xp = m_a; break; + case 0xe81: m_xp = m_b; break; + case 0xe82: m_xp = read_mx(); break; + case 0xe83: m_xp = read_my(); break; + case 0xe84: m_xh = m_a; break; + case 0xe85: m_xh = m_b; break; + case 0xe86: m_xh = read_mx(); break; + case 0xe87: m_xh = read_my(); break; + case 0xe88: m_xl = m_a; break; + case 0xe89: m_xl = m_b; break; + case 0xe8a: m_xl = read_mx(); break; + case 0xe8b: m_xl = read_my(); break; + case 0xe90: m_yp = m_a; break; + case 0xe91: m_yp = m_b; break; + case 0xe92: m_yp = read_mx(); break; + case 0xe93: m_yp = read_my(); break; + case 0xe94: m_yh = m_a; break; + case 0xe95: m_yh = m_b; break; + case 0xe96: m_yh = read_mx(); break; + case 0xe97: m_yh = read_my(); break; + case 0xe98: m_yl = m_a; break; + case 0xe99: m_yl = m_b; break; + case 0xe9a: m_yl = read_mx(); break; + case 0xe9b: m_yl = read_my(); break; + + // LD r,Xphl/Yphl: load register with IX/IY + case 0xea0: m_a = m_xp; break; + case 0xea1: m_b = m_xp; break; + case 0xea2: write_mx(m_xp); break; + case 0xea3: write_my(m_xp); break; + case 0xea4: m_a = m_xh; break; + case 0xea5: m_b = m_xh; break; + case 0xea6: write_mx(m_xh); break; + case 0xea7: write_my(m_xh); break; + case 0xea8: m_a = m_xl; break; + case 0xea9: m_b = m_xl; break; + case 0xeaa: write_mx(m_xl); break; + case 0xeab: write_my(m_xl); break; + case 0xeb0: m_a = m_yp; break; + case 0xeb1: m_b = m_yp; break; + case 0xeb2: write_mx(m_yp); break; + case 0xeb3: write_my(m_yp); break; + case 0xeb4: m_a = m_yh; break; + case 0xeb5: m_b = m_yh; break; + case 0xeb6: write_mx(m_yh); break; + case 0xeb7: write_my(m_yh); break; + case 0xeb8: m_a = m_yl; break; + case 0xeb9: m_b = m_yl; break; + case 0xeba: write_mx(m_yl); break; + case 0xebb: write_my(m_yl); break; + + // LD SPhl,r: load stackpointer with register + case 0xfe0: m_sp = (m_sp & 0xf0) | m_a; break; + case 0xfe1: m_sp = (m_sp & 0xf0) | m_b; break; + case 0xfe2: m_sp = (m_sp & 0xf0) | read_mx(); break; + case 0xfe3: m_sp = (m_sp & 0xf0) | read_my(); break; + case 0xff0: m_sp = (m_sp & 0x0f) | m_a << 4; break; + case 0xff1: m_sp = (m_sp & 0x0f) | m_b << 4; break; + case 0xff2: m_sp = (m_sp & 0x0f) | read_mx() << 4; break; + case 0xff3: m_sp = (m_sp & 0x0f) | read_my() << 4; break; + + // LD r,SPhl: load register with stackpointer + case 0xfe4: m_a = m_sp >> 4 & 0xf; break; + case 0xfe5: m_b = m_sp >> 4 & 0xf; break; + case 0xfe6: write_mx(m_sp >> 4 & 0xf); break; + case 0xfe7: write_my(m_sp >> 4 & 0xf); break; + case 0xff4: m_a = m_sp & 0xf; break; + case 0xff5: m_b = m_sp & 0xf; break; + case 0xff6: write_mx(m_sp & 0xf); break; + case 0xff7: write_my(m_sp & 0xf); break; + + // ADD r,q: add register to register (flags: C, Z) + case 0xa80: m_a = op_add(m_a, m_a, D_FLAG); break; + case 0xa81: m_a = op_add(m_a, m_b, D_FLAG); break; + case 0xa82: m_a = op_add(m_a, read_mx(), D_FLAG); break; + case 0xa83: m_a = op_add(m_a, read_my(), D_FLAG); break; + case 0xa84: m_b = op_add(m_b, m_a, D_FLAG); break; + case 0xa85: m_b = op_add(m_b, m_b, D_FLAG); break; + case 0xa86: m_b = op_add(m_b, read_mx(), D_FLAG); break; + case 0xa87: m_b = op_add(m_b, read_my(), D_FLAG); break; + case 0xa88: write_mx(op_add(read_mx(), m_a, D_FLAG)); break; + case 0xa89: write_mx(op_add(read_mx(), m_b, D_FLAG)); break; + case 0xa8a: write_mx(op_add(read_mx(), read_mx(), D_FLAG)); break; + case 0xa8b: write_mx(op_add(read_mx(), read_my(), D_FLAG)); break; + case 0xa8c: write_my(op_add(read_my(), m_a, D_FLAG)); break; + case 0xa8d: write_my(op_add(read_my(), m_b, D_FLAG)); break; + case 0xa8e: write_my(op_add(read_my(), read_mx(), D_FLAG)); break; + case 0xa8f: write_my(op_add(read_my(), read_my(), D_FLAG)); break; + + // ADC r,q: add with carry register to register (flags: C, Z) + case 0xa90: m_a = op_adc(m_a, m_a, D_FLAG); break; + case 0xa91: m_a = op_adc(m_a, m_b, D_FLAG); break; + case 0xa92: m_a = op_adc(m_a, read_mx(), D_FLAG); break; + case 0xa93: m_a = op_adc(m_a, read_my(), D_FLAG); break; + case 0xa94: m_b = op_adc(m_b, m_a, D_FLAG); break; + case 0xa95: m_b = op_adc(m_b, m_b, D_FLAG); break; + case 0xa96: m_b = op_adc(m_b, read_mx(), D_FLAG); break; + case 0xa97: m_b = op_adc(m_b, read_my(), D_FLAG); break; + case 0xa98: write_mx(op_adc(read_mx(), m_a, D_FLAG)); break; + case 0xa99: write_mx(op_adc(read_mx(), m_b, D_FLAG)); break; + case 0xa9a: write_mx(op_adc(read_mx(), read_mx(), D_FLAG)); break; + case 0xa9b: write_mx(op_adc(read_mx(), read_my(), D_FLAG)); break; + case 0xa9c: write_my(op_adc(read_my(), m_a, D_FLAG)); break; + case 0xa9d: write_my(op_adc(read_my(), m_b, D_FLAG)); break; + case 0xa9e: write_my(op_adc(read_my(), read_mx(), D_FLAG)); break; + case 0xa9f: write_my(op_adc(read_my(), read_my(), D_FLAG)); break; + + // ACPX MX,r: ADC MX,r, then increment X (flags: C, Z) + case 0xf28: write_mx(op_adc(read_mx(), m_a, D_FLAG)); inc_x(); break; + case 0xf29: write_mx(op_adc(read_mx(), m_b, D_FLAG)); inc_x(); break; + case 0xf2a: write_mx(op_adc(read_mx(), read_mx(), D_FLAG)); inc_x(); break; + case 0xf2b: write_mx(op_adc(read_mx(), read_my(), D_FLAG)); inc_x(); break; + + // ACPY MY,r: ADC MY,r, then increment Y (flags: C, Z) + case 0xf2c: write_my(op_adc(read_my(), m_a, D_FLAG)); inc_y(); break; + case 0xf2d: write_my(op_adc(read_my(), m_b, D_FLAG)); inc_y(); break; + case 0xf2e: write_my(op_adc(read_my(), read_mx(), D_FLAG)); inc_y(); break; + case 0xf2f: write_my(op_adc(read_my(), read_my(), D_FLAG)); inc_y(); break; + + // SUB r,q: subtract register from register (flags: C, Z) + case 0xaa0: m_a = op_sub(m_a, m_a, D_FLAG); break; + case 0xaa1: m_a = op_sub(m_a, m_b, D_FLAG); break; + case 0xaa2: m_a = op_sub(m_a, read_mx(), D_FLAG); break; + case 0xaa3: m_a = op_sub(m_a, read_my(), D_FLAG); break; + case 0xaa4: m_b = op_sub(m_b, m_a, D_FLAG); break; + case 0xaa5: m_b = op_sub(m_b, m_b, D_FLAG); break; + case 0xaa6: m_b = op_sub(m_b, read_mx(), D_FLAG); break; + case 0xaa7: m_b = op_sub(m_b, read_my(), D_FLAG); break; + case 0xaa8: write_mx(op_sub(read_mx(), m_a, D_FLAG)); break; + case 0xaa9: write_mx(op_sub(read_mx(), m_b, D_FLAG)); break; + case 0xaaa: write_mx(op_sub(read_mx(), read_mx(), D_FLAG)); break; + case 0xaab: write_mx(op_sub(read_mx(), read_my(), D_FLAG)); break; + case 0xaac: write_my(op_sub(read_my(), m_a, D_FLAG)); break; + case 0xaad: write_my(op_sub(read_my(), m_b, D_FLAG)); break; + case 0xaae: write_my(op_sub(read_my(), read_mx(), D_FLAG)); break; + case 0xaaf: write_my(op_sub(read_my(), read_my(), D_FLAG)); break; + + // SBC r,q: subtract with carry register from register (flags: C, Z) + case 0xab0: m_a = op_sbc(m_a, m_a, D_FLAG); break; + case 0xab1: m_a = op_sbc(m_a, m_b, D_FLAG); break; + case 0xab2: m_a = op_sbc(m_a, read_mx(), D_FLAG); break; + case 0xab3: m_a = op_sbc(m_a, read_my(), D_FLAG); break; + case 0xab4: m_b = op_sbc(m_b, m_a, D_FLAG); break; + case 0xab5: m_b = op_sbc(m_b, m_b, D_FLAG); break; + case 0xab6: m_b = op_sbc(m_b, read_mx(), D_FLAG); break; + case 0xab7: m_b = op_sbc(m_b, read_my(), D_FLAG); break; + case 0xab8: write_mx(op_sbc(read_mx(), m_a, D_FLAG)); break; + case 0xab9: write_mx(op_sbc(read_mx(), m_b, D_FLAG)); break; + case 0xaba: write_mx(op_sbc(read_mx(), read_mx(), D_FLAG)); break; + case 0xabb: write_mx(op_sbc(read_mx(), read_my(), D_FLAG)); break; + case 0xabc: write_my(op_sbc(read_my(), m_a, D_FLAG)); break; + case 0xabd: write_my(op_sbc(read_my(), m_b, D_FLAG)); break; + case 0xabe: write_my(op_sbc(read_my(), read_mx(), D_FLAG)); break; + case 0xabf: write_my(op_sbc(read_my(), read_my(), D_FLAG)); break; + + // SCPX MX,r: SBC MX,r, then increment X (flags: C, Z) + case 0xf38: write_mx(op_sbc(read_mx(), m_a, D_FLAG)); inc_x(); break; + case 0xf39: write_mx(op_sbc(read_mx(), m_b, D_FLAG)); inc_x(); break; + case 0xf3a: write_mx(op_sbc(read_mx(), read_mx(), D_FLAG)); inc_x(); break; + case 0xf3b: write_mx(op_sbc(read_mx(), read_my(), D_FLAG)); inc_x(); break; + + // SCPY MY,r: SBC MY,r, then increment Y (flags: C, Z) + case 0xf3c: write_my(op_sbc(read_my(), m_a, D_FLAG)); inc_y(); break; + case 0xf3d: write_my(op_sbc(read_my(), m_b, D_FLAG)); inc_y(); break; + case 0xf3e: write_my(op_sbc(read_my(), read_mx(), D_FLAG)); inc_y(); break; + case 0xf3f: write_my(op_sbc(read_my(), read_my(), D_FLAG)); inc_y(); break; + + // CP r,q: compare: SUB r,q, but discard result (flags: C, Z, no D flag) + case 0xf00: op_sub(m_a, m_a); break; + case 0xf01: op_sub(m_a, m_b); break; + case 0xf02: op_sub(m_a, read_mx()); break; + case 0xf03: op_sub(m_a, read_my()); break; + case 0xf04: op_sub(m_b, m_a); break; + case 0xf05: op_sub(m_b, m_b); break; + case 0xf06: op_sub(m_b, read_mx()); break; + case 0xf07: op_sub(m_b, read_my()); break; + case 0xf08: op_sub(read_mx(), m_a); break; + case 0xf09: op_sub(read_mx(), m_b); break; + case 0xf0a: op_sub(read_mx(), read_mx()); break; + case 0xf0b: op_sub(read_mx(), read_my()); break; + case 0xf0c: op_sub(read_my(), m_a); break; + case 0xf0d: op_sub(read_my(), m_b); break; + case 0xf0e: op_sub(read_my(), read_mx()); break; + case 0xf0f: op_sub(read_my(), read_my()); break; + + // AND r,q: logical AND register with register (flags: Z) + case 0xac0: m_a = op_and(m_a, m_a); break; + case 0xac1: m_a = op_and(m_a, m_b); break; + case 0xac2: m_a = op_and(m_a, read_mx()); break; + case 0xac3: m_a = op_and(m_a, read_my()); break; + case 0xac4: m_b = op_and(m_b, m_a); break; + case 0xac5: m_b = op_and(m_b, m_b); break; + case 0xac6: m_b = op_and(m_b, read_mx()); break; + case 0xac7: m_b = op_and(m_b, read_my()); break; + case 0xac8: write_mx(op_and(read_mx(), m_a)); break; + case 0xac9: write_mx(op_and(read_mx(), m_b)); break; + case 0xaca: write_mx(op_and(read_mx(), read_mx())); break; + case 0xacb: write_mx(op_and(read_mx(), read_my())); break; + case 0xacc: write_my(op_and(read_my(), m_a)); break; + case 0xacd: write_my(op_and(read_my(), m_b)); break; + case 0xace: write_my(op_and(read_my(), read_mx())); break; + case 0xacf: write_my(op_and(read_my(), read_my())); break; + + // FAN r,q: flag-check: AND r,q, but discard result (flags: Z) + case 0xf10: op_and(m_a, m_a); break; + case 0xf11: op_and(m_a, m_b); break; + case 0xf12: op_and(m_a, read_mx()); break; + case 0xf13: op_and(m_a, read_my()); break; + case 0xf14: op_and(m_b, m_a); break; + case 0xf15: op_and(m_b, m_b); break; + case 0xf16: op_and(m_b, read_mx()); break; + case 0xf17: op_and(m_b, read_my()); break; + case 0xf18: op_and(read_mx(), m_a); break; + case 0xf19: op_and(read_mx(), m_b); break; + case 0xf1a: op_and(read_mx(), read_mx()); break; + case 0xf1b: op_and(read_mx(), read_my()); break; + case 0xf1c: op_and(read_my(), m_a); break; + case 0xf1d: op_and(read_my(), m_b); break; + case 0xf1e: op_and(read_my(), read_mx()); break; + case 0xf1f: op_and(read_my(), read_my()); break; + + // OR r,q: logical OR register with register (flags: Z) + case 0xad0: m_a = op_or(m_a, m_a); break; + case 0xad1: m_a = op_or(m_a, m_b); break; + case 0xad2: m_a = op_or(m_a, read_mx()); break; + case 0xad3: m_a = op_or(m_a, read_my()); break; + case 0xad4: m_b = op_or(m_b, m_a); break; + case 0xad5: m_b = op_or(m_b, m_b); break; + case 0xad6: m_b = op_or(m_b, read_mx()); break; + case 0xad7: m_b = op_or(m_b, read_my()); break; + case 0xad8: write_mx(op_or(read_mx(), m_a)); break; + case 0xad9: write_mx(op_or(read_mx(), m_b)); break; + case 0xada: write_mx(op_or(read_mx(), read_mx())); break; + case 0xadb: write_mx(op_or(read_mx(), read_my())); break; + case 0xadc: write_my(op_or(read_my(), m_a)); break; + case 0xadd: write_my(op_or(read_my(), m_b)); break; + case 0xade: write_my(op_or(read_my(), read_mx())); break; + case 0xadf: write_my(op_or(read_my(), read_my())); break; + + // XOR r,q: exclusive-OR register with register (flags: Z) + case 0xae0: m_a = op_xor(m_a, m_a); break; + case 0xae1: m_a = op_xor(m_a, m_b); break; + case 0xae2: m_a = op_xor(m_a, read_mx()); break; + case 0xae3: m_a = op_xor(m_a, read_my()); break; + case 0xae4: m_b = op_xor(m_b, m_a); break; + case 0xae5: m_b = op_xor(m_b, m_b); break; + case 0xae6: m_b = op_xor(m_b, read_mx()); break; + case 0xae7: m_b = op_xor(m_b, read_my()); break; + case 0xae8: write_mx(op_xor(read_mx(), m_a)); break; + case 0xae9: write_mx(op_xor(read_mx(), m_b)); break; + case 0xaea: write_mx(op_xor(read_mx(), read_mx())); break; + case 0xaeb: write_mx(op_xor(read_mx(), read_my())); break; + case 0xaec: write_my(op_xor(read_my(), m_a)); break; + case 0xaed: write_my(op_xor(read_my(), m_b)); break; + case 0xaee: write_my(op_xor(read_my(), read_mx())); break; + case 0xaef: write_my(op_xor(read_my(), read_my())); break; + + // RLC r(,r): rotate register left through carry (flags: C, Z) + case 0xaf0: m_a = op_rlc(m_a); break; + case 0xaf5: m_b = op_rlc(m_b); break; + case 0xafa: read_mx(); write_mx(op_rlc(read_mx())); break; + case 0xaff: read_my(); write_my(op_rlc(read_my())); break; + + // RRC r: rotate register right through carry (flags: C, Z) + case 0xe8c: m_a = op_rrc(m_a); break; + case 0xe8d: m_b = op_rrc(m_b); break; + case 0xe8e: write_mx(op_rrc(read_mx())); break; + case 0xe8f: write_my(op_rrc(read_my())); break; + + // INC SP: increment stackpointer + case 0xfdb: + m_sp++; + break; + + // DEC SP: decrement stackpointer + case 0xfcb: + m_sp--; + break; + + // PUSH r/Xphl/Yphl/F: push register to stack + case 0xfc0: push(m_a); break; + case 0xfc1: push(m_b); break; + case 0xfc2: push(read_mx()); break; + case 0xfc3: push(read_my()); break; + case 0xfc4: push(m_xp); break; + case 0xfc5: push(m_xh); break; + case 0xfc6: push(m_xl); break; + case 0xfc7: push(m_yp); break; + case 0xfc8: push(m_yh); break; + case 0xfc9: push(m_yl); break; + case 0xfca: push(m_f); break; + + // POP r/Xphl/Yphl/F: pop value from stack + case 0xfd0: m_a = pop(); break; + case 0xfd1: m_b = pop(); break; + case 0xfd2: write_mx(pop()); break; + case 0xfd3: write_my(pop()); break; + case 0xfd4: m_xp = pop(); break; + case 0xfd5: m_xh = pop(); break; + case 0xfd6: m_xl = pop(); break; + case 0xfd7: m_yp = pop(); break; + case 0xfd8: m_yh = pop(); break; + case 0xfd9: m_yl = pop(); break; + case 0xfda: m_f = pop(); m_possible_irq = true; break; + + // RETS: return from subroutine, then skip next instruction + case 0xfde: + m_icount -= 7; + pop_pc(); + m_pc = (m_pc & 0x1000) | ((m_pc + 1) & 0x0fff); + break; + + // RET: return from subroutine + case 0xfdf: + m_icount -= 2; + pop_pc(); + break; + + // JPBA: jump indirect using registers A and B + case 0xfe8: + m_pc = m_jpc | m_b << 4 | m_a; + break; + + // HALT: halt (stop cpu core clock) + case 0xff8: + m_halt = true; + break; + + // SLP: sleep (stop source oscillation) + case 0xff9: + m_sleep = true; + break; + + // NOP5: no operation (5 clock cycles) + case 0xffb: + break; + + // NOP7: no operation (7 clock cycles) + case 0xfff: + m_icount -= 2; + break; + + + default: + switch (m_op & 0xff0) + { + // LD r,i: load register with 4-bit immediate data + case 0xe00: m_a = m_op & 0xf; break; + case 0xe10: m_b = m_op & 0xf; break; + case 0xe20: write_mx(m_op & 0xf); break; + case 0xe30: write_my(m_op & 0xf); break; + + // LDPX MX,i: LD MX,i, then increment X + case 0xe60: + write_mx(m_op & 0xf); inc_x(); + break; + + // LDPY MY,i: LD MY,i, then increment Y + case 0xe70: + write_my(m_op & 0xf); inc_y(); + break; + + // LD A,Mn: load A with memory + case 0xfa0: + m_a = read_mn(); + break; + + // LD B,Mn: load B with memory + case 0xfb0: + m_b = read_mn(); + break; + + // LD Mn,A: load memory with A + case 0xf80: + write_mn(m_a); + break; + + // LD Mn,B: load memory with B + case 0xf90: + write_mn(m_b); + break; + + // INC Mn: increment memory (flags: C, Z) + case 0xf60: + write_mn(op_inc(read_mn())); + break; + + // DEC Mn: decrement memory (flags: C, Z) + case 0xf70: + write_mn(op_dec(read_mn())); + break; + + // ADD r,i: add 4-bit immediate data to register (flags: C, Z) + case 0xc00: m_a = op_add(m_a, m_op & 0xf, D_FLAG); break; + case 0xc10: m_b = op_add(m_b, m_op & 0xf, D_FLAG); break; + case 0xc20: write_mx(op_add(read_mx(), m_op & 0xf, D_FLAG)); break; + case 0xc30: write_my(op_add(read_my(), m_op & 0xf, D_FLAG)); break; + + // ADC r,i: add with carry 4-bit immediate data to register (flags: C, Z) + case 0xc40: m_a = op_adc(m_a, m_op & 0xf, D_FLAG); break; + case 0xc50: m_b = op_adc(m_b, m_op & 0xf, D_FLAG); break; + case 0xc60: write_mx(op_adc(read_mx(), m_op & 0xf, D_FLAG)); break; + case 0xc70: write_my(op_adc(read_my(), m_op & 0xf, D_FLAG)); break; + + // ADC Xhl/Yhl,i: add with carry 4-bit immediate data to X/Y (flags: C, Z, no D flag) + case 0xa00: m_xh = op_adc(m_xh, m_op & 0xf); break; + case 0xa10: m_xl = op_adc(m_xl, m_op & 0xf); break; + case 0xa20: m_yh = op_adc(m_yh, m_op & 0xf); break; + case 0xa30: m_yl = op_adc(m_yl, m_op & 0xf); break; + + // SBC r,i: subtract with carry 4-bit immediate data from register (flags: C, Z) + case 0xd40: m_a = op_sbc(m_a, m_op & 0xf, D_FLAG); break; + case 0xd50: m_b = op_sbc(m_b, m_op & 0xf, D_FLAG); break; + case 0xd60: write_mx(op_sbc(read_mx(), m_op & 0xf, D_FLAG)); break; + case 0xd70: write_my(op_sbc(read_my(), m_op & 0xf, D_FLAG)); break; + + // CP r,i: compare: SUB r,i, but discard result (flags: C, Z, no D flag) + case 0xdc0: op_sub(m_a, m_op & 0xf); break; + case 0xdd0: op_sub(m_b, m_op & 0xf); break; + case 0xde0: op_sub(read_mx(), m_op & 0xf); break; + case 0xdf0: op_sub(read_my(), m_op & 0xf); break; + + // CP XH,i: compare: SUB Xhl/Yhl,i, but discard result (flags: C, Z, no D flag) + case 0xa40: op_sub(m_xh, m_op & 0xf); break; + case 0xa50: op_sub(m_xl, m_op & 0xf); break; + case 0xa60: op_sub(m_yh, m_op & 0xf); break; + case 0xa70: op_sub(m_yl, m_op & 0xf); break; + + // AND r,i: logical AND register with 4-bit immediate data (flags: Z) + case 0xc80: m_a = op_and(m_a, m_op & 0xf); break; + case 0xc90: m_b = op_and(m_b, m_op & 0xf); break; + case 0xca0: write_mx(op_and(read_mx(), m_op & 0xf)); break; + case 0xcb0: write_my(op_and(read_my(), m_op & 0xf)); break; + + // FAN r,i: flag-check: AND r,i, but discard result (flags: Z) + case 0xd80: op_and(m_a, m_op & 0xf); break; + case 0xd90: op_and(m_b, m_op & 0xf); break; + case 0xda0: op_and(read_mx(), m_op & 0xf); break; + case 0xdb0: op_and(read_my(), m_op & 0xf); break; + + // OR r,i: logical OR register with 4-bit immediate data (flags: Z) + case 0xcc0: m_a = op_or(m_a, m_op & 0xf); break; + case 0xcd0: m_b = op_or(m_b, m_op & 0xf); break; + case 0xce0: write_mx(op_or(read_mx(), m_op & 0xf)); break; + case 0xcf0: write_my(op_or(read_my(), m_op & 0xf)); break; + + // XOR r,i: exclusive-OR register with 4-bit immediate data (flags: Z) + case 0xd00: m_a = op_xor(m_a, m_op & 0xf); break; + case 0xd10: m_b = op_xor(m_b, m_op & 0xf); break; + case 0xd20: write_mx(op_xor(read_mx(), m_op & 0xf)); break; + case 0xd30: write_my(op_xor(read_my(), m_op & 0xf)); break; + + // SET F,i: set flag(s), this includes opcodes SCF, SZF, SDF, EI + case 0xf40: + m_icount -= 2; + m_f |= (m_op & 0xf); + m_possible_irq = true; + break; + + // RST F,i: reset flag(s), this includes opcodes RCF, RZF, RDF, DI + case 0xf50: + m_icount -= 2; + m_f &= (m_op & 0xf); + break; + + // PSET p: page set, used to set page/bank before a jump instruction + case 0xe40: case 0xe50: + m_npc = m_op << 8 & 0x1f00; + break; + + + // illegal opcode + default: + logerror("%s unknown opcode $%03X at $%04X\n", tag(), m_op, m_prev_pc); + break; + + } // 0xff0 + break; + + } // 0xfff + break; + + } // 0xf00 (big switch) +} diff --git a/src/devices/cpu/e0c6200/e0c6200d.c b/src/devices/cpu/e0c6200/e0c6200d.c deleted file mode 100644 index 4fc0599ac24..00000000000 --- a/src/devices/cpu/e0c6200/e0c6200d.c +++ /dev/null @@ -1,708 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:hap -/* - - Seiko Epson E0C6200 disassembler - -*/ - -#include "emu.h" -#include "debugger.h" -#include "e0c6200.h" - -// opcode mnemonics -enum e_mnemonics -{ - em_JP, em_RETD, em_CALL, em_CALZ, - em_LD, em_LBPX, em_ADC, em_CP, em_ADD, em_SUB, em_SBC, em_AND, em_OR, em_XOR, - em_RLC, em_FAN, em_PSET, em_LDPX, em_LDPY, em_SET, em_RST, em_INC, em_DEC, - em_RRC, em_ACPX, em_ACPY, em_SCPX, em_SCPY, em_PUSH, em_POP, - em_RETS, em_RET, em_JPBA, em_HALT, em_SLP, em_NOP5, em_NOP7, - em_NOT, em_SCF, em_SZF, em_SDF, em_EI, em_DI, em_RDF, em_RZF, em_RCF, em_ILL -}; - -static const char *const em_name[] = -{ - "JP", "RETD", "CALL", "CALZ", - "LD", "LBPX", "ADC", "CP", "ADD", "SUB", "SBC", "AND", "OR", "XOR", - "RLC", "FAN", "PSET", "LDPX", "LDPY", "SET", "RST", "INC", "DEC", - "RRC", "ACPX", "ACPY", "SCPX", "SCPY", "PUSH", "POP", - "RETS", "RET", "JPBA", "HALT", "SLP", "NOP5", "NOP7", - "NOT", "SCF", "SZF", "SDF", "EI", "DI", "RDF", "RZF", "RCF", "?" -}; - -#define _OVER DASMFLAG_STEP_OVER -#define _OUT DASMFLAG_STEP_OUT - -static const UINT32 em_flags[] = -{ - 0, _OUT, _OVER, _OVER, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - _OUT, _OUT, 0, _OVER, _OVER, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - - -// opcode params -enum e_params -{ - ep_S, ep_E, ep_I, ep_R0, ep_R2, ep_R4, ep_Q, - ep_cC, ep_cNC, ep_cZ, ep_cNZ, - ep_A, ep_B, ep_X, ep_Y, ep_MX, ep_MY, ep_XP, ep_XH, ep_XL, ep_YP, ep_YH, ep_YL, - ep_P, ep_F, ep_MN, ep_SP, ep_SPH, ep_SPL -}; - -// 0-digit is number of bits per opcode parameter, 0 bits is literal, -// 0x10-digit is for shift-right, 0x100-digit is special flag for r/q param -static const UINT16 ep_bits[] = -{ - 8, 8, 4, 0x102, 0x122, 0x142, 0x102, - 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 4, 0, 0, 0 -}; - -// redirect for r/q param -static const UINT8 ep_redirect_r[4] = { ep_A, ep_B, ep_MX, ep_MY }; - -// literal opcode parameter -static const char *const ep_name[] = -{ - " ", " ", " ", " ", " ", " ", " ", - "C", "NC", "Z", "NZ", - "A", "B", "X", "Y", "MX", "MY", "XP", "XH", "XL", "YP", "YH", "YL", - " ", "F", " ", "SP", "SPH", "SPL" -}; - - -static char* decode_param(UINT16 opcode, int param, char* buffer) -{ - int bits = ep_bits[param] & 0xf; - int shift = ep_bits[param] >> 4 & 0xf; - UINT16 opmask = opcode >> shift & ((1 << bits) - 1); - - // redirect r/q to A/B/MX/MY - if (ep_bits[param] & 0x100) - param = ep_redirect_r[opmask]; - - // literal param - if (ep_bits[param] == 0) - { - strcpy(buffer, ep_name[param]); - return buffer; - } - - // print value like how it's documented in the manual - char val[10]; - if (bits > 4 || opmask > 9) - sprintf(val, "%02XH", opmask); - else - sprintf(val, "%d", opmask); - - if (param == ep_MN) - sprintf(buffer, "M%s", val); - else - strcpy(buffer, val); - - return buffer; -} - - -CPU_DISASSEMBLE(e0c6200) -{ - UINT16 op = (oprom[1] | oprom[0] << 8) & 0xfff; - - int m = -1; - int p1 = -1; - int p2 = -1; - - // determine id for mnemonic and param(s) - switch (op & 0xf00) - { - // JP s - case 0x000: - m = em_JP; p1 = ep_S; - break; - - // RETD e - case 0x100: - m = em_RETD; p1 = ep_E; - break; - - // JP C,s - case 0x200: - m = em_JP; p1 = ep_cC; p2 = ep_S; - break; - - // JP NC,s - case 0x300: - m = em_JP; p1 = ep_cNC; p2 = ep_S; - break; - - // CALL s - case 0x400: - m = em_CALL; p1 = ep_S; - break; - - // CALZ s - case 0x500: - m = em_CALZ; p1 = ep_S; - break; - - // JP Z,s - case 0x600: - m = em_JP; p1 = ep_cZ; p2 = ep_S; - break; - - // JP NZ,s - case 0x700: - m = em_JP; p1 = ep_cNZ; p2 = ep_S; - break; - - // LD Y,e - case 0x800: - m = em_LD; p1 = ep_Y; p2 = ep_E; - break; - - // LBPX MX,e - case 0x900: - m = em_LBPX; p1 = ep_MX; p2 = ep_E; - break; - - // LD X,e - case 0xb00: - m = em_LD; p1 = ep_X; p2 = ep_E; - break; - - - default: - switch (op) - { - // RLC r - case 0xaf0: case 0xaf5: case 0xafa: case 0xaff: - m = em_RLC; p1 = ep_R0; - break; - - // NOT r - case 0xd0f: case 0xd1f: case 0xd2f: case 0xd3f: - m = em_NOT; p1 = ep_R4; - break; - - // LD XP,r - case 0xe80: case 0xe81: case 0xe82: case 0xe83: - m = em_LD; p1 = ep_XP; p2 = ep_R0; - break; - - // LD XH,r - case 0xe84: case 0xe85: case 0xe86: case 0xe87: - m = em_LD; p1 = ep_XH; p2 = ep_R0; - break; - - // LD XL,r - case 0xe88: case 0xe89: case 0xe8a: case 0xe8b: - m = em_LD; p1 = ep_XL; p2 = ep_R0; - break; - - // RRC r - case 0xe8c: case 0xe8d: case 0xe8e: case 0xe8f: - m = em_RRC; p1 = ep_R0; - break; - - // LD YP,r - case 0xe90: case 0xe91: case 0xe92: case 0xe93: - m = em_LD; p1 = ep_YP; p2 = ep_R0; - break; - - // LD YH,r - case 0xe94: case 0xe95: case 0xe96: case 0xe97: - m = em_LD; p1 = ep_YH; p2 = ep_R0; - break; - - // LD YL,r - case 0xe98: case 0xe99: case 0xe9a: case 0xe9b: - m = em_LD; p1 = ep_YL; p2 = ep_R0; - break; - - // LD r,XP - case 0xea0: case 0xea1: case 0xea2: case 0xea3: - m = em_LD; p1 = ep_R0; p2 = ep_XP; - break; - - // LD r,XH - case 0xea4: case 0xea5: case 0xea6: case 0xea7: - m = em_LD; p1 = ep_R0; p2 = ep_XH; - break; - - // LD r,XL - case 0xea8: case 0xea9: case 0xeaa: case 0xeab: - m = em_LD; p1 = ep_R0; p2 = ep_XL; - break; - - // LD r,YP - case 0xeb0: case 0xeb1: case 0xeb2: case 0xeb3: - m = em_LD; p1 = ep_R0; p2 = ep_YP; - break; - - // LD r,YH - case 0xeb4: case 0xeb5: case 0xeb6: case 0xeb7: - m = em_LD; p1 = ep_R0; p2 = ep_YH; - break; - - // LD r,YL - case 0xeb8: case 0xeb9: case 0xeba: case 0xebb: - m = em_LD; p1 = ep_R0; p2 = ep_YL; - break; - - // INC X - case 0xee0: - m = em_INC; p1 = ep_X; - break; - - // INC Y - case 0xef0: - m = em_INC; p1 = ep_Y; - break; - - // ACPX MX,r - case 0xf28: case 0xf29: case 0xf2a: case 0xf2b: - m = em_ACPX; p1 = ep_MX; p2 = ep_R0; - break; - - // ACPY MY,r - case 0xf2c: case 0xf2d: case 0xf2e: case 0xf2f: - m = em_ACPY; p1 = ep_MY; p2 = ep_R0; - break; - - // SCPX MX,r - case 0xf38: case 0xf39: case 0xf3a: case 0xf3b: - m = em_SCPX; p1 = ep_MX; p2 = ep_R0; - break; - - // SCPY MY,r - case 0xf3c: case 0xf3d: case 0xf3e: case 0xf3f: - m = em_SCPY; p1 = ep_MY; p2 = ep_R0; - break; - - // SCF - case 0xf41: - m = em_SCF; - break; - - // SZF - case 0xf42: - m = em_SZF; - break; - - // SDF - case 0xf44: - m = em_SDF; - break; - - // EI - case 0xf48: - m = em_EI; - break; - - // DI - case 0xf57: - m = em_DI; - break; - - // RDF - case 0xf5b: - m = em_RDF; - break; - - // RZF - case 0xf5d: - m = em_RZF; - break; - - // RCF - case 0xf5e: - m = em_RCF; - break; - - // PUSH r - case 0xfc0: case 0xfc1: case 0xfc2: case 0xfc3: - m = em_PUSH; p1 = ep_R0; - break; - - // PUSH XP - case 0xfc4: - m = em_PUSH; p1 = ep_XP; - break; - - // PUSH XH - case 0xfc5: - m = em_PUSH; p1 = ep_XH; - break; - - // PUSH XL - case 0xfc6: - m = em_PUSH; p1 = ep_XL; - break; - - // PUSH YP - case 0xfc7: - m = em_PUSH; p1 = ep_YP; - break; - - // PUSH YH - case 0xfc8: - m = em_PUSH; p1 = ep_YH; - break; - - // PUSH YL - case 0xfc9: - m = em_PUSH; p1 = ep_YL; - break; - - // PUSH F - case 0xfca: - m = em_PUSH; p1 = ep_F; - break; - - // DEC SP - case 0xfcb: - m = em_DEC; p1 = ep_SP; - break; - - // POP r - case 0xfd0: case 0xfd1: case 0xfd2: case 0xfd3: - m = em_POP; p1 = ep_R0; - break; - - // POP XP - case 0xfd4: - m = em_POP; p1 = ep_XP; - break; - - // POP XH - case 0xfd5: - m = em_POP; p1 = ep_XH; - break; - - // POP XL - case 0xfd6: - m = em_POP; p1 = ep_XL; - break; - - // POP YP - case 0xfd7: - m = em_POP; p1 = ep_YP; - break; - - // POP YH - case 0xfd8: - m = em_POP; p1 = ep_YH; - break; - - // POP YL - case 0xfd9: - m = em_POP; p1 = ep_YL; - break; - - // POP F - case 0xfda: - m = em_POP; p1 = ep_F; - break; - - // INC SP - case 0xfdb: - m = em_INC; p1 = ep_SP; - break; - - // RETS - case 0xfde: - m = em_RETS; - break; - - // RET - case 0xfdf: - m = em_RET; - break; - - // LD SPH,r - case 0xfe0: case 0xfe1: case 0xfe2: case 0xfe3: - m = em_LD; p1 = ep_SPH; p2 = ep_R0; - break; - - // LD r,SPH - case 0xfe4: case 0xfe5: case 0xfe6: case 0xfe7: - m = em_LD; p1 = ep_R0; p2 = ep_SPH; - break; - - // JPBA - case 0xfe8: - m = em_JPBA; - break; - - // LD SPL,r - case 0xff0: case 0xff1: case 0xff2: case 0xff3: - m = em_LD; p1 = ep_SPL; p2 = ep_R0; - break; - - // LD r,SPL - case 0xff4: case 0xff5: case 0xff6: case 0xff7: - m = em_LD; p1 = ep_R0; p2 = ep_SPL; - break; - - // HALT - case 0xff8: - m = em_HALT; - break; - - // SLP - case 0xff9: - m = em_SLP; - break; - - // NOP5 - case 0xffb: - m = em_NOP5; - break; - - // NOP7 - case 0xfff: - m = em_NOP7; - break; - - - default: - switch (op & 0xff0) - { - // ADC XH,i - case 0xa00: - m = em_ADC; p1 = ep_XH; p2 = ep_I; - break; - - // ADC XL,i - case 0xa10: - m = em_ADC; p1 = ep_XL; p2 = ep_I; - break; - - // ADC YH,i - case 0xa20: - m = em_ADC; p1 = ep_YH; p2 = ep_I; - break; - - // ADC YL,i - case 0xa30: - m = em_ADC; p1 = ep_YL; p2 = ep_I; - break; - - // CP XH,i - case 0xa40: - m = em_CP; p1 = ep_XH; p2 = ep_I; - break; - - // CP XL,i - case 0xa50: - m = em_CP; p1 = ep_XL; p2 = ep_I; - break; - - // CP YH,i - case 0xa60: - m = em_CP; p1 = ep_YH; p2 = ep_I; - break; - - // CP YL,i - case 0xa70: - m = em_CP; p1 = ep_YL; p2 = ep_I; - break; - - // ADD r,q - case 0xa80: - m = em_ADD; p1 = ep_R2; p2 = ep_Q; - break; - - // ADC r,q - case 0xa90: - m = em_ADC; p1 = ep_R2; p2 = ep_Q; - break; - - // SUB r,q - case 0xaa0: - m = em_SUB; p1 = ep_R2; p2 = ep_Q; - break; - - // SBC r,q - case 0xab0: - m = em_SBC; p1 = ep_R2; p2 = ep_Q; - break; - - // AND r,q - case 0xac0: - m = em_AND; p1 = ep_R2; p2 = ep_Q; - break; - - // OR r,q - case 0xad0: - m = em_OR; p1 = ep_R2; p2 = ep_Q; - break; - - // XOR r,q - case 0xae0: - m = em_XOR; p1 = ep_R2; p2 = ep_Q; - break; - - // ADD r,i - case 0xc00: case 0xc10: case 0xc20: case 0xc30: - m = em_ADD; p1 = ep_R4; p2 = ep_I; - break; - - // ADC r,i - case 0xc40: case 0xc50: case 0xc60: case 0xc70: - m = em_ADC; p1 = ep_R4; p2 = ep_I; - break; - - // AND r,i - case 0xc80: case 0xc90: case 0xca0: case 0xcb0: - m = em_AND; p1 = ep_R4; p2 = ep_I; - break; - - // OR r,i - case 0xcc0: case 0xcd0: case 0xce0: case 0xcf0: - m = em_OR; p1 = ep_R4; p2 = ep_I; - break; - - // XOR r,i - case 0xd00: case 0xd10: case 0xd20: case 0xd30: - m = em_XOR; p1 = ep_R4; p2 = ep_I; - break; - - // SBC r,i - case 0xd40: case 0xd50: case 0xd60: case 0xd70: - m = em_SBC; p1 = ep_R4; p2 = ep_I; - break; - - // FAN r,i - case 0xd80: case 0xd90: case 0xda0: case 0xdb0: - m = em_FAN; p1 = ep_R4; p2 = ep_I; - break; - - // CP r,i - case 0xdc0: case 0xdd0: case 0xde0: case 0xdf0: - m = em_CP; p1 = ep_R4; p2 = ep_I; - break; - - // LD r,i - case 0xe00: case 0xe10: case 0xe20: case 0xe30: - m = em_LD; p1 = ep_R4; p2 = ep_I; - break; - - // PSET p - case 0xe40: case 0xe50: - m = em_PSET; p1 = ep_P; - break; - - // LDPX MX,i - case 0xe60: - m = em_LDPX; p1 = ep_MX; p2 = ep_I; - break; - - // LDPY MY,i - case 0xe70: - m = em_LDPY; p1 = ep_MY; p2 = ep_I; - break; - - // LD r,q - case 0xec0: - m = em_LD; p1 = ep_R2; p2 = ep_Q; - break; - - // LDPX r,q - case 0xee0: - m = em_LDPX; p1 = ep_R2; p2 = ep_Q; - break; - - // LDPY r,q - case 0xef0: - m = em_LDPY; p1 = ep_R2; p2 = ep_Q; - break; - - // CP r,q - case 0xf00: - m = em_CP; p1 = ep_R2; p2 = ep_Q; - break; - - // FAN r,q - case 0xf10: - m = em_FAN; p1 = ep_R2; p2 = ep_Q; - break; - - // SET F,i - case 0xf40: - m = em_SET; p1 = ep_F; p2 = ep_I; - break; - - // RST F,i - case 0xf50: - m = em_RST; p1 = ep_F; p2 = ep_I; - break; - - // INC Mn - case 0xf60: - m = em_INC; p1 = ep_MN; - break; - - // DEC Mn - case 0xf70: - m = em_DEC; p1 = ep_MN; - break; - - // LD Mn,A - case 0xf80: - m = em_LD; p1 = ep_MN; p2 = ep_A; - break; - - // LD Mn,B - case 0xf90: - m = em_LD; p1 = ep_MN; p2 = ep_B; - break; - - // LD A,Mn - case 0xfa0: - m = em_LD; p1 = ep_A; p2 = ep_MN; - break; - - // LD B,Mn - case 0xfb0: - m = em_LD; p1 = ep_B; p2 = ep_MN; - break; - - - // illegal opcode - default: - m = em_ILL; - break; - - } // 0xff0 - break; - - } // 0xfff - break; - - } // 0xf00 (big switch) - - - // fetch mnemonic - char *dst = buffer; - dst += sprintf(dst, "%-6s", em_name[m]); - - // fetch param(s) - char pbuffer[10]; - if (p1 != -1) - { - dst += sprintf(dst, "%s", decode_param(op, p1, pbuffer)); - if (p2 != -1) - { - dst += sprintf(dst, ",%s", decode_param(op, p2, pbuffer)); - } - } - - return 1 | em_flags[m] | DASMFLAG_SUPPORTED; -} diff --git a/src/devices/cpu/e0c6200/e0c6200d.cpp b/src/devices/cpu/e0c6200/e0c6200d.cpp new file mode 100644 index 00000000000..4fc0599ac24 --- /dev/null +++ b/src/devices/cpu/e0c6200/e0c6200d.cpp @@ -0,0 +1,708 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Seiko Epson E0C6200 disassembler + +*/ + +#include "emu.h" +#include "debugger.h" +#include "e0c6200.h" + +// opcode mnemonics +enum e_mnemonics +{ + em_JP, em_RETD, em_CALL, em_CALZ, + em_LD, em_LBPX, em_ADC, em_CP, em_ADD, em_SUB, em_SBC, em_AND, em_OR, em_XOR, + em_RLC, em_FAN, em_PSET, em_LDPX, em_LDPY, em_SET, em_RST, em_INC, em_DEC, + em_RRC, em_ACPX, em_ACPY, em_SCPX, em_SCPY, em_PUSH, em_POP, + em_RETS, em_RET, em_JPBA, em_HALT, em_SLP, em_NOP5, em_NOP7, + em_NOT, em_SCF, em_SZF, em_SDF, em_EI, em_DI, em_RDF, em_RZF, em_RCF, em_ILL +}; + +static const char *const em_name[] = +{ + "JP", "RETD", "CALL", "CALZ", + "LD", "LBPX", "ADC", "CP", "ADD", "SUB", "SBC", "AND", "OR", "XOR", + "RLC", "FAN", "PSET", "LDPX", "LDPY", "SET", "RST", "INC", "DEC", + "RRC", "ACPX", "ACPY", "SCPX", "SCPY", "PUSH", "POP", + "RETS", "RET", "JPBA", "HALT", "SLP", "NOP5", "NOP7", + "NOT", "SCF", "SZF", "SDF", "EI", "DI", "RDF", "RZF", "RCF", "?" +}; + +#define _OVER DASMFLAG_STEP_OVER +#define _OUT DASMFLAG_STEP_OUT + +static const UINT32 em_flags[] = +{ + 0, _OUT, _OVER, _OVER, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + _OUT, _OUT, 0, _OVER, _OVER, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +// opcode params +enum e_params +{ + ep_S, ep_E, ep_I, ep_R0, ep_R2, ep_R4, ep_Q, + ep_cC, ep_cNC, ep_cZ, ep_cNZ, + ep_A, ep_B, ep_X, ep_Y, ep_MX, ep_MY, ep_XP, ep_XH, ep_XL, ep_YP, ep_YH, ep_YL, + ep_P, ep_F, ep_MN, ep_SP, ep_SPH, ep_SPL +}; + +// 0-digit is number of bits per opcode parameter, 0 bits is literal, +// 0x10-digit is for shift-right, 0x100-digit is special flag for r/q param +static const UINT16 ep_bits[] = +{ + 8, 8, 4, 0x102, 0x122, 0x142, 0x102, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 4, 0, 0, 0 +}; + +// redirect for r/q param +static const UINT8 ep_redirect_r[4] = { ep_A, ep_B, ep_MX, ep_MY }; + +// literal opcode parameter +static const char *const ep_name[] = +{ + " ", " ", " ", " ", " ", " ", " ", + "C", "NC", "Z", "NZ", + "A", "B", "X", "Y", "MX", "MY", "XP", "XH", "XL", "YP", "YH", "YL", + " ", "F", " ", "SP", "SPH", "SPL" +}; + + +static char* decode_param(UINT16 opcode, int param, char* buffer) +{ + int bits = ep_bits[param] & 0xf; + int shift = ep_bits[param] >> 4 & 0xf; + UINT16 opmask = opcode >> shift & ((1 << bits) - 1); + + // redirect r/q to A/B/MX/MY + if (ep_bits[param] & 0x100) + param = ep_redirect_r[opmask]; + + // literal param + if (ep_bits[param] == 0) + { + strcpy(buffer, ep_name[param]); + return buffer; + } + + // print value like how it's documented in the manual + char val[10]; + if (bits > 4 || opmask > 9) + sprintf(val, "%02XH", opmask); + else + sprintf(val, "%d", opmask); + + if (param == ep_MN) + sprintf(buffer, "M%s", val); + else + strcpy(buffer, val); + + return buffer; +} + + +CPU_DISASSEMBLE(e0c6200) +{ + UINT16 op = (oprom[1] | oprom[0] << 8) & 0xfff; + + int m = -1; + int p1 = -1; + int p2 = -1; + + // determine id for mnemonic and param(s) + switch (op & 0xf00) + { + // JP s + case 0x000: + m = em_JP; p1 = ep_S; + break; + + // RETD e + case 0x100: + m = em_RETD; p1 = ep_E; + break; + + // JP C,s + case 0x200: + m = em_JP; p1 = ep_cC; p2 = ep_S; + break; + + // JP NC,s + case 0x300: + m = em_JP; p1 = ep_cNC; p2 = ep_S; + break; + + // CALL s + case 0x400: + m = em_CALL; p1 = ep_S; + break; + + // CALZ s + case 0x500: + m = em_CALZ; p1 = ep_S; + break; + + // JP Z,s + case 0x600: + m = em_JP; p1 = ep_cZ; p2 = ep_S; + break; + + // JP NZ,s + case 0x700: + m = em_JP; p1 = ep_cNZ; p2 = ep_S; + break; + + // LD Y,e + case 0x800: + m = em_LD; p1 = ep_Y; p2 = ep_E; + break; + + // LBPX MX,e + case 0x900: + m = em_LBPX; p1 = ep_MX; p2 = ep_E; + break; + + // LD X,e + case 0xb00: + m = em_LD; p1 = ep_X; p2 = ep_E; + break; + + + default: + switch (op) + { + // RLC r + case 0xaf0: case 0xaf5: case 0xafa: case 0xaff: + m = em_RLC; p1 = ep_R0; + break; + + // NOT r + case 0xd0f: case 0xd1f: case 0xd2f: case 0xd3f: + m = em_NOT; p1 = ep_R4; + break; + + // LD XP,r + case 0xe80: case 0xe81: case 0xe82: case 0xe83: + m = em_LD; p1 = ep_XP; p2 = ep_R0; + break; + + // LD XH,r + case 0xe84: case 0xe85: case 0xe86: case 0xe87: + m = em_LD; p1 = ep_XH; p2 = ep_R0; + break; + + // LD XL,r + case 0xe88: case 0xe89: case 0xe8a: case 0xe8b: + m = em_LD; p1 = ep_XL; p2 = ep_R0; + break; + + // RRC r + case 0xe8c: case 0xe8d: case 0xe8e: case 0xe8f: + m = em_RRC; p1 = ep_R0; + break; + + // LD YP,r + case 0xe90: case 0xe91: case 0xe92: case 0xe93: + m = em_LD; p1 = ep_YP; p2 = ep_R0; + break; + + // LD YH,r + case 0xe94: case 0xe95: case 0xe96: case 0xe97: + m = em_LD; p1 = ep_YH; p2 = ep_R0; + break; + + // LD YL,r + case 0xe98: case 0xe99: case 0xe9a: case 0xe9b: + m = em_LD; p1 = ep_YL; p2 = ep_R0; + break; + + // LD r,XP + case 0xea0: case 0xea1: case 0xea2: case 0xea3: + m = em_LD; p1 = ep_R0; p2 = ep_XP; + break; + + // LD r,XH + case 0xea4: case 0xea5: case 0xea6: case 0xea7: + m = em_LD; p1 = ep_R0; p2 = ep_XH; + break; + + // LD r,XL + case 0xea8: case 0xea9: case 0xeaa: case 0xeab: + m = em_LD; p1 = ep_R0; p2 = ep_XL; + break; + + // LD r,YP + case 0xeb0: case 0xeb1: case 0xeb2: case 0xeb3: + m = em_LD; p1 = ep_R0; p2 = ep_YP; + break; + + // LD r,YH + case 0xeb4: case 0xeb5: case 0xeb6: case 0xeb7: + m = em_LD; p1 = ep_R0; p2 = ep_YH; + break; + + // LD r,YL + case 0xeb8: case 0xeb9: case 0xeba: case 0xebb: + m = em_LD; p1 = ep_R0; p2 = ep_YL; + break; + + // INC X + case 0xee0: + m = em_INC; p1 = ep_X; + break; + + // INC Y + case 0xef0: + m = em_INC; p1 = ep_Y; + break; + + // ACPX MX,r + case 0xf28: case 0xf29: case 0xf2a: case 0xf2b: + m = em_ACPX; p1 = ep_MX; p2 = ep_R0; + break; + + // ACPY MY,r + case 0xf2c: case 0xf2d: case 0xf2e: case 0xf2f: + m = em_ACPY; p1 = ep_MY; p2 = ep_R0; + break; + + // SCPX MX,r + case 0xf38: case 0xf39: case 0xf3a: case 0xf3b: + m = em_SCPX; p1 = ep_MX; p2 = ep_R0; + break; + + // SCPY MY,r + case 0xf3c: case 0xf3d: case 0xf3e: case 0xf3f: + m = em_SCPY; p1 = ep_MY; p2 = ep_R0; + break; + + // SCF + case 0xf41: + m = em_SCF; + break; + + // SZF + case 0xf42: + m = em_SZF; + break; + + // SDF + case 0xf44: + m = em_SDF; + break; + + // EI + case 0xf48: + m = em_EI; + break; + + // DI + case 0xf57: + m = em_DI; + break; + + // RDF + case 0xf5b: + m = em_RDF; + break; + + // RZF + case 0xf5d: + m = em_RZF; + break; + + // RCF + case 0xf5e: + m = em_RCF; + break; + + // PUSH r + case 0xfc0: case 0xfc1: case 0xfc2: case 0xfc3: + m = em_PUSH; p1 = ep_R0; + break; + + // PUSH XP + case 0xfc4: + m = em_PUSH; p1 = ep_XP; + break; + + // PUSH XH + case 0xfc5: + m = em_PUSH; p1 = ep_XH; + break; + + // PUSH XL + case 0xfc6: + m = em_PUSH; p1 = ep_XL; + break; + + // PUSH YP + case 0xfc7: + m = em_PUSH; p1 = ep_YP; + break; + + // PUSH YH + case 0xfc8: + m = em_PUSH; p1 = ep_YH; + break; + + // PUSH YL + case 0xfc9: + m = em_PUSH; p1 = ep_YL; + break; + + // PUSH F + case 0xfca: + m = em_PUSH; p1 = ep_F; + break; + + // DEC SP + case 0xfcb: + m = em_DEC; p1 = ep_SP; + break; + + // POP r + case 0xfd0: case 0xfd1: case 0xfd2: case 0xfd3: + m = em_POP; p1 = ep_R0; + break; + + // POP XP + case 0xfd4: + m = em_POP; p1 = ep_XP; + break; + + // POP XH + case 0xfd5: + m = em_POP; p1 = ep_XH; + break; + + // POP XL + case 0xfd6: + m = em_POP; p1 = ep_XL; + break; + + // POP YP + case 0xfd7: + m = em_POP; p1 = ep_YP; + break; + + // POP YH + case 0xfd8: + m = em_POP; p1 = ep_YH; + break; + + // POP YL + case 0xfd9: + m = em_POP; p1 = ep_YL; + break; + + // POP F + case 0xfda: + m = em_POP; p1 = ep_F; + break; + + // INC SP + case 0xfdb: + m = em_INC; p1 = ep_SP; + break; + + // RETS + case 0xfde: + m = em_RETS; + break; + + // RET + case 0xfdf: + m = em_RET; + break; + + // LD SPH,r + case 0xfe0: case 0xfe1: case 0xfe2: case 0xfe3: + m = em_LD; p1 = ep_SPH; p2 = ep_R0; + break; + + // LD r,SPH + case 0xfe4: case 0xfe5: case 0xfe6: case 0xfe7: + m = em_LD; p1 = ep_R0; p2 = ep_SPH; + break; + + // JPBA + case 0xfe8: + m = em_JPBA; + break; + + // LD SPL,r + case 0xff0: case 0xff1: case 0xff2: case 0xff3: + m = em_LD; p1 = ep_SPL; p2 = ep_R0; + break; + + // LD r,SPL + case 0xff4: case 0xff5: case 0xff6: case 0xff7: + m = em_LD; p1 = ep_R0; p2 = ep_SPL; + break; + + // HALT + case 0xff8: + m = em_HALT; + break; + + // SLP + case 0xff9: + m = em_SLP; + break; + + // NOP5 + case 0xffb: + m = em_NOP5; + break; + + // NOP7 + case 0xfff: + m = em_NOP7; + break; + + + default: + switch (op & 0xff0) + { + // ADC XH,i + case 0xa00: + m = em_ADC; p1 = ep_XH; p2 = ep_I; + break; + + // ADC XL,i + case 0xa10: + m = em_ADC; p1 = ep_XL; p2 = ep_I; + break; + + // ADC YH,i + case 0xa20: + m = em_ADC; p1 = ep_YH; p2 = ep_I; + break; + + // ADC YL,i + case 0xa30: + m = em_ADC; p1 = ep_YL; p2 = ep_I; + break; + + // CP XH,i + case 0xa40: + m = em_CP; p1 = ep_XH; p2 = ep_I; + break; + + // CP XL,i + case 0xa50: + m = em_CP; p1 = ep_XL; p2 = ep_I; + break; + + // CP YH,i + case 0xa60: + m = em_CP; p1 = ep_YH; p2 = ep_I; + break; + + // CP YL,i + case 0xa70: + m = em_CP; p1 = ep_YL; p2 = ep_I; + break; + + // ADD r,q + case 0xa80: + m = em_ADD; p1 = ep_R2; p2 = ep_Q; + break; + + // ADC r,q + case 0xa90: + m = em_ADC; p1 = ep_R2; p2 = ep_Q; + break; + + // SUB r,q + case 0xaa0: + m = em_SUB; p1 = ep_R2; p2 = ep_Q; + break; + + // SBC r,q + case 0xab0: + m = em_SBC; p1 = ep_R2; p2 = ep_Q; + break; + + // AND r,q + case 0xac0: + m = em_AND; p1 = ep_R2; p2 = ep_Q; + break; + + // OR r,q + case 0xad0: + m = em_OR; p1 = ep_R2; p2 = ep_Q; + break; + + // XOR r,q + case 0xae0: + m = em_XOR; p1 = ep_R2; p2 = ep_Q; + break; + + // ADD r,i + case 0xc00: case 0xc10: case 0xc20: case 0xc30: + m = em_ADD; p1 = ep_R4; p2 = ep_I; + break; + + // ADC r,i + case 0xc40: case 0xc50: case 0xc60: case 0xc70: + m = em_ADC; p1 = ep_R4; p2 = ep_I; + break; + + // AND r,i + case 0xc80: case 0xc90: case 0xca0: case 0xcb0: + m = em_AND; p1 = ep_R4; p2 = ep_I; + break; + + // OR r,i + case 0xcc0: case 0xcd0: case 0xce0: case 0xcf0: + m = em_OR; p1 = ep_R4; p2 = ep_I; + break; + + // XOR r,i + case 0xd00: case 0xd10: case 0xd20: case 0xd30: + m = em_XOR; p1 = ep_R4; p2 = ep_I; + break; + + // SBC r,i + case 0xd40: case 0xd50: case 0xd60: case 0xd70: + m = em_SBC; p1 = ep_R4; p2 = ep_I; + break; + + // FAN r,i + case 0xd80: case 0xd90: case 0xda0: case 0xdb0: + m = em_FAN; p1 = ep_R4; p2 = ep_I; + break; + + // CP r,i + case 0xdc0: case 0xdd0: case 0xde0: case 0xdf0: + m = em_CP; p1 = ep_R4; p2 = ep_I; + break; + + // LD r,i + case 0xe00: case 0xe10: case 0xe20: case 0xe30: + m = em_LD; p1 = ep_R4; p2 = ep_I; + break; + + // PSET p + case 0xe40: case 0xe50: + m = em_PSET; p1 = ep_P; + break; + + // LDPX MX,i + case 0xe60: + m = em_LDPX; p1 = ep_MX; p2 = ep_I; + break; + + // LDPY MY,i + case 0xe70: + m = em_LDPY; p1 = ep_MY; p2 = ep_I; + break; + + // LD r,q + case 0xec0: + m = em_LD; p1 = ep_R2; p2 = ep_Q; + break; + + // LDPX r,q + case 0xee0: + m = em_LDPX; p1 = ep_R2; p2 = ep_Q; + break; + + // LDPY r,q + case 0xef0: + m = em_LDPY; p1 = ep_R2; p2 = ep_Q; + break; + + // CP r,q + case 0xf00: + m = em_CP; p1 = ep_R2; p2 = ep_Q; + break; + + // FAN r,q + case 0xf10: + m = em_FAN; p1 = ep_R2; p2 = ep_Q; + break; + + // SET F,i + case 0xf40: + m = em_SET; p1 = ep_F; p2 = ep_I; + break; + + // RST F,i + case 0xf50: + m = em_RST; p1 = ep_F; p2 = ep_I; + break; + + // INC Mn + case 0xf60: + m = em_INC; p1 = ep_MN; + break; + + // DEC Mn + case 0xf70: + m = em_DEC; p1 = ep_MN; + break; + + // LD Mn,A + case 0xf80: + m = em_LD; p1 = ep_MN; p2 = ep_A; + break; + + // LD Mn,B + case 0xf90: + m = em_LD; p1 = ep_MN; p2 = ep_B; + break; + + // LD A,Mn + case 0xfa0: + m = em_LD; p1 = ep_A; p2 = ep_MN; + break; + + // LD B,Mn + case 0xfb0: + m = em_LD; p1 = ep_B; p2 = ep_MN; + break; + + + // illegal opcode + default: + m = em_ILL; + break; + + } // 0xff0 + break; + + } // 0xfff + break; + + } // 0xf00 (big switch) + + + // fetch mnemonic + char *dst = buffer; + dst += sprintf(dst, "%-6s", em_name[m]); + + // fetch param(s) + char pbuffer[10]; + if (p1 != -1) + { + dst += sprintf(dst, "%s", decode_param(op, p1, pbuffer)); + if (p2 != -1) + { + dst += sprintf(dst, ",%s", decode_param(op, p2, pbuffer)); + } + } + + return 1 | em_flags[m] | DASMFLAG_SUPPORTED; +} diff --git a/src/devices/cpu/e0c6200/e0c6s46.c b/src/devices/cpu/e0c6200/e0c6s46.c deleted file mode 100644 index be131a1e5dc..00000000000 --- a/src/devices/cpu/e0c6200/e0c6s46.c +++ /dev/null @@ -1,893 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:hap -/* - - Seiko Epson E0C6S46 MCU - QFP5-128pin, see manual for pinout - - TODO: - - OSC3 - - K input interrupts - - finish i/o ports - - serial interface - - buzzer envelope addition - - add mask options to MCFG (eg. buzzer on output port R4x is optional) - -*/ - -#include "e0c6s46.h" - -enum -{ - IRQREG_CLKTIMER = 0, - IRQREG_STOPWATCH, - IRQREG_PRGTIMER, - IRQREG_SERIAL, - IRQREG_INPUT0, - IRQREG_INPUT1 -}; - -const device_type E0C6S46 = &device_creator; - - -// internal memory maps -static ADDRESS_MAP_START(e0c6s46_program, AS_PROGRAM, 16, e0c6s46_device) - AM_RANGE(0x0000, 0x17ff) AM_ROM -ADDRESS_MAP_END - - -static ADDRESS_MAP_START(e0c6s46_data, AS_DATA, 8, e0c6s46_device) - AM_RANGE(0x0000, 0x027f) AM_RAM - AM_RANGE(0x0e00, 0x0e4f) AM_RAM AM_SHARE("vram1") - AM_RANGE(0x0e80, 0x0ecf) AM_RAM AM_SHARE("vram2") - AM_RANGE(0x0f00, 0x0f7f) AM_READWRITE(io_r, io_w) -ADDRESS_MAP_END - - -// device definitions -e0c6s46_device::e0c6s46_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : e0c6200_cpu_device(mconfig, E0C6S46, "E0C6S46", tag, owner, clock, ADDRESS_MAP_NAME(e0c6s46_program), ADDRESS_MAP_NAME(e0c6s46_data), "e0c6s46", __FILE__) - , m_vram1(*this, "vram1") - , m_vram2(*this, "vram2") - , m_pixel_update_handler(NULL) - , m_write_r0(*this), m_write_r1(*this), m_write_r2(*this), m_write_r3(*this), m_write_r4(*this) - , m_read_p0(*this), m_read_p1(*this), m_read_p2(*this), m_read_p3(*this) - , m_write_p0(*this), m_write_p1(*this), m_write_p2(*this), m_write_p3(*this) -{ } - - - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void e0c6s46_device::device_start() -{ - e0c6200_cpu_device::device_start(); - - // find ports - m_write_r0.resolve_safe(); - m_write_r1.resolve_safe(); - m_write_r2.resolve_safe(); - m_write_r3.resolve_safe(); - m_write_r4.resolve_safe(); - - m_read_p0.resolve_safe(0); - m_read_p1.resolve_safe(0); - m_read_p2.resolve_safe(0); - m_read_p3.resolve_safe(0); - m_write_p0.resolve_safe(); - m_write_p1.resolve_safe(); - m_write_p2.resolve_safe(); - m_write_p3.resolve_safe(); - - // create timers - m_core_256_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::core_256_cb), this)); - m_core_256_handle->adjust(attotime::from_ticks(64, unscaled_clock())); - m_prgtimer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::prgtimer_cb), this)); - m_prgtimer_handle->adjust(attotime::never); - m_buzzer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::buzzer_cb), this)); - m_buzzer_handle->adjust(attotime::never); - - // zerofill - memset(m_port_r, 0x0, sizeof(m_port_r)); - m_r_dir = 0; - memset(m_port_p, 0x0, sizeof(m_port_p)); - m_p_dir = 0; - m_p_pullup = 0; - memset(m_port_k, 0xf, sizeof(m_port_k)); - m_dfk0 = 0xf; - - memset(m_irqflag, 0, sizeof(m_irqflag)); - memset(m_irqmask, 0, sizeof(m_irqmask)); - m_osc = 0; - m_svd = 0; - m_lcd_control = 0; - m_lcd_contrast = 0; - - m_256_src_pulse = 0; - m_watchdog_count = 0; - m_clktimer_count = 0; - - m_stopwatch_on = 0; - m_swl_cur_pulse = 0; - m_swl_slice = 0; - m_swl_count = 0; - m_swh_count = 0; - - m_prgtimer_select = 0; - m_prgtimer_on = 0; - m_prgtimer_src_pulse = 0; - m_prgtimer_cur_pulse = 0; - m_prgtimer_count = 0; - m_prgtimer_reload = 0; - - m_bz_43_on = 0; - m_bz_freq = 0; - m_bz_envelope = 0; - m_bz_duty_ratio = 0; - m_bz_1shot_on = 0; - m_bz_1shot_running = false; - m_bz_1shot_count = 0; - m_bz_pulse = 0; - - // register for savestates - save_item(NAME(m_port_r)); - save_item(NAME(m_r_dir)); - save_item(NAME(m_port_p)); - save_item(NAME(m_p_dir)); - save_item(NAME(m_p_pullup)); - save_item(NAME(m_port_k)); - save_item(NAME(m_dfk0)); - - save_item(NAME(m_irqflag)); - save_item(NAME(m_irqmask)); - save_item(NAME(m_osc)); - save_item(NAME(m_svd)); - save_item(NAME(m_lcd_control)); - save_item(NAME(m_lcd_contrast)); - - save_item(NAME(m_256_src_pulse)); - save_item(NAME(m_watchdog_count)); - save_item(NAME(m_clktimer_count)); - - save_item(NAME(m_stopwatch_on)); - save_item(NAME(m_swl_cur_pulse)); - save_item(NAME(m_swl_slice)); - save_item(NAME(m_swl_count)); - save_item(NAME(m_swh_count)); - - save_item(NAME(m_prgtimer_select)); - save_item(NAME(m_prgtimer_on)); - save_item(NAME(m_prgtimer_src_pulse)); - save_item(NAME(m_prgtimer_cur_pulse)); - save_item(NAME(m_prgtimer_count)); - save_item(NAME(m_prgtimer_reload)); - - save_item(NAME(m_bz_43_on)); - save_item(NAME(m_bz_freq)); - save_item(NAME(m_bz_envelope)); - save_item(NAME(m_bz_duty_ratio)); - save_item(NAME(m_bz_1shot_on)); - save_item(NAME(m_bz_1shot_running)); - save_item(NAME(m_bz_1shot_count)); - save_item(NAME(m_bz_pulse)); -} - - - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void e0c6s46_device::device_reset() -{ - e0c6200_cpu_device::device_reset(); - - // reset interrupts - memset(m_irqflag, 0, sizeof(m_irqflag)); - memset(m_irqmask, 0, sizeof(m_irqmask)); - - // reset other i/o - m_data->write_byte(0xf41, 0xf); - m_data->write_byte(0xf54, 0xf); - m_data->write_byte(0xf70, 0x0); - m_data->write_byte(0xf71, 0x8); - m_data->write_byte(0xf73, m_svd & 0xc0); - - m_data->write_byte(0xf74, 0x0); - m_data->write_byte(0xf75, 0x4); - m_data->write_byte(0xf76, 0x3); - m_data->write_byte(0xf77, 0x2); - m_data->write_byte(0xf78, 0x2); - m_data->write_byte(0xf79, 0x0); - m_data->write_byte(0xf7a, 0x0); - m_data->write_byte(0xf7b, 0x0); - m_data->write_byte(0xf7d, 0x0); - m_data->write_byte(0xf7e, 0x0); - - // reset ports - for (int i = 0; i < 5; i++) - write_r(i, m_port_r[i]); - for (int i = 0; i < 4; i++) - write_p(i, m_port_p[i]); -} - - - -//------------------------------------------------- -// execute -//------------------------------------------------- - -void e0c6s46_device::execute_one() -{ - // E0C6S46 has no support for SLP opcode - if (m_op == 0xff9) - return; - - e0c6200_cpu_device::execute_one(); -} - - - -//------------------------------------------------- -// interrupts -//------------------------------------------------- - -bool e0c6s46_device::check_interrupt() -{ - // priority order is not the same as register order - static const int priorder[6] = - { - IRQREG_CLKTIMER, - IRQREG_STOPWATCH, - IRQREG_INPUT0, - IRQREG_INPUT1, - IRQREG_SERIAL, - IRQREG_PRGTIMER, - }; - - // check interrupts from high to low priority - for (int pri = 5; pri >= 0; pri--) - { - // hw glitch note, not emulated: if a new interrupt is requested in the - // middle of handling this interrupt, irq vector may be an OR of 2 vectors - m_irq_vector = 2*pri + 2; - int reg = priorder[pri]; - m_irq_id = reg; - - switch (reg) - { - // other: mask vs flag - default: - if (m_irqflag[reg] & m_irqmask[reg]) - return true; - break; - } - } - - return false; -} - -void e0c6s46_device::execute_set_input(int line, int state) -{ - // only support 8 K input lines at the moment - if (line < 0 || line > 7) - return; - - state = (state) ? 1 : 0; - int port = line >> 2 & 1; - UINT8 bit = 1 << (line & 3); - - m_port_k[port] = (m_port_k[port] & ~bit) | (state ? bit : 0); -} - - - -//------------------------------------------------- -// ports -//------------------------------------------------- - -// R output ports - -void e0c6s46_device::write_r(UINT8 port, UINT8 data) -{ - data &= 0xf; - m_port_r[port] = data; - - // ports R0x-R3x can be high-impedance - UINT8 out = data; - if (port < 4 && !(m_r_dir >> port & 1)) - out = 0xf; - - switch (port) - { - case 0: m_write_r0(port, out, 0xff); break; - case 1: m_write_r1(port, out, 0xff); break; - case 2: m_write_r2(port, out, 0xff); break; - case 3: m_write_r3(port, out, 0xff); break; // TODO: R33 PTCLK/_SRDY - - // R4x: special output - case 4: - // d3: buzzer on: direct output or 1-shot output - if ((data & 8) != m_bz_43_on) - { - m_bz_43_on = data & 8; - reset_buzzer(); - } - write_r4_out(); - break; - } -} - -void e0c6s46_device::write_r4_out() -{ - // R40: _FOUT(clock inverted output) - // R42: FOUT or _BZ - // R43: BZ(buzzer) - UINT8 out = (m_port_r[4] & 2) | (m_bz_pulse << 3) | (m_bz_pulse << 2 ^ 4); - m_write_r4(4, out, 0xff); -} - - -// P I/O ports - -void e0c6s46_device::write_p(UINT8 port, UINT8 data) -{ - data &= 0xf; - m_port_p[port] = data; - - // don't output if port direction is set to input - if (!(m_p_dir >> port & 1)) - return; - - switch (port) - { - case 0: m_write_p0(port, data, 0xff); break; - case 1: m_write_p1(port, data, 0xff); break; - case 2: m_write_p2(port, data, 0xff); break; - case 3: m_write_p3(port, data, 0xff); break; - } -} - -UINT8 e0c6s46_device::read_p(UINT8 port) -{ - // return written value if port direction is set to output - if (m_p_dir >> port & 1) - return m_port_p[port]; - - switch (port) - { - case 0: return m_read_p0(port, 0xff); - case 1: return m_read_p1(port, 0xff); - case 2: return m_read_p2(port, 0xff); - case 3: return m_read_p3(port, 0xff); - } - - return 0; -} - - - -//------------------------------------------------- -// timers -//------------------------------------------------- - -TIMER_CALLBACK_MEMBER(e0c6s46_device::core_256_cb) -{ - // clock-timer, stopwatch timer, and some features of the buzzer all run - // from the same internal 256hz timer (64 ticks high+low at default clock of 32768hz) - m_256_src_pulse ^= 1; - m_core_256_handle->adjust(attotime::from_ticks(64, unscaled_clock())); - - // clock stopwatch on falling edge of pulse+on - m_swl_cur_pulse = m_256_src_pulse | (m_stopwatch_on ^ 1); - if (m_swl_cur_pulse == 0) - clock_stopwatch(); - - // clock 1-shot buzzer on rising edge if it's on - if (m_bz_1shot_on != 0 && m_256_src_pulse == 1) - clock_bz_1shot(); - - // clock-timer is always running, advance it on falling edge - // (handle clock_clktimer last in case of watchdog reset) - if (m_256_src_pulse == 0) - clock_clktimer(); -} - - -// clock-timer - -void e0c6s46_device::clock_watchdog() -{ - // initial reset after 3 to 4 seconds - if (++m_watchdog_count == 4) - { - logerror("%s watchdog reset\n", tag()); - m_watchdog_count = 0; - device_reset(); - } -} - -void e0c6s46_device::clock_clktimer() -{ - m_clktimer_count++; - - // irq on falling edge of 32, 8, 2, 1hz - UINT8 flag = 0; - if ((m_clktimer_count & 0x07) == 0) - flag |= 1; - if ((m_clktimer_count & 0x1f) == 0) - flag |= 2; - if ((m_clktimer_count & 0x7f) == 0) - flag |= 4; - if (m_clktimer_count == 0) - flag |= 8; - - m_irqflag[IRQREG_CLKTIMER] |= flag; - if (m_irqflag[IRQREG_CLKTIMER] & m_irqmask[IRQREG_CLKTIMER]) - m_possible_irq = true; - - // 1hz falling edge also clocks the watchdog timer - if (m_clktimer_count == 0) - clock_watchdog(); -} - - -// stopwatch timer - -void e0c6s46_device::clock_stopwatch() -{ - m_swl_slice++; - - // 1 slice is 3 ticks(256hz) on even and 2 ticks on uneven counts, - // but from count 1 to 2 it's 3 ticks, 6 out of 100 times, to make - // exactly 26/256hz * 6 + 25/256hz * 4 = 1 second - int swl_next = 3 - (m_swl_count & 1); - if (m_swl_count == 1 && !(m_swh_count >> 1 & 1)) - swl_next = 3; - - if (m_swl_slice == swl_next) - { - m_swl_slice = 0; - - // bcd counter, irq on falling edge of 10 and 1hz - m_swl_count = (m_swl_count + 1) % 10; - if (m_swl_count == 0) - { - m_irqflag[IRQREG_STOPWATCH] |= 1; - m_swh_count = (m_swh_count + 1) % 10; - if (m_swh_count == 0) - m_irqflag[IRQREG_STOPWATCH] |= 2; - } - - if (m_irqflag[IRQREG_STOPWATCH] & m_irqmask[IRQREG_STOPWATCH]) - m_possible_irq = true; - } -} - - -// programmable timer - -void e0c6s46_device::clock_prgtimer() -{ - // irq and reload when it reaches zero - if (--m_prgtimer_count == 0) - { - m_irqflag[IRQREG_PRGTIMER] |= 1; - if (m_irqflag[IRQREG_PRGTIMER] & m_irqmask[IRQREG_PRGTIMER]) - m_possible_irq = true; - - // note: a reload of 0 indicates a 256-counter - m_prgtimer_count = m_prgtimer_reload; - } -} - -bool e0c6s46_device::prgtimer_reset_prescaler() -{ - // only 2 to 7 are clock dividers - UINT8 sel = m_prgtimer_select & 7; - if (sel >= 2) - m_prgtimer_handle->adjust(attotime::from_ticks(2 << (sel ^ 7), unscaled_clock())); - - return (sel >= 2); -} - -TIMER_CALLBACK_MEMBER(e0c6s46_device::prgtimer_cb) -{ - // check if it's clocked by osc1, schedule next timeout - if (!prgtimer_reset_prescaler()) - return; - - m_prgtimer_src_pulse ^= 1; - m_prgtimer_cur_pulse = m_prgtimer_src_pulse | (m_prgtimer_on ^ 1); - - // clock prgtimer on falling edge of pulse+on - if (m_prgtimer_cur_pulse == 0) - clock_prgtimer(); -} - - -// buzzer - -void e0c6s46_device::schedule_buzzer() -{ - // only schedule next buzzer timeout if it's on - if (m_bz_43_on != 0 && !m_bz_1shot_running) - return; - - // pulse width differs per frequency selection - int mul = (m_bz_freq & 4) ? 1 : 2; - int high = (m_bz_freq & 2) ? 12 : 8; - int low = 16 + (m_bz_freq << 2 & 0xc); - - // pulse width envelope if it's on - if (m_bz_envelope & 1) - high -= m_bz_duty_ratio; - low -= high; - - m_buzzer_handle->adjust(attotime::from_ticks(m_bz_pulse ? high : low, mul * unscaled_clock())); -} - -TIMER_CALLBACK_MEMBER(e0c6s46_device::buzzer_cb) -{ - // invert pulse wave and write to output - m_bz_pulse ^= 1; - write_r4_out(); - - schedule_buzzer(); -} - -void e0c6s46_device::reset_buzzer() -{ - // don't reset if the timer is running - if (m_buzzer_handle->remaining() == attotime::never) - schedule_buzzer(); -} - -void e0c6s46_device::clock_bz_1shot() -{ - m_bz_1shot_running = true; - - // reload counter the 1st time - if (m_bz_1shot_count == 0) - { - reset_buzzer(); - m_bz_1shot_count = (m_bz_freq & 8) ? 16 : 8; - } - - // stop ringing when counter reaches 0 - else if (--m_bz_1shot_count == 0) - { - m_bz_1shot_on = 0; - m_bz_1shot_running = false; - } -} - - - -//------------------------------------------------- -// LCD Driver -//------------------------------------------------- - -UINT32 e0c6s46_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) -{ - // call this 32 times per second (osc1/1024: 32hz at default clock of 32768hz) - for (int bank = 0; bank < 2; bank++) - { - const UINT8* vram = bank ? m_vram2 : m_vram1; - - // determine operating mode - bool lcd_on = false; - int pixel = 0; - if (m_lcd_control & 8 || (bank == 1 && m_lcd_control & 2)) - pixel = 0; - else if (m_lcd_control & 4) - pixel = 1; - else - lcd_on = true; - - // draw pixels - for (int offset = 0; offset < 0x50; offset++) - { - for (int c = 0; c < 4; c++) - { - if (lcd_on) - pixel = vram[offset] >> c & 1; - - // 16 COM(common) pins, 40 SEG(segment) pins - int seg = offset / 2; - int com = bank * 8 + (offset & 1) * 4 + c; - - if (m_pixel_update_handler != NULL) - m_pixel_update_handler(*this, bitmap, cliprect, m_lcd_contrast, seg, com, pixel); - else if (cliprect.contains(seg, com)) - bitmap.pix16(com, seg) = pixel; - } - } - } - - return 0; -} - - - -//------------------------------------------------- -// internal I/O -//------------------------------------------------- - -READ8_MEMBER(e0c6s46_device::io_r) -{ - switch (offset) - { - // irq flags, masks - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: - { - // irq flags are reset(acked) when read - UINT8 flag = m_irqflag[offset]; - if (!space.debugger_access()) - m_irqflag[offset] = 0; - return flag; - } - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: - return m_irqmask[offset-0x10]; - - // K input ports - case 0x40: case 0x42: - return m_port_k[offset >> 1 & 1]; - case 0x41: - return m_dfk0; - - // R output ports - case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: - return m_port_r[offset & 7]; - case 0x7b: - return m_r_dir; - - // P I/O ports - case 0x60: case 0x61: case 0x62: case 0x63: - return read_p(offset & 3); - case 0x7d: - return m_p_dir; - case 0x7e: - return m_p_pullup; - - // clock-timer (lo, hi) - case 0x20: case 0x21: - return m_clktimer_count >> (4 * (offset & 1)) & 0xf; - - // stopwatch timer - case 0x22: - return m_swl_count; - case 0x23: - return m_swh_count; - case 0x77: - return m_stopwatch_on; - - // programmable timer - case 0x24: case 0x25: - return m_prgtimer_count >> (4 * (offset & 1)) & 0xf; - case 0x26: case 0x27: - return m_prgtimer_reload >> (4 * (offset & 1)) & 0xf; - case 0x78: - return m_prgtimer_on; - case 0x79: - return m_prgtimer_select; - - // buzzer - case 0x74: - return m_bz_freq; - case 0x75: - // d3: 1-shot buzzer is on - return m_bz_1shot_on | m_bz_envelope; - - // OSC circuit - case 0x70: - return m_osc; - - // LCD driver - case 0x71: - return m_lcd_control; - case 0x72: - return m_lcd_contrast; - - // SVD circuit (supply voltage detection) - case 0x73: - // d3: criteria voltage* is 0: <=, 1: > source voltage (Vdd-Vss) - // *0,1,2,3: -2.2V, -2.5V, -3.1V, -4.2V, 1 when off - return m_svd | ((m_svd & 4 && m_svd != 7) ? 0 : 8); - - // write-only registers - case 0x76: - break; - - default: - if (!space.debugger_access()) - logerror("%s unknown io_r from $0F%02X at $%04X\n", tag(), offset, m_prev_pc); - break; - } - - return 0; -} - -WRITE8_MEMBER(e0c6s46_device::io_w) -{ - switch (offset) - { - // irq masks - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: - { - static const UINT8 maskmask[6] = { 0xf, 3, 1, 1, 0xf, 0xf }; - m_irqmask[offset-0x10] = data & maskmask[offset-0x10]; - m_possible_irq = true; - break; - } - - // K input ports - case 0x41: - // d0-d3: K0x irq on 0: rising edge, 1: falling edge - m_dfk0 = data; - break; - - // R output ports - case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: - write_r(offset & 7, data); - break; - case 0x7b: - // d0-d3: Rx* direction 0: high-impedance, 1: output - if (data != m_r_dir) - { - m_r_dir = data; - - // refresh outputs - for (int i = 0; i < 5; i++) - write_r(i, m_port_r[i]); - } - break; - - // P I/O ports - case 0x60: case 0x61: case 0x62: case 0x63: - write_p(offset & 3, data); - break; - case 0x7d: - // d0-d3: Px* direction 0: input, 1: output - if (data != m_p_dir) - { - m_p_dir = data; - - // refresh outputs - for (int i = 0; i < 4; i++) - write_p(i, m_port_p[i]); - } - break; - case 0x7e: - // d0-d3: Px* pull up resistor on/off - m_p_pullup = data; - break; - - // OSC circuit - case 0x70: - // d0,d1: CPU operating voltage - // d2: OSC3 on (high freq) - // d3: clock source OSC1 or OSC3 - if (data & 8) - logerror("%s io_w selected OSC3! PC=$%04X\n", tag(), m_prev_pc); - m_osc = data; - break; - - // LCD driver - case 0x71: - // d0: heavy load protection - // d1: duty 1/16 or 1/8 - // d2,d3: all pixels on,off - m_lcd_control = data; - break; - case 0x72: - // contrast adjustment (0=light, 15=dark) - m_lcd_contrast = data; - break; - - // SVD circuit (supply voltage detection) - case 0x73: - // d0,d1: criteria voltage - // d2: on - m_svd = data & 7; - break; - - // clock-timer - case 0x76: - // d0: reset watchdog - // d1: reset clktimer (hw glitch note, not emulated: this also "sometimes"(when??) - // sets the clktimer interrupt and clocks the watchdog) - if (data & 1) - m_watchdog_count = 0; - if (data & 2) - m_clktimer_count = 0; - break; - - // stopwatch timer - case 0x77: - // d0: run/stop counter - // d1: reset stopwatch - m_stopwatch_on = data & 1; - if (data & 2) - { - m_swh_count = 0; - m_swl_count = 0; - m_swl_slice = 0; - } - if (m_stopwatch_on && m_swl_cur_pulse && !m_256_src_pulse) - { - // clock stopwatch on falling edge of pulse+on - m_swl_cur_pulse = 0; - clock_stopwatch(); - } - break; - - // programmable timer - case 0x26: - m_prgtimer_reload = (m_prgtimer_reload & 0xf0) | data; - break; - case 0x27: - m_prgtimer_reload = (m_prgtimer_reload & 0x0f) | data << 4; - break; - - case 0x78: - // d0: run/stop counter - // d1: reset timer - m_prgtimer_on = data & 1; - if (data & 2) - { - m_prgtimer_count = m_prgtimer_reload; - } - if (m_prgtimer_on && (m_prgtimer_select & 7) >= 2 && m_prgtimer_cur_pulse && !m_prgtimer_src_pulse) - { - // if input clock is osc1, clock timer on falling edge of pulse+on - m_prgtimer_cur_pulse = 0; - clock_prgtimer(); - } - break; - - case 0x79: - // d0-d2: input clock select: 2-7 = osc1 divider 256hz-8192hz, - // 0/1 = K03 input (0 enables noise rejector, no need to emulate that) - // d3: output input clock to output port R33 - if ((data & 7) != (m_prgtimer_select & 7)) - { - m_prgtimer_src_pulse = 0; - m_prgtimer_cur_pulse = m_prgtimer_on ^ 1; - m_prgtimer_select = data; - prgtimer_reset_prescaler(); - } - m_prgtimer_select = data; - break; - - // buzzer - case 0x74: - // d0-d2: frequency (8 steps, 4096hz to ~1170hz) - // d3: 1-shot buzzer duration 31.25ms or 62.5ms - m_bz_freq = data; - break; - case 0x75: - // d0: envelope on/off - // d1: envelope cycle selection - // d2: reset envelope - // d3: trigger one-shot buzzer - if (data & 1) - logerror("%s io_w enabled envelope, PC=$%04X\n", tag(), m_prev_pc); - m_bz_envelope = data & 3; - m_bz_1shot_on |= data & 8; - break; - - // read-only registers - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: - case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: - case 0x40: case 0x42: - break; - - default: - if (machine().phase() > MACHINE_PHASE_RESET) - logerror("%s unknown io_w $%X to $0F%02X at $%04X\n", tag(), data, offset, m_prev_pc); - break; - } -} diff --git a/src/devices/cpu/e0c6200/e0c6s46.cpp b/src/devices/cpu/e0c6200/e0c6s46.cpp new file mode 100644 index 00000000000..be131a1e5dc --- /dev/null +++ b/src/devices/cpu/e0c6200/e0c6s46.cpp @@ -0,0 +1,893 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Seiko Epson E0C6S46 MCU + QFP5-128pin, see manual for pinout + + TODO: + - OSC3 + - K input interrupts + - finish i/o ports + - serial interface + - buzzer envelope addition + - add mask options to MCFG (eg. buzzer on output port R4x is optional) + +*/ + +#include "e0c6s46.h" + +enum +{ + IRQREG_CLKTIMER = 0, + IRQREG_STOPWATCH, + IRQREG_PRGTIMER, + IRQREG_SERIAL, + IRQREG_INPUT0, + IRQREG_INPUT1 +}; + +const device_type E0C6S46 = &device_creator; + + +// internal memory maps +static ADDRESS_MAP_START(e0c6s46_program, AS_PROGRAM, 16, e0c6s46_device) + AM_RANGE(0x0000, 0x17ff) AM_ROM +ADDRESS_MAP_END + + +static ADDRESS_MAP_START(e0c6s46_data, AS_DATA, 8, e0c6s46_device) + AM_RANGE(0x0000, 0x027f) AM_RAM + AM_RANGE(0x0e00, 0x0e4f) AM_RAM AM_SHARE("vram1") + AM_RANGE(0x0e80, 0x0ecf) AM_RAM AM_SHARE("vram2") + AM_RANGE(0x0f00, 0x0f7f) AM_READWRITE(io_r, io_w) +ADDRESS_MAP_END + + +// device definitions +e0c6s46_device::e0c6s46_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : e0c6200_cpu_device(mconfig, E0C6S46, "E0C6S46", tag, owner, clock, ADDRESS_MAP_NAME(e0c6s46_program), ADDRESS_MAP_NAME(e0c6s46_data), "e0c6s46", __FILE__) + , m_vram1(*this, "vram1") + , m_vram2(*this, "vram2") + , m_pixel_update_handler(NULL) + , m_write_r0(*this), m_write_r1(*this), m_write_r2(*this), m_write_r3(*this), m_write_r4(*this) + , m_read_p0(*this), m_read_p1(*this), m_read_p2(*this), m_read_p3(*this) + , m_write_p0(*this), m_write_p1(*this), m_write_p2(*this), m_write_p3(*this) +{ } + + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void e0c6s46_device::device_start() +{ + e0c6200_cpu_device::device_start(); + + // find ports + m_write_r0.resolve_safe(); + m_write_r1.resolve_safe(); + m_write_r2.resolve_safe(); + m_write_r3.resolve_safe(); + m_write_r4.resolve_safe(); + + m_read_p0.resolve_safe(0); + m_read_p1.resolve_safe(0); + m_read_p2.resolve_safe(0); + m_read_p3.resolve_safe(0); + m_write_p0.resolve_safe(); + m_write_p1.resolve_safe(); + m_write_p2.resolve_safe(); + m_write_p3.resolve_safe(); + + // create timers + m_core_256_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::core_256_cb), this)); + m_core_256_handle->adjust(attotime::from_ticks(64, unscaled_clock())); + m_prgtimer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::prgtimer_cb), this)); + m_prgtimer_handle->adjust(attotime::never); + m_buzzer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::buzzer_cb), this)); + m_buzzer_handle->adjust(attotime::never); + + // zerofill + memset(m_port_r, 0x0, sizeof(m_port_r)); + m_r_dir = 0; + memset(m_port_p, 0x0, sizeof(m_port_p)); + m_p_dir = 0; + m_p_pullup = 0; + memset(m_port_k, 0xf, sizeof(m_port_k)); + m_dfk0 = 0xf; + + memset(m_irqflag, 0, sizeof(m_irqflag)); + memset(m_irqmask, 0, sizeof(m_irqmask)); + m_osc = 0; + m_svd = 0; + m_lcd_control = 0; + m_lcd_contrast = 0; + + m_256_src_pulse = 0; + m_watchdog_count = 0; + m_clktimer_count = 0; + + m_stopwatch_on = 0; + m_swl_cur_pulse = 0; + m_swl_slice = 0; + m_swl_count = 0; + m_swh_count = 0; + + m_prgtimer_select = 0; + m_prgtimer_on = 0; + m_prgtimer_src_pulse = 0; + m_prgtimer_cur_pulse = 0; + m_prgtimer_count = 0; + m_prgtimer_reload = 0; + + m_bz_43_on = 0; + m_bz_freq = 0; + m_bz_envelope = 0; + m_bz_duty_ratio = 0; + m_bz_1shot_on = 0; + m_bz_1shot_running = false; + m_bz_1shot_count = 0; + m_bz_pulse = 0; + + // register for savestates + save_item(NAME(m_port_r)); + save_item(NAME(m_r_dir)); + save_item(NAME(m_port_p)); + save_item(NAME(m_p_dir)); + save_item(NAME(m_p_pullup)); + save_item(NAME(m_port_k)); + save_item(NAME(m_dfk0)); + + save_item(NAME(m_irqflag)); + save_item(NAME(m_irqmask)); + save_item(NAME(m_osc)); + save_item(NAME(m_svd)); + save_item(NAME(m_lcd_control)); + save_item(NAME(m_lcd_contrast)); + + save_item(NAME(m_256_src_pulse)); + save_item(NAME(m_watchdog_count)); + save_item(NAME(m_clktimer_count)); + + save_item(NAME(m_stopwatch_on)); + save_item(NAME(m_swl_cur_pulse)); + save_item(NAME(m_swl_slice)); + save_item(NAME(m_swl_count)); + save_item(NAME(m_swh_count)); + + save_item(NAME(m_prgtimer_select)); + save_item(NAME(m_prgtimer_on)); + save_item(NAME(m_prgtimer_src_pulse)); + save_item(NAME(m_prgtimer_cur_pulse)); + save_item(NAME(m_prgtimer_count)); + save_item(NAME(m_prgtimer_reload)); + + save_item(NAME(m_bz_43_on)); + save_item(NAME(m_bz_freq)); + save_item(NAME(m_bz_envelope)); + save_item(NAME(m_bz_duty_ratio)); + save_item(NAME(m_bz_1shot_on)); + save_item(NAME(m_bz_1shot_running)); + save_item(NAME(m_bz_1shot_count)); + save_item(NAME(m_bz_pulse)); +} + + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void e0c6s46_device::device_reset() +{ + e0c6200_cpu_device::device_reset(); + + // reset interrupts + memset(m_irqflag, 0, sizeof(m_irqflag)); + memset(m_irqmask, 0, sizeof(m_irqmask)); + + // reset other i/o + m_data->write_byte(0xf41, 0xf); + m_data->write_byte(0xf54, 0xf); + m_data->write_byte(0xf70, 0x0); + m_data->write_byte(0xf71, 0x8); + m_data->write_byte(0xf73, m_svd & 0xc0); + + m_data->write_byte(0xf74, 0x0); + m_data->write_byte(0xf75, 0x4); + m_data->write_byte(0xf76, 0x3); + m_data->write_byte(0xf77, 0x2); + m_data->write_byte(0xf78, 0x2); + m_data->write_byte(0xf79, 0x0); + m_data->write_byte(0xf7a, 0x0); + m_data->write_byte(0xf7b, 0x0); + m_data->write_byte(0xf7d, 0x0); + m_data->write_byte(0xf7e, 0x0); + + // reset ports + for (int i = 0; i < 5; i++) + write_r(i, m_port_r[i]); + for (int i = 0; i < 4; i++) + write_p(i, m_port_p[i]); +} + + + +//------------------------------------------------- +// execute +//------------------------------------------------- + +void e0c6s46_device::execute_one() +{ + // E0C6S46 has no support for SLP opcode + if (m_op == 0xff9) + return; + + e0c6200_cpu_device::execute_one(); +} + + + +//------------------------------------------------- +// interrupts +//------------------------------------------------- + +bool e0c6s46_device::check_interrupt() +{ + // priority order is not the same as register order + static const int priorder[6] = + { + IRQREG_CLKTIMER, + IRQREG_STOPWATCH, + IRQREG_INPUT0, + IRQREG_INPUT1, + IRQREG_SERIAL, + IRQREG_PRGTIMER, + }; + + // check interrupts from high to low priority + for (int pri = 5; pri >= 0; pri--) + { + // hw glitch note, not emulated: if a new interrupt is requested in the + // middle of handling this interrupt, irq vector may be an OR of 2 vectors + m_irq_vector = 2*pri + 2; + int reg = priorder[pri]; + m_irq_id = reg; + + switch (reg) + { + // other: mask vs flag + default: + if (m_irqflag[reg] & m_irqmask[reg]) + return true; + break; + } + } + + return false; +} + +void e0c6s46_device::execute_set_input(int line, int state) +{ + // only support 8 K input lines at the moment + if (line < 0 || line > 7) + return; + + state = (state) ? 1 : 0; + int port = line >> 2 & 1; + UINT8 bit = 1 << (line & 3); + + m_port_k[port] = (m_port_k[port] & ~bit) | (state ? bit : 0); +} + + + +//------------------------------------------------- +// ports +//------------------------------------------------- + +// R output ports + +void e0c6s46_device::write_r(UINT8 port, UINT8 data) +{ + data &= 0xf; + m_port_r[port] = data; + + // ports R0x-R3x can be high-impedance + UINT8 out = data; + if (port < 4 && !(m_r_dir >> port & 1)) + out = 0xf; + + switch (port) + { + case 0: m_write_r0(port, out, 0xff); break; + case 1: m_write_r1(port, out, 0xff); break; + case 2: m_write_r2(port, out, 0xff); break; + case 3: m_write_r3(port, out, 0xff); break; // TODO: R33 PTCLK/_SRDY + + // R4x: special output + case 4: + // d3: buzzer on: direct output or 1-shot output + if ((data & 8) != m_bz_43_on) + { + m_bz_43_on = data & 8; + reset_buzzer(); + } + write_r4_out(); + break; + } +} + +void e0c6s46_device::write_r4_out() +{ + // R40: _FOUT(clock inverted output) + // R42: FOUT or _BZ + // R43: BZ(buzzer) + UINT8 out = (m_port_r[4] & 2) | (m_bz_pulse << 3) | (m_bz_pulse << 2 ^ 4); + m_write_r4(4, out, 0xff); +} + + +// P I/O ports + +void e0c6s46_device::write_p(UINT8 port, UINT8 data) +{ + data &= 0xf; + m_port_p[port] = data; + + // don't output if port direction is set to input + if (!(m_p_dir >> port & 1)) + return; + + switch (port) + { + case 0: m_write_p0(port, data, 0xff); break; + case 1: m_write_p1(port, data, 0xff); break; + case 2: m_write_p2(port, data, 0xff); break; + case 3: m_write_p3(port, data, 0xff); break; + } +} + +UINT8 e0c6s46_device::read_p(UINT8 port) +{ + // return written value if port direction is set to output + if (m_p_dir >> port & 1) + return m_port_p[port]; + + switch (port) + { + case 0: return m_read_p0(port, 0xff); + case 1: return m_read_p1(port, 0xff); + case 2: return m_read_p2(port, 0xff); + case 3: return m_read_p3(port, 0xff); + } + + return 0; +} + + + +//------------------------------------------------- +// timers +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(e0c6s46_device::core_256_cb) +{ + // clock-timer, stopwatch timer, and some features of the buzzer all run + // from the same internal 256hz timer (64 ticks high+low at default clock of 32768hz) + m_256_src_pulse ^= 1; + m_core_256_handle->adjust(attotime::from_ticks(64, unscaled_clock())); + + // clock stopwatch on falling edge of pulse+on + m_swl_cur_pulse = m_256_src_pulse | (m_stopwatch_on ^ 1); + if (m_swl_cur_pulse == 0) + clock_stopwatch(); + + // clock 1-shot buzzer on rising edge if it's on + if (m_bz_1shot_on != 0 && m_256_src_pulse == 1) + clock_bz_1shot(); + + // clock-timer is always running, advance it on falling edge + // (handle clock_clktimer last in case of watchdog reset) + if (m_256_src_pulse == 0) + clock_clktimer(); +} + + +// clock-timer + +void e0c6s46_device::clock_watchdog() +{ + // initial reset after 3 to 4 seconds + if (++m_watchdog_count == 4) + { + logerror("%s watchdog reset\n", tag()); + m_watchdog_count = 0; + device_reset(); + } +} + +void e0c6s46_device::clock_clktimer() +{ + m_clktimer_count++; + + // irq on falling edge of 32, 8, 2, 1hz + UINT8 flag = 0; + if ((m_clktimer_count & 0x07) == 0) + flag |= 1; + if ((m_clktimer_count & 0x1f) == 0) + flag |= 2; + if ((m_clktimer_count & 0x7f) == 0) + flag |= 4; + if (m_clktimer_count == 0) + flag |= 8; + + m_irqflag[IRQREG_CLKTIMER] |= flag; + if (m_irqflag[IRQREG_CLKTIMER] & m_irqmask[IRQREG_CLKTIMER]) + m_possible_irq = true; + + // 1hz falling edge also clocks the watchdog timer + if (m_clktimer_count == 0) + clock_watchdog(); +} + + +// stopwatch timer + +void e0c6s46_device::clock_stopwatch() +{ + m_swl_slice++; + + // 1 slice is 3 ticks(256hz) on even and 2 ticks on uneven counts, + // but from count 1 to 2 it's 3 ticks, 6 out of 100 times, to make + // exactly 26/256hz * 6 + 25/256hz * 4 = 1 second + int swl_next = 3 - (m_swl_count & 1); + if (m_swl_count == 1 && !(m_swh_count >> 1 & 1)) + swl_next = 3; + + if (m_swl_slice == swl_next) + { + m_swl_slice = 0; + + // bcd counter, irq on falling edge of 10 and 1hz + m_swl_count = (m_swl_count + 1) % 10; + if (m_swl_count == 0) + { + m_irqflag[IRQREG_STOPWATCH] |= 1; + m_swh_count = (m_swh_count + 1) % 10; + if (m_swh_count == 0) + m_irqflag[IRQREG_STOPWATCH] |= 2; + } + + if (m_irqflag[IRQREG_STOPWATCH] & m_irqmask[IRQREG_STOPWATCH]) + m_possible_irq = true; + } +} + + +// programmable timer + +void e0c6s46_device::clock_prgtimer() +{ + // irq and reload when it reaches zero + if (--m_prgtimer_count == 0) + { + m_irqflag[IRQREG_PRGTIMER] |= 1; + if (m_irqflag[IRQREG_PRGTIMER] & m_irqmask[IRQREG_PRGTIMER]) + m_possible_irq = true; + + // note: a reload of 0 indicates a 256-counter + m_prgtimer_count = m_prgtimer_reload; + } +} + +bool e0c6s46_device::prgtimer_reset_prescaler() +{ + // only 2 to 7 are clock dividers + UINT8 sel = m_prgtimer_select & 7; + if (sel >= 2) + m_prgtimer_handle->adjust(attotime::from_ticks(2 << (sel ^ 7), unscaled_clock())); + + return (sel >= 2); +} + +TIMER_CALLBACK_MEMBER(e0c6s46_device::prgtimer_cb) +{ + // check if it's clocked by osc1, schedule next timeout + if (!prgtimer_reset_prescaler()) + return; + + m_prgtimer_src_pulse ^= 1; + m_prgtimer_cur_pulse = m_prgtimer_src_pulse | (m_prgtimer_on ^ 1); + + // clock prgtimer on falling edge of pulse+on + if (m_prgtimer_cur_pulse == 0) + clock_prgtimer(); +} + + +// buzzer + +void e0c6s46_device::schedule_buzzer() +{ + // only schedule next buzzer timeout if it's on + if (m_bz_43_on != 0 && !m_bz_1shot_running) + return; + + // pulse width differs per frequency selection + int mul = (m_bz_freq & 4) ? 1 : 2; + int high = (m_bz_freq & 2) ? 12 : 8; + int low = 16 + (m_bz_freq << 2 & 0xc); + + // pulse width envelope if it's on + if (m_bz_envelope & 1) + high -= m_bz_duty_ratio; + low -= high; + + m_buzzer_handle->adjust(attotime::from_ticks(m_bz_pulse ? high : low, mul * unscaled_clock())); +} + +TIMER_CALLBACK_MEMBER(e0c6s46_device::buzzer_cb) +{ + // invert pulse wave and write to output + m_bz_pulse ^= 1; + write_r4_out(); + + schedule_buzzer(); +} + +void e0c6s46_device::reset_buzzer() +{ + // don't reset if the timer is running + if (m_buzzer_handle->remaining() == attotime::never) + schedule_buzzer(); +} + +void e0c6s46_device::clock_bz_1shot() +{ + m_bz_1shot_running = true; + + // reload counter the 1st time + if (m_bz_1shot_count == 0) + { + reset_buzzer(); + m_bz_1shot_count = (m_bz_freq & 8) ? 16 : 8; + } + + // stop ringing when counter reaches 0 + else if (--m_bz_1shot_count == 0) + { + m_bz_1shot_on = 0; + m_bz_1shot_running = false; + } +} + + + +//------------------------------------------------- +// LCD Driver +//------------------------------------------------- + +UINT32 e0c6s46_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + // call this 32 times per second (osc1/1024: 32hz at default clock of 32768hz) + for (int bank = 0; bank < 2; bank++) + { + const UINT8* vram = bank ? m_vram2 : m_vram1; + + // determine operating mode + bool lcd_on = false; + int pixel = 0; + if (m_lcd_control & 8 || (bank == 1 && m_lcd_control & 2)) + pixel = 0; + else if (m_lcd_control & 4) + pixel = 1; + else + lcd_on = true; + + // draw pixels + for (int offset = 0; offset < 0x50; offset++) + { + for (int c = 0; c < 4; c++) + { + if (lcd_on) + pixel = vram[offset] >> c & 1; + + // 16 COM(common) pins, 40 SEG(segment) pins + int seg = offset / 2; + int com = bank * 8 + (offset & 1) * 4 + c; + + if (m_pixel_update_handler != NULL) + m_pixel_update_handler(*this, bitmap, cliprect, m_lcd_contrast, seg, com, pixel); + else if (cliprect.contains(seg, com)) + bitmap.pix16(com, seg) = pixel; + } + } + } + + return 0; +} + + + +//------------------------------------------------- +// internal I/O +//------------------------------------------------- + +READ8_MEMBER(e0c6s46_device::io_r) +{ + switch (offset) + { + // irq flags, masks + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + { + // irq flags are reset(acked) when read + UINT8 flag = m_irqflag[offset]; + if (!space.debugger_access()) + m_irqflag[offset] = 0; + return flag; + } + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: + return m_irqmask[offset-0x10]; + + // K input ports + case 0x40: case 0x42: + return m_port_k[offset >> 1 & 1]; + case 0x41: + return m_dfk0; + + // R output ports + case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: + return m_port_r[offset & 7]; + case 0x7b: + return m_r_dir; + + // P I/O ports + case 0x60: case 0x61: case 0x62: case 0x63: + return read_p(offset & 3); + case 0x7d: + return m_p_dir; + case 0x7e: + return m_p_pullup; + + // clock-timer (lo, hi) + case 0x20: case 0x21: + return m_clktimer_count >> (4 * (offset & 1)) & 0xf; + + // stopwatch timer + case 0x22: + return m_swl_count; + case 0x23: + return m_swh_count; + case 0x77: + return m_stopwatch_on; + + // programmable timer + case 0x24: case 0x25: + return m_prgtimer_count >> (4 * (offset & 1)) & 0xf; + case 0x26: case 0x27: + return m_prgtimer_reload >> (4 * (offset & 1)) & 0xf; + case 0x78: + return m_prgtimer_on; + case 0x79: + return m_prgtimer_select; + + // buzzer + case 0x74: + return m_bz_freq; + case 0x75: + // d3: 1-shot buzzer is on + return m_bz_1shot_on | m_bz_envelope; + + // OSC circuit + case 0x70: + return m_osc; + + // LCD driver + case 0x71: + return m_lcd_control; + case 0x72: + return m_lcd_contrast; + + // SVD circuit (supply voltage detection) + case 0x73: + // d3: criteria voltage* is 0: <=, 1: > source voltage (Vdd-Vss) + // *0,1,2,3: -2.2V, -2.5V, -3.1V, -4.2V, 1 when off + return m_svd | ((m_svd & 4 && m_svd != 7) ? 0 : 8); + + // write-only registers + case 0x76: + break; + + default: + if (!space.debugger_access()) + logerror("%s unknown io_r from $0F%02X at $%04X\n", tag(), offset, m_prev_pc); + break; + } + + return 0; +} + +WRITE8_MEMBER(e0c6s46_device::io_w) +{ + switch (offset) + { + // irq masks + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: + { + static const UINT8 maskmask[6] = { 0xf, 3, 1, 1, 0xf, 0xf }; + m_irqmask[offset-0x10] = data & maskmask[offset-0x10]; + m_possible_irq = true; + break; + } + + // K input ports + case 0x41: + // d0-d3: K0x irq on 0: rising edge, 1: falling edge + m_dfk0 = data; + break; + + // R output ports + case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: + write_r(offset & 7, data); + break; + case 0x7b: + // d0-d3: Rx* direction 0: high-impedance, 1: output + if (data != m_r_dir) + { + m_r_dir = data; + + // refresh outputs + for (int i = 0; i < 5; i++) + write_r(i, m_port_r[i]); + } + break; + + // P I/O ports + case 0x60: case 0x61: case 0x62: case 0x63: + write_p(offset & 3, data); + break; + case 0x7d: + // d0-d3: Px* direction 0: input, 1: output + if (data != m_p_dir) + { + m_p_dir = data; + + // refresh outputs + for (int i = 0; i < 4; i++) + write_p(i, m_port_p[i]); + } + break; + case 0x7e: + // d0-d3: Px* pull up resistor on/off + m_p_pullup = data; + break; + + // OSC circuit + case 0x70: + // d0,d1: CPU operating voltage + // d2: OSC3 on (high freq) + // d3: clock source OSC1 or OSC3 + if (data & 8) + logerror("%s io_w selected OSC3! PC=$%04X\n", tag(), m_prev_pc); + m_osc = data; + break; + + // LCD driver + case 0x71: + // d0: heavy load protection + // d1: duty 1/16 or 1/8 + // d2,d3: all pixels on,off + m_lcd_control = data; + break; + case 0x72: + // contrast adjustment (0=light, 15=dark) + m_lcd_contrast = data; + break; + + // SVD circuit (supply voltage detection) + case 0x73: + // d0,d1: criteria voltage + // d2: on + m_svd = data & 7; + break; + + // clock-timer + case 0x76: + // d0: reset watchdog + // d1: reset clktimer (hw glitch note, not emulated: this also "sometimes"(when??) + // sets the clktimer interrupt and clocks the watchdog) + if (data & 1) + m_watchdog_count = 0; + if (data & 2) + m_clktimer_count = 0; + break; + + // stopwatch timer + case 0x77: + // d0: run/stop counter + // d1: reset stopwatch + m_stopwatch_on = data & 1; + if (data & 2) + { + m_swh_count = 0; + m_swl_count = 0; + m_swl_slice = 0; + } + if (m_stopwatch_on && m_swl_cur_pulse && !m_256_src_pulse) + { + // clock stopwatch on falling edge of pulse+on + m_swl_cur_pulse = 0; + clock_stopwatch(); + } + break; + + // programmable timer + case 0x26: + m_prgtimer_reload = (m_prgtimer_reload & 0xf0) | data; + break; + case 0x27: + m_prgtimer_reload = (m_prgtimer_reload & 0x0f) | data << 4; + break; + + case 0x78: + // d0: run/stop counter + // d1: reset timer + m_prgtimer_on = data & 1; + if (data & 2) + { + m_prgtimer_count = m_prgtimer_reload; + } + if (m_prgtimer_on && (m_prgtimer_select & 7) >= 2 && m_prgtimer_cur_pulse && !m_prgtimer_src_pulse) + { + // if input clock is osc1, clock timer on falling edge of pulse+on + m_prgtimer_cur_pulse = 0; + clock_prgtimer(); + } + break; + + case 0x79: + // d0-d2: input clock select: 2-7 = osc1 divider 256hz-8192hz, + // 0/1 = K03 input (0 enables noise rejector, no need to emulate that) + // d3: output input clock to output port R33 + if ((data & 7) != (m_prgtimer_select & 7)) + { + m_prgtimer_src_pulse = 0; + m_prgtimer_cur_pulse = m_prgtimer_on ^ 1; + m_prgtimer_select = data; + prgtimer_reset_prescaler(); + } + m_prgtimer_select = data; + break; + + // buzzer + case 0x74: + // d0-d2: frequency (8 steps, 4096hz to ~1170hz) + // d3: 1-shot buzzer duration 31.25ms or 62.5ms + m_bz_freq = data; + break; + case 0x75: + // d0: envelope on/off + // d1: envelope cycle selection + // d2: reset envelope + // d3: trigger one-shot buzzer + if (data & 1) + logerror("%s io_w enabled envelope, PC=$%04X\n", tag(), m_prev_pc); + m_bz_envelope = data & 3; + m_bz_1shot_on |= data & 8; + break; + + // read-only registers + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: + case 0x40: case 0x42: + break; + + default: + if (machine().phase() > MACHINE_PHASE_RESET) + logerror("%s unknown io_w $%X to $0F%02X at $%04X\n", tag(), data, offset, m_prev_pc); + break; + } +} -- cgit v1.2.3-70-g09d2