//
// Serial.cpp : Serial ports operations
//
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "romdump.h"
#include "Serial.h"
#include "Step2.h"	// for Progress control

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define PACKET_START	1
#define PACKET_STOP		13
#define CHR(ch)	(((ch)+32)&0xFF)
#define NUM(ch)	((ch)-32)

static DWORD crc1[16] =
{
	0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387,
	0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F
};
static DWORD crc2[16] =
{
	0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
	0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7
};

int		Port = 0;
char	*sPort = "COMx";
HANDLE	hCom;
BYTE	pkt[1024];

BOOL SerialOpen()
{
	BOOL fSuccess;
	DCB dcb;

	sPort[3] = Port+'0';
    hCom = CreateFile(sPort,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
	if (hCom == INVALID_HANDLE_VALUE) return FALSE;
	fSuccess = GetCommState(hCom, &dcb);
	if (!fSuccess)
	{
		CloseHandle(hCom);
		return FALSE;
	}
	dcb.BaudRate = 9600;
	dcb.fBinary = 1;
	dcb.ByteSize = 8;
	dcb.Parity = 0;
	dcb.StopBits = 0;
	dcb.fDtrControl = 0;
	dcb.fRtsControl = 0;
	dcb.fOutX = 1;
	dcb.fInX = 1;
	fSuccess = SetCommState(hCom, &dcb);
	if (!fSuccess)
	{
		CloseHandle(hCom);
		return FALSE;
	}
	return TRUE;
}

void SerialClose()
{
	CloseHandle(hCom);
	return;
}

BYTE chk1(BYTE *pkt)
{
	BYTE chk;
	for (chk=0; *pkt; pkt++) chk += *pkt;
	return ((chk>>6)+chk)&0x3F;
}

WORD chk3(BYTE *pkt)
{
	DWORD crc;
	BYTE c;
	for (crc = 0; *pkt != '\0'; pkt++)
	{
		c = ((BYTE)crc) ^ (*pkt);
		crc = (crc>>8) ^ (crc1[c>>4] ^ crc2[c&0x0F]);
	}
	return (WORD)crc;
}

BOOL SendPKT(BYTE type, int n, char *d)
{
	DWORD len = strlen(d);

	pkt[0] = PACKET_START;
	pkt[1] = CHR((BYTE)len+2+(n?3:1));
	pkt[2] = CHR(n);
	pkt[3] = type;
	strcpy((char*)(pkt+4),d);
	len += 4;
	pkt[len] = 0;

	if (n)
	{ // 16-bit CRC
		WORD crc = chk3(pkt+1);
		pkt[len++] = CHR((crc>>12)&0x0F);
		pkt[len++] = CHR((crc>>6)&0x3F);
		pkt[len++] = CHR(crc&0x3F);
	}
	else
	{ // 6-bit chksum
		pkt[len++] = CHR(chk1(pkt+1));
	}
    pkt[len++] = PACKET_STOP;
    pkt[len] = 0;

	WriteFile(hCom, pkt, len, &len, NULL);
	return pkt[len];
}

BYTE RecvPKT()
{
	int timeout = 10;
	DWORD len;

	pkt[0] = 0;
	while (timeout--)
	{
		ReadFile(hCom, pkt, 1, &len, NULL);
		if (pkt[0]==1) break; // received a packed !
		if (pkt[0]=='0') ASSERT(FALSE);
	}
	if (!timeout) return 0;
	ReadFile(hCom, pkt+1, 3, &len, NULL);
	len = NUM(pkt[1])-1;
	ReadFile(hCom, pkt+4, len, &len, NULL);
	if (pkt[len+3]!=PACKET_STOP) return 0;
	pkt[len+4] = 0;
	return pkt[3];
}

char *DataPKT(char *data, DWORD *len)
{
	char *p = (char*)(pkt+4), *q;
	int i, n = NUM(pkt[1])-5, j, l;

	for (i=0, l=0; i<n; i++)
	{
		if (p[i]=='#') i++;
		l++;
	}
	data = (char*)realloc(data, *len+l+1);
	q = data + *len;
	*len += l;
	for (i=0, j=0; i<n; i++,j++)
	{
		if (p[i]=='#')
		{
			i++;
			if (p[i]=='#')
				q[j] = '#';
			else
				q[j] = p[i]-64;
		}
		else
			q[j] = p[i];
	}
	q[j] = 0;
	return data;
}

char *CommandPKT(char *cmd, DWORD *len)
{
	char *result = NULL;
	int i;
	BYTE t;

	*len = 0;

l1:	SendPKT('I',0,"~* @-#Y3");
	t = RecvPKT();
	if (t!='Y') // not ACK ?
	{
		if (t=='E') return NULL; // ERROR, abort
		if (t=='N') goto l1; // NAK, resend
		if (t==0) goto l1; // received nothing, resend
		ASSERT(FALSE);
	}

l2:	SendPKT('C',0,cmd);
	t = RecvPKT();
	if (t!='S') // not an 'S' packet ?
	{
		if (t=='E') return NULL; // ERROR, abort
		if (t=='N') goto l2; // NAK, resend
		if (t==0) goto l1; // received nothing, restart
		ASSERT(FALSE);
	}
	SendPKT('Y',0,"~& @-# 3"); // ACK

	i = 1;
	if (RecvPKT()!='X') return NULL;
	SendPKT('Y',i++,"");

	do
	{
		t = RecvPKT();
		if (t!='D') break;
		result = DataPKT(result,len);
		SendPKT('Y',i++,"");
	} while (i<64);		// limited to 64 packets

	if (t!='Z')
	{
		ASSERT(FALSE);
		if (result) free(result);
		return NULL;
	}
	SendPKT('Y',i++,"");

	if (RecvPKT()!='B')
	{
		ASSERT(FALSE);
		if (result) free(result);
		return NULL;
	}
	SendPKT('Y',i++,"");

	return result;
}

char *DumpRange(DWORD a, DWORD b)
{
	char cmd[32];
	DWORD len = 0;
	char *result = NULL;
	BYTE t;

	wsprintf(cmd,"##%05lX ##%05lX ROMDump",a,b);
	a&=0xFFFF0; // round down 
	b=(b+0xF)&0xFFFF0; // round up
	len = ((b-a)>>4) * 24;

	result = (char*)malloc(len+1);
	result[len] = 0;

l1:	SendPKT('I',0,"~* @-#Y3");
	t = RecvPKT();
	if (t!='Y') // not ACK ?
	{
		if (t=='E') return NULL; // ERROR, abort
		if (t=='N') goto l1; // NAK, resend
		if (t==0) goto l1; // received nothing, resend
		ASSERT(FALSE);
	}
	SendPKT('C',0,cmd);
	ReadFile(hCom, result, len, &len, NULL);
	do t=RecvPKT(); while (t!='E');
	Sleep(5);
	return result;
}

__inline nib(char c) {return (c<='9')?(c-'0'):(c-'A'+10);}

BYTE *DumpRangeBin(CStep2 *dlg,DWORD a, DWORD b)
{
	char buf[32];
	DWORD d, c, len;
	BYTE *res;
	BYTE t;

	wsprintf(buf,"##%05lX ##%05lX ROMDump",a,b);
	dlg->m_Progress.SetRange(0,(b-a)>>4);
	dlg->m_Progress.SetStep(1);
	dlg->m_Progress.SetPos(0);

	res = (BYTE*)malloc(b-a);

l1:	SendPKT('I',0,"~* @-#Y3");
	t = RecvPKT();
	if (t!='Y') // not ACK ?
	{
		if (t=='E') return NULL; // ERROR, abort
		if (t=='N') goto l1; // NAK, resend
		if (t==0) goto l1; // received nothing, resend
		ASSERT(FALSE);
	}
	SendPKT('C',0,buf);
	for (d=a; d<b; d+=16)
	{
		ReadFile(hCom, buf, 24, &len, NULL);
		c = (nib(buf[0])<<16)
		|	(nib(buf[1])<<12)
		|	(nib(buf[2])<< 8)
		|	(nib(buf[3])<< 4)
		|	(nib(buf[4]));
		if (c!=d)
		{
			free(res);
			return NULL;
		}
		res[d+ 0] = nib(buf[ 6]);
		res[d+ 1] = nib(buf[ 7]);
		res[d+ 2] = nib(buf[ 8]);
		res[d+ 3] = nib(buf[ 9]);
		res[d+ 4] = nib(buf[10]);
		res[d+ 5] = nib(buf[11]);
		res[d+ 6] = nib(buf[12]);
		res[d+ 7] = nib(buf[13]);
		res[d+ 8] = nib(buf[14]);
		res[d+ 9] = nib(buf[15]);
		res[d+10] = nib(buf[16]);
		res[d+11] = nib(buf[17]);
		res[d+12] = nib(buf[18]);
		res[d+13] = nib(buf[19]);
		res[d+14] = nib(buf[20]);
		res[d+15] = nib(buf[21]);
		dlg->m_Progress.StepIt();
	}
	do t=RecvPKT(); while (t!='E');
	Sleep(5);
	return res;
}

static WORD wCRC;

static WORD crc_table[16] =
{
	0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387,
	0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F
};

static __inline VOID CRC(BYTE nib)
{
	wCRC = (WORD)((wCRC>>4)^crc_table[(wCRC^nib)&0xf]);
}

BOOL CheckROM(LPBYTE pRom, UINT uType)
{
	DWORD dwD0, dwD1;
	WORD  wRomCRC;
	UINT  i;
	DWORD dwBase = 0x00000;
	UINT  nPass = 0;
	UINT  nPasses;

	switch (uType)
	{
	case HP48G:
		nPasses = 2;
		break;
	case HP48S:
		nPasses = 1;
		break;
	default:
		return TRUE;
	}

	for (dwD0=0x00100; dwD0<0x00140; dwD0++) pRom[dwD0]=0;

again:

	wRomCRC = pRom[dwBase+0x7FFFC]
			|(pRom[dwBase+0x7FFFD]<<4)
			|(pRom[dwBase+0x7FFFE]<<8)
			|(pRom[dwBase+0x7FFFF]<<12);

	wCRC = 0x0000;
	dwD0 = dwBase + 0x00000;
	dwD1 = dwBase + 0x40000;
	do
	{
		for (i=0; i<16; i++) CRC(pRom[dwD0+i]);
		for (i=0; i<16; i++) CRC(pRom[dwD1+i]);
		dwD0 += 16;
		dwD1 += 16;
	} while (dwD0&0x3FFFF);

	if (wCRC!=0xFFFF) return FALSE;
	if (++nPass == nPasses) return TRUE;

	dwBase += 0x80000;
	goto again;

	return TRUE;
}
