summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/hmcs400/hmcs400op.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/hmcs400/hmcs400op.cpp')
-rw-r--r--src/devices/cpu/hmcs400/hmcs400op.cpp692
1 files changed, 692 insertions, 0 deletions
diff --git a/src/devices/cpu/hmcs400/hmcs400op.cpp b/src/devices/cpu/hmcs400/hmcs400op.cpp
new file mode 100644
index 00000000000..045261fb534
--- /dev/null
+++ b/src/devices/cpu/hmcs400/hmcs400op.cpp
@@ -0,0 +1,692 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+
+// HMCS400 opcode handlers
+
+#include "emu.h"
+#include "hmcs400.h"
+
+
+// internal helpers
+
+inline u8 hmcs400_cpu_device::ram_r(u8 mem_mask)
+{
+ return m_data->read_byte(m_param & 0x3ff, mem_mask & 0xf) & 0xf;
+}
+
+inline void hmcs400_cpu_device::ram_w(u8 data, u8 mem_mask)
+{
+ m_data->write_byte(m_param & 0x3ff, data & 0xf, mem_mask & 0xf);
+}
+
+void hmcs400_cpu_device::pop_stack()
+{
+ u16 data = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ m_sp = ((m_sp + 1) | 0x3c0) & 0x3ff;
+ data = data << 4 | (m_data->read_byte(m_sp) & 0xf);
+ }
+
+ if (m_op & 1)
+ {
+ // RTNI restores CA and ST
+ m_ca = BIT(data, 7);
+ m_st = BIT(data, 15);
+ }
+ m_pc = (~data & 0x7f00) >> 1 | (~data & 0x7f);
+}
+
+void hmcs400_cpu_device::push_stack()
+{
+ u16 data = (~m_pc << 1 & 0x7f00) | (~m_pc & 0x7f) | m_ca << 7 | m_st << 15;
+ m_sp = (m_sp | 0x3c0) & 0x3ff;
+
+ for (int i = 0; i < 4; i++)
+ {
+ m_data->write_byte(m_sp, data & 0xf);
+ data >>= 4;
+ m_sp = ((m_sp - 1) | 0x3c0) & 0x3ff;
+ }
+}
+
+
+// instruction set
+
+void hmcs400_cpu_device::op_illegal()
+{
+ logerror("unknown opcode $%03X @ $%04X\n", m_op, m_prev_pc);
+}
+
+void hmcs400_cpu_device::op_todo()
+{
+ logerror("unimplemented opcode $%03X @ $%04X\n", m_op, m_prev_pc);
+}
+
+
+// immediate instructions
+
+void hmcs400_cpu_device::op_lai()
+{
+ // LAI i: Load A from Immediate
+ m_a = m_i;
+}
+
+void hmcs400_cpu_device::op_lbi()
+{
+ // LBI i: Load B from Immediate
+ m_b = m_i;
+}
+
+void hmcs400_cpu_device::op_lmi()
+{
+ // LMID i,d: Load Memory from Immediate
+ ram_w(m_i);
+}
+
+void hmcs400_cpu_device::op_lmiiy()
+{
+ // LMIIY i: Load Memory from Immediate, Increment Y
+ op_lmi();
+ op_iy();
+}
+
+
+// register-to-register instructions
+
+void hmcs400_cpu_device::op_lab()
+{
+ // LAB: Load A from B
+ m_a = m_b;
+}
+
+void hmcs400_cpu_device::op_lba()
+{
+ // LBA: Load B from A
+ m_b = m_a;
+}
+
+void hmcs400_cpu_device::op_law()
+{
+ // LAW: Load A from W
+ m_a = m_w;
+}
+
+void hmcs400_cpu_device::op_lay()
+{
+ // LAY: Load A from Y
+ m_a = m_y;
+}
+
+void hmcs400_cpu_device::op_laspx()
+{
+ // LASPX: Load A from SPX
+ m_a = m_spx;
+}
+
+void hmcs400_cpu_device::op_laspy()
+{
+ // LASPY: Load A from SPY
+ m_a = m_spy;
+}
+
+void hmcs400_cpu_device::op_lamr()
+{
+ // LAMR m: Load A from MR
+ m_param = 0x20 | m_i;
+ m_a = ram_r();
+}
+
+void hmcs400_cpu_device::op_xmra()
+{
+ // XMRA m: Exchange MR and A
+ m_param = 0x20 | m_i;
+ u8 old_a = m_a;
+ m_a = ram_r();
+ ram_w(old_a);
+}
+
+
+// RAM address instructions
+
+void hmcs400_cpu_device::op_lwi()
+{
+ // LWI i: Load W from Immediate
+ m_w = m_i;
+}
+
+void hmcs400_cpu_device::op_lxi()
+{
+ // LXI i: Load X from Immediate
+ m_x = m_i;
+}
+
+void hmcs400_cpu_device::op_lyi()
+{
+ // LYI i: Load Y from Immediate
+ m_y = m_i;
+}
+
+void hmcs400_cpu_device::op_lwa()
+{
+ // LWA: Load W from A
+ m_w = m_a & 3;
+}
+
+void hmcs400_cpu_device::op_lxa()
+{
+ // LXA: Load X from A
+ m_x = m_a;
+}
+
+void hmcs400_cpu_device::op_lya()
+{
+ // LYA: Load Y from A
+ m_y = m_a;
+}
+
+void hmcs400_cpu_device::op_iy()
+{
+ // IY: Increment Y
+ m_y = (m_y + 1) & 0xf;
+ m_st = (m_y != 0);
+}
+
+void hmcs400_cpu_device::op_dy()
+{
+ // DY: Decrement Y
+ m_y = (m_y - 1) & 0xf;
+ m_st = (m_y != 0xf);
+}
+
+void hmcs400_cpu_device::op_ayy()
+{
+ // AYY: Add A to Y
+ m_y += m_a;
+ m_st = BIT(m_y, 4);
+ m_y &= 0xf;
+}
+
+void hmcs400_cpu_device::op_syy()
+{
+ // SYY: Subtract A from Y
+ m_y -= m_a;
+ m_st = BIT(~m_y, 4);
+ m_y &= 0xf;
+}
+
+void hmcs400_cpu_device::op_xsp()
+{
+ // XSP(XY): Exchange X and SPX, Y and SPY, or NOP if 0
+ if (m_op & 1)
+ {
+ u8 old_x = m_x;
+ m_x = m_spx;
+ m_spx = old_x;
+ }
+ if (m_op & 2)
+ {
+ u8 old_y = m_y;
+ m_y = m_spy;
+ m_spy = old_y;
+ }
+}
+
+
+// RAM register instructions
+
+void hmcs400_cpu_device::op_lam()
+{
+ // LAM(XY) / LAMD d: Load A from Memory
+ m_a = ram_r();
+ op_xsp();
+}
+
+void hmcs400_cpu_device::op_lbm()
+{
+ // LBM(XY): Load B from Memory
+ m_b = ram_r();
+ op_xsp();
+}
+
+void hmcs400_cpu_device::op_lma()
+{
+ // LMA(XY) / LMAD d: Load Memory from A
+ ram_w(m_a);
+ op_xsp();
+}
+
+void hmcs400_cpu_device::op_lmaiy()
+{
+ // LMAIY(X): Load Memory from A, Increment Y
+ op_lma();
+ op_iy();
+}
+
+void hmcs400_cpu_device::op_lmady()
+{
+ // LMADY(X): Load Memory from A, Decrement Y
+ op_lma();
+ op_dy();
+}
+
+void hmcs400_cpu_device::op_xma()
+{
+ // XMA(XY) / XMAD d: Exchange Memory and A
+ u8 old_a = m_a;
+ m_a = ram_r();
+ ram_w(old_a);
+ op_xsp();
+}
+
+void hmcs400_cpu_device::op_xmb()
+{
+ // XMB(XY): Exchange Memory and B
+ u8 old_b = m_b;
+ m_b = ram_r();
+ ram_w(old_b);
+ op_xsp();
+}
+
+
+// arithmetic instructions
+
+void hmcs400_cpu_device::op_ai()
+{
+ // AI i: Add Immediate to A
+ m_a += m_i;
+ m_st = BIT(m_a, 4);
+ m_a &= 0xf;
+}
+
+void hmcs400_cpu_device::op_ib()
+{
+ // IB: Increment B
+ m_b = (m_b + 1) & 0xf;
+ m_st = (m_b != 0);
+}
+
+void hmcs400_cpu_device::op_db()
+{
+ // DB: Decrement B
+ m_b = (m_b - 1) & 0xf;
+ m_st = (m_b != 0xf);
+}
+
+void hmcs400_cpu_device::op_daa()
+{
+ // DAA: Decimal Adjust for Addition
+ if (m_ca || m_a > 9)
+ {
+ m_a = (m_a + 6) & 0xf;
+ m_ca = 1;
+ }
+}
+
+void hmcs400_cpu_device::op_das()
+{
+ // DAS: Decimal Adjust for Subtraction
+ if (!m_ca || m_a > 9)
+ {
+ m_a = (m_a + 10) & 0xf;
+ m_ca = 0;
+ }
+}
+
+void hmcs400_cpu_device::op_nega()
+{
+ // NEGA: Negate A
+ m_a = (0 - m_a) & 0xf;
+}
+
+void hmcs400_cpu_device::op_comb()
+{
+ // COMB: Complement B
+ m_b ^= 0xf;
+}
+
+void hmcs400_cpu_device::op_rotr()
+{
+ // ROTR: Rotate Right with Carry
+ u8 ca = m_a & 1;
+ m_a = m_a >> 1 | m_ca << 3;
+ m_ca = ca;
+}
+
+void hmcs400_cpu_device::op_rotl()
+{
+ // ROTL: Rotate Left with Carry
+ m_a = m_a << 1 | m_ca;
+ m_ca = BIT(m_a, 4);
+ m_a &= 0xf;
+}
+
+void hmcs400_cpu_device::op_sec()
+{
+ // SEC: Set Carry
+ m_ca = 1;
+}
+
+void hmcs400_cpu_device::op_rec()
+{
+ // REC: Reset Carry
+ m_ca = 0;
+}
+
+void hmcs400_cpu_device::op_tc()
+{
+ // TC: Test Carry
+ m_st = m_ca;
+}
+
+void hmcs400_cpu_device::op_am()
+{
+ // AM / AMD d: Add A to Memory
+ m_a += ram_r();
+ m_st = BIT(m_a, 4);
+ m_a &= 0xf;
+}
+
+void hmcs400_cpu_device::op_amc()
+{
+ // AMC / AMCD d: Add A to Memory with Carry
+ m_a += ram_r() + m_ca;
+ m_ca = BIT(m_a, 4);
+ m_st = m_ca;
+ m_a &= 0xf;
+}
+
+void hmcs400_cpu_device::op_smc()
+{
+ // SMC / SMCD d: Subtract A from Memory with Carry
+ m_a = ram_r() - m_a - (m_ca ^ 1);
+ m_ca = BIT(~m_a, 4);
+ m_st = m_ca;
+ m_a &= 0xf;
+}
+
+void hmcs400_cpu_device::op_or()
+{
+ // OR: Or A with B
+ m_a |= m_b;
+}
+
+void hmcs400_cpu_device::op_anm()
+{
+ // ANM / ANMD d: And Memory with A
+ m_a &= ram_r();
+ m_st = (m_a != 0);
+}
+
+void hmcs400_cpu_device::op_orm()
+{
+ // ORM / ORMD d: Or Memory with A
+ m_a |= ram_r();
+ m_st = (m_a != 0);
+}
+
+void hmcs400_cpu_device::op_eorm()
+{
+ // EORM / EORMD d: Exclusive Or Memory with A
+ m_a ^= ram_r();
+ m_st = (m_a != 0);
+}
+
+
+// compare instructions
+
+void hmcs400_cpu_device::op_inem()
+{
+ // INEM i / INEMD i,d: Immediate Not Equal to Memory
+ m_st = (m_i != ram_r());
+}
+
+void hmcs400_cpu_device::op_anem()
+{
+ // ANEM / ANEMD d: A Not Equal to Memory
+ m_st = (m_a != ram_r());
+}
+
+void hmcs400_cpu_device::op_bnem()
+{
+ // BNEM: B Not Equal to Memory
+ m_st = (m_b != ram_r());
+}
+
+void hmcs400_cpu_device::op_ynei()
+{
+ // YNEI i: Y Not Equal to Immediate
+ m_st = (m_y != m_i);
+}
+
+void hmcs400_cpu_device::op_ilem()
+{
+ // ILEM i / ILEMD i,d: Immediate Less or Equal to Memory
+ m_st = (m_i <= ram_r());
+}
+
+void hmcs400_cpu_device::op_alem()
+{
+ // ALEM / ALEMD d: A Less or Equal to Memory
+ m_st = (m_a <= ram_r());
+}
+
+void hmcs400_cpu_device::op_blem()
+{
+ // BLEM: B Less or Equal to Memory
+ m_st = (m_b <= ram_r());
+}
+
+void hmcs400_cpu_device::op_alei()
+{
+ // ALEI i: A Less or Equal to Immediate
+ m_st = (m_a <= m_i);
+}
+
+
+// RAM bit manipulation instructions
+
+void hmcs400_cpu_device::op_sem()
+{
+ // SEM n / SEMD n,d: Set Memory Bit
+ u8 mask = 1 << (m_op & 3);
+ ram_w(ram_r(~mask) | mask, mask);
+}
+
+void hmcs400_cpu_device::op_rem()
+{
+ // REM n / REMD n,d: Reset Memory Bit
+ u8 mask = 1 << (m_op & 3);
+ ram_w(ram_r(~mask) & ~mask, mask);
+}
+
+void hmcs400_cpu_device::op_tm()
+{
+ // TM n / TMD n,d: Test Memory Bit
+ u8 mask = 1 << (m_op & 3);
+ m_st = (ram_r(mask) & mask) ? 1 : 0;
+}
+
+
+// ROM address instructions
+
+void hmcs400_cpu_device::op_br()
+{
+ // BR b: Branch on Status 1
+ if (m_st)
+ m_pc = (m_pc & ~0xff) | (m_op & 0xff);
+ else
+ m_st = 1;
+}
+
+void hmcs400_cpu_device::op_brl()
+{
+ // BRL u: Long Branch on Status 1
+ if (m_st)
+ op_jmpl();
+ else
+ m_st = 1;
+}
+
+void hmcs400_cpu_device::op_jmpl()
+{
+ // JMPL u: Long Jump Unconditionally
+ m_pc = m_i << 10 | m_param;
+}
+
+void hmcs400_cpu_device::op_cal()
+{
+ // CAL a: Subroutine Jump on Status 1
+ if (m_st)
+ {
+ cycle();
+ push_stack();
+ m_pc = m_op & 0x3f;
+ }
+ else
+ m_st = 1;
+}
+
+void hmcs400_cpu_device::op_call()
+{
+ // CALL u: Long Subroutine Jump on Status 1
+ if (m_st)
+ {
+ push_stack();
+ op_jmpl();
+ }
+ else
+ m_st = 1;
+}
+
+void hmcs400_cpu_device::op_tbr()
+{
+ // TBR p: Table Branch
+ m_pc = m_i << 8 | m_b << 4 | m_a;
+}
+
+void hmcs400_cpu_device::op_rtn()
+{
+ // RTN: Return from Subroutine
+ cycle();
+ cycle();
+ pop_stack();
+}
+
+void hmcs400_cpu_device::op_rtni()
+{
+ // RTNI: Return from Interrupt
+ op_rtn();
+ m_irq_flags |= 1;
+}
+
+
+// input/output instructions
+
+void hmcs400_cpu_device::op_sed()
+{
+ // SED: Set Discrete I/O Latch
+ write_d(m_y, 1);
+}
+
+void hmcs400_cpu_device::op_sedd()
+{
+ // SEDD m: Set Discrete I/O Latch Direct
+ write_d(m_i, 1);
+}
+
+void hmcs400_cpu_device::op_red()
+{
+ // RED: Reset Discrete I/O Latch
+ write_d(m_y, 0);
+}
+
+void hmcs400_cpu_device::op_redd()
+{
+ // REDD m: Reset Discrete I/O Latch Direct
+ write_d(m_i, 0);
+}
+
+void hmcs400_cpu_device::op_td()
+{
+ // TD: Test Discrete I/O Latch
+ m_st = read_d(m_y);
+}
+
+void hmcs400_cpu_device::op_tdd()
+{
+ // TDD m: Test Discrete I/O Latch Direct
+ m_st = read_d(m_i);
+}
+
+void hmcs400_cpu_device::op_lar()
+{
+ // LAR m: Load A from R Port Register
+ m_a = read_r(m_i);
+}
+
+void hmcs400_cpu_device::op_lbr()
+{
+ // LBR m: Load B from R Port Register
+ m_b = read_r(m_i);
+}
+
+void hmcs400_cpu_device::op_lra()
+{
+ // LRA m: Load R Port Register from A
+ write_r(m_i, m_a);
+}
+
+void hmcs400_cpu_device::op_lrb()
+{
+ // LRB m: Load R Port Register from B
+ write_r(m_i, m_b);
+}
+
+void hmcs400_cpu_device::op_p()
+{
+ // P p: Pattern Generation
+ cycle();
+ u16 data = m_program->read_word(m_i << 8 | m_b << 4 | m_a);
+
+ // destination is determined by the 2 highest bits
+ if (data & 0x100)
+ {
+ // to A/B registers
+ m_a = data & 0xf;
+ m_b = data >> 4 & 0xf;
+ }
+ if (data & 0x200)
+ {
+ // to R1/R2 ports
+ write_r(1, data & 0xf);
+ write_r(2, data >> 4 & 0xf);
+ }
+}
+
+
+// control instructions
+
+void hmcs400_cpu_device::op_sts()
+{
+ // STS: Start Serial
+ op_todo();
+}
+
+void hmcs400_cpu_device::op_sby()
+{
+ // SBY: Standby Mode
+ m_standby = true;
+}
+
+void hmcs400_cpu_device::op_stop()
+{
+ // STOP: Stop Mode
+ m_stop = true;
+
+ if (m_icount > 0)
+ m_icount = 0;
+
+ // all I/O pins go high-impedance
+ m_stop_cb(1);
+}