]> localhost Git - WindEmu.git/commitdiff
sa1100: add initial interrupt controller implementation
authorGeorge Wright <gw@gwright.org.uk>
Thu, 25 Jun 2026 02:47:10 +0000 (19:47 -0700)
committerGeorge Wright <gw@gwright.org.uk>
Fri, 26 Jun 2026 03:44:07 +0000 (20:44 -0700)
CMakeLists.txt
WindCore/series7/interrupt_controller.cpp [new file with mode: 0644]
WindCore/series7/interrupt_controller.h [new file with mode: 0644]

index 586305b08d500d1080ee130bfcaa3a26ba7e3b2f..e1335edff480d50b55548712a29d7f45cc0e8b2e 100644 (file)
@@ -24,6 +24,7 @@ set(WIND_CORE_SOURCES
     WindCore/series5mx/etna.cpp
     WindCore/series5mx/windermere.cpp
     WindCore/series7/gpio_controller.cpp
+    WindCore/series7/interrupt_controller.cpp
     WindCore/series7/lcd_controller.cpp
     WindCore/series7/memory_controller.cpp
     WindCore/series7/os_timer.cpp
diff --git a/WindCore/series7/interrupt_controller.cpp b/WindCore/series7/interrupt_controller.cpp
new file mode 100644 (file)
index 0000000..9af74db
--- /dev/null
@@ -0,0 +1,485 @@
+// ARMware - an ARM emulator
+// Copyright (C) <2007>  Wei Hu <wei.hu.tw@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "interrupt_controller.h"
+
+#include "logger.h"
+
+namespace SA1100 {
+
+IntController::IntController(const LCDController* lcdController,
+                             const OsTimer* osTimer,
+                             const GPIOController* gpioController,
+                             const Uart* uart, const RTC* rtc)
+    : mOsTimer(osTimer), mLCDController(lcdController),
+      mGPIOController(gpioController), mUart(uart), mRTC(rtc) {
+  initRegisters();
+}
+
+void IntController::initRegisters() {
+  mICIP = 0;
+  mICMR = 0;
+  mICLR = 0;
+  mICCR = 0;
+  mICFP = 0;
+  mICPR = 0;
+}
+
+void IntController::reset() {
+  mICIP = 0;
+
+  // :NOTE: Wei 2004-Apr-25:
+  //
+  // Although the SA-1110 Developer's Manual doesn't specify whether ICFP will
+  // be initialized to 0 when reset. However, SA-1110 Developer's Manual does
+  // specify ICIP will be initialized to 0 when reset. Thus I _assume_ this is a
+  // bug in the SA-1110 Developer's Manual, and will do the initialization by my
+  // self.
+
+  // :NOTE: Wei 2004-Jul-20;
+  //
+  // It is really a bug.
+  // According to the Intel-StrongARM SA-1110 Microprocessor Specification
+  // Update (December 2001), p.37:
+  //
+  // Changed the reset values for the Interrupt Controller IRQ and FIQ Pending
+  // Registers (ICIP and ICFP) from undefined to 0.
+  mICFP = 0;
+
+  mICCR = 0;
+  // mICMR = ~0;
+
+  // :NOTE: Wei 2004-Jun-06:
+  //
+  // Althought there is no words in SA-1110 Developer's manual says that
+  // ICPR will be set to 0 when reset.
+  // However, I think it should set to 0 when reset to indicate that no
+  // pending interrupt exist.
+  mICPR = 0;
+}
+
+void IntController::checkGpioInterruptStatus() {
+  const uint32_t gpioStatus = mGPIOController->getInterruptStatus();
+  // clear all GPIO bits in pending interrupts
+  mICPR &= ~(GPIO_0_10_EDGE_BIT_MASK | GPIO_11_27_EDGE_BIT_MASK);
+
+  if (gpioStatus != 0) {
+    if (gpioStatus & GPIO_0_EDGE_BIT_MASK) {
+      mICPR |= GPIO_0_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_1_EDGE_BIT_MASK) {
+      mICPR |= GPIO_1_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_2_EDGE_BIT_MASK) {
+      mICPR |= GPIO_2_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_3_EDGE_BIT_MASK) {
+      mICPR |= GPIO_3_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_4_EDGE_BIT_MASK) {
+      mICPR |= GPIO_4_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_5_EDGE_BIT_MASK) {
+      mICPR |= GPIO_5_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_6_EDGE_BIT_MASK) {
+      mICPR |= GPIO_6_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_7_EDGE_BIT_MASK) {
+      mICPR |= GPIO_7_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_8_EDGE_BIT_MASK) {
+      mICPR |= GPIO_8_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_9_EDGE_BIT_MASK) {
+      mICPR |= GPIO_9_EDGE_BIT_MASK;
+    }
+    if (gpioStatus & GPIO_10_EDGE_BIT_MASK) {
+      mICPR |= GPIO_10_EDGE_BIT_MASK;
+    }
+    constexpr uint32_t GPIO_11_27_BITS =
+        (1 << 11) | (1 << 12) | (1 << 13) | (1 << 14) | (1 << 15) | (1 << 16) |
+        (1 << 17) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21) | (1 << 22) |
+        (1 << 23) | (1 << 24) | (1 << 25) | (1 << 26) | (1 << 27);
+    if (gpioStatus & GPIO_11_27_BITS) {
+      mICPR |= GPIO_11_27_EDGE_BIT_MASK;
+    }
+  }
+}
+
+void IntController::checkOsTimerInterruptStatus() {
+  const uint32_t osTimerStatus = mOsTimer->getInterruptStatus();
+
+  switch (osTimerStatus) {
+  case 0: // 0000
+  {
+    mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK |
+               OS_TIMER_3_BIT_MASK);
+  } break;
+
+  case OsTimer::OSMR0_MASK: // 0001
+  {
+    mICPR |= OS_TIMER_0_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK | OS_TIMER_3_BIT_MASK);
+  } break;
+
+  case OsTimer::OSMR1_MASK: // 0010
+  {
+    mICPR |= OS_TIMER_1_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_2_BIT_MASK | OS_TIMER_3_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK): // 0011
+  {
+    mICPR |= OS_TIMER_0_BIT_MASK;
+    mICPR |= OS_TIMER_1_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_2_BIT_MASK | OS_TIMER_3_BIT_MASK);
+  } break;
+
+  case OsTimer::OSMR2_MASK: // 0100
+  {
+    mICPR |= OS_TIMER_2_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_3_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR0_MASK | OsTimer::OSMR2_MASK): // 0101
+  {
+    mICPR |= OS_TIMER_0_BIT_MASK;
+    mICPR |= OS_TIMER_2_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_1_BIT_MASK | OS_TIMER_3_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR1_MASK | OsTimer::OSMR2_MASK): // 0110
+  {
+    mICPR |= OS_TIMER_1_BIT_MASK;
+    mICPR |= OS_TIMER_2_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_3_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK |
+        OsTimer::OSMR2_MASK): // 0111
+  {
+    mICPR |= OS_TIMER_0_BIT_MASK;
+    mICPR |= OS_TIMER_1_BIT_MASK;
+    mICPR |= OS_TIMER_2_BIT_MASK;
+
+    mICPR &= ~OS_TIMER_3_BIT_MASK;
+  } break;
+
+  case OsTimer::OSMR3_MASK: // 1000
+  {
+    mICPR |= OS_TIMER_3_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR0_MASK | OsTimer::OSMR3_MASK): // 1001
+  {
+    mICPR |= OS_TIMER_0_BIT_MASK;
+    mICPR |= OS_TIMER_3_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR1_MASK | OsTimer::OSMR3_MASK): // 1010
+  {
+    mICPR |= OS_TIMER_1_BIT_MASK;
+    mICPR |= OS_TIMER_3_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_2_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK |
+        OsTimer::OSMR3_MASK): // 1011
+  {
+    mICPR |= OS_TIMER_0_BIT_MASK;
+    mICPR |= OS_TIMER_1_BIT_MASK;
+    mICPR |= OS_TIMER_3_BIT_MASK;
+
+    mICPR &= ~OS_TIMER_2_BIT_MASK;
+  } break;
+
+  case (OsTimer::OSMR2_MASK | OsTimer::OSMR3_MASK): // 1100
+  {
+    mICPR |= OS_TIMER_2_BIT_MASK;
+    mICPR |= OS_TIMER_3_BIT_MASK;
+
+    mICPR &= ~(OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK);
+  } break;
+
+  case (OsTimer::OSMR0_MASK | OsTimer::OSMR2_MASK |
+        OsTimer::OSMR3_MASK): // 1101
+  {
+    mICPR |= OS_TIMER_0_BIT_MASK;
+    mICPR |= OS_TIMER_2_BIT_MASK;
+    mICPR |= OS_TIMER_3_BIT_MASK;
+
+    mICPR &= ~OS_TIMER_1_BIT_MASK;
+  } break;
+
+  case (OsTimer::OSMR1_MASK | OsTimer::OSMR2_MASK |
+        OsTimer::OSMR3_MASK): // 1110
+  {
+    mICPR |= OS_TIMER_1_BIT_MASK;
+    mICPR |= OS_TIMER_2_BIT_MASK;
+    mICPR |= OS_TIMER_3_BIT_MASK;
+
+    mICPR &= ~OS_TIMER_0_BIT_MASK;
+  } break;
+
+  case (OsTimer::OSMR0_MASK | OsTimer::OSMR1_MASK | OsTimer::OSMR2_MASK |
+        OsTimer::OSMR3_MASK): // 1111
+  {
+    mICPR |= (OS_TIMER_0_BIT_MASK | OS_TIMER_1_BIT_MASK | OS_TIMER_2_BIT_MASK |
+              OS_TIMER_3_BIT_MASK);
+  } break;
+
+  default:
+    break;
+  }
+}
+
+void IntController::checkRtcInterruptStatus() {
+  const uint32_t rtcStatus = mRTC->getInterruptStatus();
+
+  switch (rtcStatus) {
+  case 0:
+    mICPR &= ~(RTC_ALARM_BIT_MASK | RTC_HZ_BIT_MASK);
+    break;
+
+  case RTC::STATUS_AL_BIT:
+    mICPR |= RTC_ALARM_BIT_MASK;
+    mICPR &= ~RTC_HZ_BIT_MASK;
+    break;
+
+  case RTC::STATUS_HZ_BIT:
+    mICPR |= RTC_HZ_BIT_MASK;
+    mICPR &= ~RTC_ALARM_BIT_MASK;
+    break;
+
+  case (RTC::STATUS_AL_BIT | RTC::STATUS_HZ_BIT):
+    mICPR |= (RTC_ALARM_BIT_MASK | RTC_HZ_BIT_MASK);
+    break;
+
+  default:
+    break;
+  }
+}
+
+void IntController::checkLcdInterruptStatus() {
+  const uint32_t lcdStatus = mLCDController->getInterruptStatus();
+
+  if (0 == lcdStatus) {
+    mICPR &= ~LCD_SERVICE_BIT_MASK;
+  } else {
+    if ((lcdStatus & ~(LCDController::LCSR_BAU | LCDController::LCSR_LDD)) !=
+        0) {
+      mICPR |= LCD_SERVICE_BIT_MASK;
+    } else {
+      switch (lcdStatus & (LCDController::LCSR_BAU | LCDController::LCSR_LDD)) {
+      case 0:
+        // If we go here, means (lcdStatus == 0), however, (lcdStatus == 0)
+        // should go to the upper 'if' block, thus if we go here, it is a bug.
+        break;
+
+      case LCDController::LCSR_BAU: {
+        const uint32_t lcdCtrl = mLCDController->getLcdCtrlRegister();
+
+        if (0 == (lcdCtrl & LCDController::LCCR0_BAM)) {
+          // Enable BAU
+          mICPR |= LCD_SERVICE_BIT_MASK;
+        } else {
+          mICPR &= ~LCD_SERVICE_BIT_MASK;
+        }
+      } break;
+
+      case LCDController::LCSR_LDD: {
+        const uint32_t lcdCtrl = mLCDController->getLcdCtrlRegister();
+
+        if (0 == (lcdCtrl & LCDController::LCCR0_LDM)) {
+          // Enable LDD
+          mICPR |= LCD_SERVICE_BIT_MASK;
+        } else {
+          mICPR &= ~LCD_SERVICE_BIT_MASK;
+        }
+      } break;
+
+      case (LCDController::LCSR_BAU | LCDController::LCSR_LDD): {
+        const uint32_t lcdCtrl = mLCDController->getLcdCtrlRegister();
+
+        switch (lcdCtrl &
+                (LCDController::LCCR0_BAM | LCDController::LCCR0_LDM)) {
+        case 0:
+        case LCDController::LCCR0_BAM:
+        case LCDController::LCCR0_LDM:
+          // Enable BAU
+          mICPR |= LCD_SERVICE_BIT_MASK;
+          break;
+
+        case (LCDController::LCCR0_BAM | LCDController::LCCR0_LDM):
+          mICPR &= ~LCD_SERVICE_BIT_MASK;
+          break;
+
+        default:
+          break;
+        }
+      } break;
+      }
+    }
+  }
+}
+
+void IntController::checkUartInterruptStatus() {
+  const uint32_t uartStatus = mUart->getInterruptStatus();
+
+  if (0 == uartStatus) {
+    mICPR &= ~SERIAL_3_BIT_MASK;
+  } else {
+    const uint32_t uartCtrl = mUart->getCtrlRegister();
+
+    switch (uartCtrl & (Uart::UTCR3_TIE | Uart::UTCR3_RIE)) {
+    case 0:
+      break;
+
+    case Uart::UTCR3_TIE:
+      if ((uartStatus & Uart::UTSR0_TFS) != 0) {
+        mICPR |= SERIAL_3_BIT_MASK;
+      } else {
+        mICPR &= ~SERIAL_3_BIT_MASK;
+      }
+      break;
+
+    case Uart::UTCR3_RIE:
+      if ((uartStatus & (Uart::UTSR0_RFS | Uart::UTSR0_RID)) != 0) {
+        mICPR |= SERIAL_3_BIT_MASK;
+      } else {
+        mICPR &= ~SERIAL_3_BIT_MASK;
+      }
+      break;
+
+    case (Uart::UTCR3_TIE | Uart::UTCR3_RIE):
+      if ((uartStatus &
+           (Uart::UTSR0_TFS | Uart::UTSR0_RFS | Uart::UTSR0_RID)) != 0) {
+        mICPR |= SERIAL_3_BIT_MASK;
+      } else {
+        mICPR &= ~SERIAL_3_BIT_MASK;
+      }
+      break;
+    }
+  }
+}
+
+void IntController::run() {
+  checkGpioInterruptStatus();
+  checkOsTimerInterruptStatus();
+  checkRtcInterruptStatus();
+  checkLcdInterruptStatus();
+  checkUartInterruptStatus();
+
+  // Determine IRQ & FIQ
+  if (mICPR != 0) {
+    const uint32_t temp = mICPR & mICMR;
+
+    mICIP = temp & (~mICLR);
+    mICFP = temp & mICLR;
+  } else {
+    mICIP = 0;
+    mICFP = 0;
+  }
+}
+
+uint32_t IntController::read32(const uint32_t address) const {
+  switch (address) {
+  case ICIP:
+    LOG_REG_R("IntController ICIP", address, mICIP);
+    return mICIP;
+  case ICMR:
+    LOG_REG_R("IntController ICMR", address, mICMR);
+    return mICMR;
+  case ICLR:
+    LOG_REG_R("IntController ICLR", address, mICLR);
+    return mICLR;
+  case ICCR:
+    LOG_REG_R("IntController ICCR", address, mICCR);
+    return mICCR;
+  case ICFP:
+    LOG_REG_R("IntController ICFP", address, mICFP);
+    return mICFP;
+  case ICPR:
+    LOG_REG_R("IntController ICPR", address, mICPR);
+    return mICPR;
+
+  default:
+    LOG_REG_R("IntController Unknown", address, 0);
+    return 0;
+  }
+}
+
+void IntController::write32(const uint32_t address, const uint32_t value) {
+  switch (address) {
+  case ICIP:
+    // :SA-1110 Developer's Manual: Wei 2003-Dec-09:
+    //
+    // ICIP is a read-only register
+    LOG_REG_W("IntController ICIP", address, value);
+    break;
+
+  case ICMR:
+    LOG_REG_W("IntController ICMR", address, value);
+    mICMR = value;
+    break;
+
+  case ICLR:
+    LOG_REG_W("IntController ICLR", address, value);
+    mICLR = value;
+    break;
+
+  case ICCR:
+    // :SA-1110 Developer's Manual: p.89: Wei 2004-May-09:
+    //
+    // bits[31:1] are reserved.
+    // LOG_REG_W("IntController ICCR", address, value);
+    mICCR = (value & 0x1);
+    break;
+
+  case ICFP:
+    // :SA-1110 Developer's Manual: Wei 2003-Dec-09:
+    //
+    // ICFP is a read-only register
+    LOG_REG_W("IntController ICFP", address, value);
+    break;
+
+  case ICPR:
+    // :SA-1110 Developer's Manual: Wei 2003-Dec-09:
+    //
+    // ICPR is a read-only register
+    LOG_REG_W("IntController ICPR", address, value);
+    break;
+
+  default:
+    LOG_REG_W("IntController Unknown", address, value);
+    break;
+  }
+}
+
+} // namespace SA1100
diff --git a/WindCore/series7/interrupt_controller.h b/WindCore/series7/interrupt_controller.h
new file mode 100644 (file)
index 0000000..42b36ef
--- /dev/null
@@ -0,0 +1,118 @@
+// ARMware - an ARM emulator
+// Copyright (C) <2007>  Wei Hu <wei.hu.tw@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INTERRUPT_CONTROLLER_H
+#define INTERRUPT_CONTROLLER_H
+
+#include <cstdint>
+
+#include "gpio_controller.h"
+#include "lcd_controller.h"
+#include "os_timer.h"
+#include "rtc.h"
+#include "uart.h"
+
+namespace SA1100 {
+
+class IntController {
+public:
+  IntController(const LCDController* lcdController, const OsTimer* osTimer,
+                const GPIOController* gpioController, const Uart* uart,
+                const RTC* rtc);
+
+  void run();
+  void reset();
+
+  uint32_t read32(const uint32_t address) const;
+  void write32(const uint32_t address, const uint32_t value);
+
+  inline bool havePendingIrq() const { return (0 == mICIP) ? false : true; }
+  inline bool havePendingFiq() const { return (0 == mICFP) ? false : true; }
+
+private:
+  void initRegisters();
+
+  void checkGpioInterruptStatus();
+  void checkOsTimerInterruptStatus();
+  void checkRtcInterruptStatus();
+  void checkLcdInterruptStatus();
+  void checkUartInterruptStatus();
+
+  enum IntCtrlBitMaskEnum {
+    GPIO_0_EDGE_BIT_MASK = (1 << 0),
+    GPIO_1_EDGE_BIT_MASK = (1 << 1),
+    GPIO_2_EDGE_BIT_MASK = (1 << 2),
+    GPIO_3_EDGE_BIT_MASK = (1 << 3),
+    GPIO_4_EDGE_BIT_MASK = (1 << 4),
+    GPIO_5_EDGE_BIT_MASK = (1 << 5),
+    GPIO_6_EDGE_BIT_MASK = (1 << 6),
+    GPIO_7_EDGE_BIT_MASK = (1 << 7),
+    GPIO_8_EDGE_BIT_MASK = (1 << 8),
+    GPIO_9_EDGE_BIT_MASK = (1 << 9),
+    GPIO_10_EDGE_BIT_MASK = (1 << 10),
+
+    GPIO_0_10_EDGE_BIT_MASK =
+        (GPIO_0_EDGE_BIT_MASK | GPIO_1_EDGE_BIT_MASK | GPIO_2_EDGE_BIT_MASK |
+         GPIO_3_EDGE_BIT_MASK | GPIO_4_EDGE_BIT_MASK | GPIO_5_EDGE_BIT_MASK |
+         GPIO_6_EDGE_BIT_MASK | GPIO_7_EDGE_BIT_MASK | GPIO_8_EDGE_BIT_MASK |
+         GPIO_9_EDGE_BIT_MASK | GPIO_10_EDGE_BIT_MASK),
+
+    GPIO_11_27_EDGE_BIT_MASK = (1 << 11),
+
+    LCD_SERVICE_BIT_MASK = (1 << 12),
+
+    SERIAL_1_BIT_MASK = (1 << 15),
+    SERIAL_2_BIT_MASK = (1 << 16),
+    SERIAL_3_BIT_MASK = (1 << 17),
+
+    OS_TIMER_0_BIT_MASK = (1 << 26),
+    OS_TIMER_1_BIT_MASK = (1 << 27),
+    OS_TIMER_2_BIT_MASK = (1 << 28),
+    OS_TIMER_3_BIT_MASK = (1 << 29),
+
+    RTC_HZ_BIT_MASK = (1 << 30),
+    RTC_ALARM_BIT_MASK = (1 << 31)
+  };
+
+  typedef enum IntCtrlBitMaskEnum IntCtrlBitMaskEnum;
+
+  enum {
+    ICIP = 0x90050000,
+    ICMR = 0x90050004,
+    ICLR = 0x90050008,
+    ICCR = 0x9005000C,
+    ICFP = 0x90050010,
+    ICPR = 0x90050020
+  };
+
+  const OsTimer* mOsTimer;
+  const LCDController* mLCDController;
+  const GPIOController* mGPIOController;
+  const RTC* mRTC;
+  const Uart* mUart;
+
+  uint32_t mICIP;
+  uint32_t mICMR;
+  uint32_t mICLR;
+  uint32_t mICCR;
+  uint32_t mICFP;
+  uint32_t mICPR;
+};
+
+} // namespace SA1100
+
+#endif // INTERRUPT_CONTROLLER_H