summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/am9516.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/am9516.cpp')
-rw-r--r--src/devices/machine/am9516.cpp1027
1 files changed, 1027 insertions, 0 deletions
diff --git a/src/devices/machine/am9516.cpp b/src/devices/machine/am9516.cpp
new file mode 100644
index 00000000000..451a436511c
--- /dev/null
+++ b/src/devices/machine/am9516.cpp
@@ -0,0 +1,1027 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * Am9516 Universal DMA Controller (UDC)
+ *
+ * Am9516A appears to be identical to the Am9516 but available in faster
+ * clock speeds.
+ *
+ * It seems likely that the Am9516 was derived from the Z8016. The latter
+ * supports Z80-style interrupt daisy chains, and has some additional
+ * functionality for dealing with Z8000-family segmented memory.
+ *
+ *
+ * Sources:
+ * - Personal Computer Products Data Book, © 1989 Advanced Micro Devices
+ *
+ * TODO:
+ * - search modes
+ * - chain load abort
+ * - hardware masks
+ */
+
+#include "emu.h"
+#include "am9516.h"
+
+#define LOG_REGR (1U << 1)
+#define LOG_REGW (1U << 2)
+#define LOG_COMMAND (1U << 3)
+#define LOG_DMA (1U << 4)
+
+//#define VERBOSE (LOG_GENERAL|LOG_REGR|LOG_REGW|LOG_COMMAND|LOG_DMA)
+#include "logmacro.h"
+
+enum master_mode_mask : u8
+{
+ MM0 = 1, // chip enable
+ MM1 = 2, // cpu interleave enable
+ MM2 = 4, // wait line enable
+ MM3 = 8, // no vector on interrupt
+};
+
+enum chain_control_mask : u16
+{
+ CC_CA = 0x0001, // chain address
+ CC_CM = 0x0002, // channel mode
+ CC_IV = 0x0004, // interrupt vector
+ CC_PM = 0x0008, // pattern and mask
+ CC_BOC = 0x0010, // base operation count
+ CC_BAB = 0x0020, // base address b
+ CC_BAA = 0x0040, // base address a
+ CC_COC = 0x0080, // current operation count
+ CC_CAB = 0x0100, // current address b
+ CC_CAA = 0x0200, // current address a
+
+ CC_WM = 0x03ff,
+};
+
+enum status_mask : u16
+{
+ S_TC = 0x0001, // terminal count
+ S_EOP = 0x0002, // end of process
+ S_MC = 0x0004, // match condition
+ S_MCL = 0x0008, // match condition low
+ S_MCH = 0x0010, // match condition high
+ S_HRQ = 0x0020, // hardware request
+ S_HM = 0x0040, // hardware mask
+ // reserved
+ // reserved
+ S_SIP = 0x0200, // second interrupt pending
+ S_WFB = 0x0400, // waiting for bus
+ S_NAC = 0x0800, // no auto-reload or chaining
+ S_CA = 0x1000, // chaining abort
+ S_IP = 0x2000, // interrupt pending
+ // reserved
+ S_CIE = 0x8000, // channel interrupt enable
+};
+
+enum cmh_mask : u16
+{
+ CMH_MC = 0x0003, // match control
+ CMH_DC = 0x0004, // dack control
+ CMH_HM = 0x0008, // hardware mask
+ CMH_SR = 0x0010, // software request
+
+ CMH_WM = 0x001f,
+};
+
+enum cmh_mc_mask : u16
+{
+ MC_00 = 0x0000, // stop on no match
+ MC_01 = 0x0020, // stop on no match
+ MC_10 = 0x0040, // stop on word match
+ MC_11 = 0x0060, // stop on byte match
+};
+
+enum cml_mask : u16
+{
+ CML_OPER = 0x000f, // operation
+ CML_FLIP = 0x0010, // flip bit
+ CML_TT = 0x0060, // transfer type
+ CML_IEOP = 0x0080, // interrupt enable - end of process
+ CML_IMC = 0x0100, // interrupt enable - match condition
+ CML_ITC = 0x0200, // interrupt enable - terminal count
+ CML_REOP = 0x0400, // reload enable - end of process
+ CML_RMC = 0x0800, // reload enable - match condition
+ CML_RTC = 0x1000, // reload enable - terminal count
+ CML_CEOP = 0x2000, // chain enable - end of process
+ CML_CMC = 0x4000, // chain enable - match condition
+ CML_CTC = 0x8000, // chain enable - terminal count
+};
+
+enum cml_tt_mask : u16
+{
+ TT_00 = 0x0000, // single transfer
+ TT_01 = 0x0020, // demand dedicated bus hold
+ TT_10 = 0x0040, // demand dedicated bus release
+ TT_11 = 0x0060, // demand interleave
+};
+
+enum aru_mask : u16
+{
+ ARU_WC = 0x0006, // wait control
+ ARU_AC = 0x0018, // address control
+ ARU_MI = 0x0040, // memory or i/o space
+ ARU_NS = 0x0080, // normal or system space
+ ARU_AR = 0x00c0, // address reference
+ ARU_UA = 0xff00, // upper address
+
+ ARU_WM = 0xffde,
+};
+
+enum aru_ac_mask : u16
+{
+ AC_INC = 0x0000, // increment
+ AC_DEC = 0x0008, // decrement
+ AC_HLD = 0x0010, // hold
+};
+
+enum is_mask : u16
+{
+ IS_VEC = 0x00ff, // vector
+ IS_CHN = 0x0100, // channel
+ IS_TC = 0x0200, // terminal count
+ IS_EOP = 0x0400, // end of process
+ IS_MC = 0x0800, // match condition
+ IS_CA = 0x1000, // chain aborted
+ IS_MCL = 0x2000, // match condition low
+ IS_MCH = 0x4000, // match condition high
+ IS_HRQ = 0x8000, // hardware request
+};
+
+DEFINE_DEVICE_TYPE(AM9516, am9516_device, "am9516", "Am9516 Universal DMA Controller")
+
+am9516_device::am9516_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, AM9516, tag, owner, clock)
+ , device_memory_interface(mconfig, *this)
+ , m_space_config{
+ { "system_io", ENDIANNESS_BIG, 16, 24 },
+ { "system_mem", ENDIANNESS_BIG, 16, 24 },
+ { "normal_io", ENDIANNESS_BIG, 16, 24 },
+ { "normal_mem", ENDIANNESS_BIG, 16, 24 } }
+ , m_int(*this)
+ , m_eop(*this)
+ , m_int_state(true)
+ , m_eop_out_state(true)
+ , m_eop_in_state(false)
+ , m_pointer(0)
+ , m_channel{ *this, *this }
+{
+}
+
+void am9516_device::device_start()
+{
+ save_item(NAME(m_int_state));
+ save_item(NAME(m_eop_out_state));
+ save_item(NAME(m_eop_in_state));
+
+ save_item(NAME(m_mode));
+ save_item(NAME(m_pointer));
+ save_item(NAME(m_temporary));
+
+ save_item(STRUCT_MEMBER(m_channel, cabl));
+ save_item(STRUCT_MEMBER(m_channel, babl));
+ save_item(STRUCT_MEMBER(m_channel, caal));
+ save_item(STRUCT_MEMBER(m_channel, baal));
+ save_item(STRUCT_MEMBER(m_channel, cabu));
+ save_item(STRUCT_MEMBER(m_channel, babu));
+ save_item(STRUCT_MEMBER(m_channel, caau));
+ save_item(STRUCT_MEMBER(m_channel, baau));
+ save_item(STRUCT_MEMBER(m_channel, cal));
+ save_item(STRUCT_MEMBER(m_channel, cau));
+ save_item(STRUCT_MEMBER(m_channel, is));
+ save_item(STRUCT_MEMBER(m_channel, status));
+ save_item(STRUCT_MEMBER(m_channel, coc));
+ save_item(STRUCT_MEMBER(m_channel, boc));
+ save_item(STRUCT_MEMBER(m_channel, pattern));
+ save_item(STRUCT_MEMBER(m_channel, mask));
+ save_item(STRUCT_MEMBER(m_channel, cml));
+ save_item(STRUCT_MEMBER(m_channel, cmh));
+ save_item(STRUCT_MEMBER(m_channel, iv));
+
+ for (channel &ch : m_channel)
+ {
+ ch.cabl = 0;
+ ch.babl = 0;
+ ch.caal = 0;
+ ch.baal = 0;
+ ch.cabu = 0;
+ ch.babu = 0;
+ ch.caau = 0;
+ ch.baau = 0;
+ ch.cal = 0;
+ ch.cau = 0;
+ ch.status = 0;
+ ch.coc = 0;
+ ch.boc = 0;
+ ch.pattern = 0;
+ ch.mask = 0;
+ ch.cml = 0;
+ ch.cmh = 0;
+ ch.iv = 0;
+ }
+
+ m_channel[0].is = 0;
+ m_channel[1].is = IS_CHN;
+
+ m_channel[0].run = timer_alloc(timer_expired_delegate(FUNC(am9516_device::operate<0>), this));
+ m_channel[1].run = timer_alloc(timer_expired_delegate(FUNC(am9516_device::operate<1>), this));
+}
+
+void am9516_device::device_reset()
+{
+ m_mode = 0;
+
+ for (channel &ch : m_channel)
+ {
+ ch.run->reset();
+
+ ch.status = S_CA | S_NAC;
+ ch.is &= IS_CHN;
+ }
+
+ if (m_eop_out_state)
+ {
+ m_eop_out_state = false;
+ eop_w(!m_eop_out_state);
+ }
+
+ interrupt();
+}
+
+device_memory_interface::space_config_vector am9516_device::memory_space_config() const
+{
+ return space_config_vector{
+ std::make_pair(SYSTEM_IO, &m_space_config[0]),
+ std::make_pair(SYSTEM_MEM, &m_space_config[1]),
+ std::make_pair(NORMAL_IO, &m_space_config[2]),
+ std::make_pair(NORMAL_MEM, &m_space_config[3]),
+ };
+}
+
+u16 am9516_device::data_r()
+{
+ switch (m_pointer)
+ {
+ // address b lower
+ case 0x00: return m_channel[1].cabl;
+ case 0x02: return m_channel[0].cabl;
+ case 0x04: return m_channel[1].babl;
+ case 0x06: return m_channel[0].babl;
+ // address a lower
+ case 0x08: return m_channel[1].caal;
+ case 0x0a: return m_channel[0].caal;
+ case 0x0c: return m_channel[1].baal;
+ case 0x0e: return m_channel[0].baal;
+ // address b upper
+ case 0x10: return m_channel[1].cabu;
+ case 0x12: return m_channel[0].cabu;
+ case 0x14: return m_channel[1].babu;
+ case 0x16: return m_channel[0].babu;
+ // address a upper
+ case 0x18: return m_channel[1].caau;
+ case 0x1a: return m_channel[0].caau;
+ case 0x1c: return m_channel[1].baau;
+ case 0x1e: return m_channel[0].baau;
+ // chain address
+ case 0x20: return m_channel[1].cal;
+ case 0x22: return m_channel[0].cal;
+ case 0x24: return m_channel[1].cau;
+ case 0x26: return m_channel[0].cau;
+ // interrupt save
+ case 0x28: return m_channel[1].is;
+ case 0x2a: return m_channel[0].is;
+ // status
+ case 0x2c: return m_channel[1].status;
+ case 0x2e: return m_channel[0].status;
+ // operation count
+ case 0x30: return m_channel[1].coc;
+ case 0x32: return m_channel[0].coc;
+ case 0x34: return m_channel[1].boc;
+ case 0x36: return m_channel[0].boc;
+ // master mode
+ case 0x38: return m_mode;
+ // pattern
+ case 0x48: return m_channel[1].pattern;
+ case 0x4a: return m_channel[0].pattern;
+ // mask
+ case 0x4c: return m_channel[1].mask;
+ case 0x4e: return m_channel[0].mask;
+ // channel mode
+ case 0x50: return m_channel[1].cml;
+ case 0x52: return m_channel[0].cml;
+ case 0x54: return m_channel[1].cmh;
+ case 0x56: return m_channel[0].cmh;
+ // interrupt vector
+ case 0x58: return m_channel[1].iv;
+ case 0x5a: return m_channel[0].iv;
+
+ default:
+ LOG("undefined register 0x%02 (%s)\n",
+ m_pointer, machine().describe_context());
+ return 0;
+ }
+}
+
+void am9516_device::data_w(u16 data)
+{
+ switch (m_pointer)
+ {
+ // address b lower
+ case 0x00: m_channel[1].cabl = data; break;
+ case 0x02: m_channel[0].cabl = data; break;
+ case 0x04: m_channel[1].babl = data; break;
+ case 0x06: m_channel[0].babl = data; break;
+ // address a lower
+ case 0x08: m_channel[1].caal = data; break;
+ case 0x0a: m_channel[0].caal = data; break;
+ case 0x0c: m_channel[1].baal = data; break;
+ case 0x0e: m_channel[0].baal = data; break;
+ // address b upper
+ case 0x10: m_channel[1].cabu = data & ARU_WM; break;
+ case 0x12: m_channel[0].cabu = data & ARU_WM; break;
+ case 0x14: m_channel[1].babu = data & ARU_WM; break;
+ case 0x16: m_channel[0].babu = data & ARU_WM; break;
+ // address a upper
+ case 0x18: m_channel[1].caau = data & ARU_WM; break;
+ case 0x1a: m_channel[0].caau = data & ARU_WM; break;
+ case 0x1c: m_channel[1].baau = data & ARU_WM; break;
+ case 0x1e: m_channel[0].baau = data & ARU_WM; break;
+ // chain address
+ case 0x20: m_channel[1].cal = data; break;
+ case 0x22: m_channel[0].cal = data; break;
+ case 0x24: m_channel[1].cau = data & (ARU_UA | ARU_WC); break;
+ case 0x26: m_channel[0].cau = data & (ARU_UA | ARU_WC); break;
+ // command
+ case 0x2c:
+ case 0x2e:
+ command(data);
+ return;
+ // operation count
+ case 0x30: m_channel[1].coc = data; break;
+ case 0x32: m_channel[0].coc = data; break;
+ case 0x34: m_channel[1].boc = data; break;
+ case 0x36: m_channel[0].boc = data; break;
+ // master mode
+ case 0x38:
+ LOGMASKED(LOG_REGW, "master mode 0x%04x (%s)\n",
+ data, machine().describe_context());
+ m_mode = data & 0xf;
+ return;
+ // pattern
+ case 0x48: m_channel[1].pattern = data; break;
+ case 0x4a: m_channel[0].pattern = data; break;
+ // mask
+ case 0x4c: m_channel[1].mask = data; break;
+ case 0x4e: m_channel[0].mask = data; break;
+ // channel mode
+ case 0x50: m_channel[1].cml = data; m_channel[1].log_mode(LOG_GENERAL); break;
+ case 0x52: m_channel[0].cml = data; m_channel[0].log_mode(LOG_GENERAL); break;
+ // interrupt vector
+ case 0x58: m_channel[1].iv = data; break;
+ case 0x5a: m_channel[0].iv = data; break;
+
+ default:
+ LOG("undefined register 0x%02 data 0x%04x (%s)\n",
+ m_pointer, data, machine().describe_context());
+ return;
+ }
+
+ if (VERBOSE & LOG_REGW)
+ {
+ static char const *const reg_name[] =
+ {
+ "current address b lower", "base address b lower",
+ "current address a lower", "base address a lower",
+ "current address b upper", "base address b upper",
+ "current address a upper", "base address a upper",
+ "chain address lower", "chain address upper",
+ "interrupt save", "status",
+ "current operation count", "base operation count",
+ nullptr, nullptr, nullptr, nullptr,
+ "pattern", "mask",
+ "channel mode low", "channel mode high",
+ "interrupt vector", nullptr,
+ };
+
+ LOGMASKED(LOG_REGW, "channel %d %s 0x%04x (%s)\n",
+ !(m_pointer & 2), reg_name[m_pointer >> 2], data, machine().describe_context());
+ }
+}
+
+void am9516_device::command(u8 data)
+{
+ channel &ch = m_channel[BIT(data, 0)];
+
+ switch (data & 0xe0)
+ {
+ case 0x00: // reset
+ LOGMASKED(LOG_COMMAND, "reset (%s)\n", machine().describe_context());
+
+ reset();
+ break;
+
+ case 0x20: // interrupt control
+ LOGMASKED(LOG_COMMAND, "channel %d %s%s%s (%s)\n", BIT(data, 0), BIT(data, 1) ? "set" : "clear",
+ BIT(data, 4) ? " CIE" : "", BIT(data, 2) ? " IP" : "", machine().describe_context());
+
+ // update channel interrupt enable
+ if (BIT(data, 4))
+ {
+ if (BIT(data, 1))
+ ch.status |= S_CIE;
+ else
+ ch.status &= ~S_CIE;
+ }
+
+ // update interrupt pending
+ if (BIT(data, 2))
+ ch.interrupt(BIT(data, 1));
+
+ interrupt();
+ break;
+
+ case 0x40: // software request
+ LOGMASKED(LOG_COMMAND, "channel %d %s software request bit (%s)\n",
+ BIT(data, 0), BIT(data, 1) ? "set" : "clear", machine().describe_context());
+
+ if (BIT(data, 1))
+ {
+ ch.cmh |= CMH_SR;
+ ch.run->adjust(attotime::zero);
+ }
+ else
+ ch.cmh &= ~CMH_SR;
+ break;
+
+ case 0x60: // set/clear flip bit
+ LOGMASKED(LOG_COMMAND, "channel %d %s flip bit (%s)\n",
+ BIT(data, 0), BIT(data, 1) ? "set" : "clear", machine().describe_context());
+
+ if (BIT(data, 1))
+ ch.cml |= CML_FLIP;
+ else
+ ch.cml &= ~CML_FLIP;
+ break;
+
+ case 0x80: // hardware mask
+ LOGMASKED(LOG_COMMAND, "channel %d %s hardware mask bit (%s)\n",
+ BIT(data, 0), BIT(data, 1) ? "set" : "clear", machine().describe_context());
+
+ if (BIT(data, 1))
+ ch.cmh |= CMH_HM;
+ else
+ ch.cmh &= ~CMH_HM;
+ break;
+
+ case 0xa0: // start chain
+ LOGMASKED(LOG_COMMAND, "channel %d start chain (%s)\n",
+ BIT(data, 0), machine().describe_context());
+
+ ch.chain();
+ break;
+
+ default:
+ LOGMASKED(LOG_COMMAND, "channel %d unrecognized command 0x%02x (%s)\n",
+ BIT(data, 0), data, machine().describe_context());
+ break;
+ }
+}
+
+void am9516_device::eop_w(int state)
+{
+ LOGMASKED(LOG_DMA, "eop %s\n", state ? "cleared" : "asserted");
+ m_eop_in_state = !state;
+}
+
+template <unsigned Channel> void am9516_device::dreq_w(int state)
+{
+ LOGMASKED(LOG_DMA, "channel %d dreq %s\n", Channel, state ? "cleared" : "asserted");
+ channel &ch = m_channel[Channel];
+
+ if (!state)
+ {
+ ch.status |= S_HRQ;
+
+ if (!(ch.status & S_HM) && ch.run->expire().is_never())
+ ch.run->adjust(attotime::zero);
+ }
+ else
+ ch.status &= ~S_HRQ;
+}
+
+template void am9516_device::dreq_w<0>(int state);
+template void am9516_device::dreq_w<1>(int state);
+
+template <unsigned Channel> void am9516_device::operate(s32 param)
+{
+ channel &ch = m_channel[Channel];
+
+ if (!(m_mode & MM0) || (ch.status & S_SIP))
+ {
+ LOGMASKED(LOG_DMA, "channel %d bus access disabled\n", Channel);
+ return;
+ }
+
+ if (!(ch.status & S_HRQ) && !(ch.cmh & CMH_SR))
+ {
+ LOGMASKED(LOG_DMA, "channel %d no request pending\n", Channel);
+ return;
+ }
+
+ u16 status = 0;
+ unsigned cycles = 0;
+
+ switch (ch.cml & CML_OPER)
+ {
+ // transfer
+ case 0x1:
+ // byte/byte flowthru
+ cycles = 6;
+ m_temporary = ch.read_byte(cycles, ch.cml & CML_FLIP);
+ ch.write_byte(u8(m_temporary), cycles, ch.cml & CML_FLIP);
+
+ ch.coc--;
+ break;
+ case 0x8:
+ case 0x9:
+ cycles = 9;
+ if (ch.cml & CML_FLIP)
+ {
+ // word/byte flowthru
+ m_temporary = ch.read_word(cycles, ch.cml & CML_FLIP);
+
+ unsigned const shift = ch.cabu & AC_DEC;
+ ch.write_byte(u8(m_temporary >> (8 - shift)), cycles, ch.cml & CML_FLIP);
+ ch.write_byte(u8(m_temporary >> shift), cycles, ch.cml & CML_FLIP);
+ }
+ else
+ {
+ // byte/word flowthru
+ unsigned const shift = ch.cabu & AC_DEC;
+ m_temporary = u16(ch.read_byte(cycles, ch.cml & CML_FLIP)) << (8 - shift);
+ m_temporary |= u16(ch.read_byte(cycles, ch.cml & CML_FLIP)) << shift;
+
+ ch.write_word(m_temporary, cycles, ch.cml & CML_FLIP);
+ }
+
+ ch.coc--;
+ break;
+ case 0x0:
+ // word/word flowthru
+ cycles = 6;
+ m_temporary = ch.read_word(cycles, ch.cml & CML_FLIP);
+ ch.write_word(m_temporary, cycles, ch.cml & CML_FLIP);
+
+ ch.coc--;
+ break;
+ case 0x3:
+ // byte/byte flyby
+ cycles = 3;
+ if (ch.cml & CML_FLIP)
+ // from flyby to arb
+ ch.write_byte(ch.flyby_byte_r(), cycles);
+ else
+ // from ara to flyby
+ ch.flyby_byte_w(ch.read_byte(cycles));
+
+ ch.coc--;
+ break;
+ case 0x2:
+ // word/word flyby
+ cycles = 3;
+ if (ch.cml & CML_FLIP)
+ // from flyby to arb
+ ch.write_word(ch.flyby_word_r(), cycles);
+ else
+ // from ara to flyby
+ ch.flyby_word_w(ch.read_word(cycles));
+
+ ch.coc--;
+ break;
+
+ // transfer and search
+ case 0x5:
+ // byte/byte flowthru
+ break;
+ case 0xc:
+ case 0xd:
+ // byte/word flowthru
+ break;
+ case 0x4:
+ // word/word flowthru
+ break;
+ case 0x7:
+ // byte/byte flyby
+ break;
+ case 0x6:
+ // word/word flyby
+ break;
+
+ // search
+ case 0xf:
+ // byte/byte
+ break;
+ case 0xe:
+ // word/word
+ break;
+ case 0xa:
+ case 0xb:
+ // illegal
+ break;
+ }
+
+ // check for terminal count
+ if (ch.coc == 0)
+ status |= S_TC;
+
+ // check for end of process
+ if (m_eop_in_state)
+ status |= S_EOP;
+
+ if (status & (S_MC | S_EOP | S_TC))
+ complete(param, status);
+ else if (ch.cml & CML_TT)
+ ch.run->adjust(attotime::from_ticks(cycles, clock()));
+}
+
+void am9516_device::complete(unsigned const c, u16 status)
+{
+ channel &ch = m_channel[c];
+
+ ch.status &= ~(S_MCH | S_MCL | S_MC | S_EOP | S_TC);
+ ch.status |= status | S_NAC;
+
+ LOGMASKED(LOG_DMA, "channel %d complete status 0x%04x\n", c, ch.status);
+
+ m_eop(0);
+ m_eop(1);
+
+ // completion interrupt
+ if (((ch.status & S_TC) && (ch.cml & CML_ITC))
+ || ((ch.status & S_EOP) && (ch.cml & CML_IEOP))
+ || ((ch.status & S_MC) && (ch.cml & CML_IMC)))
+ {
+ ch.interrupt(true);
+
+ interrupt();
+ }
+
+ if (!(ch.status & S_SIP))
+ ch.reload();
+}
+
+void am9516_device::interrupt()
+{
+ bool const int_state =
+ ((m_channel[0].status & S_CIE) && (m_channel[0].status & S_IP)) ||
+ ((m_channel[1].status & S_CIE) && (m_channel[1].status & S_IP));
+
+ if (m_int_state != int_state)
+ {
+ LOG("interrupt %s\n", int_state ? "asserted" : "cleared");
+ m_int_state = int_state;
+ m_int(!m_int_state);
+ }
+}
+
+u16 am9516_device::acknowledge()
+{
+ for (channel &ch : m_channel)
+ {
+ if ((ch.status & S_CIE) && (ch.status & S_IP))
+ {
+ u16 const data = (m_mode & MM3) ? 0 : ch.is;
+
+ ch.interrupt(false);
+ interrupt();
+
+ return data;
+ }
+ }
+
+ fatalerror("%s: interrupt acknowledge with no pending interrupts\n", tag());
+}
+
+#undef LOG_OUTPUT_FUNC
+#define LOG_OUTPUT_FUNC udc.logerror
+
+u32 am9516_device::channel::address(u16 &aru, u16 &arl, int delta)
+{
+ u32 const current = u32(aru & ARU_UA) << 8 | arl;
+
+ if (!delta || (aru & AC_HLD))
+ return current;
+
+ if (!(aru & ARU_MI))
+ delta = 2;
+
+ if (aru & AC_DEC)
+ delta = -delta;
+
+ u32 const adjusted = current + delta;
+
+ aru = ((adjusted >> 8) & ARU_UA) | (aru & ~ARU_UA);
+ arl = u16(adjusted);
+
+ return current;
+}
+
+u8 am9516_device::channel::read_byte(unsigned &cycles, bool flip)
+{
+ u16 &cau = flip ? cabu : caau;
+ u16 &cal = flip ? cabl : caal;
+
+ cycles += wait_states[BIT(cau, 1, 2)];
+
+ return udc.space((cau & ARU_AR) >> 6).read_byte(address(cau, cal, 1));
+}
+
+void am9516_device::channel::write_byte(u8 data, unsigned &cycles, bool flip)
+{
+ u16 &cau = flip ? caau : cabu;
+ u16 &cal = flip ? caal : cabl;
+
+ cycles += wait_states[BIT(cau, 1, 2)];
+
+ udc.space((cau & ARU_AR) >> 6).write_byte(address(cau, cal, 1), data);
+}
+
+u16 am9516_device::channel::read_word(unsigned &cycles, bool flip)
+{
+ u16 &cau = flip ? cabu : caau;
+ u16 &cal = flip ? cabl : caal;
+
+ cycles += wait_states[BIT(cau, 1, 2)];
+
+ return udc.space((cau & ARU_AR) >> 6).read_word(address(cau, cal, 2));
+}
+
+void am9516_device::channel::write_word(u16 data, unsigned &cycles, bool flip)
+{
+ u16 &cau = flip ? caau : cabu;
+ u16 &cal = flip ? caal : cabl;
+
+ cycles += wait_states[BIT(cau, 1, 2)];
+
+ udc.space((cau & ARU_AR) >> 6).write_word(address(cau, cal, 2), data);
+}
+
+void am9516_device::channel::interrupt(bool assert)
+{
+ if (assert && (status & S_IP))
+ {
+ LOG("second interrupt pending\n");
+ status |= S_SIP;
+ return;
+ }
+
+ if (!assert)
+ {
+ status &= ~S_IP;
+ is &= IS_CHN;
+
+ if (status & S_SIP)
+ {
+ LOG("second interrupt cleared\n");
+ status &= ~S_SIP;
+
+ reload();
+ }
+ else
+ return;
+ }
+
+ status |= S_IP;
+ is |= bitswap<u16>(status, 5, 4, 3, 12, 2, 1, 0) << 9 | iv;
+}
+
+void am9516_device::channel::reload()
+{
+ // reload base to current
+ if (((status & S_TC) && (cml & CML_RTC))
+ || ((status & S_EOP) && (cml & CML_REOP))
+ || ((status & S_MC) && (cml & CML_RMC)))
+ {
+ LOGMASKED(LOG_DMA, "reload base to current\n");
+ caau = baau;
+ caal = baal;
+ cabu = babu;
+ cabl = babl;
+ coc = boc;
+
+ log_addr(LOG_DMA, "current address a", caau, caal);
+ log_addr(LOG_DMA, "current address b", cabu, cabl);
+ LOGMASKED(LOG_DMA, "current operation count 0x%04x\n", coc);
+
+ status &= ~S_NAC;
+ }
+
+ // reload chain
+ if (((status & S_TC) && (cml & CML_CTC))
+ || ((status & S_EOP) && (cml & CML_CEOP))
+ || ((status & S_MC) && (cml & CML_CMC)))
+ {
+ LOGMASKED(LOG_DMA, "reload chain\n");
+ chain();
+ }
+}
+
+void am9516_device::channel::chain()
+{
+ address_space &s(udc.space(SYSTEM_MEM));
+
+ // TODO: abort on EOP
+
+ // fetch reload word
+ u32 chain_address = address(cau, cal);
+ u16 reload = s.read_word(chain_address) & CC_WM;
+ LOGMASKED(LOG_REGW, "chain address 0x%06x reload word 0x%04x\n", chain_address, reload);
+ chain_address += 2;
+
+ // current address a
+ if (reload & CC_CAA)
+ {
+ caau = s.read_word(chain_address + 0) & ARU_WM;
+ caal = s.read_word(chain_address + 2);
+
+ LOGMASKED(LOG_REGW, "current address a 0x%04x 0x%04x\n", caau, caal);
+ log_addr(LOG_DMA, "current address a", caau, caal);
+
+ chain_address += 4;
+ }
+
+ // current address b
+ if (reload & CC_CAB)
+ {
+ cabu = s.read_word(chain_address + 0) & ARU_WM;
+ cabl = s.read_word(chain_address + 2);
+
+ LOGMASKED(LOG_REGW, "current address b 0x%04x 0x%04x\n", cabu, cabl);
+ log_addr(LOG_DMA, "current address b", cabu, cabl);
+
+ chain_address += 4;
+ }
+
+ // current operation count
+ if (reload & CC_COC)
+ {
+ coc = s.read_word(chain_address);
+
+ LOGMASKED(LOG_DMA, "current operation count 0x%04x\n", coc);
+
+ chain_address += 2;
+ }
+
+ // base address a
+ if (reload & CC_BAA)
+ {
+ baau = s.read_word(chain_address + 0) & ARU_WM;
+ baal = s.read_word(chain_address + 2);
+
+ LOGMASKED(LOG_REGW, "base address a 0x%04x 0x%04x\n", baau, baal);
+
+ chain_address += 4;
+ }
+
+ // base address b
+ if (reload & CC_BAB)
+ {
+ babu = s.read_word(chain_address + 0) & ARU_WM;
+ babl = s.read_word(chain_address + 2);
+
+ LOGMASKED(LOG_REGW, "base address b 0x%04x 0x%04x\n", babu, babl);
+
+ chain_address += 4;
+ }
+
+ // base operation count
+ if (reload & CC_BOC)
+ {
+ boc = s.read_word(chain_address);
+
+ LOGMASKED(LOG_REGW, "base operation count 0x%04x\n", boc);
+
+ chain_address += 2;
+ }
+
+ // pattern and mask
+ if (reload & CC_PM)
+ {
+ pattern = s.read_word(chain_address + 0);
+ mask = s.read_word(chain_address + 2);
+
+ LOGMASKED(LOG_REGW, "pattern 0x%04x mask %04x\n", pattern, mask);
+
+ chain_address += 4;
+ }
+
+ // interrupt vector
+ if (reload & CC_IV)
+ {
+ iv = s.read_word(chain_address);
+
+ LOGMASKED(LOG_REGW, "interrupt vector 0x%04x\n", iv);
+
+ chain_address += 2;
+ }
+
+ // channel mode
+ if (reload & CC_CM)
+ {
+ cmh = s.read_word(chain_address + 0) & CMH_WM;
+ cml = s.read_word(chain_address + 2);
+
+ LOGMASKED(LOG_REGW, "channel mode 0x%04x %04x\n", cmh, cml);
+ log_mode(LOG_DMA, true);
+
+ chain_address += 4;
+ }
+
+ // chain address
+ if (reload & CC_CA)
+ {
+ cau = s.read_word(chain_address + 0) & (ARU_UA | ARU_WC);
+ cal = s.read_word(chain_address + 2);
+
+ LOGMASKED(LOG_REGW, "chain address 0x%04x %04x\n", cau, cal);
+
+ chain_address += 4;
+ }
+ else
+ {
+ // update chain address register
+ cau = ((chain_address >> 8) & ARU_UA) | u8(cau);
+ cal = u16(chain_address);
+ }
+
+ status &= ~(S_CA | S_NAC);
+
+ if ((status & S_HRQ) || (cmh & CMH_SR))
+ run->adjust(attotime::zero);
+}
+
+void am9516_device::channel::log_mode(unsigned mask, bool high) const
+{
+ if (VERBOSE & mask)
+ {
+ static const char *const match[] =
+ {
+ "no match", "no match", "word match", "byte match"
+ };
+ static const char *const operation[] =
+ {
+ "transfer, word/word flowthru",
+ "transfer, byte/byte flowthru",
+ "transfer, word/word flyby",
+ "transfer, byte/byte flyby",
+ "transfer/search, word/word flowthru",
+ "transfer/search, byte/byte flowthru",
+ "transfer/search, word/word flyby",
+ "transfer/search, byte/byte flyby",
+ "transfer, byte/word flowthru",
+ "transfer, byte/word flowthru",
+ "illegal",
+ "illegal",
+ "transfer/search, byte/word flowthru",
+ "transfer/search, byte/word flowthru",
+ "search, word/word",
+ "search, byte/byte",
+ };
+ static const char *const tt[] =
+ {
+ "single transfer", "demand dedicated/bus hold", "demand dedicated/bus release", "demand interleave"
+ };
+ static const char *const flags[] =
+ {
+ "-", "eop", "mc", "mc|eop", "tc", "tc|eop", "tc|mc", "tc|mc|eop"
+ };
+
+ LOGMASKED(mask, "channel mode %s, %s, complete(int:%s rld:%s chn:%s)\n",
+ operation[cml & CML_OPER], tt[BIT(cml, 5, 2)], flags[BIT(cml, 7, 3)], flags[BIT(cml, 10, 3)], flags[BIT(cml, 13, 3)]);
+
+ if (high)
+ LOGMASKED(mask, "channel mode stop:%s /dack:%d hm:%d sr:%d\n",
+ match[BIT(cmh, 0, 2)], BIT(cmh, 2), BIT(cmh, 3), BIT(cmh, 4));
+ }
+}
+
+void am9516_device::channel::log_addr(unsigned mask, const char *const name, u16 aru, u16 arl) const
+{
+ if (VERBOSE & mask)
+ {
+ static const char *const ar[] =
+ {
+ "system i/o", "system mem", "normal i/o", "normal mem"
+ };
+ static const char *const ac[] =
+ {
+ "inc", "dec", "hld", "hld"
+ };
+
+ LOGMASKED(mask, "%s %s 0x%06x %s wait:%d\n",
+ name, ar[BIT(aru, 6, 2)], u32(aru & ARU_UA) << 8 | arl,
+ ac[BIT(aru, 3, 2)], wait_states[BIT(aru, 1, 2)]);
+ }
+}