diff options
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 |
commit | e990156da54fcc53db804dbd2b16e0eda8147608 (patch) | |
tree | 62d553b7669916080ac24d8d9b2d0ee5af98c5cc | |
parent | 23ce1f5b457fbf4efe4485db6ca073ac54a04662 (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.cpp | 365 | ||||
-rw-r--r-- | src/devices/cpu/mcs40/mcs40.h | 44 | ||||
-rw-r--r-- | src/mame/drivers/intellec4.cpp | 69 |
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 |