summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/am2910.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/am2910.cpp')
-rw-r--r--src/devices/machine/am2910.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/devices/machine/am2910.cpp b/src/devices/machine/am2910.cpp
new file mode 100644
index 00000000000..f547d93f00b
--- /dev/null
+++ b/src/devices/machine/am2910.cpp
@@ -0,0 +1,306 @@
+// 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 (1 << 0)
+#define LOG_STACK (1 << 1)
+#define LOG_ERROR (1 << 2)
+#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));
+
+ m_y.resolve_safe();
+ m_full.resolve_safe();
+ m_pl.resolve_safe();
+ m_map.resolve_safe();
+ m_vect.resolve_safe();
+
+ if (clock())
+ m_execute_timer = timer_alloc(TIMER_CLOCK);
+ 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()));
+}
+
+void am2910_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == TIMER_CLOCK)
+ {
+ cp_w(m_cp ? 0 : 1);
+ }
+}
+
+WRITE_LINE_MEMBER(am2910_device::cc_w)
+{
+ m_cc = state;
+}
+
+WRITE_LINE_MEMBER(am2910_device::ccen_w)
+{
+ m_ccen = state;
+}
+
+WRITE_LINE_MEMBER(am2910_device::ci_w)
+{
+ m_ci = state;
+}
+
+WRITE_LINE_MEMBER(am2910_device::rld_w)
+{
+ m_rld = state;
+}
+
+WRITE_LINE_MEMBER(am2910_device::cp_w)
+{
+ 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);
+ }
+ }
+}