m50734: Partially emulate stepper motor controller registers

This commit is contained in:
AJR 2023-03-12 16:41:40 -04:00
parent 6c22952441
commit 19a765d75a
2 changed files with 102 additions and 5 deletions

View File

@ -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 <int N>
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<u32>(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<u32>(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));
}

View File

@ -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 <int N> 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];
};