summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/cpu.lua14
-rw-r--r--scripts/src/machine.lua28
-rw-r--r--scripts/src/sound.lua12
-rw-r--r--src/devices/bus/ata/atapicdr.cpp7
-rw-r--r--src/devices/cpu/sh/sh7014.cpp201
-rw-r--r--src/devices/cpu/sh/sh7014.h83
-rw-r--r--src/devices/cpu/sh/sh7014_bsc.cpp148
-rw-r--r--src/devices/cpu/sh/sh7014_bsc.h62
-rw-r--r--src/devices/cpu/sh/sh7014_dmac.cpp400
-rw-r--r--src/devices/cpu/sh/sh7014_dmac.h173
-rw-r--r--src/devices/cpu/sh/sh7014_intc.cpp313
-rw-r--r--src/devices/cpu/sh/sh7014_intc.h147
-rw-r--r--src/devices/cpu/sh/sh7014_mtu.cpp571
-rw-r--r--src/devices/cpu/sh/sh7014_mtu.h211
-rw-r--r--src/devices/cpu/sh/sh7014_port.cpp243
-rw-r--r--src/devices/cpu/sh/sh7014_port.h98
-rw-r--r--src/devices/cpu/sh/sh7014_sci.cpp380
-rw-r--r--src/devices/cpu/sh/sh7014_sci.h135
-rw-r--r--src/devices/machine/icd2061a.cpp312
-rw-r--r--src/devices/machine/icd2061a.h105
-rw-r--r--src/devices/machine/t10mmc.cpp10
-rw-r--r--src/devices/sound/lc78836m.cpp208
-rw-r--r--src/devices/sound/lc78836m.h66
-rw-r--r--src/mame/mame.lst1
-rw-r--r--src/mame/namco/namcos12.cpp235
-rw-r--r--src/mame/namco/namcos12_cdxa.cpp256
-rw-r--r--src/mame/namco/namcos12_cdxa.h93
27 files changed, 4473 insertions, 39 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index 273dc7b5597..e10e7a84de6 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -871,6 +871,20 @@ if CPUS["SH"] then
MAME_DIR .. "src/devices/cpu/sh/sh4regs.h",
MAME_DIR .. "src/devices/cpu/sh/sh4tmu.cpp",
MAME_DIR .. "src/devices/cpu/sh/sh4tmu.h",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_bsc.cpp",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_bsc.h",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_dmac.cpp",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_dmac.h",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_intc.cpp",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_intc.h",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_mtu.cpp",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_mtu.h",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_port.cpp",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_port.h",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_sci.cpp",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014_sci.h",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014.cpp",
+ MAME_DIR .. "src/devices/cpu/sh/sh7014.h",
MAME_DIR .. "src/devices/cpu/sh/sh7021.cpp",
MAME_DIR .. "src/devices/cpu/sh/sh7021.h",
MAME_DIR .. "src/devices/cpu/sh/sh7032.cpp",
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index 9b8c615500c..5138a692fee 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -1756,25 +1756,37 @@ end
---------------------------------------------------
--
---@src/devices/machine/icm7170.h,MACHINES["ICM7170"] = true
+--@src/devices/machine/ibm21s850.h,MACHINES["IBM21S850"] = true
---------------------------------------------------
-if (MACHINES["ICM7170"]~=null) then
+if (MACHINES["IBM21S850"]~=null) then
files {
- MAME_DIR .. "src/devices/machine/icm7170.cpp",
- MAME_DIR .. "src/devices/machine/icm7170.h",
+ MAME_DIR .. "src/devices/machine/ibm21s850.cpp",
+ MAME_DIR .. "src/devices/machine/ibm21s850.h",
}
end
---------------------------------------------------
--
---@src/devices/machine/ibm21s850.h,MACHINES["IBM21S850"] = true
+--@src/devices/machine/icd2061a.h,MACHINES["ICD2061A"] = true
---------------------------------------------------
-if (MACHINES["IBM21S850"]~=null) then
+if (MACHINES["ICD2061A"]~=null) then
files {
- MAME_DIR .. "src/devices/machine/ibm21s850.cpp",
- MAME_DIR .. "src/devices/machine/ibm21s850.h",
+ MAME_DIR .. "src/devices/machine/icd2061a.cpp",
+ MAME_DIR .. "src/devices/machine/icd2061a.h",
+ }
+end
+
+---------------------------------------------------
+--
+--@src/devices/machine/icm7170.h,MACHINES["ICM7170"] = true
+---------------------------------------------------
+
+if (MACHINES["ICM7170"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/icm7170.cpp",
+ MAME_DIR .. "src/devices/machine/icm7170.h",
}
end
diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua
index 59ebe104b2e..4959be4e3a1 100644
--- a/scripts/src/sound.lua
+++ b/scripts/src/sound.lua
@@ -1442,6 +1442,18 @@ if (SOUNDS["LC7535"]~=null) then
end
---------------------------------------------------
+-- Sanyo LC78836M
+--@src/devices/sound/lc78836m.h,SOUNDS["LC78836M"] = true
+---------------------------------------------------
+
+if (SOUNDS["LC78836M"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/sound/lc78836m.cpp",
+ MAME_DIR .. "src/devices/sound/lc78836m.h",
+ }
+end
+
+---------------------------------------------------
-- Sanyo LC82310
--@src/devices/sound/lc82310.h,SOUNDS["LC82310"] = true
---------------------------------------------------
diff --git a/src/devices/bus/ata/atapicdr.cpp b/src/devices/bus/ata/atapicdr.cpp
index 5c22d686222..9d2be8d51c5 100644
--- a/src/devices/bus/ata/atapicdr.cpp
+++ b/src/devices/bus/ata/atapicdr.cpp
@@ -169,6 +169,7 @@ void atapi_cdrom_device::ExecCommand()
case T10MMC_CMD_PLAY_AUDIO_12:
case T10MMC_CMD_READ_CD:
case T10SBC_CMD_READ_12:
+ case T10SBC_CMD_SEEK_10:
if(!m_image->exists())
{
m_phase = SCSI_PHASE_STATUS;
@@ -197,4 +198,10 @@ void atapi_cdrom_device::ExecCommand()
break;
}
t10mmc::ExecCommand();
+
+ // truckk requires seek complete flag to be set after calling the SEEK command
+ // so set the seek complete status flag after a successful request to emulate
+ // having asked the device itself to seek
+ if (command[0] == T10SBC_CMD_SEEK_10 && m_status_code == SCSI_STATUS_CODE_GOOD)
+ m_status |= IDE_STATUS_DSC;
}
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
diff --git a/src/devices/machine/icd2061a.cpp b/src/devices/machine/icd2061a.cpp
new file mode 100644
index 00000000000..62c90f72055
--- /dev/null
+++ b/src/devices/machine/icd2061a.cpp
@@ -0,0 +1,312 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ IC Designs 2061A Dual Programmable Graphics Clock Generator
+ ____ ____
+ SEL0/CLK -> 1 | \_/ | 16 <- /PWRDWN
+ SEL1/DATA -> 2 | | 15 <- INTCLK
+ AVDD -> 3 | | 14 <- INIT1
+ /OUTDIS -> 4 | ICD2061A | 13 <- VDD
+ GND -> 5 | | 12 <- INIT0
+ XTALIN -> 6 | | 11 <- FEATCLK
+ XTALOUT <- 7 | | 10 -> /ERROUT
+ MCLKOUT <- 8 |___________| 9 -> VCLKOUT
+
+
+ TODO:
+ - Not handled: MCLKOUT_HIGH_Z, VCLKOUT_HIGH_Z, VCLKOUT_FORCED_HIGH
+
+***************************************************************************/
+
+#include "emu.h"
+#include "icd2061a.h"
+
+#define LOG_PINS (1 << 1)
+#define LOG_STATE (1 << 2)
+
+// #define VERBOSE (LOG_GENERAL | LOG_PINS | LOG_STATE)
+
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(ICD2061A, icd2061a_device, "icd2061a", "IC Designs 2061A Dual Programmable Graphics Clock Generator")
+
+
+icd2061a_device::icd2061a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, ICD2061A, tag, owner, clock)
+ , m_vclkout_changed_cb(*this)
+ , m_mclkout_changed_cb(*this)
+ , m_errout_cb(*this)
+ , m_init0(0), m_init1(0)
+ , m_outdis(1), m_pwrdwn(1)
+ , m_intclk(0)
+{
+}
+
+void icd2061a_device::device_start()
+{
+ save_item(NAME(m_state));
+ save_item(NAME(m_unlock_step));
+ save_item(NAME(m_cur_bit));
+ save_item(NAME(m_data));
+ save_item(NAME(m_data_prev));
+ save_item(NAME(m_clk));
+ save_item(NAME(m_cmd));
+
+ save_item(NAME(m_init0));
+ save_item(NAME(m_init1));
+ save_item(NAME(m_sel0));
+ save_item(NAME(m_sel1));
+ save_item(NAME(m_outdis));
+ save_item(NAME(m_pwrdwn));
+ save_item(NAME(m_intclk));
+ save_item(NAME(m_vclkout_select));
+ save_item(NAME(m_mclkout_select));
+
+ save_item(NAME(m_reg_clocks));
+ save_item(NAME(m_prescale));
+ save_item(NAME(m_powerdown_mode));
+ save_item(NAME(m_muxref_vclkout_source));
+ save_item(NAME(m_timeout_interval));
+ save_item(NAME(m_muxref_adjust));
+
+ save_item(NAME(m_featclock));
+ save_item(NAME(m_vclkout_clock));
+ save_item(NAME(m_mclkout_clock));
+
+ m_watchdog_timer = timer_alloc(FUNC(icd2061a_device::watchdog_callback), this);
+ m_watchdog_timer->adjust(attotime::never);
+
+ m_update_timer = timer_alloc(FUNC(icd2061a_device::update_clock_callback), this);
+ m_update_timer->adjust(attotime::never);
+
+ m_state = CLOCKGEN_UNLOCK;
+ m_unlock_step = 0;
+ m_cur_bit = 0;
+ m_data = m_data_prev = 1;
+ m_clk = 1;
+ m_cmd = 0;
+
+ std::fill(std::begin(m_prescale), std::end(m_prescale), 2);
+ m_powerdown_mode = 0;
+ m_muxref_vclkout_source = 0;
+ m_timeout_interval = 5;
+ m_muxref_adjust = 1;
+ m_powerdown_divisor = 8;
+ m_sel0 = m_sel1 = 0;
+ m_vclkout_select = m_mclkout_select = -1;
+ m_vclkout_clock = m_mclkout_clock = 0;
+
+ // Are these values derived from the XTALIN at all?
+ // The manual only gives the frequencies as these values,
+ // not p/q/m values that could be used to derive it from XTALIN
+ if (m_init0 == 0 && m_init1 == 0) {
+ m_reg_clocks[MREG] = 32'500'000;
+ m_reg_clocks[REG0] = 25'175'000;
+ m_reg_clocks[REG1] = m_reg_clocks[REG2] = 28'322'000;
+ } else if (m_init0 == 0 && m_init1 == 1) {
+ m_reg_clocks[MREG] = 40'000'000;
+ m_reg_clocks[REG0] = 25'175'000;
+ m_reg_clocks[REG1] = m_reg_clocks[REG2] = 28'322'000;
+ } else if (m_init0 == 1 && m_init1 == 0) {
+ m_reg_clocks[MREG] = 50'350'000;
+ m_reg_clocks[REG0] = 40'000'000;
+ m_reg_clocks[REG1] = m_reg_clocks[REG2] = 28'322'000;
+ } else if (m_init0 == 1 && m_init1 == 1) {
+ m_reg_clocks[MREG] = 56'644'000;
+ m_reg_clocks[REG0] = 40'000'000;
+ m_reg_clocks[REG1] = m_reg_clocks[REG2] = 50'350'000;
+ }
+
+ m_errout_cb(1); // set no error start
+ update_clock_callback(0);
+}
+
+void icd2061a_device::set_featclock(const uint32_t clock)
+{
+ m_featclock = clock;
+}
+
+TIMER_CALLBACK_MEMBER( icd2061a_device::watchdog_callback )
+{
+ // If the timeout is hit then rearm the locked mode and accept the last values as sel0/sel1
+ LOG("watchdog timed out, setting sel0 = %d, sel1 = %d\n", m_clk, m_data);
+
+ if (m_sel0 != m_clk || m_sel1 != m_data) {
+ m_sel0 = m_clk;
+ m_sel1 = m_data;
+
+ update_clock();
+ }
+
+ m_state = CLOCKGEN_UNLOCK;
+ m_unlock_step = 0;
+ m_unlock_step = 0;
+ m_cur_bit = 0;
+ m_cmd = 0;
+}
+
+TIMER_CALLBACK_MEMBER( icd2061a_device::update_clock_callback )
+{
+ // mclkout
+ if (m_outdis == 0)
+ m_mclkout_select = MCLKOUT_HIGH_Z;
+ else if (m_outdis == 1 && m_pwrdwn == 1)
+ m_mclkout_select = MCLKOUT_MREG;
+ else if (m_outdis == 1 && m_pwrdwn == 0)
+ m_mclkout_select = MCLKOUT_PWRDWN;
+
+ if (m_mclkout_select == MCLKOUT_PWRDWN) {
+ if (m_powerdown_mode == 1 || m_powerdown_divisor == 0)
+ m_reg_clocks[MREG] = 0;
+ else
+ m_reg_clocks[MREG] = clock() / ((17 - m_powerdown_divisor) * 2); // 1 = divisor of 32, 15 = divisor of 4
+ } else if (m_mclkout_select == MCLKOUT_MREG) {
+ const int a = BIT(m_regs[MREG], 21, 2); // register addr
+ const int p = BIT(m_regs[MREG], 10, 7) + 3; // p counter value
+ const int m = BIT(m_regs[MREG], 7, 3); // post-vco divisor
+ const int q = BIT(m_regs[MREG], 0, 7) + 2; // q counter value
+ m_reg_clocks[MREG] = (clock() * m_prescale[a] * (p / double(q))) / (1 << m);
+ }
+
+ if (m_reg_clocks[MREG] != m_mclkout_clock) {
+ m_mclkout_changed_cb(m_reg_clocks[MREG]);
+ m_mclkout_clock = m_reg_clocks[MREG];
+ }
+
+ // vclkout
+ if (m_outdis == 0)
+ m_vclkout_select = VCLKOUT_HIGH_Z;
+ else if (m_outdis == 1 && m_pwrdwn == 0)
+ m_vclkout_select = VCLKOUT_FORCED_HIGH;
+ else if (m_outdis == 1 && m_pwrdwn == 1 && m_sel1 == 0 && m_sel0 == 0)
+ m_vclkout_select = VCLKOUT_REG0;
+ else if (m_outdis == 1 && m_pwrdwn == 1 && m_sel1 == 0 && m_sel0 == 1)
+ m_vclkout_select = VCLKOUT_REG1;
+ else if (m_outdis == 1 && m_pwrdwn == 1 && m_intclk == 0 && m_sel1 == 1 && m_sel0 == 0)
+ m_vclkout_select = VCLKOUT_FEATCLK;
+ else if (m_outdis == 1 && m_pwrdwn == 1 && m_sel1 == 1 && (m_intclk == 1 || m_sel0 == 1))
+ m_vclkout_select = VCLKOUT_REG2;
+
+ if (m_vclkout_select == VCLKOUT_FEATCLK) {
+ m_reg_clocks[m_vclkout_select] = m_featclock;
+ } else if (m_vclkout_select >= VCLKOUT_REG0 && m_vclkout_select <= VCLKOUT_REG2) {
+ const int a = BIT(m_regs[m_vclkout_select], 21, 2); // register addr
+ const int p = BIT(m_regs[m_vclkout_select], 10, 7) + 3; // p counter value
+ const int m = BIT(m_regs[m_vclkout_select], 7, 3); // post-vco divisor
+ const int q = BIT(m_regs[m_vclkout_select], 0, 7) + 2; // q counter value
+ m_reg_clocks[m_vclkout_select] = (clock() * m_prescale[a] * (p / double(q))) / (1 << m);
+ }
+
+ m_vclkout_changed_cb(m_reg_clocks[m_vclkout_select]);
+ m_vclkout_clock = m_reg_clocks[m_vclkout_select];
+}
+
+void icd2061a_device::update_clock()
+{
+ // Set muxed clock during transition period
+ m_vclkout_changed_cb(m_muxref_vclkout_source ? m_mclkout_clock : clock());
+
+ m_watchdog_timer->adjust(attotime::never);
+ m_update_timer->adjust(attotime::from_msec(m_timeout_interval));
+}
+
+void icd2061a_device::data_w(int state)
+{
+ LOGMASKED(LOG_PINS, "data_w %d\n", state);
+
+ m_watchdog_timer->adjust(attotime::from_msec(m_timeout_interval));
+
+ m_data = state;
+}
+
+void icd2061a_device::clk_w(int state)
+{
+ LOGMASKED(LOG_PINS, "clk_w %d\n", state);
+
+ m_watchdog_timer->adjust(attotime::from_msec(m_timeout_interval));
+
+ if (!m_clk && state) {
+ if (m_state == CLOCKGEN_UNLOCK && m_data == 1) {
+ // Any number of 1s can be read until the final transition with data low
+ m_errout_cb(1); // clear any previous errors since we're in a good state now
+
+ m_unlock_step++;
+ LOGMASKED(LOG_STATE, "unlock count = %d\n", m_unlock_step);
+ } else if (m_state == CLOCKGEN_UNLOCK && m_data == 0 && m_unlock_step >= 5) {
+ // Found last part of unlock sequence, move on to start bit
+ m_state = CLOCKGEN_START_BIT;
+ m_unlock_step = 0;
+ LOGMASKED(LOG_STATE, "found unlock end, state = CLOCKGEN_START_BIT\n");
+ } else if (m_state == CLOCKGEN_START_BIT && m_data == 0) {
+ // Found start bit transition, move on to data
+ m_state = CLOCKGEN_DATA;
+ m_cur_bit = 0;
+ m_cmd = 0;
+ LOGMASKED(LOG_STATE, "found start bit, state = CLOCKGEN_DATA\n");
+ } else if (m_state == CLOCKGEN_DATA && m_data_prev != m_data && m_cur_bit < 24) {
+ // Data uses modified Manchester encoding so the data bit read on each edge must be different
+ // Must read exactly 24 bits of data here
+ m_cmd |= m_data << m_cur_bit;
+ LOGMASKED(LOG_STATE, "data %d %06x\n", m_cur_bit, m_cmd);
+ m_cur_bit++;
+ } else if (m_state == CLOCKGEN_DATA && m_data == 1 && m_cur_bit == 24) {
+ // Found end bit transition, accept data and then rearm lock
+ const int idx = BIT(m_cmd, 21, 3);
+
+ if (idx == 4) {
+ const int p = BIT(m_cmd, 17, 4);
+ LOG("PWRDWN register %06x p[%d]\n", m_cmd, p);
+ m_powerdown_divisor = p;
+ } else if (idx == 6) {
+ const int c = BIT(m_cmd, 15, 6);
+ const int ps = BIT(m_cmd, 12, 3);
+
+ m_powerdown_mode = BIT(c, 5);
+ m_muxref_vclkout_source = BIT(c, 4);
+ m_timeout_interval = 5 * (1 << BIT(c, 3));
+ m_muxref_adjust = BIT(c, 1);
+
+ m_prescale[0] = 2 << BIT(ps, 0);
+ m_prescale[1] = 2 << BIT(ps, 1);
+ m_prescale[2] = 2 << BIT(ps, 2);
+
+ LOG("CNTL program %06x c[%d] ps[%d]\n", m_cmd, c, ps);
+ } else if (idx <= 3) {
+ const int a = BIT(m_cmd, 21, 2); // register addr
+ const int i = BIT(m_cmd, 17, 4); // index, used to make sure clock is in expected range
+ const int p = BIT(m_cmd, 10, 7) + 3; // p counter value
+ const int m = BIT(m_cmd, 7, 3); // post-vco divisor
+ const int q = BIT(m_cmd, 0, 7) + 2; // q counter value
+ const double outclock = (clock() * m_prescale[a] * (p / double(q))) / 1000000.0;
+ const double outclock_scaled = outclock / (1 << m);
+
+ m_regs[idx] = m_cmd;
+
+ LOG("VCO program %06x a[%d] i[%d] p[%d] m[%d] q[%d] prescale[%d] clock[%lf] clock_scaled[%lf]\n", m_cmd, a, i, p, m, q, m_prescale[a], outclock, outclock_scaled);
+ } else {
+ LOG("Unknown register selected: %06x %d\n", m_cmd, idx);
+ }
+
+ m_state = CLOCKGEN_UNLOCK;
+ m_unlock_step = 0;
+
+ LOGMASKED(LOG_STATE, "accepted\n");
+
+ update_clock();
+ } else {
+ // Error state, rearm lock
+ m_state = CLOCKGEN_UNLOCK;
+ m_unlock_step = 0;
+
+ m_errout_cb(0); // notify of error
+
+ LOGMASKED(LOG_STATE, "error\n");
+ }
+ } else {
+ m_data_prev = m_data;
+ }
+
+ m_clk = state;
+}
diff --git a/src/devices/machine/icd2061a.h b/src/devices/machine/icd2061a.h
new file mode 100644
index 00000000000..2308d2e9288
--- /dev/null
+++ b/src/devices/machine/icd2061a.h
@@ -0,0 +1,105 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ IC Designs 2061ASC-1 Programmable Clock Generator
+
+***************************************************************************/
+
+#ifndef MAME_MACHINE_ICD2061A_H
+#define MAME_MACHINE_ICD2061A_H
+
+#pragma once
+
+class icd2061a_device : public device_t
+{
+public:
+ icd2061a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ auto vclkout_changed() { return m_vclkout_changed_cb.bind(); }
+ auto mclkout_changed() { return m_mclkout_changed_cb.bind(); }
+ auto errout() { return m_errout_cb.bind(); }
+
+ void init0_w(int state) { m_init0 = state; }
+ void init1_w(int state) { m_init1 = state; }
+ void outdis_w(int state) { m_outdis = state; }
+ void pwrdwn_w(int state) { m_pwrdwn = state; }
+ void intclk_w(int state) { m_intclk = state; }
+
+ void set_featclock(const uint32_t clock);
+
+ void data_w(int state);
+ void clk_w(int state);
+
+protected:
+ virtual void device_start() override;
+
+private:
+ enum : uint8_t {
+ CLOCKGEN_UNLOCK = 0,
+ CLOCKGEN_START_BIT,
+ CLOCKGEN_DATA,
+ };
+
+ enum : int8_t {
+ VCLKOUT_REG0 = 0,
+ VCLKOUT_REG1,
+ VCLKOUT_REG2,
+ VCLKOUT_HIGH_Z,
+ VCLKOUT_FORCED_HIGH,
+ VCLKOUT_FEATCLK,
+
+ MCLKOUT_MREG = 0,
+ MCLKOUT_HIGH_Z,
+ MCLKOUT_PWRDWN,
+ };
+
+ enum {
+ REG0 = 0,
+ REG1,
+ REG2,
+ MREG,
+ };
+
+ TIMER_CALLBACK_MEMBER( watchdog_callback );
+ TIMER_CALLBACK_MEMBER( update_clock_callback );
+
+ void update_clock();
+
+ emu_timer *m_watchdog_timer;
+ emu_timer *m_update_timer;
+
+ devcb_write32 m_vclkout_changed_cb, m_mclkout_changed_cb;
+ devcb_write_line m_errout_cb;
+
+ uint8_t m_init0, m_init1;
+ uint8_t m_sel0, m_sel1;
+ uint8_t m_outdis;
+ uint8_t m_pwrdwn;
+ uint8_t m_intclk;
+ int8_t m_vclkout_select, m_mclkout_select;
+
+ uint8_t m_state;
+ uint8_t m_unlock_step;
+ uint8_t m_cur_bit;
+ uint8_t m_data, m_data_prev;
+ uint8_t m_clk;
+ uint32_t m_cmd;
+
+ uint32_t m_regs[4];
+ uint32_t m_reg_clocks[4];
+ uint8_t m_prescale[4];
+ uint8_t m_powerdown_mode;
+ uint8_t m_muxref_vclkout_source;
+ uint8_t m_timeout_interval;
+ uint8_t m_muxref_adjust;
+ uint8_t m_powerdown_divisor;
+
+ uint32_t m_featclock;
+ uint32_t m_vclkout_clock, m_mclkout_clock;
+};
+
+
+DECLARE_DEVICE_TYPE(ICD2061A, icd2061a_device)
+
+#endif // MAME_MACHINE_ICD2061A_H
diff --git a/src/devices/machine/t10mmc.cpp b/src/devices/machine/t10mmc.cpp
index 09e7314de2c..b597c27a4af 100644
--- a/src/devices/machine/t10mmc.cpp
+++ b/src/devices/machine/t10mmc.cpp
@@ -760,6 +760,16 @@ void t10mmc::ExecCommand()
break;
}
+ case T10SBC_CMD_SEEK_10:
+ m_lba = get_u32be(&command[2]);
+
+ m_device->logerror("T10SBC: SEEK EXTENDED to LBA %x\n", m_lba);
+
+ m_phase = SCSI_PHASE_STATUS;
+ m_status_code = SCSI_STATUS_CODE_GOOD;
+ m_transfer_length = 0;
+ break;
+
default:
t10spc::ExecCommand();
}
diff --git a/src/devices/sound/lc78836m.cpp b/src/devices/sound/lc78836m.cpp
new file mode 100644
index 00000000000..7b611e59cf7
--- /dev/null
+++ b/src/devices/sound/lc78836m.cpp
@@ -0,0 +1,208 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ Sanyo LC78836M Digital Audio 16-bit D/A Converter
+ with On-Chip Digital Filters
+ ____ ____
+ REFH 1 | \_/ | 24 AVDD
+ VrefH 2 | | 23 CH1OUT
+ MUTE 3 | | 22 AGND
+ D/N 4 | | 21 CH2OUT
+ BCLK 5 | | 20 REFL
+ DATA 6 | LC78836M | 19 VrefL
+ LRCK 7 | | 18 CKSL2
+ DVDD 8 | | 17 CKSL1
+ CKOUT 9 | | 16 FS2
+ XOUT 10 | | 15 FS1
+ XIN 11 | | 14 EMP
+ DGND 12 |___________| 13 INITB
+
+
+ TOOD:
+ - INITB, DN aren't implemented
+ - Filters aren't implemented
+
+***************************************************************************/
+
+#include "emu.h"
+#include "lc78836m.h"
+
+// #define VERBOSE (LOG_GENERAL)
+
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(LC78836M, lc78836m_device, "lc78836m", "Sanyo LC78836M Digital Audio 16-bit D/A Converter")
+
+
+lc78836m_device::lc78836m_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, LC78836M, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_stream(nullptr)
+ , m_cksl1(0), m_cksl2(0)
+ , m_fs1(0), m_fs2(0)
+ , m_emp(0)
+{
+}
+
+void lc78836m_device::device_start()
+{
+ save_item(NAME(m_cksl1));
+ save_item(NAME(m_cksl2));
+ save_item(NAME(m_fs1));
+ save_item(NAME(m_fs2));
+ save_item(NAME(m_emp));
+
+ save_item(NAME(m_mute));
+ save_item(NAME(m_lrck));
+ save_item(NAME(m_data));
+ save_item(NAME(m_bclk));
+ save_item(NAME(m_sample));
+ save_item(NAME(m_sample_bit));
+ save_item(NAME(m_clock_fs));
+
+ save_item(NAME(m_sample_ch1));
+ save_item(NAME(m_sample_ch2));
+ save_item(NAME(m_att));
+
+ m_stream = stream_alloc(2, 2, m_clock);
+}
+
+void lc78836m_device::device_reset()
+{
+ m_mute = 0;
+ m_lrck = 0;
+ m_data = 0;
+ m_bclk = 0;
+ m_sample = 0;
+ m_sample_bit = 0;
+ m_att = 1024;
+ m_sample_ch1 = m_sample_ch2 = 0;
+
+ update_clock();
+}
+
+void lc78836m_device::device_clock_changed()
+{
+ update_clock();
+}
+
+void lc78836m_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ outputs[0].fill(0);
+ outputs[0].put(0, m_sample_ch1 * m_att / 1024.0);
+ m_sample_ch1 = 0;
+
+ outputs[1].fill(0);
+ outputs[1].put(0, m_sample_ch2 * m_att / 1024.0);
+ m_sample_ch2 = 0;
+
+ if (m_mute && m_att > 0)
+ m_att--;
+ else if (!m_mute && m_att < 1024)
+ m_att++;
+
+}
+
+void lc78836m_device::mute_w(int state)
+{
+ // Soft mute
+ m_mute = state;
+}
+
+void lc78836m_device::lrck_w(int state)
+{
+ // CH1 when high, CH2 when low
+ m_lrck = state;
+}
+
+void lc78836m_device::data_w(int state)
+{
+ m_data = state;
+}
+
+void lc78836m_device::bclk_w(int state)
+{
+ if (!m_bclk && state) {
+ m_sample |= m_data << m_sample_bit;
+ m_sample_bit++;
+
+ if (m_sample_bit >= 16) {
+ stream_buffer::sample_t sample = m_sample / double(std::numeric_limits<int16_t>::max());
+
+ if (m_lrck)
+ m_sample_ch1 = sample;
+ else
+ m_sample_ch2 = sample;
+
+ m_sample = 0;
+ m_sample_bit = 0;
+
+ m_stream->update();
+ }
+ }
+
+ m_bclk = state;
+}
+
+void lc78836m_device::initb_w(int state)
+{
+ // Initialization signal input
+ // LSI is initialized when set low
+ m_initb = state;
+}
+
+void lc78836m_device::cksl1_w(int state)
+{
+ // System clock selection bit 1
+ if (state != m_cksl1) {
+ m_cksl1 = state;
+ update_clock();
+ }
+}
+
+void lc78836m_device::cksl2_w(int state)
+{
+ // System clock selection bit 2
+ if (state != m_cksl2) {
+ m_cksl2 = state;
+ update_clock();
+ }
+}
+
+void lc78836m_device::fs1_w(int state)
+{
+ // De-emphasis filter mode selection bit 1
+ m_fs1 = state;
+}
+
+void lc78836m_device::fs2_w(int state)
+{
+ // De-emphasis filter mode selection bit 2
+ m_fs2 = state;
+}
+
+void lc78836m_device::emp_w(int state)
+{
+ // De-emphasis filter on/off
+ m_emp = state;
+}
+
+void lc78836m_device::update_clock()
+{
+ if (m_cksl1 == 0 && m_cksl2 == 0)
+ m_clock_fs = 384;
+ else if (m_cksl1 == 0 && m_cksl2 == 1)
+ m_clock_fs = 392;
+ else if (m_cksl1 == 1 && m_cksl2 == 0)
+ m_clock_fs = 448;
+ else if (m_cksl1 == 1 && m_cksl2 == 1)
+ m_clock_fs = 512;
+
+ const uint32_t new_sample_rate = m_clock / m_clock_fs;
+ if (m_stream != nullptr && new_sample_rate != m_stream->sample_rate()) {
+ LOG("sample rate changed to %d\n", new_sample_rate);
+ m_stream->set_sample_rate(new_sample_rate);
+ }
+}
diff --git a/src/devices/sound/lc78836m.h b/src/devices/sound/lc78836m.h
new file mode 100644
index 00000000000..3a95e79b656
--- /dev/null
+++ b/src/devices/sound/lc78836m.h
@@ -0,0 +1,66 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ Sanyo LC78836M Digital Audio 16-bit D/A Converter
+ with On-Chip Digital Filters
+
+***************************************************************************/
+
+#ifndef MAME_SOUND_LC78836M_H
+#define MAME_SOUND_LC78836M_H
+
+#pragma once
+
+class lc78836m_device : public device_t, public device_sound_interface
+{
+public:
+ lc78836m_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void mute_w(int state);
+
+ void lrck_w(int state);
+ void data_w(int state);
+ void bclk_w(int state);
+
+ void cksl1_w(int state);
+ void cksl2_w(int state);
+
+ void fs1_w(int state);
+ void fs2_w(int state);
+
+ void emp_w(int state);
+
+ void initb_w(int state);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_clock_changed() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ void update_clock();
+
+ sound_stream *m_stream;
+
+ uint8_t m_mute;
+ uint8_t m_cksl1, m_cksl2;
+ uint16_t m_clock_fs;
+ uint8_t m_fs1, m_fs2;
+ uint8_t m_emp;
+ uint8_t m_initb;
+
+ uint8_t m_bclk, m_lrck, m_data;
+ uint8_t m_sample_bit;
+ int16_t m_sample;
+
+ stream_buffer::sample_t m_sample_ch1, m_sample_ch2;
+ double m_att;
+};
+
+
+DECLARE_DEVICE_TYPE(LC78836M, lc78836m_device)
+
+#endif // MAME_SOUND_LC78836M_H
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 16681fc18c8..a7fc982331e 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -32826,6 +32826,7 @@ tenkomor // 1998.?? Tenkomori Shooting (World, TKM2/VER.A
tenkomorja // 1998.12 Tenkomori Shooting (Japan, TKM1/VER.A1)
toukon3 // 1997.12 Shin Nihon Pro Wrestling Toukon Retsuden 3 Arcade Edition (Japan, TR1/VER.A)
truckk // 2000.06 Truck Kyousoukyoku (Metro) (Japan, TKK2/VER.A)
+ujlnow // 1999.10? Um Jammer Lammy NOW! (Japan, UL1/VER.A)
@source:namco/namcos21.cpp
winrun // (c) 1988
diff --git a/src/mame/namco/namcos12.cpp b/src/mame/namco/namcos12.cpp
index a1cc479fb5c..f753c5d57ed 100644
--- a/src/mame/namco/namcos12.cpp
+++ b/src/mame/namco/namcos12.cpp
@@ -18,9 +18,6 @@
- kartduel frame rate is choppy, it freezes every half second
memo: this is due to the link PCB not being implemented, it keeps trying to communicate over the network causing hitching.
Forcing 0x1f781701 (which is within the shared RAM between the PCBs) to 0xff will stop the hitching.
- - truckk doesn't boot: the H8/3002 never enters InitJVSBoards @ 1DE2. 1DE2 is referenced in a table of commands at 4032,
- which is called by the routine at 3FEA. It is not clear how execution is intended to get to 3FEA - there are no direct
- branches to that location, and the bytes 3F EA don't appear at all in the program.
- technodr: printer not emulated. To play the game, press F2 to enter the test menu, navigate to GAME OPTIONS and disable
the printer by setting "PRINTER" to OFF.
- sws2001 crashes at random times in-game, and always after the opening video. You can spam insert coin and start to get in-game.
@@ -1055,9 +1052,11 @@ Notes:
MB87078 : Fujitsu MB87078 6-bit, 4-channel Electronic Volume Controller (SOIC24)
J2 : Custom Namco connector for plug-in CPU PCB
J3 : 40 pin connector for IDE CDROM data cable
- J4 : 6 pin connector (possibly to re-program the CPLD)
- J8 : 4 pin connector (left/right audio output)
- J9 : 3 pin connector (possibly mono audio output or another audio output)
+ J4 : 6 pin connector (serial)
+ For Um Jammer Lammy Now this goes to J205 on the M148 EMI DRIVE PCB.
+ According to the manual schematics, only GND and TX are connected going to RxD0 on EMI DRIVE PCB side.
+ J8 : 4 pin connector (left/right audio output, RGND/R/LGND/L pinout)
+ J9 : 3 pin connector (left/right audio output, L/GND/R pinout)
This PCB was found on the following games (so far)....
@@ -1101,15 +1100,21 @@ The lever must be wired to analog port 0 (pin B22 parts side) of the Namco 48-wa
*/
#include "emu.h"
-#include "cpu/psx/psx.h"
+
+#include "bus/ata/ataintf.h"
#include "cpu/h8/h83002.h"
#include "cpu/h8/h83337.h"
-#include "video/psx.h"
+#include "cpu/sh/sh7014.h"
+#include "cpu/psx/psx.h"
#include "machine/at28c16.h"
#include "machine/ram.h"
-#include "sound/c352.h"
#include "machine/rtc4543.h"
+#include "sound/c352.h"
+#include "video/psx.h"
+
#include "namco_settings.h"
+#include "namcos12_cdxa.h"
+
#include "screen.h"
#include "speaker.h"
@@ -1237,7 +1242,6 @@ public:
void technodr(machine_config &config);
void golgo13(machine_config &config);
void aplarail(machine_config &config);
- void truckk(machine_config &config);
void tektagt(machine_config &config);
void ptblank2(machine_config &config);
void kartduel(machine_config &config);
@@ -1247,6 +1251,28 @@ private:
};
+class namcos12_cdxa_state : public namcos12_boothack_state
+{
+public:
+ namcos12_cdxa_state(const machine_config &mconfig, device_type type, const char *tag)
+ : namcos12_boothack_state(mconfig, type, tag)
+ , m_cdxa_pcb(*this, "cdxa_pcb")
+ {
+ }
+
+ void cdxa_pcb(machine_config &config);
+
+ void truckk(machine_config &config);
+
+ void init_truckk();
+
+private:
+ void cdxa_psx_map(address_map &map);
+
+ required_device<namcos12_cdxa_device> m_cdxa_pcb;
+};
+
+
inline void ATTR_PRINTF(3,4) namcos12_state::verboselog( int n_level, const char *s_fmt, ... )
{
if( VERBOSE_LEVEL >= n_level )
@@ -1360,10 +1386,10 @@ void namcos12_state::namcos12_rom_read( uint32_t *p_n_psxram, uint32_t n_address
{
n_region = m_bankedroms;
n_offset &= 0x7fffffff;
- }
- if( !m_has_tektagt_dma && m_alt_bank ) // alt bank method stores the upper bits of the bank into m_n_bankoffset
- n_offset += m_n_bankoffset * 0x200000;
+ if( !m_has_tektagt_dma && m_alt_bank ) // alt bank method stores the upper bits of the bank into m_n_bankoffset
+ n_offset += m_n_bankoffset * 0x200000;
+ }
m_has_tektagt_dma = 0;
@@ -1851,21 +1877,6 @@ void namcos12_state::jvsmap(address_map &map)
map(0xc000, 0xfb7f).ram();
}
-void namcos12_boothack_state::truckk(machine_config &config)
-{
- coh700(config);
- // Timer at 115200*16 for the jvs serial clock
- m_sub->sci_set_external_clock_period(0, attotime::from_hz(JVSCLOCK/8));
-
- h83334_device &iocpu(H83334(config, "iocpu", JVSCLOCK));
- iocpu.set_addrmap(AS_PROGRAM, &namcos12_boothack_state::jvsmap);
-
- iocpu.write_sci_tx<0>().set(m_sub, FUNC(h8_device::sci_rx_w<0>));
- m_sub->write_sci_tx<0>().set(iocpu, FUNC(h8_device::sci_rx_w<0>));
-
- config.set_maximum_quantum(attotime::from_hz(2*115200));
-}
-
uint16_t namcos12_state::iob_p4_r()
{
return m_tssio_port_4;
@@ -1966,6 +1977,82 @@ void namcos12_boothack_state::aplarail(machine_config &config)
config.set_maximum_quantum(attotime::from_hz(2*115200));
}
+///
+
+void namcos12_cdxa_state::cdxa_pcb(machine_config &config)
+{
+ coh700(config);
+
+ NAMCOS12_CDXA(config, m_cdxa_pcb, XTAL(14'745'600));
+ m_cdxa_pcb->add_route(0, "lspeaker", 0.30); // roughly matched the volume of speaking lines between the CDXA audio vs non-CDXA audio
+ m_cdxa_pcb->add_route(1, "rspeaker", 0.30);
+ m_cdxa_pcb->psx_int10_callback().set("maincpu:irq", FUNC(psxirq_device::intin10));
+
+ m_maincpu->set_addrmap(AS_PROGRAM, &namcos12_cdxa_state::cdxa_psx_map);
+}
+
+void namcos12_cdxa_state::cdxa_psx_map(address_map &map)
+{
+ namcos12_map(map);
+
+ map(0x1f7c0000, 0x1f7cffff).rw(m_cdxa_pcb, FUNC(namcos12_cdxa_device::sh2_ram_r), FUNC(namcos12_cdxa_device::sh2_ram_w));
+
+ map(0x1f7d6002, 0x1f7d6003).w(m_cdxa_pcb, FUNC(namcos12_cdxa_device::reset_sh2_w));
+ map(0x1f7d6004, 0x1f7d600b).w(m_cdxa_pcb, FUNC(namcos12_cdxa_device::clockgen_w));
+ map(0x1f7d6010, 0x1f7d6011).w(m_cdxa_pcb, FUNC(namcos12_cdxa_device::ide_sh2_enabled_w));
+ map(0x1f7d6012, 0x1f7d6013).w(m_cdxa_pcb, FUNC(namcos12_cdxa_device::ide_ps1_enabled_w));
+ // 1f7d6018 unknown, only set once to 1 between the "SH2 Reset" and "SH2 Pll Clock Set" steps during boot
+ map(0x1f7d601a, 0x1f7d601b).w(m_cdxa_pcb, FUNC(namcos12_cdxa_device::sram_enabled_w));
+ map(0x1f7d601e, 0x1f7d601f).w(m_cdxa_pcb, FUNC(namcos12_cdxa_device::ps1_int10_finished_w));
+
+ map(0x1f7d800a, 0x1f7d800b).lr16(NAME([] () {
+ // Might be for the M148 PCB instead of CDXA PCB
+ // Code loops until this returns 0x20 before it starts writing to 0x1f7d8000
+ // Writes "55 x y z" to 0x1f7d8000 where x, y, z are related to the current I/O state
+ // Maybe used for lights?
+ return 0x20;
+ }));
+
+ map(0x1f7e0000, 0x1f7e000f).rw(m_cdxa_pcb, FUNC(namcos12_cdxa_device::cdrom_cs0_r), FUNC(namcos12_cdxa_device::cdrom_cs0_w));
+ // map(0x1f7e8000, 0x1f7e800f).rw(m_cdxa_pcb, FUNC(namcos12_cdxa_device::cdrom_cs1_r), FUNC(namcos12_cdxa_device::cdrom_cs1_w));
+ // 1f7d7000 volume enabled/set? gets set to 6 between the "SH2 Volume Set" and "SH2 Trf Program" steps, after setting 4 volumes registers to 0x7e
+ map(0x1f7f8000, 0x1f7f80ff).w(m_cdxa_pcb, FUNC(namcos12_cdxa_device::volume_w));
+}
+
+void namcos12_cdxa_state::truckk(machine_config &config)
+{
+ cdxa_pcb(config);
+
+ m_sub->read_adc<0>().set_ioport("STEER");
+ m_sub->read_adc<1>().set_ioport("BRAKE");
+ m_sub->read_adc<2>().set_ioport("GAS");
+
+ // Timer at 115200*16 for the jvs serial clock
+ m_sub->sci_set_external_clock_period(0, attotime::from_hz(JVSCLOCK/8));
+
+ h83334_device &iocpu(H83334(config, "iocpu", JVSCLOCK));
+ iocpu.set_addrmap(AS_PROGRAM, &namcos12_cdxa_state::jvsmap);
+
+ iocpu.write_sci_tx<0>().set(m_sub, FUNC(h8_device::sci_rx_w<0>));
+ m_sub->write_sci_tx<0>().set(iocpu, FUNC(h8_device::sci_rx_w<0>));
+
+ config.set_maximum_quantum(attotime::from_hz(2*115200));
+}
+
+void namcos12_cdxa_state::init_truckk()
+{
+ init_alt_bank1();
+
+ /*
+ HACK: Change order of code so that the status flags are set before DMA 5 is started
+ Can be checked at 0x8001f6e4
+ */
+ uint8_t temp[5 * 4];
+ memcpy(temp, memregion( "maincpu:rom" )->base() + 0x376ec, sizeof(temp)); // copy the 5 instructions that initialize statuses before DMA 5 finishes
+ memcpy(memregion( "maincpu:rom" )->base() + 0x376e4 + sizeof(temp), memregion( "maincpu:rom" )->base() + 0x376e4, 2 * 4); // move the 2 DMA start call instructions below where the initialization code will go
+ memcpy(memregion( "maincpu:rom" )->base() + 0x376e4, temp, sizeof(temp)); // write initialization code in new spot
+}
+
static INPUT_PORTS_START( namcos12 )
PORT_START("DSW")
PORT_DIPNAME( 0x0080, 0x0080, DEF_STR(Service_Mode) ) PORT_DIPLOCATION( "DIP SW2:1" )
@@ -2186,6 +2273,66 @@ static INPUT_PORTS_START( aplarail )
PORT_BIT(0xff, IP_ACTIVE_LOW, IPT_UNKNOWN)
INPUT_PORTS_END
+static INPUT_PORTS_START( truckk )
+ PORT_START("DSW")
+ PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
+
+ PORT_START("IN0")
+ PORT_BIT( 0xfc23, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Select Up")
+ PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Select Down")
+ PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_START1 )
+ PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Horn")
+ PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("View Change")
+ PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Music Next")
+ PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Music Back")
+
+ PORT_START("IN1")
+ PORT_BIT( 0x1fff, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_COIN1 )
+ PORT_SERVICE( 0x4000, IP_ACTIVE_LOW )
+ PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_SERVICE1 )
+
+ PORT_START("GAS")
+ PORT_BIT( 0x3ff, 0x0200, IPT_PEDAL ) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_NAME("Gas Pedal") PORT_REVERSE
+
+ PORT_START("BRAKE")
+ PORT_BIT( 0x3ff, 0x0200, IPT_PEDAL2 ) PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_NAME("Brake Pedal") PORT_REVERSE
+
+ PORT_START("STEER")
+ PORT_BIT( 0x3ff, 0x0200, IPT_PADDLE ) PORT_SENSITIVITY(100) PORT_KEYDELTA(20) PORT_NAME("Steering Wheel")
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( ujlnow )
+ PORT_START("DSW")
+ PORT_BIT( 0xffff, IP_ACTIVE_LOW, IPT_UNKNOWN )
+
+ PORT_START("IN0")
+ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 Chin Switch (C)")
+ PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("P1 Joe Switch (J)")
+ PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_NAME("Select Down")
+ PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_NAME("Select Up")
+ PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Circle / Enter") // The cabinet enter switch is linked to the P1 circle input as per the manual (bottom of pg 33)
+ PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Cross")
+ PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Triangle")
+ PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_START1 )
+ PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Chin Switch (C)")
+ PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("P2 Joe Switch (J)")
+ PORT_BIT( 0x0c00, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // Must be high to get rid of I/O errors
+ PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Circle")
+ PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Cross")
+ PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Triangle")
+ PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_START2 )
+
+ PORT_START("IN1")
+ PORT_BIT( 0x13ff, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Square")
+ PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Square")
+ PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_COIN1 )
+ PORT_SERVICE( 0x4000, IP_ACTIVE_LOW )
+ PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_SERVICE1 )
+INPUT_PORTS_END
+
ROM_START( aquarush )
ROM_REGION32_LE( 0x00400000, "maincpu:rom", 0 ) /* main prg */
ROM_LOAD16_BYTE( "aq1vera.2l", 0x0000000, 0x200000, CRC(91eb9258) SHA1(30e225eb551bfe1bed6b342dd6d597345d64b677) )
@@ -3338,8 +3485,8 @@ ROM_START( truckk )
ROM_REGION( 0x20000, "iocpu", 0) /* Truck K. I/O board */
ROM_LOAD( "tkk1prg0.ic7", 0x000000, 0x020000, CRC(11fd9c31) SHA1(068b8364ec0eb1e88f9f85f40b8b322876f6f3e2) )
- DISK_REGION( "cdrom" )
- DISK_IMAGE( "tkk2-a", 0, SHA1(6b7c3686b22a508c44f67295b188504b757dd482) )
+ DISK_REGION( "cdxa_pcb:ata:0:cdrom" )
+ DISK_IMAGE_READONLY( "tkk2-a", 0, SHA1(6b7c3686b22a508c44f67295b188504b757dd482) )
ROM_END
ROM_START( technodr )
@@ -3396,6 +3543,31 @@ ROM_START( aplarail )
ROM_LOAD( "at28c16", 0x000000, 0x000800, CRC(db1b63c5) SHA1(01fc3386a2d1cb1bed1b7fd9bd2fd59e503832d3) )
ROM_END
+ROM_START( ujlnow )
+ ROM_REGION32_LE( 0x00400000, "maincpu:rom", 0 ) /* main prg */
+ ROM_LOAD16_BYTE( "ul1vera.2l", 0x000000, 0x200000, CRC(40251bc3) SHA1(377a156d738b29d8f19d9696a261cc6e3470ee3e) )
+ ROM_LOAD16_BYTE( "ul1vera.2p", 0x000001, 0x200000, CRC(98181955) SHA1(febd73361d0de10282a850611b5f966a99eecdec) )
+
+ ROM_REGION32_LE( 0x2000000, "bankedroms", 0 ) /* main data */
+ ROM_LOAD16_BYTE( "ul1rom0l.ic12", 0x0000000, 0x800000, CRC(7a77cffd) SHA1(6bd18837f475615061be73ee6d1bc09726e96fb5) )
+ ROM_LOAD16_BYTE( "ul1rom0u.ic11", 0x0000001, 0x800000, CRC(1fdb34b3) SHA1(3773b3fb1574a0b4acebfaf510c75fa117394ae4) )
+ ROM_LOAD16_BYTE( "ul1rom1l.ic10", 0x1000000, 0x800000, CRC(2d300d8c) SHA1(eabd0b4525530e5b33a6d85fa4d959e88c6fbd42) )
+ ROM_LOAD16_BYTE( "ul1rom1u.ic9", 0x1000001, 0x800000, CRC(a4762002) SHA1(0faadc04bcc32e4f805069af8e635765a0b77ca9) )
+
+ ROM_REGION( 0x0080000, "sub", 0 ) /* sound prg */
+ ROM_LOAD16_WORD_SWAP( "ul1vera.11s", 0x000000, 0x080000, CRC(2f466df3) SHA1(6468089c6cf3e056a30fa04274056ff2c9a01d5b) )
+
+ ROM_REGION( 0x1000000, "c352", 0 ) /* samples */
+ ROM_LOAD( "ul1wave0.ic2", 0x0000000, 0x800000, CRC(4efbff5d) SHA1(add4caf9aa9443707bcb6cf9ebad0a5a59900ca9) )
+ ROM_LOAD( "ul1wave1.ic1", 0x0800000, 0x800000, CRC(ad512f4c) SHA1(cfbecaad13a2239be0e697f3f9856401f11a1bc5) )
+
+ ROM_REGION( 0x80000, "m148", 0) /* M148 EMI DRIVE PCB */
+ ROM_LOAD( "ul1spro.10c", 0x000000, 0x080000, CRC(1bd0e763) SHA1(07e9ad77bb983fcb4e4e6f66fbe55808b5a27d2e) )
+
+ DISK_REGION( "cdxa_pcb:ata:0:cdrom" )
+ DISK_IMAGE_READONLY( "ul1-a", 0, SHA1(676f2c530f8d422ab9895ea04a43b2e9272fb8f8) )
+ROM_END
+
} // anonymous namespace
@@ -3446,13 +3618,14 @@ GAME( 1999, ohbakyuun, ghlpanic, ptblank2, ghlpanic, namcos12_boothack_state, i
GAME( 1999, pacapp2, 0, coh700, namcos12, namcos12_boothack_state, init_namcos12, ROT0, "Produce / Namco", "Paca Paca Passion 2 (Japan, PKS1/VER.A)", 0 ) /* KC046 */
GAME( 1999, mrdrillr, 0, coh700, namcos124w,namcos12_boothack_state, init_namcos12, ROT0, "Namco", "Mr. Driller (US, DRI3/VER.A2)", 0 ) /* KC048 */
GAME( 1999, mrdrillrj, mrdrillr, coh700, namcos124w,namcos12_boothack_state, init_namcos12, ROT0, "Namco", "Mr. Driller (Japan, DRI1/VER.A2)", 0 ) /* KC048 */
+GAME( 1999, ujlnow, 0, cdxa_pcb, ujlnow, namcos12_cdxa_state, init_alt_bank1,ROT0, "Namco", "Um Jammer Lammy NOW! (Japan, UL1/VER.A)", 0 ) /* KC049 */
GAME( 1999, kaiunqz, 0, coh700, namcos12, namcos12_state, init_alt_bank1,ROT0, "Namco", "Kaiun Quiz (Japan, KW1/VER.A)", 0 ) /* KC050 */
GAME( 1999, pacappsp, 0, coh700, namcos12, namcos12_boothack_state, init_namcos12, ROT0, "Produce / Namco", "Paca Paca Passion Special (Japan, PSP1/VER.A)", 0 ) /* KC052 */
GAME( 1999, aquarush, 0, coh700, namcos12, namcos12_state, init_namcos12, ROT0, "Namco", "Aqua Rush (Japan, AQ1/VER.A1)", 0 ) /* KC053 */
GAME( 1999, golgo13, 0, golgo13, golgo13, namcos12_boothack_state, init_alt_bank1,ROT0, "Eighting / Raizing / Namco", "Golgo 13 (Japan, GLG1/VER.A)", 0 ) /* KC054 */
GAME( 2000, g13knd, 0, golgo13, golgo13, namcos12_boothack_state, init_alt_bank1,ROT0, "Eighting / Raizing / Namco", "Golgo 13 Kiseki no Dandou (Japan, GLS1/VER.A)", 0 ) /* KC059 */
GAME( 2000, sws2000, 0, coh700, namcos12, namcos12_boothack_state, init_namcos12, ROT0, "Namco", "Super World Stadium 2000 (Japan, SS01/VER.A)", MACHINE_NOT_WORKING ) /* KC055 */
-GAME( 2000, truckk, 0, truckk, namcos12, namcos12_boothack_state, init_alt_bank1,ROT0, "Metro / Namco", "Truck Kyosokyoku (Japan, TKK2/VER.A)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* KC056 */
+GAME( 2000, truckk, 0, truckk, truckk, namcos12_cdxa_state, init_truckk, ROT0, "Metro / Namco", "Truck Kyosokyoku (Japan, TKK2/VER.A)", MACHINE_IMPERFECT_SOUND ) /* KC056 */
GAME( 2000, kartduel, 0, kartduel, kartduel, namcos12_boothack_state, init_namcos12, ROT0, "Gaps / Namco", "Kart Duel (World, KTD2/VER.A)", MACHINE_NOT_WORKING ) /* KC057 */
GAME( 2000, kartduelj, kartduel, kartduel, kartduel, namcos12_boothack_state, init_namcos12, ROT0, "Gaps / Namco", "Kart Duel (Japan, KTD1/VER.A)", MACHINE_NOT_WORKING ) /* KC057 */
GAME( 2001, sws2001, sws2000, coh716, namcos12, namcos12_boothack_state, init_alt_bank1,ROT0, "Namco", "Super World Stadium 2001 (Japan, SS11/VER.A)", MACHINE_NOT_WORKING ) /* KC061 */
diff --git a/src/mame/namco/namcos12_cdxa.cpp b/src/mame/namco/namcos12_cdxa.cpp
new file mode 100644
index 00000000000..d279b47bb6c
--- /dev/null
+++ b/src/mame/namco/namcos12_cdxa.cpp
@@ -0,0 +1,256 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ Namco System 12 CDXA PCB
+
+***************************************************************************/
+
+#include "emu.h"
+#include "namcos12_cdxa.h"
+
+#include "bus/ata/atapicdr.h"
+
+// Any drive as long as the ident name starts with "TOSHIB" will do, but this is the one that's used with CDXA games specifically
+DECLARE_DEVICE_TYPE(TOSHIBA_XM6402B_CDROM, toshiba_xm6402b_cdrom_device)
+
+class toshiba_xm6402b_cdrom_device : public atapi_fixed_cdrom_device
+{
+public:
+ toshiba_xm6402b_cdrom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : atapi_fixed_cdrom_device(mconfig, TOSHIBA_XM6402B_CDROM, tag, owner, clock)
+ {
+ }
+
+ virtual void device_start() override
+ {
+ atapi_fixed_cdrom_device::device_start();
+
+ std::fill_n(&m_identify_buffer[27], ' ', 47 - 27);
+
+ const char cdrom_ident_name[] = "TOSHIBA CD-ROM XM-6402B ";
+ for (int i = 0; i < strlen(cdrom_ident_name) / 2; i++)
+ m_identify_buffer[27 + i] = (cdrom_ident_name[i * 2] << 8) | cdrom_ident_name[i * 2 + 1];
+ }
+};
+
+DEFINE_DEVICE_TYPE(TOSHIBA_XM6402B_CDROM, toshiba_xm6402b_cdrom_device, "toshiba_xm6402b_cdrom", "Toshiba XM-6402B CD-ROM")
+
+///
+
+DEFINE_DEVICE_TYPE(NAMCOS12_CDXA, namcos12_cdxa_device, "namcos12_cdxa", "Namco System 12 CDXA PCB")
+
+
+namcos12_cdxa_device::namcos12_cdxa_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, NAMCOS12_CDXA, tag, owner, clock)
+ , m_maincpu(*this, "cdxa_cpu")
+ , m_cram(*this, "cram")
+ , m_sram(*this, "sram")
+ , m_ata(*this, "ata")
+ , m_lc78836m(*this, "lc78836m")
+ , m_mb87078(*this, "mb87078")
+ , m_icd2061a(*this, "icd2061a")
+ , m_psx_int10_cb(*this)
+{
+}
+
+void namcos12_cdxa_device::device_start()
+{
+ save_item(NAME(m_ide_sh2_enabled));
+ save_item(NAME(m_ide_ps1_enabled));
+ save_item(NAME(m_sram_enabled));
+ save_item(NAME(m_psx_int10_busy));
+ save_item(NAME(m_audio_cur_bit));
+ save_item(NAME(m_volume_write_counter));
+ save_item(NAME(m_audio_lrck));
+
+ m_lc78836m->cksl1_w(1); // configure for 448 fs for 37800hz. setting to 0 gives 384 fs to make 44100hz
+ m_lc78836m->cksl2_w(0); // these are all grounded
+ m_lc78836m->fs1_w(0);
+ m_lc78836m->fs2_w(0);
+ m_lc78836m->emp_w(0);
+}
+
+void namcos12_cdxa_device::device_reset()
+{
+ m_ide_sh2_enabled = m_ide_ps1_enabled = false;
+ m_sram_enabled = false;
+ m_psx_int10_busy = false;
+ m_audio_cur_bit = 0;
+ m_volume_write_counter = 0;
+ m_audio_lrck = 1; // start with left channel first
+
+ m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
+}
+
+void namcos12_cdxa_device::device_add_mconfig(machine_config &config)
+{
+ SH2_SH7014(config, m_maincpu, XTAL(14'745'600));
+ m_maincpu->set_addrmap(AS_PROGRAM, &namcos12_cdxa_device::sh7014_map);
+
+ ATA_INTERFACE(config, m_ata).options([] (device_slot_interface &device) { device.option_add("cdrom", TOSHIBA_XM6402B_CDROM); }, "cdrom", nullptr, true);
+
+ LC78836M(config, m_lc78836m, XTAL(16'934'400));
+
+ MB87078(config, m_mb87078);
+ m_mb87078->gain_changed().set(FUNC(namcos12_cdxa_device::mb87078_gain_changed));
+
+ // fudge the input clock for clockgen slightly so the sample rate becomes an even 37800hz instead of 37806hz, otherwise audio has a slight crackle
+ ICD2061A(config, m_icd2061a, 14'742'890);
+ m_icd2061a->vclkout_changed().set([this] (uint32_t clock) {
+ m_maincpu->sci_set_external_clock_period<0>(attotime::from_hz(clock * 16 / 448));
+ m_lc78836m->set_clock(clock / 2);
+ });
+
+ m_maincpu->sci_set_send_full_data_transmit_on_sync_hack<0>(true);
+ m_maincpu->sci_tx_w<0>().set(FUNC(namcos12_cdxa_device::audio_dac_w));
+ m_maincpu->write_portb().set(FUNC(namcos12_cdxa_device::portb_w));
+}
+
+void namcos12_cdxa_device::sh7014_map(address_map &map)
+{
+ map(0x00000000, 0x0003ffff).ram().share("cram"); // 128k x8 SRAM (x2) connected directly to both the SH2 and C448
+ map(0x00400000, 0x0040ffff).ram().share("sram"); // 32k x8 SRAM (x2) connected via the C448 chip
+ map(0x0041601c, 0x0041601f).w(FUNC(namcos12_cdxa_device::trigger_psx_int10_w));
+ // 417000 ?
+ map(0x0041c000, 0x0041c003).nopw().r(FUNC(namcos12_cdxa_device::cdrom_status_flag_r));
+ map(0x00c00000, 0x00c1ffff).rw(FUNC(namcos12_cdxa_device::sh2_cdrom_cs0_r), FUNC(namcos12_cdxa_device::sh2_cdrom_cs0_w));
+
+ // Definitely is cs1 but hooking it up breaks the CD drive state seemingly due to timing issues.
+ // The code sets cs1 control reg to 0xe, causing it go into the software reset routine immediately.
+ // The game's code wants to immediately send a command for IDE_COMMAND_CHECK_POWER_MODE after setting cs1, but MAME rejects
+ // all commands immediately after SRST is set so it gets stuck in an infinite loop waiting for the status register to become
+ // 8 (IDE_STATUS_DRDY) so it can finish sending the rest of the check power mode command.
+ // Things seem to work fine without it being hooked up so this is for documentation purposes.
+ // map(0x00c20000, 0x00c3ffff).rw(FUNC(namcos12_cdxa_device::sh2_cdrom_cs1_r), FUNC(namcos12_cdxa_device::sh2_cdrom_cs1_w));
+}
+
+void namcos12_cdxa_device::reset_sh2_w(uint16_t data)
+{
+ if (data == 1)
+ m_maincpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
+}
+
+uint32_t namcos12_cdxa_device::sh2_ram_r(offs_t offset)
+{
+ auto r = m_sram_enabled ? m_sram : m_cram;
+ return rotl_32(r[offset], 16);
+}
+
+void namcos12_cdxa_device::sh2_ram_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ mem_mask = rotl_32(mem_mask, 16);
+ data = rotl_32(data, 16);
+
+ if (!m_sram_enabled)
+ COMBINE_DATA(&m_cram[offset]);
+
+ COMBINE_DATA(&m_sram[offset]);
+}
+
+void namcos12_cdxa_device::sram_enabled_w(uint16_t data)
+{
+ m_sram_enabled = data != 0;
+}
+
+void namcos12_cdxa_device::ide_sh2_enabled_w(uint16_t data)
+{
+ // The SH2 is allowed to use the IDE device
+ // In the code it's one or the other so this and cdxa_ide_ps1_enabled_w might be mutually exclusive
+ m_ide_sh2_enabled = data != 0;
+}
+
+void namcos12_cdxa_device::ide_ps1_enabled_w(uint16_t data)
+{
+ // The PS1 is allowed to access the CD-ROM directly, uses the 0x1f7e0000-0x1f7e000f registers
+ // truckk does not use this feature so it can't be tested, but the code exists in truckk to use it so its usage can at least be confirmed
+ m_ide_ps1_enabled = data != 0;
+}
+
+void namcos12_cdxa_device::ps1_int10_finished_w(uint16_t data)
+{
+ m_psx_int10_busy = false;
+}
+
+void namcos12_cdxa_device::trigger_psx_int10_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ m_psx_int10_cb(1);
+ m_psx_int10_busy = true;
+}
+
+uint16_t namcos12_cdxa_device::cdrom_status_flag_r(offs_t offset, uint16_t mem_mask)
+{
+ // Needs to return 0x800 for it to signal the interrupt for the PS1
+ // Guessed
+ return m_psx_int10_busy ? 0 : 0x800;
+}
+
+uint16_t namcos12_cdxa_device::sh2_cdrom_cs0_r(offs_t offset, uint16_t mem_mask)
+{
+ return m_ata->cs0_r(offset >> 13, mem_mask);
+}
+
+void namcos12_cdxa_device::sh2_cdrom_cs0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ m_ata->cs0_w(offset >> 13, data, mem_mask);
+}
+
+void namcos12_cdxa_device::volume_w(offs_t offset, uint16_t data)
+{
+ // The data/pins to be held low/high is encoded into the memory address
+ m_volume_write_counter ^= 1;
+ m_mb87078->data_w(m_volume_write_counter, offset & 0xff);
+}
+
+uint16_t namcos12_cdxa_device::cdrom_cs0_r(offs_t offset, uint16_t mem_mask)
+{
+ return m_ata->cs0_r(offset, mem_mask);
+}
+
+void namcos12_cdxa_device::cdrom_cs0_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ m_ata->cs0_w(offset, data, mem_mask);
+}
+
+void namcos12_cdxa_device::mb87078_gain_changed(offs_t offset, uint8_t data)
+{
+ m_lc78836m->set_output_gain(offset, m_mb87078->gain_factor_r(offset));
+}
+
+void namcos12_cdxa_device::audio_dac_w(int state)
+{
+ m_lc78836m->bclk_w(0);
+ m_lc78836m->lrck_w(m_audio_lrck);
+ m_lc78836m->data_w(state);
+ m_lc78836m->bclk_w(1);
+
+ m_audio_cur_bit++;
+
+ if (m_audio_cur_bit >= 16) {
+ m_audio_lrck ^= 1;
+ m_audio_cur_bit = 0;
+ }
+}
+
+void namcos12_cdxa_device::portb_w(uint16_t data)
+{
+ m_lc78836m->mute_w(BIT(data, 6));
+}
+
+void namcos12_cdxa_device::clockgen_w(offs_t offset, uint16_t data)
+{
+ switch (offset) {
+ case 0:
+ m_icd2061a->data_w(0);
+ break;
+ case 1:
+ m_icd2061a->data_w(1);
+ break;
+ case 2:
+ m_icd2061a->clk_w(0);
+ break;
+ case 3:
+ m_icd2061a->clk_w(1);
+ break;
+ }
+}
diff --git a/src/mame/namco/namcos12_cdxa.h b/src/mame/namco/namcos12_cdxa.h
new file mode 100644
index 00000000000..cce65e0b428
--- /dev/null
+++ b/src/mame/namco/namcos12_cdxa.h
@@ -0,0 +1,93 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ Namco System 12 CDXA PCB
+
+***************************************************************************/
+
+#ifndef MAME_NAMCO_NAMCOS12_CDXA_H
+#define MAME_NAMCO_NAMCOS12_CDXA_H
+
+#pragma once
+
+#include "bus/ata/ataintf.h"
+#include "cpu/sh/sh7014.h"
+#include "machine/icd2061a.h"
+#include "machine/mb87078.h"
+#include "sound/lc78836m.h"
+
+
+class namcos12_cdxa_device : public device_t
+{
+public:
+ namcos12_cdxa_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ template <typename... T> void add_route(T &&... args) { m_lc78836m.lookup()->add_route(std::forward<T>(args)...); }
+
+ auto psx_int10_callback() { return m_psx_int10_cb.bind(); }
+
+ void psx_map(address_map &map);
+
+ uint32_t sh2_ram_r(offs_t offset);
+ void sh2_ram_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+ void reset_sh2_w(uint16_t data);
+
+ void ide_sh2_enabled_w(uint16_t data);
+
+ void ide_ps1_enabled_w(uint16_t data);
+
+ void sram_enabled_w(uint16_t data);
+
+ void ps1_int10_finished_w(uint16_t data);
+
+ void volume_w(offs_t offset, uint16_t data);
+
+ void clockgen_w(offs_t offset, uint16_t data);
+
+ uint16_t cdrom_cs0_r(offs_t offset, uint16_t mem_mask = ~0);
+ void cdrom_cs0_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+private:
+ void sh7014_map(address_map &map);
+
+ void audio_dac_w(int state);
+
+ void portb_w(uint16_t data);
+
+ uint16_t cdrom_status_flag_r(offs_t offset, uint16_t mem_mask = ~0);
+ void trigger_psx_int10_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+ uint16_t sh2_cdrom_cs0_r(offs_t offset, uint16_t mem_mask = ~0);
+ void sh2_cdrom_cs0_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+ void mb87078_gain_changed(offs_t offset, uint8_t data);
+
+ required_device<sh2_sh7014_device> m_maincpu;
+ required_shared_ptr<uint32_t> m_cram;
+ required_shared_ptr<uint32_t> m_sram;
+ required_device<ata_interface_device> m_ata;
+ required_device<lc78836m_device> m_lc78836m;
+ required_device <mb87078_device> m_mb87078;
+ required_device <icd2061a_device> m_icd2061a;
+ devcb_write_line m_psx_int10_cb;
+
+ bool m_ide_sh2_enabled, m_ide_ps1_enabled;
+ bool m_sram_enabled;
+ bool m_psx_int10_busy;
+
+ uint8_t m_audio_cur_bit;
+ uint8_t m_audio_lrck;
+
+ uint8_t m_volume_write_counter;
+};
+
+DECLARE_DEVICE_TYPE(NAMCOS12_CDXA, namcos12_cdxa_device)
+
+#endif // MAME_NAMCO_NAMCOS12_CDXA_H