summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/sh/sh7014_intc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/sh/sh7014_intc.cpp')
-rw-r--r--src/devices/cpu/sh/sh7014_intc.cpp313
1 files changed, 313 insertions, 0 deletions
diff --git a/src/devices/cpu/sh/sh7014_intc.cpp b/src/devices/cpu/sh/sh7014_intc.cpp
new file mode 100644
index 00000000000..c6f1ea521ad
--- /dev/null
+++ b/src/devices/cpu/sh/sh7014_intc.cpp
@@ -0,0 +1,313 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ SH7014 Interrupt Controller
+
+***************************************************************************/
+
+#include "emu.h"
+#include "sh7014_intc.h"
+
+// #define VERBOSE (LOG_GENERAL)
+
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(SH7014_INTC, sh7014_intc_device, "sh7014intc", "SH7014 Interrupt Controller")
+
+
+sh7014_intc_device::sh7014_intc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, SH7014_INTC, tag, owner, clock)
+ , m_set_irq_cb(*this)
+{
+}
+
+void sh7014_intc_device::device_start()
+{
+ m_set_irq_cb.resolve();
+
+ save_item(NAME(m_ipra));
+ save_item(NAME(m_iprb));
+ save_item(NAME(m_iprc));
+ save_item(NAME(m_iprd));
+ save_item(NAME(m_ipre));
+ save_item(NAME(m_iprf));
+ save_item(NAME(m_iprg));
+ save_item(NAME(m_iprh));
+ save_item(NAME(m_icr));
+ save_item(NAME(m_isr));
+ save_item(NAME(m_irq_type));
+ save_item(NAME(m_nmi_input));
+ save_item(NAME(m_pending_irqs));
+ save_item(NAME(m_irq_levels));
+}
+
+void sh7014_intc_device::device_reset()
+{
+ m_ipra = 0;
+ m_iprb = 0;
+ m_iprc = 0;
+ m_iprd = 0;
+ m_ipre = 0;
+ m_iprf = 0;
+ m_iprg = 0;
+ m_iprh = 0;
+ m_icr = m_isr = 0;
+ m_nmi_input = false;
+
+ std::fill(std::begin(m_irq_type), std::end(m_irq_type), IRQ_LEVEL);
+ std::fill(std::begin(m_irq_levels), std::end(m_irq_levels), 0);
+ std::fill(std::begin(m_pending_irqs), std::end(m_pending_irqs), 0);
+
+ m_irq_levels[INT_VECTOR_NMI] = 16; // fixed value
+}
+
+void sh7014_intc_device::map(address_map &map)
+{
+ map(0x00, 0x01).rw(FUNC(sh7014_intc_device::ipra_r), FUNC(sh7014_intc_device::ipra_w));
+ map(0x02, 0x03).rw(FUNC(sh7014_intc_device::iprb_r), FUNC(sh7014_intc_device::iprb_w));
+ map(0x04, 0x05).rw(FUNC(sh7014_intc_device::iprc_r), FUNC(sh7014_intc_device::iprc_w));
+ map(0x06, 0x07).rw(FUNC(sh7014_intc_device::iprd_r), FUNC(sh7014_intc_device::iprd_w));
+ map(0x08, 0x09).rw(FUNC(sh7014_intc_device::ipre_r), FUNC(sh7014_intc_device::ipre_w));
+ map(0x0a, 0x0b).rw(FUNC(sh7014_intc_device::iprf_r), FUNC(sh7014_intc_device::iprf_w));
+ map(0x0c, 0x0d).rw(FUNC(sh7014_intc_device::iprg_r), FUNC(sh7014_intc_device::iprg_w));
+ map(0x0e, 0x0f).rw(FUNC(sh7014_intc_device::iprh_r), FUNC(sh7014_intc_device::iprh_w));
+ map(0x10, 0x11).rw(FUNC(sh7014_intc_device::icr_r), FUNC(sh7014_intc_device::icr_w));
+ map(0x12, 0x13).rw(FUNC(sh7014_intc_device::isr_r), FUNC(sh7014_intc_device::isr_w));
+}
+
+///
+
+uint16_t sh7014_intc_device::ipra_r()
+{
+ return m_ipra;
+}
+
+void sh7014_intc_device::ipra_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_ipra);
+
+ m_irq_levels[INT_VECTOR_IRQ0] = BIT(m_ipra, 12, 4);
+ m_irq_levels[INT_VECTOR_IRQ1] = BIT(m_ipra, 8, 4);
+ m_irq_levels[INT_VECTOR_IRQ2] = BIT(m_ipra, 4, 4);
+ m_irq_levels[INT_VECTOR_IRQ3] = BIT(m_ipra, 0, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::iprb_r()
+{
+ return m_iprb & 0xff00;
+}
+
+void sh7014_intc_device::iprb_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_iprb);
+
+ m_irq_levels[INT_VECTOR_IRQ6] = BIT(m_iprb, 4, 4);
+ m_irq_levels[INT_VECTOR_IRQ7] = BIT(m_iprb, 0, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::iprc_r()
+{
+ return m_iprc;
+}
+
+void sh7014_intc_device::iprc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_iprc);
+
+ m_irq_levels[INT_VECTOR_DMA_CH0] = BIT(m_iprc, 12, 4);
+ m_irq_levels[INT_VECTOR_DMA_CH1] = BIT(m_iprc, 8, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::iprd_r()
+{
+ return m_iprd;
+}
+
+void sh7014_intc_device::iprd_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_iprd);
+
+ m_irq_levels[INT_VECTOR_MTU_TGI0A] = m_irq_levels[INT_VECTOR_MTU_TGI0B] = m_irq_levels[INT_VECTOR_MTU_TGI0C] = m_irq_levels[INT_VECTOR_MTU_TGI0D] = BIT(m_iprd, 12, 4);
+ m_irq_levels[INT_VECTOR_MTU_TGI0V] = BIT(m_iprd, 8, 4);
+ m_irq_levels[INT_VECTOR_MTU_TGI1A] = m_irq_levels[INT_VECTOR_MTU_TGI1B] = BIT(m_iprd, 4, 4);
+ m_irq_levels[INT_VECTOR_MTU_TGI1V] = m_irq_levels[INT_VECTOR_MTU_TGI1U] = BIT(m_iprd, 0, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::ipre_r()
+{
+ return m_ipre & 0xff00;
+}
+
+void sh7014_intc_device::ipre_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_ipre);
+
+ m_irq_levels[INT_VECTOR_MTU_TGI2A] = m_irq_levels[INT_VECTOR_MTU_TGI2B] = BIT(m_ipre, 12, 4);
+ m_irq_levels[INT_VECTOR_MTU_TGI2V] = m_irq_levels[INT_VECTOR_MTU_TGI2U] = BIT(m_ipre, 8, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::iprf_r()
+{
+ return m_iprf & 0xff;
+}
+
+void sh7014_intc_device::iprf_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_iprf);
+
+ m_irq_levels[INT_VECTOR_SCI_ERI0] = m_irq_levels[INT_VECTOR_SCI_RXI0] = m_irq_levels[INT_VECTOR_SCI_TXI0] = m_irq_levels[INT_VECTOR_SCI_TEI0] = BIT(m_iprf, 4, 4);
+ m_irq_levels[INT_VECTOR_SCI_ERI1] = m_irq_levels[INT_VECTOR_SCI_RXI1] = m_irq_levels[INT_VECTOR_SCI_TXI1] = m_irq_levels[INT_VECTOR_SCI_TEI1] = BIT(m_iprf, 0, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::iprg_r()
+{
+ return m_iprg & 0xf0ff;
+}
+
+void sh7014_intc_device::iprg_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_iprg);
+
+ m_irq_levels[INT_VECTOR_AD] = BIT(m_iprg, 12, 4);
+ m_irq_levels[INT_VECTOR_CMT_CH0] = BIT(m_iprg, 4, 4);
+ m_irq_levels[INT_VECTOR_CMT_CH1] = BIT(m_iprg, 0, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::iprh_r()
+{
+ return m_iprh & 0xf000;
+}
+
+void sh7014_intc_device::iprh_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_iprh);
+
+ m_irq_levels[INT_VECTOR_WDT] = m_irq_levels[INT_VECTOR_BSC] = BIT(m_iprh, 12, 4);
+
+ update_irq_state();
+}
+
+uint16_t sh7014_intc_device::icr_r()
+{
+ uint16_t r = m_icr & 0x01f3;
+
+ if (m_nmi_input == ((m_icr & ICR_NMIE) ? ASSERT_LINE : CLEAR_LINE))
+ r |= ICR_NMIL;
+
+ return r;
+}
+
+void sh7014_intc_device::icr_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ COMBINE_DATA(&m_icr);
+
+ for (int i = 0; i < 8; i++)
+ m_irq_type[i] = BIT(m_icr, 7 - i);
+}
+
+uint16_t sh7014_intc_device::isr_r()
+{
+ return m_isr & 0xf3;
+}
+
+void sh7014_intc_device::isr_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ // ICR will be set to 1 for edge detection, and it's only possible to
+ // clear flags in ISR for IRQs set to edge detection
+ auto old = m_isr;
+
+ COMBINE_DATA(&m_isr);
+
+ m_isr = (old & ~m_icr) | (old & m_isr & m_icr);
+
+ update_irq_state();
+}
+
+void sh7014_intc_device::set_interrupt(int vector, int state)
+{
+ if (vector != -1) {
+ if (state == ASSERT_LINE)
+ m_pending_irqs[vector >> 5] |= 1 << (vector & 31);
+ else if (state == CLEAR_LINE)
+ m_pending_irqs[vector >> 5] &= ~(1 << (vector & 31));
+
+ if (vector == INT_VECTOR_NMI) {
+ m_nmi_input = state == ASSERT_LINE;
+ } else if ((vector >= INT_VECTOR_IRQ0 && vector <= INT_VECTOR_IRQ3) || vector == INT_VECTOR_IRQ6 || vector == INT_VECTOR_IRQ7) {
+ const int irq = vector - INT_VECTOR_IRQ0;
+
+ if (state == ASSERT_LINE) {
+ if (m_irq_type[irq] == IRQ_LEVEL)
+ m_isr |= 1 << (7 - irq);
+ } else if (state == CLEAR_LINE) {
+ if (m_irq_type[irq] == IRQ_LEVEL)
+ m_isr &= ~(1 << (7 - irq));
+ }
+ }
+ }
+
+ update_irq_state();
+}
+
+void sh7014_intc_device::update_irq_state()
+{
+ int cur_vector = 0;
+ int cur_level = -1;
+
+ // isr has the bits in reverse order
+ m_pending_irqs[INT_VECTOR_IRQ0 >> 5] &= ~0xff;
+ m_pending_irqs[INT_VECTOR_IRQ0 >> 5] |= BIT(m_isr, 7)
+ | (BIT(m_isr, 6) << 1)
+ | (BIT(m_isr, 5) << 2)
+ | (BIT(m_isr, 4) << 3)
+ | (BIT(m_isr, 1) << 6)
+ | (BIT(m_isr, 0) << 7);
+
+ for (int i = 0; i < MAX_VECTORS / 32; i++) {
+ if (!m_pending_irqs[i])
+ continue;
+
+ for (int j = 0; j < 32; j++) {
+ if (BIT(m_pending_irqs[i], j)) {
+ const int vector = i * 32 + j;
+ const int level = m_irq_levels[vector];
+
+ if (level > cur_level) {
+ cur_vector = vector;
+ cur_level = level;
+ }
+ }
+ }
+ }
+
+ if (cur_vector == INT_VECTOR_NMI)
+ m_set_irq_cb(INPUT_LINE_NMI, cur_level, false);
+ else if ((cur_vector >= INT_VECTOR_IRQ0 && cur_vector <= INT_VECTOR_IRQ3) || cur_vector == INT_VECTOR_IRQ6 || cur_vector == INT_VECTOR_IRQ7)
+ m_set_irq_cb(cur_vector - INT_VECTOR_IRQ0, cur_level, false);
+ else if (cur_vector > INT_VECTOR_IRQ7)
+ m_set_irq_cb(cur_vector, cur_level, true);
+}
+
+void sh7014_intc_device::set_input(int inputnum, int state)
+{
+ if (inputnum == INPUT_LINE_NMI)
+ set_interrupt(INT_VECTOR_NMI, state);
+ else if ((inputnum >= INPUT_LINE_IRQ0 && inputnum <= INPUT_LINE_IRQ3) || inputnum == INPUT_LINE_IRQ6 || inputnum == INPUT_LINE_IRQ7)
+ set_interrupt(INT_VECTOR_IRQ0 + (inputnum - INPUT_LINE_IRQ0), state);
+}