Interrupts

An interrupt is a mechanism for some other portion of Mappy VM to interrupt the current program flow, so the program can attend to what caused the interruption. The main subsystems of Mappy VM (video hardware, DMA, timers, and networking) can generate interrupt requests (IRQs) if configured to do so. These IRQs are evaluated against the IME and IE registers explained below to set flags in the IF register and generate the IRQ input to the CPU. For even more flexibility, the CPU itself can ignore IRQs indefinitely when the I bit in the CPSR is set.

Interrupts and other CPU exceptions force the CPU to resume execution at exception vectors that are within the BIOS. Since the BIOS deals with the interrupt and the special conditions that the CPU requires to return from exceptions, writing an interrupt handler is simplified and can even be done purely in C or C++.

The BIOS handler loads an address from 0x03007FFC and branches to it after preserving r0-r3, r12, and lr. This is the normal ARM calling convention, and your interrupt handler should return using a BX LR. After this, the BIOS restores the saved registers and returns to the previously interrupted code. There is a predefined macro INTR_ADDRESS to store the address of the user interrupt handler in 0x03007FFC.

Thus, the order of events looks like:

  1. A subsystem completes its task (timer overflow, DMA transfer completes, etc...)
  2. If the IRQ enabled bit is set in the subsystem CR, then the IRQ is checked against the IE and the IME
  3. If the corresponding bit is set in IE, and IME is 1, IF is modified to indicate a valid IRQ.
  4. Once this happens, the CPU will enter the BIOS IRQ handler as soon as the I bit in the CPSR is cleared
  5. The CPU resumes execution at 0x18 in ARM mode, and the BIOS saves important registers
  6. The user interrupt handler (pointed to by 0x03007FFC) is called in ARM mode
  7. The user interrupt handler returns to the BIOS with a standard BX LR
  8. The BIOS handler restores the saved registers and returns the CPU to executing the interrupted code


Master Interrupt Enable Register (IME)

OffsetNameType FEDCBA98 76543210
$208 IME Read Write   Enabled

Details


Interrupt Enable Register (IE)

OffsetNameType FEDCBA98 76543210
$200 IE Read Write   Cartridge Joypad DMA 3 DMA 2 DMA 1 DMA 0 Network Timer 3 Timer 2 Timer 1 Timer 0 Y-trigger H-blank V-blank

Details


Interrupt Flags Registers (IF)

OffsetNameType FEDCBA98 76543210
$202 IF Read Write*   Cartridge Joypad DMA 3 DMA 2 DMA 1 DMA 0 Network Timer 3 Timer 2 Timer 1 Timer 0 Y-trigger H-blank V-blank
* Writing to the IF register performs an XOR on the latched flags. To clear IF, write the current value back like so: IF |= IF

Details

Uses and examples

Example: A very simple interrupt handler.

void vblankHandler(void) {
  dprintf("This code was called by an interrupt!\n");

  // Clear the v-blank IRQ bit in IF
  IF |= IRQ_VBLANK;
}

void configureInterrupts(void) {
  DISP_SR |= DISP_SR_VBLANK_IRQ;
  IE |= IRQ_VBLANK;
  INTR_ADDRESS = vblankHandler;
  IME = 1;
}

// Note that although the handler is called vblankHandler, there can only be
// one interrupt handler for all interrupts.  You can determine which interrupts
// have occured by looking at the IF register.

Copyright © 2001 to 2002, Bottled Light, Inc.