diff --git a/src/devices/machine/atmel_arm_aic.cpp b/src/devices/machine/atmel_arm_aic.cpp index 0d2b548a6a2..dfc7077bf28 100644 --- a/src/devices/machine/atmel_arm_aic.cpp +++ b/src/devices/machine/atmel_arm_aic.cpp @@ -5,10 +5,13 @@ ARM AIC (Advanced Interrupt Controller) from Atmel typically integrated into the AM91SAM series of chips - see http://sam7-ex256.narod.ru/include/HTML/AT91SAM7X256_AIC.html - current implementation only handles basics needed for pgm2 (pgm2.cpp) + current implementation was tested in pgm2 (pgm2.cpp) only, there might be mistakes if more advanced usage. if this peripheral is not available as a standalone chip it could also be moved to the CPU folder alongside the ARM instead + TODO: + low/high input source types + check if level/edge input source types logic correct + FIQ output */ #include "emu.h" @@ -22,29 +25,61 @@ DEVICE_ADDRESS_MAP_START( regs_map, 32, arm_aic_device ) AM_RANGE(0x080, 0x0ff) AM_READWRITE(aic_svr_r, aic_svr_w) // AIC_SVR[32] (AIC_SVR) Source Vector Register AM_RANGE(0x100, 0x103) AM_READ(irq_vector_r) // AIC_IVR IRQ Vector Register AM_RANGE(0x104, 0x107) AM_READ(firq_vector_r) // AIC_FVR FIQ Vector Register -// 0x108 AIC_ISR Interrupt Status Register -// 0x10C AIC_IPR Interrupt Pending Register -// 0x110 AIC_IMR Interrupt Mask Register -// 0x114 AIC_CISR Core Interrupt Status Register - AM_RANGE(0x120, 0x123) AM_WRITE(aic_iecr_w) // 0x120 AIC_IECR Interrupt Enable Command Register - AM_RANGE(0x124, 0x127) AM_WRITE(aic_idcr_w) // 0x124 AIC_IDCR Interrupt Disable Command Register - AM_RANGE(0x128, 0x12b) AM_WRITE(aic_iccr_w) // 0x128 AIC_ICCR Interrupt Clear Command Register -// 0x12C AIC_ISCR Interrupt Set Command Register - AM_RANGE(0x130, 0x133) AM_WRITE(aic_eoicr_w) // 0x130 AIC_EOICR End of Interrupt Command Register -// 0x134 AIC_SPU Spurious Vector Register -// 0x138 AIC_DCR Debug Control Register (Protect) -// 0x140 AIC_FFER Fast Forcing Enable Register -// 0x144 AIC_FFDR Fast Forcing Disable Register -// 0x148 AIC_FFSR Fast Forcing Status Register + AM_RANGE(0x108, 0x10b) AM_READ(aic_isr_r) // AIC_ISR Interrupt Status Register + AM_RANGE(0x10c, 0x10f) AM_READ(aic_ipr_r) // AIC_IPR Interrupt Pending Register + AM_RANGE(0x110, 0x113) AM_READ(aic_imr_r) // AIC_IMR Interrupt Mask Register + AM_RANGE(0x114, 0x117) AM_READ(aic_cisr_r) // AIC_CISR Core Interrupt Status Register + AM_RANGE(0x120, 0x123) AM_WRITE(aic_iecr_w) // AIC_IECR Interrupt Enable Command Register + AM_RANGE(0x124, 0x127) AM_WRITE(aic_idcr_w) // AIC_IDCR Interrupt Disable Command Register + AM_RANGE(0x128, 0x12b) AM_WRITE(aic_iccr_w) // AIC_ICCR Interrupt Clear Command Register + AM_RANGE(0x12c, 0x12f) AM_WRITE(aic_iscr_w) // AIC_ISCR Interrupt Set Command Register + AM_RANGE(0x130, 0x133) AM_WRITE(aic_eoicr_w) // AIC_EOICR End of Interrupt Command Register + AM_RANGE(0x134, 0x137) AM_WRITE(aic_spu_w) // AIC_SPU Spurious Vector Register + AM_RANGE(0x138, 0x13b) AM_WRITE(aic_dcr_w) // AIC_DCR Debug Control Register (Protect) + AM_RANGE(0x140, 0x143) AM_WRITE(aic_ffer_w) // AIC_FFER Fast Forcing Enable Register + AM_RANGE(0x144, 0x147) AM_WRITE(aic_ffdr_w) // AIC_FFDR Fast Forcing Disable Register + AM_RANGE(0x144, 0x147) AM_READ(aic_ffsr_r) // AIC_FFSR Fast Forcing Status Register ADDRESS_MAP_END READ32_MEMBER(arm_aic_device::irq_vector_r) { + uint32_t mask = m_irqs_enabled & m_irqs_pending & ~m_fast_irqs; + m_current_irq_vector = m_spurious_vector; + if (mask) + { + // looking for highest level pending interrupt, bigger than current + int pri = get_level(); + int midx = -1; + do + { + uint8_t idx = 31 - count_leading_zeros(mask); + if ((int)(m_aic_smr[idx] & 7) >= pri) + { + midx = idx; + pri = m_aic_smr[idx] & 7; + } + mask ^= 1 << idx; + } while (mask); + + if (midx > 0) + { + m_status = midx; + m_current_irq_vector = m_aic_svr[midx]; + // note: Debug PROTect mode not implemented (new level pushed on stack and pending line clear only when this register writen after read) + push_level(m_aic_smr[midx] & 7); + if (m_aic_smr[midx] & 0x20) // auto clear pending if edge trigger mode + m_irqs_pending ^= 1 << midx; + } + } + + m_core_status &= ~2; + set_lines(); return m_current_irq_vector; } READ32_MEMBER(arm_aic_device::firq_vector_r) { + m_current_firq_vector = (m_irqs_enabled & m_irqs_pending & m_fast_irqs) ? m_aic_svr[0] : m_spurious_vector; return m_current_firq_vector; } @@ -56,9 +91,16 @@ void arm_aic_device::device_start() save_item(NAME(m_irqs_pending)); save_item(NAME(m_current_irq_vector)); save_item(NAME(m_current_firq_vector)); + save_item(NAME(m_status)); + save_item(NAME(m_core_status)); + save_item(NAME(m_spurious_vector)); + save_item(NAME(m_debug)); + save_item(NAME(m_fast_irqs)); + save_item(NAME(m_lvlidx)); save_item(NAME(m_aic_smr)); save_item(NAME(m_aic_svr)); + save_item(NAME(m_level_stack)); } void arm_aic_device::device_reset() @@ -67,43 +109,54 @@ void arm_aic_device::device_reset() m_irqs_pending = 0; m_current_irq_vector = 0; m_current_firq_vector = 0; + m_status = 0; + m_core_status = 0; + m_spurious_vector = 0; + m_debug = 0; + m_fast_irqs = 1; + m_lvlidx = 0; for(auto & elem : m_aic_smr) { elem = 0; } for(auto & elem : m_aic_svr) { elem = 0; } + for(auto & elem : m_level_stack) { elem = 0; } + m_level_stack[0] = -1; } void arm_aic_device::set_irq(int line, int state) { - // note: configurable edge / level logic is not simulated, TODO + // note: might be incorrect if edge triggered mode set if (state == ASSERT_LINE) m_irqs_pending |= 1 << line; else - m_irqs_pending &= ~(1 << line); - recalc_irqs(); + if (m_aic_smr[line] & 0x40) + m_irqs_pending &= ~(1 << line); + + check_irqs(); } -void arm_aic_device::recalc_irqs() +void arm_aic_device::check_irqs() { - uint32_t mask = m_irqs_enabled & m_irqs_pending; + m_core_status = 0; + + uint32_t mask = m_irqs_enabled & m_irqs_pending & ~m_fast_irqs; if (mask) { - int pri = -1; - uint8_t midx = 0; + // check if we have pending interrupt with level more than current + int pri = get_level(); do { uint8_t idx = 31 - count_leading_zeros(mask); if ((int)(m_aic_smr[idx] & 7) > pri) { - midx = idx; - pri = m_aic_smr[idx] & 7; + m_core_status |= 2; + break; } - mask &= ~(1 << idx); + mask ^= 1 << idx; } while (mask); - - // handle FIQ (idx 0) case ?? - m_current_irq_vector = m_aic_svr[midx]; - m_irq_out(ASSERT_LINE); } - else - m_irq_out(CLEAR_LINE); + + if (m_irqs_enabled & m_irqs_pending & m_fast_irqs) + m_core_status |= 1; + + set_lines(); } diff --git a/src/devices/machine/atmel_arm_aic.h b/src/devices/machine/atmel_arm_aic.h index 9083c44fd95..c17d95e86d0 100644 --- a/src/devices/machine/atmel_arm_aic.h +++ b/src/devices/machine/atmel_arm_aic.h @@ -31,17 +31,27 @@ public: DECLARE_READ32_MEMBER(irq_vector_r); DECLARE_READ32_MEMBER(firq_vector_r); + DECLARE_READ32_MEMBER(aic_isr_r) { return m_status; }; + DECLARE_READ32_MEMBER(aic_cisr_r) { return m_core_status; }; + DECLARE_READ32_MEMBER(aic_ipr_r) { return m_irqs_pending; }; + DECLARE_READ32_MEMBER(aic_imr_r) { return m_irqs_enabled; }; + DECLARE_READ32_MEMBER(aic_ffsr_r) { return m_fast_irqs; }; // can't use AM_RAM and AM_SHARE in device submaps DECLARE_READ32_MEMBER(aic_smr_r) { return m_aic_smr[offset]; }; DECLARE_READ32_MEMBER(aic_svr_r) { return m_aic_svr[offset]; }; DECLARE_WRITE32_MEMBER(aic_smr_w) { COMBINE_DATA(&m_aic_smr[offset]); }; DECLARE_WRITE32_MEMBER(aic_svr_w) { COMBINE_DATA(&m_aic_svr[offset]); }; + DECLARE_WRITE32_MEMBER(aic_spu_w) { COMBINE_DATA(&m_spurious_vector); }; + DECLARE_WRITE32_MEMBER(aic_dcr_w) { COMBINE_DATA(&m_debug); check_irqs(); }; + DECLARE_WRITE32_MEMBER(aic_ffer_w) { m_fast_irqs |= data & mem_mask; check_irqs(); }; + DECLARE_WRITE32_MEMBER(aic_ffdr_w) { m_fast_irqs &= ~(data & mem_mask) | 1; check_irqs(); }; - DECLARE_WRITE32_MEMBER(aic_iecr_w) { m_irqs_enabled |= data & mem_mask; recalc_irqs(); }; - DECLARE_WRITE32_MEMBER(aic_idcr_w) { m_irqs_enabled &= ~(data & mem_mask); recalc_irqs(); }; - DECLARE_WRITE32_MEMBER(aic_iccr_w) { m_irqs_pending &= ~(data & mem_mask); recalc_irqs(); }; - DECLARE_WRITE32_MEMBER(aic_eoicr_w){ /*logerror("%s: aic_eoicr_w (End of Interrupt Command Register)\n", machine().describe_context().c_str());*/ }; // value doesn't matter + DECLARE_WRITE32_MEMBER(aic_iecr_w) { m_irqs_enabled |= data & mem_mask; check_irqs(); }; + DECLARE_WRITE32_MEMBER(aic_idcr_w) { m_irqs_enabled &= ~(data & mem_mask); check_irqs(); }; + DECLARE_WRITE32_MEMBER(aic_iccr_w) { m_irqs_pending &= ~(data & mem_mask); check_irqs(); }; + DECLARE_WRITE32_MEMBER(aic_iscr_w) { m_irqs_pending |= data & mem_mask; check_irqs(); }; + DECLARE_WRITE32_MEMBER(aic_eoicr_w) { m_status = 0; pop_level(); check_irqs(); }; void set_irq(int line, int state); @@ -54,12 +64,24 @@ private: uint32_t m_irqs_pending; uint32_t m_current_irq_vector; uint32_t m_current_firq_vector; + uint32_t m_status; + uint32_t m_core_status; + uint32_t m_spurious_vector; + uint32_t m_debug; + uint32_t m_fast_irqs; + int m_lvlidx; + int m_level_stack[9]; uint32_t m_aic_smr[32]; uint32_t m_aic_svr[32]; devcb_write_line m_irq_out; - void recalc_irqs(); + void check_irqs(); + void set_lines() { m_irq_out((m_core_status & ~m_debug & 2) ? ASSERT_LINE : CLEAR_LINE); }; // TODO FIQ + + void push_level(int lvl) { m_level_stack[++m_lvlidx] = lvl; }; + void pop_level() { if (m_lvlidx) --m_lvlidx; }; + int get_level() { return m_level_stack[m_lvlidx]; }; }; #endif diff --git a/src/mame/drivers/pgm2.cpp b/src/mame/drivers/pgm2.cpp index bfddc66741a..1e53b5b4607 100644 --- a/src/mame/drivers/pgm2.cpp +++ b/src/mame/drivers/pgm2.cpp @@ -76,13 +76,13 @@ 08 - fg scroll x 0a - fg scroll y 0e - resolution, 0 - low (kof98), 1 - high (rest of games) - 10 - ? orleg2 - 0x13, kov2nl, kof - 0x14 at init (sprite related?) + 10 - ? orleg2 - 0x13, kov2nl, kof98 - 0x14 at init 14 - sprite enable ? set to 0 before spriteram update, to 1 after 16 - enable access to vrams/palettes/etc ? (bitmask) 18 - vblank ack 1a - ? 0 at init - 1c - ? orleg2 - 5, kov2nl, kof - 7 at init (sprite related?) - 1e - ? 2 at init (sprite related?) + 1c - ? orleg2 - 5, kov2nl, kof - 7 at init + 1e - ? 2 at init 32 - shared RAM bank 34, 36 - ? 0 at init, some unused xor feature ? 38, 3a - sprite mask xor key @@ -388,11 +388,11 @@ static ADDRESS_MAP_START( pgm2_map, AS_PROGRAM, 32, pgm2_state ) AM_RANGE(0x40000000, 0x40000003) AM_DEVREADWRITE8("ymz774", ymz774_device, read, write, 0xffffffff) // internal to IGS036? - various other writes down here on startup too - could be other standard ATMEL peripherals like the ARM_AIC mixed with custom bits - AM_RANGE(0xffffec00, 0xffffec5f) AM_RAM + AM_RANGE(0xffffec00, 0xffffec5f) AM_RAM // SMC controller AM_RANGE(0xfffffc00, 0xfffffcff) AM_READWRITE8(encryption_r,encryption_w, 0xffffffff) // confirmed as encryption table for main program rom (see code at 3950) AM_RANGE(0xfffff000, 0xfffff14b) AM_DEVICE("arm_aic", arm_aic_device, regs_map) - + // PIOA (gpio) AM_RANGE(0xfffff430, 0xfffff433) AM_WRITENOP // often AM_RANGE(0xfffff434, 0xfffff437) AM_WRITENOP // often