summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/m6502/m50734.cpp
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2022-10-10 17:18:57 -0400
committer AJR <ajrhacker@users.noreply.github.com>2022-10-10 17:19:28 -0400
commitb09ea4008927f93d1605a48921eb5e18838c979d (patch)
tree015aa1cc6da5d846ae12b337718c19ecfd31d55d /src/devices/cpu/m6502/m50734.cpp
parentd6bf6306859943c87315b169b4c0267327e1ae9e (diff)
m50734: Emulate some on-chip timers
* r100: Add LCD unit
Diffstat (limited to 'src/devices/cpu/m6502/m50734.cpp')
-rw-r--r--src/devices/cpu/m6502/m50734.cpp97
1 files changed, 93 insertions, 4 deletions
diff --git a/src/devices/cpu/m6502/m50734.cpp b/src/devices/cpu/m6502/m50734.cpp
index dda9a6c8969..b4e31f003c7 100644
--- a/src/devices/cpu/m6502/m50734.cpp
+++ b/src/devices/cpu/m6502/m50734.cpp
@@ -15,6 +15,11 @@
#include "emu.h"
#include "m50734.h"
+#define LOG_INIT (1 << 1U)
+#define LOG_TIMER (1 << 2U)
+#define VERBOSE (0)
+#include "logmacro.h"
+
// device type definition
DEFINE_DEVICE_TYPE(M50734, m50734_device, "m50734", "Mitsubishi M50734")
@@ -28,6 +33,8 @@ m50734_device::m50734_device(const machine_config &mconfig, const char *tag, dev
, m_port_3state{0, 0, 0, 0}
, m_ad_control(0)
, m_ad_register(0)
+ , m_prescaler_reload{0xff, 0xff, 0xff}
+ , m_timer_reload{0xff, 0xff, 0xff}
{
program_config.m_internal_map = address_map_constructor(FUNC(m50734_device::internal_map), this);
}
@@ -49,6 +56,23 @@ void m50734_device::device_resolve_objects()
m_analog_in_cb.resolve_all_safe(0);
}
+template <int N>
+TIMER_CALLBACK_MEMBER(m50734_device::timer_interrupt)
+{
+ if (!BIT(m_interrupt_control[1], N * 2 + 1))
+ {
+ m_interrupt_control[1] |= 1 << (N * 2 + 1);
+ if (BIT(m_interrupt_control[1], N * 2))
+ {
+ LOGMASKED(LOG_TIMER, "Timer %d interrupt asserted at %s\n", N + 1, machine().time().to_string());
+ set_input_line(M740_INT2_LINE, ASSERT_LINE);
+ }
+ }
+
+ // Reload timer and prescaler
+ m_timer[N]->adjust(clocks_to_attotime(16 * u32(m_prescaler_reload[N] + 1) * (m_timer_reload[N] + 1)));
+}
+
void m50734_device::device_start()
{
m740_device::device_start();
@@ -56,6 +80,9 @@ void m50734_device::device_start()
space(has_space(AS_DATA) ? AS_DATA : AS_PROGRAM).specific(m_data);
m_ad_timer = timer_alloc(FUNC(m50734_device::ad_complete), this);
+ m_timer[0] = timer_alloc(FUNC(m50734_device::timer_interrupt<0>), this);
+ m_timer[1] = timer_alloc(FUNC(m50734_device::timer_interrupt<1>), this);
+ m_timer[2] = timer_alloc(FUNC(m50734_device::timer_interrupt<2>), this);
save_item(NAME(m_port_latch));
save_item(NAME(m_port_direction));
@@ -63,6 +90,9 @@ void m50734_device::device_start()
save_item(NAME(m_p2_p3_function));
save_item(NAME(m_ad_control));
save_item(NAME(m_ad_register));
+ save_item(NAME(m_prescaler_reload));
+ save_item(NAME(m_timer_reload));
+ save_item(NAME(m_interrupt_control));
}
void m50734_device::device_reset()
@@ -77,6 +107,10 @@ void m50734_device::device_reset()
m_p2_p3_function = 0x00;
m_ad_control |= 0x04;
m_ad_timer->adjust(attotime::never);
+
+ // Reset interrupts
+ std::fill(std::begin(m_interrupt_control), std::end(m_interrupt_control), 0x00);
+ set_input_line(M740_INT2_LINE, CLEAR_LINE);
}
void m50734_device::read_dummy(u16 adr)
@@ -100,6 +134,28 @@ void m50734_device::write_data(u16 adr, u8 val)
m740_device::write(adr, val);
}
+u8 m50734_device::interrupt_control_r(offs_t offset)
+{
+ return m_interrupt_control[2 - offset];
+}
+
+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)
+ {
+ bool timer_interrupt = (data & (data >> 1) & 0x15) != 0;
+ bool old_interrupt = (old_control & (old_control >> 1) & 0x15) != 0;
+ if (timer_interrupt != old_interrupt)
+ {
+ LOGMASKED(LOG_TIMER, "%s: Timer interrupt %sactivated by write to interrupt control register 2 ($%02X -> $%02X)\n", machine().describe_context(), timer_interrupt ? "": "de", old_control, data);
+ set_input_line(M740_INT2_LINE, timer_interrupt ? ASSERT_LINE : CLEAR_LINE);
+ }
+ }
+}
+
template <int N>
u8 m50734_device::port_r(offs_t offset)
{
@@ -118,7 +174,7 @@ void m50734_device::port_w(offs_t offset, u8 data)
{
if (m_port_direction[N] != data)
{
- logerror("%s: Port P%d direction = $%02X\n", machine().describe_context(), N, data);
+ LOGMASKED(LOG_INIT, "%s: Port P%d direction = $%02X\n", machine().describe_context(), N, data);
m_port_direction[N] = data;
m_port_out_cb[N]((data & m_port_latch[N]) | (m_port_3state[N] & ~m_port_direction[N]));
}
@@ -140,7 +196,7 @@ u8 m50734_device::p0_function_r()
void m50734_device::p0_function_w(u8 data)
{
- logerror("%s: Port P0 function = $%02X\n", machine().describe_context(), data);
+ LOGMASKED(LOG_INIT, "%s: Port P0 function = $%02X\n", machine().describe_context(), data);
m_p0_function = data;
}
@@ -151,7 +207,7 @@ u8 m50734_device::p2_p3_function_r()
void m50734_device::p2_p3_function_w(u8 data)
{
- logerror("%s: Port P2/P3 function = $%02X\n", machine().describe_context(), data);
+ LOGMASKED(LOG_INIT, "%s: Port P2/P3 function = $%02X\n", machine().describe_context(), data);
m_p2_p3_function = data & 0xc7;
}
@@ -177,9 +233,41 @@ u8 m50734_device::ad_r()
return m_ad_register;
}
+u8 m50734_device::timer_r(offs_t offset)
+{
+ if (!m_timer[offset >> 1]->enabled())
+ return 0;
+
+ u32 ticks = attotime_to_clocks(m_timer[offset >> 1]->remaining()) / 16;
+ u8 pre = m_prescaler_reload[offset >> 1];
+ if (BIT(offset, 0))
+ return std::min<u32>(ticks / (pre + 1), 0xff);
+ else
+ return ticks % (pre + 1);
+}
+
+void m50734_device::timer_w(offs_t offset, u8 data)
+{
+ u32 ticks = m_timer[offset >> 1]->enabled() ? attotime_to_clocks(m_timer[offset >> 1]->remaining()) / 16 : 0x10000;
+ 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);
+ m_timer_reload[offset >> 1] = data;
+ m_timer[offset >> 1]->adjust(clocks_to_attotime(16 * (data * pre + (ticks % (pre + 1)) + 1)));
+ }
+ else
+ {
+ LOGMASKED(LOG_INIT, "%s: Reload prescaler %d latch = %d\n", machine().describe_context(), (offset >> 1) + 1, data);
+ 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)));
+ }
+}
+
void m50734_device::internal_map(address_map &map)
{
- // TODO: timers, etc.
+ // TODO: other timers, UART, etc.
+ map(0x00dc, 0x00e1).rw(FUNC(m50734_device::timer_r), FUNC(m50734_device::timer_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));
@@ -189,4 +277,5 @@ void m50734_device::internal_map(address_map &map)
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(0x00fd, 0x00ff).rw(FUNC(m50734_device::interrupt_control_r), FUNC(m50734_device::interrupt_control_w));
}