#include<stdio.h>
#include<stdlib.h>
#include<termios.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>

#define SQDP	0
#define DPKB	1

typedef struct
{
	char code;
	char *str;
	int cnt;
} code_t;

// display control codes
code_t from_sq80[]={
	{0xd0,"blink on",0},
	{0xd1,"blink off",0},
	{0xd2,"",0},
	{0xd3,"",0},
	{0xd4,"cursor right",0},
	{0xd5,"cursor left",0},
	{0xd6,"cls",0},
	{0xd7,"",0},
	{0xd8,"",0},
	{0xd9,"underline",0},
	{0xda,"",0},
	{0xdb,"erase",0},
	{0xdc,"",0},
	{0xdd,"",0},
	{0xde,"???",0},
	{0xdf,"",0},
	
	{0xe0,"",0},
	{0xe1,"",0},
	{0xe2,"",0},
	{0xe3,"",0},
	{0xe4,"",0},
	{0xe5,"",0},
	{0xe6,"",0},
	{0xe7,"",0},
	{0xe8,"???",0},
	{0xe9,"",0},
	{0xea,"",0},
	{0xeb,"",0},
	{0xec,"",0},
	{0xed,"",0},
	{0xee,"",0},
	{0xef,"",0},

	{0xf0,"",0},
	{0xf1,"",0},
	{0xf2,"",0},
	{0xf3,"",0},
	{0xf4,"",0},
	{0xf5,"",0},
	{0xf6,"",0},
	{0xf7,"",0},
	{0xf8,"",0},
	{0xf9,"de-underline chars",1},
	{0xfa,"underline chars",1},
	{0xfb,"KPC command",1},
	{0xfc,"",0},
	{0xfd,"",0},
	{0xfe,"",0},
	{0xff,"",0}
};

// keyboard control codes (0xfb xx)
code_t fbcodes[]={
	{0xd0,"Kbd Hardness Soft-0"},
	{0xd1,"Kbd Hardness Soft-1"},
	{0xd2,"Kbd Hardness Soft-2"},
	{0xd3,"Kbd Hardness Soft-3"},
	{0xd4,"Kbd Hardness Med-0"},
	{0xd5,"Kbd Hardness Med-1"},
	{0xd6,"Kbd Hardness Med-2"},
	{0xd7,"Kbd Hardness Med-3"},
	{0xd8,"Kbd Hardness Firm-0"},
	{0xd9,"Kbd Hardness Firm-1"},
	{0xda,"Kbd Hardness Firm-2"},
	{0xdb,"Kbd Hardness Firm-3"},
	{0xdc,"Kbd Hardness Hard-0"},
	{0xdd,"Kbd Hardness Hard-1"},
	{0xde,"Kbd Hardness Hard-2"},
	{0xdf,"Kbd Hardness Hard-3"},

	{0xe5,"AT=Poly"},
	{0xe6,"AT=Channel"},
	
	{0xf0,"Request KPC Init"},
	{0xfc,"Request KPC OS"},
	{0xfd,"Request Keyboard Calibration"},
	{0xfe,"AT=off"},
	{0xff,"Reset"},
	
	{0x00,""}
};

char *buttons[]={
	"SEQ","CART A","CART B","INT","BANK1","BANK2","BANK3","BANK4",		// 00-07
	"COMPARE","INC","DEC","WRITE","S0","S1","S2","S3",			// 08-0f
	"S5","S6","S7","S8","S9","S4","LFO1","ENV2",				// 10-17
	"MASTER","CREATE","TRK/SEL","REC","STORAGE","EDIT","TRK/MIX","STOP",	// 18-1f
	"MIDI","CONTROL","LOCATE","PLAY","OSC1","OSC2","OSC3","ENV1",		// 20-27
	"DCA1","DCA2","DCA3","LFO2","LFO3","FILTER","ENV4","ENV3",		// 28-2f
	"DCA4","MODES","SPLIT/LAYER","","","","","",				// 30-37
	"","","","","","","",""							// 38-3f
};

int open_serial(int serial)
{
	int fd;
	struct termios tty;
	char *device;
	
	device=(char *)malloc(12);
	memset(device,0,12);
	sprintf(device,"/dev/ttyS%d",serial);
	
	fd=open(device,O_RDWR|O_NONBLOCK);
	if(fd==-1)
	{
		perror("open failed");
		exit(-1);
	}
	
	tcgetattr(fd,&tty);
	tty.c_iflag=IGNBRK|IGNPAR;	// no break, no parity;
	tty.c_oflag=0;
	tty.c_lflag=0;
	tty.c_cflag=CS8|CLOCAL;		// 8bit, no modem control
	cfsetispeed(&tty,B57600);	// 62.5 = 57.6 * 16/14.7456
	cfsetospeed(&tty,B57600);
	tcsetattr(fd,TCSAFLUSH,&tty);
	
	return fd;
}

void close_serial(int fd)
{
	close(fd);
}

// sq80 to display
int analyze_sqdp(int fd, char buf, int last)
{
int x=0, i;

	printf("to display: ",buf);
	if(last!=0)
		printf("[%02x] ",last);
	printf("%02x ",buf);
	
	if(last!=0)
	{
		switch(last)
		{
			case 0xfb:
				i=0;
				do{
					if(fbcodes[i].code==buf)
						printf("(%s)",fbcodes[i].code);
				
					i++;
				} while(fbcodes[i].code!=0);
				break;
		
			case 0xf9:
			case 0xfa:
				break;

			default:
				perror("Unkown code.\n");
				exit(-1);
		}
		return 0;
	}

	// char
	if((buf>>4)<0x8)
	{
		if(buf>0x1f)
			printf("'%c'",buf);
	}
	
	// position
	else if((buf>>4)<0xd)
	{
		printf("(Line %d, Pos %d)",((buf&0x7f)<40)?1:2,buf&0x7f);
	}	

	// all others
	else
	{
		printf("(%s)",from_sq80[buf-0xd0].str);
		x=from_sq80[buf-0xd0].cnt;
	}

	printf("\n");
	
	if(x!=0)
		return buf;
	
	return 0;
}

// display to keyboard
void analyze_dpkb(int fd, char buf, int last)
{
	printf("to kbd:	%02x\n",buf);
}

// keyboard to sq80
void analyze_kbsq(int fd, char buf, int last)
{
	printf("to cpu: %02x\n",buf);
}

int main(int argc, char **argv)
{
char buf;
int l1, l2, f1, f2, mode;
ssize_t x;

	argv++;
	if(argc!=2)
	{
		perror("Usage:\tsnoop mode\n");
		exit(-1);
	}

	f1=open_serial(0);	// SQ80->DPY	||	DPY->KBD
	f2=open_serial(1);	// DPY ->KBD	||	KBD->SQ80

	mode=strtoul(*argv,0,10);
	if(mode==SQDP)
		printf("Snooping CPU->Display->Keyboard\n");
	else
		printf("Snooping Display->Keyboard->CPU\n");	

	while(1)
	{
		if (read(f1,&buf,1)>0)
		{
			if(mode==SQDP)
				analyze_sqdp(f1,buf,l1);
			else
				analyze_dpkb(f1,buf,l1);
			
			do{	
				x=write(f1,&buf,1);
			} while(x==EAGAIN);

			l1=buf;
		}
		
		if(read(f2,&buf,1)>0)
		{	
			if(mode==SQDP)
				analyze_dpkb(f2,buf,l2);
			else
				analyze_kbsq(f2,buf,l2);	

			do{
				x=write(f2,&buf,1);
			} while(x==EAGAIN);
			
			l2=buf;
		}				
	}
}

