From 19a765d75af317844b0c448c81fd89d5915a9ad5 Mon Sep 17 00:00:00 2001 From: AJR Date: Sun, 12 Mar 2023 16:41:40 -0400 Subject: m50734: Partially emulate stepper motor controller registers --- src/devices/cpu/m6502/m50734.cpp | 97 +++++++++++++++++++++++++++++++++++++--- src/devices/cpu/m6502/m50734.h | 10 +++++ 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/src/devices/cpu/m6502/m50734.cpp b/src/devices/cpu/m6502/m50734.cpp index b4e31f003c7..1ea6af7760c 100644 --- a/src/devices/cpu/m6502/m50734.cpp +++ b/src/devices/cpu/m6502/m50734.cpp @@ -35,6 +35,9 @@ m50734_device::m50734_device(const machine_config &mconfig, const char *tag, dev , m_ad_register(0) , m_prescaler_reload{0xff, 0xff, 0xff} , m_timer_reload{0xff, 0xff, 0xff} + , m_step_counter{0, 0} + , m_phase_counter(0) + , m_smcon{0, 0} { program_config.m_internal_map = address_map_constructor(FUNC(m50734_device::internal_map), this); } @@ -56,6 +59,25 @@ void m50734_device::device_resolve_objects() m_analog_in_cb.resolve_all_safe(0); } +void m50734_device::step_motor(int which) +{ + if (!BIT(m_smcon[which], 2)) + return; + + if (--m_step_counter[which] == 0 && !BIT(m_interrupt_control[0], which * 2 + 5)) + { + LOGMASKED(LOG_TIMER, "%s counter empty at %s\n", which ? "Vertical" : "Horizontal", machine().time().to_string()); + m_interrupt_control[0] |= 0x20 << (which * 2); + if (BIT(m_interrupt_control[0], which * 2 + 4)) + set_input_line(M740_INT3_LINE, ASSERT_LINE); + } + + // Increment or decrement HPHC/VPHC + m_phase_counter = (m_phase_counter + ((BIT(m_smcon[which], 1) ? 7 : 1) << (which * 4))) & 0x77; + + // TODO: single shot mode, phase decoder and P2 output +} + template TIMER_CALLBACK_MEMBER(m50734_device::timer_interrupt) { @@ -69,6 +91,11 @@ TIMER_CALLBACK_MEMBER(m50734_device::timer_interrupt) } } + if (N == 2) + step_motor(0); // Timer 3 steps HC + else if (N == 0) + step_motor(1); // Timer 0 steps VC + // Reload timer and prescaler m_timer[N]->adjust(clocks_to_attotime(16 * u32(m_prescaler_reload[N] + 1) * (m_timer_reload[N] + 1))); } @@ -92,6 +119,9 @@ void m50734_device::device_start() save_item(NAME(m_ad_register)); save_item(NAME(m_prescaler_reload)); save_item(NAME(m_timer_reload)); + save_item(NAME(m_step_counter)); + save_item(NAME(m_phase_counter)); + save_item(NAME(m_smcon)); save_item(NAME(m_interrupt_control)); } @@ -100,17 +130,25 @@ void m50734_device::device_reset() m740_device::device_reset(); SP = 0x01ff; + // Reset port registers std::fill(std::begin(m_port_direction), std::end(m_port_direction), 0x00); for (int n = 0; n < 4; n++) m_port_out_cb[n](m_port_3state[n]); m_p0_function = 0x00; m_p2_p3_function = 0x00; + + // Reset A-D m_ad_control |= 0x04; m_ad_timer->adjust(attotime::never); + // Reset stepper motor control registers (datasheet has no implication of RESET affecting these, but mps1200 never fully initializes SMCONH) + m_smcon[0] = 0; + m_smcon[1] = 0; + // Reset interrupts std::fill(std::begin(m_interrupt_control), std::end(m_interrupt_control), 0x00); set_input_line(M740_INT2_LINE, CLEAR_LINE); + set_input_line(M740_INT3_LINE, CLEAR_LINE); } void m50734_device::read_dummy(u16 adr) @@ -144,7 +182,17 @@ void m50734_device::interrupt_control_w(offs_t offset, u8 data) if (offset == 1) data &= 0x3f; u8 old_control = std::exchange(m_interrupt_control[2 - offset], data); - if (offset == 1) + if (offset == 2) + { + bool he_ve_interrupt = (data & (data >> 1) & 0x50) != 0; + bool old_interrupt = (old_control & (old_control >> 1) & 0x50) != 0; + if (he_ve_interrupt != old_interrupt) + { + LOGMASKED(LOG_TIMER, "%s: HE/VE interrupt %sactivated by write to interrupt control register 1 ($%02X -> $%02X)\n", machine().describe_context(), he_ve_interrupt ? "": "de", old_control, data); + set_input_line(M740_INT3_LINE, he_ve_interrupt ? ASSERT_LINE : CLEAR_LINE); + } + } + else if (offset == 1) { bool timer_interrupt = (data & (data >> 1) & 0x15) != 0; bool old_interrupt = (old_control & (old_control >> 1) & 0x15) != 0; @@ -252,30 +300,69 @@ void m50734_device::timer_w(offs_t offset, u8 data) u8 pre = m_prescaler_reload[offset >> 1]; if (BIT(offset, 0)) { - LOGMASKED(LOG_INIT, "%s: Reload timer %d latch = %d\n", machine().describe_context(), (offset >> 1) + 1, data); + attotime expire_time = clocks_to_attotime(16 * (data * pre + (ticks % (pre + 1)) + 1)); + LOGMASKED(LOG_INIT, "%s: Reload timer %d latch = %u (expires in %.1f usec)\n", machine().describe_context(), (offset >> 1) + 1, data, expire_time.as_double() * 1.0E6); m_timer_reload[offset >> 1] = data; - m_timer[offset >> 1]->adjust(clocks_to_attotime(16 * (data * pre + (ticks % (pre + 1)) + 1))); + m_timer[offset >> 1]->adjust(expire_time); } else { - LOGMASKED(LOG_INIT, "%s: Reload prescaler %d latch = %d\n", machine().describe_context(), (offset >> 1) + 1, data); + attotime expire_time = clocks_to_attotime(16 * (data + 1) * (std::min(ticks / (pre + 1), 0xff) + 1)); + LOGMASKED(LOG_INIT, "%s: Reload prescaler %d latch = %u (expires in %.1f usec)\n", machine().describe_context(), (offset >> 1) + 1, data, expire_time.as_double() * 1.0E6); m_prescaler_reload[offset >> 1] = data; - m_timer[offset >> 1]->adjust(clocks_to_attotime(16 * (data + 1) * (std::min(ticks / (pre + 1), 0xff) + 1))); + m_timer[offset >> 1]->adjust(expire_time); } } +u8 m50734_device::step_counter_r(offs_t offset) +{ + return m_step_counter[offset]; +} + +void m50734_device::step_counter_w(offs_t offset, u8 data) +{ + LOGMASKED(LOG_INIT, "%s: %s counter = %u steps\n", machine().describe_context(), BIT(offset, 0) ? "Vertical" : "Horizontal", data); + m_step_counter[offset] = data; +} + +u8 m50734_device::phase_counter_r() +{ + return m_phase_counter; +} + +void m50734_device::phase_counter_w(u8 data) +{ + // HPHC and VPHC are 3-bit counters (TODO: P2 output) + m_phase_counter = data & 0x77; +} + +u8 m50734_device::smcon_r(offs_t offset) +{ + return m_smcon[offset]; +} + +void m50734_device::smcon_w(offs_t offset, u8 data) +{ + if (m_smcon[offset] != (data & 0x0f)) + LOGMASKED(LOG_INIT, "%s: SMCON%c = $%02X\n", machine().describe_context(), "HV"[offset], data); + m_smcon[offset] = data & 0x0f; +} + void m50734_device::internal_map(address_map &map) { // TODO: other timers, UART, etc. map(0x00dc, 0x00e1).rw(FUNC(m50734_device::timer_r), FUNC(m50734_device::timer_w)); + map(0x00e2, 0x00e3).rw(FUNC(m50734_device::step_counter_r), FUNC(m50734_device::step_counter_w)); map(0x00e9, 0x00e9).rw(FUNC(m50734_device::ad_control_r), FUNC(m50734_device::ad_control_w)); map(0x00ea, 0x00ea).r(FUNC(m50734_device::ad_r)); map(0x00eb, 0x00eb).r(FUNC(m50734_device::p4_r)); + map(0x00ec, 0x00ec).rw(FUNC(m50734_device::phase_counter_r), FUNC(m50734_device::phase_counter_w)); map(0x00ed, 0x00ed).rw(FUNC(m50734_device::p2_p3_function_r), FUNC(m50734_device::p2_p3_function_w)); map(0x00ee, 0x00ef).rw(FUNC(m50734_device::port_r<3>), FUNC(m50734_device::port_w<3>)); map(0x00f0, 0x00f1).rw(FUNC(m50734_device::port_r<2>), FUNC(m50734_device::port_w<2>)); map(0x00f3, 0x00f4).rw(FUNC(m50734_device::port_r<1>), FUNC(m50734_device::port_w<1>)); map(0x00f5, 0x00f5).rw(FUNC(m50734_device::p0_function_r), FUNC(m50734_device::p0_function_w)); map(0x00f6, 0x00f7).rw(FUNC(m50734_device::port_r<0>), FUNC(m50734_device::port_w<0>)); + map(0x00f8, 0x00f9).rw(FUNC(m50734_device::smcon_r), FUNC(m50734_device::smcon_w)); map(0x00fd, 0x00ff).rw(FUNC(m50734_device::interrupt_control_r), FUNC(m50734_device::interrupt_control_w)); } diff --git a/src/devices/cpu/m6502/m50734.h b/src/devices/cpu/m6502/m50734.h index 0c98f86aa31..def00593617 100644 --- a/src/devices/cpu/m6502/m50734.h +++ b/src/devices/cpu/m6502/m50734.h @@ -114,7 +114,14 @@ private: u8 timer_r(offs_t offset); void timer_w(offs_t offset, u8 data); + void step_motor(int which); template TIMER_CALLBACK_MEMBER(timer_interrupt); + u8 step_counter_r(offs_t offset); + void step_counter_w(offs_t offset, u8 data); + u8 phase_counter_r(); + void phase_counter_w(u8 data); + u8 smcon_r(offs_t offset); + void smcon_w(offs_t offset, u8 data); void internal_map(address_map &map); @@ -137,6 +144,9 @@ private: u8 m_ad_register; u8 m_prescaler_reload[3]; u8 m_timer_reload[3]; + u8 m_step_counter[2]; + u8 m_phase_counter; + u8 m_smcon[2]; u8 m_interrupt_control[3]; }; -- cgit v1.2.3