// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************
am2910.cpp
AMD Am2910 Microprogram Controller emulation
TODO:
- Check /RLD behavior
- Find and fix bugs that almost surely exist
***************************************************************************/
#include "emu.h"
#include "am2910.h"
#define LOG_INSN (1U << 1)
#define LOG_STACK (1U << 2)
#define LOG_ERROR (1U << 3)
#define LOG_ALL (LOG_INSN | LOG_STACK | LOG_ERROR)
#define VERBOSE (0)
#include "logmacro.h"
/*****************************************************************************/
DEFINE_DEVICE_TYPE(AM2910, am2910_device, "am2910", "AMD Am2910 Microprogram Controller")
//-------------------------------------------------
// am2910_device - constructor
//-------------------------------------------------
am2910_device::am2910_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, AM2910, tag, owner, clock)
, m_pc(0)
, m_r(0)
, m_sp(0)
, m_cc(0)
, m_ccen(0)
, m_ci(0)
, m_rld(0)
, m_cp(0)
, m_y(*this)
, m_full(*this)
, m_pl(*this)
, m_map(*this)
, m_vect(*this)
{
}
void am2910_device::device_start()
{
save_item(NAME(m_pc));
save_item(NAME(m_r));
save_item(NAME(m_sp));
save_item(NAME(m_stack));
save_item(NAME(m_cc));
save_item(NAME(m_ccen));
save_item(NAME(m_ci));
save_item(NAME(m_rld));
save_item(NAME(m_cp));
save_item(NAME(m_d));
save_item(NAME(m_i));
if (clock())
m_execute_timer = timer_alloc(FUNC(am2910_device::clock_tick), this);
else
m_execute_timer = nullptr;
}
void am2910_device::device_reset()
{
m_pc = 0;
m_r = 0;
m_sp = 0;
memset(m_stack, 0, sizeof(uint16_t) * 5);
if (m_execute_timer)
m_execute_timer->adjust(attotime::from_hz(clock()), 0, attotime::from_hz(clock()));
}
TIMER_CALLBACK_MEMBER(am2910_device::clock_tick)
{
cp_w(m_cp ? 0 : 1);
}
void am2910_device::cc_w(int state)
{
m_cc = state;
}
void am2910_device::ccen_w(int state)
{
m_ccen = state;
}
void am2910_device::ci_w(int state)
{
m_ci = state;
}
void am2910_device::rld_w(int state)
{
m_rld = state;
}
void am2910_device::cp_w(int state)
{
int old_state = m_cp;
m_cp = state;
if (!old_state && state)
execute();
}
void am2910_device::d_w(uint16_t data)
{
m_d = data;
}
void am2910_device::i_w(uint8_t data)
{
m_i = data;
}
void am2910_device::update_source()
{
switch (m_i)
{
case 2:
m_pl(0);
m_map(1);
m_vect(0);
break;
case 6:
m_pl(0);
m_map(0);
m_vect(1);
break;
default:
m_pl(1);
m_map(0);
m_vect(0);
break;
}
}
void am2910_device::execute()
{
if (m_rld == 0)
m_r = m_d;
m_pc += (uint16_t)m_ci;
switch (m_i)
{
case 0: // JZ, Jump Zero
LOGMASKED(LOG_INSN, "%04x: JZ\n", m_pc);
m_pc = 0;
m_sp = 0;
break;
case 1: // CJS, Conditional Jump-To-Subroutine PL
LOGMASKED(LOG_INSN, "%04x: CJS\n", m_pc);
if (test_pass())
{
push(m_pc);
m_pc = m_d;
}
break;
case 2: // JMAP, Jump Map
LOGMASKED(LOG_INSN, "%04x: JMAP\n", m_pc);
m_pc = m_d;
break;
case 3: // CJP, Conditional Jump PL
LOGMASKED(LOG_INSN, "%04x: CJP\n", m_pc);
if (test_pass())
m_pc = m_d;
break;
case 4: // PUSH, Push / Conditional Load Counter
LOGMASKED(LOG_INSN, "%04x: PUSH\n", m_pc);
push(m_pc);
if (test_pass())
m_r = m_d;
break;
case 5: // JSRP, Conditional JSB R/PL
LOGMASKED(LOG_INSN, "%04x: JSRP\n", m_pc);
push(m_pc);
if (test_pass())
m_pc = m_d;
else
m_pc = m_r;
break;
case 6: // CJV, Conditional Jump Vector
LOGMASKED(LOG_INSN, "%04x: CJV\n", m_pc);
if (test_pass())
{
m_pc = m_d;
}
break;
case 7: // JRP, Conditional Jump R/PL
LOGMASKED(LOG_INSN, "%04x: JRP\n", m_pc);
if (test_pass())
m_pc = m_d;
else
m_pc = m_r;
break;
case 8: // RFCT, Repeat Loop, Counter != 0
LOGMASKED(LOG_INSN, "%04x: RFCT\n", m_pc);
if (m_r != 0)
{
m_r--;
m_pc = m_stack[m_sp];
}
break;
case 9: // RPCT, Repeat PL, Counter != 0
LOGMASKED(LOG_INSN, "%04x: RPCT\n", m_pc);
if (m_r != 0)
{
m_r--;
m_pc = m_d;
}
break;
case 10: // CRTN, Conditional Return
LOGMASKED(LOG_INSN, "%04x: CRTN\n", m_pc);
if (test_pass())
m_pc = m_stack[m_sp];
break;
case 11: // CJPP, Conditional Jump PL & Pop
LOGMASKED(LOG_INSN, "%04x: CJPP\n", m_pc);
if (test_pass())
{
m_pc = m_d;
pop();
}
break;
case 12: // LDCT, Load Counter & Continue
LOGMASKED(LOG_INSN, "%04x: LDCT\n", m_pc);
m_r = m_d;
break;
case 13: // LOOP, Test End Loop
LOGMASKED(LOG_INSN, "%04x: LOOP\n", m_pc);
if (!test_pass())
m_pc = m_stack[m_sp];
break;
case 14: // CONT, Continue
LOGMASKED(LOG_INSN, "%04x: CONT\n", m_pc);
break;
case 15: // TWB, Three-Way Branch
LOGMASKED(LOG_INSN, "%04x: TWB\n", m_pc);
if (!test_pass())
{
if (m_r != 0)
{
m_r--;
m_pc = m_stack[m_sp];
}
else
{
m_pc = m_d;
}
}
break;
default:
LOGMASKED(LOG_ERROR, "Unrecognized Instruction: %08x\n", m_i);
}
m_y(m_pc);
}
bool am2910_device::test_pass()
{
return m_ccen != 0 || m_cc == 0;
}
void am2910_device::push(uint16_t value)
{
if (m_sp < 4)
{
m_sp++;
if (m_sp == 4)
{
m_full(1);
}
}
m_stack[m_sp] = value;
}
void am2910_device::pop()
{
if (m_sp > 0)
{
m_sp--;
if (m_sp == 3)
{
m_full(0);
}
}
}