Interrupt nesting

IRQ_ISR()
{
    (1)  reset device interrupt flag (get ready for the next interrupt of the same kind)
    (2)  update VIC priority hardware
    (3)  r0-r12 -> STACK_IRQ
    (4)  LR_IRQ -> STACK_IRQ
    (5)  SPSR_IRQ -> STACK_IRQ
    (6)  change mode to SYS and remove I flag (enable interrupt nesting)
    (7)  LR_SYS -> STACK_SYS
    (8)     ...
    (9)  interrupt service routine code
    (10)    ...
    (11) LR_SYS <- STACK_SYS
    (12) change mode back to IRQ and set I flag (disable interrupt nesting)
    (13) SPSR_IRQ <- STACK_IRQ
    (14) LR_IRQ <- STACK_IRQ
    (15) r0-r12 <- STACK_IRQ
}

(1)  Omit device interrupt request flag. Current request is being handled. The device can request another interrupt of the same kind.
(2)  Perform a write to VICVectAddr to reset VIC internal interrupt priority hardware.
(3)  Push original values of working registers from r0 to r12 to IRQ stack. Now the registers can be used freely inside the interrupt code. Actually only registers from r4 to r10 need to be pushed to stack. Registers from r0 to r3, r11 and r12 are handled by interrupt function irq() entry code.
(4)  Push IRQ link register to IRQ stack. Now nested interrupt can store new return address to IRQ link register. This step is performed automatically by interrupt function irq() entry code.
(5)  Push IRQ saved program status register to IRQ stack. Now nested interrupt can save current program status register to IRQ saved program status register.
(6)  Change mode to SYS and enable nested interrupts by omitting the I flag. From now on a nested interrupt can actually occur. Changing to SYS mode is crucial to enable function calls while nested interrupts are enabled. When a function (subroutine) is called (bl instruction) the return address is stored into link register. Function entry code immediately pushes link register to stack. Problem occurs if nested interrupt takes place after function (subroutine) call (bl instruction), but before pushing link register to stack. Nested interrupt stores its own return address into link register and overwrite function's return address before pushing it to stack. Therefore function's return address is lost. To avoid such a case two link registers are needed. This is achieved by switching to SYS mode. Since the code now runs in SYS mode (instead of original IRQ mode) function's return address is stored into SYS link register (instead of IRQ link register). On nested interrupt mode is changed back to IRQ and return address is stored into IRQ link register.
(7)  Push SYS link register to SYS stack. Now link register value, currently valid in interrupted code, is safely stored. Link register can be freely used. Function calls are enabled.
(8)  Interrupt service routine code.
(9)  Interrupt service routine code.
(10) Interrupt service routine code.
(11) Restore original SYS link register value from SYS stack.
(12) Change mode back to IRQ and disable nested interrupts by setting the I flag.
(13) Restore original IRQ saved program status register from IRQ stack.
(14) Restore original IRQ link register value from IRQ stack.
(15) Restore original working registers (from r0 to r12) values from IRQ stack.
