#include "pwrglove.h"
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <hardware/cia.h>
#include <resources/cia.h>

#include <proto/exec.h>

static volatile int times_up;
extern struct CIA ciab;
static struct Library *CIAResource;
static struct Interrupt interrupt;
static UBYTE interrupting;

void closetimer(void);

void
fatal(char *s)
{
	printf("fatal error: %s\n", s);
	closetimer();
	exit(1);
}

void
timerhandler()
{
	/* hardware timer interrupt */
	times_up = 1;
}

void
timersleep(long ticks)
{
	times_up = 0;

	ticks -= 8; if (ticks <= 0) { times_up=1; return; } /* yucko */

	/* 1.397 usec * n */
	ciab.ciatblo = ticks & 0xff;
	ciab.ciatbhi = (ticks>>8) & 0xff;

	/* one-shot */
	ciab.ciacrb |= CIACRBF_RUNMODE | CIACRBF_LOAD | CIACRBF_START;

	while (!times_up) {}
}

void
opentimer()
{
	static char* timername = "glove.timer";

	/* install interrupt handler */
	CIAResource = OpenResource(CIABNAME);
	if (CIAResource == NULL) fatal("can't get timer resource");

	interrupt.is_Node.ln_Type = NT_INTERRUPT;
	interrupt.is_Node.ln_Pri = 127;
	interrupt.is_Node.ln_Name = timername;
	interrupt.is_Data = NULL;
	interrupt.is_Code = timerhandler;
	if (AddICRVector(CIAResource, CIAICRB_TB, &interrupt) != NULL) {
		fatal("can't use CIA B Timer B");
	}

	/* this timer is all mine */
	interrupting = 1;

	SetICR(CIAResource, CIAICRF_TB);
	AbleICR(CIAResource, CIAICRF_SETCLR | CIAICRF_TB);
	times_up = 0;
}

void
closetimer()
{
	if (interrupting) {
		ciab.ciacrb &= ~CIACRBF_START;
		AbleICR(CIAResource, CIAICRF_TB);
		RemICRVector(CIAResource, CIAICRB_TB, &interrupt);
		interrupting = 0;
	}
}

extern struct CIA far ciaa;

/* bits from parallel port -- Alan's hack */
#define		GDATA           0x04    /* glove data in */
#define		GLATCH          0x02    /* glove latch out */
#define		GCLOCK          0x01    /* glove clock out */
#define		GCLOLAT         (GLATCH|GCLOCK) /* latch and clock */

#define getbit()  	(ciaa.ciaprb & GDATA) >> 2
#define initport()	ciaa.ciaddrb = GCLOLAT

/* delays in microseconds */
#define     D2BYTES      96
#define     D2BITS       22
#define     D2SLOW       14720

#define     C0L0()       ciaa.ciaprb = 0        /* clock 0 latch 0 */
#define     C0L1()       ciaa.ciaprb = GLATCH   /* clock 0 latch 1 */
#define     C1L0()       ciaa.ciaprb = GCLOCK   /* clock 1 latch 0 */
#define     C1L1()       ciaa.ciaprb = GCLOLAT  /* clock 1 latch 1 */

#define     setporta()	delay(3)
#define     setportb()  delay(3)

unsigned char getbyte (void);

/* convert microseconds to cia ticks */
#define delay(usec) timersleep((usec*1397)/1000)

int control_c()
{
	closetimer();
	printf("<<goodbye>>\n");
	return 1; /* causes exit */
}

void query_glove(glove_data *g)
{
	unsigned char buf[12];
	register char *bp;

	/* read 12 byte packet */
	bp = buf;
	*bp++ = getbyte ();
	delay (D2BYTES);
	*bp++ = getbyte ();
	delay (D2BYTES);
	*bp++ = getbyte ();
	delay (D2BYTES);
	*bp++ = getbyte ();
	delay (D2BYTES);
	*bp++ = getbyte ();
	delay (D2BYTES);
	*bp++ = getbyte ();
	delay (D2BYTES);
	*bp++ = getbyte ();
	delay (D2BYTES);
	*bp++ = getbyte ();
	delay (D2SLOW);
	*bp++ = getbyte ();
	delay (D2SLOW);
	*bp++ = getbyte ();
	delay (D2SLOW);
	*bp++ = getbyte ();
	delay (D2SLOW);
	*bp++ = getbyte ();
	delay (D2SLOW);

	/* Glove packet isn't quite as described above */
	/* Let's see if we can figure it out */
	{
		int i,k,n;

		/* look for FF FF A0 */
		/* almost always starts at offset 7 so look there first */
		n = -1;
		for (k=0,i=7; k<12; ++k,++i) {
			if (buf[i%12] == (unsigned char)0xff &&
			    buf[(i+1)%12] == (unsigned char)0xff &&
			    buf[(i+2)%12] == (unsigned char)0xa0) {

				/* yah! */
				n = (i+3)%12;
				g->x = buf[n];
				g->y = buf[(n+1)%12];
				g->z = buf[(n+2)%12];
				g->rot = buf[(n+3)%12];
				g->fingers = buf[(n+4)%12];
				g->keys = buf[(n+5)%12];
				g->dum0 = 0xa0;
				g->dum7 = buf[(n+6)%12];
				g->dum8 = buf[(n+7)%12];
				g->dum9 = buf[(n+8)%12];
				g->dumA = 0xff;
				g->dumB = 0xff;
				return;
			}
		}
		/* no valid data found */
	}
}

unsigned char getbyte ()
{
	register unsigned Glov = 0;	

	/* prepare port b as output port */
	setportb ();

        /* generate a reset (latch) pulse */
	C1L0 ();
	C1L1 ();
	delay(5); /* 5 us delay */
	C1L0 ();

	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();

	return (unsigned char) Glov;  /* return the byte */
}


void init_glove()
{
	register unsigned char Glov = 0;

	opentimer();
	onbreak(control_c);

	/* initialize hardware interface */
	initport();

	/* read 4 bits from glove */
	setportb ();

        /* generate a reset (latch) pulse */
	C1L0 ();
	C1L1 ();
	delay(5);  /* 5 us delay */
	C1L0 ();

	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* configure port a as input */
	setporta ();

	/* read a bit */
	Glov <<= 1;
	Glov |= getbit();

	/* prepare port b as output port */
	setportb ();

	/* generate a clock pulse */
	C0L0 ();
	C1L0 ();
	
	/* end of read 4 bits */

	/* prepare port b as output port */
	setportb ();

	C1L0 ();
	delay(7212);
/*	delay (16950);  /* 7212 us delay */

	setportb ();

	C1L1 ();
	delay(2260);
/*	delay (4750);   /* 2260 us delay */

	/* prepare port b as output port */
	setportb ();

	C1L0 ();	/* Start of 1. Byte */
	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);
	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);	

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C1L1 ();
	C0L1 ();
	C1L1 ();
	delay (D2BITS);
	
	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);	

	/* prepare port b as output port */
	setportb ();

	C1L0 ();
	C0L0 ();
	C1L0 ();
	delay (D2BYTES);		

	/* prepare port b as output port */
	setportb ();

	C1L1 ();	/* Start of 2. Byte */
	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C1L0 ();
	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C1L1 ();
	C0L1 ();
	C1L1 ();
	delay (D2BYTES);

	/* prepare port b as output port */
	setportb ();

	C1L0 ();	/* Start of 3. Byte */
	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);	

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);	

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);	

	/* prepare port b as output port */
	setportb ();

	C1L1 ();
	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C1L0 ();
	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BYTES);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();	/* start of 4. byte */
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BYTES);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();	/* start of 5. byte */
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C1L1 ();
	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C1L0 ();
	C0L0 ();
	C1L0 ();
	delay (D2BYTES);

	/* prepare port b as output port */
	setportb ();

	C1L1 ();	/* start of 6. byte */
	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L1 ();
	C1L1 ();
	delay (D2BYTES);

	/* prepare port b as output port */
	setportb ();

	C1L0 ();	/* start of 7. byte */
	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C0L0 ();
	C1L0 ();
	delay (D2BITS);

	/* prepare port b as output port */
	setportb ();

	C1L1 ();
	C0L1 ();
	C1L1 ();
	delay(892);
/*	delay (1090); /* 892 us delay (end of 7. byte) */

	/* prepare port b as output port */
	setportb ();

	C1L0 ();
	delay(50000);
/*	delay (60000);   /* some time for the glove controller
						to relax */
}
