mirror of
https://github.com/holub/mame
synced 2025-05-30 01:23:07 +03:00
684 lines
10 KiB
C++
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);
|
|
}
|
|
}
|