summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2020-01-13 09:35:48 -0500
committer AJR <ajrhacker@users.noreply.github.com>2020-01-13 09:35:48 -0500
commitafd37345c8bd8ddab48360054796566306e829b2 (patch)
treeb4169fc9bfa13ebe24c08d60a727aa5f235c616f
parent0605a13cc8111dae47da56c205fbd1f6fc13f943 (diff)
Add preliminary execution core for PACE architecture
-rw-r--r--src/devices/cpu/pace/pace.cpp848
-rw-r--r--src/devices/cpu/pace/pace.h116
-rw-r--r--src/mame/drivers/ns5652.cpp26
3 files changed, 939 insertions, 51 deletions
diff --git a/src/devices/cpu/pace/pace.cpp b/src/devices/cpu/pace/pace.cpp
index 03b36312930..73d6edfb6c9 100644
--- a/src/devices/cpu/pace/pace.cpp
+++ b/src/devices/cpu/pace/pace.cpp
@@ -5,21 +5,31 @@
National Semiconductor PACE/INS8900
The IPC-16A PACE (Processing and Control Element) was one of the first
- commercially available 16-bit microprocessors. It was a successor to
- the multiple-chip IMP-16 processor (itself loosely based on the Data
- General Nova) and implemented a similar four-accumulator architecture
- in a single PMOS LSI package using internal microcode. The two clock
- inputs with non-overlapping low phases required by PACE may be
- generated by the DP8302 STE (System Timing Element), which divides its
- oscillator input by 2.
+ commercially available 16-bit microprocessors. It was am architectural
+ successor to the multiple-chip IMP-16 processor contained in a single
+ PMOS LSI package with internal microcode (though the emulation
+ currently uses a state machine instead, loosely based on the published
+ IMP-16 microcode). The two clock inputs with non-overlapping low
+ phases required by PACE may be generated by the DP8302 STE (System
+ Timing Element), which divides its oscillator input by 2.
+
+ Like the Data General Nova, PACE and IMP-16 have four 16-bit
+ accumulators, two of which can be used as index registers. The program
+ counter can also be used as a base for indexing, and instructions may
+ also directly access 256 words of memory. The upper 128 words of the
+ direct region may be mapped to either 0080–00FF or FF80–FFFF,
+ depending on the state of the BPS input (which National suggests
+ connecting to one of the flag outputs). The on-chip 10-level LIFO
+ stack (IMP-16 had a 16-level stack) may hold both return addresses
+ and register data. The flag register may be transferred to or from
+ the stack or one of the accumulators.
The standard machine cycle takes 4 clock periods (and the shortest
instructions take 4 cycles each), though cycles can be stretched by
- asserting the EXTEND pin. Six interrupts are available, one triggered
- only by internal stack full/empty conditions; the nonmaskable level 0
- interrupt is intended primarily for debugging. The on-chip 10-level
- LIFO stack may hold both return addresses and register data. All
- instructions are single-word.
+ asserting the EXTEND pin (the emulation does not currently support
+ this). Six interrupts are available, one triggered only by internal
+ stack full/empty conditions; the nonmaskable level 0 interrupt is
+ intended primarily for debugging. Each instruction is one word.
INS8900 was a NMOS reimplementation of PACE which takes a single-phase
clock and allows up to 2 MHz operation. It has different power supply
@@ -44,6 +54,8 @@ DEFINE_DEVICE_TYPE(INS8900, ins8900_device, "ins8900", "National Semiconductor I
// DEVICE CONSTRUCTION AND INITIALIZATION
//**************************************************************************
+ALLOW_SAVE_TYPE(pace_device::cycle);
+
//-------------------------------------------------
// pace_device - constructor
//-------------------------------------------------
@@ -52,15 +64,23 @@ pace_device::pace_device(const machine_config &mconfig, device_type type, const
: cpu_device(mconfig, type, tag, owner, clock)
, m_space_config("program", ENDIANNESS_LITTLE, 16, 16, -1)
, m_space(nullptr)
- , m_inst_cache(nullptr)
+ , m_cache(nullptr)
+ , m_bps_callback(*this)
, m_jc_callback{{*this}, {*this}, {*this}}
, m_flag_callback{{*this}, {*this}, {*this}, {*this}}
- , m_icount(0)
- , m_pc(0)
, m_fr(0xffff)
+ , m_pc(0)
+ , m_mdr(0)
+ , m_mar(0)
, m_ac{0, 0, 0, 0}
- , m_sp(0)
- , m_stk{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ , m_stkp(0)
+ , m_stack_depth(0)
+ , m_stack{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ , m_ppc(0)
+ , m_cir(0)
+ , m_shift_link(false)
+ , m_cycle(cycle::UNKNOWN)
+ , m_icount(0)
{
}
@@ -97,6 +117,7 @@ device_memory_interface::space_config_vector pace_device::memory_space_config()
void pace_device::device_resolve_objects()
{
// resolve callbacks
+ m_bps_callback.resolve_safe(0);
for (auto &cb : m_jc_callback)
cb.resolve_safe(0);
for (auto &cb : m_flag_callback)
@@ -112,30 +133,38 @@ void pace_device::device_start()
{
// get memory spaces
m_space = &space(AS_PROGRAM);
- m_inst_cache = m_space->cache<1, -1, ENDIANNESS_LITTLE>();
+ m_cache = m_space->cache<1, -1, ENDIANNESS_LITTLE>();
set_icountptr(m_icount);
// debug state registration
state_add(PACE_PC, "PC", m_pc);
state_add(STATE_GENPC, "GENPC", m_pc).noshow();
- state_add(STATE_GENPCBASE, "GENPCBASE", m_pc).noshow();
- state_add<u16>(PACE_FR, "FR", [this]() { return m_fr; }, [this](u16 data) { fr_w(data); });
+ state_add(STATE_GENPCBASE, "GENPCBASE", m_ppc).noshow();
+ state_add<u16>(PACE_FR, "FR", [this]() { return m_fr; }, [this](u16 data) { set_fr(data); });
state_add(STATE_GENFLAGS, "GENFLAGS", m_fr).noshow();
for (int i = 0; i < 4; i++)
state_add(PACE_AC0 + i, string_format("AC%d", i).c_str(), m_ac[i]);
- state_add<u8>(PACE_SP, "SP",
- [this]() { return m_sp; },
- [this](u8 data) { m_sp = data < 10 ? data : BIT(data, 0) ? 9 : 0; }).mask(0xf);
+ state_add<u8>(PACE_STKD, "STKD", [this]() { return m_stack_depth; }, [this](u8 data) { m_stack_depth = data >= 10 ? 10 : data; }).mask(0xf);
for (int i = 0; i < 10; i++)
- state_add(PACE_STK0 + i, string_format("STK%d", i).c_str(), m_stk[i]);
+ state_add<u16>(PACE_STK0 + i, string_format("STK%d", i).c_str(),
+ [this, i]() { return m_stack[m_stkp > i ? m_stkp - i - 1 : m_stkp + 9 - i]; },
+ [this, i](u16 data) { m_stack[m_stkp > i ? m_stkp - i - 1 : m_stkp + 9 - i] = data; }
+ );
// save states
- save_item(NAME(m_pc));
save_item(NAME(m_fr));
+ save_item(NAME(m_pc));
+ save_item(NAME(m_mdr));
+ save_item(NAME(m_mar));
save_item(NAME(m_ac));
- save_item(NAME(m_sp));
- save_item(NAME(m_stk));
+ save_item(NAME(m_stkp));
+ save_item(NAME(m_stack_depth));
+ save_item(NAME(m_stack));
+ save_item(NAME(m_ppc));
+ save_item(NAME(m_cir));
+ save_item(NAME(m_shift_link));
+ save_item(NAME(m_cycle));
}
@@ -145,9 +174,11 @@ void pace_device::device_start()
void pace_device::device_reset()
{
- m_pc = 0;
- fr_w(0);
- m_sp = 0;
+ set_fr(0);
+ m_pc = m_ppc = 0;
+ m_stkp = 0;
+ m_stack_depth = 0;
+ m_cycle = cycle::IFETCH_M1;
}
@@ -163,21 +194,60 @@ std::unique_ptr<util::disasm_interface> pace_device::create_disassembler()
//**************************************************************************
-// I/O LINES, INTERRUPTS AND FLAGS
+// I/O LINES, INTERRUPTS AND CONTROL FLAGS
//**************************************************************************
//-------------------------------------------------
-// fr_w - update the flag register
+// set_control_flag - set one bit in the flag
+// register
//-------------------------------------------------
-void pace_device::fr_w(u16 data)
+void pace_device::set_control_flag(u8 fc)
{
- for (int i = 0; i < 4; i++)
- if (BIT(data, 11 + i) != BIT(m_fr, 11 + i))
- m_flag_callback[i](BIT(data, 11 + i));
+ if (fc == 15)
+ {
+ // TODO: SFLG/PFLG F15 is used to exit from level 0 interrupt
+ }
+ else if (!BIT(m_fr, fc))
+ {
+ m_fr |= 1 << fc;
+ if (fc >= 11 && fc <= 14)
+ m_flag_callback[fc - 11](1);
+ }
+}
+
+
+//-------------------------------------------------
+// reset_control_flag - reset one bit in the flag
+// register
+//-------------------------------------------------
+
+void pace_device::reset_control_flag(u8 fc)
+{
+ // F0 and F15 are always 1 (not implemented in hardware)
+ if (fc != 0 && fc != 15 && BIT(m_fr, fc))
+ {
+ m_fr &= ~(1 << fc);
+ if (fc >= 11 && fc <= 14)
+ m_flag_callback[fc - 11](0);
+ }
+}
+
+
+//-------------------------------------------------
+// set_fr - update the flag register
+//-------------------------------------------------
+
+void pace_device::set_fr(u16 r)
+{
+ // F0 and F15 are always logic 1 (not implemented in hardware)
+ r |= 0x8001;
+ std::swap(m_fr, r);
- // Bits 0 and 15 are always logic 1
- m_fr = data | 0x8001;
+ // Update flag outputs
+ for (int i = 0; i < 4; i++)
+ if (BIT(r, 11 + i) != BIT(m_fr, 11 + i))
+ m_flag_callback[i](BIT(m_fr, 11 + i));
}
@@ -192,17 +262,711 @@ void pace_device::execute_set_input(int irqline, int state)
//**************************************************************************
+// CONDITION CODES AND ALU
+//**************************************************************************
+
+//-------------------------------------------------
+// sign_bit - test the sign bit of a word, taking
+// BYTE mode into account
+//-------------------------------------------------
+
+bool pace_device::sign_bit(u16 r) const noexcept
+{
+ return BIT(m_fr, 10) ? BIT(r, 7) : BIT(r, 15);
+}
+
+
+//-------------------------------------------------
+// equals_0 - test whether a word is equal to
+// zero, taking BYTE mode into account
+//-------------------------------------------------
+
+bool pace_device::equals_0(u16 r) const noexcept
+{
+ // Only lower 7 bits are tested in BYTE mode
+ return (BIT(m_fr, 10) ? r & 0x00ff : r) == 0;
+}
+
+
+//-------------------------------------------------
+// poll_condition - test condition by index
+//-------------------------------------------------
+
+bool pace_device::poll_condition(u8 cc)
+{
+ switch (cc)
+ {
+ case 0: // STFL
+ return m_stack_depth == 10;
+
+ case 1: // REQ0
+ return equals_0(m_ac[0]);
+
+ case 2: // PSIGN
+ return !sign_bit(m_ac[0]);
+
+ case 3: // BIT0
+ return BIT(m_ac[0], 0);
+
+ case 4: // BIT1
+ return BIT(m_ac[0], 1);
+
+ case 5: // NREQ0
+ return !equals_0(m_ac[0]);
+
+ case 6: // BIT2
+ return BIT(m_ac[0], 2);
+
+ case 7: // CONTIN (TODO)
+ return false;
+
+ case 8: // LINK
+ return BIT(m_fr, 8);
+
+ case 9: // IEN
+ return BIT(m_fr, 9);
+
+ case 10: // CARRY
+ return BIT(m_fr, 7);
+
+ case 11: // NSIGN
+ return sign_bit(m_ac[0]);
+
+ case 12: // OVF
+ return BIT(m_fr, 6);
+
+ case 13:
+ case 14:
+ case 15:
+ return m_jc_callback[cc - 13]();
+
+ default:
+ return false;
+ }
+}
+
+
+//-------------------------------------------------
+// sign_extend - extend byte sign into upper 8
+// bits of word
+//-------------------------------------------------
+
+void pace_device::sign_extend(u16 &r)
+{
+ if (BIT(r, 7))
+ r |= 0xff00;
+ else
+ r &= 0x00ff;
+}
+
+
+//-------------------------------------------------
+// add - add source to destination, with or
+// without carry
+//-------------------------------------------------
+
+void pace_device::add(u16 &dr, u16 sr, bool c)
+{
+ bool carry_out = sign_bit((u32(dr) + u32(sr) + (c && BIT(m_fr, 7) ? 1 : 0)) >> (BIT(m_fr, 10) ? 1 : 9));
+ s32 overflow_test = BIT(m_fr, 10) ? s32(s8(dr & 0x00ff)) + s32(s8(sr & 0x00ff)) : s32(s16(dr)) + s32(s16(sr));
+ dr += sr + (c && BIT(m_fr, 7) ? 1 : 0);
+
+ if (carry_out)
+ set_control_flag(7);
+ else
+ reset_control_flag(7);
+
+ if (overflow_test > s16(0x7fff) || overflow_test < s16(0x8000))
+ set_control_flag(6);
+ else
+ reset_control_flag(6);
+}
+
+
+//-------------------------------------------------
+// decimal_add - perform one adjusted stage of
+// decimal addition
+//-------------------------------------------------
+
+void pace_device::decimal_add(u16 &dr, u16 sr, unsigned stage)
+{
+ bool carry_out = ((sr >> stage) & 15) + ((dr >> stage) & 15) >= BIT(m_fr, 7) ? 9 : 10;
+ s32 overflow_test = s32(s16(sr << (12 - stage))) + s32(s16(dr << (12 - stage)));
+ dr += (sr & (0x000f << stage)) + ((BIT(m_fr, 7) + (carry_out ? 6 : 0)) << stage);
+
+ if (stage == 0)
+ reset_control_flag(7);
+ else if (stage == BIT(m_fr, 10) ? 4 : 12)
+ {
+ if (carry_out)
+ set_control_flag(7);
+ else
+ reset_control_flag(7);
+
+ if (overflow_test > s16(0x7fff) || overflow_test < s16(0x8000))
+ set_control_flag(6);
+ else
+ reset_control_flag(6);
+ }
+}
+
+
+//-------------------------------------------------
+// prepare_shift - setup registers for shift or
+// rotate instruction
+//-------------------------------------------------
+
+void pace_device::prepare_shift()
+{
+ m_shift_link = BIT(m_mdr, 0);
+ m_mdr = (m_mdr & 0x00fe) >> 1;
+}
+
+
+//-------------------------------------------------
+// shift_left - shift or rotate a register left
+// by one bit
+//-------------------------------------------------
+
+void pace_device::shift_left(u16 &r, bool rotate)
+{
+ bool shift_out = sign_bit(r);
+ r <<= 1;
+ if (rotate && (m_shift_link ? BIT(m_fr, 8) : shift_out))
+ r |= 0x0001;
+ if (BIT(m_fr, 10))
+ r &= 0x00ff;
+
+ if (m_shift_link)
+ {
+ if (shift_out)
+ set_control_flag(8);
+ else
+ reset_control_flag(8);
+ }
+}
+
+
+//-------------------------------------------------
+// shift_right - shift or rotate a register right
+// by one bit
+//-------------------------------------------------
+
+void pace_device::shift_right(u16 &r, bool rotate)
+{
+ bool shift_out = BIT(r, 0);
+ r >>= 1;
+ if (rotate && (m_shift_link ? BIT(m_fr, 8) : shift_out))
+ r |= BIT(m_fr, 10) ? 0x0080 : 0x8000;
+
+ if (m_shift_link)
+ {
+ if (shift_out)
+ set_control_flag(8);
+ else
+ reset_control_flag(8);
+ }
+}
+
+
+//**************************************************************************
+// LIFO STACK MANAGEMENT
+//**************************************************************************
+
+//-------------------------------------------------
+// stack_push - push one word onto the stack
+//-------------------------------------------------
+
+void pace_device::stack_push(u16 r)
+{
+ m_stack[m_stkp] = r;
+ m_stkp = m_stkp == 9 ? 0 : m_stkp + 1;
+
+ if (m_stack_depth == 10)
+ logerror("%04X: Stack overflow\n", m_ppc);
+ else
+ m_stack_depth++; // TODO: stack full interrupt
+}
+
+
+//-------------------------------------------------
+// stack_pull - extract top entry from the stack
+//-------------------------------------------------
+
+u16 pace_device::stack_pull()
+{
+ if (m_stack_depth == 0)
+ {
+ logerror("%04X: Stack underflow\n", m_ppc);
+ return 0;
+ }
+ m_stack_depth--;
+
+ m_stkp = m_stkp == 0 ? 9 : m_stkp - 1;
+ return std::exchange(m_stack[m_stkp], 0);
+}
+
+
+//**************************************************************************
// PROGRAM EXECUTION
//**************************************************************************
+// instruction decode
+const pace_device::cycle pace_device::s_decode[64] = {
+ cycle::HALT_M4, cycle::CFR_M4, cycle::CRF_M4, cycle::PUSHF_M4,
+ cycle::PULLF_M4, cycle::JSR_M4, cycle::JMP_M4, cycle::XCHRS_M4,
+ cycle::ROL_M4, cycle::ROR_M4, cycle::SHL_M4, cycle::SHR_M4,
+ cycle::PFLG_M4, cycle::PFLG_M4, cycle::PFLG_M4, cycle::PFLG_M4,
+ cycle::BOC_M4, cycle::BOC_M4, cycle::BOC_M4, cycle::BOC_M4,
+ cycle::LI_M4, cycle::RAND_M4, cycle::RXOR_M4, cycle::RCPY_M4,
+ cycle::PUSH_M4, cycle::PULL_M4, cycle::RADD_M4, cycle::RXCH_M4,
+ cycle::CAI_M4, cycle::RADC_M4, cycle::AISZ_M4, cycle::RTI_M4,
+ cycle::RTS_M4, cycle::UNKNOWN, cycle::DECA_M4, cycle::ISZ_M4,
+ cycle::SUBB_M4, cycle::JSR_IND_M4, cycle::JMP_IND_M4, cycle::SKG_M4,
+ cycle::LD_IND_M4, cycle::OR_M4, cycle::AND_M4, cycle::DSZ_M4,
+ cycle::ST_IND_M4, cycle::UNKNOWN, cycle::SKAZ_M4, cycle::LSEX_M4,
+ cycle::LD_M4, cycle::LD_M4, cycle::LD_M4, cycle::LD_M4,
+ cycle::ST_M4, cycle::ST_M4, cycle::ST_M4, cycle::ST_M4,
+ cycle::ADD_M4, cycle::ADD_M4, cycle::ADD_M4, cycle::ADD_M4,
+ cycle::SKNE_M4, cycle::SKNE_M4, cycle::SKNE_M4, cycle::SKNE_M4
+};
+
+
+//-------------------------------------------------
+// read_instruction - read the next instruction
+// into internal registers
+//-------------------------------------------------
+
+void pace_device::read_instruction()
+{
+ // TODO: interrupt check
+ m_ppc = m_pc;
+ debugger_instruction_hook(m_pc);
+ m_mdr = m_cache->read_word(m_pc++);
+ m_cir = m_mdr >> 6;
+ sign_extend(m_mdr);
+}
+
+
+//-------------------------------------------------
+// get_effective_address - calculate effective
+// address for the current instruction
+//-------------------------------------------------
+
+u16 pace_device::get_effective_address()
+{
+ if ((m_cir & 0x00c) == 0)
+ {
+ // Direct page mapping depends on BPS
+ return m_mdr & (m_bps_callback() ? 0xffff : 0x00ff);
+ }
+ else if ((m_cir & 0x00c) == 0x004)
+ {
+ // PC-relative
+ return m_mdr + m_pc;
+ }
+ else
+ {
+ // AC2 or AC3-indexed
+ return m_mdr + m_ac[(m_cir & 0x00c) >> 2];
+ }
+}
+
+
+//-------------------------------------------------
+// read_effective_address - read the word at the
+// effective address into MDR, if applicable to
+// the current instruction
+//-------------------------------------------------
+
+void pace_device::read_effective_address()
+{
+ // All opcodes with bit 15 set read from an EA except RTS and non-indirect ST
+ if (m_cir > 0x200 && (m_cir & 0x3c0) != 0x340)
+ {
+ if ((m_cir & 0x00c) == 0x004)
+ m_mdr = m_cache->read_word(m_mar);
+ else
+ m_mdr = m_space->read_word(m_mar);
+ }
+}
+
+
+//-------------------------------------------------
+// write_effective_address - write one word to
+// the effective address
+//-------------------------------------------------
+
+void pace_device::write_effective_address(u16 r)
+{
+ if ((m_cir & 0x00c) == 0x004)
+ m_cache->write_word(m_mar, r);
+ else
+ m_space->write_word(m_mar, r);
+}
+
+
+//-------------------------------------------------
+// execute_one - execute one machine cycle of an
+// instruction
+//-------------------------------------------------
+
+pace_device::cycle pace_device::execute_one()
+{
+ switch (m_cycle)
+ {
+ case cycle::IFETCH_M1:
+ read_instruction();
+ return cycle::LEA_M2;
+
+ case cycle::LEA_M2:
+ m_mar = get_effective_address();
+ return cycle::RDEA_M3;
+
+ case cycle::RDEA_M3:
+ read_effective_address();
+ return s_decode[(m_cir & 0x3f0) >> 4];
+
+ case cycle::BOC_M4:
+ return cycle::BOC_M5;
+
+ case cycle::BOC_M5:
+ return poll_condition((m_cir & 0x03c) >> 2) ? cycle::BRANCH : cycle::IFETCH_M1;
+
+ case cycle::JMP_M4:
+ case cycle::JSR_M5:
+ m_pc = m_mar;
+ return cycle::IFETCH_M1;
+
+ case cycle::JMP_IND_M4:
+ case cycle::JSR_IND_M5:
+ m_pc = m_mdr;
+ return cycle::IFETCH_M1;
+
+ case cycle::JSR_M4:
+ stack_push(m_pc);
+ return cycle::JSR_M5;
+
+ case cycle::JSR_IND_M4:
+ stack_push(m_pc);
+ return cycle::JSR_IND_M5;
+
+ case cycle::RTS_M4:
+ case cycle::RTI_M5:
+ m_pc = stack_pull();
+ return cycle::RTS_M5;
+
+ case cycle::RTI_M4:
+ set_control_flag(9); // enable interrupts
+ return cycle::RTI_M5;
+
+ case cycle::RTS_M5:
+ case cycle::RTI_M6:
+ case cycle::BRANCH:
+ m_pc += m_mdr;
+ return cycle::IFETCH_M1;
+
+ case cycle::SKNE_M4:
+ m_mdr ^= m_ac[(m_cir & 0x030) >> 4];
+ return cycle::SKNE_M5;
+
+ case cycle::SKNE_M5:
+ return equals_0(m_mdr) ? cycle::IFETCH_M1 : cycle::SKIP;
+
+ case cycle::SKG_M4:
+ if (BIT(m_fr, 10))
+ sign_extend(m_mdr);
+ return cycle::SKG_M5;
+
+ case cycle::SKG_M5:
+ m_mdr = ~m_mdr + m_ac[0];
+ return cycle::SKG_M6;
+
+ case cycle::SKG_M6:
+ if (BIT(m_fr, 10))
+ sign_extend(m_mdr);
+ return cycle::SKG_M7;
+
+ case cycle::SKG_M7:
+ return BIT(m_mdr, 15) ? cycle::SKIP : cycle::IFETCH_M1;
+
+ case cycle::SKAZ_M4:
+ m_mdr &= m_ac[0];
+ return cycle::SKAZ_M5;
+
+ case cycle::SKAZ_M5:
+ case cycle::ISZ_M7:
+ case cycle::DSZ_M7:
+ return equals_0(m_mdr) ? cycle::SKIP : cycle::IFETCH_M1;
+
+ case cycle::ISZ_M4:
+ m_mdr += 1;
+ return cycle::ISZ_M5;
+
+ case cycle::ISZ_M5:
+ case cycle::DSZ_M5:
+ write_effective_address(m_mdr);
+ return cycle::ISZ_M6;
+
+ case cycle::ISZ_M6:
+ case cycle::DSZ_M6:
+ return cycle::ISZ_M7;
+
+ case cycle::DSZ_M4:
+ m_mdr -= 1;
+ return cycle::DSZ_M5;
+
+ case cycle::AISZ_M4:
+ m_ac[(m_cir & 0x00c) >> 2] += m_mdr;
+ return cycle::AISZ_M5;
+
+ case cycle::AISZ_M5:
+ // 16-bit test always performed even in BYTE mode
+ return m_ac[(m_cir & 0x00c) >> 2] == 0 ? cycle::SKIP : cycle::IFETCH_M1;
+
+ case cycle::SKIP:
+ m_pc += 1;
+ return cycle::IFETCH_M1;
+
+ case cycle::LD_M4:
+ m_ac[(m_cir & 0x030) >> 4] = m_mdr;
+ return cycle::IFETCH_M1;
+
+ case cycle::LD_IND_M4:
+ m_mdr = m_space->read_word(m_mdr);
+ return cycle::LD_IND_M5;
+
+ case cycle::LD_IND_M5:
+ m_ac[0] = m_mdr;
+ return cycle::IFETCH_M1;
+
+ case cycle::ST_M4:
+ write_effective_address(m_ac[(m_cir & 0x030) >> 4]);
+ return cycle::IFETCH_M1;
+
+ case cycle::ST_IND_M4:
+ m_space->write_word(m_mdr, m_ac[0]);
+ return cycle::IFETCH_M1;
+
+ case cycle::LSEX_M4:
+ m_mdr = m_space->read_word(m_mdr);
+ sign_extend(m_mdr);
+ return cycle::IFETCH_M1;
+
+ case cycle::AND_M4:
+ m_ac[0] &= m_mdr;
+ return cycle::IFETCH_M1;
+
+ case cycle::OR_M4:
+ m_ac[0] |= m_mdr;
+ return cycle::IFETCH_M1;
+
+ case cycle::ADD_M4:
+ add(m_ac[(m_cir & 0x030) >> 4], m_mdr, false);
+ return cycle::IFETCH_M1;
+
+ case cycle::SUBB_M4:
+ add(m_ac[0], ~m_mdr, true);
+ return cycle::IFETCH_M1;
+
+ case cycle::DECA_M4:
+ decimal_add(m_ac[0], m_mdr, 0);
+ return cycle::DECA_M5;
+
+ case cycle::DECA_M5:
+ decimal_add(m_ac[0], m_mdr, 4);
+ return cycle::DECA_M6;
+
+ case cycle::DECA_M6:
+ decimal_add(m_ac[0], m_mdr, 8);
+ return cycle::DECA_M7;
+
+ case cycle::DECA_M7:
+ decimal_add(m_ac[0], m_mdr, 12);
+ return cycle::IFETCH_M1;
+
+ case cycle::LI_M4:
+ case cycle::RXCH_M6:
+ case cycle::XCHRS_M6:
+ case cycle::CAI_M5:
+ m_ac[(m_cir & 0x00c) >> 2] = m_mdr;
+ return cycle::IFETCH_M1;
+
+ case cycle::RCPY_M4:
+ m_ac[(m_cir & 0x00c) >> 2] = m_ac[m_cir & 0x003];
+ return cycle::IFETCH_M1;
+
+ case cycle::RXCH_M4:
+ m_mdr = m_ac[(m_cir & 0x00c) >> 2];
+ return cycle::RXCH_M5;
+
+ case cycle::RXCH_M5:
+ m_ac[(m_cir & 0x00c) >> 2] = m_ac[m_cir & 0x003];
+ return cycle::RXCH_M6;
+
+ case cycle::XCHRS_M4:
+ m_mdr = stack_pull(); // IMP-16 uses MAR as the temporary
+ return cycle::XCHRS_M5;
+
+ case cycle::XCHRS_M5:
+ stack_push(m_ac[(m_cir & 0x00c) >> 2]);
+ return cycle::XCHRS_M6;
+
+ case cycle::CFR_M4:
+ m_ac[(m_cir & 0x00c) >> 2] = m_fr;
+ return cycle::IFETCH_M1;
+
+ case cycle::CRF_M4:
+ set_fr(m_ac[(m_cir & 0x00c) >> 2]);
+ return cycle::IFETCH_M1;
+
+ case cycle::PUSH_M4:
+ stack_push(m_ac[(m_cir & 0x00c) >> 2]);
+ return cycle::IFETCH_M1;
+
+ case cycle::PULL_M4:
+ m_ac[(m_cir & 0x00c) >> 2] = stack_pull();
+ return cycle::IFETCH_M1;
+
+ case cycle::PUSHF_M4:
+ stack_push(m_fr);
+ return cycle::IFETCH_M1;
+
+ case cycle::PULLF_M4:
+ set_fr(stack_pull());
+ return cycle::IFETCH_M1;
+
+ case cycle::RADD_M4:
+ add(m_ac[(m_cir & 0x00c) >> 2], m_ac[m_cir & 0x003], false);
+ return cycle::IFETCH_M1;
+
+ case cycle::RADC_M4:
+ add(m_ac[(m_cir & 0x00c) >> 2], m_ac[m_cir & 0x003], true);
+ return cycle::IFETCH_M1;
+
+ case cycle::RAND_M4:
+ m_ac[(m_cir & 0x00c) >> 2] &= m_ac[m_cir & 0x003];
+ return cycle::IFETCH_M1;
+
+ case cycle::RXOR_M4:
+ m_ac[(m_cir & 0x00c) >> 2] ^= m_ac[m_cir & 0x003];
+ return cycle::IFETCH_M1;
+
+ case cycle::CAI_M4:
+ m_mdr += ~m_ac[(m_cir & 0x00c) >> 2];
+ return cycle::CAI_M5;
+
+ case cycle::SHL_M4:
+ prepare_shift();
+ return cycle::SHL_M5;
+
+ case cycle::SHL_M5:
+ if (BIT(m_fr, 10))
+ m_ac[(m_cir & 0x00c) >> 2] &= 0x00ff;
+ return cycle::SHL_M6;
+
+ case cycle::SHL_M6:
+ return m_mdr == 0 ? cycle::IFETCH_M1 : cycle::SHL_M7;
+
+ case cycle::SHL_M7:
+ shift_left(m_ac[(m_cir & 0x00c) >> 2], false);
+ return cycle::SHL_M8;
+
+ case cycle::SHL_M8:
+ m_mdr -= 1;
+ return cycle::SHL_M6;
+
+ case cycle::SHR_M4:
+ prepare_shift();
+ return cycle::SHR_M5;
+
+ case cycle::SHR_M5:
+ if (BIT(m_fr, 10))
+ m_ac[(m_cir & 0x00c) >> 2] &= 0x00ff;
+ return cycle::SHR_M6;
+
+ case cycle::SHR_M6:
+ return m_mdr == 0 ? cycle::IFETCH_M1 : cycle::SHR_M7;
+
+ case cycle::SHR_M7:
+ shift_right(m_ac[(m_cir & 0x00c) >> 2], false);
+ return cycle::SHR_M8;
+
+ case cycle::SHR_M8:
+ m_mdr -= 1;
+ return cycle::SHR_M6;
+
+ case cycle::ROL_M4:
+ prepare_shift();
+ return cycle::ROL_M5;
+
+ case cycle::ROL_M5:
+ if (BIT(m_fr, 10))
+ m_ac[(m_cir & 0x00c) >> 2] &= 0x00ff;
+ return cycle::ROL_M6;
+
+ case cycle::ROL_M6:
+ return m_mdr == 0 ? cycle::IFETCH_M1 : cycle::ROL_M7;
+
+ case cycle::ROL_M7:
+ shift_left(m_ac[(m_cir & 0x00c) >> 2], true);
+ return cycle::ROL_M7;
+
+ case cycle::ROL_M8:
+ m_mdr -= 1;
+ return cycle::ROL_M5;
+
+ case cycle::ROR_M4:
+ prepare_shift();
+ return cycle::ROR_M5;
+
+ case cycle::ROR_M5:
+ if (BIT(m_fr, 10))
+ m_ac[(m_cir & 0x00c) >> 2] &= 0x00ff;
+ return cycle::ROR_M6;
+
+ case cycle::ROR_M6:
+ return m_mdr == 0 ? cycle::IFETCH_M1 : cycle::ROR_M7;
+
+ case cycle::ROR_M7:
+ shift_right(m_ac[(m_cir & 0x00c) >> 2], true);
+ return cycle::ROR_M8;
+
+ case cycle::ROR_M8:
+ m_mdr -= 1;
+ return cycle::ROR_M6;
+
+ case cycle::HALT_M4: // TODO
+ logerror("%04X: HALT opcode encountered\n", m_ppc);
+ return cycle::IFETCH_M1;
+
+ case cycle::PFLG_M4: // PFLG or SFLG
+ return cycle::PFLG_M5;
+
+ case cycle::PFLG_M5: // PFLG or SFLG
+ set_control_flag((m_cir & 0x03c) >> 2);
+ return BIT(m_mdr, 7) ? cycle::IFETCH_M1 : cycle::PFLG_M6;
+
+ case cycle::PFLG_M6: // PFLG only
+ reset_control_flag((m_cir & 0x03c) >> 2);
+ return cycle::IFETCH_M1;
+
+ case cycle::UNKNOWN:
+ logerror("%04X: Unknown %02XXX opcode encountered\n", m_ppc, m_cir >> 2);
+ return cycle::IFETCH_M1;
+ }
+}
+
+
//-------------------------------------------------
// execute_run -
//-------------------------------------------------
void pace_device::execute_run()
{
- // TODO
-
- debugger_instruction_hook(m_pc);
- m_icount = 0;
+ while (m_icount > 0)
+ {
+ m_cycle = execute_one();
+ m_icount--;
+ }
}
diff --git a/src/devices/cpu/pace/pace.h b/src/devices/cpu/pace/pace.h
index 60ba9766768..6fb0daaafeb 100644
--- a/src/devices/cpu/pace/pace.h
+++ b/src/devices/cpu/pace/pace.h
@@ -52,7 +52,7 @@ public:
PACE_PC,
PACE_FR,
PACE_AC0, PACE_AC1, PACE_AC2, PACE_AC3,
- PACE_SP,
+ PACE_STKD,
PACE_STK0, PACE_STK1, PACE_STK2, PACE_STK3, PACE_STK4,
PACE_STK5, PACE_STK6, PACE_STK7, PACE_STK8, PACE_STK9
};
@@ -86,25 +86,123 @@ protected:
virtual space_config_vector memory_space_config() const override;
private:
- // internal helpers
- void fr_w(u16 data);
+ enum class cycle {
+ IFETCH_M1, LEA_M2, RDEA_M3,
+
+ BOC_M4, BOC_M5,
+ JMP_M4,
+ JMP_IND_M4,
+ JSR_M4, JSR_M5,
+ JSR_IND_M4, JSR_IND_M5,
+ RTS_M4, RTS_M5,
+ RTI_M4, RTI_M5, RTI_M6,
+ BRANCH,
+
+ SKNE_M4, SKNE_M5,
+ SKG_M4, SKG_M5, SKG_M6, SKG_M7,
+ SKAZ_M4, SKAZ_M5,
+ ISZ_M4, ISZ_M5, ISZ_M6, ISZ_M7,
+ DSZ_M4, DSZ_M5, DSZ_M6, DSZ_M7,
+ AISZ_M4, AISZ_M5,
+ SKIP,
+
+ LD_M4,
+ LD_IND_M4, LD_IND_M5,
+ ST_M4,
+ ST_IND_M4,
+ LSEX_M4,
+
+ AND_M4,
+ OR_M4,
+ ADD_M4,
+ SUBB_M4,
+ DECA_M4, DECA_M5, DECA_M6, DECA_M7,
+
+ LI_M4,
+ RCPY_M4,
+ RXCH_M4, RXCH_M5, RXCH_M6,
+ XCHRS_M4, XCHRS_M5, XCHRS_M6,
+ CFR_M4,
+ CRF_M4,
+ PUSH_M4,
+ PULL_M4,
+ PUSHF_M4,
+ PULLF_M4,
+
+ RADD_M4,
+ RADC_M4,
+ RAND_M4,
+ RXOR_M4,
+ CAI_M4, CAI_M5,
+
+ SHL_M4, SHL_M5, SHL_M6, SHL_M7, SHL_M8,
+ SHR_M4, SHR_M5, SHR_M6, SHR_M7, SHR_M8,
+ ROL_M4, ROL_M5, ROL_M6, ROL_M7, ROL_M8,
+ ROR_M4, ROR_M5, ROR_M6, ROR_M7, ROR_M8,
+
+ HALT_M4,
+ PFLG_M4, PFLG_M5, PFLG_M6,
+
+ UNKNOWN
+ };
+
+ static const cycle s_decode[64];
+
+ // flag and interrupt helpers
+ void set_control_flag(u8 fc);
+ void reset_control_flag(u8 fc);
+ void set_fr(u16 r);
+
+ // conditions and ALU helpers
+ bool sign_bit(u16 r) const noexcept;
+ bool equals_0(u16 r) const noexcept;
+ bool poll_condition(u8 cc);
+ static void sign_extend(u16 &r);
+ void add(u16 &dr, u16 sr, bool c);
+ void decimal_add(u16 &dr, u16 sr, unsigned stage);
+ void prepare_shift();
+ void shift_left(u16 &r, bool rotate);
+ void shift_right(u16 &r, bool rotate);
+
+ // stack helpers
+ void stack_push(u16 r);
+ u16 stack_pull();
+
+ // execution helpers
+ void read_instruction();
+ u16 get_effective_address();
+ void read_effective_address();
+ void write_effective_address(u16 r);
+ cycle execute_one();
// address space and cache
address_space_config m_space_config;
address_space *m_space;
- memory_access_cache<1, -1, ENDIANNESS_LITTLE> *m_inst_cache;
+ memory_access_cache<1, -1, ENDIANNESS_LITTLE> *m_cache;
// callback objects
+ devcb_read_line m_bps_callback;
devcb_read_line m_jc_callback[3];
devcb_write_line m_flag_callback[4];
- // execution state
- s32 m_icount;
- u16 m_pc;
+ // core registers
u16 m_fr;
+ u16 m_pc;
+ u16 m_mdr;
+ u16 m_mar;
u16 m_ac[4];
- u8 m_sp;
- u16 m_stk[10];
+
+ // stack
+ u8 m_stkp;
+ u8 m_stack_depth;
+ u16 m_stack[10];
+
+ // execution state
+ u16 m_ppc;
+ u16 m_cir;
+ bool m_shift_link;
+ cycle m_cycle;
+ s32 m_icount;
};
// ======================> ins8900_device
diff --git a/src/mame/drivers/ns5652.cpp b/src/mame/drivers/ns5652.cpp
index b059c756c0e..6e4e25f474b 100644
--- a/src/mame/drivers/ns5652.cpp
+++ b/src/mame/drivers/ns5652.cpp
@@ -35,6 +35,32 @@ void ns5652_state::mem_map(address_map &map)
map(0x0000, 0x0bff).mirror(0xd000).rom().region("program", 0);
map(0xe800, 0xebff).ram(); // 4x MM2114J-3
map(0xec00, 0xecff).lr16([this](offs_t offset) { return m_ecprom[offset]; }, "ecprom_r");
+ map(0xf600, 0xf600).nopw(); // shift register outputs?
+ map(0xf610, 0xf610).nopw();
+ map(0xf620, 0xf620).nopw();
+ map(0xf630, 0xf630).nopw();
+ map(0xf640, 0xf640).nopw();
+ map(0xf650, 0xf650).nopw();
+ map(0xf660, 0xf660).nopw();
+ map(0xf670, 0xf670).nopw();
+ map(0xf680, 0xf680).nopw();
+ map(0xf690, 0xf690).nopw();
+ map(0xf6a0, 0xf6a0).nopw();
+ map(0xf6b0, 0xf6b0).nopw();
+ map(0xf6c0, 0xf6c0).nopw();
+ map(0xf6d0, 0xf6d0).nopw();
+ map(0xf6e0, 0xf6e0).nopw();
+ map(0xf6f0, 0xf6f0).nopw();
+ map(0xf700, 0xf700).nopw();
+ map(0xf710, 0xf710).nopw();
+ map(0xf720, 0xf720).nopw();
+ map(0xf730, 0xf730).nopw();
+ map(0xf740, 0xf740).nopw();
+ map(0xf750, 0xf750).nopw();
+ map(0xf760, 0xf760).nopw();
+ map(0xf770, 0xf770).nopw();
+ map(0xf780, 0xf780).nopw();
+ map(0xf790, 0xf790).nopw();
}