summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/am9519.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/am9519.cpp')
-rw-r--r--src/devices/machine/am9519.cpp305
1 files changed, 305 insertions, 0 deletions
diff --git a/src/devices/machine/am9519.cpp b/src/devices/machine/am9519.cpp
new file mode 100644
index 00000000000..99fde6fc1c1
--- /dev/null
+++ b/src/devices/machine/am9519.cpp
@@ -0,0 +1,305 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol,Carl
+
+#include "emu.h"
+#include "machine/am9519.h"
+
+#define LOG_GENERAL (1U << 0)
+
+//#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
+
+void am9519_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if(!BIT(m_mode, 7)) // chip disabled
+ return;
+
+ /* check the various IRQs */
+ for(int n = 0, irq = m_prio; n < 8; n++, irq = (irq + 1) & 7)
+ {
+ u8 mask = 1 << irq;
+
+ /* is this IRQ in service and not cascading and sfnm? */
+ if(m_isr & mask)
+ {
+ LOG("am9519_timerproc(): PIC IRQ #%d still in service\n", irq);
+ break;
+ }
+
+ bool active = !(BIT(m_mode, 4) ^ BIT(m_irr, irq));
+
+ /* is this IRQ pending and enabled? */
+ if(active && !(m_imr & mask))
+ {
+ LOG("am9519_timerproc(): PIC triggering IRQ #%d\n", irq);
+ if(BIT(m_mode, 4))
+ m_out_int_func(1);
+ return;
+ }
+ }
+ m_out_int_func(0);
+}
+
+
+void am9519_device::set_irq_line(int irq, int state)
+{
+ u8 mask = (1 << irq);
+
+ if(state)
+ {
+ /* setting IRQ line */
+ LOG("am9519_set_irq_line(): PIC set IRQ line #%d\n", irq);
+
+ if(!(m_irq_lines & mask)) // edge trig only
+ m_irr |= mask;
+ m_irq_lines |= mask;
+ }
+ else
+ {
+ /* clearing IRQ line */
+ LOG("am9519_device::set_irq_line(): PIC cleared IRQ line #%d\n", irq);
+
+ m_irq_lines &= ~mask;
+ m_irr &= ~mask;
+ }
+ set_timer();
+}
+
+
+u32 am9519_device::acknowledge()
+{
+ for(int n = 0, irq = m_prio; n < 8; n++, irq = (irq + 1) & 7)
+ {
+ u8 mask = 1 << irq;
+
+ /* is this IRQ pending and enabled? */
+ if((m_irr & mask) && !(m_imr & mask))
+ {
+ LOG("am9519_acknowledge(): PIC acknowledge IRQ #%d\n", irq);
+
+ if(!(mask & m_aclear))
+ m_isr |= mask;
+
+ set_timer();
+
+ u32 ret = 0;
+ int irqr = BIT(m_mode, 1) ? 0 : irq;
+ for(int i = 0; i < m_count[irqr]; i++)
+ ret = (ret << 8) | m_resp[irqr][i];
+
+ return ret;
+ }
+ }
+ logerror("Spurious IRQ\n");
+ return 0;
+}
+
+
+IRQ_CALLBACK_MEMBER(am9519_device::iack_cb)
+{
+ return acknowledge();
+}
+
+
+READ8_MEMBER( am9519_device::stat_r )
+{
+ u8 stat = 0;
+ for(int n = 0, irq = m_prio; n < 8; n++, irq = (irq + 1) & 7)
+ {
+ u8 mask = 1 << irq;
+ if((m_irr & mask) && !(m_imr & mask))
+ {
+ stat = 0x80 | irq;
+ break;
+ }
+ }
+ stat |= BIT(m_mode, 7) << 3;
+ stat |= BIT(m_mode, 2) << 4;
+ stat |= BIT(m_mode, 0) << 5;
+ return stat;
+}
+
+READ8_MEMBER( am9519_device::data_r )
+{
+ switch((m_mode & 0x60) >> 5)
+ {
+ case 0:
+ return m_isr;
+ case 1:
+ return m_imr;
+ case 2:
+ return m_irr;
+ case 3:
+ return m_aclear;
+ }
+ return 0;
+}
+
+WRITE8_MEMBER( am9519_device::cmd_w )
+{
+ m_cmd = data;
+ switch(data >> 3)
+ {
+ case 0:
+ reset();
+ break;
+ case 2:
+ m_irr = m_imr = 0;
+ break;
+ case 3:
+ m_irr &= ~(1 << (data & 7));
+ m_imr &= ~(1 << (data & 7));
+ break;
+ case 4:
+ m_imr = 0;
+ break;
+ case 5:
+ m_imr &= ~(1 << (data & 7));
+ break;
+ case 6:
+ m_imr = 0xff;
+ break;
+ case 7:
+ m_imr |= 1 << (data & 7);
+ break;
+ case 8:
+ m_irr = 0;
+ break;
+ case 9:
+ m_irr &= ~(1 << (data & 7));
+ break;
+ case 10:
+ m_irr = 0xff;
+ break;
+ case 11:
+ m_irr |= 1 << (data & 7);
+ break;
+ case 12:
+ case 13:
+ {
+ for(int n = 0, irq = m_prio; n < 8; n++, irq = (irq + 1) & 7)
+ {
+ if(m_isr & (1 << irq))
+ m_isr &= ~(1 << irq);
+ }
+ break;
+ }
+ case 14:
+ m_isr = 0;
+ break;
+ case 15:
+ m_isr &= ~(1 << (data & 7));
+ break;
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ m_mode = (m_mode & 0xe0) | (data & 0x1f);
+ break;
+ case 20:
+ case 21:
+ m_mode = (m_mode & 0x9f) | (data & 0x60);
+ switch(data & 3)
+ {
+ case 1:
+ m_mode |= 0x80;
+ break;
+ case 2:
+ m_mode &= 0x7f;
+ break;
+ }
+ break;
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ m_count[m_cmd & 7] = ((data >> 3) & 3) + 1;
+ m_curcnt = 0;
+ break;
+ }
+ set_timer();
+}
+
+WRITE8_MEMBER( am9519_device::data_w )
+{
+ if((m_cmd & 0xf0) > 0xb0)
+ {
+ switch(m_cmd >> 4)
+ {
+ case 11:
+ m_imr = data;
+ break;
+ case 12:
+ m_aclear = data;
+ break;
+ case 14:
+ case 15:
+ if(m_curcnt < m_count[m_cmd & 7])
+ {
+ m_resp[m_cmd & 7][m_curcnt] = data;
+ m_curcnt++;
+ return;
+ }
+ break;
+ }
+ m_cmd = 0;
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void am9519_device::device_start()
+{
+ // resolve callbacks
+ m_out_int_func.resolve_safe();
+
+ // Register save state items
+ save_item(NAME(m_isr));
+ save_item(NAME(m_irr));
+ save_item(NAME(m_prio));
+ save_item(NAME(m_imr));
+ save_item(NAME(m_irq_lines));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_count));
+ save_item(NAME(m_resp));
+ save_item(NAME(m_aclear));
+ save_item(NAME(m_cmd));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void am9519_device::device_reset()
+{
+ m_isr = 0;
+ m_irr = 0;
+ m_irq_lines = 0;
+ m_prio = 0;
+ m_imr = 0xff;
+ m_mode = 0;
+ m_aclear = 0;
+ m_cmd = 0;
+ m_curcnt = 0;
+ for(int i = 0; i < 8; i++)
+ {
+ m_count[i] = 0;
+ m_resp[i][0] = 0;
+ m_resp[i][1] = 0;
+ m_resp[i][2] = 0;
+ m_resp[i][3] = 0;
+ }
+}
+
+DEFINE_DEVICE_TYPE(AM9519, am9519_device, "am9519", "AMD AM9519 Universal Interrupt Controller")
+
+am9519_device::am9519_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, AM9519, tag, owner, clock)
+ , m_out_int_func(*this)
+ , m_irr(0)
+ , m_irq_lines(0)
+{
+}