diff options
| author | 2023-09-22 13:56:24 +0900 | |
|---|---|---|
| committer | 2023-09-22 14:56:24 +1000 | |
| commit | f60fd23e3e10f140aa4a63ec1cf26cffd78f422a (patch) | |
| tree | e203d59afaa7da25cf63748eac9501ed65108216 /src/devices/cpu | |
| parent | 85ce279c637a21d9a62627392d1636ece7ec360d (diff) | |
namco/namcos12.cpp: Emulated games with CDXA board. (#11558)
* machine/t10mmc.cpp: Added support for T10SBC_CMD_SEEK_10 command.
* cpu/sh: Added SH7014 SoC.
* machine/icd2061a.cpp: Emulated IC Designs 2061A programmable clock generator.
* sound/lc78836m.cpp: Emulated Sanyo LC78836M audio DAC.
* namco/namcos12_cdxa.cpp: Emulated Namco System 12 CDXA board.
Systems promoted to working
-----------------------------
Truck Kyosokyoku (Japan, TKK2/VER.A) [Windy Fairy]
New working systems
-----------------------------
Um Jammer Lammy NOW! (Japan, UL1/VER.A) [Phil Bennett, Eric Yockey, Windy Fairy]
Diffstat (limited to 'src/devices/cpu')
| -rw-r--r-- | src/devices/cpu/sh/sh7014.cpp | 201 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014.h | 83 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_bsc.cpp | 148 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_bsc.h | 62 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_dmac.cpp | 400 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_dmac.h | 173 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_intc.cpp | 313 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_intc.h | 147 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_mtu.cpp | 571 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_mtu.h | 211 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_port.cpp | 243 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_port.h | 98 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_sci.cpp | 380 | ||||
| -rw-r--r-- | src/devices/cpu/sh/sh7014_sci.h | 135 |
14 files changed, 3165 insertions, 0 deletions
diff --git a/src/devices/cpu/sh/sh7014.cpp b/src/devices/cpu/sh/sh7014.cpp new file mode 100644 index 00000000000..ba47606c399 --- /dev/null +++ b/src/devices/cpu/sh/sh7014.cpp @@ -0,0 +1,201 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH-2 SH7014 + +***************************************************************************/ + +#include "emu.h" + +#include "sh7014.h" + + +DEFINE_DEVICE_TYPE(SH2_SH7014, sh2_sh7014_device, "sh2_sh7014", "Hitachi SH-2 (SH7014)") + +sh2_sh7014_device::sh2_sh7014_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sh2_device(mconfig, SH2_SH7014, tag, owner, clock, CPU_TYPE_SH2, address_map_constructor(FUNC(sh2_sh7014_device::sh7014_map), this), 32, 0xffffffff) + , m_sci(*this, "sci%u", 0u) + , m_bsc(*this, "bsc") + , m_dmac(*this, "dmac") + , m_intc(*this, "intc") + , m_mtu(*this, "mtu") + , m_port(*this, "io_port") + , m_sci_tx_cb(*this) +{ +} + +void sh2_sh7014_device::device_start() +{ + sh2_device::device_start(); + + save_item(NAME(m_ccr)); +} + +void sh2_sh7014_device::device_reset() +{ + sh2_device::device_reset(); + + // CAC + m_ccr = 0; +} + +void sh2_sh7014_device::device_add_mconfig(machine_config &config) +{ + SH7014_SCI(config, m_sci[0], DERIVED_CLOCK(1, 1), m_intc, + 0, // id + sh7014_intc_device::INT_VECTOR_SCI_ERI0, + sh7014_intc_device::INT_VECTOR_SCI_RXI0, + sh7014_intc_device::INT_VECTOR_SCI_TXI0, + sh7014_intc_device::INT_VECTOR_SCI_TEI0 + ); + + SH7014_SCI(config, m_sci[1], DERIVED_CLOCK(1, 1), m_intc, + 1, // id + sh7014_intc_device::INT_VECTOR_SCI_ERI1, + sh7014_intc_device::INT_VECTOR_SCI_RXI1, + sh7014_intc_device::INT_VECTOR_SCI_TXI1, + sh7014_intc_device::INT_VECTOR_SCI_TEI1 + ); + + SH7014_BSC(config, m_bsc); + + SH7014_DMAC(config, m_dmac, DERIVED_CLOCK(1, 1), *this, m_intc); + m_dmac->set_notify_dma_source_callback(FUNC(sh2_sh7014_device::notify_dma_source)); + + SH7014_INTC(config, m_intc); + m_intc->set_irq_callback(FUNC(sh2_sh7014_device::set_irq)); + + SH7014_MTU(config, m_mtu, DERIVED_CLOCK(1, 1), m_intc); + + SH7014_PORT(config, m_port); +} + +void sh2_sh7014_device::sh7014_map(address_map &map) +{ + // SCI - Serial Communication Interface + map(0xffff81a0, 0xffff81af).m(m_sci[0], FUNC(sh7014_sci_device::map)); + map(0xffff81b0, 0xffff81bf).m(m_sci[1], FUNC(sh7014_sci_device::map)); + + // MTU - Multifunction Timer Pulse Unit + map(0xffff8240, 0xffff82af).m(m_mtu, FUNC(sh7014_mtu_device::map)); + + // INTC - Interrupt Controller + map(0xffff8348, 0xffff835b).m(m_intc, FUNC(sh7014_intc_device::map)); + + // I/O - I/O Ports (DR registers) + // PFC - Pin Function Controller (IOR, CR registers) + // TODO: SH7016/SH7017 support additionally C and D ports in addition to the A, B, E, F that the SH7014 supports + map(0xffff8382, 0xffff8383).rw(m_port, FUNC(sh7014_port_device::padrl_r), FUNC(sh7014_port_device::padrl_w)); + map(0xffff8386, 0xffff8387).rw(m_port, FUNC(sh7014_port_device::paiorl_r), FUNC(sh7014_port_device::paiorl_w)); + map(0xffff838c, 0xffff838d).rw(m_port, FUNC(sh7014_port_device::pacrl1_r), FUNC(sh7014_port_device::pacrl1_w)); + map(0xffff838e, 0xffff838f).rw(m_port, FUNC(sh7014_port_device::pacrl2_r), FUNC(sh7014_port_device::pacrl2_w)); + + map(0xffff8390, 0xffff8391).rw(m_port, FUNC(sh7014_port_device::pbdr_r), FUNC(sh7014_port_device::pbdr_w)); + map(0xffff8394, 0xffff8395).rw(m_port, FUNC(sh7014_port_device::pbior_r), FUNC(sh7014_port_device::pbior_w)); + map(0xffff8398, 0xffff8399).rw(m_port, FUNC(sh7014_port_device::pbcr1_r), FUNC(sh7014_port_device::pbcr1_w)); + map(0xffff839a, 0xffff839b).rw(m_port, FUNC(sh7014_port_device::pbcr2_r), FUNC(sh7014_port_device::pbcr2_w)); + + map(0xffff83b0, 0xffff83b1).rw(m_port, FUNC(sh7014_port_device::pedr_r), FUNC(sh7014_port_device::pedr_w)); + map(0xffff83b4, 0xffff83b5).rw(m_port, FUNC(sh7014_port_device::peior_r), FUNC(sh7014_port_device::peior_w)); + map(0xffff83b8, 0xffff83b9).rw(m_port, FUNC(sh7014_port_device::pecr1_r), FUNC(sh7014_port_device::pecr1_w)); + map(0xffff83ba, 0xffff83bb).rw(m_port, FUNC(sh7014_port_device::pecr2_r), FUNC(sh7014_port_device::pecr2_w)); + + map(0xffff83b3, 0xffff83b3).r(m_port, FUNC(sh7014_port_device::pfdr_r)); + + // TODO: CMT - Compare Match Timer + // 0xffff83d0 - 0xffff83df + + // TODO: A/D - A/D Converter (High Speed, for SH7014) + // 0xffff83e0 - 0xffff83ff + + // TODO: A/D - A/D Converter (Mid Speed, for SH7016/SH7017) + // 0xffff8420 - 0xffff8429 + + // TODO: WDT - Watchdog Timer + // 0xffff8610 - 0xffff8613 + + // TODO: Power-down state + // 0xffff8614 + + // BSC - Bus State Controller + map(0xffff8620, 0xffff8633).m(m_bsc, FUNC(sh7014_bsc_device::map)); + + // DMAC - Direct Memory Access Controller + map(0xffff86b0, 0xffff86df).m(m_dmac, FUNC(sh7014_dmac_device::map)); + + // CAC - Cache Memory + map(0xffff8740, 0xffff8741).rw(FUNC(sh2_sh7014_device::ccr_r), FUNC(sh2_sh7014_device::ccr_w)); + + // Cache space + map(0xfffff000, 0xffffffff).ram(); +} + +void sh2_sh7014_device::sh2_exception_internal(const char *message, int irqline, int vector) +{ + // IRQ was taken so clear it in the interrupt controller and pass it down + m_intc->set_interrupt(vector, CLEAR_LINE); + sh2_device::sh2_exception_internal(message, irqline, vector); +} + +void sh2_sh7014_device::execute_set_input(int irqline, int state) +{ + /* + Flow for SH7014 IRQs: + sh2_sh7014_device::execute_set_input (for externally triggered IRQs) + -> sh7014_intc_device::set_input + -> sh2_sh7014_device::set_irq + -> sh2_device::execute_set_input (if not internal peripheral IRQ) OR DMA interception OR set sh2_device's internal IRQ flags + */ + m_intc->set_input(irqline, state); +} + +void sh2_sh7014_device::set_irq(int vector, int level, bool is_internal) +{ + if (!is_internal) { + sh2_device::execute_set_input(vector, ASSERT_LINE); + return; + } + + // SH7014's DMA controller can be configured to trigger based on various + // on-board peripheral IRQs, so on-board peripheral IRQs must go through here + if (m_dmac->is_dma_activated(vector)) { + m_intc->set_interrupt(vector, CLEAR_LINE); + return; + } + + m_sh2_state->internal_irq_level = level; + m_internal_irq_vector = vector; + m_test_irq = 1; +} + +void sh2_sh7014_device::notify_dma_source(uint32_t source) +{ + if (source == sh7014_dmac_channel_device::RS_SCI_TXI0) + m_sci[0]->set_dma_source_tx(true); + else if (source == sh7014_dmac_channel_device::RS_SCI_TXI1) + m_sci[1]->set_dma_source_tx(true); + else if (source == sh7014_dmac_channel_device::RS_SCI_RXI0) + m_sci[0]->set_dma_source_rx(true); + else if (source == sh7014_dmac_channel_device::RS_SCI_RXI1) + m_sci[1]->set_dma_source_rx(true); +} + +/////// +// CAC + +uint16_t sh2_sh7014_device::ccr_r() +{ + // bits 15-5 are undefined + return m_ccr & 0x1f; +} + +void sh2_sh7014_device::ccr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + // bit 0 - CECS0 CS0 Space Cache Enable + // bit 1 - CECS1 CS1 Space Cache Enable + // bit 2 - CECS2 CS2 Space Cache Enable + // bit 3 - CECS3 CS3 Space Cache Enable + // bit 4 - CEDRAM DRAM Space Cache Enable + COMBINE_DATA(&m_ccr); +} diff --git a/src/devices/cpu/sh/sh7014.h b/src/devices/cpu/sh/sh7014.h new file mode 100644 index 00000000000..873581404d1 --- /dev/null +++ b/src/devices/cpu/sh/sh7014.h @@ -0,0 +1,83 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH-2 SH7014 + +***************************************************************************/ + +#ifndef MAME_CPU_SH_SH7014_H +#define MAME_CPU_SH_SH7014_H + +#pragma once + +#include "sh2.h" +#include "sh7014_bsc.h" +#include "sh7014_dmac.h" +#include "sh7014_intc.h" +#include "sh7014_mtu.h" +#include "sh7014_port.h" +#include "sh7014_sci.h" + +class sh2_sh7014_device : public sh2_device +{ +public: + sh2_sh7014_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template<int Sci> auto sci_tx_w() { + return m_sci[Sci].lookup()->write_sci_tx(); + } + + template<int Sci> void sci_set_external_clock_period(const attotime &period) { + m_sci[Sci].lookup()->set_external_clock_period(period); + } + + template<int Sci> void sci_set_send_full_data_transmit_on_sync_hack(bool enabled) { + m_sci[Sci].lookup()->set_send_full_data_transmit_on_sync_hack(enabled); + } + + auto read_porta() { return m_port.lookup()->port_a_read_callback(); } + auto write_porta() { return m_port.lookup()->port_a_write_callback(); } + + auto read_portb() { return m_port.lookup()->port_b_read_callback(); } + auto write_portb() { return m_port.lookup()->port_b_write_callback(); } + + auto read_porte() { return m_port.lookup()->port_e_read_callback(); } + auto write_porte() { return m_port.lookup()->port_e_write_callback(); } + + auto read_portf() { return m_port.lookup()->port_f_read_callback(); } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + + virtual void execute_set_input(int inputnum, int state) override; + + virtual void sh2_exception_internal(const char *message, int irqline, int vector) override; + +private: + void sh7014_map(address_map &map); + + void set_irq(int vector, int level, bool is_internal); + + void notify_dma_source(uint32_t source); + + uint16_t ccr_r(); + void ccr_w(offs_t offset, uint16_t dat, uint16_t mem_mask = ~0); + + required_device_array<sh7014_sci_device, 2> m_sci; + required_device<sh7014_bsc_device> m_bsc; + required_device<sh7014_dmac_device> m_dmac; + required_device<sh7014_intc_device> m_intc; + required_device<sh7014_mtu_device> m_mtu; + required_device<sh7014_port_device> m_port; + + devcb_write_line::array<2> m_sci_tx_cb; + + uint16_t m_ccr; +}; + +DECLARE_DEVICE_TYPE(SH2_SH7014, sh2_sh7014_device) + +#endif // MAME_CPU_SH_SH7014_H diff --git a/src/devices/cpu/sh/sh7014_bsc.cpp b/src/devices/cpu/sh/sh7014_bsc.cpp new file mode 100644 index 00000000000..467bfdd1f46 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_bsc.cpp @@ -0,0 +1,148 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Bus State Controller + +***************************************************************************/ + +#include "emu.h" +#include "sh7014_bsc.h" + +// #define VERBOSE (LOG_GENERAL) + +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(SH7014_BSC, sh7014_bsc_device, "sh7014bsc", "SH7014 Bus State Controller") + + +sh7014_bsc_device::sh7014_bsc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_BSC, tag, owner, clock) +{ +} + +void sh7014_bsc_device::device_start() +{ + save_item(NAME(m_bcr1)); + save_item(NAME(m_bcr2)); + save_item(NAME(m_wcr1)); + save_item(NAME(m_wcr2)); + save_item(NAME(m_dcr)); + save_item(NAME(m_rtcsr)); + save_item(NAME(m_rtcnt)); + save_item(NAME(m_rtcor)); +} + +void sh7014_bsc_device::device_reset() +{ + m_bcr1 = 0; + m_bcr2 = 0xffff; + m_wcr1 = 0xffff; + m_wcr2 = 0x000f; + m_dcr = 0; + m_rtcsr = 0; + m_rtcnt = 0; + m_rtcor = 0; +} + +void sh7014_bsc_device::map(address_map &map) +{ + map(0x00, 0x01).rw(FUNC(sh7014_bsc_device::bcr1_r), FUNC(sh7014_bsc_device::bcr1_w)); + map(0x02, 0x03).rw(FUNC(sh7014_bsc_device::bcr2_r), FUNC(sh7014_bsc_device::bcr2_w)); + map(0x04, 0x05).rw(FUNC(sh7014_bsc_device::wcr1_r), FUNC(sh7014_bsc_device::wcr1_w));; + map(0x06, 0x07).rw(FUNC(sh7014_bsc_device::wcr2_r), FUNC(sh7014_bsc_device::wcr2_w)); + map(0x0a, 0x0b).rw(FUNC(sh7014_bsc_device::dcr_r), FUNC(sh7014_bsc_device::dcr_w)); + map(0x0c, 0x0d).rw(FUNC(sh7014_bsc_device::rtcsr_r), FUNC(sh7014_bsc_device::rtcsr_w)); + map(0x0e, 0x0f).rw(FUNC(sh7014_bsc_device::rtcnt_r), FUNC(sh7014_bsc_device::rtcnt_w)); + map(0x10, 0x11).rw(FUNC(sh7014_bsc_device::rtcor_r), FUNC(sh7014_bsc_device::rtcor_w)); +} + +/// + +uint16_t sh7014_bsc_device::bcr1_r() +{ + // bit 0 [R/W] CS0 Space Size Specification (A0SZ) + // bit 1 [R/W] CS1 Space Size Specification (A1SZ) + // bit 2 [R/W] CS2 Space Size Specification (A2SZ) + // bit 3 [R/W] CS3 Space Size Specification (A3SZ) + // bit 8 [R/W] Multiplex I/O Enable (IOE) + // bit 13 [R] Always returns 1 + // Everything else is read-only and will return 0 + return (m_bcr1 & 0x10f) | (1 << 13); +} + +void sh7014_bsc_device::bcr1_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_bcr1); +} + +uint16_t sh7014_bsc_device::bcr2_r() +{ + return m_bcr2; +} + +void sh7014_bsc_device::bcr2_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_bcr2); +} + +uint16_t sh7014_bsc_device::wcr1_r() +{ + return m_wcr1; +} + +void sh7014_bsc_device::wcr1_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_wcr1); +} + +uint16_t sh7014_bsc_device::wcr2_r() +{ + return m_wcr2 & 0x3ff; +} + +void sh7014_bsc_device::wcr2_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_wcr2); +} + +uint16_t sh7014_bsc_device::dcr_r() +{ + return m_dcr & ~0x48; +} + +void sh7014_bsc_device::dcr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_dcr); +} + +uint16_t sh7014_bsc_device::rtcsr_r() +{ + return m_rtcsr & 0x7f; +} + +void sh7014_bsc_device::rtcsr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_rtcsr); +} + +uint16_t sh7014_bsc_device::rtcnt_r() +{ + return m_rtcnt & 0xff; +} + +void sh7014_bsc_device::rtcnt_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_rtcnt); +} + +uint16_t sh7014_bsc_device::rtcor_r() +{ + return m_rtcor & 0xff; +} + +void sh7014_bsc_device::rtcor_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_rtcor); +} diff --git a/src/devices/cpu/sh/sh7014_bsc.h b/src/devices/cpu/sh/sh7014_bsc.h new file mode 100644 index 00000000000..97f4b0c879c --- /dev/null +++ b/src/devices/cpu/sh/sh7014_bsc.h @@ -0,0 +1,62 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Bus State Controller + +***************************************************************************/ + +#ifndef MAME_CPU_SH_SH7014_BSC_H +#define MAME_CPU_SH_SH7014_BSC_H + +#pragma once + + +class sh7014_bsc_device : public device_t +{ +public: + sh7014_bsc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + void map(address_map &map); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + uint16_t bcr1_r(); + void bcr1_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t bcr2_r(); + void bcr2_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t wcr1_r(); + void wcr1_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t wcr2_r(); + void wcr2_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t dcr_r(); + void dcr_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t rtcsr_r(); + void rtcsr_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t rtcor_r(); + void rtcor_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t rtcnt_r(); + void rtcnt_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t m_bcr1, m_bcr2; + uint16_t m_wcr1, m_wcr2; + uint16_t m_dcr; + uint16_t m_rtcsr; + uint16_t m_rtcnt; + uint16_t m_rtcor; +}; + + +DECLARE_DEVICE_TYPE(SH7014_BSC, sh7014_bsc_device) + +#endif // MAME_CPU_SH_SH7014_BSC_H diff --git a/src/devices/cpu/sh/sh7014_dmac.cpp b/src/devices/cpu/sh/sh7014_dmac.cpp new file mode 100644 index 00000000000..5dcb02304e6 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_dmac.cpp @@ -0,0 +1,400 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Direct Memory Access Controller + + TODO list (not comprehensive): + - Channel priority + - External dual and single address modes + - DREQ, DACK, DRAK pins + +***************************************************************************/ + +#include "emu.h" +#include "sh7014_dmac.h" + +#define LOG_TX (1U << 1) + +// #define VERBOSE (LOG_GENERAL | LOG_TX) + +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(SH7014_DMAC, sh7014_dmac_device, "sh7014dmac", "SH7014 Direct Memory Access Controller") +DEFINE_DEVICE_TYPE(SH7014_DMAC_CHANNEL, sh7014_dmac_channel_device, "sh7014dmacchan", "SH7014 Direct Memory Access Controller Channel") + + +sh7014_dmac_device::sh7014_dmac_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_DMAC, tag, owner, clock) + , m_cpu(*this, finder_base::DUMMY_TAG) + , m_intc(*this, finder_base::DUMMY_TAG) + , m_chan(*this, "ch%u", 0u) +{ +} + +void sh7014_dmac_device::device_start() +{ + save_item(NAME(m_dmaor)); +} + +void sh7014_dmac_device::device_reset() +{ + m_dmaor = 0; +} + +void sh7014_dmac_device::device_add_mconfig(machine_config &config) +{ + SH7014_DMAC_CHANNEL(config, m_chan[0], DERIVED_CLOCK(1, 1), *this, m_cpu, m_intc, + 0, // channel + sh7014_intc_device::INT_VECTOR_DMA_CH0 + ); + + SH7014_DMAC_CHANNEL(config, m_chan[1], DERIVED_CLOCK(1, 1), *this, m_cpu, m_intc, + 1, // channel + sh7014_intc_device::INT_VECTOR_DMA_CH1 + ); +} + +void sh7014_dmac_device::map(address_map &map) +{ + map(0x00, 0x01).rw(FUNC(sh7014_dmac_device::dmaor_r), FUNC(sh7014_dmac_device::dmaor_w)); + map(0x10, 0x1f).m(m_chan[0], FUNC(sh7014_dmac_channel_device::map)); + map(0x20, 0x2f).m(m_chan[1], FUNC(sh7014_dmac_channel_device::map)); +} + +/// + +uint16_t sh7014_dmac_device::dmaor_r() +{ + return m_dmaor & 7; +} + +void sh7014_dmac_device::dmaor_w(uint16_t data) +{ + m_dmaor = (data & ~6) | (m_dmaor & data & 6); + m_chan[0]->dma_check(); + m_chan[1]->dma_check(); +} + +bool sh7014_dmac_device::is_transfer_allowed() +{ + const bool is_enabled = (m_dmaor & DMAOR_DME) != 0; + const bool has_address_error = (m_dmaor & DMAOR_AE) != 0; + const bool has_nmi_flag = (m_dmaor & DMAOR_NMIF) != 0; + return is_enabled && !has_nmi_flag && !has_address_error; +} + +int sh7014_dmac_device::is_dma_activated(int vector) +{ + int activated = 0; + + if (!is_transfer_allowed()) + return 0; + + if (m_chan[0]->is_dma_activated(vector)) + activated |= 1; + if (m_chan[1]->is_dma_activated(vector)) + activated |= 2; + + return activated; +} + + +////////////////// + + +void sh7014_dmac_channel_device::map(address_map &map) +{ + map(0x00, 0x03).rw(FUNC(sh7014_dmac_channel_device::sar_r), FUNC(sh7014_dmac_channel_device::sar_w)); + map(0x04, 0x07).rw(FUNC(sh7014_dmac_channel_device::dar_r), FUNC(sh7014_dmac_channel_device::dar_w)); + map(0x08, 0x0b).rw(FUNC(sh7014_dmac_channel_device::dmatcr_r), FUNC(sh7014_dmac_channel_device::dmatcr_w)); + map(0x0c, 0x0f).rw(FUNC(sh7014_dmac_channel_device::chcr_r), FUNC(sh7014_dmac_channel_device::chcr_w)); +} + +sh7014_dmac_channel_device::sh7014_dmac_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_DMAC_CHANNEL, tag, owner, clock) + , m_dmac(*this, finder_base::DUMMY_TAG) + , m_cpu(*this, finder_base::DUMMY_TAG) + , m_intc(*this, finder_base::DUMMY_TAG) + , m_notify_dma_source_cb(*this) +{ +} + +void sh7014_dmac_channel_device::device_start() +{ + save_item(NAME(m_sar)); + save_item(NAME(m_dar)); + save_item(NAME(m_dmatcr)); + save_item(NAME(m_chcr)); + + save_item(NAME(m_dma_timer_active)); + save_item(NAME(m_active_dma_addr_mode_source)); + save_item(NAME(m_active_dma_addr_mode_dest)); + save_item(NAME(m_active_dma_unit_size)); + save_item(NAME(m_active_dma_is_burst)); + + save_item(NAME(m_selected_resource)); + save_item(NAME(m_request_source_type)); + save_item(NAME(m_expected_irq_vector)); + + m_dma_current_active_timer = timer_alloc(FUNC(sh7014_dmac_channel_device::dma_timer_callback), this); +} + +void sh7014_dmac_channel_device::device_reset() +{ + m_sar = m_dar = 0; + m_dmatcr = 0; + m_chcr = 0; + + m_dma_timer_active = false; + m_active_dma_addr_mode_source = 0; + m_active_dma_addr_mode_dest = 0; + m_active_dma_unit_size = 1; + m_active_dma_is_burst = false; + + m_selected_resource = RS_EXTERNAL_DUAL_ADDR; + m_request_source_type = RS_TYPE_EXTERNAL_DUAL; + m_expected_irq_vector = -1; + + m_dma_current_active_timer->adjust(attotime::never); +} + +/// + +bool sh7014_dmac_channel_device::is_enabled() +{ + return (m_chcr & CHCR_DE) != 0; +} + +uint32_t sh7014_dmac_channel_device::sar_r() +{ + return m_sar; +} + +void sh7014_dmac_channel_device::sar_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_sar); +} + +uint32_t sh7014_dmac_channel_device::dar_r() +{ + return m_dar; +} + +void sh7014_dmac_channel_device::dar_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dar); +} + +uint32_t sh7014_dmac_channel_device::dmatcr_r() +{ + return m_dmatcr & 0xffff; +} + +void sh7014_dmac_channel_device::dmatcr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_dmatcr); + m_dmatcr &= 0xffff; +} + +uint32_t sh7014_dmac_channel_device::chcr_r() +{ + return m_chcr & 0xbff7f; +} + +void sh7014_dmac_channel_device::chcr_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + COMBINE_DATA(&m_chcr); + + m_selected_resource = BIT(m_chcr, 8, 4); + m_expected_irq_vector = -1; + + switch (m_selected_resource) { + case RS_MTU_TGI0A: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_MTU_TGI0A; + break; + case RS_MTU_TGI1A: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_MTU_TGI1A; + break; + case RS_MTU_TGI2A: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_MTU_TGI2A; + break; + case RS_AD: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_AD; + break; + case RS_SCI_TXI0: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_SCI_TXI0; + break; + case RS_SCI_RXI0: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_SCI_RXI0; + break; + case RS_SCI_TXI1: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_SCI_TXI1; + break; + case RS_SCI_RXI1: + m_request_source_type = RS_TYPE_INTERNAL; + m_expected_irq_vector = sh7014_intc_device::INT_VECTOR_SCI_RXI1; + break; + + case RS_AUTO_REQUEST: + m_request_source_type = RS_TYPE_AUTO; + break; + + case RS_EXTERNAL_DUAL_ADDR: + m_request_source_type = RS_TYPE_EXTERNAL_DUAL; + break; + + case RS_EXTERNAL_SINGLE_ADDR_TO_DEV: + case RS_EXTERNAL_SINGLE_DEV_TO_ADDR: + m_request_source_type = RS_TYPE_EXTERNAL_SINGLE; + break; + + default: + m_request_source_type = RS_TYPE_PROHIBITED; + break; + } + + dma_check(); +} + +bool sh7014_dmac_channel_device::is_dma_activated(int vector) +{ + if (m_request_source_type != RS_TYPE_INTERNAL || m_expected_irq_vector == -1 || vector != m_expected_irq_vector) + return false; + + if (!m_dma_timer_active) + dma_check(); + + if (!m_dma_timer_active) + return false; + + m_dma_current_active_timer->adjust(attotime::from_ticks(2, clock())); + + return true; +} + +TIMER_CALLBACK_MEMBER( sh7014_dmac_channel_device::dma_timer_callback ) +{ + if (!m_dma_timer_active) + return; + + if (!m_dmac->is_transfer_allowed()) { + LOG("SH7014: DMA %d transfer aborted\n", m_channel_id); + m_dma_timer_active = false; + return; + } + + LOGMASKED(LOG_TX, "DMAC ch %d remaining %08x size %d | source %08x dest %08x\n", m_channel_id, m_dmatcr, m_active_dma_unit_size, m_sar, m_dar); + + if (m_active_dma_addr_mode_source == DMA_ADDR_MODE_DEC) + m_sar -= m_active_dma_unit_size; + + if (m_active_dma_addr_mode_dest == DMA_ADDR_MODE_DEC) + m_dar -= m_active_dma_unit_size; + + if (m_request_source_type == RS_TYPE_INTERNAL) + m_notify_dma_source_cb(m_selected_resource); + + switch (m_active_dma_unit_size) { + case 1: + m_cpu->m_program->write_byte( + m_dar, + m_cpu->m_program->read_byte(m_sar) + ); + break; + case 2: + m_cpu->m_program->write_word( + m_dar, + m_cpu->m_program->read_word(m_sar) + ); + break; + case 4: + m_cpu->m_program->write_dword( + m_dar, + m_cpu->m_program->read_dword(m_sar) + ); + break; + } + + if (m_active_dma_addr_mode_source == DMA_ADDR_MODE_INC) + m_sar += m_active_dma_unit_size; + + if (m_active_dma_addr_mode_dest == DMA_ADDR_MODE_INC) + m_dar += m_active_dma_unit_size; + + m_dmatcr--; + + if (m_dmatcr <= 0) { + LOGMASKED(LOG_TX, "SH7014: DMA %d complete\n", m_channel_id); + + m_dmatcr = 0; + m_chcr |= CHCR_TE; // transfer end + m_dma_timer_active = false; + + if (m_active_dma_is_burst) + m_cpu->resume(SUSPEND_REASON_HALT); + + const auto interrupt_enable = (m_chcr & CHCR_IE) != 0; + if (interrupt_enable) + m_intc->set_interrupt(m_vector, ASSERT_LINE); + } else { + // schedule next DMA callback + // Internal source transfers in burst mode end on last transfer, otherwise should end after the first transfer when burst mode is off + // TODO: DREQ and DACK are needed for external sources instead of being handled like an auto requests + if (m_request_source_type != RS_TYPE_INTERNAL || (m_request_source_type == RS_TYPE_INTERNAL && m_active_dma_is_burst)) + m_dma_current_active_timer->adjust(attotime::from_ticks(2, clock())); + } +} + +void sh7014_dmac_channel_device::dma_check() +{ + if (!m_dmac->is_transfer_allowed()) { + if (m_dma_timer_active) { + LOG("SH7014: DMA %d cancelled in-flight\n", m_channel_id); + m_dma_current_active_timer->adjust(attotime::never); + m_dma_timer_active = false; + } + + return; + } + + if (m_dma_timer_active) + return; + + m_active_dma_addr_mode_dest = BIT(m_chcr, 14, 2); // DM + m_active_dma_addr_mode_source = BIT(m_chcr, 12, 2); // SM + m_active_dma_unit_size = 1 << BIT(m_chcr, 3, 2); // TS + m_active_dma_is_burst = (m_chcr & CHCR_TM) != 0; + + if (m_active_dma_addr_mode_dest == DMA_ADDR_MODE_PROHIBITED || m_active_dma_addr_mode_source == DMA_ADDR_MODE_PROHIBITED || m_active_dma_unit_size > 4) { + LOG("SH7014: DMA %d: bad increment values (%d, %d, %d, %04x)\n", m_channel_id, m_active_dma_addr_mode_dest, m_active_dma_addr_mode_source, m_active_dma_unit_size, m_chcr); + return; + } + + if (m_dmatcr == 0) + m_dmatcr = 0x10000; + + m_dma_timer_active = true; + + LOG("SH7014: DMA %d start %x, %x, %x, %04x, %d, %d, %d, %d\n", m_channel_id, m_sar, m_dar, m_dmatcr, m_chcr, m_active_dma_addr_mode_source, m_active_dma_addr_mode_dest, m_active_dma_unit_size, m_active_dma_is_burst); + + if (m_active_dma_unit_size > 1) { + const int mask = m_active_dma_unit_size - 1; + m_sar &= ~mask; + m_dar &= ~mask; + } + + if (m_active_dma_is_burst) + m_cpu->suspend(SUSPEND_REASON_HALT, 1); + + if (m_request_source_type != RS_TYPE_INTERNAL || (m_request_source_type == RS_TYPE_INTERNAL && m_active_dma_is_burst)) + m_dma_current_active_timer->adjust(attotime::from_ticks(2, clock())); +} diff --git a/src/devices/cpu/sh/sh7014_dmac.h b/src/devices/cpu/sh/sh7014_dmac.h new file mode 100644 index 00000000000..d9c92918222 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_dmac.h @@ -0,0 +1,173 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Direct Memory Access Controller + +***************************************************************************/ + +#ifndef MAME_CPU_SH_SH7014_DMAC_H +#define MAME_CPU_SH_SH7014_DMAC_H + +#pragma once + +#include "sh2.h" +#include "sh7014_intc.h" + +DECLARE_DEVICE_TYPE(SH7014_DMAC, sh7014_dmac_device) +DECLARE_DEVICE_TYPE(SH7014_DMAC_CHANNEL, sh7014_dmac_channel_device) + +class sh7014_dmac_device; + +class sh7014_dmac_channel_device : public device_t +{ +public: + enum { + RS_EXTERNAL_DUAL_ADDR = 0, // External request, dual address mode + RS_EXTERNAL_SINGLE_ADDR_TO_DEV = 2, // External request, single address mode. External address space -> external device + RS_EXTERNAL_SINGLE_DEV_TO_ADDR = 3, // External request, single address mode. External device -> external address space + RS_AUTO_REQUEST = 4, + RS_MTU_TGI0A = 6, + RS_MTU_TGI1A = 7, + RS_MTU_TGI2A = 8, + RS_AD = 11, + RS_SCI_TXI0 = 12, + RS_SCI_RXI0 = 13, + RS_SCI_TXI1 = 14, + RS_SCI_RXI1 = 15, + }; + + sh7014_dmac_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template<typename T, typename U, typename V> sh7014_dmac_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&dmac, U &&cpu, V &&intc, int chan_id, int vector) + : sh7014_dmac_channel_device(mconfig, tag, owner, clock) + { + m_dmac.set_tag(std::forward<T>(dmac)); + m_cpu.set_tag(std::forward<U>(cpu)); + m_intc.set_tag(std::forward<V>(intc)); + m_channel_id = chan_id; + m_vector = vector; + } + + auto notify_dma_source_callback() { return m_notify_dma_source_cb.bind(); } + + void map(address_map &map); + + void dma_check(); + + bool is_dma_activated(int vector); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + enum { + RS_TYPE_EXTERNAL_DUAL = 0, + RS_TYPE_EXTERNAL_SINGLE, + RS_TYPE_AUTO, + RS_TYPE_INTERNAL, + RS_TYPE_PROHIBITED, + }; + + enum { + DMA_ADDR_MODE_FIXED = 0, + DMA_ADDR_MODE_INC, + DMA_ADDR_MODE_DEC, + DMA_ADDR_MODE_PROHIBITED, + }; + + enum { + CHCR_DE = 1 << 0, + CHCR_TE = 1 << 1, + CHCR_IE = 1 << 2, + CHCR_TM = 1 << 5, + }; + + bool is_enabled(); + + uint32_t sar_r(); + void sar_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + + uint32_t dar_r(); + void dar_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + + uint32_t dmatcr_r(); + void dmatcr_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + + uint32_t chcr_r(); + void chcr_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + + TIMER_CALLBACK_MEMBER( dma_timer_callback ); + + required_device<sh7014_dmac_device> m_dmac; + required_device<sh2_device> m_cpu; + required_device<sh7014_intc_device> m_intc; + + emu_timer *m_dma_current_active_timer; + + devcb_write32 m_notify_dma_source_cb; + + uint32_t m_channel_id; + uint32_t m_vector; + + uint32_t m_chcr; + uint32_t m_sar; + uint32_t m_dar; + int32_t m_dmatcr; + + bool m_dma_timer_active; + uint32_t m_active_dma_addr_mode_source; + uint32_t m_active_dma_addr_mode_dest; + uint32_t m_active_dma_unit_size; + bool m_active_dma_is_burst; + + int32_t m_request_source_type, m_selected_resource; + int32_t m_expected_irq_vector; +}; + +class sh7014_dmac_device : public device_t +{ +public: + sh7014_dmac_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template<typename T, typename U> sh7014_dmac_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&cpu, U &&intc) + : sh7014_dmac_device(mconfig, tag, owner, clock) + { + m_cpu.set_tag(std::forward<T>(cpu)); + m_intc.set_tag(std::forward<U>(intc)); + } + + template <typename... T> void set_notify_dma_source_callback(T &&... args) { + m_chan[0].lookup()->notify_dma_source_callback().set(std::forward<T>(args)...); + m_chan[1].lookup()->notify_dma_source_callback().set(std::forward<T>(args)...); + } + + void map(address_map &map); + + int is_dma_activated(int vector); + bool is_transfer_allowed(); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + +private: + enum { + DMAOR_DME = 1 << 0, + DMAOR_NMIF = 1 << 1, + DMAOR_AE = 1 << 2, + }; + + void dmaor_w(uint16_t data); + uint16_t dmaor_r(); + + required_device<sh2_device> m_cpu; + required_device<sh7014_intc_device> m_intc; + required_device_array<sh7014_dmac_channel_device, 2> m_chan; + + uint16_t m_dmaor; +}; + +#endif // MAME_CPU_SH_SH7014_DMAC_H diff --git a/src/devices/cpu/sh/sh7014_intc.cpp b/src/devices/cpu/sh/sh7014_intc.cpp new file mode 100644 index 00000000000..c6f1ea521ad --- /dev/null +++ b/src/devices/cpu/sh/sh7014_intc.cpp @@ -0,0 +1,313 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Interrupt Controller + +***************************************************************************/ + +#include "emu.h" +#include "sh7014_intc.h" + +// #define VERBOSE (LOG_GENERAL) + +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(SH7014_INTC, sh7014_intc_device, "sh7014intc", "SH7014 Interrupt Controller") + + +sh7014_intc_device::sh7014_intc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_INTC, tag, owner, clock) + , m_set_irq_cb(*this) +{ +} + +void sh7014_intc_device::device_start() +{ + m_set_irq_cb.resolve(); + + save_item(NAME(m_ipra)); + save_item(NAME(m_iprb)); + save_item(NAME(m_iprc)); + save_item(NAME(m_iprd)); + save_item(NAME(m_ipre)); + save_item(NAME(m_iprf)); + save_item(NAME(m_iprg)); + save_item(NAME(m_iprh)); + save_item(NAME(m_icr)); + save_item(NAME(m_isr)); + save_item(NAME(m_irq_type)); + save_item(NAME(m_nmi_input)); + save_item(NAME(m_pending_irqs)); + save_item(NAME(m_irq_levels)); +} + +void sh7014_intc_device::device_reset() +{ + m_ipra = 0; + m_iprb = 0; + m_iprc = 0; + m_iprd = 0; + m_ipre = 0; + m_iprf = 0; + m_iprg = 0; + m_iprh = 0; + m_icr = m_isr = 0; + m_nmi_input = false; + + std::fill(std::begin(m_irq_type), std::end(m_irq_type), IRQ_LEVEL); + std::fill(std::begin(m_irq_levels), std::end(m_irq_levels), 0); + std::fill(std::begin(m_pending_irqs), std::end(m_pending_irqs), 0); + + m_irq_levels[INT_VECTOR_NMI] = 16; // fixed value +} + +void sh7014_intc_device::map(address_map &map) +{ + map(0x00, 0x01).rw(FUNC(sh7014_intc_device::ipra_r), FUNC(sh7014_intc_device::ipra_w)); + map(0x02, 0x03).rw(FUNC(sh7014_intc_device::iprb_r), FUNC(sh7014_intc_device::iprb_w)); + map(0x04, 0x05).rw(FUNC(sh7014_intc_device::iprc_r), FUNC(sh7014_intc_device::iprc_w)); + map(0x06, 0x07).rw(FUNC(sh7014_intc_device::iprd_r), FUNC(sh7014_intc_device::iprd_w)); + map(0x08, 0x09).rw(FUNC(sh7014_intc_device::ipre_r), FUNC(sh7014_intc_device::ipre_w)); + map(0x0a, 0x0b).rw(FUNC(sh7014_intc_device::iprf_r), FUNC(sh7014_intc_device::iprf_w)); + map(0x0c, 0x0d).rw(FUNC(sh7014_intc_device::iprg_r), FUNC(sh7014_intc_device::iprg_w)); + map(0x0e, 0x0f).rw(FUNC(sh7014_intc_device::iprh_r), FUNC(sh7014_intc_device::iprh_w)); + map(0x10, 0x11).rw(FUNC(sh7014_intc_device::icr_r), FUNC(sh7014_intc_device::icr_w)); + map(0x12, 0x13).rw(FUNC(sh7014_intc_device::isr_r), FUNC(sh7014_intc_device::isr_w)); +} + +/// + +uint16_t sh7014_intc_device::ipra_r() +{ + return m_ipra; +} + +void sh7014_intc_device::ipra_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_ipra); + + m_irq_levels[INT_VECTOR_IRQ0] = BIT(m_ipra, 12, 4); + m_irq_levels[INT_VECTOR_IRQ1] = BIT(m_ipra, 8, 4); + m_irq_levels[INT_VECTOR_IRQ2] = BIT(m_ipra, 4, 4); + m_irq_levels[INT_VECTOR_IRQ3] = BIT(m_ipra, 0, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::iprb_r() +{ + return m_iprb & 0xff00; +} + +void sh7014_intc_device::iprb_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_iprb); + + m_irq_levels[INT_VECTOR_IRQ6] = BIT(m_iprb, 4, 4); + m_irq_levels[INT_VECTOR_IRQ7] = BIT(m_iprb, 0, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::iprc_r() +{ + return m_iprc; +} + +void sh7014_intc_device::iprc_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_iprc); + + m_irq_levels[INT_VECTOR_DMA_CH0] = BIT(m_iprc, 12, 4); + m_irq_levels[INT_VECTOR_DMA_CH1] = BIT(m_iprc, 8, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::iprd_r() +{ + return m_iprd; +} + +void sh7014_intc_device::iprd_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_iprd); + + m_irq_levels[INT_VECTOR_MTU_TGI0A] = m_irq_levels[INT_VECTOR_MTU_TGI0B] = m_irq_levels[INT_VECTOR_MTU_TGI0C] = m_irq_levels[INT_VECTOR_MTU_TGI0D] = BIT(m_iprd, 12, 4); + m_irq_levels[INT_VECTOR_MTU_TGI0V] = BIT(m_iprd, 8, 4); + m_irq_levels[INT_VECTOR_MTU_TGI1A] = m_irq_levels[INT_VECTOR_MTU_TGI1B] = BIT(m_iprd, 4, 4); + m_irq_levels[INT_VECTOR_MTU_TGI1V] = m_irq_levels[INT_VECTOR_MTU_TGI1U] = BIT(m_iprd, 0, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::ipre_r() +{ + return m_ipre & 0xff00; +} + +void sh7014_intc_device::ipre_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_ipre); + + m_irq_levels[INT_VECTOR_MTU_TGI2A] = m_irq_levels[INT_VECTOR_MTU_TGI2B] = BIT(m_ipre, 12, 4); + m_irq_levels[INT_VECTOR_MTU_TGI2V] = m_irq_levels[INT_VECTOR_MTU_TGI2U] = BIT(m_ipre, 8, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::iprf_r() +{ + return m_iprf & 0xff; +} + +void sh7014_intc_device::iprf_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_iprf); + + m_irq_levels[INT_VECTOR_SCI_ERI0] = m_irq_levels[INT_VECTOR_SCI_RXI0] = m_irq_levels[INT_VECTOR_SCI_TXI0] = m_irq_levels[INT_VECTOR_SCI_TEI0] = BIT(m_iprf, 4, 4); + m_irq_levels[INT_VECTOR_SCI_ERI1] = m_irq_levels[INT_VECTOR_SCI_RXI1] = m_irq_levels[INT_VECTOR_SCI_TXI1] = m_irq_levels[INT_VECTOR_SCI_TEI1] = BIT(m_iprf, 0, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::iprg_r() +{ + return m_iprg & 0xf0ff; +} + +void sh7014_intc_device::iprg_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_iprg); + + m_irq_levels[INT_VECTOR_AD] = BIT(m_iprg, 12, 4); + m_irq_levels[INT_VECTOR_CMT_CH0] = BIT(m_iprg, 4, 4); + m_irq_levels[INT_VECTOR_CMT_CH1] = BIT(m_iprg, 0, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::iprh_r() +{ + return m_iprh & 0xf000; +} + +void sh7014_intc_device::iprh_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_iprh); + + m_irq_levels[INT_VECTOR_WDT] = m_irq_levels[INT_VECTOR_BSC] = BIT(m_iprh, 12, 4); + + update_irq_state(); +} + +uint16_t sh7014_intc_device::icr_r() +{ + uint16_t r = m_icr & 0x01f3; + + if (m_nmi_input == ((m_icr & ICR_NMIE) ? ASSERT_LINE : CLEAR_LINE)) + r |= ICR_NMIL; + + return r; +} + +void sh7014_intc_device::icr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_icr); + + for (int i = 0; i < 8; i++) + m_irq_type[i] = BIT(m_icr, 7 - i); +} + +uint16_t sh7014_intc_device::isr_r() +{ + return m_isr & 0xf3; +} + +void sh7014_intc_device::isr_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + // ICR will be set to 1 for edge detection, and it's only possible to + // clear flags in ISR for IRQs set to edge detection + auto old = m_isr; + + COMBINE_DATA(&m_isr); + + m_isr = (old & ~m_icr) | (old & m_isr & m_icr); + + update_irq_state(); +} + +void sh7014_intc_device::set_interrupt(int vector, int state) +{ + if (vector != -1) { + if (state == ASSERT_LINE) + m_pending_irqs[vector >> 5] |= 1 << (vector & 31); + else if (state == CLEAR_LINE) + m_pending_irqs[vector >> 5] &= ~(1 << (vector & 31)); + + if (vector == INT_VECTOR_NMI) { + m_nmi_input = state == ASSERT_LINE; + } else if ((vector >= INT_VECTOR_IRQ0 && vector <= INT_VECTOR_IRQ3) || vector == INT_VECTOR_IRQ6 || vector == INT_VECTOR_IRQ7) { + const int irq = vector - INT_VECTOR_IRQ0; + + if (state == ASSERT_LINE) { + if (m_irq_type[irq] == IRQ_LEVEL) + m_isr |= 1 << (7 - irq); + } else if (state == CLEAR_LINE) { + if (m_irq_type[irq] == IRQ_LEVEL) + m_isr &= ~(1 << (7 - irq)); + } + } + } + + update_irq_state(); +} + +void sh7014_intc_device::update_irq_state() +{ + int cur_vector = 0; + int cur_level = -1; + + // isr has the bits in reverse order + m_pending_irqs[INT_VECTOR_IRQ0 >> 5] &= ~0xff; + m_pending_irqs[INT_VECTOR_IRQ0 >> 5] |= BIT(m_isr, 7) + | (BIT(m_isr, 6) << 1) + | (BIT(m_isr, 5) << 2) + | (BIT(m_isr, 4) << 3) + | (BIT(m_isr, 1) << 6) + | (BIT(m_isr, 0) << 7); + + for (int i = 0; i < MAX_VECTORS / 32; i++) { + if (!m_pending_irqs[i]) + continue; + + for (int j = 0; j < 32; j++) { + if (BIT(m_pending_irqs[i], j)) { + const int vector = i * 32 + j; + const int level = m_irq_levels[vector]; + + if (level > cur_level) { + cur_vector = vector; + cur_level = level; + } + } + } + } + + if (cur_vector == INT_VECTOR_NMI) + m_set_irq_cb(INPUT_LINE_NMI, cur_level, false); + else if ((cur_vector >= INT_VECTOR_IRQ0 && cur_vector <= INT_VECTOR_IRQ3) || cur_vector == INT_VECTOR_IRQ6 || cur_vector == INT_VECTOR_IRQ7) + m_set_irq_cb(cur_vector - INT_VECTOR_IRQ0, cur_level, false); + else if (cur_vector > INT_VECTOR_IRQ7) + m_set_irq_cb(cur_vector, cur_level, true); +} + +void sh7014_intc_device::set_input(int inputnum, int state) +{ + if (inputnum == INPUT_LINE_NMI) + set_interrupt(INT_VECTOR_NMI, state); + else if ((inputnum >= INPUT_LINE_IRQ0 && inputnum <= INPUT_LINE_IRQ3) || inputnum == INPUT_LINE_IRQ6 || inputnum == INPUT_LINE_IRQ7) + set_interrupt(INT_VECTOR_IRQ0 + (inputnum - INPUT_LINE_IRQ0), state); +} diff --git a/src/devices/cpu/sh/sh7014_intc.h b/src/devices/cpu/sh/sh7014_intc.h new file mode 100644 index 00000000000..d8d71f6dbc3 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_intc.h @@ -0,0 +1,147 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Interrupt Controller + +***************************************************************************/ + +#ifndef MAME_CPU_SH_SH7014_INTC_H +#define MAME_CPU_SH_SH7014_INTC_H + +#pragma once + + +class sh7014_intc_device : public device_t +{ +public: + enum { + INT_VECTOR_NMI = 11, + + INT_VECTOR_IRQ0 = 64, + INT_VECTOR_IRQ1 = 65, + INT_VECTOR_IRQ2 = 66, + INT_VECTOR_IRQ3 = 67, + // 68 and 69 are reserved + INT_VECTOR_IRQ6 = 70, + INT_VECTOR_IRQ7 = 71, + + INT_VECTOR_DMA_CH0 = 72, + INT_VECTOR_DMA_CH1 = 76, + + INT_VECTOR_MTU_TGI0A = 88, + INT_VECTOR_MTU_TGI0B = 89, + INT_VECTOR_MTU_TGI0C = 90, + INT_VECTOR_MTU_TGI0D = 91, + INT_VECTOR_MTU_TGI0V = 92, + + INT_VECTOR_MTU_TGI1A = 96, + INT_VECTOR_MTU_TGI1B = 97, + INT_VECTOR_MTU_TGI1V = 100, + INT_VECTOR_MTU_TGI1U = 101, + + INT_VECTOR_MTU_TGI2A = 104, + INT_VECTOR_MTU_TGI2B = 105, + INT_VECTOR_MTU_TGI2V = 108, + INT_VECTOR_MTU_TGI2U = 109, + + INT_VECTOR_SCI_ERI0 = 128, + INT_VECTOR_SCI_RXI0 = 129, + INT_VECTOR_SCI_TXI0 = 130, + INT_VECTOR_SCI_TEI0 = 131, + + INT_VECTOR_SCI_ERI1 = 132, + INT_VECTOR_SCI_RXI1 = 133, + INT_VECTOR_SCI_TXI1 = 134, + INT_VECTOR_SCI_TEI1 = 135, + + INT_VECTOR_AD = 136, + + INT_VECTOR_CMT_CH0 = 144, + INT_VECTOR_CMT_CH1 = 148, + + INT_VECTOR_WDT = 152, + + INT_VECTOR_BSC = 153, + }; + + typedef device_delegate<void (int vector, int level, bool is_internal)> set_irq_delegate; + + sh7014_intc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + template <typename... T> void set_irq_callback(T &&... args) { m_set_irq_cb.set(std::forward<T>(args)...); } + + void map(address_map &map); + + void set_input(int inputnum, int state); + + void set_interrupt(int vector, int state); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + enum { + IRQ_LEVEL, + IRQ_EDGE, + }; + + enum { + MAX_VECTORS = 256, + }; + + enum { + ICR_NMIE = 1 << 8, + ICR_NMIL = 1 << 15, + }; + + uint16_t ipra_r(); + void ipra_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t iprb_r(); + void iprb_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t iprc_r(); + void iprc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t iprd_r(); + void iprd_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t ipre_r(); + void ipre_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t iprf_r(); + void iprf_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t iprg_r(); + void iprg_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t iprh_r(); + void iprh_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t icr_r(); + void icr_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + uint16_t isr_r(); + void isr_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + void update_irq_state(); + + set_irq_delegate m_set_irq_cb; + + uint16_t m_ipra, m_iprb, m_iprc, m_iprd, m_ipre, m_iprf, m_iprg, m_iprh; + uint16_t m_icr, m_isr; + + int8_t m_irq_levels[MAX_VECTORS]; + + uint32_t m_irq_type[8]; + bool m_nmi_input; + + uint32_t m_pending_irqs[MAX_VECTORS/32]; +}; + + +DECLARE_DEVICE_TYPE(SH7014_INTC, sh7014_intc_device) + +#endif // MAME_CPU_SH_SH7014_INTC_H diff --git a/src/devices/cpu/sh/sh7014_mtu.cpp b/src/devices/cpu/sh/sh7014_mtu.cpp new file mode 100644 index 00000000000..1f1f2ddf879 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_mtu.cpp @@ -0,0 +1,571 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Multifunction Timer Pulse Unit + + TODO list (not comprehensive): + - Synchronized operation + - Cascade connection operation + - External clocks + - Timer modes (PWM mode, phase counting mode (+ decrementing counter mode), etc) + +***************************************************************************/ + +#include "emu.h" + +#include "sh7014_mtu.h" + +// #define VERBOSE (LOG_GENERAL) + +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(SH7014_MTU, sh7014_mtu_device, "sh7014mtu", "SH7014 Multifunction Timer Pulse Unit") +DEFINE_DEVICE_TYPE(SH7014_MTU_CHANNEL, sh7014_mtu_channel_device, "sh7014mtuchan", "SH7014 Multifunction Timer Pulse Unit Channel") + +sh7014_mtu_device::sh7014_mtu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_MTU, tag, owner, clock) + , m_intc(*this, finder_base::DUMMY_TAG) + , m_chan(*this, "ch%u", 0u) +{ +} + +void sh7014_mtu_device::device_start() +{ + save_item(NAME(m_tsyr)); +} + +void sh7014_mtu_device::device_reset() +{ + m_tsyr = 0; +} + +void sh7014_mtu_device::device_add_mconfig(machine_config &config) +{ + SH7014_MTU_CHANNEL(config, m_chan[0], DERIVED_CLOCK(1, 1), m_intc, + 0, // channel + sh7014_intc_device::INT_VECTOR_MTU_TGI0A, + sh7014_intc_device::INT_VECTOR_MTU_TGI0B, + sh7014_intc_device::INT_VECTOR_MTU_TGI0C, + sh7014_intc_device::INT_VECTOR_MTU_TGI0D, + sh7014_intc_device::INT_VECTOR_MTU_TGI0V, + -1 + ); + + SH7014_MTU_CHANNEL(config, m_chan[1], DERIVED_CLOCK(1, 1), m_intc, + 1, // channel + sh7014_intc_device::INT_VECTOR_MTU_TGI1A, + sh7014_intc_device::INT_VECTOR_MTU_TGI1B, + -1, + -1, + sh7014_intc_device::INT_VECTOR_MTU_TGI1V, + sh7014_intc_device::INT_VECTOR_MTU_TGI1U + ); + + SH7014_MTU_CHANNEL(config, m_chan[2], DERIVED_CLOCK(1, 1), m_intc, + 2, // channel + sh7014_intc_device::INT_VECTOR_MTU_TGI2A, + sh7014_intc_device::INT_VECTOR_MTU_TGI2B, + -1, + -1, + sh7014_intc_device::INT_VECTOR_MTU_TGI2V, + sh7014_intc_device::INT_VECTOR_MTU_TGI2U + ); +} + +void sh7014_mtu_device::map(address_map &map) +{ + map(0x00, 0x00).rw(FUNC(sh7014_mtu_device::tstr_r), FUNC(sh7014_mtu_device::tstr_w)); + map(0x01, 0x01).rw(FUNC(sh7014_mtu_device::tsyr_r), FUNC(sh7014_mtu_device::tsyr_w)); + + map(0x20, 0x3f).m(m_chan[0], FUNC(sh7014_mtu_channel_device::map_chan0)); + map(0x40, 0x5f).m(m_chan[1], FUNC(sh7014_mtu_channel_device::map_chan1_2)); + map(0x60, 0x7f).m(m_chan[2], FUNC(sh7014_mtu_channel_device::map_chan1_2)); +} + +/// + +uint8_t sh7014_mtu_device::tstr_r() +{ + return m_chan[0]->is_enabled() + | (m_chan[1]->is_enabled() << 1) + | (m_chan[2]->is_enabled() << 2); +} + +void sh7014_mtu_device::tstr_w(uint8_t data) +{ + LOG("%s: tstr_w %02x\n", machine().describe_context().c_str(), data); + + m_chan[0]->set_enable((data & TSTR_CST0) != 0); + m_chan[1]->set_enable((data & TSTR_CST1) != 0); + m_chan[2]->set_enable((data & TSTR_CST2) != 0); +} + +uint8_t sh7014_mtu_device::tsyr_r() +{ + return m_tsyr & (TSYR_SYNC0 | TSYR_SYNC1 | TSYR_SYNC2); +} + +void sh7014_mtu_device::tsyr_w(uint8_t data) +{ + LOG("%s: tsyr_w %02x\n", machine().describe_context().c_str(), data); + + m_tsyr = data; +} + + +////////////////// + +sh7014_mtu_channel_device::sh7014_mtu_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_MTU_CHANNEL, tag, owner, clock) + , m_intc(*this, finder_base::DUMMY_TAG) + , m_timer(nullptr) +{ +} + +void sh7014_mtu_channel_device::device_start() +{ + save_item(NAME(m_tcr)); + save_item(NAME(m_tmdr)); + save_item(NAME(m_tiorh)); + save_item(NAME(m_tiorl)); + save_item(NAME(m_tier)); + save_item(NAME(m_tsr)); + save_item(NAME(m_tcnt)); + save_item(NAME(m_tgr)); + + save_item(NAME(m_last_clock_update)); + save_item(NAME(m_clock_type)); + save_item(NAME(m_clock_divider)); + save_item(NAME(m_channel_active)); + save_item(NAME(m_phase)); + save_item(NAME(m_counter_cycle)); + save_item(NAME(m_tgr_clearing)); + + m_timer = timer_alloc(FUNC(sh7014_mtu_channel_device::timer_callback), this); +} + +void sh7014_mtu_channel_device::device_reset() +{ + m_tcr = 0; + m_tmdr = 0; + m_tier = 0; + m_tsr = TSR_TCFD; + m_tiorh = m_tiorl = 0; + std::fill(std::begin(m_tgr), std::end(m_tgr), 0); + + m_tcnt = 0; + m_last_clock_update = 0; + m_clock_type = INPUT_INTERNAL; + m_clock_divider = DIV_1; + m_channel_active = false; + m_phase = 0; + m_counter_cycle = 0x10000; + m_tgr_clearing = TGR_CLEAR_NONE; + + m_timer->adjust(attotime::never); +} + +void sh7014_mtu_channel_device::map_chan0(address_map &map) +{ + map(0x00, 0x00).rw(FUNC(sh7014_mtu_channel_device::tcr_r), FUNC(sh7014_mtu_channel_device::tcr_w)); + map(0x01, 0x01).rw(FUNC(sh7014_mtu_channel_device::tmdr_r), FUNC(sh7014_mtu_channel_device::tmdr_w)); + map(0x02, 0x02).rw(FUNC(sh7014_mtu_channel_device::tiorh_r), FUNC(sh7014_mtu_channel_device::tiorh_w)); + map(0x03, 0x03).rw(FUNC(sh7014_mtu_channel_device::tiorl_r), FUNC(sh7014_mtu_channel_device::tiorl_w)); + map(0x04, 0x04).rw(FUNC(sh7014_mtu_channel_device::tier_r), FUNC(sh7014_mtu_channel_device::tier_w)); + map(0x05, 0x05).rw(FUNC(sh7014_mtu_channel_device::tsr_r), FUNC(sh7014_mtu_channel_device::tsr_w)); + map(0x06, 0x07).rw(FUNC(sh7014_mtu_channel_device::tcnt_r), FUNC(sh7014_mtu_channel_device::tcnt_w)); + map(0x08, 0x09).rw(FUNC(sh7014_mtu_channel_device::tgra_r), FUNC(sh7014_mtu_channel_device::tgra_w)); + map(0x0a, 0x0b).rw(FUNC(sh7014_mtu_channel_device::tgrb_r), FUNC(sh7014_mtu_channel_device::tgrb_w)); + map(0x0c, 0x0d).rw(FUNC(sh7014_mtu_channel_device::tgrc_r), FUNC(sh7014_mtu_channel_device::tgrc_w)); + map(0x0e, 0x0f).rw(FUNC(sh7014_mtu_channel_device::tgrd_r), FUNC(sh7014_mtu_channel_device::tgrd_w)); +} + +void sh7014_mtu_channel_device::map_chan1_2(address_map &map) +{ + map(0x00, 0x00).rw(FUNC(sh7014_mtu_channel_device::tcr_r), FUNC(sh7014_mtu_channel_device::tcr_w)); + map(0x01, 0x01).rw(FUNC(sh7014_mtu_channel_device::tmdr_r), FUNC(sh7014_mtu_channel_device::tmdr_w)); + map(0x02, 0x02).rw(FUNC(sh7014_mtu_channel_device::tiorh_r), FUNC(sh7014_mtu_channel_device::tiorh_w)); + map(0x04, 0x04).rw(FUNC(sh7014_mtu_channel_device::tier_r), FUNC(sh7014_mtu_channel_device::tier_w)); + map(0x05, 0x05).rw(FUNC(sh7014_mtu_channel_device::tsr_r), FUNC(sh7014_mtu_channel_device::tsr_w)); + map(0x06, 0x07).rw(FUNC(sh7014_mtu_channel_device::tcnt_r), FUNC(sh7014_mtu_channel_device::tcnt_w)); + map(0x08, 0x09).rw(FUNC(sh7014_mtu_channel_device::tgra_r), FUNC(sh7014_mtu_channel_device::tgra_w)); + map(0x0a, 0x0b).rw(FUNC(sh7014_mtu_channel_device::tgrb_r), FUNC(sh7014_mtu_channel_device::tgrb_w)); +} + +/// + +TIMER_CALLBACK_MEMBER( sh7014_mtu_channel_device::timer_callback ) +{ + update_counter(); + schedule_next_event(); +} + +void sh7014_mtu_channel_device::set_enable(bool enabled) +{ + update_counter(); + + m_channel_active = enabled; + + schedule_next_event(); +} + +uint8_t sh7014_mtu_channel_device::tcr_r() +{ + uint8_t r = m_tcr; + + if (m_channel_id > 0) + r &= 0x7f; + + return r; +} + +void sh7014_mtu_channel_device::tcr_w(uint8_t data) +{ + LOG("%s: tcr_w<%d> %02x\n", machine().describe_context().c_str(), m_channel_id, data); + + update_counter(); + + m_tcr = data; + + constexpr uint32_t divider_type[3][8] = { + {INPUT_INTERNAL, INPUT_INTERNAL, INPUT_INTERNAL, INPUT_INTERNAL, INPUT_A, INPUT_B, INPUT_C, INPUT_D}, + {INPUT_INTERNAL, INPUT_INTERNAL, INPUT_INTERNAL, INPUT_INTERNAL, INPUT_A, INPUT_B, INPUT_INTERNAL, INPUT_TCNT2}, + {INPUT_INTERNAL, INPUT_INTERNAL, INPUT_INTERNAL, INPUT_INTERNAL, INPUT_A, INPUT_B, INPUT_C, INPUT_INTERNAL}, + }; + + const int prescaler = BIT(m_tcr, 0, 3); + + m_clock_type = divider_type[m_channel_id][prescaler]; + + if (m_clock_type == INPUT_INTERNAL) { + constexpr int32_t dividers[3][8] = { + {DIV_1, DIV_4, DIV_16, DIV_64, -1, -1, -1, -1}, + {DIV_1, DIV_4, DIV_16, DIV_64, -1, -1, DIV_256, -1}, + {DIV_1, DIV_4, DIV_16, DIV_64, -1, -1, -1, DIV_1024}, + }; + + m_clock_divider = dividers[m_channel_id][prescaler]; + } else { + m_clock_divider = DIV_1; + } + + const int clock_edge = BIT(m_tcr, 3, 2); + if (m_clock_divider < DIV_4) { + m_phase = 0; + } else { + switch (clock_edge) { + case 0: + m_phase = 0; + break; + case 1: + m_phase = 1 << (m_clock_divider - 1); + break; + case 2: + // If count on both rising and falling edges, input clock frequency becomes 1/2 + m_phase = 0; + m_clock_divider--; + break; + } + } + + const int counting_clear = BIT(m_tcr, 5, m_channel_id == 0 ? 3 : 2); + if (counting_clear == 3 || (m_channel_id == 0 && counting_clear == 7)) + m_tgr_clearing = TGR_CLEAR_SYNC; + else if (counting_clear >= 1 && counting_clear <= 2) + m_tgr_clearing = counting_clear - 1; // TGRA, TGRB + else if (m_channel_id == 0 && counting_clear >= 5 && counting_clear <= 6) + m_tgr_clearing = counting_clear - 3; // TGRC, TGRD (ch 0 only) + else + m_tgr_clearing = TGR_CLEAR_NONE; // 0, 4, and anything else + + schedule_next_event(); +} + +uint8_t sh7014_mtu_channel_device::tmdr_r() +{ + uint8_t r = m_tmdr; + + if (m_channel_id > 0) + r &= 0x0f; + + return r | 0xc0; +} + +void sh7014_mtu_channel_device::tmdr_w(uint8_t data) +{ + LOG("%s: tmdr_w<%d> %02x\n", machine().describe_context().c_str(), m_channel_id, data); + + m_tmdr = data; +} + +uint8_t sh7014_mtu_channel_device::tiorh_r() +{ + return m_tiorh; +} + +void sh7014_mtu_channel_device::tiorh_w(uint8_t data) +{ + LOG("%s: tiorh_w<%d> %02x\n", machine().describe_context().c_str(), m_channel_id, data); + + const bool trga_is_output_compare_register = BIT(data, 3) == 0; + const bool trga_was_output_compare_register = BIT(m_tiorh, 3) == 0; + if (trga_is_output_compare_register) { + const auto tgra_mode = BIT(data, 2); + const auto old_tgra_mode = BIT(m_tiorh, 2); + + if ((tgra_mode != old_tgra_mode) || (trga_is_output_compare_register != trga_was_output_compare_register)) { + const auto initial_output = tgra_mode ? TSR_TGFA : 0; + m_tsr = (m_tsr & ~TSR_TGFA) | initial_output; + } + } + + const bool trgb_is_output_compare_register = BIT(data, 7) == 0; + const bool trgb_was_output_compare_register = BIT(m_tiorh, 7) == 0; + if (trgb_is_output_compare_register) { + const auto tgrb_mode = BIT(data, 6); + const auto old_tgrb_mode = BIT(m_tiorh, 6); + + if ((tgrb_mode != old_tgrb_mode) || (trgb_is_output_compare_register != trgb_was_output_compare_register)) { + const auto initial_output = tgrb_mode ? TSR_TGFB : 0; + m_tsr = (m_tsr & ~TSR_TGFB) | initial_output; + } + } + + m_tiorh = data; +} + +uint8_t sh7014_mtu_channel_device::tiorl_r() +{ + if (m_channel_id > 0) + return 0; + + return m_tiorl; +} + +void sh7014_mtu_channel_device::tiorl_w(uint8_t data) +{ + LOG("%s: tiorl_w<%d> %02x\n", machine().describe_context().c_str(), m_channel_id, data); + + if (m_channel_id > 0) + return; + + const bool trgc_is_output_compare_register = BIT(data, 3) == 0; + const bool trgc_was_output_compare_register = BIT(m_tiorl, 3) == 0; + if (trgc_is_output_compare_register) { + const auto tgrc_mode = BIT(data, 2); + const auto old_tgrc_mode = BIT(m_tiorl, 2); + + if ((tgrc_mode != old_tgrc_mode) || (trgc_is_output_compare_register != trgc_was_output_compare_register)) { + const auto initial_output = tgrc_mode ? TSR_TGFC : 0; + m_tsr = (m_tsr & ~TSR_TGFC) | initial_output; + } + } + + const bool trgd_is_output_compare_register = BIT(data, 7) == 0; + const bool trgd_was_output_compare_register = BIT(m_tiorl, 7) == 0; + if (trgd_is_output_compare_register) { + const auto tgrd_mode = BIT(data, 6); + const auto old_tgrd_mode = BIT(m_tiorl, 6); + + if ((tgrd_mode != old_tgrd_mode) || (trgd_is_output_compare_register != trgd_was_output_compare_register)) { + const auto initial_output = tgrd_mode ? TSR_TGFD : 0; + m_tsr = (m_tsr & ~TSR_TGFD) | initial_output; + } + } + + m_tiorl = data; +} + +uint8_t sh7014_mtu_channel_device::tier_r() +{ + return m_tier | 0x40; +} + +void sh7014_mtu_channel_device::tier_w(uint8_t data) +{ + LOG("%s: tier_w<%d> %02x\n", machine().describe_context().c_str(), m_channel_id, data); + + m_tier = data; +} + +uint8_t sh7014_mtu_channel_device::tsr_r() +{ + uint8_t r; + + if (m_channel_id == 0) + r = (m_tsr & (TSR_TGFA | TSR_TGFB | TSR_TGFC | TSR_TGFD | TSR_TCFV)) | TSR_TCFD; + else + r = m_tsr & (TSR_TGFA | TSR_TGFB | TSR_TCFV | TSR_TCFU | TSR_TCFD); + + return r | (1 << 6); +} + +void sh7014_mtu_channel_device::tsr_w(uint8_t data) +{ + LOG("%s: tsr_w<%d> %02x\n", machine().describe_context().c_str(), m_channel_id, data); + + if (m_channel_id == 0) { + const uint8_t mask = TSR_TGFA | TSR_TGFB | TSR_TGFC | TSR_TGFD | TSR_TCFV; + m_tsr = (data & ~mask) | (m_tsr & data & mask); + } else { + const uint8_t mask = TSR_TGFA | TSR_TGFB | TSR_TCFV | TSR_TCFU; + m_tsr = (data & ~mask) | (m_tsr & data & mask) | (m_tsr & TSR_TCFD); + } +} + +uint16_t sh7014_mtu_channel_device::tcnt_r() +{ + update_counter(); + return m_tcnt; +} + +void sh7014_mtu_channel_device::tcnt_w(uint16_t data) +{ + LOG("%s: tcnt_w<%d> %04x -> %04x\n", machine().describe_context().c_str(), m_channel_id, m_tcnt, data); + + m_tcnt = data; +} + +uint16_t sh7014_mtu_channel_device::tgra_r() +{ + return m_tgr[0]; +} + +void sh7014_mtu_channel_device::tgra_w(uint16_t data) +{ + LOG("%s: tgra_w<%d> %04x -> %04x\n", machine().describe_context().c_str(), m_channel_id, m_tgr[0], data); + + m_tgr[0] = data; +} + +uint16_t sh7014_mtu_channel_device::tgrb_r() +{ + return m_tgr[1]; +} + +void sh7014_mtu_channel_device::tgrb_w(uint16_t data) +{ + LOG("%s: tgrb_w<%d> %04x -> %04x\n", machine().describe_context().c_str(), m_channel_id, m_tgr[1], data); + + m_tgr[1] = data; +} + +uint16_t sh7014_mtu_channel_device::tgrc_r() +{ + return m_tgr[2]; +} + +void sh7014_mtu_channel_device::tgrc_w(uint16_t data) +{ + LOG("%s: tgrc_w<%d> %04x -> %04x\n", machine().describe_context().c_str(), m_channel_id, m_tgr[2], data); + + m_tgr[2] = data; +} + +uint16_t sh7014_mtu_channel_device::tgrd_r() +{ + return m_tgr[3]; +} + +void sh7014_mtu_channel_device::tgrd_w(uint16_t data) +{ + LOG("%s: tgrd_w<%d> %04x -> %04x\n", machine().describe_context().c_str(), m_channel_id, m_tgr[3], data); + + m_tgr[3] = data; +} + +void sh7014_mtu_channel_device::update_counter() +{ + if (m_clock_type != INPUT_INTERNAL) + return; + + uint64_t cur_time = machine().time().as_ticks(clock()); + + if (!m_channel_active) { + m_last_clock_update = cur_time; + return; + } + + if (m_last_clock_update == cur_time) + return; + + uint64_t base_time = (m_last_clock_update + m_phase) >> m_clock_divider; + uint64_t new_time = (cur_time + m_phase) >> m_clock_divider; + int tt = m_tcnt + (new_time - base_time); + m_tcnt = tt % m_counter_cycle; + + for (int i = 0; i < m_tgr_count; i++) { + if (!BIT(m_tsr, i) && BIT(m_tier, i) && tt >= m_tgr[i]) { + m_tsr |= 1 << i; + m_intc->set_interrupt(m_vectors[i], ASSERT_LINE); + } + } + + if (!(m_tsr & TSR_TCFV) && tt >= 0x10000) { + // Overflowed + m_tsr |= TSR_TCFV; + + if (m_tier & TIER_TCIEV) + m_intc->set_interrupt(m_vectors[VECTOR_TCIV], ASSERT_LINE); + } else if (m_channel_id == 0 && !(m_tsr & TSR_TCFU) && tt < 0) { + // Underflowed, only usable on channel 0 + m_tsr |= TSR_TCFU; + + if (m_tier & TIER_TCIEU) + m_intc->set_interrupt(m_vectors[VECTOR_TCIU], ASSERT_LINE); + } + + m_last_clock_update = cur_time; +} + +void sh7014_mtu_channel_device::schedule_next_event() +{ + m_timer->adjust(attotime::never); + + if (!m_channel_active || m_clock_type != INPUT_INTERNAL) { + return; + } + + uint32_t event_delay = 0xffffffff; + if (m_tgr_clearing >= 0 && m_tgr[m_tgr_clearing]) { + m_counter_cycle = m_tgr[m_tgr_clearing]; + } else { + m_counter_cycle = 0x10000; + + if (m_tier & TIER_TCIEV) { + // Try to schedule next event for when the overflow should happen if overflow interrupt is enabled + event_delay = m_counter_cycle - m_tcnt; + + if (event_delay == 0) + event_delay = m_counter_cycle; + } + } + + // If one of the comparison register interrupts is enabled then set the next event time to be when the next interrupt should happen + for (int i = 0; i < m_tgr_count; i++) { + if (BIT(m_tier, i)) { + uint32_t new_delay = 0xffffffff; + + if (m_tgr[i] > m_tcnt) { + if (m_tcnt >= m_counter_cycle || m_tgr[i] <= m_counter_cycle) + new_delay = m_tgr[i] - m_tcnt; + } else if (m_tgr[i] <= m_counter_cycle) { + if (m_tcnt < m_counter_cycle) + new_delay = (m_counter_cycle - m_tcnt) + m_tgr[i]; + else + new_delay = (0x10000 - m_tcnt) + m_tgr[i]; + } + + if (event_delay > new_delay) + event_delay = new_delay; + } + } + + if (event_delay != 0xffffffff) { + const uint32_t next_event = (((((1ULL << m_clock_divider) - m_phase) >> m_clock_divider) + event_delay - 1) << m_clock_divider) + m_phase; + m_timer->adjust(attotime::from_ticks(next_event, clock())); + + } +} diff --git a/src/devices/cpu/sh/sh7014_mtu.h b/src/devices/cpu/sh/sh7014_mtu.h new file mode 100644 index 00000000000..c9c9e784390 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_mtu.h @@ -0,0 +1,211 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Multifunction Timer Pulse Unit + +***************************************************************************/ + +#ifndef MAME_CPU_SH_SH7014_MTU_H +#define MAME_CPU_SH_SH7014_MTU_H + +#pragma once + +#include "sh7014_intc.h" + +DECLARE_DEVICE_TYPE(SH7014_MTU, sh7014_mtu_device) +DECLARE_DEVICE_TYPE(SH7014_MTU_CHANNEL, sh7014_mtu_channel_device) + + +class sh7014_mtu_device : public device_t +{ +public: + sh7014_mtu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template<typename T> sh7014_mtu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&intc) + : sh7014_mtu_device(mconfig, tag, owner, clock) + { + m_intc.set_tag(std::forward<T>(intc)); + } + + void map(address_map &map); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + +private: + enum { + TSTR_CST0 = 1 << 0, + TSTR_CST1 = 1 << 1, + TSTR_CST2 = 1 << 2, + + TSYR_SYNC0 = 1 << 0, + TSYR_SYNC1 = 1 << 1, + TSYR_SYNC2 = 1 << 2, + }; + + uint8_t tstr_r(); + void tstr_w(uint8_t data); + + uint8_t tsyr_r(); + void tsyr_w(uint8_t data); + + required_device<sh7014_intc_device> m_intc; + required_device_array<sh7014_mtu_channel_device, 3> m_chan; + + uint8_t m_tsyr; +}; + + +class sh7014_mtu_channel_device : public device_t +{ +public: + sh7014_mtu_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template<typename T> sh7014_mtu_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&intc, int chan_id, int32_t vector_tgia, int32_t vector_tgib, int32_t vector_tgic, int32_t vector_tgid, int32_t vector_tgiv, int32_t vector_tgiu) + : sh7014_mtu_channel_device(mconfig, tag, owner, clock) + { + m_intc.set_tag(std::forward<T>(intc)); + m_channel_id = chan_id; + m_vectors[VECTOR_TGIA] = vector_tgia; + m_vectors[VECTOR_TGIB] = vector_tgib; + m_vectors[VECTOR_TGIC] = vector_tgic; + m_vectors[VECTOR_TGID] = vector_tgid; + m_vectors[VECTOR_TCIV] = vector_tgiv; + m_vectors[VECTOR_TCIU] = vector_tgiu; + m_tgr_count = chan_id == 0 ? 4 : 2; + } + + void map_chan0(address_map &map); + void map_chan1_2(address_map &map); + + void set_enable(bool enabled); + bool is_enabled() { return m_channel_active; } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + enum { + INPUT_INTERNAL = 0, + INPUT_A, + INPUT_B, + INPUT_C, + INPUT_D, + INPUT_TCNT2 + }; + + enum { + DIV_1 = 0, + DIV_2, + DIV_4, + DIV_8, + DIV_16, + DIV_32, + DIV_64, + DIV_128, + DIV_256, + DIV_512, + DIV_1024 + }; + + enum { + TGR_CLEAR_NONE = -1, + TGR_CLEAR_SYNC = -2 + }; + + enum { + VECTOR_TGIA = 0, + VECTOR_TGIB = 1, + VECTOR_TGIC = 2, + VECTOR_TGID = 3, + VECTOR_TCIV = 4, + VECTOR_TCIU = 5 + }; + + enum { + + TSR_TGFA = 1 << 0, + TSR_TGFB = 1 << 1, + TSR_TGFC = 1 << 2, // ch 0 only + TSR_TGFD = 1 << 3, // ch 0 only + TSR_TCFV = 1 << 4, + TSR_TCFU = 1 << 5, // ch 1/2 only + TSR_TCFD = 1 << 7, // ch 1/2 only + + TIER_TGIEA = 1 << 0, + TIER_TGIEB = 1 << 1, + TIER_TGIEC = 1 << 2, // ch 0 only + TIER_TGIED = 1 << 3, // ch 0 only + TIER_TCIEV = 1 << 4, + TIER_TCIEU = 1 << 5, // ch 1/2 only + TIER_TTGE = 1 << 7, + }; + + uint8_t tcr_r(); + void tcr_w(uint8_t data); + + uint8_t tmdr_r(); + void tmdr_w(uint8_t data); + + uint8_t tiorh_r(); + void tiorh_w(uint8_t data); + + uint8_t tiorl_r(); + void tiorl_w(uint8_t data); + + uint8_t tier_r(); + void tier_w(uint8_t data); + + uint8_t tsr_r(); + void tsr_w(uint8_t data); + + uint16_t tcnt_r(); + void tcnt_w(uint16_t data); + + uint16_t tgra_r(); + void tgra_w(uint16_t data); + + uint16_t tgrb_r(); + void tgrb_w(uint16_t data); + + uint16_t tgrc_r(); + void tgrc_w(uint16_t data); + + uint16_t tgrd_r(); + void tgrd_w(uint16_t data); + + void update_counter(); + void schedule_next_event(); + + TIMER_CALLBACK_MEMBER( timer_callback ); + + required_device<sh7014_intc_device> m_intc; + + emu_timer *m_timer; + + uint32_t m_channel_id; + int32_t m_vectors[6]; + uint32_t m_tgr_count; + + uint8_t m_tcr; + uint8_t m_tmdr; + uint8_t m_tiorh, m_tiorl; + uint8_t m_tier; + uint8_t m_tsr; + uint16_t m_tgr[4]; + uint16_t m_tcnt; + + uint64_t m_last_clock_update; + uint32_t m_clock_type, m_clock_divider; + uint32_t m_phase; + uint32_t m_counter_cycle; + int32_t m_tgr_clearing; + + bool m_channel_active; +}; + +#endif // MAME_CPU_SH_SH7014_MTU_H diff --git a/src/devices/cpu/sh/sh7014_port.cpp b/src/devices/cpu/sh/sh7014_port.cpp new file mode 100644 index 00000000000..96ed0701648 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_port.cpp @@ -0,0 +1,243 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 I/O Port + + TODO list (not comprehensive): + - Special functions specified by control registers are not implemented + +***************************************************************************/ + +#include "emu.h" +#include "sh7014_port.h" + +#define LOG_READ (1U << 1) +#define LOG_WRITE (1U << 2) + +// #define VERBOSE (LOG_GENERAL | LOG_READ | LOG_WRITE) + +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(SH7014_PORT, sh7014_port_device, "sh7014port", "SH7014 I/O Port") + + +sh7014_port_device::sh7014_port_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_PORT, tag, owner, clock) + , m_port_a_read_cb(*this, 0) + , m_port_a_write_cb(*this) + , m_port_b_read_cb(*this, 0) + , m_port_b_write_cb(*this) + , m_port_e_read_cb(*this, 0) + , m_port_e_write_cb(*this) + , m_port_f_read_cb(*this, 0) +{ +} + +void sh7014_port_device::device_start() +{ + save_item(NAME(m_padr)); + save_item(NAME(m_paior)); + save_item(NAME(m_pacr1)); + save_item(NAME(m_pacr2)); + + save_item(NAME(m_pbdr)); + save_item(NAME(m_pbior)); + save_item(NAME(m_pbcr1)); + save_item(NAME(m_pbcr2)); + + save_item(NAME(m_pedr)); + save_item(NAME(m_peior)); + save_item(NAME(m_pecr1)); + save_item(NAME(m_pecr2)); + + save_item(NAME(m_pfdr)); +} + +void sh7014_port_device::device_reset() +{ + m_padr = 0; + m_paior = 0; + m_pacr1 = m_pacr2 = 0; + + m_pbdr = 0; + m_pbior = 0; + m_pbcr1 = m_pbcr2 = 0; + + m_pedr = 0; + m_peior = 0; + m_pecr1 = m_pecr2 = 0; + + m_pfdr = 0; +} + +/// + +uint16_t sh7014_port_device::padrl_r() +{ + uint16_t pins = m_port_a_read_cb(); + uint16_t r = ((m_padr & m_paior) | (pins & ~m_paior)) & 0x83ff; + LOGMASKED(LOG_READ, "padrl_r %04x (pins: %04x %04x)\n", r, m_paior, pins); + return r; +} + +void sh7014_port_device::padrl_w(uint16_t data) +{ + m_padr = data & 0x83ff; + LOGMASKED(LOG_WRITE, "padrl_w %04x\n", m_padr); + + if (m_paior) + m_port_a_write_cb(m_padr & m_paior); +} + +uint16_t sh7014_port_device::paiorl_r() +{ + LOGMASKED(LOG_READ, "paiorl_r %04x\n", m_paior); + return m_paior; +} + +void sh7014_port_device::paiorl_w(uint16_t data) +{ + m_paior = data & 0x83ff; + LOGMASKED(LOG_WRITE, "paiorl_w %04x\n", m_paior); +} + +uint16_t sh7014_port_device::pacrl1_r() +{ + LOGMASKED(LOG_READ, "m_pacr1 %04x\n", m_pacr1); + return m_pacr1; +} + +void sh7014_port_device::pacrl1_w(uint16_t data) +{ + m_pacr1 = data & 0x400f; + LOGMASKED(LOG_WRITE, "pacrl1_w %04x\n", m_pacr1); +} + +uint16_t sh7014_port_device::pacrl2_r() +{ + LOGMASKED(LOG_READ, "pacrl2_r %04x\n", m_pacr2); + return m_pacr2; +} + +void sh7014_port_device::pacrl2_w(uint16_t data) +{ + m_pacr2 = data & 0xfd75; + LOGMASKED(LOG_WRITE, "pacrl2_w %04x\n", m_pacr2); +} + +uint16_t sh7014_port_device::pbdr_r() +{ + uint16_t pins = m_port_b_read_cb(); + uint16_t r = ((m_pbdr & m_pbior) | (pins & ~m_pbior)) & 0x3ff; + LOGMASKED(LOG_READ, "pbdr_r %04x (pins: %04x %04x)\n", r, m_pbior, pins); + return r; +} + +void sh7014_port_device::pbdr_w(uint16_t data) +{ + m_pbdr = data; + LOGMASKED(LOG_WRITE, "pbdr_w %04x\n", m_pbdr); + + if (m_pbior) + m_port_b_write_cb(m_pbdr & m_pbior); +} + +uint16_t sh7014_port_device::pbior_r() +{ + LOGMASKED(LOG_READ, "pbior_r %04x\n", m_pbior); + return m_pbior; +} + +void sh7014_port_device::pbior_w(uint16_t data) +{ + // For SH7014 specifically, bits 0 and 1 are read only + m_pbior = data & 0x3fc; + LOGMASKED(LOG_WRITE, "pbior_w %04x\n", m_pbior); +} + +uint16_t sh7014_port_device::pbcr1_r() +{ + LOGMASKED(LOG_READ, "pbcr1_r %04x\n", m_pbcr1); + return m_pbcr1; +} + +void sh7014_port_device::pbcr1_w(uint16_t data) +{ + m_pbcr1 = data & 0xf; + LOGMASKED(LOG_WRITE, "pbcr1_w %04x\n", m_pbcr1); +} + +uint16_t sh7014_port_device::pbcr2_r() +{ + LOGMASKED(LOG_READ, "pbcr2_r %04x\n", m_pbcr2); + return m_pbcr2; +} + +void sh7014_port_device::pbcr2_w(uint16_t data) +{ + m_pbcr2 = data & 0xfff5; + LOGMASKED(LOG_WRITE, "pbcr2_w %04x\n", m_pbcr2); +} + +uint16_t sh7014_port_device::pedr_r() +{ + uint16_t pins = m_port_e_read_cb(); + uint16_t r = ((m_pedr & m_peior) | (pins & ~m_peior)); + LOGMASKED(LOG_READ, "pedr_r %04x (pins: %04x %04x)\n", r, m_peior, pins); + return r; +} + +void sh7014_port_device::pedr_w(uint16_t data) +{ + m_pedr = data; + LOGMASKED(LOG_WRITE, "pedr_w %04x\n", m_pedr); + + if (m_peior) + m_port_e_write_cb(m_pedr & m_peior); +} + +uint16_t sh7014_port_device::peior_r() +{ + LOGMASKED(LOG_READ, "peior_r %04x\n", m_peior); + return m_peior; +} + +void sh7014_port_device::peior_w(uint16_t data) +{ + m_peior = data; + LOGMASKED(LOG_WRITE, "peior_w %04x\n", m_peior); +} + +uint16_t sh7014_port_device::pecr1_r() +{ + LOGMASKED(LOG_READ, "pecr1_r %04x\n", m_pecr1); + return m_pecr1; +} + +void sh7014_port_device::pecr1_w(uint16_t data) +{ + m_pecr1 = data & 0xf000; + LOGMASKED(LOG_WRITE, "pecr1_w %04x\n", m_pecr1); +} + +uint16_t sh7014_port_device::pecr2_r() +{ + LOGMASKED(LOG_READ, "pecr2_r %04x\n", m_pecr2); + return m_pecr2; +} + +void sh7014_port_device::pecr2_w(uint16_t data) +{ + m_pecr2 = data & 0x55ff; + LOGMASKED(LOG_WRITE, "pecr2_w %04x\n", m_pecr2); +} + + +uint8_t sh7014_port_device::pfdr_r() +{ + auto r = m_port_f_read_cb(); + LOGMASKED(LOG_READ, "pfdr_r %02x\n", r); + return r; +} diff --git a/src/devices/cpu/sh/sh7014_port.h b/src/devices/cpu/sh/sh7014_port.h new file mode 100644 index 00000000000..675812ebbaa --- /dev/null +++ b/src/devices/cpu/sh/sh7014_port.h @@ -0,0 +1,98 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 Bus State Controller + +***************************************************************************/ + +#ifndef MAME_CPU_SH_SH7014_PORT_H +#define MAME_CPU_SH_SH7014_PORT_H + +#pragma once + + +class sh7014_port_device : public device_t +{ +public: + sh7014_port_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + auto port_a_read_callback() { return m_port_a_read_cb.bind(); } + auto port_a_write_callback() { return m_port_a_write_cb.bind(); } + + auto port_b_read_callback() { return m_port_b_read_cb.bind(); } + auto port_b_write_callback() { return m_port_b_write_cb.bind(); } + + auto port_e_read_callback() { return m_port_e_read_cb.bind(); } + auto port_e_write_callback() { return m_port_e_write_cb.bind(); } + + auto port_f_read_callback() { return m_port_f_read_cb.bind(); } + + // port A + uint16_t padrl_r(); + void padrl_w(uint16_t data); + + uint16_t paiorl_r(); + void paiorl_w(uint16_t data); + + uint16_t pacrl1_r(); + void pacrl1_w(uint16_t data); + + uint16_t pacrl2_r(); + void pacrl2_w(uint16_t data); + + // port b + uint16_t pbdr_r(); + void pbdr_w(uint16_t data); + + uint16_t pbior_r(); + void pbior_w(uint16_t data); + + uint16_t pbcr1_r(); + void pbcr1_w(uint16_t data); + + uint16_t pbcr2_r(); + void pbcr2_w(uint16_t data); + + // port e + uint16_t pedr_r(); + void pedr_w(uint16_t data); + + uint16_t peior_r(); + void peior_w(uint16_t data); + + uint16_t pecr1_r(); + void pecr1_w(uint16_t data); + + uint16_t pecr2_r(); + void pecr2_w(uint16_t data); + + // port f + uint8_t pfdr_r(); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + devcb_read16 m_port_a_read_cb; + devcb_write16 m_port_a_write_cb; + + devcb_read16 m_port_b_read_cb; + devcb_write16 m_port_b_write_cb; + + devcb_read16 m_port_e_read_cb; + devcb_write16 m_port_e_write_cb; + + devcb_read16 m_port_f_read_cb; + + uint16_t m_padr, m_paior, m_pacr1, m_pacr2; + uint16_t m_pbdr, m_pbior, m_pbcr1, m_pbcr2; + uint16_t m_pedr, m_peior, m_pecr1, m_pecr2; + uint8_t m_pfdr; +}; + + +DECLARE_DEVICE_TYPE(SH7014_PORT, sh7014_port_device) + +#endif // MAME_CPU_SH_SH7014_PORT_H diff --git a/src/devices/cpu/sh/sh7014_sci.cpp b/src/devices/cpu/sh/sh7014_sci.cpp new file mode 100644 index 00000000000..ffa19247382 --- /dev/null +++ b/src/devices/cpu/sh/sh7014_sci.cpp @@ -0,0 +1,380 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 SCI Controller + + TODO list (not comprehensive): + - RX is untested + - Multiprocessor bit is not handled at all + +***************************************************************************/ + +#include "emu.h" +#include "sh7014_sci.h" + +#define LOG_REGISTERS (1U << 1) +#define LOG_TXRX (1U << 2) +#define LOG_CLOCK (1U << 3) + +// #define VERBOSE (LOG_GENERAL | LOG_REGISTERS | LOG_TXRX | LOG_CLOCK) + +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(SH7014_SCI, sh7014_sci_device, "sh7014sci", "SH7014 SCI Controller") + +sh7014_sci_device::sh7014_sci_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SH7014_SCI, tag, owner, clock) + , device_serial_interface(mconfig, *this) + , m_intc(*this, finder_base::DUMMY_TAG) + , m_sci_tx_cb(*this) + , m_hack_set_full_data_transmit_on_sync(false) +{ + m_external_clock_period = attotime::never; +} + +void sh7014_sci_device::device_start() +{ + save_item(NAME(m_smr)); + save_item(NAME(m_brr)); + save_item(NAME(m_scr)); + save_item(NAME(m_tdr)); + save_item(NAME(m_ssr)); + save_item(NAME(m_rdr)); + save_item(NAME(m_is_dma_source_tx)); + save_item(NAME(m_is_dma_source_rx)); + save_item(NAME(m_clock_speed)); + save_item(NAME(m_external_clock_period)); +} + +void sh7014_sci_device::device_reset() +{ + m_smr = 0; + m_brr = 0xff; + m_scr = 0; + m_tdr = 0xff; + m_ssr = SSR_TDRE | SSR_TEND; + m_rdr = 0; + m_is_dma_source_tx = m_is_dma_source_rx = false; + m_clock_speed = attotime::never; + + update_data_format(); + update_clock(); +} + +void sh7014_sci_device::map(address_map &map) +{ + map(0x00, 0x00).rw(FUNC(sh7014_sci_device::smr_r), FUNC(sh7014_sci_device::smr_w)); + map(0x01, 0x01).rw(FUNC(sh7014_sci_device::brr_r), FUNC(sh7014_sci_device::brr_w)); + map(0x02, 0x02).rw(FUNC(sh7014_sci_device::scr_r), FUNC(sh7014_sci_device::scr_w)); + map(0x03, 0x03).rw(FUNC(sh7014_sci_device::tdr_r), FUNC(sh7014_sci_device::tdr_w)); + map(0x04, 0x04).rw(FUNC(sh7014_sci_device::ssr_r), FUNC(sh7014_sci_device::ssr_w)); + map(0x05, 0x05).r(FUNC(sh7014_sci_device::rdr_r)); +} + +/// + +void sh7014_sci_device::set_send_full_data_transmit_on_sync_hack(bool enabled) +{ + // Synchronous clock mode forces a fixed 8-bit transmissions with no start, stop, parity, or multiprocessor bits. + // This flag makes it so that all 8 bits in the transmit register will be transferred at the start of a transmission + // instead of transmitting 1 bit at a time when in synchronous clock mode, allowing the transmit clock to be set to + // 1/8th of its normal speed. + m_hack_set_full_data_transmit_on_sync = enabled; +} + +void sh7014_sci_device::set_external_clock_period(const attotime &period) +{ + m_external_clock_period = period; + + // Update clock again if it's being used + if (m_scr & SCR_CKE1) + update_clock(); +} + +uint8_t sh7014_sci_device::smr_r() +{ + return m_smr; +} + +void sh7014_sci_device::smr_w(uint8_t data) +{ + LOGMASKED(LOG_REGISTERS, "smr_w %02x %s %c%c%c%s /%d\n", data, + data & SMR_CA ? "sync" : "async", + data & SMR_CHR ? '7' : '8', + data & SMR_PE ? data & SMR_OE ? 'o' : 'e' : 'n', + data & SMR_STOP ? '2' : '1', + data & SMR_MP ? " mp" : "", + 1 << (2 * (data & SMR_CKS))); + + bool do_clock_update = (data & (SMR_CKS | SMR_CA)) != (m_smr & (SMR_CKS | SMR_CA)); + bool do_format_update = false; + + if (!(data & SMR_CA)) + do_format_update = (data & ~SMR_CKS) != (m_smr & ~SMR_CKS); + else + do_format_update = (data & SMR_CA) != (m_smr & SMR_CA); + + m_smr = data; + + if (do_format_update) + update_data_format(); + + if (do_clock_update) + update_clock(); +} + +uint8_t sh7014_sci_device::scr_r() +{ + return m_scr; +} + +void sh7014_sci_device::scr_w(uint8_t data) +{ + const auto old = m_scr; + + LOGMASKED(LOG_REGISTERS, "scr_w %02x%s%s%s%s%s%s clk=%d\n", data, + (data & SCR_TIE) ? " txi" : "", + (data & SCR_RIE) ? " rxi" : "", + (data & SCR_TE) ? " tx" : "", + (data & SCR_RE) ? " rx" : "", + (data & SCR_MPIE) ? " mpi" : "", + (data & SCR_TEIE) ? " tei" : "", + data & SCR_CKE); + + if ((m_scr & SCR_TE) && !(data & SCR_TE)) + m_ssr |= SSR_TEND | SSR_TDRE; + + if ((m_scr & SCR_TIE) && !(data & SCR_TIE)) + m_intc->set_interrupt(m_txi_int, CLEAR_LINE); + + if ((m_scr & SCR_TEIE) && !(data & SCR_TEIE)) + m_intc->set_interrupt(m_tei_int, CLEAR_LINE); + + if ((m_scr & SCR_RIE) && !(data & SCR_RIE)) { + m_intc->set_interrupt(m_rxi_int, CLEAR_LINE); + m_intc->set_interrupt(m_eri_int, CLEAR_LINE); + } + + m_scr = data; + + if ((data & SCR_CKE) != (old & SCR_CKE)) + update_clock(); +} + +uint8_t sh7014_sci_device::ssr_r() +{ + LOGMASKED(LOG_REGISTERS, "ssr_r %02x\n", m_ssr); + return m_ssr; +} + +void sh7014_sci_device::ssr_w(uint8_t data) +{ + const auto old = m_ssr; + bool do_tx_update = false; + + m_ssr = (data & (m_ssr & (SSR_TDRE | SSR_RDRF | SSR_ORER | SSR_FER | SSR_PER))) + | (m_ssr & (SSR_TEND | SSR_MPB)) + | (data & SSR_MPBT); + + if (!(m_scr & SCR_TE)) { + m_ssr |= SSR_TEND | SSR_TDRE; + } else if ((old & SSR_TDRE) || !(m_scr & SSR_TDRE)) { + do_tx_update = true; + m_ssr &= ~(SSR_TEND | SSR_TDRE); + } + + if ((old & (SSR_ORER | SSR_FER | SSR_PER)) && !(data & (SSR_ORER | SSR_FER | SSR_PER))) + m_intc->set_interrupt(m_eri_int, CLEAR_LINE); + + LOGMASKED(LOG_REGISTERS, "ssr_w %02x | %02x -> %02x\n", data, old, m_ssr); + + if (do_tx_update) + update_tx_state(); +} + +uint8_t sh7014_sci_device::brr_r() +{ + return m_brr; +} + +void sh7014_sci_device::brr_w(uint8_t data) +{ + LOGMASKED(LOG_REGISTERS, "brr_w %02x\n", data); + m_brr = data; + update_clock(); +} + +uint8_t sh7014_sci_device::tdr_r() +{ + LOGMASKED(LOG_TXRX, "tdr_r %02x\n", m_tdr); + return m_tdr; +} + +void sh7014_sci_device::tdr_w(uint8_t data) +{ + LOGMASKED(LOG_TXRX, "tdr_w%s %02x %d %d\n", m_is_dma_source_tx ? " (dma)" : "", data, is_transmit_register_empty(), (m_scr & SCR_TE) != 0); + + m_tdr = data; + + if (!(m_scr & SCR_TE)) + return; + + if (m_is_dma_source_tx) { + // Normally this would happen with a write to SCR but DMAs can only write to TDR, + // so these flags are handled here as a special case for DMA writes only + m_ssr &= ~(SSR_TEND | SSR_TDRE); + m_is_dma_source_tx = false; + + update_tx_state(); + } +} + +void sh7014_sci_device::tra_callback() +{ + if (!is_transmit_register_empty()) + m_sci_tx_cb(transmit_register_get_data_bit()); + + if ((m_smr & SMR_CA) && m_hack_set_full_data_transmit_on_sync) { + while (!is_transmit_register_empty()) + m_sci_tx_cb(transmit_register_get_data_bit()); + } +} + +void sh7014_sci_device::tra_complete() +{ + if (!(m_ssr & SSR_TDRE)) { + update_tx_state(); + return; + } + + LOGMASKED(LOG_TXRX, "transmit ended\n"); + + m_ssr |= SSR_TEND; + if (m_scr & SCR_TEIE) + m_intc->set_interrupt(m_tei_int, ASSERT_LINE); +} + +void sh7014_sci_device::update_tx_state() +{ + if (!(m_scr & SCR_TE) || (m_ssr & SSR_TDRE) || !is_transmit_register_empty()) + return; + + LOGMASKED(LOG_TXRX, "transmitting %02x\n", m_tdr); + + transmit_register_setup(m_tdr); + + m_ssr = (m_ssr & ~SSR_TEND) | SSR_TDRE; + + if (m_scr & SCR_TIE) + m_intc->set_interrupt(m_txi_int, ASSERT_LINE); +} + +uint8_t sh7014_sci_device::rdr_r() +{ + auto r = m_rdr; + + LOGMASKED(LOG_TXRX, "rdr_r%s %02x\n", m_is_dma_source_rx ? " (dma)" : "", m_rdr); + + // DMA reads cause RDRF to be cleared + if (m_is_dma_source_rx) + m_ssr &= ~SSR_RDRF; + + return r; +} + +void sh7014_sci_device::rcv_complete() +{ + receive_register_extract(); + + if (!(m_scr & SCR_RE)) + return; + + if (is_receive_framing_error()) + m_ssr |= SSR_FER; + if (is_receive_parity_error()) + m_ssr |= SSR_PER; + + if (!(m_ssr & SSR_RDRF)) { + m_rdr = get_received_char(); + m_ssr |= SSR_RDRF; + + if (m_scr & SCR_RIE) + m_intc->set_interrupt(m_rxi_int, ASSERT_LINE); + + if ((m_ssr & SSR_FER) | (m_ssr & SSR_PER) || (m_ssr & SSR_ORER)) + m_intc->set_interrupt(m_eri_int, ASSERT_LINE); + } else { + m_ssr |= SSR_ORER; + } +} + +void sh7014_sci_device::update_data_format() +{ + if (m_smr & SMR_CA) { + // Synchronous clock is a fixed 8-bit data length transmission + set_data_frame(0, 8, PARITY_NONE, STOP_BITS_0); + return; + } + + // Async + set_data_frame( + 1, + (m_smr & SMR_CHR) ? 8 : 7, + (m_smr & SMR_MP) ? PARITY_NONE : ((m_smr & SMR_PE) ? PARITY_ODD : PARITY_EVEN), // Multiprocessor mode does not use parity + (m_smr & SMR_STOP) ? STOP_BITS_1 : STOP_BITS_2 + ); +} + +void sh7014_sci_device::update_clock() +{ + auto clock_mode = INTERNAL_SYNC_OUT; + if (m_smr & SMR_CA) { + if (m_scr & SCR_CKE1) + clock_mode = EXTERNAL_SYNC; + else + clock_mode = INTERNAL_SYNC_OUT; + } else { + if (m_scr & SCR_CKE1) + clock_mode = EXTERNAL_ASYNC; + else if (m_scr & SCR_CKE0) + clock_mode = INTERNAL_ASYNC_OUT; + else + clock_mode = INTERNAL_ASYNC; + } + + if (clock_mode == EXTERNAL_ASYNC && !m_external_clock_period.is_never()) + clock_mode = EXTERNAL_RATE_ASYNC; + else if (clock_mode == EXTERNAL_SYNC && !m_external_clock_period.is_never()) + clock_mode = EXTERNAL_RATE_SYNC; + + auto clock_speed = attotime::zero; + switch (clock_mode) { + case INTERNAL_ASYNC: + case INTERNAL_ASYNC_OUT: + case INTERNAL_SYNC_OUT: + { + int divider = (1 << (2 * (m_smr & SMR_CKS))) * (m_brr + 1); + clock_speed = attotime::from_ticks(divider, clock()); + } + break; + + case EXTERNAL_ASYNC: + case EXTERNAL_RATE_ASYNC: + case EXTERNAL_SYNC: + case EXTERNAL_RATE_SYNC: + clock_speed = m_external_clock_period; + break; + } + + if (clock_speed != m_clock_speed && !clock_speed.is_never()) { + LOGMASKED(LOG_CLOCK, "Changing SCI ch %d rate set to %lf (%d %04x %04x)\n", m_channel_id, clock_speed.as_hz(), clock_mode, m_brr, m_smr); + + if (m_hack_set_full_data_transmit_on_sync && (m_smr & SMR_CA)) + set_rate(clock_speed * 8); + else + set_rate(clock_speed); + + m_clock_speed = clock_speed; + } +} diff --git a/src/devices/cpu/sh/sh7014_sci.h b/src/devices/cpu/sh/sh7014_sci.h new file mode 100644 index 00000000000..cc713bc737c --- /dev/null +++ b/src/devices/cpu/sh/sh7014_sci.h @@ -0,0 +1,135 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/*************************************************************************** + + SH7014 SCI Controller + +***************************************************************************/ + +#ifndef MAME_CPU_SH_SH7014_SCI_H +#define MAME_CPU_SH_SH7014_SCI_H + +#pragma once + +#include "sh2.h" +#include "sh7014_intc.h" +#include "diserial.h" + +class sh7014_sci_device : public device_t, public device_serial_interface +{ +public: + sh7014_sci_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template<typename T> sh7014_sci_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&intc, int channel_id, int eri, int rxi, int txi, int tei) + : sh7014_sci_device(mconfig, tag, owner, clock) + { + m_intc.set_tag(std::forward<T>(intc)); + m_channel_id = channel_id; + m_eri_int = eri; + m_rxi_int = rxi; + m_txi_int = txi; + m_tei_int = tei; + } + + auto write_sci_tx() { return m_sci_tx_cb.bind(); } + + void set_dma_source_tx(bool val) { m_is_dma_source_tx = val; } + void set_dma_source_rx(bool val) { m_is_dma_source_rx = val; } + + void set_external_clock_period(const attotime &_period); + void set_send_full_data_transmit_on_sync_hack(bool enabled); + + void map(address_map &map); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + + virtual void rcv_complete() override; + + virtual void tra_callback() override; + virtual void tra_complete() override; + +private: + enum : uint32_t { + INTERNAL_ASYNC = 0, + INTERNAL_ASYNC_OUT, + EXTERNAL_ASYNC, + EXTERNAL_RATE_ASYNC, + INTERNAL_SYNC_OUT, + EXTERNAL_SYNC, + EXTERNAL_RATE_SYNC + }; + + enum { + SMR_CA = 1 << 7, + SMR_CHR = 1 << 6, + SMR_PE = 1 << 5, + SMR_OE = 1 << 4, + SMR_STOP = 1 << 3, + SMR_MP = 1 << 2, + SMR_CKS = (1 << 1) | (1 << 0), + + SCR_TIE = 1 << 7, + SCR_RIE = 1 << 6, + SCR_TE = 1 << 5, + SCR_RE = 1 << 4, + SCR_MPIE = 1 << 3, + SCR_TEIE = 1 << 2, + SCR_CKE = (1 << 1) | (1 << 0), + SCR_CKE1 = 1 << 1, + SCR_CKE0 = 1 << 0, + + SSR_TDRE = 1 << 7, + SSR_RDRF = 1 << 6, + SSR_ORER = 1 << 5, + SSR_FER = 1 << 4, + SSR_PER = 1 << 3, + SSR_TEND = 1 << 2, + SSR_MPB = 1 << 1, + SSR_MPBT = 1 << 0 + }; + + uint8_t smr_r(); + void smr_w(uint8_t data); + + uint8_t brr_r(); + void brr_w(uint8_t data); + + uint8_t scr_r(); + void scr_w(uint8_t data); + + uint8_t tdr_r(); + void tdr_w(uint8_t data); + + uint8_t ssr_r(); + void ssr_w(uint8_t data); + + uint8_t rdr_r(); + + void update_tx_state(); + void update_clock(); + void update_data_format(); + + required_device<sh7014_intc_device> m_intc; + + devcb_write_line m_sci_tx_cb; + + int m_channel_id, m_eri_int, m_rxi_int, m_txi_int, m_tei_int; + bool m_hack_set_full_data_transmit_on_sync; + + uint8_t m_smr; + uint8_t m_brr; + uint8_t m_scr; + uint8_t m_tdr; + uint8_t m_ssr; + uint8_t m_rdr; + + attotime m_external_clock_period, m_clock_speed; + bool m_is_dma_source_tx, m_is_dma_source_rx; +}; + + +DECLARE_DEVICE_TYPE(SH7014_SCI, sh7014_sci_device) + +#endif // MAME_CPU_SH_SH7014_SCI_H |
