/**
 * This example is for communication with a PS/2 host device, the 
 * opposite of 0012-keyboard.c.  See 0012-keyboard.c for pin details.
 *
 * For more details on the PS/2 protocol, see Adam Chapweske's
 * very helpful page at http://computer-engineering.org/ps2protocol/
 */
#define __16f628a
// If KHZ is not specified by the makefile, assume it to be 4 MHZ
#ifndef KHZ
#define KHZ	4000
#endif

#include "pic16f628a.h"
#include "tsmtypes.h"
#include "tsmdelay.h"
#include "tsmserial.h"

// Set the __CONFIG word:
Uint16 __at 0x2007  __CONFIG = CONFIG_WORD;

// RX_PORT, TX_PORT are fixed.  The 16f628a can use only these pins as RS232.
enum
{
	// PS2 clock attached to B0
	CLK_PORT=0,
	// RS232 receive hardwired to B1
	RX_PORT=1,
	// RS232 send hardwired to B2
	TX_PORT=2,
	// PS2 data attached to B3
	DAT_PORT=3,
	// LED attached to B5
	FIN_PORT=5,

	CLK_BIT=(1<<CLK_PORT),
	TX_BIT=(1<<TX_PORT),
	RX_BIT=(1<<RX_PORT),
	DAT_BIT=(1<<DAT_PORT),
	FIN_BIT=(1<<FIN_PORT)
};

// Twiddle these as you like BUT remember that not all values work right!
// See the datasheet for what values can work with what clock frequencies.
#define	BAUD	9600
#define BAUD_HI	1

enum
{
	// Does not pull clock or data low.
	PS2_IDLE=(TX_BIT|RX_BIT|CLK_BIT|DAT_BIT),
	// Pulls clock low.
	PS2_ASSERT=(TX_BIT|RX_BIT|DAT_BIT),
	// Pulls data low.
	PS2_ZERO=(TX_BIT|RX_BIT|CLK_BIT),
	// Pulls clock and data low.
	PS2_ASSERTZ=(TX_BIT|RX_BIT),
	// Pulls clock low
	PS2_INHIBIT=(TX_BIT|RX_BIT|CLK_BIT)
};

// Wait for high-to-low transition on clock.
#define PS2_WAITLO()	while(PORTB&CLK_BIT)
// Wait for low-to-high transition on clock.
#define PS2_WAITHI()	while(!(PORTB&CLK_BIT))
// Wait for data to go low
#define PS2_WAITDAT()	while(PORTB&DAT_BIT)

#define DELAY_US	(50L)

#define SCOPE_TRIGGER()	do	{	\
	PORTB=FIN_BIT;			\
	DELAY_SMALL_US(200);		\
	PORTB=0;			\
	}	while(0)

void ps2_sendbit(Uint8 b)
{
	if(b&0x01)	TRISB=PS2_IDLE;
	else		TRISB=PS2_ZERO;

	DELAY_SMALL_US(DELAY_US);

	if(b&0x01)	TRISB=PS2_ASSERT;
	else		TRISB=PS2_ASSERTZ;

	DELAY_SMALL_US(DELAY_US);
}

Uint8 ps2;
volatile Uint8 ps2in;

static void Intr(void) __interrupt 0
{
	static Uint8 n;

	GIE=0;	// Turn off other interrupts for the duration

	ps2in=0;

			//	1)	Other side has brought CLOCK low.	
	PS2_WAITHI();	// 	2)	Wait for host to stop asserting clock
	PS2_WAITDAT();	//	3)	Wait for host to bring data line low

	TRISB=PS2_ASSERT;	// 4)	Bring clock line low.

	// We don't need to get the start bit.  That's already been, up
	// at WAITDAT
	for(n=0; n<8; n++)
	{
		// Host sets data when clock is low
		// We read data when clock is high
		ps2in >>= 1;

		//	5)	Other side writes data
		DELAY_SMALL_US(DELAY_US);
		//	6)	Bring clock high
		TRISB=PS2_IDLE;

		// 	7)	Read data
		if(PORTB & DAT_BIT)		
		{
			ps2in|=0x80;
		}

		DELAY_SMALL_US(DELAY_US);
		//	Bring clock low
		TRISB=PS2_ASSERT;	// 8)	Bring clock line low.
	}

	// 9)	Other side sets parity bit
	DELAY_SMALL_US(DELAY_US);
	// 10)	Bring clock high
	TRISB=PS2_IDLE;
	// 11)	Read parity bit (n/a)
	DELAY_SMALL_US(DELAY_US);

		// Acknowledge

		TRISB=PS2_ASSERT;
		DELAY_SMALL_US(DELAY_US);
		TRISB=PS2_IDLE;
		DELAY_SMALL_US(DELAY_US);

	// 12)	Other side waits for clock high

	// 13)	Bring clock and data low.
	TRISB=PS2_ASSERTZ;
	DELAY_SMALL_US(DELAY_US);
	// 14)	Bring data high.
	TRISB=PS2_ASSERT;
	DELAY_SMALL_US(DELAY_US);
	// 15)	Bring clock high.
	TRISB=PS2_IDLE;

	// Clear B0 interrupt so it can reoccur
	INTF=0;

	// Communication is now done.
}

#define ps2host_sendchar(X)	ps2=(X),_ps2host_sendchar()

void _ps2host_sendchar(void)
{
	Uint8 n;
	static Uint8 p;	// Parity

	p=0x01;	// Odd parity starts as 1 with all-zero data

	GIE=0;

	ps2_sendbit(0);	// Send start bit

	// Send D0-D7
	for(n=0; n<8; n++)
	{
		if(ps2&0x01)	p^=0x01;
		ps2_sendbit(ps2);
		ps2 >>= 1;
	}

	ps2_sendbit(p);	// Send parity
	ps2_sendbit(1);	// Send stop

	TRISB=PS2_IDLE;	// Return clock to idle state

	INTF=0;
	GIE=1;
}

void main(void)
{
	NOT_RBPU=0;	// Enable pullups

	TRISB=PS2_IDLE;		// Setup I/O on port B
	PORTB=0;

	ASYNC_INIT();

	ps2in=0;

	INTCON=0x00;	// Clear interrupt register completely
	INTE=1;		// Set B0 interrupt enable
	INTEDG=0;	// Interrupt on falling edge
	GIE=1;

	DELAY_BIG_US(5000L);
	SCOPE_TRIGGER();

	// Send on-init signal
	ps2host_sendchar(0xAA);

	while(1)
	{
		switch(ps2in)
		{
		case 0xEE:
//			PORTB=FIN_BIT;
//			DELAY_BIG_US(1000L);
			ps2host_sendchar(0xEE);
		default:
			SENDHEX(ps2in);
			ps2in=0;
		case 0:
			break;
		}
	}

}


