diff options
Diffstat (limited to 'src/mame/machine/dmac_0448.cpp')
-rw-r--r-- | src/mame/machine/dmac_0448.cpp | 189 |
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); +} |