// Copyright (c) 2001-2004 Rowley Associates Limited.
//
// This file may be distributed under the terms of the License Agreement
// provided with this software.
//
// THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
////////////////////////////////////////////////////////////////////////////////
//
//                    Atmel AT91SAM7 Interrupt Example
//
// Description
// -----------
// This example demonstrates a simple interrupt handler. When running, pressing
// the SW1-SW4 buttons causes the irq_handler to execute which toggles
// the LEDs.
//
////////////////////////////////////////////////////////////////////////////////

#include <targets/AT91SAM7.h>

// Interrupt definitions
#define AIC_SMR(n) (*(&AIC_SMR0 + n))
#define AIC_SVR(n) (*(&AIC_SVR0 + n))

#define AIC_SRCTYPE_INT_LEVEL_SENSITIVE 0x00    /* Level Sensitive */
#define AIC_SRCTYPE_INT_EDGE_TRIGGERED  0x20    /* Edge Triggered */
#define AIC_SRCTYPE_EXT_LOW_LEVEL       0x00    /* Low Level */
#define AIC_SRCTYPE_EXT_NEGATIVE_EDGE   0x20    /* Negative Edge */
#define AIC_SRCTYPE_EXT_HIGH_LEVEL      0x40    /* High Level */
#define AIC_SRCTYPE_EXT_POSITIVE_EDGE   0x60    /* Positive Edge */

#define SW1 (1 << 19)
#define SW2 (1 << 20)
#define SW3 (1 << 15)
#define SW4 (1 << 14)

#define PIOA_ID 2

static void
ledInit(void)
{
  PIOA_PER = 0x0000000F;
  PIOA_OER = 0x0000000F;
}

static void
ledOn(int n)
{
  PIOA_CODR = 1 << n;
}

static void
ledOff(int n)
{
  PIOA_SODR = 1 << n;
}

void pioa_irq_handler(void) __attribute__ ((interrupt ("IRQ")));

void
pioa_irq_handler(void)
{
  unsigned long isr = PIOA_ISR;
  unsigned long pdsr = PIOA_PDSR;
  if (isr & SW1)
    {
      if (pdsr & SW1)
        ledOn(0);
      else
        ledOff(0);
    }
  if (isr & SW2)
    {
      if (pdsr & SW2)
        ledOn(1);
      else
        ledOff(1);
    }
  if (isr & SW3)
    {
      if (pdsr & SW3)
        ledOn(2);
      else
        ledOff(2);
    }
  if (isr & SW4)
    {
      if (pdsr & SW4)
        ledOn(3);
      else
        ledOff(3);
    }
  AIC_EOICR = 0;
}

int
main(void)
{
  ledInit();
  
  // Put LDR PC, [PC, #-0xF20] instruction into IRQ vector in order to use 
  // hardware interrupt vectoring.
  *(volatile unsigned int *)0x00000018 = 0xE51FFF20;

  // Enable PIO clocks.
  PMC_PCER = 1 << PIOA_ID;

   // Set up PIO interrupt
  AIC_IDCR = 1 << PIOA_ID;
  AIC_SVR(PIOA_ID) = (unsigned)pioa_irq_handler;
  AIC_SMR(PIOA_ID) = AIC_SRCTYPE_INT_LEVEL_SENSITIVE;
  AIC_ICCR = 1 << PIOA_ID;
  AIC_IECR = 1 << PIOA_ID;
  PIOA_ODR = SW1 | SW2 | SW3 | SW4;
  PIOA_PER = SW1 | SW2 | SW3 | SW4;
  PIOA_MDDR = ~(SW1 | SW2 | SW3 | SW4);
  PIOA_MDER = SW1 | SW2 |SW3 | SW4;
  PIOA_IER = SW1 | SW2 | SW3 | SW4;

   __ARMLIB_enableIRQ();

  while(1);

}
