/*********************** DESMIM Hardware Engine Comms Client (c) 2001 Michael Bond ****************/

/* Shell Type :  C Mk. IIb */
 
/* 
Filename : talk-desmim.cpp
Created : 2nd May 2001
Creators : Michael Bond
Format : C
Compilation : Win32 Console App - Visual C++ 6.0 project (destalk)
Detail : a program to communicate with the DESMIM hardware engine, and load it up for a DESMIM run

Version History (Defined as DATE VERSION REVISION SUBSIDIARY)

DATE            V.      R.      S.      INFO
02/05/01        1.0     1       0       started converting from the smtalk.c client
21/05/01		1.0		1		1		integrating with the NIOS desmim cpu instruction set
22/05/01		1.0		2		0		Pulling in Win32 COM port code from MTTTY sample proj
23/05/01		1.0		2		1		Designing user CLI interface command set
29/05/01		1.0		3		0		Adding more interesting composite commands
31/05/01		1.0		3		1		Bells and whistles for the composite commands. fixing endian probs.
11/06/01		1.0		4		0		Putting CRC in commands for reliability, just like PRISM
26/06/01		1.0		5		0		Serious code tidy up, improving help, adding test vector load
09/07/01		1.0		6		0		New desmim command set filtering through

*/

//************************************************************************************************* //
// ******************************** Inclusions  *************************************************** //
// ************************************************************************************************ //

#include "stdafx.h"

#include <stdlib.h>
#include <time.h>
#include <stdio.h>   // Standard input/output definitions
#include <string.h>  // String function definitions
#include <conio.h>

//************************************************************************************************* //
// ******************************** Definitions  *************************************************** //
// ************************************************************************************************ //

#define VERSION 1.0
#define REVISION 6.0

#define UINT16 unsigned short

#define WAIT_TIME 0					// time to wait between sending command & fetching response (ms)
#define SMAK_WAIT_TIME 150

// parameters for setting up COM port
#define MAX_STATUS_BUFFER       20000
#define MAX_WRITE_BUFFER        1024
#define MAX_READ_BUFFER         2048
#define READ_TIMEOUT            500
#define STATUS_CHECK_TIMEOUT    500
#define WRITE_CHECK_TIMEOUT     500
#define PURGE_FLAGS             PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR 
#define EVENTFLAGS_DEFAULT      EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD
#define FLAGCHAR_DEFAULT        '\n'

// write request types
#define WRITE_CHAR          0x01
#define WRITE_FILE          0x02
#define WRITE_FILESTART     0x03
#define WRITE_FILEEND       0x04
#define WRITE_ABORT         0x05
#define WRITE_BLOCK         0x06

// Read states
#define RECEIVE_TTY         0x01
#define RECEIVE_CAPTURED    0x02

// definitions of some ascii characters (for initialising COM port)
#define ASCII_XON       0x11
#define ASCII_XOFF      0x13

//--------------------- return codes from local functions ------------------

#define NO_PARAM 0

#define SUCCESS 0
#define ERROR_BAD_CMD 1
#define ERROR_COMMS_FAILED 2		// this is only used internally - the desmim engine should never send this

//---------------------- miscellaneous --------------------------------------
// crc parameters
#define GENPOLY 0xA001
#define CRCINIT 0 // or 0xffff

#define	RAMSIZE 16384		// number of parallel compares done by engine


//************************************************************************************************* //
// ************************************** Prototypes  ********************************************* //
// ************************************************************************************************ //

// housekeeping functions
void do_clean_exit(void);
void do_error_exit(char message[]);

// communications functions
void init_comms(void);
void shutdown_comms(void);

int read(TTYInfoStruct &tty, char *instring, unsigned int maxlen);
void write(TTYInfoStruct &tty, char *string, unsigned int len);

char *send_command(char *command, char *data);

// command related functions

void load_test_vectors(char filename[]);
UINT16 tv_hash(const UCHAR *data);
void await_results(void);
void fetch_results(void);

void load(char *data);
void loadaddress(char * data);
void fetch(char *data);
unsigned short crc_gen(unsigned char *data, short len);

// utility functions

char * hexparam(char *data, const int bits);
void fix_parity(char *bytes, const int choice);
int to_decimal(const char c);
char to_hex(const int i);
void to_hex_8(const UCHAR * value, char * buffer);
int ishexdigit(const char c);

//************************************************************************************************* //
// ***************************** Data Structures & Variables  ************************************* //
// ************************************************************************************************ //

FILE * log = NULL;					// logging file

struct TTYInfoStruct tty;			// TTY handling

int baud        = CBR_115200;		// current baud rate of link

int online      = FALSE;			// TRUE iff online to hardware engine
int suppress    = FALSE;			// TRUE to suppress commands on the screen
int logSuppress = FALSE;			// TRUE to suppress commands in the log

UCHAR key_value[RAMSIZE][8];		// values in RAM locations
int key_count[RAMSIZE];				// and the key number

//************************************************************************************************* //
// ********************************** Main Function *********************************************** //
// ************************************************************************************************ //

void main(int argc,char *argv[])
    {	
    int cmdsent=FALSE;

	time_t curtime;

    char command_buffer[256];
    
    printf("DESTALK - DESMIM Hardware Engine Communications Program\n" 
           "-------------------------------------------------------\n" 
           "Copyright (c) 2001 Michael Bond, University of Cambridge\n" 
           "Version %1.1f Revision %1.1f\nCompiled : %s\n\n"
		   "Type 'help' for a list of commands.\n\n" ,
           VERSION, REVISION , __TIMESTAMP__ );

    system("echo ------------------------------------------ >>destalk.log");
    
	log=fopen("destalk.log","a");

	time(&curtime);
	fprintf(log,"%s\n", ctime( &curtime ));

    if( log == NULL )
        {
        printf("Could not open log file for appending.\n");
        do_clean_exit();
        }
    
    while( TRUE )
        {        
        if( online )
            printf("ONLINE  ");
        else
            printf("OFFLINE ");

        printf(">");

        fgets(command_buffer,250,stdin);
        command_buffer[strlen(command_buffer)-1]='\0'; // chop of carriage return

        //---------------------------- command processing -----------------

        if( !strcmp("quit",command_buffer) )
            do_clean_exit();

		if( !strcmp("help",command_buffer) )
			{
			printf("Client configuration commands\n\n"
				   "online / offline, baud <baudrate>\n"
				   "quit\n\n"
				   "Composite commands\n\n"
				   "wait, results, fetch key, fetch result\n"
				   "address <address>, fetch, set data <data64>\n"
				   "load <data32>, loadtv <filename> , runtv <filename>\n"
				   "blank <address>, dump <address>, memcheck. loop\n"
				   "Primitive Commands\n\n"
				   "reset, load0 <data16>, load1 <data16>, fetch0, fetch1\n"
				   "sram write, sram read, set sram address\n"
				   "latch data0, latch data1, latch key0, latch key1, latch result0, latch result1\n"
				   "go/run/start, status\n\n" );
			continue;
			}
		
		if( !strncmp("baud",command_buffer,4) )
			{
			int newbaud;
			int n = sscanf(command_buffer, "baud %d", &newbaud);
			if (n != 1)
				printf("Missing baud rate\n");
			else
				switch (newbaud)
				{
				case 115200:
					baud = CBR_115200;
					break;
				case 57600:
					baud = CBR_57600;
					break;
				case 38400:
					baud = CBR_38400;
					break;
				case 19200:
					baud = CBR_19200;
					break;
				case 9600:
					baud = CBR_9600;
					break;
				case 4800:
					baud = CBR_4800;
					break;
				case 2400:
					baud = CBR_2400;
					break;
				case 1200:
					baud = CBR_1200;
					break;
				default:
					printf("Baud rate not supported\n");
					break;
				}
			continue;
			}

        if( !strcmp("online",command_buffer) )
            {
		    printf("DESMIM Engine on COM1\n\n");
			init_comms();	   
			fprintf(log,"COM port opened. Session Started.\n");

            online=TRUE;
            fprintf(log,"(ONLINE)\n");
            continue;
            }

		// must be online for any other command

		if( !online )
			{
			printf("(Offline)\n");
			continue;
			}

        if( !strcmp("offline",command_buffer) )
            {
			shutdown_comms();
			fprintf(log,"COM port closed. Session Finished.\n");
            online=FALSE;
            fprintf(log,"(OFFLINE)\n");
            continue;
            }

		// ------------- composite commands ------------------------------------

		if (!strncmp("results", command_buffer, 7))
			{
			fetch_results();
			continue;
			}

		if( !strcmp("fetch key",command_buffer) )
			{
			char result[17];

			send_command("k1","\0");
			fetch(result + 0);			
			send_command("k0","\0");
			fetch(result + 8);			
			printf("%s\n",result);
			continue;            
			}

		if( !strcmp("fetch result",command_buffer) )
			{
			char result[17];

			send_command("r1","\0");
			fetch(result + 0);			
			send_command("r0","\0");
			fetch(result + 8);			
			printf("%s\n",result);
			continue;            
			}

		if( !strcmp("fetch",command_buffer) )
            {
			char result[9];
			fetch(result);
			printf("\n%s\n",result);
            continue;
            }

		if( !strncmp("address",command_buffer,7) )
			{
			char * value = hexparam(command_buffer+7, 15);
			if (value)
				{
				loadaddress(value);
				}
			continue;            
			}

		if( !strncmp("set data",command_buffer,8) )
			{
			char * value = hexparam(command_buffer+8, 64);
			if (value)
				{
				load(value+0);
				send_command("t1","\0");
				load(value+8);
				send_command("t0","\0");
				}
			continue;            
			}

		if( !strncmp("load ",command_buffer,5))	// NB trailing space
            {
			char * value = hexparam(command_buffer+5, 32);
			if (value)
				{
				load(value);
				}
			continue;
            }

	    if( !strcmp("wait",command_buffer) )
            {
			await_results();
			continue;
            }

        if( !strncmp("loadtv",command_buffer,6) )
            {
            load_test_vectors(command_buffer+7); // feed it the test vector filename
            continue;
            }       

		
        if( !strncmp("runtv",command_buffer,5) )
            {
            load_test_vectors(command_buffer+6); // feed it the test vector filename
            send_command("rr","\0");
			await_results();
			fetch_results();
            continue;
            }       


		//--------------------- debug commands ------------------------------------

		if( !strcmp("reset",command_buffer) )
			{
			send_command("xx","\0");
			continue;
			}

		if( !strcmp("status",command_buffer) )
			{
			printf("%s\n",send_command("gs","\0"));
			continue;
			}

		if( !strcmp("memcheck",command_buffer) )
			{
			char zeroes[]= "00000000\0" ;
			char pat[]=    "a5a5a5a5\0" ;
			char notpat[]= "5a5a5a5a\0" ;
			char spat[20];
			int i;
			
			suppress=TRUE;

			// set the address
			load(zeroes);
			send_command("sa","\0");
			
			for(i=1;i<=0x00FF;i++)
				{
				sprintf(spat,"%04X%04X",i , i ^ 0xFFFF );

				load(spat);
				// write next
				send_command("sl","\0");
				
				if( i % 256 == 0 )
					printf(".%X.",i);
				}

			printf("\n\nTest Patterns Loaded.\n");

			// set the address
			load(zeroes);
			send_command("sa","\0");
			
			for(i=1;i<=0x00FF;i++)
				{
				char result[20];

				sprintf(spat,"%04X%04X",i , i ^ 0xFFFF );
				
				// read next
				send_command("sf","\0");				
				memset(result,'\0',20);
				
				//fetch(reply);				
				strncat(result,send_command("f1","\0")+2,4);
				result[4]='\0';
				strncat(result,send_command("f0","\0")+2,4);			
				result[8]='\0';

				if( !strcmp(result,spat) )
					printf(".");
				else
					printf("X");
				
				if( i % 256 == 0 )
					printf(".%X.",i);
				}

			printf("\n\nDone.\n");

			suppress=FALSE;
			continue;            
			}

		if( !strncmp("dump",command_buffer,4) )
			{
			char * value = hexparam(command_buffer+4, 15);
			if (value)
			{
				char reply[20];
				int i;

				suppress=TRUE;

				// set the address
				loadaddress(value);

				// read first
				send_command("sf","\0");
				fetch(reply);

				printf("\n%s : %s\n", value, reply);

				for(i=1;i<=9;i++)
					{
					char result[20];

					// read next
					send_command("sf","\0");				
					memset(reply,'\0',20);
				
					//fetch(reply);
				
					strncpy(result,send_command("f1","\0")+2,4);
					result[4]='\0';
					strncat(result,send_command("f0","\0")+2,4);			
					result[8]='\0';

					printf("       %s\n",result);
					}

				suppress=FALSE;
				}
			continue;            
			}

		if( !strncmp("blank",command_buffer,5) )
			{
			char * value = hexparam(command_buffer + 5, 15);
			if (value)
				{
				char zeroes[]= "00000000\0" ;
				int i;

				suppress=TRUE;

				// set the address
				loadaddress(value);
			
				for(i=1;i<=10;i++)
					{
					load(zeroes);
					// write next
					send_command("sl","\0");								
					}

				printf("Done.\n");

				suppress=FALSE;
				}
			continue;            
			}

		if( !strcmp("loop",command_buffer) )
            {
			suppress=TRUE;

			while( !kbhit() )
				{
				// set the address
				load("00000010");
				send_command("sa","\0");

				// do four write operations
				load("11121314\0");
				send_command("sl","\0");
				load("00112233\0");
				send_command("sl","\0");
				load("DEADBEEF\0");
				send_command("sl","\0");
				load("11224488\0");
				send_command("sl","\0");

				// set the address
				load("00000010");
				send_command("sa","\0");

				// do four read operations
				send_command("sf","\0");
				send_command("sf","\0");
				send_command("sf","\0");
				send_command("sf","\0");
				}

			getch();

			continue;
            }
		
//--------------------- issue desmim engine commands manually -----------------

		if( !strncmp("load0",command_buffer,5) )
            {
			char * value = hexparam(command_buffer+5, 16);
			if (value)
				send_command("l0", value);
            continue;
            }

		if( !strncmp("load1",command_buffer,5) )
            {
			char * value = hexparam(command_buffer+5, 16);
				send_command("l1", value);
			continue;
            }

		if( !strcmp("fetch0",command_buffer) )
            {
			send_command("f0","\0");
			continue;
            }

		if( !strcmp("fetch1",command_buffer) )
            {
            send_command("f1","\0");
			continue;
            }

		if( !strcmp("sram write",command_buffer) )
            {
            send_command("sl","\0");
			continue;
            }

		if( !strcmp("sram read",command_buffer) )
            {
            send_command("sf","\0");
			continue;
            }

		if( !strcmp("set sram address",command_buffer) )
            {
            send_command("sa","\0");
			continue;
            }

		if( !strcmp("latch data1",command_buffer) )
            {
            send_command("t1","\0");
			continue;
            }

		if( !strcmp("latch data0",command_buffer) )
            {
            send_command("t0","\0");
			continue;
            }

		if( !strcmp("latch key1",command_buffer) )
            {
            send_command("k1","\0");
			continue;
            }

		if( !strcmp("latch key0",command_buffer) )
            {
            send_command("k0","\0");
			continue;
            }

			if( !strcmp("latch result1",command_buffer) )
            {
            send_command("r1","\0");
			continue;
            }

		if( !strcmp("latch result0",command_buffer) )
            {
            send_command("r0","\0");
			continue;
            }

		if( !strcmp("go",command_buffer)    ||
			!strcmp("run",command_buffer)   ||
			!strcmp("start",command_buffer)     )
            {
            send_command("rr","\0");
			continue;
            }
		}
    }

//************************************************************************************************* //
// ********************************** Other Functions ********************************************* //
// ************************************************************************************************ //

//------------------------------- do a clean exit from system -------------------------------

void do_clean_exit(void)
    {
    if( log != NULL )
        {
        fprintf(log,"Terminating Session.\n");
        fclose(log);
        }

	shutdown_comms();

    printf("Session Terminated.\n");

    exit(0);
    }

//------------------------------- print an error message & exit as cleanly as poss ----------

void do_error_exit(char message[])
	{	
	printf("%s\n",message);
	exit(1);
	}

//--------------------------- send command to the security module -------------------------

char * send_command(char *command,char *data)
    {
	char complete_command[20];
	static char repl[10];
	char crcbuf[10];
	long cstart;
	
	strcpy(complete_command,command);

	if( command[0] == 'l' )		// this command provides data
		strncat(complete_command,data,4);		// always 4 chars!

	// add on the CRC
	sprintf(crcbuf,"%04X",crc_gen((unsigned char *) &complete_command,strlen(complete_command)));	
	strcat(complete_command,crcbuf);
	// end CRC add on

	if( !online )
		{
		printf("(Offline)\n");
		return repl;
		}

	if (!logSuppress)
		fprintf(log,"sent '%s'\n",complete_command);
	
	if( !suppress )
		printf("%s\n",complete_command);

	write(tty,complete_command,strlen(complete_command));
	// pause for posterity

    cstart=clock();
	while( ((clock()-cstart)*1000)/CLOCKS_PER_SEC < WAIT_TIME );

	// read the 8 char response (OK/BD then checksum, then \r\n)

    read(tty,repl,8);

	if (!logSuppress)
		fprintf(log,"received '%c%c' %c%c%c%c ",repl[0],repl[1],repl[2],repl[3], repl[4], repl[5]);

	if (command[0] == 'f' || command[0] == 'g')		//f0 f1 or gs
		{
		read(tty,repl+8,4);
		repl[12]='\0';

		if (!logSuppress)
			fprintf(log,"%c%c%c%c %d %d\n",repl[6],repl[7],repl[8],repl[9], repl[10], repl[11]);
		}
	else
		{
		repl[8]='\0';

		if (!logSuppress)
			fprintf(log,"%d %d\n",repl[6],repl[7]);
		}

	if( !suppress )
		printf("%s",repl);

	if( repl[0] == 'B' && repl[1] == 'C' )
		{
		printf("(BAD CHECKSUM)\n");
		}

	if( repl[0] == 'B' && repl[1] == 'D' )
		{
		printf("(BAD COMMAND)\n");
		}

	return repl;
    }

//---------------------------- load test vectors from standard .tv file format --------------------

// .tv file format definition
//
// SIZE    QTY             DESCRIPTION 
// -----------------------------------------------------------------------
// 8       1               Test Pattern (usually all zeroes)
// 4       1               Number of test vectors (tv_count_size)
// 8       tv_count_size   A test vector (the test pattern (encrypted 
//                         under the nth key variant, where applicable))
//

void load_test_vectors(char filename[])
    {
	
    int tv_count;
	long count;
    long clash_count  = 0;
	long reject_count = 0;

    UCHAR test_vector[8];
    UCHAR cur_test_pattern[8];
   
	const UCHAR binary_00[9] = "\0\0\0\0\0\0\0\0";		
	const UCHAR binary_FF[9] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";		
	
    long tv_count_size;
    
    FILE *tvin;

    // open file to get test vectors from    
    if( (tvin=fopen(filename,"rb")) == NULL )
        {
        fprintf(log,"Error opening tv file %s for input. Aborted.\n",filename);
        printf("Error opening tv file %s for input. Aborted.\n",filename);
        return;
        }

	// set all RAM locations unused

	for (count=0; count<RAMSIZE; count++)
		memset(&key_value[count][0], 0xFF, 8);
    
    // read the test vector (in the prism this is 0x0000000000000000)

    fread(test_vector,8,1,tvin);
    
    // read how many test vecytor we have

    fread(&tv_count_size, sizeof(long), 1, tvin);

	// now read all the test vectors

    fprintf(log,"Retrieving test vector set from file '%s'\n",filename);
    printf("Retrieving test vector set from file '%s'\n\n",filename);
           
    for(tv_count=0; tv_count<tv_count_size; tv_count++)
	    {
		UINT16 addr;

	    fread(cur_test_pattern,8,1,tvin);

		// cannot use 00..00 (matches first 16 results from DES engine) or FF..FF (unused space)

		if (!memcmp(cur_test_pattern, binary_00, 8) || !memcmp(cur_test_pattern, binary_FF, 8))
			{
			reject_count++;
			continue;
			}
        
        addr = tv_hash(cur_test_pattern);

#ifdef	_DEBUG
		if( addr < 5 )
			{
			char buffer[17];
			to_hex_8(cur_test_pattern+0, buffer+0);
			to_hex_8(cur_test_pattern+4, buffer+8);

			printf( "\n"
				    "number : %d (%X)\n"
					"test vector : %s\n"
					"hash   : %08X\n"
					"offset : %08X\n",
					tv_count, tv_count, buffer, addr, (addr << 3)
				  );
			}
#endif

        // check to see whether we are overwriting an existing tv

        if( memcmp(&key_value[addr][0], binary_FF, 8))
			clash_count++;	

        // store the test vector in the ram at address specified by hash function

		memcpy(&key_value[addr][0], cur_test_pattern, 8);
		key_count[addr] = tv_count;
                                
        if( tv_count % 1024 == 0 ) // don't print too much to screen
            {
			printf(".");
            }
        
        }   

    fclose(tvin);
	
    printf("\n\n");
	printf("Total test vectors loaded   : %d\n", tv_count_size);
    printf("Total number of clashes     : %d\n", clash_count);
    printf("Total unusable test vectors : %d\n", reject_count);

    fprintf(log,"Loading test vector set into DESMIM engine\n");
    printf("Loading test vector set into DESMIM engine\n");

	suppress=TRUE;

    load("00000000");
    send_command("sa","\0");	    
	    
	for(count=0; count<RAMSIZE; count++)	// step through, one test vector at a time
		{		
		char buffer[9];

		// load the data into the ram in the correct order

		to_hex_8(&key_value[count][4], buffer);
		load(buffer);
		send_command("sl","\0");

		to_hex_8(&key_value[count][0], buffer);
		load(buffer);
		send_command("sl","\0");

		if( count % 256 == 0 )
		     printf(".%X.",count * 8);
		}
			
    printf("\n\n%d lots of 2 * 32-bit chunks loaded.\n", count);

    printf("\n\nDone.\n");

    suppress=FALSE;
    }

//--------------------------- test vector hashing routine ---------------

UINT16 tv_hash(const UCHAR * data)
	{
	UINT16 msb, lsb;


#define bit(NUM) (((data[7 - ((NUM) >> 3)]) >> ((NUM) % 8)) & 0x01)

	// NB the 'bit' macro is hard-wired to operate on the
	// 8 byte (ms byte first, ls byte last) variable 'data'

 	msb =	   ( (bit(63) ^ bit(62) ^ bit(61) ^ bit(60)) << 5 )
			|  ( (bit(59) ^ bit(58) ^ bit(57) ^ bit(56)) << 4 )
			|  ( (bit(55) ^ bit(54) ^ bit(53) ^ bit(52)) << 3 )
            |  ( (bit(51) ^ bit(50) ^ bit(49) ^ bit(48)) << 2 )
            |  ( (bit(47) ^ bit(46) ^ bit(45) ^ bit(44)) << 1 )
            |  ( (bit(43) ^ bit(42) ^ bit(41) ^ bit(40)) << 0 ) ;

 	lsb =      ( (bit(39)^bit(38)^bit(37)^bit(31)^bit(35)) << 7 )
            |  ( (bit(34)^bit(33)^bit(32)^bit(31)^bit(30)) << 6 )
            |  ( (bit(29)^bit(28)^bit(27)^bit(26)^bit(25)) << 5 )
            |  ( (bit(24)^bit(23)^bit(22)^bit(21)^bit(20)) << 4 )
            |  ( (bit(19)^bit(18)^bit(17)^bit(16)^bit(15)) << 3 )
            |  ( (bit(14)^bit(13)^bit(12)^bit(11)^bit(10)) << 2 )
            |  ( (bit( 9)^bit( 8)^bit( 7)^bit( 6)^bit( 5)) << 1 )
            |  ( (bit( 4)^bit( 3)^bit( 2)^bit( 1)^bit( 0)) << 0 ) ;

	return (msb<<8) | lsb;
	}	


//------------- wait for results to be available --------------

void await_results(void)
	{

	time_t now;
	time(&now);
	printf("Wait started at:  %s\n", ctime(&now));

	suppress    = TRUE;
	logSuppress = TRUE;

	while( !kbhit() )
		{
		char * result = send_command("gs", "");

		if (result[5] == '1')
			{
			time(&now);
			printf("Run completed at: %s\n", ctime(&now));
			break;
			}
		}

	if (kbhit())
		{
		getch();
		printf("Wait interrupted!\n");
		}

	suppress    = FALSE;
	logSuppress = FALSE;
	}

//------------- fetch the results and interpret them --------------

void fetch_results(void)
	{
	char result[17];
	char pipekey[17];

	UCHAR result_value[8];

	int count;

	// fetch results from the hardware engine

	suppress = TRUE;

	send_command("r1","");
	fetch(result + 0);
	send_command("r0","");
	fetch(result + 8);

	send_command("k1","");
	fetch(pipekey+0);
	send_command("k0","");
	fetch(pipekey+8);

	suppress = FALSE;

	// display the basic results

	printf("\nResult = #%s, Key = #%s\n", result, pipekey);

	// now show the key with correct (odd) parity

	fix_parity(pipekey, 1);

	printf("\nWith corrected (odd) parity key = #%s\n", pipekey);

	// now locate the value we loaded

	for (count=0; count<8; count++)
		{
			result_value[count] = (to_decimal(result[count * 2 + 0]) << 4)
								 + to_decimal(result[count * 2 + 1]);
		}

	for (count=0; count<RAMSIZE; count++)
		{
		if (memcmp(&key_value[count][0], result_value, 8) == 0)
			{
			int i;
			long total = 0;
			char buffer[17];

			int key = key_count[count];

			printf("\nResult corresponds to key number #%04X\n", key);

			for (i=0; i<3; i++)
			{
				total |= (key & 0x7F) << (1 + (i * 8));
				key = key >> 7;
			}

			sprintf(buffer, "%016X", total);
			fix_parity(buffer, 0);

			printf("\nwhich is an XORing value of #%s\n", buffer);

			for (i=0; i<16; i++)
			{
				const int b = to_decimal(buffer[i]);
				const int c = to_decimal(pipekey[i]);
				buffer[i] = to_hex(b ^ c);
			}

			printf("ie: the key really wanted = #%s\n", buffer);

			break;
			}
		}
	}

            
//--------------------------- hex conversion routines ---------------

int to_decimal(const char c)
	{	
	if (isdigit(c))
		return (c - '0');
	else
		return (10 + (toupper(c) - 'A'));
	}

char to_hex(const int i)
	{
	if (i < 10)
		return '0' + i;
	else
		return 'A' + (i - 10);
	}

void to_hex_8(const UCHAR * value, char * buffer)
	{
	int i;
	for (i=0; i<4; i++)
		{
		buffer[i*2+0] = to_hex((value[i] & 0xF0) >> 4);
		buffer[i*2+1] = to_hex(value[i] & 0x0F);
		}
	buffer[8] = '\0';
	}

int ishexdigit(const char c)
	{
		if (c >= '0' && c <= '9')
			return 1;
		if (c >= 'a' && c <= 'f')
			return 1;
		if (c >= 'A' && c <= 'A')
			return 1;

		return 0;
	}


//------------------------------ parity values ---------------------------------

void fix_parity(char * value, const int choice)
	{
	int count;
	for (count=0; count<16; count+=2)
		{
		int parity = 0;

		switch (value[count + 0])
		{
		case '0': case '3': case '5': case '6':
		case '9': case 'A': case 'C': case 'F':
			break;
		case '1': case '2': case '4': case '7':
		case '8': case 'B': case 'D': case 'E':
			parity = 1;
			break;
		default:
			printf("Bad character in parity string!\n");
			break;
		}

		switch (value[count + 1])
		{
		case '0': case '6':
		case 'A': case 'C':
			break;
		case '2': case '4':
		case '8': case 'E':
			parity ^= 1;
			break;
		default:
			printf("Bad character in parity string!\n");
			break;
		}

		if (parity != choice)
			value[count+1]++;
		}
	}

//------------------------------ load ------------------------------------------

void load(char *data)
	{
	const int save_suppress = suppress;
	suppress = TRUE;

	send_command("l1", data+0);
	send_command("l0", data+4);
	
	suppress = save_suppress;
	}

void loadaddress(char *data)
	{
	const int save_suppress = suppress;
	suppress = TRUE;

	send_command("l1", "0000");
	send_command("l0", data);
	
	send_command("sa","\0");

	suppress = save_suppress;
	}

//------------------------------ fetch -----------------------------------------

void fetch(char *data)
	{	
	const int save_suppress = suppress;
	suppress = TRUE;

	strncpy(data, send_command("f1","")+2, 4);
	data[4]='\0';
	strncat(data, send_command("f0","")+2, 4);			
	data[8]='\0';

	suppress = save_suppress;
	}

//------------------------------ hex parameters --------------------------------------

char * hexparam(char * command, const int bits)
	{
	char parameter[17];

	char * src = command;
	char * dst = parameter;

	int length = 0;

	while (ishexdigit(*src) || isspace(*src))
		{
		if (ishexdigit(*src))
			length++;
		src++;
		}

	if (*src)
		{
		printf("Unexpected character '%c'in hex parameter\n", *src);
		return NULL;
		}

	const int chars = (bits+3)/4;

	if (length > chars)
		{
		printf("Hex parameter is unexpectedly long\n");
		return NULL;
		}

	src = command;
	while (*src)
		{
		if (!(isspace(*src)))
			*dst++ = *src;
		src++;
		}
	*dst = '\0';

	memset(command, '0', chars);

	memcpy(command + (chars-strlen(parameter)), parameter, strlen(parameter)+1);

	return command;
	}

//------------------------------ init_comms ------------------------------------------

void init_comms(void)
	{	
	// taken from function SetupCommPort from MTTTY project - init.c
	// and UpdateTTYInfo

	DCB dcb = {0};

    //----------------------- open up the port for business --------------------------

    // open communication port handle
    tty.hCommPort = CreateFile( "COM1" ,  
                                GENERIC_READ | GENERIC_WRITE, 
                                0, 
                                NULL, 
                                OPEN_EXISTING,
                                0 ,
                                NULL );

    if ( tty.hCommPort == INVALID_HANDLE_VALUE)
		do_error_exit("Opening COM port failed.");
    
    //----------------------- set up all the parameters from the COM port --------------
    
    dcb.DCBlength = sizeof(dcb);

    // get current DCB settings
    if( !GetCommState(tty.hCommPort, &dcb) )
		do_error_exit("GetCommState returned an error");

	// get current comm timeouts
	if( !GetCommTimeouts( tty.hCommPort, &(tty.timeoutsorig)) )
		do_error_exit("GetCommTimeouts returned an error.");
		
    // update DCB rate, byte size, parity, and stop bits size
    dcb.BaudRate = baud;
    dcb.ByteSize = 8;
    dcb.Parity   = NOPARITY;
    dcb.StopBits = TWOSTOPBITS;

    // update event flags
    if( tty.dwEventFlags & EV_RXFLAG )
		dcb.EvtChar = tty.chFlag;      
    else
		dcb.EvtChar = '\0';

    // update flow control settings
    dcb.fDtrControl     = DTR_CONTROL_DISABLE;
    dcb.fRtsControl     = RTS_CONTROL_DISABLE;

    dcb.fOutxCtsFlow    = FALSE;
    dcb.fOutxDsrFlow    = FALSE;
    dcb.fDsrSensitivity = FALSE;
    dcb.fOutX           = TRUE;
    dcb.fInX            = TRUE;
    dcb.fTXContinueOnXoff = TRUE;
    dcb.XonChar         = ASCII_XON;		// what should this character be???
    dcb.XoffChar        = ASCII_XOFF;
    dcb.XonLim          = 1024;
    dcb.XoffLim         = 1024;

    // DCB settings not in the user's control
    dcb.fParity = FALSE;

	//------------------------ update the systems parameters to match our new ones ---------
    // set new state
    if( !SetCommState(tty.hCommPort, &dcb) )
		do_error_exit("SetCommState returned an error");

    // set new timeouts
    if( !SetCommTimeouts(tty.hCommPort, &(tty.timeoutsnew)) )
		do_error_exit("SetCommTimeouts returned an error.");
	            
    // set comm buffer sizes
    SetupComm(tty.hCommPort, MAX_READ_BUFFER, MAX_WRITE_BUFFER);

    // raise DTR
    if (!EscapeCommFunction(tty.hCommPort, SETDTR))
        do_error_exit("EscapeCommFunction (SETDTR) returned an error.");
    
    // set overall connect flag
    tty.fConnected = TRUE ;	
	}

//---------------------------------- shutdown_comms ----------------------------------

void shutdown_comms(void)
	{
	CloseHandle(tty.hCommPort);
	tty.hCommPort=INVALID_HANDLE_VALUE;
	printf("COM port closed.\n");
	}

//--------------------------------------- read ---------------------------------------

int read(TTYInfoStruct &tty, char *instring, unsigned int maxlen)
	{
	DWORD bytes_read;
	OVERLAPPED osReader = {0};
    

	if (!ReadFile(tty.hCommPort, instring, maxlen, &bytes_read, &osReader))
		{
		printf("read failed.\n");

		if (GetLastError() != ERROR_IO_PENDING)	  // read not delayed?
            do_error_exit("ReadFile in ReaderAndStatusProc");

        }
    else
		{
		//printf("(Read %d chars)\n",bytes_read);

		// read completed immediately
        if ((bytes_read != MAX_READ_BUFFER) && tty.fDisplayTimeouts)
            printf("Read timed out immediately.\r\n");
        }

	return 0;
	}

//------------------------------------ write ----------------------------------

void write(TTYInfoStruct &tty, char *string, unsigned int len)
	{	
    OVERLAPPED osWrite = {0};
    DWORD bytes_written;
    

    // issue write
    if (!WriteFile(tty.hCommPort, string, len , &bytes_written, &osWrite))
		{
        if (GetLastError() == ERROR_IO_PENDING)
			{ 
            SetLastError(ERROR_SUCCESS);
            
			if (!GetOverlappedResult(tty.hCommPort, &osWrite, &bytes_written, FALSE))
				{
                if (GetLastError() == ERROR_OPERATION_ABORTED)
                     do_error_exit("Write aborted.");
                else
                     do_error_exit("GetOverlappedResult(in Writer).");
                }
                           
            if (bytes_written != len)
				{
                if ((GetLastError() == ERROR_SUCCESS) && tty.fDisplayTimeouts)
                     do_error_exit("Write timed out. (overlapped).");
                else
                     do_error_exit("Error writing data to port (overlapped).");
               }
            }	
		else    
            do_error_exit("Error writing to COM port");
		
		}

	// writefile returned immediately
	if (bytes_written != len)
        do_error_exit("Error : not all data written.");  

	//printf("Written %d bytes - \"%s\"\n",bytes_written,string);

    }

//------------------------------------ crc_gen ----------------------------------

unsigned short crc_gen(unsigned char *data, short len)
{
	unsigned short  crc;
	unsigned short  i, j;

	crc = CRCINIT;
	for (i=0; i<len; i++)
	{
		crc = crc ^ data[i];
		for (j=0; j<8; j++)
		{
			if ((crc & 0x0001) > 0)
			{
				crc = crc >> 1;
				crc = crc ^ GENPOLY;
			}
			else
				crc = crc >> 1;
		}
	}

	return crc;
}
