diff options
Diffstat (limited to 'src/devices/cpu/sh/sh7014_intc.cpp')
-rw-r--r-- | src/devices/cpu/sh/sh7014_intc.cpp | 313 |
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); +} |