/*
 * Copyright(C) Paul und Scherer (mct.de/mct.net)
 *
 * This example demonstrates how to...
 *
 *  ... install interrupt mode for stdin using
 *      redirection to a user-defined function.
 */

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

static unsigned char fifo[256];			// char buffer
static unsigned char wind;			// write index
static unsigned char rind;			// read  index

/*
 * UART0 interrupt service
 */
static void __attribute__((interrupt))		// handle as ISR!
u0_isr(void)
{
	Intern_u0iir;				// clear int flag
	Intern_vicvectaddr = 0;			// reset VIC priority logic
	fifo[wind++] = Intern_u0rbr;		// put received char to buf
}

/*
 * User I/O
 *
 * Only the std input is redirected to use this function.
 * So, the file descriptor can be ignored, and only getc
 * and ungetc need to be done.
 *
 * action =0 =flush
 *         1 =getc
 *         2 =putc
 *         3 =ungetc
 *
 * Return byte read or <0 if no byte available for getc,
 * else 0.
 */
static int
myio(int fd, int action, int c)
{
	static int ungetch = -1;
	int r = 0;

	switch (action) {
	case 1: // getc
		if ((r = ungetch) >= 0) ungetch = -1;
		else if (rind != wind) r = fifo[rind++];
		break;

	case 3: // ungetc
		ungetch = (unsigned char)c;
		break;
	}
	return r;
}

/*
 * The main program
 *
 *  - initializes the UART0 receiver interrupt
 *
 *  - sets _usriop to the I/O function address
 *
 *  - overwrites the file descriptor for stdin
 *    with a value >4 which redirects stdin to
 *    _usriop().
 */
int
main(void)
{
	Intern_vicvectaddr0 = (long)u0_isr;	// set ISR addr
	Intern_vicintenable = 0x40;		// enable u0 int
	Intern_vicvectcntl0 = 0x26;		//  vector

	Intern_u0ier = 1;			// enable u0 rec int
	Intern_u0iir;				// clear pending int

	ENABLE_INTERRUPTS;			// enable core ints

	_usriop = myio;				// set user I/O addr
	*stdin  = 6;				// stdin -> _usriop()

	/*
	 * All following operations on stdin use now interrupt mode.
	 */
	while (1) getchar();			// simply echo input
}
