From c605f2ca4af851f470fc7425e937c512f09f8878 Mon Sep 17 00:00:00 2001 From: hap Date: Fri, 20 Sep 2024 09:42:32 +0200 Subject: hmcs400: add timers --- src/devices/cpu/hmcs400/hmcs400.cpp | 117 ++++++++++++++++++++++++++++++++++++ src/devices/cpu/hmcs400/hmcs400.h | 17 +++++- src/mame/handheld/hh_hmcs40.cpp | 1 + 3 files changed, 133 insertions(+), 2 deletions(-) diff --git a/src/devices/cpu/hmcs400/hmcs400.cpp b/src/devices/cpu/hmcs400/hmcs400.cpp index 823ac4fb896..19249f2f6b8 100644 --- a/src/devices/cpu/hmcs400/hmcs400.cpp +++ b/src/devices/cpu/hmcs400/hmcs400.cpp @@ -197,6 +197,12 @@ void hmcs400_cpu_device::device_start() m_int_line[0] = m_int_line[1] = 1; m_irq_flags = 0; m_pmr = 0; + m_prescaler = 0; + m_timer_mode[0] = m_timer_mode[1] = 0; + m_timer_div[0] = m_timer_div[1] = 0; + m_timer_count[0] = m_timer_count[1] = 0; + m_timer_load = 0; + m_timer_b_low = 0; // register for savestates save_item(NAME(m_pc)); @@ -215,11 +221,19 @@ void hmcs400_cpu_device::device_start() save_item(NAME(m_ca)); save_item(NAME(m_r)); + save_item(NAME(m_r_mask)); save_item(NAME(m_d)); + save_item(NAME(m_d_mask)); save_item(NAME(m_int_line)); save_item(NAME(m_irq_flags)); save_item(NAME(m_pmr)); + save_item(NAME(m_prescaler)); + save_item(NAME(m_timer_mode)); + save_item(NAME(m_timer_div)); + save_item(NAME(m_timer_count)); + save_item(NAME(m_timer_load)); + save_item(NAME(m_timer_b_low)); // register state for debugger state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%04X").noshow(); @@ -253,6 +267,13 @@ void hmcs400_cpu_device::device_reset() m_irq_flags = 0xaaa8; // IM=1, IF=0, IE=0 m_pmr = 0; + m_prescaler = 0; + tm_w(0, 0, 0xf); + tm_w(1, 0, 0xf); + m_timer_count[0] = m_timer_count[1] = 0; + m_timer_load = 0; + m_timer_b_low = 0; + // all I/O ports set to input reset_io(); } @@ -298,6 +319,9 @@ void hmcs400_cpu_device::data_map(address_map &map) map.unmap_value_high(); map(0x000, 0x003).rw(FUNC(hmcs400_cpu_device::irq_control_r), FUNC(hmcs400_cpu_device::irq_control_w)); map(0x004, 0x004).w(FUNC(hmcs400_cpu_device::pmr_w)); + map(0x008, 0x009).w(FUNC(hmcs400_cpu_device::tm_w)); + map(0x00a, 0x00a).rw(FUNC(hmcs400_cpu_device::tcbl_r), FUNC(hmcs400_cpu_device::tlrl_w)); + map(0x00b, 0x00b).rw(FUNC(hmcs400_cpu_device::tcbu_r), FUNC(hmcs400_cpu_device::tlru_w)); map(0x020, 0x020 + m_ram_size - 1).ram(); map(0x3c0, 0x3ff).ram(); @@ -475,6 +499,7 @@ void hmcs400_cpu_device::irq_control_w(offs_t offset, u8 data, u8 mem_mask) if (!access_mode(mem_mask, true)) return; + data &= mem_mask; u16 mask = mem_mask << (offset * 4); // ignore writes to unused bits @@ -540,7 +565,13 @@ void hmcs400_cpu_device::ext_int_edge(int line) { // ext interrupts are masked with PMR2/3 if (!m_int_line[line] && BIT(m_pmr, line + 2)) + { m_irq_flags |= 1 << (line * 2 + 2); + + // timer B event counter on INT1 + if (line == 1 && !m_timer_div[1]) + clock_timer(1); + } } void hmcs400_cpu_device::execute_set_input(int line, int state) @@ -568,6 +599,91 @@ void hmcs400_cpu_device::execute_set_input(int line, int state) } +//------------------------------------------------- +// timers +//------------------------------------------------- + +void hmcs400_cpu_device::tm_w(offs_t offset, u8 data, u8 mem_mask) +{ + if (!access_mode(mem_mask)) + return; + + // TMA/TMB prescaler divide ratio + static const int div[2][8] = + { + { 0x400, 0x200, 0x100, 0x40, 0x10, 4, 2, 1 }, + { 0x400, 0x100, 0x40, 0x10, 4, 2, 1, 0 } + }; + + m_timer_mode[offset] = data; + m_timer_div[offset] = div[offset][data & 7]; +} + +void hmcs400_cpu_device::tlrl_w(offs_t offset, u8 data, u8 mem_mask) +{ + if (!access_mode(mem_mask)) + return; + + // TLRL: timer load register lower + m_timer_load = (m_timer_load & 0xf0) | (data & 0xf); +} + +void hmcs400_cpu_device::tlru_w(offs_t offset, u8 data, u8 mem_mask) +{ + if (!access_mode(mem_mask)) + return; + + // TLRU: timer load register upper + m_timer_load = (m_timer_load & 0x0f) | data << 4; + m_timer_count[1] = m_timer_load; +} + +u8 hmcs400_cpu_device::tcbl_r(offs_t offset, u8 mem_mask) +{ + if (!access_mode(mem_mask)) + return 0xf; + + // TCBL: timer counter B lower + return m_timer_b_low; +} + +u8 hmcs400_cpu_device::tcbu_r(offs_t offset, u8 mem_mask) +{ + if (!access_mode(mem_mask)) + return 0xf; + + // TCBU: timer counter B upper (latches TCBL) + if (!machine().side_effects_disabled()) + m_timer_b_low = m_timer_count[1] & 0xf; + + return m_timer_count[1] >> 4; +} + +void hmcs400_cpu_device::clock_timer(int timer) +{ + if (++m_timer_count[timer] == 0) + { + // set timer overflow irq flag + m_irq_flags |= 1 << (timer * 2 + 6); + + // timer B reload function + if (timer == 1 && m_timer_mode[1] & 8) + m_timer_count[1] = m_timer_load; + } +} + +void hmcs400_cpu_device::clock_prescaler() +{ + u16 prev = m_prescaler; + m_prescaler = (m_prescaler + 1) & 0x7ff; + + // increment timers based on prescaler divide ratio + for (int i = 0; i < 2; i++) + if (m_prescaler & ~prev & m_timer_div[i]) + clock_timer(i); +} + + //------------------------------------------------- // execute //------------------------------------------------- @@ -575,6 +691,7 @@ void hmcs400_cpu_device::execute_set_input(int line, int state) void hmcs400_cpu_device::cycle() { m_icount--; + clock_prescaler(); } u16 hmcs400_cpu_device::fetch() diff --git a/src/devices/cpu/hmcs400/hmcs400.h b/src/devices/cpu/hmcs400/hmcs400.h index e21db4cba1d..10b3da0f1f9 100644 --- a/src/devices/cpu/hmcs400/hmcs400.h +++ b/src/devices/cpu/hmcs400/hmcs400.h @@ -147,10 +147,16 @@ protected: u8 m_int_line[2]; // INT0/INT1 pin state u16 m_irq_flags; // interrupt control bits u8 m_pmr; // port mode register + u16 m_prescaler; // 11-bit clock prescaler + u8 m_timer_mode[2]; // TMA/TMB: timer mode registers + u16 m_timer_div[2]; // timer prescaler divide ration derived from TMA/TMB + u8 m_timer_count[2]; // TCA/TCA: timer counters + u8 m_timer_load; // timer B reload register + u8 m_timer_b_low; // timer B counter low latch // I/O handlers - devcb_read8::array<8> m_read_r; - devcb_write8::array<8> m_write_r; + devcb_read8::array<11> m_read_r; + devcb_write8::array<11> m_write_r; devcb_read16 m_read_d; devcb_write16 m_write_d; @@ -170,9 +176,16 @@ protected: u8 irq_control_r(offs_t offset, u8 mem_mask); void irq_control_w(offs_t offset, u8 data, u8 mem_mask); void pmr_w(offs_t offset, u8 data, u8 mem_mask); + void tm_w(offs_t offset, u8 data, u8 mem_mask); + void tlrl_w(offs_t offset, u8 data, u8 mem_mask); + void tlru_w(offs_t offset, u8 data, u8 mem_mask); + u8 tcbl_r(offs_t offset, u8 mem_mask); + u8 tcbu_r(offs_t offset, u8 mem_mask); void ext_int_edge(int line); void check_interrupts(); + void clock_timer(int timer); + void clock_prescaler(); void cycle(); u16 fetch(); diff --git a/src/mame/handheld/hh_hmcs40.cpp b/src/mame/handheld/hh_hmcs40.cpp index 45fd4c9b756..fc9d8edea04 100644 --- a/src/mame/handheld/hh_hmcs40.cpp +++ b/src/mame/handheld/hh_hmcs40.cpp @@ -88,6 +88,7 @@ known chips: *A13 HD44840 1982, CXG Computachess II *A14 HD44840 1982, CXG Computachess II / Advanced Portachess + *B29 HD44860 1987, Diamond Bridge Computer (M1011) *B55 HD44860 1987, Saitek Pro Bridge 100 *A04 HD44868 1984, SciSys Rapier -- cgit v1.2.3