dmac_0266: new device

This commit is contained in:
Patrick Mackinlay 2020-04-16 15:47:37 +07:00
parent 0b6dd07e04
commit 4d679b4b58
2 changed files with 270 additions and 0 deletions

View File

@ -0,0 +1,195 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
/*
* Sony 0266 DMA Controller gate array.
*
* This device is a single-channel DMA controller for the CXD1180 SCSI chip
* (NCR5380 derivative) in Sony NEWS NWS-1[2457]x0 workstations.
*
* Sources:
* - https://github.com/NetBSD/src/blob/trunk/sys/arch/news68k/dev/dmac_0266.h
* - https://github.com/NetBSD/src/blob/trunk/sys/arch/news68k/dev/si.c
*
* TODO:
* - tczero vs interrupt status
* - verify if eop output exists
* - verify map count/width
*/
#include "emu.h"
#include "dmac_0266.h"
#define VERBOSE 0
#include "logmacro.h"
DEFINE_DEVICE_TYPE(DMAC_0266, dmac_0266_device, "dmac_0266", "Sony 0266 DMA Controller")
dmac_0266_device::dmac_0266_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
: device_t(mconfig, DMAC_0266, tag, owner, clock)
, m_bus(*this, finder_base::DUMMY_TAG, -1, 32)
, m_eop(*this)
, m_dma_r(*this)
, m_dma_w(*this)
{
}
void dmac_0266_device::map(address_map &map)
{
map(0x00, 0x03).w(FUNC(dmac_0266_device::control_w));
map(0x04, 0x07).r(FUNC(dmac_0266_device::status_r));
map(0x08, 0x0b).w(FUNC(dmac_0266_device::tcount_w));
map(0x0c, 0x0f).w(FUNC(dmac_0266_device::tag_w));
map(0x10, 0x13).w(FUNC(dmac_0266_device::offset_w));
map(0x14, 0x17).w(FUNC(dmac_0266_device::entry_w));
}
void dmac_0266_device::device_start()
{
m_eop.resolve();
m_dma_r.resolve_safe(0);
m_dma_w.resolve_safe();
save_item(NAME(m_status));
save_item(NAME(m_tcount));
save_item(NAME(m_tag));
save_item(NAME(m_offset));
save_item(NAME(m_map));
save_item(NAME(m_eop_state));
save_item(NAME(m_drq_state));
m_dma_check = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dmac_0266_device::dma_check), this));
m_eop_state = false;
m_drq_state = false;
}
void dmac_0266_device::device_reset()
{
for (u32 &entry : m_map)
entry = 0;
soft_reset();
}
void dmac_0266_device::soft_reset()
{
// soft reset does not clear map entries
m_status = 0;
m_tcount = 0;
m_tag = 0;
m_offset = 0;
set_eop(false);
m_dma_check->enable(false);
}
void dmac_0266_device::drq_w(int state)
{
m_drq_state = bool(state);
if (m_drq_state)
m_dma_check->adjust(attotime::zero);
}
void dmac_0266_device::set_eop(bool eop_state)
{
if (eop_state != m_eop_state)
{
m_eop_state = eop_state;
m_eop(eop_state);
}
}
void dmac_0266_device::control_w(u32 data)
{
LOG("control_w 0x%08x (%s)\n", data, machine().describe_context());
if (!(data & RESET))
{
if ((data ^ m_status) & ENABLE)
{
if (data & ENABLE)
{
LOG("transfer started address 0x%08x count 0x%x\n",
(m_map[m_tag & 0x7f] << 12) | (m_offset & 0xfff), m_tcount);
m_dma_check->adjust(attotime::zero);
}
else
m_dma_check->enable(false);
}
m_status = data & (ENABLE | DIRECTION);
}
else
soft_reset();
}
void dmac_0266_device::dma_check(void *ptr, s32 param)
{
// check drq active
if (!m_drq_state)
return;
// check enabled
if (!(m_status & ENABLE))
return;
// check transfer count
if (!m_tcount)
return;
u32 const address = u32(m_map[m_tag & 0x7f]) << 12 | (m_offset & 0xfff);
// assert eop during last transfer
if (m_tcount == 1)
set_eop(true);
// perform dma transfer
if (m_status & DIRECTION)
{
// device to memory
u8 const data = m_dma_r();
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(data);
}
// increment offset
if ((m_offset & 0xfff) == 0xfff)
{
// advance to next page
m_tag++;
m_offset = 0;
}
else
m_offset++;
// decrement count
m_tcount--;
// set terminal count flag
if (!m_tcount)
{
LOG("transfer complete\n");
m_status &= ~ENABLE;
m_status |= INTERRUPT | TCZERO;
set_eop(false);
}
else
m_dma_check->adjust(attotime::zero);
}

View File

@ -0,0 +1,75 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
#ifndef MAME_MACHINE_DMAC_0266_H
#define MAME_MACHINE_DMAC_0266_H
#pragma once
class dmac_0266_device : public device_t
{
public:
dmac_0266_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
// configuration
template <typename T> void set_bus(T &&tag, int spacenum) { m_bus.set_tag(std::forward<T>(tag), spacenum); }
auto out_eop_cb() { return m_eop.bind(); }
auto dma_r_cb() { return m_dma_r.bind(); }
auto dma_w_cb() { return m_dma_w.bind(); }
void map(address_map &map);
void drq_w(int state);
protected:
// device_t overrides
virtual void device_start() override;
virtual void device_reset() override;
// register handlers
void control_w(u32 data);
u32 status_r() { return m_status; }
void tcount_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_tcount); }
void tag_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_tag); }
void offset_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_offset); }
void entry_w(offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_map[m_tag & 0x7f]); }
// dma logic
void soft_reset();
void set_eop(bool eop_state);
void dma_check(void *ptr, s32 param);
private:
required_address_space m_bus;
devcb_write_line m_eop;
devcb_read8 m_dma_r;
devcb_write8 m_dma_w;
emu_timer *m_dma_check;
enum status_mask : u32
{
ENABLE = 0x01,
DIRECTION = 0x02,
RESET = 0x04,
INTERRUPT = 0x08,
TCZERO = 0x10,
};
// registers
u32 m_status;
u32 m_tcount;
u32 m_tag;
u32 m_offset;
u32 m_map[128];
// internal state
bool m_eop_state;
bool m_drq_state;
};
DECLARE_DEVICE_TYPE(DMAC_0266, dmac_0266_device)
#endif // MAME_MACHINE_DMAC_0266_H