summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/cpu/amis2000/amis2000op.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/cpu/amis2000/amis2000op.c')
-rw-r--r--src/emu/cpu/amis2000/amis2000op.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/src/emu/cpu/amis2000/amis2000op.c b/src/emu/cpu/amis2000/amis2000op.c
new file mode 100644
index 00000000000..02f23ebed0a
--- /dev/null
+++ b/src/emu/cpu/amis2000/amis2000op.c
@@ -0,0 +1,524 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+
+// AMI S2000 opcode handlers
+
+#include "amis2000.h"
+
+
+// internal helpers
+
+inline UINT8 amis2000_base_device::ram_r()
+{
+ UINT16 address = m_bu << 4 | m_bl;
+ return m_data->read_byte(address) & 0xf;
+}
+
+inline void amis2000_base_device::ram_w(UINT8 data)
+{
+ UINT16 address = m_bu << 4 | m_bl;
+ m_data->write_byte(address, data & 0xf);
+}
+
+void amis2000_base_device::pop_callstack()
+{
+ m_pc = (m_pc & ~m_callstack_mask) | (m_callstack[0] & m_callstack_mask);
+ for (int i = 0; i < m_callstack_depth-1; i++)
+ m_callstack[i] = m_callstack[i+1];
+}
+
+void amis2000_base_device::push_callstack()
+{
+ for (int i = m_callstack_depth-1; i >= 1; i--)
+ m_callstack[i] = m_callstack[i-1];
+ m_callstack[0] = m_pc & m_callstack_mask;
+}
+
+void amis2000_base_device::d_latch_out(bool active)
+{
+ m_write_d(0, active ? (m_d ^ m_d_polarity) : 0, 0xff);
+ m_d_active = active;
+}
+
+
+// Register Instructions
+
+void amis2000_base_device::op_lai()
+{
+ // LAI X: load ACC with X, select I and K inputs
+ // note: only execute the first one in a sequence of LAI
+ if ((m_prev_op & 0xf0) != (m_op & 0xf0))
+ {
+ UINT8 param = m_op & 0x0f;
+ m_acc = param;
+ m_ki_mask = param;
+ }
+}
+
+void amis2000_base_device::op_lab()
+{
+ // LAB: load ACC with BL
+ m_acc = m_bl;
+}
+
+void amis2000_base_device::op_lae()
+{
+ // LAE: load ACC with E
+ m_acc = m_e;
+}
+
+void amis2000_base_device::op_xab()
+{
+ // XAB: exchange ACC with BL
+ UINT8 old_acc = m_acc;
+ m_acc = m_bl;
+ m_bl = old_acc;
+}
+
+void amis2000_base_device::op_xabu()
+{
+ // XABU: exchange ACC with BU
+ UINT8 old_acc = m_acc;
+ m_acc = (m_acc & ~m_bu_mask) | (m_bu & m_bu_mask);
+ m_bu = old_acc & m_bu_mask;
+}
+
+void amis2000_base_device::op_xae()
+{
+ // XAE: exchange ACC with E
+ UINT8 old_acc = m_acc;
+ m_acc = m_e;
+ m_e = old_acc;
+}
+
+void amis2000_base_device::op_lbe()
+{
+ // LBE Y: load BU with Y, load BL with E
+ // note: only execute the first one in a sequence of LB*
+ if ((m_prev_op & 0xf0) != (m_op & 0xf0))
+ {
+ UINT8 param = m_op & 0x03;
+ m_bu = param & m_bu_mask;
+ m_bl = m_e;
+ }
+}
+
+void amis2000_base_device::op_lbep()
+{
+ // LBEP Y: load BU with Y, load BL with E+1
+ // note: only execute the first one in a sequence of LB*
+ if ((m_prev_op & 0xf0) != (m_op & 0xf0))
+ {
+ UINT8 param = m_op & 0x03;
+ m_bu = param & m_bu_mask;
+ m_bl = (m_e + 1) & 0xf;
+ }
+}
+
+void amis2000_base_device::op_lbz()
+{
+ // LBZ Y: load BU with Y, load BL with 0
+ // note: only execute the first one in a sequence of LB*
+ if ((m_prev_op & 0xf0) != (m_op & 0xf0))
+ {
+ UINT8 param = m_op & 0x03;
+ m_bu = param & m_bu_mask;
+ m_bl = 0;
+ }
+}
+
+void amis2000_base_device::op_lbf()
+{
+ // LBF Y: load BU with Y, load BL with 15
+ // note: only execute the first one in a sequence of LB*
+ if ((m_prev_op & 0xf0) != (m_op & 0xf0))
+ {
+ UINT8 param = m_op & 0x03;
+ m_bu = param & m_bu_mask;
+ m_bl = 0xf;
+ }
+}
+
+
+// RAM Instructions
+
+void amis2000_base_device::op_lam()
+{
+ // LAM _Y: load ACC with RAM, xor BU with _Y
+ m_acc = ram_r();
+ UINT8 param = ~m_op & 0x03;
+ m_bu ^= (param & m_bu_mask);
+}
+
+void amis2000_base_device::op_xc()
+{
+ // XC _Y: exchange ACC with RAM, xor BU with _Y
+ UINT8 old_acc = m_acc;
+ m_acc = ram_r();
+ ram_w(old_acc);
+ UINT8 param = ~m_op & 0x03;
+ m_bu ^= (param & m_bu_mask);
+}
+
+void amis2000_base_device::op_xci()
+{
+ // XCI _Y: exchange ACC with RAM, increment BL(skip next on carry), xor BU with _Y
+ op_xc();
+ m_bl = (m_bl + 1) & 0xf;
+ m_skip = (m_bl == 0);
+}
+
+void amis2000_base_device::op_xcd()
+{
+ // XCD _Y: exchange ACC with RAM, decrement BL(skip next on carry), xor BU with _Y
+ op_xc();
+ m_bl = (m_bl - 1) & 0xf;
+ m_skip = (m_bl == 0xf);
+}
+
+void amis2000_base_device::op_stm()
+{
+ // STM Z: set RAM bit Z
+ UINT8 param = 1 << (m_op & 0x03);
+ ram_w(ram_r() | param);
+}
+
+void amis2000_base_device::op_rsm()
+{
+ // RSM Z: reset RAM bit Z
+ UINT8 param = 1 << (m_op & 0x03);
+ ram_w(ram_r() & ~param);
+}
+
+
+// Input/Output Instructions
+
+void amis2000_base_device::op_inp()
+{
+ // INP: input D-pins to ACC and RAM
+ UINT8 in = m_d_active ? m_d : m_read_d(0, 0xff);
+ m_acc = in & 0xf;
+ ram_w(in >> 4 & 0xf);
+}
+
+void amis2000_base_device::op_out()
+{
+ // OUT: pulse output ACC and RAM to D-pins
+ logerror("%s unknown opcode $%02X at $%04X\n", tag(), m_op, m_pc);
+}
+
+void amis2000_base_device::op_disb()
+{
+ // DISB: set D-latch to ACC and RAM directly
+ m_d = m_acc | ram_r() << 4;
+ d_latch_out(true);
+}
+
+void amis2000_base_device::op_disn()
+{
+ // DISN: set D-latch to ACC+carry via on-die segment decoder
+ static const UINT8 lut_segment_decoder[0x10] =
+ {
+ // 0-F digits in bit order [DP]abcdefg
+ 0x7e, 0x30, 0x6d, 0x79, 0x33, 0x5b, 0x5f, 0x70, 0x7f, 0x7b, 0x77, 0x1f, 0x4e, 0x3d, 0x4f, 0x47
+ };
+ const UINT8 *ptr = (m_7seg_table != NULL) ? m_7seg_table : lut_segment_decoder;
+ m_d = ptr[m_acc] | (m_carry ? 0x80 : 0x00);
+ d_latch_out(true);
+}
+
+void amis2000_base_device::op_mvs()
+{
+ // MVS: output master strobe latch to A-pins
+ d_latch_out(false);
+ m_write_a(0, m_a, 0xffff);
+}
+
+void amis2000_base_device::op_psh()
+{
+ // PSH: preset high(BL) master strobe latch
+ switch (m_bl)
+ {
+ case 0xd:
+ // set multiplex operation
+ // ?
+ break;
+
+ case 0xe:
+ // exit from floating mode on D-pins
+ d_latch_out(true);
+ break;
+
+ case 0xf:
+ // set all latch bits high
+ m_a = 0x1fff;
+ break;
+
+ default:
+ // set selected latch bit high
+ m_a |= (1 << m_bl);
+ break;
+ }
+}
+
+void amis2000_base_device::op_psl()
+{
+ // PSL: preset low(BL) master strobe latch
+ switch (m_bl)
+ {
+ case 0xd:
+ // set static operation
+ // ?
+ break;
+
+ case 0xe:
+ // enter floating mode on D-pins
+ d_latch_out(false);
+ break;
+
+ case 0xf:
+ // set all latch bits low
+ m_a = 0;
+ break;
+
+ default:
+ // set selected latch bit low
+ m_a &= ~(1 << m_bl);
+ break;
+ }
+}
+
+void amis2000_base_device::op_eur()
+{
+ // EUR: set timer frequency(European) and D-latch polarity, via ACC
+ m_d_polarity = (m_acc & 1) ? 0x00 : 0xff;
+ d_latch_out(m_d_active); // refresh
+}
+
+
+// Program Control Instructions
+
+void amis2000_base_device::op_pp()
+{
+ // PP _X: prepare page/bank with _X
+ UINT8 param = ~m_op & 0x0f;
+ if ((m_prev_op & 0xf0) != (m_op & 0xf0))
+ m_ppr = param;
+ else
+ m_pbr = param & 7;
+}
+
+void amis2000_base_device::op_jmp()
+{
+ // JMP X: jump to X(+PP)
+ UINT16 mask = 0x3f;
+ UINT16 param = m_op & mask;
+
+ // if previous opcode was PP, change PC high bits too
+ if ((m_prev_op & 0xf0) == 0x60)
+ {
+ param |= (m_ppr << 6) | (m_pbr << 10);
+ mask = 0x1fff;
+ }
+ m_pc = (m_pc & ~mask) | param;
+}
+
+void amis2000_base_device::op_jms()
+{
+ // JMS X: call to X(+PP)
+ m_icount--;
+ push_callstack();
+ op_jmp();
+
+ // subroutines default location is page 15
+ if ((m_prev_op & 0xf0) != 0x60)
+ m_pc |= 0x3c0;
+}
+
+void amis2000_base_device::op_rt()
+{
+ // RT: return from subroutine
+ pop_callstack();
+}
+
+void amis2000_base_device::op_rts()
+{
+ // RTS: return from subroutine and skip next
+ op_rt();
+ m_skip = true;
+}
+
+void amis2000_base_device::op_nop()
+{
+ // NOP: no operation
+}
+
+void amis2000_base_device::op_halt()
+{
+ // HALT: debugger breakpoint for devkit-use
+ logerror("%s unknown opcode $%02X at $%04X\n", tag(), m_op, m_pc);
+}
+
+
+// Skip Instructions
+
+void amis2000_base_device::op_szc()
+{
+ // SZC: skip next on zero(no) carry
+ m_skip = !m_carry;
+}
+
+void amis2000_base_device::op_szm()
+{
+ // SZM Z: skip next on zero RAM bit Z
+ UINT8 param = 1 << (m_op & 0x03);
+ m_skip = !(ram_r() & param);
+}
+
+void amis2000_base_device::op_szi()
+{
+ // SZI: skip next on I pin(s)
+ m_skip = ((~m_read_i(0, 0xff) & m_ki_mask) != 0);
+}
+
+void amis2000_base_device::op_szk()
+{
+ // SZK: skip next on K pin(s)
+ m_skip = ((~m_read_k(0, 0xff) & m_ki_mask) != 0);
+}
+
+void amis2000_base_device::op_sbe()
+{
+ // SBE: skip next on BL equals E
+ m_skip = (m_bl == m_e);
+}
+
+void amis2000_base_device::op_sam()
+{
+ // SAM: skip next on ACC equals RAM
+ m_skip = (m_acc == ram_r());
+}
+
+void amis2000_base_device::op_sos()
+{
+ // SOS: skip next on SF(timer output), clear SF
+ logerror("%s unknown opcode $%02X at $%04X\n", tag(), m_op, m_pc);
+}
+
+void amis2000_base_device::op_tf1()
+{
+ // TF1: skip next on flag 1
+ m_skip = ((m_f & 0x01) != 0);
+}
+
+void amis2000_base_device::op_tf2()
+{
+ // TF2: skip next on flag 2
+ m_skip = ((m_f & 0x02) != 0);
+}
+
+
+// Arithmetic and Logical Instructions
+
+void amis2000_base_device::op_adcs()
+{
+ // ADCS: add RAM to ACC+carry, skip next on not carry
+ m_acc += ram_r() + m_carry;
+ m_carry = m_acc >> 4 & 1;
+ m_skip = !m_carry;
+ m_acc &= 0xf;
+}
+
+void amis2000_base_device::op_adis()
+{
+ // ADIS X: add X to ACC, skip next on not carry
+ UINT8 param = m_op & 0x0f;
+ m_acc += param;
+ m_skip = !(m_acc & 0x10);
+ m_acc &= 0xf;
+}
+
+void amis2000_base_device::op_add()
+{
+ // ADD: add RAM to ACC
+ m_acc = (m_acc + ram_r()) & 0xf;
+}
+
+void amis2000_base_device::op_and()
+{
+ // AND: and ACC with RAM
+ m_acc &= ram_r();
+}
+
+void amis2000_base_device::op_xor()
+{
+ // XOR: xor ACC with RAM
+ m_acc ^= ram_r();
+}
+
+void amis2000_base_device::op_stc()
+{
+ // STC: set carry
+ m_carry = 1;
+}
+
+void amis2000_base_device::op_rsc()
+{
+ // RSC: reset carry
+ m_carry = 0;
+}
+
+void amis2000_base_device::op_cma()
+{
+ // CMA: complement ACC
+ m_acc ^= 0xf;
+}
+
+void amis2000_base_device::op_sf1()
+{
+ // SF1: set flag 1
+ m_f |= 0x01;
+}
+
+void amis2000_base_device::op_rf1()
+{
+ // RF1: reset flag 1
+ m_f &= ~0x01;
+}
+
+void amis2000_base_device::op_sf2()
+{
+ // SF2: set flag 2
+ m_f |= 0x02;
+}
+
+void amis2000_base_device::op_rf2()
+{
+ // RF2: reset flag 2
+ m_f &= ~0x02;
+}
+
+
+
+// AMI S2152 specific handlers
+
+void amis2152_cpu_device::d2f_timer_clock()
+{
+ // schedule next timeout (frequency is guessed)
+ attotime base = attotime::from_ticks(4 * 64, unscaled_clock());
+ m_d2f_timer->adjust(base * (0x10 - m_d2f_latch));
+}
+
+TIMER_CALLBACK_MEMBER(amis2152_cpu_device::d2f_timer_cb)
+{
+ m_write_f(m_fout_state);
+ m_fout_state ^= 1;
+
+ d2f_timer_clock();
+}
+
+void amis2152_cpu_device::op_szk()
+{
+ // instead of SZK: ???: load d2f latch with ACC(?)
+ m_d2f_latch = m_acc;
+}