summaryrefslogtreecommitdiffstatshomepage
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
parentd6bf6306859943c87315b169b4c0267327e1ae9e (diff)
m50734: Emulate some on-chip timers
* r100: Add LCD unit
-rw-r--r--src/devices/cpu/m6502/m50734.cpp97
-rw-r--r--src/devices/cpu/m6502/m50734.h13
-rw-r--r--src/mame/kawai/kawai_r100.cpp52
3 files changed, 157 insertions, 5 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));
}
diff --git a/src/devices/cpu/m6502/m50734.h b/src/devices/cpu/m6502/m50734.h
index 425b6a34e34..0c98f86aa31 100644
--- a/src/devices/cpu/m6502/m50734.h
+++ b/src/devices/cpu/m6502/m50734.h
@@ -96,6 +96,9 @@ protected:
virtual void write_data(u16 adr, u8 val) override;
private:
+ u8 interrupt_control_r(offs_t offset);
+ void interrupt_control_w(offs_t offset, u8 data);
+
template <int N> u8 port_r(offs_t offset);
template <int N> void port_w(offs_t offset, u8 data);
u8 p4_r();
@@ -103,12 +106,16 @@ private:
void p0_function_w(u8 data);
u8 p2_p3_function_r();
void p2_p3_function_w(u8 data);
+
u8 ad_control_r();
void ad_control_w(u8 data);
u8 ad_r();
-
TIMER_CALLBACK_MEMBER(ad_complete);
+ u8 timer_r(offs_t offset);
+ void timer_w(offs_t offset, u8 data);
+ template <int N> TIMER_CALLBACK_MEMBER(timer_interrupt);
+
void internal_map(address_map &map);
const address_space_config m_data_config;
@@ -119,6 +126,7 @@ private:
devcb_read8::array<4> m_analog_in_cb;
emu_timer *m_ad_timer;
+ emu_timer *m_timer[3];
u8 m_port_latch[4];
u8 m_port_direction[4];
@@ -127,6 +135,9 @@ private:
u8 m_p2_p3_function;
u8 m_ad_control;
u8 m_ad_register;
+ u8 m_prescaler_reload[3];
+ u8 m_timer_reload[3];
+ u8 m_interrupt_control[3];
};
DECLARE_DEVICE_TYPE(M50734, m50734_device)
diff --git a/src/mame/kawai/kawai_r100.cpp b/src/mame/kawai/kawai_r100.cpp
index 8d9b73f8869..c98fc17366c 100644
--- a/src/mame/kawai/kawai_r100.cpp
+++ b/src/mame/kawai/kawai_r100.cpp
@@ -9,6 +9,9 @@
#include "emu.h"
#include "cpu/m6502/m50734.h"
#include "machine/nvram.h"
+#include "video/hd44780.h"
+#include "emupal.h"
+#include "screen.h"
class kawai_r100_state : public driver_device
{
@@ -16,26 +19,48 @@ public:
kawai_r100_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
+ , m_lcdc(*this, "lcdc")
{
}
void r100(machine_config &config);
+protected:
+ virtual void machine_start() override;
+
private:
+ HD44780_PIXEL_UPDATE(pixel_update);
+
void p0_w(u8 data);
void p1_w(u8 data);
void p2_w(u8 data);
void p3_w(u8 data);
+ u8 sensor_lsi_r(offs_t offset);
+ void buffer_w(u8 data);
void main_map(address_map &map);
void data_map(address_map &map);
required_device<m50734_device> m_maincpu;
+ required_device<hd44780_device> m_lcdc;
};
+void kawai_r100_state::machine_start()
+{
+ m_lcdc->rw_w(0); // write only
+}
+
+HD44780_PIXEL_UPDATE(kawai_r100_state::pixel_update)
+{
+ if (x < 5 && y < 8 && line < 2 && pos < 16)
+ bitmap.pix(line * 8 + y, pos * 6 + x) = state;
+}
+
void kawai_r100_state::p0_w(u8 data)
{
+ m_lcdc->rs_w(BIT(data, 3));
+ m_lcdc->e_w(BIT(data, 4));
}
void kawai_r100_state::p1_w(u8 data)
@@ -50,10 +75,22 @@ void kawai_r100_state::p3_w(u8 data)
{
}
+u8 kawai_r100_state::sensor_lsi_r(offs_t offset)
+{
+ return 0;
+}
+
+void kawai_r100_state::buffer_w(u8 data)
+{
+}
+
void kawai_r100_state::main_map(address_map &map)
{
map(0x0000, 0x1fff).ram().share("nvram1");
map(0x2000, 0x3fff).ram().share("nvram2");
+ map(0x4100, 0x41ff).r(FUNC(kawai_r100_state::sensor_lsi_r));
+ map(0x4200, 0x4200).mirror(0xff).w(m_lcdc, FUNC(hd44780_device::db_w));
+ map(0x4300, 0x4300).mirror(0xff).w(FUNC(kawai_r100_state::buffer_w));
map(0x4400, 0xffff).rom().region("program", 0x4400);
}
@@ -83,6 +120,21 @@ void kawai_r100_state::r100(machine_config &config)
NVRAM(config, "nvram2", nvram_device::DEFAULT_ALL_0); // MB8464-15LL-SK + battery
//M60009_AGU_DGU(config, "pcm", 5_MHz_XTAL);
+
+ // LCD unit
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
+ screen.set_refresh_hz(60);
+ screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
+ screen.set_screen_update("lcdc", FUNC(hd44780_device::screen_update));
+ screen.set_size(6*16, 8*2);
+ screen.set_visarea_full();
+ screen.set_palette("palette");
+
+ PALETTE(config, "palette", palette_device::MONOCHROME_INVERTED);
+
+ HD44780(config, m_lcdc, 0);
+ m_lcdc->set_lcd_size(2, 16);
+ m_lcdc->set_pixel_update_cb(FUNC(kawai_r100_state::pixel_update));
}
ROM_START(r100)