/* Filename: kitsrus.cc
	Interface to Tony Nixon's PIC Programmers via the Kitsrus protocol
	This works for Kits 128, 149A, 149B, 150, 182

	Copyright (c) 2005, Terran Development Corporation
	All rights reserved.
	This code is made available to the public under a BSD-like license, a copy of which
	should have been provided with this code in the file LICENSE. For a copy of the BSD 
	license template please visit http://www.opensource.org/licenses/bsd-license.php

	$Id: kitsrus.cc,v 1.4 2007/06/17 05:03:20 bfoz Exp $
 * */
#include <fcntl.h>
#include <iostream>

#include "kitsrus.h"
#include "intelhex.h"

namespace kitsrus
{
#ifndef	Q_WS_WIN
	#define	HIBYTE(a)	(uint8_t)(a>>8)
	#define	LOBYTE(a)	(uint8_t)(a&0x00FF)
#endif	//Q_WS_WIN

	//Switch from power-on mode to command mode
	bool kitsrus_t::command_mode()
	{
		write('P');
		if( read() == 'P' )
			return true;
		else
			return false;
	}

	//Do a soft reset of the device
	//Send a 1 to the device. If it is in the command table it will reset. Either way it should return 'Q'
	bool kitsrus_t::soft_reset()
	{
		//Send a 1 to the device.
		// If it is in the command table it will reset. Either way it should return 'Q'
		write(CMD_RESET);
		if((read()) == 'Q')
			return true;
		else
			return false;
	}

	//Do a hard reset of the device
	bool kitsrus_t::hard_reset()
	{
		set_dtr();		//Set DTR high
#ifdef	Q_WS_WIN	//Deal with win32 stupidity
		Sleep(100);
#else
		usleep(10);		//Delay
#endif
		clear_dtr();	//Set DTR low

		if( read()=='B' )
		{
			read();	//Ignore the firmware type
			return true;
		}
		else
			return false;
	}

	bool kitsrus_t::init_program_vars()
	{
		write(CMD_INITVAR);
		write(HIBYTE(info.rom_size));
		write(LOBYTE(info.rom_size));
		write(HIBYTE(info.eeprom_size));
		write(LOBYTE(info.eeprom_size));
		write(info.core_type);   //FIXME  CoreType
		uint8_t i = (info.cal_word)?0x01:0x00;
		i |= (info.band_gap)?0x02:0x00;
		i |= (info.single_panel)?0x04:0x00;
		i |= (info.fast_power)?0x08:0x00;
		write(i);	 //Program flags
		write(info.program_delay);
		write(info.power_sequence);
		write(info.erase_mode);
		write(info.program_tries);
		write(info.over_program);
		if(read() == 'I')
			return true;
		return false;
	}
	

	bool kitsrus_t::chip_power_on()
	{
		write(CMD_VPP_ON);
		if( read() == 'V' )
			return true;
		else
			return false;
	}
	
	bool kitsrus_t::chip_power_off()
	{
		write(CMD_VPP_OFF);
		if( read() == 'v' )
			return true;
		else
			return false;
	}
	
	bool kitsrus_t::chip_power_cycle()
	{
		write(CMD_VPP_CYCLE);
		if( read() == 'V' )
			return true;
		else
			return false;
	}

	bool kitsrus_t::write_rom(intelhex::hex_data &HexData)
	{
//		uint8_t	c;
		uint8_t i;
		intelhex::hex_data::address_t	j(0);
		uint16_t k;
		intelhex::hex_data::size_type size;
		
		//Figure out how many ROM words need to be written
		size = 1 + HexData.max_addr_below(info.rom_size-1);
//		std::cout << __FUNCTION__ << ": size = " << size << std::endl;

		//Send program rom command
		write(CMD_WRITE_ROM);
		write( (size & 0xFF00) >> 8);  //Send size hi
		write(size & 0x00FF); //Send size low

		while(1)
		{
			switch(read())
			{
				case 'P':
//					std::cout << ".\n";
					emit_callback((j>size)?size:j,size);
					return true;
				case 'N':
					std::cerr << __FUNCTION__ << ": Got N at address ";
					k = read();
					k = k << 8;
					k |= (0x00FF & read());
					std::cerr << std::hex << k;
					std::cerr << " with word ";
					k = read();
					k = k << 8;
					k |= (0x00FF & read());
					std::cerr << std::hex << k;
					std::cerr << std::endl;
					return false;
				case 'Y':
					for(i=0; i<(32/2); ++i)
					{
//					std::cout << __FUNCTION__ << ": Pre get\n";
						const intelhex::hex_data::element a = HexData.get(j, info.get_blank_value());
//					std::cout << __FUNCTION__ << ": " << std::hex << a << " @ " << std::hex << j << "\n";
						write( (a & 0xFF00) >> 8);
						write( a & 0x00FF);
						++j;
					}
					if( !emit_callback((j>size)?size:j,size) )	//Emit callback and check for cancellation
						return false;
//					std::cout << "." << std::flush;
					break;
				default:
					std::cerr << __FUNCTION__ << ": Got unexpected character\n";
					return false;
			}
		}
		return true;
	}

//#define	WRITE_EEPROM_DEBUG
	bool kitsrus_t::write_eeprom(intelhex::hex_data &HexData)
	{
//		uint8_t	c;
//		uint8_t i;
		intelhex::hex_data::address_t	j(info.get_eeprom_start());
		intelhex::hex_data::address_t	eeprom_start(info.get_eeprom_start());
		intelhex::hex_data::address_t	eeprom_end;
		uint16_t progress;
		intelhex::hex_data::size_type size;
		
		//Ideally we would figure out how many ROM words are going to be written
		//	and then write only that. But, to make things simpler we'll just write
		//	to all of the ROM.
#if defined(WRITE_EEPROM_DEBUG)
		std::cout << __FUNCTION__ << ": eeprom_start = " << std::hex << j << std::endl;
		std::cout << __FUNCTION__ << ": eeprom_size = " << std::hex << info.eeprom_size << std::endl;
#endif
		size = HexData.size_in_range(j, info.get_eeprom_start() + info.eeprom_size);
		//Make size an even number
		if( (size % 2) != 0 )
			++size;
		eeprom_end = j + size - 1;
#if defined(WRITE_EEPROM_DEBUG)
		std::cout << __FUNCTION__ << ": size = " << size << std::endl;
		std::cout << __FUNCTION__ << ": eeprom_end = " << std::hex << eeprom_end << std::endl;
#endif

		//Send program rom command
		write(CMD_WRITE_EEPROM);
		write( (size & 0xFF00) >> 8);  //Send size hi
		write(size & 0x00FF); //Send size low

		while(1)
		{
			switch(read())
			{
				case 'P':
//					std::cout << ".\n";
					emit_callback((progress>size)?size:progress,size);
					return true;
				case 'Y':			
#if !defined(WRITE_EEPROM_DEBUG)
//					std::cout << "." << std::flush;
#endif
					write( HexData.get(j, 0xFF) & 0x00FF);
#if defined(WRITE_EEPROM_DEBUG)
					std::cout << __FUNCTION__ << ": wrote " << std::hex << HexData[j] << "\n";
#endif
					++j;
					write( HexData.get(j, 0xFF) & 0x00FF);
#if defined(WRITE_EEPROM_DEBUG)
					std::cout << __FUNCTION__ << ": wrote " << std::hex << HexData[j] << "\n";
#endif
					++j;
					progress = j - eeprom_start;
					if( !emit_callback((progress>size)?size:progress,size) )	//Emit callback and check for cancellation
						return false;
					break;
				default:
					std::cerr << __FUNCTION__ << ": Got unexpected character\n";
					return false;
			}
		}
		return true;
	}

	bool kitsrus_t::write_config(intelhex::hex_data &HexData)
	{
		std::vector<uint8_t> tmp_config(22, 0xFF);
		
//		std::cout << __FUNCTION__ << std::endl;
		
		intelhex::hex_data::address_t	i;
		if( info.is14bit() )
		{
			//If the ID bits were specified use them
			//	otherwise use blanks
			i = info.get_id_start();
			if( HexData.isset(i) )
			{
				tmp_config[0] = HexData[i++];
				tmp_config[1] = HexData[i++];
				tmp_config[2] = HexData[i++];
				tmp_config[3] = HexData[i];
			}
			tmp_config[4] = 'F';
			tmp_config[5] = 'F';
			tmp_config[6] = 'F';
			tmp_config[7] = 'F';
			i = info.get_config_start();
			if( HexData.isset(i) )
			{
				tmp_config[8] = HexData[i] & 0x00FF;
				tmp_config[9] = (HexData[i] & 0xFF00) >> 8;
			}
			else
			{
				std::cerr << __FUNCTION__ << ": no config bits\n";
				return false;		//FIXME
			}
		}
		write(CMD_WRITE_CONFIG);
		write('0');
		write('0');
		for( i=0; i<22; ++i)
		{
			write(tmp_config[i]);
			if( !emit_callback(i+1+2, 22+2) )	//Emit callback and check for cancellation
				return false;
		}
//		std::cout << __FUNCTION__ << "1" << std::endl;
		read();	//Throw away the ack
//		std::cout << __FUNCTION__ << "2" << std::endl;
		return true;
	}

	void kitsrus_t::write_calibration()
	{}

	//Read from a PIC into a hex_data structure
	bool kitsrus_t::read_rom(intelhex::hex_data &HexData)
	{
		intelhex::hex_data::element a;
		
		write(CMD_READ_ROM);
//		std::cout << "About to read " << info.rom_size << " words\n";
		for(unsigned i=0; i<info.rom_size; ++i)
		{
//			std::cout << "\r" << i;
			a = (read() << 8) & 0xFF00;
			a |= read() & 0x00FF;
			HexData[i] = a;
			if( !emit_callback(i+1, info.rom_size) )	//Emit callback and check for cancellation
				return false;
		}

//		std::cout << "\r";
//		std::cout << "\nRead " << info.rom_size << " words\n";
		return true;
	}

	bool kitsrus_t::read_eeprom(intelhex::hex_data &HexData)
	{
		intelhex::hex_data::address_t i(info.get_eeprom_start());
		const intelhex::hex_data::address_t stop(i + info.eeprom_size);

//		intelhex::hex_data::element a;
		intelhex::hex_data::address_t j(1);

//		std::cout << __FUNCTION__ << ": eeprom_start = " << std::hex << i << std::endl;
//		std::cout << __FUNCTION__ << ": eeprom_stop = " << std::hex << stop << std::endl;
//		std::cout << __FUNCTION__ << ": eeprom_size = " << std::hex << info.eeprom_size << std::endl;

		write(CMD_READ_EEPROM);
		for(; i<stop; ++i)
		{
			HexData[i] = read();
			if( !emit_callback(j++, info.eeprom_size) )	//Emit callback and check for cancellation
				return false;
//			std::cout << __FUNCTION__ << ": HexData[" << std::hex << i << "] = " << std::hex << HexData[i] << std::endl;
//			a = read();
//			std::cout << __FUNCTION__ << ": j = " << std::hex << (j++) << "\t" << std::hex << a << "\n";
		}
		return true;
	}

	bool kitsrus_t::read_config(intelhex::hex_data &HexData)
	{
		intelhex::hex_data::element	a[26];
		write(CMD_READ_CONFIG);
		
		uint8_t b = read();	//Throw away the ack
		if(b != 'C')
			std::cerr << __FUNCTION__ << ": Bad config ack\n\tExpected C got " << b << std::endl;
//		else
//			std::cout << __FUNCTION__ << ": Got ack\n";

		for(unsigned i=0; i<26; ++i)
		{
			a[i] = read();
			if( !emit_callback(i+1, 26) )	//Emit callback and check for cancellation
				return false;
//			std::cout << __FUNCTION__ << ": read " << std::hex << a[i] << "\n";
		}
		
		if(info.is14bit())
		{
			HexData[0x2000] = a[2];
			HexData[0x2001] = a[3];
			HexData[0x2002] = a[4];
			HexData[0x2003] = a[5];

			HexData[0x2007] = (a[0x0B] << 8) | a[0x0A];
		}
		return true;
	}

	bool kitsrus_t::erase_chip()
	{
		write(CMD_ERASE);
		uint8_t a = read();
		if( a != 'Y')
		{
			std::cerr << __FUNCTION__ << ": Bad erase\n\tExpected Y got: " << a << std::endl;
			return false;
		}
//		else
//			std::cout << __FUNCTION__ << ": Got Y\n";
		return true;
	}

	bool kitsrus_t::detect_chip()
	{
		write(CMD_IN_SOCKET);
		if( read() == 'A' )
		{
			read();
			return true;
		}
		else
			return false;
	}

	int kitsrus_t::get_version()
	{
		if(firmware < 0)
		{
			write(CMD_GET_VERSION);
			firmware = read();
		}
		return firmware;
	}

	std::string kitsrus_t::get_protocol()
	{
		std::string s;
		write(CMD_GET_PROTOCOL);
		s.push_back(read());
		s.push_back(read());
		s.push_back(read());
		s.push_back(read());
		return s;
	}

	std::string kitsrus_t::kit_name()
	{
		switch(firmware)
		{
			case KIT_128:
				return "Kit 128";
			case KIT_149A:
				return "Kit 149A";
			case KIT_149B:
				return "Kit 149B";
			case KIT_150:
				return "Kit 150";
			case KIT_170:
				return "Kit 170";
			case KIT_182:
				return "Kit 182";
			case KIT_185:
				return "Kit 185";
			default:
				return "Unknown";
		}
	}

	//Ugly kludge to work around the K149 reset logic
	//	This function is only used to set the firmware type for reset purposes
	//	Once the programmer has been reset, get_version() should be used to get the real
	//		firmware type
	void kitsrus_t::set_149()
	{
		firmware = KIT_149A;
	}

}	//namespace pocket


syntax highlighted by Code2HTML, v. 0.9.1