summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/dmac_0448.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/dmac_0448.cpp')
-rw-r--r--src/mame/machine/dmac_0448.cpp189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/mame/machine/dmac_0448.cpp b/src/mame/machine/dmac_0448.cpp
new file mode 100644
index 00000000000..578c09fafd8
--- /dev/null
+++ b/src/mame/machine/dmac_0448.cpp
@@ -0,0 +1,189 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * Sony NEWS DMAC 0448 device.
+ *
+ * Sources:
+ * - https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/dev/dmac_0448.h
+ *
+ * TODO:
+ * - 16 and 32 bit transfers
+ * - terminal count handling
+ * - save state
+ */
+
+#include "emu.h"
+#include "dmac_0448.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(DMAC_0448, dmac_0448_device, "dmac_0448", "Sony DMA Controller 0448")
+
+dmac_0448_device::dmac_0448_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, DMAC_0448, tag, owner, clock)
+ , m_bus(*this, finder_base::DUMMY_TAG, -1, 32)
+ , m_out_int(*this)
+ , m_dma_r(*this)
+ , m_dma_w(*this)
+{
+}
+
+void dmac_0448_device::map(address_map &map)
+{
+ map(0x2, 0x2).r(FUNC(dmac_0448_device::cstat_r));
+ map(0x3, 0x3).w(FUNC(dmac_0448_device::cctl_w));
+ map(0x4, 0x4).rw(FUNC(dmac_0448_device::ctrcl_r), FUNC(dmac_0448_device::ctrcl_w));
+ map(0x5, 0x5).rw(FUNC(dmac_0448_device::ctrcm_r), FUNC(dmac_0448_device::ctrcm_w));
+ map(0x6, 0x6).rw(FUNC(dmac_0448_device::ctrch_r), FUNC(dmac_0448_device::ctrch_w));
+ map(0x7, 0x7).rw(FUNC(dmac_0448_device::ctag_r), FUNC(dmac_0448_device::ctag_w));
+ map(0x8, 0x8).rw(FUNC(dmac_0448_device::cwid_r), FUNC(dmac_0448_device::cwid_w));
+ map(0x9, 0x9).rw(FUNC(dmac_0448_device::cofsl_r), FUNC(dmac_0448_device::cofsl_w));
+ map(0xa, 0xa).rw(FUNC(dmac_0448_device::cofsh_r), FUNC(dmac_0448_device::cofsh_w));
+
+ map(0xc, 0xd).rw(FUNC(dmac_0448_device::cmap_r), FUNC(dmac_0448_device::cmap_w));
+ map(0xe, 0xe).w(FUNC(dmac_0448_device::gsel_w));
+ map(0xf, 0xf).r(FUNC(dmac_0448_device::gstat_r));
+}
+
+void dmac_0448_device::device_start()
+{
+ m_out_int.resolve();
+
+ m_dma_r.resolve_all_safe(0);
+ m_dma_w.resolve_all_safe();
+
+ m_irq_check = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dmac_0448_device::irq_check), this));
+ m_dma_check = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dmac_0448_device::dma_check), this));
+
+ m_out_int_state = false;
+ m_gsel = 0;
+ m_gstat = 0;
+}
+
+void dmac_0448_device::device_reset()
+{
+ m_irq_check->adjust(attotime::zero);
+}
+
+void dmac_0448_device::set_irq_line(int number, int state)
+{
+ u8 const mask = 1U << (number * 2);
+
+ if (state)
+ m_gstat |= mask;
+ else
+ m_gstat &= ~mask;
+
+ m_irq_check->adjust(attotime::zero);
+}
+
+void dmac_0448_device::irq_check(void *ptr, s32 param)
+{
+ bool const out_int_stat = bool(m_gstat & 0x55);
+
+ if (out_int_stat != m_out_int_state)
+ {
+ m_out_int_state = out_int_stat;
+ m_out_int(m_out_int_state);
+ }
+}
+
+void dmac_0448_device::set_drq_line(int channel, int state)
+{
+ u8 const mask = 1U << ((channel * 2) + 1);
+
+ if (state)
+ m_gstat |= mask;
+ else
+ m_gstat &= ~mask;
+
+ if (state)
+ m_dma_check->adjust(attotime::zero);
+}
+
+void dmac_0448_device::cctl_w(u8 data)
+{
+ if ((data & CS_ENABLE) && !(m_channel[m_gsel].cctl & CS_ENABLE))
+ {
+ LOG("transfer started address 0x%08x count 0x%x\n",
+ u32(m_channel[m_gsel].cmap[m_channel[m_gsel].ctag]) << 12 | m_channel[m_gsel].cofs, m_channel[m_gsel].ctrc);
+ }
+ m_channel[m_gsel].cctl = data;
+
+ m_dma_check->adjust(attotime::zero);
+}
+
+void dmac_0448_device::dma_check(void *ptr, s32 param)
+{
+ bool active = false;
+
+ for (unsigned channel = 0; channel < 4; channel++)
+ {
+ // check drq active
+ if (!BIT(m_gstat, (channel * 2) + 1))
+ continue;
+
+ dma_channel &dma = m_channel[channel];
+
+ // check channel enabled
+ if (!(dma.cctl & CS_ENABLE))
+ return;
+
+ // check transfer count
+ if (!dma.ctrc)
+ return;
+
+ // TODO: confirm if this is correct
+ u32 const address = u32(dma.cmap[dma.ctag]) << 12 | dma.cofs;
+
+ // perform dma transfer
+ if (dma.cctl & CS_MODE)
+ {
+ // device to memory
+ u8 const data = m_dma_r[channel]();
+
+ LOG("dma_r data 0x%02x address 0x%08x\n", data, address);
+
+ m_bus->write_byte(address, data);
+ }
+ else
+ {
+ // memory to device
+ u8 const data = m_bus->read_byte(address);
+
+ LOG("dma_w data 0x%02x address 0x%08x\n", data, address);
+
+ m_dma_w[channel](data);
+ }
+
+ // increment offset
+ if (dma.cofs == 0xfff)
+ {
+ // advance to next page
+ dma.cofs = 0;
+ dma.ctag++;
+ }
+ else
+ dma.cofs++;
+
+ // decrement count
+ dma.ctrc--;
+
+ // set terminal count flag
+ if (!dma.ctrc)
+ {
+ LOG("transfer complete\n");
+ dma.cstat |= CS_TCZ;
+
+ // TODO: terminal count interrupt?
+ }
+
+ if (BIT(m_gstat, (channel * 2) + 1))
+ active = true;
+ }
+
+ if (active)
+ m_dma_check->adjust(attotime::zero);
+}