mame/src/devices/cpu/hmcs40/hmcs40op.cpp

684 lines
10 KiB
C++

// license:BSD-3-Clause
// copyright-holders:hap
// HMCS40 opcode handlers
#include "emu.h"
#include "hmcs40.h"
// internal helpers
inline u8 hmcs40_cpu_device::ram_r()
{
u8 address = (m_x << 4 | m_y) & m_datamask;
return m_data->read_byte(address) & 0xf;
}
inline void hmcs40_cpu_device::ram_w(u8 data)
{
u8 address = (m_x << 4 | m_y) & m_datamask;
m_data->write_byte(address, data & 0xf);
}
void hmcs40_cpu_device::pop_stack()
{
m_pc = m_stack[0] & m_pcmask;
for (int i = 0; i < m_stack_levels-1; i++)
m_stack[i] = m_stack[i+1];
}
void hmcs40_cpu_device::push_stack()
{
for (int i = m_stack_levels-1; i >= 1; i--)
m_stack[i] = m_stack[i-1];
m_stack[0] = m_pc;
}
// instruction set
void hmcs40_cpu_device::op_illegal()
{
logerror("unknown opcode $%03X at $%04X\n", m_op, m_prev_pc);
}
// Register-to-Register Instruction
void hmcs40_cpu_device::op_lab()
{
// LAB: Load A from B
m_a = m_b;
}
void hmcs40_cpu_device::op_lba()
{
// LBA: Load B from A
m_b = m_a;
}
void hmcs40_cpu_device::op_lay()
{
// LAY: Load A from Y
m_a = m_y;
}
void hmcs40_cpu_device::op_laspx()
{
// LASPX: Load A from SPX
m_a = m_spx;
}
void hmcs40_cpu_device::op_laspy()
{
// LASPY: Load A from SPY
m_a = m_spy;
}
void hmcs40_cpu_device::op_xamr()
{
// XAMR m: Exchange A and MR(m)
// determine MR(Memory Register) location
u8 address = m_op & 0xf;
// HMCS42: MR0 on file 0, MR4-MR15 on file 4 (there is no file 1-3)
// HMCS43: MR0-MR3 on file 0-3, MR4-MR15 on file 4
if (m_family == HMCS40_FAMILY_HMCS42 || m_family == HMCS40_FAMILY_HMCS43)
address |= (address < 4) ? (address << 4) : 0x40;
// HMCS44/45/46/47: all on last file
else
address |= 0xf0;
address &= m_datamask;
u8 old_a = m_a;
m_a = m_data->read_byte(address) & 0xf;
m_data->write_byte(address, old_a & 0xf);
}
// RAM Address Instruction
void hmcs40_cpu_device::op_lxa()
{
// LXA: Load X from A
m_x = m_a;
}
void hmcs40_cpu_device::op_lya()
{
// LYA: Load Y from A
m_y = m_a;
}
void hmcs40_cpu_device::op_lxi()
{
// LXI i: Load X from Immediate
m_x = m_i;
}
void hmcs40_cpu_device::op_lyi()
{
// LYI i: Load Y from Immediate
m_y = m_i;
}
void hmcs40_cpu_device::op_iy()
{
// IY: Increment Y
m_y = (m_y + 1) & 0xf;
m_s = (m_y != 0);
}
void hmcs40_cpu_device::op_dy()
{
// DY: Decrement Y
m_y = (m_y - 1) & 0xf;
m_s = (m_y != 0xf);
}
void hmcs40_cpu_device::op_ayy()
{
// AYY: Add A to Y
m_y += m_a;
m_s = m_y >> 4 & 1;
m_y &= 0xf;
}
void hmcs40_cpu_device::op_syy()
{
// SYY: Subtract A from Y
m_y -= m_a;
m_s = ~m_y >> 4 & 1;
m_y &= 0xf;
}
void hmcs40_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 Instruction
void hmcs40_cpu_device::op_lam()
{
// LAM (XY): Load A from Memory
m_a = ram_r();
op_xsp();
}
void hmcs40_cpu_device::op_lbm()
{
// LBM (XY): Load B from Memory
m_b = ram_r();
op_xsp();
}
void hmcs40_cpu_device::op_xma()
{
// XMA (XY): Exchange Memory and A
u8 old_a = m_a;
m_a = ram_r();
ram_w(old_a);
op_xsp();
}
void hmcs40_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();
}
void hmcs40_cpu_device::op_lmaiy()
{
// LMAIY (X): Load Memory from A, Increment Y
ram_w(m_a);
op_iy();
op_xsp();
}
void hmcs40_cpu_device::op_lmady()
{
// LMADY (X): Load Memory from A, Decrement Y
ram_w(m_a);
op_dy();
op_xsp();
}
// Immediate Instruction
void hmcs40_cpu_device::op_lmiiy()
{
// LMIIY i: Load Memory from Immediate, Increment Y
ram_w(m_i);
op_iy();
}
void hmcs40_cpu_device::op_lai()
{
// LAI i: Load A from Immediate
m_a = m_i;
}
void hmcs40_cpu_device::op_lbi()
{
// LBI i: Load B from Immediate
m_b = m_i;
}
// Arithmetic Instruction
void hmcs40_cpu_device::op_ai()
{
// AI i: Add Immediate to A
m_a += m_i;
m_s = m_a >> 4 & 1;
m_a &= 0xf;
}
void hmcs40_cpu_device::op_ib()
{
// IB: Increment B
m_b = (m_b + 1) & 0xf;
m_s = (m_b != 0);
}
void hmcs40_cpu_device::op_db()
{
// DB: Decrement B
m_b = (m_b - 1) & 0xf;
m_s = (m_b != 0xf);
}
void hmcs40_cpu_device::op_amc()
{
// AMC: Add A to Memory with Carry
m_a += ram_r() + m_c;
m_c = m_a >> 4 & 1;
m_s = m_c;
m_a &= 0xf;
}
void hmcs40_cpu_device::op_smc()
{
// SMC: Subtract A from Memory with Carry
m_a = ram_r() - m_a - (m_c ^ 1);
m_c = ~m_a >> 4 & 1;
m_s = m_c;
m_a &= 0xf;
}
void hmcs40_cpu_device::op_am()
{
// AM: Add A to Memory
m_a += ram_r();
m_s = m_a >> 4 & 1;
m_a &= 0xf;
}
void hmcs40_cpu_device::op_daa()
{
// DAA: Decimal Adjust for Addition
if (m_c || m_a > 9)
{
m_a = (m_a + 6) & 0xf;
m_c = 1;
}
}
void hmcs40_cpu_device::op_das()
{
// DAS: Decimal Adjust for Subtraction
if (!m_c || m_a > 9)
{
m_a = (m_a + 10) & 0xf;
m_c = 0;
}
}
void hmcs40_cpu_device::op_nega()
{
// NEGA: Negate A
m_a = (0 - m_a) & 0xf;
}
void hmcs40_cpu_device::op_comb()
{
// COMB: Complement B
m_b ^= 0xf;
}
void hmcs40_cpu_device::op_sec()
{
// SEC: Set Carry
m_c = 1;
}
void hmcs40_cpu_device::op_rec()
{
// REC: Reset Carry
m_c = 0;
}
void hmcs40_cpu_device::op_tc()
{
// TC: Test Carry
m_s = m_c;
}
void hmcs40_cpu_device::op_rotl()
{
// ROTL: Rotate Left A with Carry
m_a = m_a << 1 | m_c;
m_c = m_a >> 4 & 1;
m_a &= 0xf;
}
void hmcs40_cpu_device::op_rotr()
{
// ROTR: Rotate Right A with Carry
u8 c = m_a & 1;
m_a = m_a >> 1 | m_c << 3;
m_c = c;
}
void hmcs40_cpu_device::op_or()
{
// OR: OR A with B
m_a |= m_b;
}
// Compare Instruction
void hmcs40_cpu_device::op_mnei()
{
// MNEI i: Memory Not Equal to Immediate
m_s = (ram_r() != m_i);
}
void hmcs40_cpu_device::op_ynei()
{
// YNEI i: Y Not Equal to Immediate
m_s = (m_y != m_i);
}
void hmcs40_cpu_device::op_anem()
{
// ANEM: A Not Equal to Memory
m_s = (m_a != ram_r());
}
void hmcs40_cpu_device::op_bnem()
{
// BNEM: B Not Equal to Memory
m_s = (m_b != ram_r());
}
void hmcs40_cpu_device::op_alei()
{
// ALEI i: A Less or Equal to Immediate
m_s = (m_a <= m_i);
}
void hmcs40_cpu_device::op_alem()
{
// ALEM: A Less or Equal to Memory
m_s = (m_a <= ram_r());
}
void hmcs40_cpu_device::op_blem()
{
// BLEM: B Less or Equal to Memory
m_s = (m_b <= ram_r());
}
// RAM Bit Manipulation Instruction
void hmcs40_cpu_device::op_sem()
{
// SEM n: Set Memory Bit
ram_w(ram_r() | (1 << (m_op & 3)));
}
void hmcs40_cpu_device::op_rem()
{
// REM n: Reset Memory Bit
ram_w(ram_r() & ~(1 << (m_op & 3)));
}
void hmcs40_cpu_device::op_tm()
{
// TM n: Test Memory Bit
m_s = ram_r() >> (m_op & 3) & 1;
}
// ROM Address Instruction
void hmcs40_cpu_device::op_br()
{
// BR a: Branch on Status 1
if (m_s)
m_pc = (m_pc & ~0x3f) | (m_op & 0x3f);
else
m_s = 1;
}
void hmcs40_cpu_device::op_cal()
{
// CAL a: Subroutine Jump on Status 1
if (m_s)
{
push_stack();
m_pc = m_op & 0x3f; // short calls default to page 0
}
else
m_s = 1;
}
void hmcs40_cpu_device::op_lpu()
{
// LPU u: Load Program Counter Upper on Status 1
if (m_s)
m_page = m_op & 0x1f;
else
m_op |= 0x400; // indicate unhandled LPU
}
void hmcs40_cpu_device::op_tbr()
{
// TBR p: Table Branch
u16 address = m_a | m_b << 4 | m_c << 8 | (m_op & 7) << 9 | (m_pc & ~0x3f);
m_pc = address & m_pcmask;
}
void hmcs40_cpu_device::op_rtn()
{
// RTN: Return from Subroutine
pop_stack();
}
// Interrupt Instruction
void hmcs40_cpu_device::op_seie()
{
// SEIE: Set I/E
m_ie = 1;
}
void hmcs40_cpu_device::op_seif0()
{
// SEIF0: Set IF0
m_if[0] = 1;
}
void hmcs40_cpu_device::op_seif1()
{
// SEIF1: Set IF1
m_if[1] = 1;
}
void hmcs40_cpu_device::op_setf()
{
// SETF: Set TF
m_tf = 1;
}
void hmcs40_cpu_device::op_secf()
{
// SECF: Set CF
m_cf = 1;
}
void hmcs40_cpu_device::op_reie()
{
// REIE: Reset I/E
m_ie = 0;
}
void hmcs40_cpu_device::op_reif0()
{
// REIF0: Reset IF0
m_if[0] = 0;
}
void hmcs40_cpu_device::op_reif1()
{
// REIF1: Reset IF1
m_if[1] = 0;
}
void hmcs40_cpu_device::op_retf()
{
// RETF: Reset TF
m_tf = 0;
}
void hmcs40_cpu_device::op_recf()
{
// RECF: Reset CF
m_cf = 0;
}
void hmcs40_cpu_device::op_ti0()
{
// TI0: Test INT0
m_s = m_int[0];
}
void hmcs40_cpu_device::op_ti1()
{
// TI1: Test INT1
m_s = m_int[1];
}
void hmcs40_cpu_device::op_tif0()
{
// TIF0: Test IF0
m_s = m_if[0];
}
void hmcs40_cpu_device::op_tif1()
{
// TIF1: Test IF1
m_s = m_if[1];
}
void hmcs40_cpu_device::op_ttf()
{
// TTF: Test TF
m_s = m_tf;
}
void hmcs40_cpu_device::op_lti()
{
// LTI i: Load Timer/Counter from Immediate
m_tc = m_i;
m_prescaler = 0;
}
void hmcs40_cpu_device::op_lta()
{
// LTA: Load Timer/Counter from A
m_tc = m_a;
m_prescaler = 0;
}
void hmcs40_cpu_device::op_lat()
{
// LAT: Load A from Timer/Counter
m_a = m_tc;
}
void hmcs40_cpu_device::op_rtni()
{
// RTNI: Return from Interrupt
op_seie();
op_rtn();
}
// Input/Output Instruction
void hmcs40_cpu_device::op_sed()
{
// SED: Set Discrete I/O Latch
write_d(m_y, 1);
}
void hmcs40_cpu_device::op_red()
{
// RED: Reset Discrete I/O Latch
write_d(m_y, 0);
}
void hmcs40_cpu_device::op_td()
{
// TD: Test Discrete I/O Latch
m_s = read_d(m_y);
}
void hmcs40_cpu_device::op_sedd()
{
// SEDD n: Set Discrete I/O Latch Direct
write_d(m_op & 3, 1);
}
void hmcs40_cpu_device::op_redd()
{
// REDD n: Reset Discrete I/O Latch Direct
write_d(m_op & 3, 0);
}
void hmcs40_cpu_device::op_lar()
{
// LAR p: Load A from R-Port Register
m_a = read_r(m_op & 7);
}
void hmcs40_cpu_device::op_lbr()
{
// LBR p: Load B from R-Port Register
m_b = read_r(m_op & 7);
}
void hmcs40_cpu_device::op_lra()
{
// LRA p: Load R-Port Register from A
write_r(m_op & 7, m_a);
}
void hmcs40_cpu_device::op_lrb()
{
// LRB p: Load R-Port Register from B
write_r(m_op & 7, m_b);
}
void hmcs40_cpu_device::op_p()
{
cycle();
// P p: Pattern Generation
u16 address = m_a | m_b << 4 | m_c << 8 | (m_op & 7) << 9 | (m_pc & ~0x3f);
u16 o = m_program->read_word(address & m_prgmask);
// destination is determined by the 2 highest bits
if (o & 0x100)
{
// B3 B2 B1 B0 A0 A1 A2 A3
m_a = bitswap<4>(o,0,1,2,3);
m_b = o >> 4 & 0xf;
}
if (o & 0x200)
{
// R20 R21 R22 R23 R30 R31 R32 R33
o = bitswap<8>(o,0,1,2,3,4,5,6,7);
write_r(2, o & 0xf);
write_r(3, o >> 4 & 0xf);
}
}