/*
 *  Serial upload of Intel HEX Files for ELV EPS-1 (EP)ROM Simulator
 *
 *  This program is intended to upload Intel HEX files produced by
 *  sdcc/asxx (*.ihx) to the Simulator.
 *
 *  Compile with gcc -o sendhex sendhex.c
 *  
 *  Usage: sendhex [device] [ihx file]
 *  Example: sendhex /dev/ttyS0 net9test.ihx
 *
 *  (c) 2004 Jochen Friedrich
 *
**-----------------------------------------------------------------------------
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
 */

#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

#define SOH 1
#define EOT 4
#define STX 2
#define ETX 3
#define DLE 16

unsigned char sendbuf[1024];
uint16_t crc;
int bufptr;

#define poly 0x1021

void addcrc(unsigned short ch)
{
    unsigned short i, xor_flag;

    for(i=0; i<8; i++)
    {
        if ((crc ^ (ch<<8)) & 0x8000)
        {
            xor_flag = 1;
        }
        else
        {
            xor_flag = 0;
        }
        crc = crc << 1;
        if (xor_flag)
        {
            crc = crc ^ poly;
        }
        ch = ch << 1;
    }
}

void reset_dtr(int fh)
{
	int x;

	ioctl(fh, TIOCMGET, (caddr_t)&x);
	x = x | TIOCM_DTR;
	ioctl(fh, TIOCMSET, (caddr_t)&x);
	usleep(100000);
	x = x & ~TIOCM_DTR;
	ioctl(fh, TIOCMSET, (caddr_t)&x);
	usleep(100000);
	x = x | TIOCM_DTR;
	ioctl(fh, TIOCMSET, (caddr_t)&x);
	usleep(100000);
}

int hex(char byte)
{
	if (byte < '0')
		return -1;
	if (byte <= '9')
		return byte - '0';
	if (byte < 'A')
		return -1;
	if (byte <= 'F')
		return byte - 'A' + 10;
	return -1;
}

void addbyte(char byte)
{
	if (byte == SOH)
	{
		sendbuf[bufptr++] = DLE;
		sendbuf[bufptr++] = SOH + 16;
	} else if (byte == EOT)
	{
		sendbuf[bufptr++] = DLE;
		sendbuf[bufptr++] = EOT + 16;
	} else if (byte == DLE)
	{
		sendbuf[bufptr++] = DLE;
		sendbuf[bufptr++] = DLE + 16;
	} else
	{
		sendbuf[bufptr++] = byte;
	}
	addcrc(byte);
}
void sendline(int fh, char *line)
{
	uint16_t addr, ncrc;
	int len;
	int byte;
	char *ptr;

	crc=0;
	bufptr=0;
	sendbuf[bufptr++] = SOH;
	addcrc(SOH);
	addbyte('2');
	ptr = line;
	if (*ptr++ != ':')
		return;
	len   = hex(*ptr++) << 4;
	len  += hex(*ptr++) << 0;
	printf("len = %x\n",len);
	addr  = hex(*ptr++) << 12;
	addr += hex(*ptr++) << 8;
	addr += hex(*ptr++) << 4;
	addr += hex(*ptr++) << 0;
	printf("addr = %x\n",addr);
	addbyte(addr & 0xff);
	addbyte(addr >> 8);

	if (*ptr++ != '0')
		return;
	if (*ptr++ != '0')
		return;
	while (len--)
	{
		byte  = hex(*ptr++) << 4;
		byte += hex(*ptr++) << 0;
		addbyte(byte);
	}
	printf ("%04x ", crc);
	ncrc = crc;
	addbyte(ncrc >> 8);
	addbyte(ncrc & 0xff);
	sendbuf[bufptr++] = EOT;
	len = write(fh, sendbuf, bufptr);
	crc=0;
	while (len = read(fh, sendbuf, 1))
	{
		if (sendbuf[0] == ETX)
			break;
		printf ("%02x ", sendbuf[0]);
		addcrc(sendbuf[0]);
	}
	printf ("CRC = %04x\n", crc);
}
	
	
int main (int argc, char *argv[])
{
	int fh,i;
	struct termios tty;
	FILE *in;
	char inbuf[1024];

	if (argc < 3)
	{
		printf("Usage: %s [device] [Intel Hex File]\n", argv[0]);
		exit(1);
	}
	fh = open(argv[1], O_RDWR);
	if (fh < 0)
	{
		printf("Can't open %s\n", argv[1]);
		exit(1);
	}

	in = fopen(argv[2], "r");
	if (!in)
	{
		printf("Can't open %s\n", argv[2]);
		exit(1);
	}
	tcgetattr(fh, &tty);
	tty.c_iflag = 0;
	tty.c_oflag = 0;
	tty.c_cflag = CS8 | CLOCAL | CREAD | PARENB | PARODD;
	cfsetospeed(&tty, B9600);
	cfsetispeed(&tty, B9600);
	tty.c_lflag = 0;
	tcsetattr(fh, TCSANOW, &tty);

	reset_dtr(fh);
	while (fscanf(in,"%s\n",inbuf) >= 0)
	{
		sendline(fh,inbuf);
	}
}
