/*
 *	TM4		Version 1.3  December, 2001
 ********************************************************************
 *
 * TM4, a Linux PIC programmer supporting the Newfound Electronics WARP-13
 * programmer.
 *
 * Original by Bill Danielson, modified for Linux by Dale A. Heatherington.
 *
 * This version (1.3) (December 2001) amended by R. J. Pryor
 *  (rjpryor@infoserve.net).
 *
 * Current version 1.3, changes from V1.2 are:
 *
 * 1)   Added Makefile.
 * 2)	Cleaned up code to reduce compiler grumbles.
 * 3)	Ran code through indent to (try to) regularize the style.
 * 4)	Added -n option to allow user to remove 1mS delay after
 *	writing each byte, reduces programming time by about a factor of 3.
 * 5)	Added -E option to suppress automatic data eeprom programming.
 * 6)	Changed output running count display to show in hex.
 * 7)	Changed debug out to stderr so it can be redirected separately.
 *
 *	All changes tested with 16F877, attempts have been made not
 *	to disturb any existing usage.	RJP
 *
 *	NOTE:	It has been found that the WAPR13 programmer does not
 *		handle the DCD/DSR lines, and that an initial open
 *		on /dev/ttyS1 may hang waiting for this line to assert.
 *		This can be fixed by adding a wire jumper on the WARP13
 *		board between pins 1-4-6 on the 9 pin connector.
 *
 *----------------------------------------------------------------------
 * 
 * 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
 * 
 * -----------------------------------------------------------------------
 *    
 * V1.2 starts here 
 *
 * tm4.c for unix originally by Bill Danielson
 * 
 * July 11 1999:  Modified for Linux by Dale A. Heatherington
 * 
 * This program talks to PIC programmers from Newfound Electronics using
 * their TM4 serial protocol. I tested it with the Warp-13 programmer. See
 * http://www.new-elect.com for more PIC programmer information.
 * 
 * The source is a single file.  There is no makefile. It was developed and
 * tested on RedHat 5.2 Linux.
 * 
 * COMPILING:
 * 
 * To compile for the Warp-13 programmer (19200 baud):
 *
 * gcc -o tm4 tm4-12.c
 *
 * To compile for a programmer that wants 38400 BAUD:
 *
 * gcc -o tm4 tm4-12.c -DBAUDRATE=B38400
 *
 * Valid BAUDRATE values are: B9600, B19200, B38400, B57600, B115200. 
 * 
 * After compiling the resulting executable, tm4, should be moved to a
 * directory where other user programs are stored such as /usr/bin or
 * /usr/local/bin.
 * 
 * The Newfound Electronics PIC programmer is expected to be connected to
 * serial device "/dev/pic" .  You should create a symbolic link from this to 
 * the actual serial device. Example for COM1 :  ln -s /dev/ttyS0 /dev/pic
 * 
 * For help use the -h option. eg: tm4 -h This will list the command line
 * options and syntax.
 * 
 * -----------------------------------------------------------------------
 * 
 * Revision notes:
 * 
 * Sep 23, 1999 version 1.2  (by Dale Heatherington)
 * 
 * (1) Fixed error in pic profiles[4] for 12 bit 8 pin parts. The mask and
 * blank values were wrong.
 * 
 * (2) Fixed coding error in set_config_word function call. It was only called 
 * if the -x command line option was used. It should always be called.
 * 
 * Aug 30-Sep 9 1999 version 1.1: (by Dale Heatherington) (1) Made
 * modifications for dealing with the calibration word . (2) Fixed "count"
 * problems with 16C5x parts. (3) Added code to compute checksums on the hex
 * object file and detect errors.
 * 
 * (4) Added the "-r" option to read the chip and format the data into an
 * Intel HEX file. The data may be written to a file or, if no file name is
 * given, the console (stdout) .
 * 
 * Added the -R option to perform a hex dump to the console of all the chips
 * memory, program ROM, configuration area and data EEPROM if present.
 * 
 * (5) Added print_calword() function. (6) Added read_pic_memory() function.
 * (7) Fixed read_config_14bit() function to properly skip the 3 reserved
 * bytes.
 * 
 * (8) Added new field to pic->profile structure "dataEEpromSize" indicates
 * the size of the data EEPROM.
 * 
 * (9) Added code to write/read/verify the data EEPROM memory in 16f8x parts.
 *  The EEPROM data is supplied from the intel hex object file starting at
 * address 0x2100.  If no EEPROM data is present in the file the data EEPROM
 * will be written with all zeros. In other words, the default value for all
 * data EEPROM locations not specified in the file is 0x00.
 * 
 * (10) Modified the find_pic() function so it's case insensitive. Now 16f84
 * is the same as 16F84.
 * 
 * (11) Default serial port is now /dev/pic . The user must create a symbolic
 * link from this to the serial device he has the programmer connected to or
 * supply the device name on the command line with the -p option. The
 * advantage is you don't need to specify a serial port in makefiles and still 
 * connect the programmer to any port by changing the symbolic link. To create 
 * a symbolic link:  ln -s /dev/ttyS0 /dev/pic
 * 
 * (12) Added code to force reading the intel hex object file from STDIN if no 
 * file is specified on the command line. This makes it possible to pipe data
 * into the programmer from other programs. eg: cat myfile.hex | tm4 -c 16f84
 * 
 * (13) Added the "-l" option to list all the pic names to the console.
 * 
 * (14) Added the "-x" option to allow user to specify the RC Oscillator
 * calibraton value on the command line.
 * 
 * (15) The program now knows the highest address used in the hex object file
 * and stops programming when this is reached. The whole rom will still be
 * written if a Cal word is specified. ** Factory set cal word will not be
 * overwritten.
 * 
 * (16) Added option "-e" to override the normal pic->package value to P40_14B 
 * which puts Vpp on pin-1 on the zif socket and also directs Vpp to the 5 pin 
 * external isp programming connector.  **Warning: If this option is used with 
 * a chip in the 40 pin zif socket it may be destroyed.  Only use this option
 * if your want to program chips connected to the 5 pin isp connector.
 * 
 * (17) Fixed RC Oscillator calibration word logic in blank_protocol for 12
 * parts. The blank check now checks all locations including IDs and config
 * word for both 12 and 14 bit parts. The Calibration word will be skipped
 * if present.
 */

/*
 * -----------------------------------------------------------------
 * 
 * The following TM4 protocol information is courtesy of: Jim Robertson
 * <newfound@pipeline.com.au> 1/30/98 email to Bill Danielson
 * 
 * NOMENCLATURE
 * ------------
 * 
 * "16C5x PARTS"
 * 
 * 16c5X refers to parts numbered as 16C5x. It does NOT include 16C55x parts
 * or the 12C50x parts. Just the 16C52/54/55/56/57/58 only. These 16C5x parts
 * are programmed via a parallel method.
 * 
 * "12-bit PARTS"
 * 
 * 12-BIT means all 16C5x parts (See above) PLUS the 8-pin 12C50x parts (ISP). 
 * 
 * "14-bit PARTS"
 * 
 * 14-bit PARTS means all the 16Cxx(x) parts not covered by the 12-bit
 * definition. It also includes the PIC14000 and also includes the newer
 * 14-bit core parts with the 2-bit parity check system (16C715, 16C64x,
 * 16C66x etc.) (See 14-bit/2P and 14-bit/NP below)
 * 
 * 14-bit/2P PARTS
 * 
 * This Definition only includes the newer 14-bit core parts that feature the
 * 2-bit parity system. Examples are the 16C715, 16C64x, 16C66x etc.
 * 
 * It DOES NOT include the 14-bit parts that DO NOT implement the 2-bit parity
 * check system.
 * 
 * 14-bit/NP PARTS
 * 
 * This Definition is the opposite to the above. It only includes the 14-bit
 * core parts WITHOUT the 2-bit parity system.
 * 
 * The destination between 14-bit parts with and without parity is required for 
 * the UNUSED BITS MASK value (See below) and for the BIT-7 of the CtrlByte for 
 * ALL READ, WRITE and BLANK CHECK operations.
 * 
 * "ISP PARTS"
 * 
 * ISP PARTS means all parts that are programmed serially (as opposed to the
 * parallel method used by the 16C5x parts.) Currently, any part that is not a
 * 16C5x part is an "ISP PART."
 * 
 * These definitions are required to cover in the broadest possible way all the 
 * various programming groups.
 * 
 * COMMANDS
 * --------
 * 
 * TM0 commands: housekeeping, firmware variables, etc.
 * 
 * 0x00 Close Device 0x01 ?? ?? ?? 0x06 Send Ctrl Byte CTRLBYTE 0x07 ?? ?? 0x0a 
 * Send Count COUNTHIGH COUNTLOW 0x0e ??
 * 
 * TM1 commands: 16C5x (parallel) programming
 * 
 * 0x00 Open PACKAGE 0x02 Enter Read Mode COUNTHIGH COUNTLOW DONTCARE MASK 0x03 
 * Enter Write Mode COUNTHIGH COUNTLOW DONTCARE MASK 0x05 Skip Locations
 * 
 * TM2 commands: ISP programming
 * 
 * 0x00 Open PACKAGE 0x02 Enter Read Mode COUNTHIGH COUNTLOW COMMAND MASK 0x03 
 * Enter Write Mode COUNTHIGH COUNTLOW COMMAND MASK 0x05 Skip Locations
 * COUNTHIGH COUNTLOW 0x06 Kill Copy Protection 0x09 Enter Config Area 0x0b
 * Skip 7 locations 0x0c Send Command COMMAND 0x0d Free Delay 0x0e Free Write 
 * WORDHIGH WORDLOW
 * 
 * CTRLBYTE (PROGRAM MODE)
 * -----------------------
 * 
 * TM4 uses a ctrlbyte to select various options within the firmware. The
 * firmware is organized as a series of universal routines that are "trimmable" 
 * to the differences between PIC devices or special locations such as the
 * config word on some devices.
 * 
 * These "trimmable" differences currently include "NOT SELF-TIMED Vs
 * SELF-TIMED, 100uS Vs 10mS (OR EPROM Vs EEPROM/FLASH for previous two),
 * 14-BIT/2P Vs REST, 12-BIT CONFIG WORD WRITE Vs REST (SPECIAL 10mS "PULSED"
 * timing as recommend for config word only)
 * 
 * The Ctrlbyte also is used to specify between a READ or BLANK CHECK OPERATION 
 * when the READ mode is entered.
 * 
 * Here is the bit breakdown of the CTRLBYTE:
 * 
 * WRITE
 * 
 * bit 0 program pulse width 0=100uS (EPROM) 1=10mS (EEPROM/FLASH) bit 1 Self
 * timed flag 0= !self timed (EPROM) 1=self timed (EEPROM/FLASH) bit 2
 * 0=read/verify 1= BLANK CHECK (return blank flag only, 0=!BLANK, 0XFF=BLANK)
 * bit 3 0= (!fuse | !EPROM), 1=(FUSE & EPROM) (Corrected 18th Aug '99) (special 
 * multi pulse program for config word) bit 4 Internal use only, write as 0 bit
 * 5 N/A bit 6 N/A bit 7 0 = 12-bit and 14-bit/NP PARTS; 1 = 14-bit/2P PARTS
 * (16C715,64x,66x)
 * 
 * 
 * COMMANDS
 * --------
 * NEW ADDED INFO SUPPLIED BY NEWFOUND ELECTRONICS 12th MAY
 * 1999
 * 
 * "TM0" & 0x01, OA, DATA, - Set programming variables, Eg, Retry count, over
 * program count
 * 
 * OA = OBJECT ADDR DATA = 1-BYTE variable
 * 
 * Usage "TM0" 0x1, OA, DATA
 * 
 * OBJECT AND ADDRESSES
 * 
 * RETRYCOUNT 0x00 MUST set at 8 for ALL 12-bit core parts, 25 for all others
 * OVERPRGRAM 0x01 MUST set at 11 " other " 3 " " DELAYx50uS 0x02 Do not change
 * DELAY10mS 0x03 New 16C87x parts have 5mS program pulse so this value _could_
 * be set a 0x64 for faster programming of these parts. You do not have to
 * change this value.
 * 
 * SPARE 0x04 Do not change VOLTS 0x05 Do not change (Reserved for production
 * programmer. BAUD 0x06 Do not change (Unless you think variable baud would be
 * useful)
 * 
 * ____________________________________
 * 
 * TM0 0x07 "PING"
 * 
 * USAGE "TM0" 0x07
 * 
 * RETURNS 0x01 FROM FIRMWARE.
 * 
 * This is a fast way to verify the presence of a NEWFOUND programmer.
 * 
 * ___________________________________
 * 
 * 0x09 SEND CHECKSUM.
 * 
 * A 16 bit checksum is generated during a blank operation. Use this command to
 * read it from the firmware.
 * ___________________________________
 * 
 * 0x0B VPPON
 * 
 * Debug command, Turns VPP on
 * ___________________________________
 * 
 * 0x0C VDDON
 * 
 * Debug command, Turns VDD on
 * ___________________________________
 * 
 * 0x0D UNUSED
 * 
 * ___________________________________
 * 
 * 0x0E SEND IDSTRING
 * 
 * 16 Bytes containing programmer and firmware version information.
 * ___________________________________
 */

/*
 **********************************************************
 *
 *	INCLUDES
 */

#include <stdio.h>
#include <termio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>

/*
 * This sets the baudrate used to communicate with the programmer. *  Valid
 * strings are: B9600, B19200, B38400, B57600, B115200. The Warp-13
 * programmer uses B19200. Valid values are: B9600, B19200, B38400, B57600,
 * B115200. 
 */
#ifndef BAUDRATE
#define BAUDRATE B19200
#endif

#define TRUE 1
#define FALSE 0
#define BOOL int

#define WAIT 1000

int     debugging = 0;		// 0-> no debugging, 1-> some data, 2-> full data
				// add -d for 1 or -d -d for 2!
int     showProgess = 0;
int	no_delay = FALSE;
int	program_data = TRUE;
char    cmd0;
int     tty;

typedef unsigned char bit08;
typedef unsigned short bit16;

#define EEPROM TRUE
#define PGM    FALSE
#define MEMSIZE 64*1024

int     tty;
bit16   cbuf[MEMSIZE]; /* Buffer used for compares */
bit16   mem[MEMSIZE];  /* Buffer used for object */
bit16   CalBuffer[1];  /* Place to store CAL word */

/*
 **********************************************************
 *
 *	PIC DATA STRUCTURES
 */

#define P18_12B 0x00
#define P18_14B 0x01
#define P28_12B 0x02
#define P28_SDIP 0x03
#define P40_14B 0x04
#define P14000 0x05
#define P08_12B 0x06
#define P08_14B 0x07

#define P_100US 0x00	/* program pulse width 100US (EPROM) */
#define P_10MS 0x01	/* program pulse width 10MS (EEPROM/FLASH) */
#define S_TIMED 0x02	/* Self timed part (EEPROM/FLASH) */
#define B_FLAG 0x04	/* Read/verify BLANK CHECK (FF=BLANK) */
#define B14_2P 0x80	/* 14BIT/2P parts (16C715, 64x, 66x) */

struct pic_profile
{
	bit08   mask;
	bit08   ctrlread;
	bit08   ctrlwrite;
	bit08   RetryCount;
	bit08   OverPgm;
	BOOL    LastWordCal;
	bit16   initval;
	bit16   dataEEpromSize;
	BOOL    flash;

} profiles[] =
{
	{ 0x0f, 0x00, 0x00, 0x08, 0x0B, FALSE, 0x0fff, 0, FALSE},	// [0] ALL 12 bit eprom parts 
	{ 0x3f, 0x00, 0x00, 0x19, 0x03, FALSE, 0x3fff, 0, FALSE},	// [1] most 14 bit eprom parts 
	{ 0x3f, S_TIMED, S_TIMED | P_10MS, 0x01, 0x01, FALSE, 0x3fff, 64, TRUE},	// [2] 16c84, 16F83, 16F84, 16F87x 
	{ 0x3f, B14_2P, B14_2P, 0x19, 0x03, FALSE, 0x3fff, 0, FALSE},	// [3] ALL 14 bit 2P parts 
	{ 0x0f, 0x20, 0x20, 0x8, 0x0b, TRUE, 0x0fff, 0, FALSE},		// [4] 12-bit 8 pin parts with calword
	{ 0x3f, 0x20, 0x20, 0x19, 0x03, TRUE, 0x3fff, 0, FALSE}		// [5] 14-bit 8 pin parts with calword
};

struct pic
{
	char    name[16];
	bit08   package;
	bit16   size;
	struct pic_profile *profile;
} pics[] =
{
	/* Start NFE 25 JULY 99 */
	{ "12c508", P08_12B, 512, &profiles[4]},
	{ "12c508a", P08_12B, 512, &profiles[4]},
	{ "12c509", P08_12B, 1024, &profiles[4]},
	{ "12c509a", P08_12B, 1024, &profiles[4]},
	{ "12ce518", P08_12B, 512, &profiles[4]},
	{ "12ce519", P08_12B, 1024, &profiles[4]},
	{ "12c671", P08_14B, 1024, &profiles[5]},
	{ "12c672", P08_14B, 2048, &profiles[5]},
	{ "12ce673", P08_14B, 1024, &profiles[5]},
	{ "12ce674", P08_14B, 2048, &profiles[5]},
		/* End NFE 25 JULY 99 */
	{ "16c52", P18_12B, 512, &profiles[0]},
	{ "16c54", P18_12B, 512, &profiles[0]},
	{ "16c54a", P18_12B, 512, &profiles[0]},
	{ "16c54b", P18_12B, 512, &profiles[0]},
	{ "16c54c", P18_12B, 512, &profiles[0]},
	{ "16c55", P28_12B, 512, &profiles[0]},
	{ "16c55a", P28_12B, 512, &profiles[0]},
	{ "16c56", P18_12B, 1024, &profiles[0]},
	{ "16c56a", P18_12B, 1024, &profiles[0]},
	{ "16c57", P28_12B, 2048, &profiles[0]},
	{ "16c57c", P28_12B, 2048, &profiles[0]},
	{ "16c58a", P18_12B, 2048, &profiles[0]},
	{ "16c58b", P18_12B, 2048, &profiles[0]},
	{ "16c61", P18_14B, 1024, &profiles[1]},
	{ "16c620", P18_14B, 512, &profiles[1]},
	{ "16c620a", P18_14B, 512, &profiles[1]},
	{ "16c621", P18_14B, 1024, &profiles[1]},
	{ "16c621a", P18_14B, 1024, &profiles[1]},
	{ "16c622", P18_14B, 2048, &profiles[1]},
	{ "16c622a", P18_14B, 2048, &profiles[1]},
	{ "16ce623", P18_14B, 512, &profiles[1]},	/* added NFE 13/July */
	{ "16ce624", P18_14B, 1024, &profiles[1]},	/* ''         ''   */
	{ "16ce625", P18_14B, 2048, &profiles[1]},	/* ''         ''   */
	{ "16c62", P28_SDIP, 2048, &profiles[1]},
	{ "16c62a", P28_SDIP, 2048, &profiles[1]},
	{ "16c62b", P28_SDIP, 2048, &profiles[1]},
	{ "16c63", P28_SDIP, 4096, &profiles[1]},
	{ "16c63a", P28_SDIP, 4096, &profiles[1]},
	{ "16c63b", P28_SDIP, 4096, &profiles[1]},
	{ "16c64", P40_14B, 2048, &profiles[1]},
	{ "16c64a", P40_14B, 2048, &profiles[1]},
	{ "16c64b", P40_14B, 2048, &profiles[1]},	/* no such part */
	{ "16c65", P40_14B, 4096, &profiles[1]},
	{ "16c65a", P40_14B, 4096, &profiles[1]},
	{ "16c65b", P40_14B, 4096, &profiles[1]},	/* no such part */
	{ "16c66", P28_SDIP, 8192, &profiles[1]},
	{ "16c67", P40_14B, 8192, &profiles[1]},
	{ "16c71", P18_14B, 1024, &profiles[1]},
	{ "16c710", P18_14B, 512, &profiles[1]},
	{ "16c711", P18_14B, 1024, &profiles[1]},
	{ "16c715", P18_14B, 2048, &profiles[3]},
	{ "16c72", P28_SDIP, 2048, &profiles[1]},
	{ "16c72a", P28_SDIP, 2048, &profiles[1]},
	{ "16c73", P28_SDIP, 4096, &profiles[1]},
	{ "16c73a", P28_SDIP, 4096, &profiles[1]},
	{ "16c73b", P28_SDIP, 4096, &profiles[1]},
	{ "16c74", P40_14B, 4096, &profiles[1]},
	{ "16c74a", P40_14B, 4096, &profiles[1]},
	{ "16c74b", P40_14B, 4096, &profiles[1]},
	{ "16c76", P28_SDIP, 8192, &profiles[1]},
	{ "16c77", P40_14B, 8192, &profiles[1]},
	{ "16c773", P28_SDIP, 4096, &profiles[1]},
	{ "16c774", P28_SDIP, 4096, &profiles[1]},
	{ "16c923", P40_14B, 4096, &profiles[1]},	/* size 68 PLCC ??  */
	{ "16c924", P40_14B, 4096, &profiles[1]},	/* size 68 PLCC ??  */
	{ "16c84", P18_14B, 1024, &profiles[2]},
	{ "16f84", P18_14B, 1024, &profiles[2]},
	{ "16f84a", P18_14B, 1024, &profiles[2]},
	{ "16f83", P18_14B, 512, &profiles[2]},
	{ "16f873", P28_SDIP, 4096, &profiles[2]},
	{ "16f874", P40_14B, 4096, &profiles[2]},
	{ "16f876", P28_SDIP, 8192, &profiles[2]},
	{ "16f877", P40_14B, 8192, &profiles[2]},
	{ "", 0, 0, (struct pic_profile *) 0}
};

/*
 **********************************************************
 *
 *	FUNCTIONS
 */

static void init();
static void read_protocol(struct pic *, bit16 *, bit16, int);
static void read_pic_memory(struct pic *, bit16 *, bit16, int);
static int blank_protocol(struct pic *);
static int core12(struct pic *);
static void read_protocol_12(struct pic *, bit16 *);
static void read_config_14bit(struct pic *, bit16 *);
static void read_data_eeprom(struct pic *, bit16 *);
static void write_protocol(struct pic *, bit16 *, bit16, int);
static void write_protocol_12(struct pic *, bit16 *, int);
static void write_fuse_12bit(struct pic *, bit16);
static void write_ID_isp(struct pic *, bit16 *);
static void write_config_isp(struct pic *, bit16 *);
static void set_count(bit16);
static void skip_locations(struct pic *, bit16);
static void skip_7();
static void kill_copy_protect();
#if 0
static void write_word(bit16);
static void delay();
#endif
static void open_device(struct pic *);
static void close_device();
static void send_count(bit16);
static void send_data(bit16 *, bit16);
static void send_word(bit16);
static void send_ctrl_byte(bit08);
static void enter_config_area();
static void send_tm0();
static void send_tm1();
static void send_tm2();
static void send_tm1_2(bit08);
static int open_tty(char *);
static void send_byte(bit08);
static bit08 get_byte();
static void get_bytes(bit08 *, bit16);
static void get_data(bit16 *, bit16);
static void timed_read(bit08 *, bit16);
static void log_read(bit08);
static void log_write(bit08);
static void log_clear();
static void log_dump();
static void terminate(int);
static void read_pic_data(struct pic *);
static void intel_hex_line_out(unsigned char *, int, int, FILE *);
static void read_pic_intel_hex(struct pic *, char *);
static void check_blank(struct pic *);
static int hex(char);
static int parse_line(struct pic *, char *, bit16 *, int, int *);
static int read_object(struct pic *, char *, bit16 *, int);
static void init_memory(struct pic *, bit16 *);
static void fix_memory(struct pic *, bit16 *);
static void dump_memory(struct pic *, bit16 *);
static struct pic *find_pic(char *);
static void print_pic_list();
static void write_pic(struct pic *, bit16 *, int);
static void write_data_eeprom(struct pic *, bit16 *);
static void verify_data_eeprom(struct pic *, bit16 *, bit16 *);
static void verify_pic(struct pic *, bit16 *, bit16 *, int);
static void write_config(struct pic *, bit16 *);
static void verify_config(struct pic *, bit16 *, bit16 *);
static void set_config_word(struct pic *, bit16 *, char *);
static void set_cal_word(struct pic *, bit16 *, char *);
static void print_calword(struct pic *, bit16 *);

/*
 ****************************************************************************
 *
 *	INITIALIZE DEVICE
 *
 * This routine initialize the programming device
 */

static void init()
{
	bit08   b;
	bit08   buffer[16];

	printf("Initializing programmer...");
	fflush(stdout);

	cmd0 = '+';		// Try +M first
	send_tm0();
	send_byte(0x07);

	b = get_byte();
	if (b != 1)
	{
		cmd0 = 'T';	// If +M fails try TM
		send_tm0();
		send_byte(0x07);
		b = get_byte();
		if (b != 1)
		{
			fprintf(stderr, "Couldn't talk to board\n");
			terminate(1);
		}
	}
	send_tm0();
	send_byte(0x0e);
	get_bytes(buffer, 16);
	if ((bcmp(buffer, "TM4", 3) != 0) && (bcmp(buffer, "PM4", 3) != 0))
	{
		printf("Not a TM4 ROM\n");
		terminate(2);
	}
	send_tm0();
	send_byte(0x01);
	send_byte(0x06);
	send_byte(0x02);

	send_tm0();
	send_byte(0x01);
	send_byte(0x06);
	send_byte(0x02);

	printf("ok\n");
	printf("TM4 programmer ROM Version %d.%d%d.%d%d\n", buffer[3],
	       buffer[4], buffer[5], buffer[6], buffer[7]);
}

/*
 *****************************************************************************
 *
 *	READ_PROTOCOL (ALL DEVICES) (INCLUDES DATA EEPROM)
 *
 * 12-BIT PARTS
 *
 * For 12-bit parts, (16C5x, 12C50x) the program memory, ID locs
 * and config word are all one homogenized, linear block and the
 * entire PIC can be read in one hit.
 *
 * When reading/writing the config word is the first location
 * accessed. If the COUNT was >1, program memory 0x0000 follows
 * then 0x0001 etc. If the COUNT was > MAX program memory +1 (for
 * config word) the ID locations will follow after the last
 * program memory address.
 *
 * 14-BIT PARTS
 *
 * For 14-bit parts address 0x0000 is returned first followed by
 * the rest of the program memory contents as the COUNT allows.
 * The ID and config word are read separately. (See READING THE ID
 * AND FUSES (14-bit PARTS) below)
 * 
 * DATA EEPROM
 *
 * To read the contents of the 16C/F8x DATA EEPROM use the above
 * protocol but specify the COUNT as MAX 64 words and change the
 * "SILICON COMMAND" to 0x05.
 *
 * The DATA EEPROM contents are returned as 16-bit words with the
 * MSB all ways containing 0x00 and the EEPROM contents in the
 * LSB.
 *
 ****************************************************************************
 */

static void read_protocol(struct pic *pic, bit16 * buffer, bit16 count, int eeprom)
{
	close_device();

	send_ctrl_byte(pic->profile->ctrlread);

	open_device(pic);

	read_pic_memory(pic, buffer, count, eeprom);

	close_device();
	dump_memory(pic, buffer);	// active for debug level 2 only
}

/*
 ****************************************************************************
 *
 *	READ_PIC_MEMORY
 */

static void read_pic_memory(struct pic *pic, bit16 * buffer, bit16 count, int eeprom)
{

	send_tm1_2(pic->package);
	send_byte(0x02);			/* Read command */
	send_count(count);
	send_byte(eeprom ? 0x05 : 0x04);	/* Silicon command */
	send_byte(pic->profile->mask);
	get_data(buffer, count);
}

/*
 ****************************************************************************
 *
 *	BLANK_PROTOCOL
 */

static int blank_protocol(struct pic *pic)
{
	int     value, count, i;
	bit16   config_buffer[8];

	close_device();

	/*
	 * NFE 18th Aug '99 if 12bit core part must add one to count for
	 * config word, Dale please add this. Also note that the ID is not
	 * blank checked as yet for the 12bit parts and the ID and the config 
	 * word are not for the 14bit parts 
	 */

	send_ctrl_byte(pic->profile->ctrlread | B_FLAG);
	open_device(pic);
	send_tm1_2(pic->package);	/* sends a TM1 or TM2 */
	send_byte(0x02);		/* Read command */

	/*
	 * Start NFE 25 JULY 99 
	 */

	/*
	 * We want to test for a PIC with LastWordCal= true if found then DO
	 * NOT blank check last program word so count = count - 1 We want to
	 * know if the CAL WORD is blank so we must READ it to the NEW
	 * CalBuffer 
	 */

	count = pic->size;
	if (core12(pic))
		count++;	// 12 bit parts, add 1 for config word

	if (pic->profile->LastWordCal == TRUE)
		count--;	// One less count 

	send_count(count);	/*
				 * sends 16 bit value high byte first 
				 */
	send_byte(0x04);	/*
				 * Silicon command 
				 */
	send_byte(pic->profile->mask);
	value = get_byte();

	/*
	 * Read Calibration word into CalBuffer from last rom address 
	 */
	if (pic->profile->LastWordCal == TRUE)
	{
		send_ctrl_byte(pic->profile->ctrlread);		// set for reading
		read_pic_memory(pic, CalBuffer, 1, PGM);	// read 1 word
		CalBuffer[0] &= pic->profile->initval;		// keep only low 12 o r14 bits
	}
	/*
	 * End NFE 25 JULY 99 
	 */

	if (value != 0xff)
	{
		close_device();
		return FALSE;	// Blank check failed on program rom, return now
	}

	if (core12(pic))
	{
		/* Check ID words in 12 bit part */
		send_ctrl_byte(pic->profile->ctrlread | B_FLAG);
		send_tm1_2(pic->package);	/* sends a TM1 or TM2 */
		send_byte(0x02);		/* Read command */
		send_count(4);			/* 4 ID words */
		send_byte(0x04);		/* Silicon command */
		send_byte(pic->profile->mask);
		value = get_byte();
	}
	else
	{	// Check IDs and config word in 14 bit parts
		value = 0xff;
		for (i = 0; i < 8; i++)
			config_buffer[i] = pic->profile->initval;
		read_config_14bit(pic, config_buffer);
		for (i = 0; i < 8; i++)
		{
			config_buffer[i] &= (pic->profile->mask << 8) | 0xff;
			if (config_buffer[i] != pic->profile->initval)
				value = 0;
		}
	}

	close_device();

	return (value == 0xff);	// return TRUE if blank = 0xff
}

/*
 ****************************************************************************
 *
 *	CORE_12
 *
 * return TRUE if part has a 12 bit core 
 */

static int core12(struct pic *pic)
{
	switch (pic->package)
	{
		case P18_12B:
		case P28_12B:
		case P08_12B:
			return TRUE;

		default:
			return FALSE;
	}
}

/*
 ****************************************************************************
 *
 *	READ_PROTOCOL_12
 */

static void read_protocol_12(struct pic *pic, bit16 * buffer)
{
	int     i;

	read_protocol(pic, buffer, pic->size + 5, 0);

	/* Fix buffer:
	 *
	 * 1. Copy configuration byte to 0xfff, its at 0x000 
	 * 2. Move buffer down by 1 byte. 
	 */

	buffer[0xfff] = buffer[0];
	for (i = 0; i < pic->size + 5; i++)
		buffer[i] = buffer[i + 1];
}

/*
 ****************************************************************************
 *
 *	READ_CONFIG_14B
 *
 * READING THE ID AND CONFIG WORD (14-bit PARTS)
 *
 * To get the ID and Fuse locations from a 14-bit part, we perform
 * a completely new read operation.
 *
 * When reading from the config area, the first 4 locations are
 * ID0 to ID3 respectively. The next 3 locations are "reserved."
 * The 8th location is the CONFIG WORD.
 *
 * This now reads all 4 ID locations, skips the reserved space
 * then reads the config word in one call.  DAH - 2 Sep 1999
 */

static void read_config_14bit(struct pic *pic, bit16 * buffer)
{

	close_device();
	send_ctrl_byte(pic->profile->ctrlread);
	open_device(pic);
	enter_config_area();
	read_pic_memory(pic, buffer, 4, PGM);		// Read ID locations
	skip_locations(pic, 3);
	read_pic_memory(pic, buffer + 7, 2, PGM);	// Read config word
	close_device();
}

/*
 ****************************************************************************
 *
 *	READ_DATA_EEPROM
 */

static void read_data_eeprom(struct pic *pic, bit16 * buffer)
{
	int     i;

	close_device();
	send_ctrl_byte(pic->profile->ctrlread);
	open_device(pic);
	read_pic_memory(pic, buffer, pic->profile->dataEEpromSize, EEPROM);
	close_device();
	for (i = 0; i < pic->profile->dataEEpromSize; i++)
		buffer[i] &= 0xff;
}

/*
 *****************************************************************************
 *
 *	WRITE_PROTOCOL
 *
 * WRITING TO THE 12-BIT PARTS
 *
 * For 12-bit parts, (16C5x, 12C50x) the program memory, ID locs
 * and config word are all one homogenized, linear block and the
 * entire PIC can be written in one hit...
 *
 * ...NFE 18th Aug '99 unless the device has a LastWordCal in which
 * case we must write: Config (dummy), (program words -1) Skip
 * one location, write ID locs.
 *
 *
 * When reading/writing the config word is the first location
 * accessed.
 *
 * DO NOT WRITE WITH CODE PROTECT BITS = 0 On first pass. YOU MUST
 * "OR" the CODE PROTECT BITS OFF or send a Dummy.
 *
 * After the 12-bit device is programmed, and verified, you then
 * reprogram the fuse word using the special purpose
 * "write_fuse_12bit()" function.
 *
 * YOU MUST REPROGRAM THE FUSE WORD REGARDLESS OF WHETHER CODE
 * PROTECTION IS DESIRED OR NOT.
 * (With Ctrlbyte bit 3 set)
 *
 * If the COUNT was >1, program memory 0x0000 follows then 0x0001
 * etc. If the COUNT was > MAX program memory +1 (for config word)
 * the ID locations will follow after the last program memory
 * address.
 *
 * WRITING TO 14-BIT PARTS
 *
 * For 14-bit parts address 0x0000 is written first followed by
 * the rest of the program memory contents as the COUNT allows.
 * The ID and config word are written separately. (See WRITING THE
 * ID AND FUSES (14-bit PARTS) below)
 *
 * DO NOT write to the config word with any of the code protect
 * bits as '0' until you have VERIFIED the device first.
 *
 * WRITING THE DATA EEPROM
 *
 * To WRITE the contents of the 16C84/16F8x DATA EEPROM use the
 * normal EEPROM/FLASH write protocol (CTRLBYTE = 0x03) but
 * specify the COUNT as number of data EEPROM words and change the
 * "SILICON COMMAND" to 0x03.
 *
 * The DATA EEPROM contents are WRITTEN as 16-bit words with the
 * MSB always containing 0x00 and the EEPROM contents in the LSB.
 */

static void write_protocol(struct pic *pic, bit16 * buffer, bit16 count, int eeprom)
{

	close_device();
	send_ctrl_byte(pic->profile->ctrlwrite);

	// Added functions to set retry count (25 for 14-bit core)
	// and over program pulse count (3).

	send_tm0();
	send_byte(0x01);
	send_byte(0x00);
	send_byte(pic->profile->RetryCount);
	send_tm0();
	send_byte(0x01);
	send_byte(0x01);
	send_byte(pic->profile->OverPgm);

	if (debugging)
		fprintf(stderr, "RetryCount= %d  OverPgm= %d\n", pic->profile->RetryCount, pic->profile->OverPgm);

	open_device(pic);
	send_tm1_2(pic->package);
	send_byte(0x03);	/* Write command */

	/*
	 * NFE 18th Aug 99 
	 *
	 * Dale please add code to test if the Calbuffer is blank Blank =
	 * 0fff for 12bit core, 3fff for 14 bit core. DAH 30 Aug 99  - DONE 
	 */

	if (count > pic->size)
		count = pic->size;

	if (    (pic->profile->LastWordCal)	// If there's a calword
	     && (CalBuffer[0] != pic->profile->initval)	// AND it's not blank
	     && (count == pic->size)		// AND we would overwrite
	     && (eeprom == FALSE)		// AND not writing to eeprom then...
	   )
		count--;	/* less one for non-blank calword */
				// don't overwrite it*/
	send_count(count);
	send_byte(eeprom ? 0x03 : 0x02);
	send_byte(pic->profile->mask);
	printf(" %d words\n", count);
	showProgess = TRUE;
	send_data(buffer, count);
	/*
	 * 18th Aug 99 Dale fix count value above for lastwordcal not blank
	 * etc DAH 30 Aug 99 - DONE 
	 */
	showProgess = FALSE;
	get_byte();
	close_device();
}

/*
 ****************************************************************************
 *
 *	WRITE_PROTOCOL_12
 */

static void write_protocol_12(struct pic *pic, bit16 * buffer, int count)
{
	int     size;

	close_device();

	send_ctrl_byte(pic->profile->ctrlwrite);

	// Added functions to set retry count (8 for 12-bit core)
	// and over program pulse count (11).
	if (debugging)
		fprintf(stderr, "RetryCount= %d  OverPgm= %d\n", pic->profile->RetryCount, pic->profile->OverPgm);

	send_tm0();
	send_byte(0x01);
	send_byte(0x00);
	send_byte(pic->profile->RetryCount);
	send_tm0();
	send_byte(0x01);
	send_byte(0x01);
	send_byte(pic->profile->OverPgm);
	open_device(pic);
	send_tm1_2(pic->package);
	send_byte(0x03);	/* Write command */

	/*
	 * NFE 18th Aug 99 
	 */
	/*
	 * Dale please add code to test if the Calbuffer is blank Blank =
	 * 0fff for 12bit core, 3fff for 14 bit core. DAH 30 Aug 99 - Done 
	 */

	if (count > pic->size)
		count = pic->size;

	if (    (pic->profile->LastWordCal)	// If there's a calword
	     && (CalBuffer[0] != pic->profile->initval)	// AND it's not blank
	     && (count == pic->size)
	   )
	{
		// AND we would overwrite
		count--;	/* less one for non-blank calword. Don't overwrite it */
	}

	printf("%d words\n", count);	// Tell user how many words we're writing
	size = count + 1;	// Add 1 for fake config word
	send_count(size);
	send_byte(0x02);
	send_byte(pic->profile->mask);
	send_word(0xfff);	/* Fake safe fuse value */
	showProgess = TRUE;

	/* Then comes the data and id */

	send_data(buffer, size);
	showProgess = FALSE;
	get_byte();

	/*
	 * NFE 18th Aug '99, if we didn't program the calword because it
	 * wasn't blank, we now must skip over it and then program the ID
	 * locs. DAH - new code skips over all unprogrammed locations to
	 * reach the ID locations. 
	 */

	if ((pic->size - count) > 0)
	{
		// Skip unwritten locations if any exist

		send_tm0();
		send_byte(0x0a);	/* load firmware internal count value */
		send_count(pic->size - count);	/* skip unprogrammed locations */
		send_tm1_2(pic->package);
		send_byte(0x05);	/* increment command x count as set above */
	}

	/*
	 * now re enter programming cycle, no need to re open device as we never closed it 
	 */

	send_tm1_2(pic->package);
	send_byte(0x03);	/* Write command */
	send_count(4);		/* 4 ID locs */
	send_byte(0x02);
	send_byte(pic->profile->mask);

	/*
	 * Dale work out the offset into the buffer for the ID locs,
	 * pic->size right? DAH - Yes.  
	 */

	send_data(buffer + pic->size, 4);	// 4 bytes starting at configuration memory
	get_byte();
	close_device();

	/* program cycle for the 12bit core parts done.  */
}

/*
 *****************************************************************************
 *
 *	WRITE_FUSE_12BIT
 *
 * WRITING THE FUSE (12-bit PARTS)
 *
 * This is a special purpose function for writing the fuse of the
 * 12-bit parts. It is used AFTER the device has been fully
 * programmed and verified.
 *
 * Not for 16Cxx parts
 */

static void write_fuse_12bit(struct pic *pic, bit16 fuse)
{

	close_device();
	send_ctrl_byte(0x08);
	open_device(pic);
	send_tm1_2(pic->package);
	send_byte(0x03);	/* Enter "FIRMWARE" WRITE MODE */
	send_count(1);
	send_byte(0x02);	/* Silicon write command */
	send_byte(pic->profile->mask);
	send_word(fuse);
	get_byte();
	close_device();
}

/*
 *****************************************************************************
 *
 *	WRITE_ID_ISP
 *
 * WRITING THE ID (14-bit PARTS)
 *
 * To write the ID locations from a 14-bit part, we
 * perform a completely new write operation.
 *
 * When WRITING TO the config area, the first 4 locations are ID0
 * to ID3 respectively. The next 3 locations are "reserved" (DON'T WRITE!) 
 * The 8th location is the CONFIG WORD and this is programmed elsewhere.
 */

static void write_ID_isp(struct pic *pic, bit16 * buffer)
{

	close_device();
	send_ctrl_byte(pic->profile->ctrlwrite);	/* Send control byte */
	open_device(pic);
	enter_config_area();
	send_tm2();		/* ISP write program */
	send_byte(0x03);
	send_count(4);
	send_byte(0x02);	/* Silicon command */
	send_byte(pic->profile->mask);
	send_data(buffer, 4);
	get_byte();
	close_device();
}

/*
 ****************************************************************************
 *
 *	WRITE_CONFIG_ISP
 *
 * WRITING THE CONFIG WORD (14-bit PARTS)
 *
 * To write the ID and Fuse locations from a 14-bit part, we
 * perform a completely new write operation.
 *
 * When WRITING TO the config area, the first 4 locations are ID0
 * to ID3 respectively. The next 3 locations are "reserved" (Do
 * NOT write anything!) The 8th location is the CONFIG WORD.
 */

static void write_config_isp(struct pic *pic, bit16 * buffer)
{

	bit08   mpp;
	if (pic->profile->flash)
		mpp = 0;
	else
		mpp = 8;

	close_device();

	/* Send control byte, set bit 3 if not flash */

	send_ctrl_byte(pic->profile->ctrlwrite | mpp);

	open_device(pic);
	enter_config_area();
	skip_7();	/* This increments the program counter to the config word */
	send_tm2();	/* ISP write program */
	send_byte(0x03);
	send_count(1);
	send_byte(0x02);	/* Silicon command */
	send_byte(pic->profile->mask);
	send_data(buffer, 1);
	get_byte();
	close_device();
}

/*
 ****************************************************************************
 *
 *	SET_COUNT
 *
 * Free standing function to set the firmware's 16-bit COUNT value
 * to a desired count.
 */

static void set_count(bit16 count)
{

	send_tm0();
	send_byte(0x0a);
	send_count(count);
}

/*
 *****************************************************************************
 *
 *	SKIP LOCATIONS
 *
 * This function is required to bypass memory locations that we do
 * not want to write to (eg, CALWORD on the 12Cxxx OTP parts.)
 * BEFORE using the in-build skip routines in the TM4 firmware, we
 * must supply a count value using the SETCOUNT() function.
 */

static void skip_locations(struct pic *pic, bit16 count)
{

	set_count(count);
	send_tm1_2(pic->package);
	send_byte(0x05);
}

/*
 **************************************************************************** 
 *
 *	SKIP_7
 *
 * This function is a convenient way to skip 7 locations without
 * having to supply a count value. This function is most useful to
 * bypass the ID and three reserved locations when we wish to only
 * READ or WRITE the config word of a 14-bit part.
 */
 
static void skip_7()
{

	send_tm2();
	send_byte(0x0b);
}

/*
 **************************************************************************** 
 *
 *	KILL_COPY_PROTECT
 *
 * This function is required to break the code protection on a 16C84/16F8x
 * part before programming.
 */

static void kill_copy_protect()
{

	send_tm2();
	send_byte(0x06);
}

/*
 **************************************************************************** 
 *
 *	WRITE_WORD
 *
 * This function sends a 14-bit or 16-bit word to the ISP device
 * depending on the state of bit-7 of the CTRLBYTE. The device
 * must already be opened via the OPEN_DEVICE() function.
 */

#if 0
static void write_word(bit16 word)
{

	send_tm2();
	send_byte(0x0e);
	send_word(word);
}
#endif

/*
 **************************************************************************** 
 *
 *	DELAY
 *
 * This function causes the firmware to delay one program pulse
 * time depending on the state of bit-0 of the CTRLBYTE. In
 * conjunction with other freestanding functions it allow the
 * firmware a way to perform write operations that were unforeseen
 * at the time the firmware was coded.
 */

#if 0
static void delay()
{

	send_tm2();
	send_byte(0x0d);
}
#endif

/*
 **************************************************************************** 
 *
 *	OPEN_DEVICE
 *
 * Once this function is performed, any sequence of READ, WRITE
 * etc. operations can be done on the target PIC until a
 * CLOSEDEVICE() function is performed. This function causes the
 * firmware to apply programming voltages and place the target PIC
 * into its programming mode.
 *
 * OPENDEVICE has a one byte parameter, the "PACKAGE" parameter.
 * This value specifies the number of pins and layout of the
 * target PIC. It is required for compatibility with programmers
 * that employ multiplexed sockets. (Eg. PICSTART)
 *
 * The value of this parameter is taken from the table below.
 *
 * 0x00 18-pin 12-bit core (16C54,56,57) 
 * 0x01 18-pin 14-bit core (16C61,62x,71,710,711,715,F83,F/C84) 
 * 0x02 28-pin 12-bit core (16C55,57) 
 * 0x03 28-pin sdip (16C62,63,66,64x,72,73,76)
 * 0x04 40-pin 14-bit core (16C64,65,67,66x,74,77, 
 * 0x05 14000 only
 * 0x06 8-pin 12-bit core (12c508,9) 
 * 0x07 8-pin 14-bit core (12C671,2) 
 *
 * Please ensure you send the correct value as the open nature of
 * the protocol and cross compatibility all depend on it.
 */

static void open_device(struct pic *pic)
{

	send_tm1_2(pic->package);
	send_byte(0x00);
	send_byte(pic->package);
}

/*
 **************************************************************************** 
 *
 *	CLOSE_DEVICE
 *
 * To remove the target PIC from the programming mode, remove
 * voltages Etc:
 */

static void close_device()
{

	send_tm0();
	send_byte(0x08);
}

/*
 **************************************************************************** 
 *
 *	SEND_COUNT
 *
 * The COUNT value is a 16-bit value but it is split into two
 * 8-bit bytes, COUNTHIGH and COUNTLOW for sending.
 * No adjusting of the COUNT is required before sending.
 */

static void send_count(bit16 count)
{

	send_byte(count >> 8);
	send_byte(count & 0xff);
}

/*
 **************************************************************************** 
 *
 * basic functions
 *
 * Below are the protocols for the basic functions used above. The
 * get_byte and send_byte functions are not included as these are
 * the lowest level byte i/o functions and are required to be
 * written to suit the HOST computer.
 */

/*
 *****************************************************************************
 *
 *	SEND_DATA
 */

static void send_data(bit16 * buffer, bit16 count)
{
	bit16   pgmcount = 1;

	while (count--)
	{
		send_word(*buffer++);
		if (showProgess)
		{
			printf("\r0x%x  ", pgmcount++);
			fflush(stdout);
		}
	}
	if (showProgess)
		printf("\n");
}

/*
 *****************************************************************************
 *
 *	SEND_WORD
 */

static void send_word(bit16 value)
{

	send_byte(value >> 8);
	send_byte(value & 0xff);
}

/*
 *****************************************************************************
 *
 *	SEND_CTRL_BYTE
 */

static void send_ctrl_byte(bit08 ctrlbyte)
{

	send_tm0();
	send_byte(0x06);	/* 0x06 means write next byte to "CtrlByte" */
	send_byte(ctrlbyte);
}

/*
 *****************************************************************************
 *
 *	ENTER_CONFIG_AREA
 */

static void enter_config_area()
{

	send_tm2();
	send_byte(0x09);	/* ENTER CONFIG AREA "FIRMWARE" COMMAND */
}

/*
 *****************************************************************************
 *
 *	SEND_TM0
 *
 * All 'TM' strings can be either "+M" or "TM" depending on the presence of a 
 * "-T" on the command line.  If a "-T" is provided then "TM" will be
 * emitted. "+M" is the default. 
 */

static void send_tm0()
{

	send_byte(cmd0);	// cmd0 is either '+' or 'T'   
	send_byte('M');
	send_byte('0');
}

/*
 *****************************************************************************
 *
 *	SEND_TM1
 */

static void send_tm1()
{

	send_byte(cmd0);
	send_byte('M');
	send_byte('1');
}

/*
 *****************************************************************************
 *
 *	SEMD_TM2
 */

static void send_tm2()
{

	send_byte(cmd0);
	send_byte('M');
	send_byte('2');
}

/*
 *****************************************************************************
 *
 *	SEND_TM1_2
 */

static void send_tm1_2(bit08 package)
{

	if ((package == P18_12B) || (package == P28_12B))
		send_tm1();
	else
		send_tm2();
}

/*
 *****************************************************************************
 *
 *	OPEN_TTY
 */

static int open_tty(char *name)
{
	struct termios termios;

	tty = open(name, O_RDWR, 0777);
	if (tty == -1)
	{
		perror("Open of programmer port failed");
		printf("Please must create a symbolic link \n"
		       "between /dev/pic and the desired serial \n"
		       "port (eg: ln -s /dev/ttyS0 /dev/pic ) or\n"
		       "supply the port name on the command line\n"
		       "with the -p option (eg:  -p /dev/ttyS0 ).\n");
		return (-1);
	}

	if (tcgetattr(tty, &termios) == -1)
	{
		perror("getting attributes of programmer port failed");
		exit(1);
	}
	termios.c_iflag = IGNBRK | IGNPAR;
	termios.c_oflag = 0;
	termios.c_cflag = BAUDRATE | CS8 | CREAD | CLOCAL | CRTSCTS;
	termios.c_lflag = 0;
	termios.c_cc[VMIN] = 0;		/* Timed read */
	termios.c_cc[VTIME] = 20;	/* Two second timeout (was 200 DAH - 3 Sept 1999) */
	if (tcsetattr(tty, TCSANOW, &termios) == -1)
	{
		perror("Setting attributes of programmer port failed");
		return (-1);
	}
	return (0);
}

/*
 *****************************************************************************
 *
 *	SEND_BYTE
 */

static void send_byte(bit08 byte)
{
	bit08   bbuf[1];

	/* Wait for ok to send (CTS = TRUE) */

	 //This not needed since "CRTSCTS" is set in c_cflag termios struct 
	 // while ((modem_bits & TIOCM_CTS) == 0)
         // {
	 //	if (ioctl(tty, TIOCMGET, * &modem_bits) == -1)
	 //	{
	 //		perror("ioctl TIOCMGET");
	 //		exit(1);
	 //	}
	 // } 

	bbuf[0] = byte;
	write(tty, bbuf, 1);
	log_write(byte);
	ioctl(tty, TCSBRK, 1);	/* Wait until the characters have been output */
	if(no_delay == FALSE)
		usleep(WAIT);	/* wait a fixed time - needs this for some reason, for some parts only */
}

/*
 *****************************************************************************
 *
 *	GET_BYTE
 */

static bit08 get_byte()
{

	bit08   buf[1];
	timed_read(buf, 1);
	return buf[0];
}

/*
 *****************************************************************************
 *
 *	GET_BYTES
 */

static void get_bytes(bit08 * buffer, bit16 length)
{

	timed_read(buffer, length);
}

/*
 *****************************************************************************
 *
 *	GET_DATA
 */

static void get_data(bit16 * buffer, bit16 length)
{
	unsigned int i;

	timed_read((bit08 *) buffer, length * 2);
	for (i = 0; i < length; i++)
	{
		buffer[i] = ntohs(buffer[i]);	// Exchange hi and lo bytes for Intel cpu byte order
	}
}

/*
 *****************************************************************************
 *
 *	TIMED_READ
 */

static void timed_read(bit08 * buffer, bit16 length)
{
	int     total = length;
	int     count;
	int     i;

	while (length)
	{
		count = read(tty, (char *) buffer, length);
		if (count <= 0)
		{
			fprintf(stderr, "read failed: wanted %d got %d error %d\n", total, total - length, count);
			terminate(3);
		}
		for (i = 0; i < count; i++)
		{

			log_read(buffer[i]);
		}
		length -= count;
		buffer += count;
	}
}

/*
 *****************************************************************************
 * Some logging stuff to try and figure out protocol problems 
 */

bit08   log_buffer[10000];
bit08   log_type[10000];
bit16   log_count = 0;

/*
 *****************************************************************************
 *
 *	LOG_READ
 */

static void log_read(bit08 c)
{

	if (log_count == 10000)
		return;
	log_type[log_count] = 0;
	log_buffer[log_count++] = c;
}

/*
 *****************************************************************************
 *
 *	LOG_WRITE
 */

static void log_write(bit08 c)
{

	if (log_count == 10000)
		return;
	log_type[log_count] = 1;
	log_buffer[log_count++] = c;
}

/*
 *****************************************************************************
 *
 *	LOG_CLEAR
 */

static void log_clear()
{

	log_count = 0;
}

/*
 *****************************************************************************
 *
 *	LOG_DUMP
 */

static void log_dump()
{
	int     offset = 0;
	int     type = -1;
	int     i;
	int     last = 0;

	if (debugging < 1)
		return;

	fprintf(stderr, "%d characters logged\n\n", log_count);
	for (i = 0; i < log_count; i++)
	{
		if (type != log_type[i])
		{
			fprintf(stderr, "\nLast read/write was %d bytes\n", last);
			last = 0;
			offset = 0;
			type = log_type[i];
			if (log_type[i])
				fprintf(stderr, "WRITE: ");
			else
				fprintf(stderr, "READ: ");
		}
		if (offset == 16)
		{
			fprintf(stderr, "\n ");
			offset = 0;
		}
		fprintf(stderr, "%02x ", log_buffer[i]);
		offset++;
		last++;
	}
	fprintf(stderr, "\nLast read/write was %d bytes\n", last);
	log_clear();
}

/*
 *****************************************************************************
 *
 *	TERMINATE
 *
 *	An error has occured, quit.
 */

static void terminate(int error)
{

	// log_dump();
	close_device();
	exit(error);
}

/*
 *****************************************************************************
 *
 *	READ_PIC_DATA
 *
 * read data from PIC chip and display as hex dump on stdout 
 */

static void read_pic_data(struct pic *pic)
{
	int     i, s;

	if (core12(pic))
	{
		read_protocol_12(pic, cbuf);
	}
	else
	{
		read_protocol(pic, cbuf, pic->size, 0);
		read_config_14bit(pic, &cbuf[0x2000]);
		if (pic->profile->dataEEpromSize > 0)
		{
			read_data_eeprom(pic, &cbuf[0x2100]);
		}
	}
	fix_memory(pic, cbuf);
	dump_memory(pic, cbuf);	// active for debug level 2 only
	for (i = 0; i < pic->size; i++)
	{
		if ((i % 8) == 0)
		{
			printf("\n%04X:", i);
		}
		printf(" %04X", cbuf[i]);
	}

	if (!core12(pic))
	{
		printf("\n\nConfiguration area\n2000:");
		for (i = 0x2000; i < 0x2008; i++)
			printf(" %04X", cbuf[i]);

		if (pic->profile->dataEEpromSize > 0)
		{
			s = 0x2100 + pic->profile->dataEEpromSize;
			printf("\n\nData EEPROM area");
			for (i = 0x2100; i < s; i++)
			{
				if ((i % 8) == 0)
				{
					printf("\n%04X:", i);
				}
				printf(" %04X", cbuf[i]);
			}
		}
	}
	else
	{
		/* Display config area for 12 bit parts */

		printf("\n\nID words\n%04X: ", pic->size);
		for (i = pic->size; i < pic->size + 4; i++)
			printf(" %04X", cbuf[i]);
		printf("\n\nConfiguration word\n0FFF: %04X\n", cbuf[0xfff]);
	}

	printf("\n");
}

/*
 *****************************************************************************
 *
 *	INTEL_HEX_LINE_OUT
 *
 *	Print a line of data in Intel HEX format, appending checksum, someone
 *	else has already printed the address.
 */

static void intel_hex_line_out(unsigned char *buf, int addr, int count, FILE * f)
{
	int     i;
	unsigned char addr_high = addr >> 8;
	unsigned char addr_low = addr & 0xff;
	unsigned char checksum = count;

	checksum += addr_high + addr_low;

	fprintf(f, ":%02X%02X%02X00", count, addr_high, addr_low);
	for (i = 0; i < count; i++)
	{
		checksum += buf[i];
		fprintf(f, "%02X", buf[i]);
	}

	checksum = (~checksum + 1) & 0xff;
	fprintf(f, "%02X\n", checksum);
}

/*
 *****************************************************************************
 *
 *	READ_PIC_INTEL_HEX
 *
 * Read data from pic chip, convert to Intel hex format and write to
 * "filename".  This procedure reads all of memory but skips unused blank
 * sections.  If "filename" is NULL data is written to stdout. 
 */

static void read_pic_intel_hex(struct pic *pic, char *filename)
{
	FILE   *f;
	unsigned char buf[16];
	int     addr;
	int     count;
	long    mptr;
	int     blank = pic->profile->initval;

	if (filename)
	{
		printf("Opening output file %s\n", filename);

		f = fopen(filename, "w");
		if (f == NULL)
		{
			fprintf(stderr, "Failed...cannot create file %s\n", filename);
			exit(1);
		}
	}
	else
		f = stdout;	// write to stdout if NULL filename

	if (core12(pic))
	{
		read_protocol_12(pic, mem);
	}
	else
	{
		read_protocol(pic, mem, pic->size, 0);
		read_config_14bit(pic, &mem[0x2000]);
		if (pic->profile->dataEEpromSize > 0)
			read_data_eeprom(pic, &mem[0x2100]);
	}

	fix_memory(pic, mem);
	dump_memory(pic, mem);	// active for debug level 2 only
	mptr = 0;
	while (mptr < MEMSIZE)
	{
		count = 0;
		addr = mptr * 2;
		while ((count < 16) && (mem[mptr] != blank) &&
		       (mptr < MEMSIZE))
		{
			buf[count++] = mem[mptr] & 0xff;
			buf[count++] = mem[mptr] >> 8;
			mptr++;
		}

		if (count > 0)
			intel_hex_line_out(buf, addr, count, f);
		if (mem[mptr] == blank)
			mptr++;
	}

	fprintf(f, ":00000001FF\n");	// Final EOF record
	fclose(f);
}

/*
 *****************************************************************************
 *
 *	BLANK_CHECK
 */

#ifdef OLDBLANKCHECK
static void check_blank(struct pic *pic)
{
	bit16   verifymask = (pic->profile->mask << 8) | 0xff;
	int     i;

	printf("Verifying PIC chip is blank...");
	fflush(stdout);
	read_protocol(pic, cbuf, pic->size, 0);
	fix_memory(pic, cbuf);
	for (i = 0; i < pic->size; i++)
	{
		if (cbuf[i] != verifymask)
		{
			printf("location %04x not blank: %04x\n", i, cbuf[i]);
			dump_memory(pic, cbuf);
			log_dump();
			exit(1);
		}
	}
	printf("done\n");
}
#else
static void check_blank(struct pic *pic)
{

	/*
	 * NFE 18th Aug '99 if 12bit core part must add one to count for
	 * config word, Dale please add this.I suggest removing the size
	 * parameter from the proceedure call and instead have the
	 * blank_protocol calculate the size.  
	 */

	if (pic->profile->flash)
	{	// Flash parts skip blank check
		close_device();
		open_device(pic);
		send_ctrl_byte(pic->profile->ctrlwrite);
		open_device(pic);
		send_tm1_2(pic->package);
		kill_copy_protect();
		close_device();
	}
	else
	{
		printf("Verifying PIC chip is blank...");
		fflush(stdout);

		if (!blank_protocol(pic))
		{	// Blank check only eprom parts
			printf("eprom not blank\n");
			log_dump();
			terminate(1);
		}
		printf("done\n");
	}
}
#endif

/*
 *****************************************************************************
 *
 *	HEX
 *	Convert one ASCII-HEX character to it's hex value
 */

static int hex(char c)
{
	int	rv = 0;

	if ((c >= '0') && (c <= '9'))
		rv = c - '0';
	else if ((c >= 'a') && (c <= 'f'))
		rv = c - 'a' + 10;
	else if ((c >= 'A') && (c <= 'F'))
		rv = c - 'A' + 10;
	else
	{
		printf("bad hex digit: '%c'\n", c);
		terminate(1);
	}

	return rv;
}

/*
 *****************************************************************************
 *
 *	PARSE_LINE
 *
 *	Extract data from one input HEX file line
 */

static int parse_line(struct pic *pic, char *buffer, bit16 * mem, int rom_size, int *max_mem)
{
	char   *c = buffer;
	unsigned int address, word_addr;
	unsigned int length, type;
	unsigned int checksum = 0;
	int     count = 0;

	if (*c++ != ':')
		return -2;	/* Skip colon */

	length = (hex(*c++) << 4) + hex(*c++);
	checksum = length;
	address = (hex(*c++) << 12) + (hex(*c++) << 8) + (hex(*c++) << 4) + hex(*c++);
	checksum += address & 255;
	checksum += address >> 8;
	if (length == 0)
	{
		return 0;	/* EOF record */
	}

	type = (hex(*c++) << 4) + hex(*c++);
	checksum += type;

	while (length--)
	{
		word_addr = address / 2;
		if ((address & 0x01) == 1)
		{
			// Odd bytes

			mem[word_addr] &= ~0xff00;
			mem[word_addr] |= (hex(*c++) << 12) + (hex(*c++) << 8);
			checksum += mem[word_addr] >> 8;
		}
		else
		{
			// Even bytes

			mem[word_addr] &= ~0x00ff;
			mem[word_addr] |= (hex(*c++) << 4) + hex(*c++);
			checksum += mem[word_addr] & 255;
		}

		/* keep a record of the highest memory address used */

		if (    (word_addr > *max_mem)
		     && (word_addr < rom_size)
		   )
			*max_mem = word_addr;

		address++;
		count++;
	}

	checksum += (hex(*c++) << 4) + hex(*c++);
	if ((checksum & 255) == 0)
		return count;
	else
		return -1;
}

/*
 *****************************************************************************
 *
 *	READ_OBJECT
 *
 *	Read an input HEX file
 */

static int read_object(struct pic *pic, char *filename, bit16 * memory, int rom_size)
{
	FILE   *f;
	char    line[80];
	int     lines_read = 0;
	int     count = 0;
	int     bytes;
	static int max_mem;

	max_mem = 0;
	if (filename)
		f = fopen(filename, "r");
	else
		f = stdin;

	if (filename == NULL)
		filename = "stdin";
	printf("Reading object file %s ...", filename);
	fflush(stdout);

	if (f == NULL)
	{
		printf("Failed...cannot open object file\n");
		exit(1);
	}

	while (fgets(line, sizeof(line) - 1, f))
	{
		lines_read++;
		bytes = parse_line(pic, line, memory, rom_size, &max_mem);
		if (bytes == -1)
		{
			printf ("\nChecksum error in object file on line %d.\n%s\n", lines_read, line);
			exit(1);
		}
		if (bytes > 0)
			count += bytes;
		if (bytes == -2)
			printf("\n%s\n", line);
	}

	fclose(f);
	printf("Done\n");
	return max_mem;	// return highest memory address used
}

/*
 *****************************************************************************
 *
 *	INIT_MEMORY
 *
 *	Initialize all memory and dataspace.
 */

static void init_memory(struct pic *pic, bit16 * mem)
{
	int     i;

	for (i = 0; i < MEMSIZE; i++)
		mem[i] = pic->profile->initval;

	// Set data EEPROM area to zeros

	for (i = 0; i < pic->profile->dataEEpromSize; i++)
	{
		mem[i + 0x2100] = 0;
	}
}

/*
 *****************************************************************************
 *
 *	FIX_MEMORY
 *
 *	Set memory to initial value, dependent upon the specific PIC
 */

static void fix_memory(struct pic *pic, bit16 * mem)
{
	int     i;

	for (i = 0; i < MEMSIZE; i++)
		mem[i] &= pic->profile->initval;
}

/*
 *****************************************************************************
 *
 *	DUMP_MEMORY
 */

static void dump_memory(struct pic *pic, bit16 * mem)
{
	int     i;
	int     j;

	if (debugging < 2)
		return;

	for (i = 0; i < MEMSIZE; i = i + 16)
	{
		for (j = i; j < i + 16; j++)
		{
			if (mem[j] != pic->profile->initval)
			{
				fprintf(stderr, "%04x: ", i);
				for (j = i; j < i + 16; j++)
				{
					fprintf(stderr, "%04x ", mem[j]);
				}
				fprintf(stderr, "\n");
				break;
			}
		}
	}
}

/*
 *****************************************************************************
 *
 *	FIND_PIC
 */

static struct pic *find_pic(char *name)
{
	struct pic *pic;

	for (pic = pics; pic->name[0]; pic++)
		if (strcasecmp(pic->name, name) == 0)	// compare strings, ignore case
			return pic;
	return NULL;
}

/*
 *****************************************************************************
 *
 *	PRINT_PIC_LIST
 */

static void print_pic_list()
{
	int     i = 0;
	struct pic *pic;

	for (pic = pics; pic->name[0]; pic++, i++)
	{
		if ((i % 4) == 0)
			printf("\n");
		printf("%s\t\t", pic->name);

	}
	printf("\n");
}

/*
 *****************************************************************************
 *
 *	WRITE_PIC
 */

static  void write_pic(struct pic *pic, bit16 * buffer, int nwords)
{

	printf("Program ROM: Writing ");
	fflush(stdout);

	if (nwords > pic->size)
		nwords = pic->size;

	switch (pic->package)
	{
		case P18_12B:
		case P28_12B:
		case P08_12B:
			write_protocol_12(pic, buffer, nwords);
			break;
		default:
			write_protocol(pic, buffer, nwords, PGM);
			break;
	}
	printf("Done\n");
}

/*
 *****************************************************************************
 *
 *	WRITE_DATA_EEPROM
 */

static void write_data_eeprom(struct pic *pic, bit16 * buffer)
{
	int     count = pic->profile->dataEEpromSize;

	printf("Data EEPROM: Writing ");
	write_protocol(pic, &buffer[0x2100], count, EEPROM);
	printf("Done\n");
}

/*
 *****************************************************************************
 *
 *	VERIFY_DATA_EEPROM
 */

static void verify_data_eeprom(struct pic *pic, bit16 * buffer, bit16 * cmpbuf)
{
	int     i, count, error;
	int     offset = 0x2100;

	count = pic->profile->dataEEpromSize;
	error = FALSE;

	printf("Verifying data EEPROM...");
	read_protocol(pic, cmpbuf + 0x2100, count, EEPROM);
	for (i = 0; i < count; i++)
		cmpbuf[offset + i] &= 0xff;

	for (i = 0; i < count; i++)
	{
		if (cmpbuf[i + offset] != buffer[i + offset])
		{
			printf("%d: %04x should be %04x\n", i, cmpbuf[i + offset], buffer[i + offset]);
			error = TRUE;
		}
	}

	if (error)
		terminate(1);

	printf("Done\n");
}

/*
 *****************************************************************************
 *
 *	VERIFY_PIC
 */

static void verify_pic(struct pic *pic, bit16 * buffer, bit16 * cmpbuf, int words)
{
	int     error = 0;
	int     count;
	int     i;

	printf("Verifying program ROM...");
	fflush(stdout);
	if (core12(pic))
	{
		// 12 bit part

		read_protocol_12(pic, cmpbuf);
		count = pic->size + 4;	/* We don't verify fuse here */
	}
	else
	{
		// 14 bit part

		read_protocol(pic, cmpbuf, pic->size, 0);
		// count = pic->size;
		count = words;
	}

	fix_memory(pic, cmpbuf);
	error = 0;
	for (i = 0; i < count; i++)
	{
		if (cmpbuf[i] != buffer[i])
		{
			/*
			 * NFE 18th Aug '99 ignore the calword if lastwordcal 
			 * and location was blank in pic and pointer is at
			 * calword in buffer 
			 */

			if (!(    (pic->profile->LastWordCal)
			       && (i == (pic->size - 1))
			       && (CalBuffer[0] != pic->profile->initval)
			     )
			   )
			{
				printf("\n%04X: %04x should be %04x", i, cmpbuf[i], buffer[i]);
				error++;
				if (error >= 20)
				{
					printf ("\nStopping after %d errors..\n", error);
					terminate(1);
				}
			}
		}
	}

	if (error)
	{
		printf("\n");
		terminate(1);
	}
	printf("Done\n");
}

/*
 *****************************************************************************
 *
 *	WRITE_CONFIG
 */

static void write_config(struct pic *pic, bit16 * buffer)
{

	printf("Writing configuration bits ");
	fflush(stdout);

	switch (pic->package)
	{
		case P18_12B:
		case P28_12B:
		case P08_12B:
			printf("%03X ...", buffer[0xfff]);
			write_fuse_12bit(pic, buffer[0xfff]);
			break;
		default:
			printf("%04X ...", buffer[0x2007]);
			write_ID_isp(pic, buffer + 0x2000);
			write_config_isp(pic, buffer + 0x2007);
			break;
	}

	printf("Done\n");
}

/*
 *****************************************************************************
 *
 *	VERIFY_CONFIG
 */

static void verify_config(struct pic *pic, bit16 * buffer, bit16 * cmpbuf)
{
	int     i;
	int     error = 0;

	printf("Verifying configuration bits...");
	fflush(stdout);
	switch (pic->package)
	{
		case P18_12B:
		case P28_12B:
		case P08_12B:
			read_protocol(pic, cmpbuf + 0xfff, 1, 0);
			fix_memory(pic, cmpbuf);
			if (buffer[0xfff] != cmpbuf[0xfff])
			{
				printf ("fuse write failed: %04x should be %04x\n", cmpbuf[0xfff], buffer[0xfff]);
				error = 1;
			}
			break;
		default:
			read_config_14bit(pic, cmpbuf + 0x2000);
			fix_memory(pic, cmpbuf);
			for (i = 0x2000; i < 0x2003; i++)
			{
				if (buffer[i] != cmpbuf[i])
				{
					printf ("ID byte at %04x is: %02X should be %02X\n", i, cmpbuf[i] & 255, buffer[i] & 255);
					error = 1;
				}
			}

			i = 0x2007;
			if (buffer[i] != cmpbuf[i])
			{
				printf ("Config word at %04x is: %04X should be %04x\n", i, cmpbuf[i], buffer[i]);
				error = 1;
			}

			break;
	}
	dump_memory(pic, mem);	// active for debug level 2 only
	dump_memory(pic, cbuf);
	if (error)
		terminate(1);
	printf("Done\n");
}

/*
 *****************************************************************************
 *
 *	SET_CONFIG_WORD
 * 
 * The assembler normally will put the config word in the hex file at either
 * 0xfff (12 bit core)  or 0x2007 (14 bit core). If the user has entered a
 * config word on the command line with the -z option this value will
 * override the value in the hex file. 
 */

static void set_config_word(struct pic *pic, bit16 * mem, char *cfg)
{
	unsigned int cfgword = 0;
	int     i;
	int     core12, cfg_word_offset;

	switch (pic->package)
	{
		case P18_12B:
		case P28_12B:
		case P08_12B:
			core12 = TRUE;
			cfg_word_offset = 0xfff;
			break;
		default:
			core12 = FALSE;
			cfg_word_offset = 0x2007;
			break;
	}

	if (cfg[0])
	{
		for (i = 0; i < strlen(cfg); i++)
			cfgword = (cfgword << 4) | hex(cfg[i]);
		mem[cfg_word_offset] = cfgword;
	}
	else
	{
		// This kludge compensates for gpasm putting config word
		// at 0x2007 for 12 bit core parts

		if (     core12
		     && (mem[0xfff] == pic->profile->initval)
		     && (mem[0x2007] != pic->profile->initval)
		   )
		{
			mem[0xfff] = mem[0x2007];
		}
	}

	printf("Configuration word is %04x\n", mem[cfg_word_offset]);
	fflush(stdout);
}

/*
 **********************************************************   
 *
 *	SET_CAL_WORD
 *
 * The assembler can put the cal word in the hex
 * file at the last program rom location in parts that have 
 * an RC oscillator calibration word.
 * examples:
 * 
 * 14 bit part
 * org   03ffh
 * retlw 90h
 * 
 * 12 bit part
 * org   03ffh
 * movlw 90h
 * 
 * If the user has entered a cal word on the command line
 * with the -x option this value will override the hex file 
 * value, if present.
 * 
 * NOTE:
 *  If the part has never been programmed the factory
 *  set cal word will not be overwritten.
 */

static void set_cal_word(struct pic *pic, bit16 * mem, char *cal)
{
	int     i;
	int     cal_word_offset = pic->size - 1;
	unsigned int calword = 0;

	if (cal[0])
	{
		for (i = 0; i < strlen(cal); i++)
			calword = (calword << 4) | hex(cal[i]);

		calword &= 0xff;			// keep 8 bit cal value only

		if (core12(pic))
			mem[cal_word_offset] = 0x0C00;	// movlw 0 for 12 bit core
		else
			mem[cal_word_offset] = 0x3400;	// retlw 0 for 14 bit core

		mem[cal_word_offset] |= calword;	// retlw|movlw calword
	}
}

/*
 *****************************************************************************
 *
 *	PRINT_CALWORD
 */

static void print_calword(struct pic *pic, bit16 * buffer)
{

	if (pic->profile->LastWordCal)
	{
		printf ("Internal RC Oscillator calibration word at %04X is ", pic->size - 1);
		if (buffer[0] != pic->profile->initval)
			printf("%04X\n", buffer[0]);
		else
			printf("BLANK\n");
	}
}

/*
 *****************************************************************************
 *
 *	MAIN
 */

int     main(int argc, char **argv)
{
	struct pic *pic = NULL;
	extern char *optarg;
	extern int optind;
	int     c;
	char    port[32];
	char    cfg[32];
	char    cal[32];
	char   *filename = NULL;

	int     objsize;
	int     picread = 0;
	BOOL    verify_only = FALSE;
	BOOL    external_isp = FALSE;

	printf("TM4: Linux PIC Programmer version %s of %s\n", VR, DT);

	strcpy(port, "/dev/pic");	/*
					 * Default serial port device. User
					 * should create a symbolic link to
					 * a real device. eg: ln -s /dev/ttyS0 /dev/pic 
					 */
	cfg[0] = (char) NULL;
	cal[0] = (char) NULL;
	picread = 0;
	while ((c = getopt(argc, argv, "neElvrRh?c:dp:z:x:")) != EOF)
	{
		switch (c)
		{
			case 'c':
				pic = find_pic(optarg);
				if (pic == NULL)
				{
					fprintf(stderr, "PIC chip '%s' not found\n", optarg);
					exit(1);
				}
				break;
			case 'n':
				no_delay = TRUE;
				break;
			case 'e':
				external_isp = TRUE;
				break;
			case 'E':
				program_data = FALSE;
				break;
			case 'l':
				print_pic_list();
				exit(0);
				break;
			case 'd':
				debugging++;
				break;
			case 'p':
				strncpy(port, optarg, 31);
				break;
			case 'v':
				verify_only = TRUE;
				break;
			case 'z':
				strncpy(cfg, optarg, 31);	// user entered config word
				break;
			case 'x':
				strncpy(cal, optarg, 31);	// user entered cal word
				break;
			case 'R':
				picread++;	// picread = 2
			case 'r':
				picread++;	// picread = 1

				if (argv[optind] != NULL)
				{
					filename = (char *) malloc(strlen(argv[optind]) + 1);
					strcpy(filename, argv[optind]);
				}
				break;
			case '?':
			default:
				printf("usage: tm4\n"
					" [-c] type sets PIC type\n"
					" [-d] ... enables debugging\n"
					" [-e] enables 5 pin external programming connector\n"
					" [-E] disable automatic data-eeprom programming\n"
					" [-l] list all the pic chips this program knows about\n"
					" [-n] eliminate delay after writing a byte\n"
					" [-p prgmmrport] sets RS232C port name (default is /dev/pic)\n"
					" [-r filename] read the chips rom to a file (intel hex format)\n"
					" [-R filename] read the chips rom to console (hex dump)\n"
					" [-v] Verify chip with object file\n"
					" [-x hexdata] sets RC osc. calibration word (overrides object file value)\n"
					" [-z hexdata] sets config word (overrides object file value)\n"
					" [objectfile] name of object file\n"
					"Examples:\n"
					" Program a chip        : tm4 -c 16c71 -p /dev/ttyS0 -z 3ffa test.hex\n"
					" Program a chip faster : tm4 -c 16c71 -p /dev/ttyS0 -n -z 3ffa test.hex\n"
					" Read a chip           : tm4 -c 16c71 -p /dev/ttyS0 -r test.hex\n"
					" Verify a chip         : tm4 -c 16c71 -p /dev/ttyS0 -v test.hex\n"
					" Use default port      : tm4 -c 16c71 test.hex\n"
					" Set RC Osc cal word   : tm4 -c 12c671 -x 80 test.hex\n"
					" Read PIC to hex file  : tm4 -c 16c71 -r outfile.hex\n"
					" Hex dump PIC mem      : tm4 -c 16c71 -R\n"
					" Pipe hex data to tm4  : cat test.hex | tm4 -c 16c71\n\n");
				exit(1);
		}
	}

	if (pic == NULL)
	{
		fprintf(stderr, "No -c chiptype specified\n");
		exit(1);
	}

	//	Removed this to allow reading from stdin if no file specified
	//	if ((optind >= argc) && (!picread))
	//	{
	//		fprintf(stderr, "No object file specified\n");
	//		exit(1);
	//	} 

	printf("Programmer port   = %s\nPIC chip type     = %s\n", port, pic->name);
	if (open_tty(port) == -1)
		exit(1);
	log_clear();
	init();
	init_memory(pic, mem);
	init_memory(pic, cbuf);

	if (external_isp)
	{
		// Override package and direct Vpp to external 5 pin connector.

		pic->package = P40_14B;
		printf("Using external 5 pin connector\n");
	}

	if (picread == 1)
	{
		//	Read PIC contents, output in Intel HEX and quit

		read_pic_intel_hex(pic, filename);
		exit(0);
	}
	if (picread == 2)
	{
		//	Read PIC contents, output in HEX on console, quit

		read_pic_data(pic);
		exit(0);
	}

	/*	Read intel hex object file */

	objsize = read_object(pic, argv[optind], mem, pic->size);	// returns highest addr used
	printf("Highest address used is %04X\n", objsize);

	/* Make sure object file data will fit in PIC program memory space */

	if (objsize >= pic->size)
	{
		fprintf(stderr, "ERROR: Data in object file outside addressable memory space!\n");
		fprintf(stderr, "Highest address in PIC = %04X\nHighest address in file = %04X\n", pic->size - 1, objsize);
		exit(1);
	}

	/*
	 * Get optional configuraton word from command line (-z option) If
	 * this is not provided it must come from the object file. 
	 */

	set_config_word(pic, mem, cfg);

	/*
	 * Get optional RC oscillator calibration word from command line (-x
	 * option). This will override the hex object file value if present.
	 * If the chip has the factory programmed cal word present it will
	 * not be over written. 
	 */

	if (cal[0])
	{
		set_cal_word(pic, mem, cal);
		objsize = pic->size - 1;
	}

	objsize++;	// objsize + 1  indicates the number of words to write.

	//	debug values
	//	if(core12(pic))
	//	{
	//		mem[pic->size] = 0x55;
	//		mem[pic->size+3] = 0xaa;
	//		mem[pic->size -1] = 0x0ca5;  //fake calword
	//	}
	//	else
	//	{
	//		mem[0x2000] = 0x55;	// ID byte
	//		mem[0x2001] = 0xaa;
	//		mem[0x2100] = 0x99;	//data eeprom
	//		mem[0x213f] = 0x55;
	//	} 

	dump_memory(pic, mem);			// active for debug level 2 only( -dd )

	if (!verify_only)
	{
		//	Actually program the PIC program area

		check_blank(pic);		// Blank check PIC memory
		print_calword(pic, CalBuffer);	// Print calibration word if present
		write_pic(pic, mem, objsize);	// Write PIC program memory
	}
	verify_pic(pic, mem, cbuf, objsize);	// Verify PIC program memory

	if (verify_only)
		print_calword(pic, &cbuf[pic->size - 1]);

	if(    (pic->profile->dataEEpromSize > 0)
	    && (program_data)
	  )
	{
		//	program data are, if there is one and we didn't stop it

		if (!verify_only)
			write_data_eeprom(pic, mem);	// Write data EEPROM
		verify_data_eeprom(pic, mem, cbuf);	// Verify data EEPROM
	}

	if (!verify_only)
		write_config(pic, mem);		// Write PIC configuration memory

	verify_config(pic, mem, cbuf);		// Verify PIC configuration memory area

	log_dump();				// only active if debug enabled

	exit(0);				// Done!
}

/*
 * ******************************************************************* **
 * END OF TM4.C 
 */
