
#include "reg3664.h"
#include "keycode.h"

#define NULL 0

typedef int bool;
enum { false, true, };

#define cli() asm("orc #0x80,ccr")
#define sti() asm("andc #0x7f,ccr")

#define kb_set_clock_0() (PDR8 |= 0x10)
#define kb_set_clock_1() (PDR8 &= 0xef)

#define kb_set_data_0() (PDR8 |= 0x01)
#define kb_set_data_1() (PDR8 &= 0xfe)

#define pc_set_clock_0() (PDR8 |= 0x20)
#define pc_set_clock_1() (PDR8 &= 0xdf)

#define pc_set_data_0() (PDR8 |= 0x02)
#define pc_set_data_1() (PDR8 &= 0xfd)

#define kb_get_clock() ((PDR8 >> 6) & 1)
#define kb_get_data() ((PDR8 >> 2) & 1)

#define pc_get_clock() ((PDR8 >> 7) & 1)
#define pc_get_data() ((PDR8 >> 3) & 1)

#define irq1_raising_edge() (IEGR1 |= 0x02)
#define irq1_sinking_edge() (IEGR1 &= 0xfd)

#define irq2_raising_edge() (IEGR1 |= 0x08)
#define irq2_sinking_edge() (IEGR1 &= 0xf7)

#define clear_irq1() (IRR1 &= 0xfd)
#define clear_irq2() (IRR1 &= 0xfb)
#define clear_timerv_ca() (TCSRV &= 0xbf)
#define clear_timerv_cb() (TCSRV &= 0x7f)
#define clear_timerv_ov() (TCSRV &= 0xdf)

#define kb_enable() (IENR1 |= 0x02)
#define kb_disable() (IENR1 &= 0xfd)
#define pc_enable() (IENR1 |= 0x04)
#define pc_disable() (IENR1 &= 0xfb)

#define timerv_ca_enable() (TCRV0 |= 0x40)
#define timerv_ca_disable() (TCRV0 &= 0xbf)
#define timerv_ov_enable() (TCRV0 |= 0x20)
#define timerv_ov_disable() (TCRV0 &= 0xdf)

#define timer_enable() timerv_ca_enable()
#define timer_disable() timerv_ca_disable()

enum {
	QUEUESIZE = 64,
};

struct Queue {
	unsigned char pos;
	unsigned char len;
	unsigned short buf[QUEUESIZE];
};

struct Queue event_queue;
struct Queue kb_input_queue;
struct Queue kb_output_queue;
struct Queue pc_input_queue;
struct Queue pc_output_queue;

unsigned short kb_input_bits;
unsigned short kb_output_bits;
unsigned short pc_input_bits;
unsigned short pc_output_bits;

unsigned short kb_timeout;
unsigned short repeat_timeout;

extern void waitloop(unsigned long n);
#define wait_40us()		waitloop(40)
#define wait_15us()		waitloop(14)
#define wait_100ms()	waitloop(100000)
#define wait_1s()		waitloop(1000000)





void debug_init()
{
	int i;
	PMR1 |= 0x02;
	SMR = 0;
	BRR = 51;
	SCR3 = 0;
	for(i = 0;i < 200;i++)
		asm("nop");
	SCR3 = 0x30;
	SSR = 0x80;
}


void debug_putchar(int c)
{
	while (!(SSR & 0x80));
	TDR = c;
}

void debug_print(const char *str)
{
	while (*str)
		debug_putchar(*str++);
}

void debug_putdec(int c)
{
	int n;
	char tmp[10];

	for (n = 0; c && n < 9; n++) {
		tmp[n] = '0' + c % 10;
		c /= 10;
	}
	if (!n) tmp[n++] = 0;

	while (n > 0) {
		debug_putchar(tmp[--n]);
	}
}
void debug_puthex(int c)
{
	static char hex[] = "0123456789abcdef";

	debug_putchar(hex[(c >> 4) & 15]);
	debug_putchar(hex[c & 15]);
}
void debug_putcrlf()
{
	debug_putchar('\r');
	debug_putchar('\n');
}



void initialize_queue(struct Queue *q)
{
	q->pos = 0;
	q->len = 0;
}

inline long qpeek(struct Queue *q)
{
	return q->len > 0 ? (long)q->buf[q->pos] : -1L;
}

long qget(struct Queue *q)
{
	long c = qpeek(q);
	if (c >= 0) {
		q->pos = (q->pos + 1) % QUEUESIZE;
		q->len--;
	}
	return c;
}

void qput(struct Queue *q, unsigned short c)
{
	if (q->len < QUEUESIZE) {
		q->buf[(q->pos + q->len) % QUEUESIZE] = c;
		q->len++;
	}
}

void qunget(struct Queue *q, unsigned short c)
{
	if (q->len < QUEUESIZE) {
		q->pos = (q->pos + QUEUESIZE - 1) % QUEUESIZE;
		q->buf[q->pos] = c;
		q->len++;
	}
}


int countbits(unsigned short c)
{
	int i;
	for (i = 0; c; c >>= 1) {
		if (c & 1) i++;
	}
	return i;
}

// initializer

void initialize_kb_output()
{
	initialize_queue(&kb_output_queue);
	kb_output_bits = 0;
	clear_irq1();
}

void initialize_kb_input()
{
	initialize_queue(&kb_input_queue);
	kb_input_bits = 0;
	clear_irq1();
}


void initialize_pc_output()
{
	initialize_queue(&pc_output_queue);
	pc_output_bits = 0;
	clear_irq2();
}

void initialize_pc_input()
{
	initialize_queue(&pc_input_queue);
	pc_input_bits = 0;
	clear_irq2();
}




void kb_next_output(int c)
{
	int i;

	c &= 0xff;
	if ((countbits(c) & 1) == 0) c |= 0x100;	// make odd parity
	c = (c | 0x600) << 1;
	//
	// c = 000011pdddddddd0
	//                    
	//                    ^ start bit
	//            ^^^^^^^^  data
	//           ^          odd parity
	//          ^           stop bit
	//         ^            reply from keyboard (ack bit)
	//
	kb_output_bits = c;

	kb_set_clock_0();	// I/O inhibit
	wait_40us();
	kb_set_clock_1();

	kb_timeout = 0;
}

bool pc_send(int c)
{
	int i;
	unsigned short bits;

	bits = c & 0xff;
	if (!(countbits(bits) & 1)) bits |= 0x100;
	bits = (bits | 0x200) << 1;
	for (i = 0; i < 11; i++) {
		if (!pc_get_clock()) break;
		if (bits & 1)	pc_set_data_1();
		else			pc_set_data_0();
		bits >>= 1;
		wait_15us();
		if (!pc_get_clock()) break;
		pc_set_clock_0();
		wait_40us();
		pc_set_clock_1();
		wait_15us();
		wait_15us();
		if (i < 10 && !pc_get_clock()) break;
	}
	pc_set_data_1();

	return i == 11;
}

int pc_recv()
{
	int i;
	unsigned short bits;

	if (pc_get_data()) return -1;
	if (!pc_get_clock()) return -1;

	bits = 0;
	for (i = 0; i < 10; i++) {
		pc_set_clock_0();
		wait_40us();
		pc_set_clock_1();
		wait_15us();
		bits >>= 1;
		if (pc_get_data()) bits |= 0x200;
	}
	wait_40us();
	pc_set_data_0();
	wait_15us();
	pc_set_clock_0();
	wait_15us();
	pc_set_clock_1();
	pc_set_data_1();

	if (!(bits & 0x200))
		return -1;							// framing error

	if (!(countbits(bits & 0x1ff) & 1))
		return -1;							// parity error

	return bits & 0xff;
}





inline void kb_put(int c)
{
	qput(&kb_output_queue, c & 0xff);
}

inline int kb_get()
{
	int c;
	cli();
	c = qget(&kb_input_queue);
	sti();
	return c;
}

inline void pc_put(int c)
{
	qput(&pc_output_queue, c & 0xff);
}

inline int pc_get()
{
	return qget(&pc_input_queue);
}

inline long get_event()
{
	long e;
	cli();
	e = qget(&event_queue);
	sti();
	return e;
}

inline void put_event(unsigned short c)
{
	cli();
	qput(&event_queue, c);
	sti();
}

inline void unget_event(unsigned short c)
{
	cli();
	qunget(&event_queue, c);
	sti();
}





#pragma interrupt
void int_irq1()
{
	int c;

	kb_timeout = 10;	// 10ms

	if (!kb_input_bits) {
		if (kb_output_bits) {			// transmit mode
			if (kb_output_bits == 1) {
				kb_output_bits = 0;		// end transmit
				kb_timeout = 0;
			} else {
				if (kb_output_bits & 1) {
					kb_set_data_1();
				} else {
					kb_set_data_0();
				}
				kb_output_bits >>= 1;
			}
		} else {
			kb_input_bits = 0x800;		// start receive
		}
	}
	if (kb_input_bits) {
		kb_input_bits >>= 1;
		if (kb_get_data()) {
			kb_input_bits |= 0x800;
		}
		if (kb_input_bits & 1) {
			if (kb_input_bits & 0x800) {				// stop bit ?
				if (countbits(kb_input_bits & 0x7fc) & 1) {	// odd parity ?
					c = (kb_input_bits >> 2) & 0xff;
					qput(&kb_input_queue, c);
				}
			}
			kb_input_bits = 0;
			kb_timeout = 0;
		}
	}
	clear_irq1();
}

#pragma interrupt
void int_irq2()
{
	clear_irq2();
}


unsigned short count10 = 0;
bool timerevent = false;

extern void interval1ms();

#pragma interrupt
void int_timerv()
{
	count10++;
	if (count10 >= 10) {
		count10 = 0;
		timerevent = true;
	}
	TCSRV &= 0x1f;
}








void system_handler()
{
	int c;

	// receive from host
	c = pc_recv();
	if (c >= 0) {
		qput(&pc_input_queue, c);
	}

	// transmit to host
	c = qget(&pc_output_queue);
	if (c >= 0) {
		if (!pc_send(c)) {
			qunget(&pc_output_queue, c);	// failed
		}
	}

	// transmit to keyboard
	if (!kb_input_bits) {
		c = qget(&kb_output_queue);
		if (c >= 0) {
			kb_next_output(c);
		}
	}
}





enum {
	PC_INIT			= 0x0000,
	PC_NORMAL		= 0x0100,
	PC_ED			= 0x0200,
	PC_F0			= 0x0300,
	PC_F3			= 0x0400,
	PC_FB			= 0x0500,
	PC_FC			= 0x0600,
	PC_FD			= 0x0700,
	PC_FF			= 0x0800,
};

enum {
	KB_INIT			= 0x0000,
	KB_NORMAL		= 0x0100,
	KB_WAIT			= 0x0200,
	KB_ED			= 0x0300,
	KB_F3			= 0x0400,
};

enum {
	EVENT_NULL = 0,
	EVENT_INIT		= 0x0100,
	EVENT_KEYMAKE		= 0x0200,
	EVENT_KEYBREAK		= 0x0300,
	EVENT_SEND_KB_INDICATOR	= 0x0400,
	EVENT_SEND_KB_TYPEMATIC	= 0x0500,
};


unsigned short key_decode_state;
unsigned short pc_state;
unsigned short kb_state;
unsigned short shiftflags;
unsigned short pc_init_count;
unsigned short indicator;
unsigned short typematic;
unsigned short repeat_count;
unsigned short repeat_event;
unsigned short real_keyboard_id;
unsigned short fake_keyboard_id;
unsigned short _keyboard_id;
bool break_enable;
bool scan_enable;

static void _send_pc(int c, void *cookie)
{
	pc_put(c);
}

void handler()
{
	int c;
	long event;

	if (timerevent) {
		timerevent = false;
		if (pc_init_count) {
			if (!--pc_init_count) {
				pc_put(0xaa);
			}
		}
		if (repeat_count) {
			if (!--repeat_count) {
				if (repeat_event) {
					put_event(repeat_event);
					repeat_count = 13;
				}
			}
		}
		if (repeat_timeout) {
			if (!--repeat_timeout) {
				repeat_count = 0;
			}
		}
		cli();
		if (kb_timeout > 0) {
			if (kb_timeout > 1) {
				kb_timeout--;
			} else {
				kb_output_bits = 0;
				kb_input_bits = 0;
				kb_set_data_1();
				kb_set_clock_1();
				kb_timeout = 0;
			}
		}
		sti();
	}

	event = get_event();
	if (event > 0) {
		switch (event & 0xff00) {
		case EVENT_INIT:
			kb_put(0xff);
			kb_state = KB_INIT;
			break;
		case EVENT_KEYMAKE:
			c = event & 0xff;
			EncodeKeyCode(c, shiftflags | KEYFLAG_MAKE, _send_pc, NULL);
			switch (c) {
			case 44:	shiftflags |= KEYFLAG_SHIFT_L;		break;
			case 57:	shiftflags |= KEYFLAG_SHIFT_R;		break;
			case 58:	shiftflags |= KEYFLAG_CTRL_L;		break;
			case 64:	shiftflags |= KEYFLAG_CTRL_R;		break;
			case 60:	shiftflags |= KEYFLAG_ALT_L;		break;
			case 62:	shiftflags |= KEYFLAG_ALT_R;		break;
			}
			break;
		case EVENT_KEYBREAK:
			c = event & 0xff;
			EncodeKeyCode(c, shiftflags, _send_pc, NULL);
			switch (c) {
			case 44:	shiftflags &= ~KEYFLAG_SHIFT_L;		break;
			case 57:	shiftflags &= ~KEYFLAG_SHIFT_R;		break;
			case 58:	shiftflags &= ~KEYFLAG_CTRL_L;		break;
			case 64:	shiftflags &= ~KEYFLAG_CTRL_R;		break;
			case 60:	shiftflags &= ~KEYFLAG_ALT_L;		break;
			case 62:	shiftflags &= ~KEYFLAG_ALT_R;		break;
			}
			break;
		case EVENT_SEND_KB_INDICATOR:
			if (kb_state != KB_NORMAL) {
				unget_event(event);
			} else {
				kb_put(0xed);
				kb_state = KB_ED;
			}
			break;
		case EVENT_SEND_KB_TYPEMATIC:
			if (kb_state != KB_NORMAL) {
				unget_event(event);
			} else {
				kb_put(0xf3);
				kb_state = KB_F3;
			}
			break;
		}
	}

	c = kb_get();
	if (c >= 0) {
		unsigned short t;

		switch (kb_state) {
		case KB_INIT + 0:
			if (c == 0xfa) {
				c = -1;
			} if (c == 0xaa) {
				kb_put(0xf2);
				kb_state++;
				c = -1;
			}
			break;
		case KB_INIT + 1:
			if (c == 0xfa) {
				kb_state++;
				c = -1;
			}
			break;
		case KB_INIT + 2:	// receive keyboard ID 1st byte
			_keyboard_id = c;
			kb_state++;
			c = -1;
			break;
		case KB_INIT + 3:	// receive keyboard ID 2nd byte
			_keyboard_id |= c << 8;
			real_keyboard_id = _keyboard_id;
			kb_put(0xf3);
			kb_state++;
			c = -1;
			break;
		case KB_INIT + 4:
			if (c == 0xfa) {
				kb_put(0x00);
				kb_state++;
				c = -1;
			}
			break;
		case KB_INIT + 5:
			if (c == 0xfa) {
				kb_put(0xf4);
				c = -1;
			}
			kb_state = KB_WAIT;
			break;
		case KB_ED:
			if (c == 0xfa) {
				kb_put(indicator);
				kb_state = KB_WAIT;
				c = -1;
			} else {
				kb_state = KB_NORMAL;
			}
			break;
		case KB_F3:
			if (c == 0xfa) {
				kb_put(typematic);
				kb_state = KB_WAIT;
				c = -1;
			} else {
				kb_state = KB_NORMAL;
			}
			break;
		case KB_WAIT:
			kb_state = KB_NORMAL;
			c = -1;
			break;
		}

		switch (c) {
		case -1:
			break;
		case 0xaa:
			kb_put(0xf2);
			kb_state = KB_INIT + 1;
			break;
		case 0xfa:
		case 0xee:
		case 0xfc:
		case 0xfe:
		case 0xff:
			key_decode_state = 0;
			kb_state = KB_NORMAL;
			break;
		default:
			t = DecodeKeyCode(&key_decode_state, c);
			if (t && scan_enable) {
				unsigned short event;
				if (t & 0x8000) {
					event = (t & 0xff) | EVENT_KEYBREAK;
					if (break_enable) {
						put_event(event);
					}
					if ((event & 0xff) == (repeat_event & 0xff)) {
						repeat_event = 0;
						repeat_count = 0;
						repeat_timeout = 0;
					}
				} else {
					event = (t & 0xff) | EVENT_KEYMAKE;
					if (event == repeat_event) {
						if (repeat_count) {
							repeat_timeout = 600;
						}
					} else {
						put_event(event);
						repeat_event = event;
						repeat_timeout = 1200;
						repeat_count = 200;

						switch (event & 0xff) {
						case 126:
							repeat_count = 0;	// inhibit repeat
							break;
						}
					}
				}
			}
			break;
		}
	}

	c = pc_get();
	if (c >= 0) {

		switch (pc_state & 0xff00) {
		case PC_ED:
			indicator = c;
			if (c & 2) {
				shiftflags |= KEYFLAG_NUMLOCK;
			} else {
				shiftflags &= ~KEYFLAG_NUMLOCK;
			}
			pc_put(0xfa);
			pc_state = PC_NORMAL;
			put_event(EVENT_SEND_KB_INDICATOR);
			break;
		case PC_F3:
			typematic = c;
			pc_put(0xfa);
			pc_state = PC_NORMAL;
			put_event(EVENT_SEND_KB_TYPEMATIC);
			break;
		default:
			switch (c) {
			case 0xed:
				pc_put(0xfa);
				pc_state = PC_ED;
				break;
			case 0xee:
				pc_put(0xee);
				break;
			case 0xf1:
				pc_put(0xfa);
				break;
			case 0xf2:
				pc_put(0xfa);
				pc_put(fake_keyboard_id);
				pc_put(fake_keyboard_id >> 8);
				break;
			case 0xf3:
				pc_put(0xfa);
				pc_state = PC_F3;
				break;
			case 0xf4:
			case 0xf5:
			case 0xf6:
			case 0xf7:
			case 0xf8:
			case 0xf9:
			case 0xfa:
			case 0xfb:
			case 0xfc:
			case 0xfd:
				pc_put(0xfa);
				break;
			case 0xfe:
				break;
			case 0xff:
				kb_put(0xff);
				pc_put(0xfa);
				pc_init_count = 400;		// after 400ms...
				break;
			default:
				pc_put(0xfe);
				break;
			}
		}
	}
}




int main()
{
	int c;
	unsigned short t;

	// external interrupt pins

	PMR1 = 0x60;
	PCR1 = 0x00;

	// PS/2 keyboard pins

	PDR8 = 0x00;
	PCR8 = 0x33;

	// setup external interrupt

	IEGR1 = 0x00;
	IENR1 = 0x10;

	// setup 10kHz interval timer

	TIOR1 = 0x00;
	TCRV0 = 0x0a;
	TCRV1 = 0xe2;
	TCORA = 99;			// 16000000 / 16 / 100 = 10000

	// for debugging ports

	PMR5 = 0x00;
	PCR5 = 0xff;
	PDR5 = 0x00;

	// start!

	kb_set_clock_0();
	kb_set_data_1();

	initialize_queue(&event_queue);
	initialize_kb_input();
	initialize_kb_output();
	initialize_pc_input();
	initialize_pc_output();

	pc_state = PC_INIT;
	kb_state = KB_INIT;
	key_decode_state = 0;
	shiftflags = 0;
	pc_init_count = 0;
	indicator = 0;
	typematic = 0x64;
	real_keyboard_id = 0;
	fake_keyboard_id = 0x83ab;
	break_enable = true;
	scan_enable = true;

	kb_set_clock_1();
	timer_enable();
	kb_enable();
	IRR1 = 0x00;
	sti();

	debug_init();
	debug_print("\r\nQuckey!\r\n\r\n");

	pc_init_count = 1000;		// delay for initialize

	// event loop

	while (1) {
#if 01
		handler();
#else
		c = kb_get();
		if (c >= 0) {
			debug_print("K");
			debug_puthex(c);
			debug_putcrlf();
			pc_put(c);
		}
		c = pc_get();
		if (c >= 0) {
			debug_print("H");
			debug_puthex(c);
			debug_putcrlf();
			kb_put(c);
		}
#endif
		system_handler();
	}

	return 0;
}





