/*
 * Copyright(C) Paul und Scherer (mct.de/mct.net)
 *
 * This example demonstrates how to...
 *
 *  ... install handlers for SWI/IRQ/FIQ
 *      using the interrupt vector table.
 *
 *  ... use programmed interrupt-IRQ/FIQ.
 */

#include <stdio.h>
#include <sys/arm7tdmi.h>
#include <target.h>

/*
 * The interrupt entry points and vectors are located in RAM at the
 * beginning of the data segment. When an exception occurs, program
 * execution is continued at the corresponding entry point.
 *
 * Each entry point contains an instruction loading the PC with the
 * value of the corresponding vector.
 *
 * All vectors are located immediately after the entry points, and
 * are by default initialized with _exit (idle loop).
 *
 *  Address           Entry point            loads vector from
 *  ----------------  ---------------------  -----------------
 *  data start + 0*4: reset (not used)
 *               1*4: undefined instruction  data start + 9*4
 *               2*4: SWI                                10*4
 *               3*4: prefetch abort                     11*4
 *               4*4: data     abort                     12*4
 *               5*4: reserved
 *               6*4: IRQ                                14*4
 *               7*4: FIQ                                15*4
 *
 *                    Vector                 defaults to
 *                    ---------------------  -----------------
 *               8*4: not used
 *               9*4: undefined instruction  _exit
 *              10*4: SWI                    _exit
 *              11*4: prefetch abort         _exit
 *              12*4: data     abort         _exit
 *              13*4: not used
 *              14*4: IRQ                    _exit
 *              15*4: FIQ                    _exit
 */
extern char _bdata;				// data start
#define SWI_VEC	(*((long *)&_bdata+10))		// SWI vector
#define IRQ_VEC	(*((long *)&_bdata+14))		// IRQ vector
#define FIQ_VEC	(*((long *)&_bdata+15))		// FIQ vector

static int swi_cnt;				// SWI count
static int t1irq_cnt, swirq_cnt;		// hard/soft IRQ count
static int t2fiq_cnt, swfiq_cnt;		//           FIQ count

/*
 * SWI service
 *
 * Exception handlers other than IRQ and FIQ use
 * a different stack frame, and need to know the
 * kind of exception to handle (UNDEF/SWI/ABORT).
 */
static void __attribute__((interrupt("SWI")))	// handle as SWI-type ISR!
swi_sr(void)
{
	swi_cnt++;				// up count
}

/*
 * IRQ service
 *
 * IRQSTA polling required (Timer1/programmed).
 */
static void __attribute__((interrupt))		// handle as ISR!
irq_sr(void)
{
	if (Intern_irqsta&2) {			// programmed IRQ?
		Intern_swicfg = 0;		// clear int flag
		swirq_cnt++;			// up soft count 
	} else	{
		Intern_t1clri = 0;		// clear t1 int flag
		t1irq_cnt++;			// up hard count
	}
}

/*
 * FIQ service
 *
 * FIQSTA polling required (Timer2/programmed).
 */
static void __attribute__((interrupt))		// handle as ISR!
fiq_sr(void)
{
	if (Intern_fiqsta&2) {			// programmed FIQ?
		Intern_swicfg = 0;		// clear int flag
		swfiq_cnt++;			// up soft count 
	} else {
		Intern_t2clri = 0;		// clear t2 int flag
		t2fiq_cnt++;			// up hard count
	}
}

/*
 * Timer1 is setup to generate an interrupt request (IRQ) once a second, and
 * Timer2 is setup to generate a fast interrupt request (FIQ) twice a second.
 *
 * Hitting 'i', or 'f' also triggers an IRQ/FIQ (programmed interrupt), with
 * 's' an SWI is generated.
 *
 * Any key displays the count for each kind of interrupt.
 */
int
main(void)
{
	SWI_VEC = (long)swi_sr;			// set SWI SR addr
	IRQ_VEC = (long)irq_sr;			//     IRQ SR addr
	FIQ_VEC = (long)fiq_sr;			//     FIQ SR addr
	Intern_irqen = 0x08;			// enable t1 for IRQ
	Intern_fiqen = 0x10;			//        t2 for FIQ

	Intern_t1ld  = _CCLK;			// set t1 load to 1s
	Intern_t1con = 0xc0;			// periodic @cclk

	Intern_t2ld  = _XCLK/2;			// set t2 load to 0.5s
	Intern_t2con = 0xc0;			// periodic @xclk

	ENABLE_INTERRUPTS;			// enable core ints

	puts("Hit 's' for SWI\n"
	     "    'i' for soft IRQ\n"
	     "    'f' for soft FIQ\n\n"
	     "or any key to display interrupt counts..."
	);
	while (1) {
		switch (getchar()) {
		case 's': asm(" swi");	     break;	// SWI
		case 'i': Intern_irqen  = 2;		// enable soft IRQ
			  Intern_swicfg = 2; break;	// trigger...
		case 'f': Intern_fiqen  = 2;		// enable soft FIQ
			  Intern_swicfg = 4; break;	// trigger...
		}
		printf("\nSWI: %d, IRQ (t1/sw): %d/%d, FIQ (t2/sw): %d/%d\n",
			swi_cnt,
			t1irq_cnt, swirq_cnt,
			t2fiq_cnt, swfiq_cnt
		);
	}
}
