/***********************************************************************
 *  avrp - Atmel AVR programming software to use with Atmel's
 *         standard serial-port programmers.
 *  Copyright (C) 1997-1998 Jon Anders Haugum
 *
 *  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; see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 *
 *  Author of avrp can be reached at:
 *     email: jonah@colargol.tihlde.hist.no
 *     www: http://www.colargol.tihlde.hist.no/~jonah/el/avrp.html
 *     Postal address: Jon Anders Haugum
 *                     Øvre Møllenbergsgt 52
 *                     7014 Trondheim
 */

#include <stdio.h>
#include <string.h>

#include "avrp.h"
#include "talk.h"

int Program(struct ProgramInfo *PI, int What, int DoWhat)
	{
	int ok = False, loopok = True, i, count, adr = 0, line, StatAdr = 0, data;
	struct DataFile *DataFile;
	unsigned char Buff[256], CheckSum;
	char *filename;

	if(What == WHAT_EEPROM)
		filename = (char *)GET_ARG(PI->args, ARG_EEPROM);
	else
		filename = (char *)GET_ARG(PI->args, ARG_FLASH);

	DataFile = OpenDataFile(filename);
	if(DataFile)
		{
		line = 0;
		while(loopok && !ok)
			{
			loopok = False;
			line++;
			if(fgets(Buff, 256, DataFile->FP))
				{
				if(DataFile->FileType == FILETYPE_INTEL_HEX8M)
					{
					if(Buff[0] == ':')
						{
						count = atox_n(&Buff[1], 2);
						if(count != 0) /* ToDo: Sjekk count bedre */
							{
							count *= 2;
							CheckSum = 0;
							for(i = 1; i < (count + 11); i += 2)
								CheckSum += atox_n(&Buff[i], 2);
							if(!CheckSum)
								{
								if((PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 2) && (What == WHAT_FLASH))
									{
									adr &= 0xffff8000;
									adr |= atox_n(&Buff[3], 4) / 2;
									}
								else
									{
									adr &= 0xffff0000;
									adr |= atox_n(&Buff[3], 4);
									}
								if(atox_n(&Buff[7], 2) == 0)
									{
									loopok = True;
									for(i = 9; (i < (count + 9)) && loopok;)
										{
										data = atox_n(&Buff[i], 2);
										if((PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 2) && (What == WHAT_FLASH))
											{
											data |= atox_n(&Buff[i + 2], 2) << 8;
											i += 2;
											}
										i += 2;
										if(DoWhat == DO_PROGRAM)
											{
											if(!prog_adr(PI, adr, data, What, &StatAdr))
												loopok = False;
											}
										else
											{
											if(!verify_adr(PI, adr, data, What, &StatAdr))
												loopok = False;
											}
										adr++;
										}
									}
								else if(atox_n(&Buff[7], 2) == 2)
									{
									if((PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 2) && (What == WHAT_FLASH))
										adr = atox_n(&Buff[3], 4) << 15;
									else
										adr = atox_n(&Buff[3], 4) << 16;
									}
								else
									printf("Error: %s: Line %4d: Wrong type. Should be :XXXXXX00 or :XXXXXX02\n", filename, line);
								}
							else
								printf("Error: %s: Line %4d: Faulty checksum\n", filename, line);
							}
						else
							if(!strncmp(&Buff[3], "000001FF", 8))
								ok = True;
						}
					else
						printf("Error: %s: Line %4d: Missing ':' at linestart\n", filename, line);
					}
				else if(DataFile->FileType == FILETYPE_ATMEL_GENERIC)
					{
					if(What == WHAT_FLASH)
						i = 6;
					else
						i = 4;
					if(Buff[i] == ':')
						{
						Buff[i] = '\0';
						adr = atox(Buff);
						if((PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 2) && (What == WHAT_FLASH))
							data = atox_n(&Buff[i + 1], 4);
						else
							data = atox_n(&Buff[i + 1], 2);
						if(DoWhat == DO_PROGRAM)
							{
							if(prog_adr(PI, adr, data, What, &StatAdr))
								loopok = True;
							}
						else
							{
							if(verify_adr(PI, adr, data, What, &StatAdr))
								loopok = True;
							}
						}
					else
						printf("Error: %s: Line %4d: Missing ':' between address and data fields\n", filename, line);
					}
				}
			else
				{
				if(feof(DataFile->FP))
					{
					if(DataFile->FileType == FILETYPE_ATMEL_GENERIC)
						ok = True;
					}
				else
					perror((char *)GET_ARG(PI->args, ARG_FLASH));
				}
			}
		if(ok)
			printf("\n");
		CloseDataFile(DataFile);
		}
	return(ok);
	}


int prog_adr(struct ProgramInfo *PI, int adr, int data, int What, int *StatAdr)
	{
	unsigned char SerBuff[2];

	if((What == WHAT_EEPROM) || !GET_ARG(PI->args, ARG_AUTOINC) || (*StatAdr != adr) || (*StatAdr == 0))
		{
		SerWriteByte(PI->Ser, 'A');
		SerWriteByte(PI->Ser, (unsigned char)(adr >> 8));
		SerWriteByte(PI->Ser, (unsigned char)adr);
		if(!((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13)))
			{
			print_comm_err('A');
			return(False);
			}
		}
	if((PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 2) && (What == WHAT_FLASH))
		{
		SerWriteByte(PI->Ser, 'c');
		SerWriteByte(PI->Ser, (unsigned char)data);
		if((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13))
			{
			SerWriteByte(PI->Ser, 'C');
			SerWriteByte(PI->Ser, (unsigned char)(data >> 8));
			if((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13))
				{
				if(!GET_ARG(PI->args, ARG_QUICK))
					Progress(StatAdr, adr, 'P');
				else
					for(; *StatAdr <= adr; (*StatAdr)++);
				return(True);
				}
			else
				print_comm_err('C');
			}
		else
			print_comm_err('c');
		}
	else
		{
		if(What == WHAT_FLASH)
			SerWriteByte(PI->Ser, 'c');
		else
			SerWriteByte(PI->Ser, 'D');
		SerWriteByte(PI->Ser, (unsigned char)data);
		if((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13))
			{
			if(!GET_ARG(PI->args, ARG_QUICK))
				Progress(StatAdr, adr, 'P');
			else
				for(; *StatAdr <= adr; (*StatAdr)++);
			return(True);
			}
		else
			{
			if(What == WHAT_FLASH)
				print_comm_err('c');
			else
				print_comm_err('D');
			}
		}
	return(False);
	}


int verify_adr(struct ProgramInfo *PI, int adr, int data, int What, int *StatAdr)
	{
	unsigned char SerBuff[2];

	if((What == WHAT_EEPROM) || !GET_ARG(PI->args, ARG_AUTOINC) || (*StatAdr != adr) || (*StatAdr == 0))
		{
		SerWriteByte(PI->Ser, 'A');
		SerWriteByte(PI->Ser, (unsigned char)(adr >> 8));
		SerWriteByte(PI->Ser, (unsigned char)adr);
		if(!((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13)))
			{
			print_comm_err('A');
			return(False);
			}
		}
	if((PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 2) && (What == WHAT_FLASH))
		{
		SerWriteByte(PI->Ser, 'R');
		if(SerReadData(PI->Ser, SerBuff, 2))
			{
			if(((SerBuff[0] << 8) | SerBuff[1]) == data)
				{
				if(!GET_ARG(PI->args, ARG_QUICK))
					Progress(StatAdr, adr, 'V');
				else
					for(; *StatAdr <= adr; (*StatAdr)++);
				return(True);
				}
			else
				printf("\nError: Verify failed! [Address: 0x%04x Filedata: 0x%04x Chipdata: 0x%04x]\n",
				       adr, data, (int)(SerBuff[0] << 8) | SerBuff[1]);
			}
		else
			print_comm_err('R');
		}
	else
		{
		if(What == WHAT_FLASH)
			SerWriteByte(PI->Ser, 'R');
		else
			SerWriteByte(PI->Ser, 'd');
		if(SerReadData(PI->Ser, SerBuff, 1))
			{
			if(SerBuff[0] == data)
				{
				if(!GET_ARG(PI->args, ARG_QUICK))
					Progress(StatAdr, adr, 'V');
				else
					for(; *StatAdr <= adr; (*StatAdr)++);
				return(True);
				}
			else
				printf("\nError: Verify failed! [Address: 0x%04x Filedata: 0x%02x Chipdata: 0x%02x]\n",
				       adr, data, (int)SerBuff[0]);
			}
		else
			{
			if(What == WHAT_FLASH)
				print_comm_err('R');
			else
				print_comm_err('d');
			}
		}
	return(False);
	}


int Read(struct ProgramInfo *PI, int What)
	{
	int ok = False, loopok = True, i, flash_size, StatAdr = 0;
	unsigned char SerBuff[2], Checksum;
	char *filename;
	FILE *FP;

	if(What == WHAT_FLASH)
		filename = (char *)GET_ARG(PI->args, ARG_FLASH);
	else
		filename = (char *)GET_ARG(PI->args, ARG_EEPROM);
	FP = fopen(filename, "wb");
	if(FP)
		{
		if(What == WHAT_FLASH)
			{
			flash_size = (PI->SelectedChipNode->FamilyNode->FlashSize * 1024)
			             / PI->SelectedChipNode->FamilyNode->ArchNode->WordSize;
			for(i = 0; (i < flash_size) && loopok; i++)
				{
				ok = False;
				if(PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 1)
					{
					if((i % 16) == 0)
						{
						fprintf(FP, ":10%04X00", i);
						Checksum = 0;
						Checksum += 0x10;
						Checksum += i >> 8;
						Checksum += i & 0xFF;
						}
					}
				else
					{
					if((i % 8) == 0)
						{
						fprintf(FP, ":10%04X00", i * 2);
						Checksum = 0;
						Checksum += 0x10;
						Checksum += (i * 2) >> 8;
						Checksum += (i * 2) & 0xFF;
						}
					}
				if(!GET_ARG(PI->args, ARG_AUTOINC) || (StatAdr != i) || (StatAdr == 0))
					{
					SerWriteByte(PI->Ser, 'A');
					SerWriteByte(PI->Ser, (unsigned char)(i >> 8));
					SerWriteByte(PI->Ser, (unsigned char)i);
					if(!((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13)))
						{
						print_comm_err('A');
						loopok = False;
						}
					}
				if(loopok)
					{
					SerWriteByte(PI->Ser, 'R');
					if(SerReadData(PI->Ser, SerBuff, PI->SelectedChipNode->FamilyNode->ArchNode->WordSize))
						{
						if(PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 1)
							{
							fprintf(FP, "%02X", (int)SerBuff[0]);
							Checksum += SerBuff[0];
							}
						else
							{
							fprintf(FP, "%02X%02X", (int)SerBuff[1], (int)SerBuff[0]);
							Checksum += SerBuff[1];
							Checksum += SerBuff[0];
							}
						ok = True;
						if(!GET_ARG(PI->args, ARG_QUICK))
							Progress(&StatAdr, i, 'R');
						else
							for(; StatAdr <= i; StatAdr++);
						}
					else
						{
						print_comm_err('R');
						loopok = False;
						}
					}
				if(PI->SelectedChipNode->FamilyNode->ArchNode->WordSize == 1)
					{
					if((i % 16) == 15)
						fprintf(FP, "%02X\x0d\x0a", (int)(unsigned char)-Checksum);
					}
				else
					{
					if((i % 8) == 7)
						fprintf(FP, "%02X\x0d\x0a", (int)(unsigned char)-Checksum);
					}
				}
			}
		else
			{
			for(i = 0; (i < PI->SelectedChipNode->EepromSize) && loopok; i++)
				{
				ok = False;
				if((i % 16) == 0)
					{
					fprintf(FP, ":10%04X00", i);
					Checksum = 0;
					Checksum += 0x10;
					Checksum += i >> 8;
					Checksum += i & 0xFF;
					}
				SerWriteByte(PI->Ser, 'A');
				SerWriteByte(PI->Ser, (unsigned char)(i >> 8));
				SerWriteByte(PI->Ser, (unsigned char)i);
				if((SerReadData(PI->Ser, SerBuff, 1)) && (SerBuff[0] == 13))
					{
					SerWriteByte(PI->Ser, 'd');
					if(SerReadData(PI->Ser, SerBuff, 1))
						{
						fprintf(FP, "%02X", (int)SerBuff[0]);
						Checksum += SerBuff[0];
						ok = True;
						if(!GET_ARG(PI->args, ARG_QUICK))
							Progress(&StatAdr, i, 'R');
						else
							for(; StatAdr <= i; StatAdr++);
						}
					else
						{
						print_comm_err('d');
						loopok = False;
						}
					}
				else
					{
					print_comm_err('A');
					loopok = False;
					}
				if((i % 16) == 15)
					fprintf(FP, "%02X\x0d\x0a", (int)(unsigned char)-Checksum);
				}
			}
		if(ok)
			{
			fprintf(FP, ":00000001FF\x0d\x0a");
			printf("\n");
			}
		fclose(FP);
		}
	else
		perror(filename);
	return(ok);
	}



void Progress(int *StatAdr, int Adr, char DidWhat)
	{
	for(; *StatAdr <= Adr; (*StatAdr)++)
		{
		if((*StatAdr % 64) == 0)
			{
			if(*StatAdr)
				printf("\n");
			printf("0x%04X: ", *StatAdr);
			}
		if(*StatAdr == Adr)
			printf("%c", DidWhat);
			else
			printf("-");
		fflush(stdout); /* Friker med NULL som arg under SAS/C */
		}
	}

