summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/cpu/melps4/melps4op.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/cpu/melps4/melps4op.c')
-rw-r--r--src/emu/cpu/melps4/melps4op.c699
1 files changed, 699 insertions, 0 deletions
diff --git a/src/emu/cpu/melps4/melps4op.c b/src/emu/cpu/melps4/melps4op.c
new file mode 100644
index 00000000000..4f77764a66b
--- /dev/null
+++ b/src/emu/cpu/melps4/melps4op.c
@@ -0,0 +1,699 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+
+// MELPS 4 opcode handlers
+
+#include "melps4.h"
+
+
+// internal helpers
+
+inline UINT8 melps4_cpu_device::ram_r()
+{
+ UINT8 address = (m_z << 6 | m_x << 4 | m_y) & m_datamask;
+ return m_data->read_byte(address) & 0xf;
+}
+
+inline void melps4_cpu_device::ram_w(UINT8 data)
+{
+ UINT8 address = (m_z << 6 | m_x << 4 | m_y) & m_datamask;
+ m_data->write_byte(address, data & 0xf);
+}
+
+void melps4_cpu_device::pop_pc()
+{
+ m_pc = m_stack[0];
+ for (int i = 0; i < m_stack_levels-1; i++)
+ m_stack[i] = m_stack[i+1];
+}
+
+void melps4_cpu_device::push_pc()
+{
+ for (int i = m_stack_levels-1; i >= 1; i--)
+ m_stack[i] = m_stack[i-1];
+ m_stack[0] = m_pc;
+}
+
+
+// Register-to-register transfers
+
+void melps4_cpu_device::op_tab()
+{
+ // TAB: transfer B to A
+ m_a = m_b;
+}
+
+void melps4_cpu_device::op_tba()
+{
+ // TBA: transfer A to B
+ m_b = m_a;
+}
+
+void melps4_cpu_device::op_tay()
+{
+ // TAY: transfer Y to A
+ m_a = m_y;
+}
+
+void melps4_cpu_device::op_tya()
+{
+ // TYA: transfer A to Y
+ m_y = m_a;
+}
+
+void melps4_cpu_device::op_teab()
+{
+ // TEAB: transfer A and B to E
+ m_e = m_b << 4 | m_a;
+}
+
+void melps4_cpu_device::op_tabe()
+{
+ // TABE(undocumented): transfer E to A and B
+ m_a = m_e & 0xf;
+ m_b = m_e >> 4;
+}
+
+void melps4_cpu_device::op_tepa()
+{
+ // TEPA: decode A by PLA and transfer to E
+ op_illegal();
+}
+
+void melps4_cpu_device::op_txa()
+{
+ // TXA: transfer bits 0,1 of A to X, inverted bit 2 to Z, inverted bit 3 to carry
+ op_illegal();
+}
+
+void melps4_cpu_device::op_tax()
+{
+ // TAX: transfer X to bits 0,1 of A, inverted Z to bit 2, inverted carry to bit 3
+ op_illegal();
+}
+
+
+// RAM addresses
+
+void melps4_cpu_device::op_lxy()
+{
+ // LXY x,y: load immediate into X,Y, skip any next LXY
+ m_prohibit_irq = true;
+ if ((m_op & ~0x3f) != (m_prev_op & ~0x3f))
+ {
+ m_x = m_op >> 4 & 3;
+ m_y = m_op & 0xf;
+ }
+}
+
+void melps4_cpu_device::op_lz()
+{
+ // LZ z: load immediate into Z
+ m_z = m_op & 1;
+}
+
+void melps4_cpu_device::op_iny()
+{
+ // INY: increment Y, skip next on overflow
+ m_y = (m_y + 1) & 0xf;
+ m_skip = (m_y == 0);
+}
+
+void melps4_cpu_device::op_dey()
+{
+ // DEY: decrement Y, skip next on overflow
+ m_y = (m_y - 1) & 0xf;
+ m_skip = (m_y == 0xf);
+}
+
+void melps4_cpu_device::op_lcps()
+{
+ // LCPS i: choose active DP,CY or DP',CY'
+ if ((m_op & 1) != m_cps)
+ {
+ m_cps = m_op & 1;
+
+ // swap registers
+ UINT8 x, y, z, cy;
+ x = m_x;
+ y = m_y;
+ z = m_z;
+ cy = m_cy;
+
+ m_x = m_x2;
+ m_y = m_y2;
+ m_z = m_z2;
+ m_cy = m_cy2;
+
+ m_x2 = x;
+ m_y2 = y;
+ m_z2 = z;
+ m_cy2 = cy;
+ }
+}
+
+void melps4_cpu_device::op_sadr()
+{
+ // SADR j: ..
+ op_illegal();
+}
+
+
+// RAM-accumulator transfers
+
+void melps4_cpu_device::op_tam()
+{
+ // TAM j: transfer RAM to A, xor X with j
+ m_a = ram_r();
+ m_x ^= m_op & 3;
+}
+
+void melps4_cpu_device::op_xam()
+{
+ // XAM j: exchange RAM with A, xor X with j
+ UINT8 a = m_a;
+ m_a = ram_r();
+ ram_w(a);
+ m_x ^= m_op & 3;
+}
+
+void melps4_cpu_device::op_xamd()
+{
+ // XAMD j: XAM j, DEY
+ op_xam();
+ op_dey();
+}
+
+void melps4_cpu_device::op_xami()
+{
+ // XAMI j: XAM j, skip next on Y mask(default 0xf), increment Y
+ op_xam();
+ m_skip = ((m_y & m_xami_mask) == m_xami_mask);
+ m_y = (m_y + 1) & 0xf;
+}
+
+
+// Arithmetic Operations
+
+void melps4_cpu_device::op_la()
+{
+ // LA n: load immediate into A, skip any next LA
+ m_prohibit_irq = true;
+ if ((m_op & ~0xf) != (m_prev_op & ~0xf))
+ m_a = m_op & 0xf;
+}
+
+void melps4_cpu_device::op_am()
+{
+ // AM: add RAM to A
+ m_a = (m_a + ram_r()) & 0xf;
+}
+
+void melps4_cpu_device::op_amc()
+{
+ // AMC: add RAM+CY to A and CY
+ m_a += ram_r() + m_cy;
+ m_cy = m_a >> 4 & 1;
+ m_a &= 0xf;
+}
+
+void melps4_cpu_device::op_amcs()
+{
+ // AMCS: AMC, skip next on carry
+ op_amc();
+ m_skip = (m_cy != 0);
+}
+
+void melps4_cpu_device::op_a()
+{
+ // A n: add immediate to A, skip next on no carry (except when n=6)
+ UINT8 n = m_op & 0xf;
+ m_a += n;
+ m_skip = !(m_a & 0x10 || n == 6);
+ m_a &= 0xf;
+}
+
+void melps4_cpu_device::op_sc()
+{
+ // SC: set carry
+ m_cy = 1;
+}
+
+void melps4_cpu_device::op_rc()
+{
+ // RC: reset carry
+ m_cy = 0;
+}
+
+void melps4_cpu_device::op_szc()
+{
+ // SZC: skip next on no carry
+ m_skip = !m_cy;
+}
+
+void melps4_cpu_device::op_cma()
+{
+ // CMA: complement A
+ m_a ^= 0xf;
+}
+
+void melps4_cpu_device::op_rl()
+{
+ // RL(undocumented): rotate A left through carry
+ UINT8 c = m_a >> 3 & 1;
+ m_a = (m_a << 1 | m_cy) & 0xf;
+ m_cy = c;
+}
+
+void melps4_cpu_device::op_rr()
+{
+ // RR(undocumented): rotate A right through carry
+ UINT8 c = m_a & 1;
+ m_a = m_a >> 1 | m_cy << 3;
+ m_cy = c;
+}
+
+
+// Bit operations
+
+void melps4_cpu_device::op_sb()
+{
+ // SB j: set RAM bit
+ ram_w(ram_r() | m_bitmask);
+}
+
+void melps4_cpu_device::op_rb()
+{
+ // RB j: reset RAM bit
+ ram_w(ram_r() & ~m_bitmask);
+}
+
+void melps4_cpu_device::op_szb()
+{
+ // SZB j: skip next if RAM bit is 0
+ m_skip = !(ram_r() & m_bitmask);
+}
+
+
+// Compares
+
+void melps4_cpu_device::op_seam()
+{
+ // SEAM: skip next if A equals RAM
+ m_skip = (m_a == ram_r());
+}
+
+void melps4_cpu_device::op_sey()
+{
+ // SEY y: skip next if Y equals immediate
+ m_skip = (m_y == (m_op & 0xf));
+}
+
+
+// A/D converter operations
+
+void melps4_cpu_device::op_tla()
+{
+ // TLA: transfer A to L
+ m_l = m_a;
+}
+
+void melps4_cpu_device::op_tha()
+{
+ // THA: transfer A to H
+ m_h = m_a;
+}
+
+void melps4_cpu_device::op_taj()
+{
+ // TAJ: transfer J(hi/lo) to A designated by Y
+ op_illegal();
+}
+
+void melps4_cpu_device::op_xal()
+{
+ // XAL: exchange A with L
+ UINT8 a = m_a;
+ m_a = m_l;
+ m_l = a;
+}
+
+void melps4_cpu_device::op_xah()
+{
+ // XAH: exchange A with H
+ UINT8 a = m_a;
+ m_a = m_h;
+ m_h = a;
+}
+
+void melps4_cpu_device::op_lc7()
+{
+ // LC7: load 7 into C
+ m_c = 7;
+}
+
+void melps4_cpu_device::op_dec()
+{
+ // DEC: decrement C, skip next on overflow
+ m_c = (m_c - 1) & 7;
+ m_skip = (m_c == 7);
+}
+
+void melps4_cpu_device::op_shl()
+{
+ // SHL: set bit in L or H designated by C
+ UINT8 mask = 1 << (m_c & 3);
+ if (m_c & 4)
+ m_h |= mask;
+ else
+ m_l |= mask;
+}
+
+void melps4_cpu_device::op_rhl()
+{
+ // RHL: reset bit in L or H designated by C
+ UINT8 mask = 1 << (m_c & 3);
+ if (m_c & 4)
+ m_h &= ~mask;
+ else
+ m_l &= ~mask;
+}
+
+void melps4_cpu_device::op_cpa()
+{
+ // CPA: ..
+ op_illegal();
+}
+
+void melps4_cpu_device::op_cpas()
+{
+ // CPAS: ..
+ op_illegal();
+}
+
+void melps4_cpu_device::op_cpae()
+{
+ // CPAE: ..
+ op_illegal();
+}
+
+void melps4_cpu_device::op_szj()
+{
+ // SZJ: skip next if J bit designated by Y is 0
+ op_illegal();
+}
+
+
+// Timer instruction
+
+void melps4_cpu_device::op_t1ab()
+{
+ // T1AB: transfer A and B to timer 1
+ m_tmr_count[0] = m_b << 4 | m_a;
+}
+
+void melps4_cpu_device::op_trab()
+{
+ // TRAB: transfer A and B to timer 2 reload
+ m_tmr_reload = m_b << 4 | m_a;
+}
+
+void melps4_cpu_device::op_t2ab()
+{
+ // T2AB: transfer A and B to timer 2 and timer 2 reload
+ m_tmr_reload = m_tmr_count[1] = m_b << 4 | m_a;
+}
+
+void melps4_cpu_device::op_tab1()
+{
+ // TAB1: transfer timer 1 to A and B
+ m_a = m_tmr_count[0] & 0xf;
+ m_b = m_tmr_count[0] >> 4;
+}
+
+void melps4_cpu_device::op_tabr()
+{
+ // TABR: transfer timer 2 reload to A and B
+ m_a = m_tmr_reload & 0xf;
+ m_b = m_tmr_reload >> 4;
+}
+
+void melps4_cpu_device::op_tab2()
+{
+ // TAB2: transfer timer 2 to A and B
+ m_a = m_tmr_count[1] & 0xf;
+ m_b = m_tmr_count[1] >> 4;
+}
+
+void melps4_cpu_device::op_tva()
+{
+ // TVA: transfer A to timer control V
+ write_v(m_a);
+}
+
+void melps4_cpu_device::op_twa()
+{
+ // TWA: transfer A to timer control W
+ write_w(m_a);
+}
+
+void melps4_cpu_device::op_snz1()
+{
+ // SNZ1: skip next on flag 1F
+ m_skip = m_irqflag[1];
+ m_irqflag[1] = false;
+}
+
+void melps4_cpu_device::op_snz2()
+{
+ // SNZ2: skip next on flag 2F
+ m_skip = m_irqflag[2];
+ m_irqflag[2] = false;
+}
+
+
+// Jumps
+
+void melps4_cpu_device::op_ba()
+{
+ // BA: indicate next branch is indirect
+ m_prohibit_irq = true;
+ m_ba_flag = true;
+}
+
+void melps4_cpu_device::op_sp()
+{
+ // SP: set page for next branch
+ // note: mnemonic is guessed, manual names it BL or BML
+ m_prohibit_irq = true;
+ m_sp_param = m_op & 0xf;
+}
+
+void melps4_cpu_device::op_b()
+{
+ // B xy: branch
+ m_prohibit_irq = true;
+
+ // determine new page:
+ // - short call: subroutine page
+ // - short jump: current page, or sub. page + 1 when in sub. mode
+ // - long jump/call(B/BM preceded by SP): temp SP register
+ UINT8 page = m_pc >> 7;
+ if ((m_prev_op & ~0xf) == m_sp_mask)
+ {
+ m_sm = false;
+ page = m_sp_param;
+ }
+ else if (m_sm)
+ page = m_sm_page | (m_op >> 7 & 1);
+
+ m_pc = page << 7 | (m_op & 0x7f);
+
+ // if BA opcode was executed, set PC low 4 bits to A
+ if (m_ba_flag)
+ {
+ m_ba_flag = false;
+ m_pc = (m_pc & ~0xf) | m_a;
+ }
+}
+
+void melps4_cpu_device::op_bm()
+{
+ // BM xy call subroutine
+ // don't push stack on short calls when in subroutine mode
+ if (!m_sm || (m_prev_op & ~0xf) == m_sp_mask)
+ push_pc();
+
+ // set subroutine mode - it is reset after long jump/call or return
+ m_sm = true;
+ op_b();
+}
+
+
+// Program returns
+
+void melps4_cpu_device::op_rt()
+{
+ // RT: return from subroutine
+ m_prohibit_irq = true;
+ m_sm = false;
+ pop_pc();
+}
+
+void melps4_cpu_device::op_rts()
+{
+ // RTS: RT, skip next
+ op_rt();
+ m_skip = true;
+}
+
+void melps4_cpu_device::op_rti()
+{
+ // RTI: return from interrupt routine
+ op_rt();
+ m_sm = m_sms;
+}
+
+
+// Input/Output
+
+void melps4_cpu_device::op_cld()
+{
+ // CLD: clear port D
+ write_d_pin(MELPS4_PORTD_CLR, 0);
+}
+
+void melps4_cpu_device::op_cls()
+{
+ // CLS: clear port S
+ write_gen_port(MELPS4_PORTS, 0);
+}
+
+void melps4_cpu_device::op_clds()
+{
+ // CLDS: CLD, CLS
+ op_cld();
+ op_cls();
+}
+
+void melps4_cpu_device::op_sd()
+{
+ // SD: set port D pin designated by Y
+ write_d_pin(m_y, 1);
+}
+
+void melps4_cpu_device::op_rd()
+{
+ // RD: reset port D pin designated by Y
+ write_d_pin(m_y, 0);
+}
+
+void melps4_cpu_device::op_szd()
+{
+ // SZD: skip next if port D pin designated by Y is 0
+ m_skip = !read_d_pin(m_y);
+}
+
+void melps4_cpu_device::op_osab()
+{
+ // OSAB: output A and B to port S
+ write_gen_port(MELPS4_PORTS, m_b << 4 | m_a);
+}
+
+void melps4_cpu_device::op_ospa()
+{
+ // OSPA: decode A by PLA and output to port S
+ op_illegal();
+}
+
+void melps4_cpu_device::op_ose()
+{
+ // OSE: output E to port S
+ write_gen_port(MELPS4_PORTS, m_e);
+}
+
+void melps4_cpu_device::op_ias()
+{
+ // IAS i: transfer port S(hi/lo) to A
+ int shift = (m_op & 1) ? 0 : 4;
+ m_a = read_gen_port(MELPS4_PORTS) >> shift & 0xf;
+}
+
+void melps4_cpu_device::op_ofa()
+{
+ // OFA: output A to port F
+ write_gen_port(MELPS4_PORTF, m_a);
+}
+
+void melps4_cpu_device::op_iaf()
+{
+ // IAF: input port F to A
+ m_a = read_gen_port(MELPS4_PORTF);
+}
+
+void melps4_cpu_device::op_oga()
+{
+ // OGA: output A to port G
+ write_gen_port(MELPS4_PORTG, m_a);
+}
+
+void melps4_cpu_device::op_iak()
+{
+ // IAK: input port K to A
+ m_a = m_read_k(0, 0xffff) & 0xf;
+}
+
+void melps4_cpu_device::op_szk()
+{
+ // SZK j: skip next if port K bit is reset
+ m_skip = !(m_read_k(0, 0xffff) & m_bitmask);
+}
+
+void melps4_cpu_device::op_su()
+{
+ // SU/RU: set/reset port U
+ write_gen_port(MELPS4_PORTU, m_op & 1);
+}
+
+
+// Interrupts
+
+void melps4_cpu_device::op_ei()
+{
+ // EI: enable interrupt flag
+ m_prohibit_irq = true;
+ m_possible_irq = true;
+ m_inte = 1;
+}
+
+void melps4_cpu_device::op_di()
+{
+ // DI: disable interrupt flag
+ m_prohibit_irq = true;
+ m_inte = 0;
+}
+
+void melps4_cpu_device::op_inth()
+{
+ // INTH: set external interrupt polarity high (rising edge)
+ m_intp = 1;
+}
+
+void melps4_cpu_device::op_intl()
+{
+ // INTL: set external interrupt polarity low (falling edge)
+ m_intp = 0;
+}
+
+
+// Misc
+
+void melps4_cpu_device::op_nop()
+{
+ // NOP: no operation
+}
+
+void melps4_cpu_device::op_illegal()
+{
+ logerror("%s unknown opcode $%03X at $%04X\n", tag(), m_op, m_prev_pc);
+}