Atmel AIC: implement most of features

This commit is contained in:
MetalliC 2017-12-16 17:19:52 +02:00
parent 0932b9c2ae
commit 8255459e5d
3 changed files with 117 additions and 42 deletions

View File

@ -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();
}

View File

@ -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

View File

@ -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