summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2017-06-29 18:41:01 +1000
committer Vas Crabb <vas@vastheman.com>2017-06-29 19:04:49 +1000
commite990156da54fcc53db804dbd2b16e0eda8147608 (patch)
tree62d553b7669916080ac24d8d9b2d0ee5af98c5cc
parent23ce1f5b457fbf4efe4485db6ca073ac54a04662 (diff)
Add support for 4040 stop/ack and HLT instruction, hook up stop/single step buttons in INTELLEC 4/MOD 40
-rw-r--r--src/devices/cpu/mcs40/mcs40.cpp365
-rw-r--r--src/devices/cpu/mcs40/mcs40.h44
-rw-r--r--src/mame/drivers/intellec4.cpp69
3 files changed, 351 insertions, 127 deletions
diff --git a/src/devices/cpu/mcs40/mcs40.cpp b/src/devices/cpu/mcs40/mcs40.cpp
index c23b4290180..23a939f4d5d 100644
--- a/src/devices/cpu/mcs40/mcs40.cpp
+++ b/src/devices/cpu/mcs40/mcs40.cpp
@@ -63,19 +63,21 @@ current state. The only way to set it to a fixed value is to reset the
write-back, using the RC value as the address and the first/last signal
as nybble lane select.
-TODO: HLT, BBS, EIN, DIN instructions
-TODO: 4040 halt/interrupt support
+TODO: 4040 interrupt support (including BBS, EIN, DIN instructions)
TODO: expose data bus
*/
+DEFINE_DEVICE_TYPE(I4004, i4004_cpu_device, "i4004", "Intel 4004")
+DEFINE_DEVICE_TYPE(I4040, i4040_cpu_device, "i4040", "Intel 4040")
+
+
ALLOW_SAVE_TYPE(mcs40_cpu_device_base::cycle);
ALLOW_SAVE_TYPE(mcs40_cpu_device_base::pmem);
ALLOW_SAVE_TYPE(mcs40_cpu_device_base::phase);
-DEFINE_DEVICE_TYPE(I4004, i4004_cpu_device, "i4004", "Intel 4004")
-DEFINE_DEVICE_TYPE(I4040, i4040_cpu_device, "i4040", "Intel 4040")
+static constexpr u8 f_cm_ram_table[8] = { 0x0eU, 0x0dU, 0x0bU, 0x07U, 0x09U, 0x05U, 0x03U, 0x01U };
mcs40_cpu_device_base::mcs40_cpu_device_base(
@@ -98,19 +100,19 @@ mcs40_cpu_device_base::mcs40_cpu_device_base(
, m_sync_cb(*this)
, m_cm_rom_cb{ { *this }, { *this } }
, m_cm_ram_cb{ { *this }, { *this }, { *this }, { *this }, }
- , m_cy_cb(*this)
- , m_4289_pm_cb(*this)
- , m_4289_f_l_cb(*this)
+ , m_cy_cb(*this), m_stp_ack_cb(*this)
+ , m_4289_pm_cb(*this), m_4289_f_l_cb(*this)
, m_extended_cm(extended_cm)
, m_stack_ptr_mask(stack_ptr_mask), m_index_reg_cnt(index_reg_cnt), m_cr_mask(cr_mask)
, m_pc_mask((1U << rom_width) - 1)
, m_phase(phase::A1), m_cycle(cycle::OP), m_io_pending(false), m_program_op(pmem::NONE)
+ , m_stop_latch(false), m_stop_ff(false), m_decoded_halt(false), m_resume(false)
, m_rom_addr(0U), m_opr(0U), m_opa(0U), m_arg(0U), m_4289_first(false)
, m_a(0U), m_c(0U)
, m_addr_stack(), m_stack_ptr(0U)
, m_index_regs(), m_index_reg_bank(0U)
, m_cr(0U), m_pending_cr3(0U), m_latched_rc(0U), m_new_rc(0U), m_src(0U), m_rc_pending(false)
- , m_test(CLEAR_LINE)
+ , m_test(CLEAR_LINE), m_stp(CLEAR_LINE)
, m_cm_rom(0U), m_cm_ram(0U), m_cy(0U), m_4289_a(0U), m_4289_c(0U), m_4289_pm(0U), m_4289_f_l(0U)
, m_index_reg_halves(), m_pc(0U), m_pcbase(0U), m_genflags(0U)
{
@@ -141,9 +143,12 @@ void mcs40_cpu_device_base::device_start()
m_cm_ram_cb[2].resolve_safe();
m_cm_ram_cb[3].resolve_safe();
m_cy_cb.resolve_safe();
+ m_stp_ack_cb.resolve_safe();
m_4289_pm_cb.resolve_safe();
m_4289_f_l_cb.resolve_safe();
+ m_stop_latch = m_decoded_halt = m_resume = false;
+
m_rom_addr = 0U;
m_opr = m_opa = m_arg = 0U;
@@ -159,6 +164,7 @@ void mcs40_cpu_device_base::device_start()
m_latched_rc = m_new_rc = m_src = 0U;
m_test = CLEAR_LINE;
+ m_stp = CLEAR_LINE;
m_cm_rom = 0x03U;
m_cm_ram = 0x0fU;
m_cy = 0x00U;
@@ -202,6 +208,10 @@ void mcs40_cpu_device_base::device_start()
save_item(NAME(m_cycle));
save_item(NAME(m_io_pending));
save_item(NAME(m_program_op));
+ save_item(NAME(m_stop_latch));
+ save_item(NAME(m_stop_ff));
+ save_item(NAME(m_decoded_halt));
+ save_item(NAME(m_resume));
save_item(NAME(m_rom_addr));
save_item(NAME(m_opr));
save_item(NAME(m_opa));
@@ -220,6 +230,7 @@ void mcs40_cpu_device_base::device_start()
save_item(NAME(m_src));
save_item(NAME(m_rc_pending));
save_item(NAME(m_test));
+ save_item(NAME(m_stp));
save_item(NAME(m_cm_ram));
save_item(NAME(m_cm_rom));
save_item(NAME(m_cy));
@@ -234,6 +245,7 @@ void mcs40_cpu_device_base::device_reset()
{
m_phase = phase::A1;
m_cycle = cycle::OP;
+ m_stop_ff = false;
m_rom_addr = 0U;
m_4289_first = true;
@@ -249,6 +261,7 @@ void mcs40_cpu_device_base::device_reset()
update_cm_rom(0x03U);
update_cm_ram(0x0fU);
+ m_stp_ack_cb(1U);
// TODO: it actually takes multiple cycles with reset asserted for everything to get cleared
m_a = m_c = 0U;
@@ -263,144 +276,40 @@ void mcs40_cpu_device_base::device_reset()
void mcs40_cpu_device_base::execute_run()
{
- static constexpr u8 cm_ram_table[8] = { 0x0eU, 0x0dU, 0x0bU, 0x07U, 0x09U, 0x05U, 0x03U, 0x01U };
while (m_icount > 0)
{
switch (m_phase)
{
case phase::A1:
- m_pending_cr3 = (m_pending_cr3 << 1) | BIT(m_cr, 3);
- m_cr = (m_cr & 0x07U) | (m_pending_cr3 & 0x08U);
- if (cycle::OP == m_cycle)
- {
- m_pcbase = rom_bank() | m_rom_addr;
- if (machine().debug_flags & DEBUG_FLAG_ENABLED)
- debugger_instruction_hook(this, pc());
- }
- m_4289_a = (m_4289_a & 0xf0U) | (m_rom_addr & 0x0fU);
- m_sync_cb(1);
- update_4289_pm(1U);
- update_4289_f_l(1U);
- // low nybble of ROM address is output here
- // STOPACK is asserted here
+ do_a1();
m_phase = phase::A2;
break;
case phase::A2:
- // mid nybble of ROM address is output here
- m_4289_a = (m_4289_a & 0x0fU) | (m_rom_addr & 0xf0U);
+ do_a2();
m_phase = phase::A3;
break;
case phase::A3:
- // high nybble of ROM address is output here
- m_4289_c = (m_rom_addr >> 8) & 0x0fU;
- update_cm_rom(BIT(m_cr, 3) ? 0x01U : 0x02U);
+ do_a3();
m_phase = phase::M1;
break;
case phase::M1:
- // OPR is read here
- {
- if (!m_extended_cm || (cycle::OP != m_cycle))
- update_cm_rom(0x03);
- else
- update_cm_ram(cm_ram_table[m_cr & 0x07U]);
- // TODO: split this up into two nybble reads - MAME doesn't support this
- u8 const read = m_direct->read_byte(rom_bank() | m_rom_addr);
- if (cycle::OP == m_cycle)
- {
- m_opr = read >> 4;
- m_opa = read & 0x0f;
- m_io_pending = is_io_op(m_opr);
- }
- else
- {
- m_arg = read;
- }
- }
+ do_m1();
m_phase = phase::M2;
break;
case phase::M2:
- // OPA is read here
- if (m_io_pending)
- {
- update_cm_rom(BIT(m_cr, 3) ? 0x01U : 0x02U);
- update_cm_ram(cm_ram_table[m_cr & 0x07U]);
- }
- if (cycle::IN != m_cycle)
- m_rom_addr = pc() = (pc() + 1) & 0x0fff;
+ do_m2();
m_phase = phase::X1;
break;
case phase::X1:
- // A or OPA is output here
- update_cy(m_c);
- update_cm_rom(0x03U);
- update_cm_ram(0x0fU);
- if (cycle::OP == m_cycle)
- {
- m_program_op = pmem::NONE;
- m_cycle = do_cycle1(m_opr, m_opa, m_program_op);
- }
- else
- {
- do_cycle2(m_opr, m_opa, m_arg);
- m_cycle = cycle::OP;
- }
- m_4289_a = m_latched_rc;
- if (pmem::NONE == m_program_op)
- {
- m_4289_c = (m_latched_rc >> 4) & 0x0fU;
- }
- else
- {
- assert(cycle::OP == m_cycle);
- m_4289_c = 0x0fU;
- update_4289_pm(0x00U);
- update_4289_f_l(m_4289_first ? 0x01 : 0x00);
- m_4289_first = !m_4289_first;
- if (pmem::READ == m_program_op)
- m_arg = m_program->read_byte(program_addr()) & 0x0fU;
- else
- assert(pmem::WRITE == m_program_op);
- }
+ do_x1();
m_phase = phase::X2;
break;
case phase::X2:
- if (m_io_pending)
- {
- assert(phase::X2 == m_phase);
- assert(m_latched_rc == m_new_rc);
- assert(!m_rc_pending);
- do_io(m_opr, m_opa);
- m_io_pending = false;
- }
- if (m_rc_pending)
- {
- update_cm_rom(BIT(m_cr, 3) ? 0x01U : 0x02U);
- update_cm_ram(cm_ram_table[m_cr & 0x07U]);
- m_latched_rc = (m_latched_rc & 0x0fU) | (m_new_rc & 0xf0U);
- }
- else
- {
- assert(m_latched_rc == m_new_rc);
- }
- if (pmem::READ == m_program_op)
- set_a(m_arg);
+ do_x2();
m_phase = phase::X3;
break;
case phase::X3:
- m_sync_cb(0);
- update_cm_rom(0x03U);
- update_cm_ram(0x0fU);
- if (m_rc_pending)
- {
- m_latched_rc = (m_latched_rc & 0xf0U) | (m_new_rc & 0x0fU);
- m_rc_pending = false;
- }
- else
- {
- assert(m_latched_rc == m_new_rc);
- }
- if (pmem::WRITE == m_program_op)
- m_program->write_byte(program_addr(), get_a());
+ do_x3();
m_phase = phase::A1;
break;
}
@@ -573,9 +482,14 @@ inline void mcs40_cpu_device_base::set_index_reg_bank(u8 val)
/***********************************************************************
- register access
+ I/O control
***********************************************************************/
+inline void mcs40_cpu_device_base::halt_decoded()
+{
+ m_decoded_halt = true;
+}
+
inline void mcs40_cpu_device_base::set_rom_addr(u16 addr, u16 mask)
{
m_rom_addr = (addr & mask) | (m_rom_addr & ~mask);
@@ -659,6 +573,187 @@ inline void mcs40_cpu_device_base::set_test(int state)
m_test = ((ASSERT_LINE == state) || (HOLD_LINE == state)) ? state : CLEAR_LINE;
}
+inline void mcs40_cpu_device_base::set_stp(int state)
+{
+ m_stp = ((ASSERT_LINE == state) || (HOLD_LINE == state)) ? state : CLEAR_LINE;
+}
+
+
+/***********************************************************************
+ instruction phases
+***********************************************************************/
+
+inline void mcs40_cpu_device_base::do_a1()
+{
+ m_pending_cr3 = (m_pending_cr3 << 1) | BIT(m_cr, 3);
+ m_cr = (m_cr & 0x07U) | (m_pending_cr3 & 0x08U);
+ if (cycle::OP == m_cycle)
+ {
+ m_pcbase = rom_bank() | m_rom_addr;
+ if (machine().debug_flags & DEBUG_FLAG_ENABLED)
+ debugger_instruction_hook(this, pc());
+ if (m_stop_latch)
+ {
+ m_stp = (ASSERT_LINE == m_stp) ? ASSERT_LINE : CLEAR_LINE;
+ if (!m_stop_ff)
+ {
+ m_stop_ff = true;
+ m_stp_ack_cb(0U);
+ }
+ }
+ }
+ m_4289_a = (m_4289_a & 0xf0U) | (m_rom_addr & 0x0fU);
+ m_sync_cb(1);
+ update_4289_pm(1U);
+ update_4289_f_l(1U);
+ // low nybble of ROM address is output here
+}
+
+inline void mcs40_cpu_device_base::do_a2()
+{
+ // mid nybble of ROM address is output here
+ m_4289_a = (m_4289_a & 0x0fU) | (m_rom_addr & 0xf0U);
+}
+
+inline void mcs40_cpu_device_base::do_a3()
+{
+ // high nybble of ROM address is output here
+ m_4289_c = (m_rom_addr >> 8) & 0x0fU;
+ update_cm_rom(BIT(m_cr, 3) ? 0x01U : 0x02U);
+}
+
+inline void mcs40_cpu_device_base::do_m1()
+{
+ // OPR is read here
+ if (!m_extended_cm || (cycle::OP != m_cycle))
+ update_cm_rom(0x03);
+ else
+ update_cm_ram(f_cm_ram_table[m_cr & 0x07U]);
+ // TODO: split this up into two nybble reads - MAME doesn't support this
+ u8 const read = m_direct->read_byte(rom_bank() | m_rom_addr);
+ if (cycle::OP == m_cycle)
+ {
+ if (m_stop_ff)
+ {
+ m_opr = 0x0U;
+ m_opa = 0x0U;
+ }
+ else
+ {
+ m_opr = read >> 4;
+ m_opa = read & 0x0fU;
+ }
+ m_io_pending = is_io_op(m_opr);
+ }
+ else
+ {
+ m_arg = read;
+ }
+ m_decoded_halt = false;
+}
+
+inline void mcs40_cpu_device_base::do_m2()
+{
+ // OPA is read here
+ if (m_io_pending)
+ {
+ update_cm_rom(BIT(m_cr, 3) ? 0x01U : 0x02U);
+ update_cm_ram(f_cm_ram_table[m_cr & 0x07U]);
+ }
+ m_resume = m_stop_latch && (CLEAR_LINE == m_stp);
+ m_stop_latch = CLEAR_LINE != m_stp;
+ if (!m_stop_ff && (cycle::IN != m_cycle))
+ m_rom_addr = pc() = (pc() + 1) & 0x0fff;
+}
+
+inline void mcs40_cpu_device_base::do_x1()
+{
+ // A or OPA is output here
+ update_cy(m_c);
+ update_cm_rom(0x03U);
+ update_cm_ram(0x0fU);
+ if (cycle::OP == m_cycle)
+ {
+ m_program_op = pmem::NONE;
+ m_cycle = do_cycle1(m_opr, m_opa, m_program_op);
+ }
+ else
+ {
+ do_cycle2(m_opr, m_opa, m_arg);
+ m_cycle = cycle::OP;
+ }
+ m_4289_a = m_latched_rc;
+ if (pmem::NONE == m_program_op)
+ {
+ m_4289_c = (m_latched_rc >> 4) & 0x0fU;
+ }
+ else
+ {
+ assert(cycle::OP == m_cycle);
+ m_4289_c = 0x0fU;
+ update_4289_pm(0x00U);
+ update_4289_f_l(m_4289_first ? 0x01 : 0x00);
+ m_4289_first = !m_4289_first;
+ if (pmem::READ == m_program_op)
+ m_arg = m_program->read_byte(program_addr()) & 0x0fU;
+ else
+ assert(pmem::WRITE == m_program_op);
+ }
+}
+
+void mcs40_cpu_device_base::do_x2()
+{
+ if (m_io_pending)
+ {
+ assert(phase::X2 == m_phase);
+ assert(m_latched_rc == m_new_rc);
+ assert(!m_rc_pending);
+ do_io(m_opr, m_opa);
+ m_io_pending = false;
+ }
+ if (m_rc_pending)
+ {
+ update_cm_rom(BIT(m_cr, 3) ? 0x01U : 0x02U);
+ update_cm_ram(f_cm_ram_table[m_cr & 0x07U]);
+ m_latched_rc = (m_latched_rc & 0x0fU) | (m_new_rc & 0xf0U);
+ }
+ else
+ {
+ assert(m_latched_rc == m_new_rc);
+ }
+ if (pmem::READ == m_program_op)
+ set_a(m_arg);
+}
+
+void mcs40_cpu_device_base::do_x3()
+{
+ m_sync_cb(0);
+ update_cm_rom(0x03U);
+ update_cm_ram(0x0fU);
+ if (m_rc_pending)
+ {
+ m_latched_rc = (m_latched_rc & 0xf0U) | (m_new_rc & 0x0fU);
+ m_rc_pending = false;
+ }
+ else
+ {
+ assert(m_latched_rc == m_new_rc);
+ }
+ if (pmem::WRITE == m_program_op)
+ m_program->write_byte(program_addr(), get_a());
+ if (!m_stop_ff && m_decoded_halt)
+ {
+ m_stop_ff = true;
+ m_stp_ack_cb(0U);
+ }
+ else if (m_stop_ff && m_resume)
+ {
+ m_stop_ff = false;
+ m_stp_ack_cb(1U);
+ }
+ m_resume = false;
+}
+
/***********************************************************************
internal helpers
@@ -1026,6 +1121,28 @@ i4040_cpu_device::i4040_cpu_device(machine_config const &mconfig, char const *ta
/***********************************************************************
+ device_execute_interface implementation
+***********************************************************************/
+
+u32 i4040_cpu_device::execute_input_lines() const
+{
+ return 3U;
+}
+
+void i4040_cpu_device::execute_set_input(int inputnum, int state)
+{
+ switch (inputnum)
+ {
+ case I4040_STP_LINE:
+ set_stp(state);
+ break;
+ default:
+ i4004_cpu_device::execute_set_input(inputnum, state);
+ }
+}
+
+
+/***********************************************************************
device_disasm_interface implementation
***********************************************************************/
@@ -1052,9 +1169,12 @@ i4040_cpu_device::cycle i4040_cpu_device::do_cycle1(u8 opr, u8 opa, pmem &progra
case 0x0:
switch (opa)
{
+ case 0x1: // HLT
+ halt_decoded();
+ return cycle::OP;
case 0x3: // LCR
set_a(get_cr());
- break;
+ return cycle::OP;
case 0x4: // OR4
case 0x5: // OR5
set_a(get_a() | get_index_reg(4U | BIT(opa, 0)));
@@ -1090,7 +1210,6 @@ void i4040_cpu_device::execute_one(unsigned opcode)
{
switch (opcode)
{
- case 0x01: // HLT
case 0x02: // BBS
case 0x0c: // EIN
case 0x0d: // DIN
diff --git a/src/devices/cpu/mcs40/mcs40.h b/src/devices/cpu/mcs40/mcs40.h
index 0b203dc86b9..e38cb98a23c 100644
--- a/src/devices/cpu/mcs40/mcs40.h
+++ b/src/devices/cpu/mcs40/mcs40.h
@@ -74,6 +74,9 @@ enum
#define MCFG_I4040_CY_CB(obj) \
devcb = &i4040_cpu_device::set_cy_cb(*device, DEVCB_##obj);
+#define MCFG_I4040_STP_ACK_CB(obj) \
+ devcb = &i4040_cpu_device::set_stp_ack_cb(*device, DEVCB_##obj);
+
#define MCFG_I4040_4289_PM_CB(obj) \
devcb = &i4040_cpu_device::set_4289_pm_cb(*device, DEVCB_##obj);
@@ -157,6 +160,7 @@ protected:
void set_index_reg_bank(u8 val);
// I/O control
+ void halt_decoded();
void set_rom_addr(u16 addr, u16 mask);
u8 get_cr();
void set_cr(u8 val, u8 mask);
@@ -173,6 +177,7 @@ protected:
// input lines
bool get_test();
void set_test(int state);
+ void set_stp(int state);
// configuration helpers
template <typename Obj> devcb_base &set_sync_cb(Obj &&cb)
@@ -183,6 +188,8 @@ protected:
{ return m_cm_ram_cb[N].set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base &set_cy_cb(Obj &&cb)
{ return m_cy_cb.set_callback(std::forward<Obj>(cb)); }
+ template <typename Obj> devcb_base &set_stp_ack_cb(Obj &&cb)
+ { return m_stp_ack_cb.set_callback(std::forward<Obj>(cb)); }
private:
enum
@@ -202,6 +209,16 @@ private:
enum class phase { A1, A2, A3, M1, M2, X1, X2, X3 };
+ // instruction phases
+ void do_a1();
+ void do_a2();
+ void do_a3();
+ void do_m1();
+ void do_m2();
+ void do_x1();
+ void do_x2();
+ void do_x3();
+
// internal helpers
u16 &pc() { return m_addr_stack[m_stack_ptr]; }
u16 rom_bank() const { return BIT(m_cr, 3) ? 0x1000U : 0x0000U; }
@@ -220,7 +237,7 @@ private:
// output callbacks
devcb_write_line m_sync_cb;
devcb_write_line m_cm_rom_cb[2], m_cm_ram_cb[4];
- devcb_write_line m_cy_cb;
+ devcb_write_line m_cy_cb, m_stp_ack_cb;
// 4008/4009 or 4289 output callbacks
devcb_write_line m_4289_pm_cb, m_4289_f_l_cb;
@@ -239,6 +256,9 @@ private:
bool m_io_pending;
pmem m_program_op;
+ // stop/halt control
+ bool m_stop_latch, m_stop_ff, m_decoded_halt, m_resume;
+
// instruction ROM fetch/decode
u16 m_rom_bank, m_rom_addr;
u8 m_opr, m_opa, m_arg;
@@ -260,7 +280,7 @@ private:
bool m_rc_pending;
// input/output lines
- int m_test;
+ int m_test, m_stp;
u8 m_cm_rom, m_cm_ram, m_cy;
u8 m_4289_a, m_4289_c, m_4289_pm, m_4289_f_l;
@@ -288,7 +308,7 @@ protected:
using mcs40_cpu_device_base::mcs40_cpu_device_base;
// device_execute_interface implementation
- u32 execute_input_lines() const override;
+ virtual u32 execute_input_lines() const override;
virtual void execute_set_input(int inputnum, int state) override;
// device_disasm_interface implementation
@@ -304,6 +324,11 @@ protected:
virtual cycle do_cycle1(u8 opr, u8 opa, pmem &program_op) override;
virtual void do_cycle2(u8 opr, u8 opa, u8 arg) override;
virtual void do_io(u8 opr, u8 opa) override;
+
+ // configuration helpers
+ using mcs40_cpu_device_base::set_sync_cb;
+ using mcs40_cpu_device_base::set_cm_rom_cb;
+ using mcs40_cpu_device_base::set_cm_ram_cb;
};
@@ -319,6 +344,8 @@ public:
{ return downcast<i4040_cpu_device &>(device).set_cm_ram_cb<N>(std::forward<Obj>(cb)); }
template <typename Obj> static devcb_base &set_cy_cb(device_t &device, Obj &&cb)
{ return downcast<i4040_cpu_device &>(device).set_cy_cb(std::forward<Obj>(cb)); }
+ template <typename Obj> static devcb_base &set_stp_ack_cb(device_t &device, Obj &&cb)
+ { return downcast<i4040_cpu_device &>(device).set_stp_ack_cb(std::forward<Obj>(cb)); }
i4040_cpu_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock);
@@ -331,8 +358,19 @@ protected:
uint8_t const *opram,
uint32_t options) override;
+ // device_execute_interface implementation
+ virtual u32 execute_input_lines() const override;
+ virtual void execute_set_input(int inputnum, int state) override;
+
// mcs40_cpu_device_base implementation
virtual cycle do_cycle1(u8 opr, u8 opa, pmem &program_op) override;
+
+ // configuration helpers
+ using mcs40_cpu_device_base::set_sync_cb;
+ using mcs40_cpu_device_base::set_cm_rom_cb;
+ using mcs40_cpu_device_base::set_cm_ram_cb;
+ using mcs40_cpu_device_base::set_cy_cb;
+ using mcs40_cpu_device_base::set_stp_ack_cb;
};
diff --git a/src/mame/drivers/intellec4.cpp b/src/mame/drivers/intellec4.cpp
index d82f981296b..2690ea3d8c6 100644
--- a/src/mame/drivers/intellec4.cpp
+++ b/src/mame/drivers/intellec4.cpp
@@ -19,10 +19,14 @@ class intellec4_40_state : public driver_device
public:
intellec4_40_state(machine_config const &mconfig, device_type type, char const *tag)
: driver_device(mconfig, type, tag)
+ , m_cpu(*this, "maincpu")
, m_tty(*this, "tty")
, m_mon_rom(*this, "monitor", 0x0400)
, m_ram(*this, "ram")
, m_banks(*this, "bank%u", 0)
+ , m_mode_sw(*this, "MODE")
+ , m_single_step_timer(nullptr)
+ , m_stp_ack(false), m_single_step(false)
, m_ram_page(0U), m_ram_data(0U), m_ram_write(false)
{
}
@@ -40,15 +44,30 @@ public:
DECLARE_WRITE8_MEMBER(ram0_out);
DECLARE_WRITE8_MEMBER(ram1_out);
+ DECLARE_WRITE_LINE_MEMBER(stp_ack);
+
+ DECLARE_INPUT_CHANGED_MEMBER(stop_sw);
+ DECLARE_INPUT_CHANGED_MEMBER(single_step_sw);
+
protected:
virtual void driver_start() override;
private:
+ TIMER_CALLBACK_MEMBER(single_step_expired);
+
+ bool get_stop_sw() { return BIT(~m_mode_sw->read(), 0); }
+
+ required_device<cpu_device> m_cpu;
required_device<rs232_port_device> m_tty;
required_region_ptr<u8> m_mon_rom;
required_shared_ptr<u8> m_ram;
required_memory_bank_array<4> m_banks;
+ required_ioport m_mode_sw;
+
+ emu_timer *m_single_step_timer;
+
+ bool m_stp_ack, m_single_step;
u8 m_ram_page, m_ram_data;
bool m_ram_write;
};
@@ -62,7 +81,9 @@ READ8_MEMBER(intellec4_40_state::pm_read)
u16 const addr((u16(m_ram_page) << 8) | ((offset >> 1) & 0x00ffU));
m_ram_data = m_ram[addr];
}
- return space.unmap();
+
+ // the C outputs of the 4289 are always high for RPM/WPM so it's equivalent to romf_in
+ return m_ram_data & 0x0fU;
}
WRITE8_MEMBER(intellec4_40_state::pm_write)
@@ -119,6 +140,37 @@ WRITE8_MEMBER(intellec4_40_state::ram1_out)
m_tty->write_rts(BIT(~data, 0));
}
+WRITE_LINE_MEMBER(intellec4_40_state::stp_ack)
+{
+ // resets the single-step monostable
+ if (m_stp_ack && state)
+ {
+ m_single_step_timer->reset();
+ m_single_step = false;
+ m_cpu->set_input_line(I4040_STP_LINE, get_stop_sw() ? ASSERT_LINE : CLEAR_LINE);
+ }
+ m_stp_ack = !state;
+}
+
+
+INPUT_CHANGED_MEMBER(intellec4_40_state::stop_sw)
+{
+ m_cpu->set_input_line(I4040_STP_LINE, (newval || m_single_step) ? CLEAR_LINE : ASSERT_LINE);
+}
+
+INPUT_CHANGED_MEMBER(intellec4_40_state::single_step_sw)
+{
+ // (re-)triggers the single-step monostable
+ if (newval && !oldval)
+ {
+ // 9602 with Rx = 20kΩ, Cx = 0.005µF
+ // K * Rx(kΩ) * (1 + (1 / Rx(kΩ))) = 0.34 * 20 * 5000 * (1 + (1 / 20)) = 35700ns
+ m_single_step_timer->adjust(attotime::from_nsec(35700));
+ m_single_step = true;
+ m_cpu->set_input_line(I4040_STP_LINE, CLEAR_LINE);
+ }
+}
+
void intellec4_40_state::driver_start()
{
@@ -129,12 +181,23 @@ void intellec4_40_state::driver_start()
m_banks[i]->set_entry(0);
}
+ m_single_step_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(intellec4_40_state::single_step_expired), this));
+
+ save_item(NAME(m_stp_ack));
+ save_item(NAME(m_single_step));
save_item(NAME(m_ram_page));
save_item(NAME(m_ram_data));
save_item(NAME(m_ram_write));
}
+TIMER_CALLBACK_MEMBER(intellec4_40_state::single_step_expired)
+{
+ m_single_step = false;
+ m_cpu->set_input_line(I4040_STP_LINE, get_stop_sw() ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
ADDRESS_MAP_START(mod40_program, AS_PROGRAM, 8, intellec4_40_state)
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x01ff) AM_READWRITE(pm_read, pm_write)
@@ -170,12 +233,16 @@ MACHINE_CONFIG_START(intlc440)
MCFG_CPU_DATA_MAP(mod40_data)
MCFG_CPU_IO_MAP(mod40_io)
MCFG_CPU_DECRYPTED_OPCODES_MAP(mod40_opcodes)
+ MCFG_I4040_STP_ACK_CB(WRITELINE(intellec4_40_state, stp_ack))
MCFG_RS232_PORT_ADD("tty", default_rs232_devices, "terminal")
MACHINE_CONFIG_END
INPUT_PORTS_START(intlc440)
+ PORT_START("MODE")
+ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_TOGGLE PORT_NAME("STOP") PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_40_state, stop_sw, 0)
+ PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("SINGLE STEP") PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_40_state, single_step_sw, 0)
INPUT_PORTS_END