/* tm4.c for unix originally by Bill Danielson


May 19th 2001: modified by Jim Robertson NEWFOUND ELECTRONICS to include new
FLASH parts and profiles


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.

----------------------------------------------------------------------
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

-----------------------------------------------------------------------
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 Set internal parameters, over program count, retries, baud 
0x06 Send Ctrl Byte CTRLBYTE 
0x07 ping, firmware returns 0x01  
0x0a Send Count COUNTHIGH COUNTLOW 
0x0e Send 16 byte signon string 
 
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) 

(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 Set internal parameters, over program count, retries, baud
0x06 Send Ctrl Byte CTRLBYTE
0x07 ping, firmware returns 0x01 
0x0a Send Count COUNTHIGH COUNTLOW
0x0e Send 16 byte signon string

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.
___________________________________

*/

#define version "Linux PIC Programmer 1.2 beta , 23 May 2001"

#include <stdio.h>
#include <termio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.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   //00

int debugging = 01;
int showProgess = 0;
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 */

#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 PF62xSDIP 0x0c
#define PF7xSDIP 0x0d
 
#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) */

/*NFE 19/05/01 */

//new package definitions


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


  /*NFE 19/05/01 */

  // Bit08 FlashMS;  // 1 count = 50uS so 200 (dec) =10mS 

}

/*NFE 19/05/01 */
// I have added a new bit08 variable to the structure above and to the profiles below. It is the last varible
// and it is the flash programming delay 1 unit = 50uS
// I have also added a new profiles [6] and [7] This is for the newer flash
// parts that have reduced program time
 
profiles[] = {
  {0xff, 0x00, 0x00, 0x08, 0x0B, FALSE, 0x0fff,0,FALSE, 0}, // [0] ALL 12 bit eprom parts

  {0x3f, 0x00, 0x00, 0x19, 0x03, FALSE, 0x3fff,0,FALSE, 0}, // [1]most 14 bit eprom parts

  {0x3f, S_TIMED, S_TIMED|P_10MS, 0x01, 0x01, FALSE,0x3fff,64,TRUE,200}, // [2] 16c84, 16F83, 16F84, 16F87x 

  {0x3f, B14_2P, B14_2P,0x19, 0x03, FALSE, 0x3fff,0,FALSE, 0}, // [3] ALL 14 bit 2P parts

  {0x0f, 0x20, 0x20, 0x8, 0x0b, TRUE, 0x0fff,0,FALSE,0}, // [4] 12-bit 8 pin parts with calword

  {0x3f, 0x20, 0x20, 0x19, 0x03, TRUE, 0x3fff,0,FALSE,0}, // [5] 14-bit 8 pin parts with calword 

  {0x3f, S_TIMED, S_TIMED|P_10MS, 0x01, 0x01,FALSE,0x3fff,64,TRUE,0X64}, //[6]  16F87x,  16F62x   

  {0x3f, S_TIMED,S_TIMED|P_10MS,0x01,0x01, FALSE,0x3fff,0,TRUE ,150} //[7]16F7x,  

};


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]},

  /*NFE 19/05/01 */
  //changed profile of these PICs

  {"16f873", P28_SDIP, 4096, &profiles[6]},
  {"16f874", P40_14B, 4096, &profiles[6]},
  {"16f876", P28_SDIP, 8192, &profiles[6]},
  {"16f877", P40_14B, 8192, &profiles[6]},

  /*NFE 19/05/01 */
  //added	new PICs

  {"16f870", P28_SDIP, 2048, &profiles[6]},
  {"16f871", P40_14B, 2048, &profiles[6]},
  {"16f872", P28_SDIP, 2048, &profiles[6]},

  {"16f627", PF62xSDIP, 1024, &profiles[6]},  //new package type
  {"16f628", PF62xSDIP, 2048, &profiles[6]}, //new package type

//these pics have been modified to a new profile due to the faster programming time.


// {"16f70", PF7xSDIP, 512, &profiles[7]},    //Tester only new package type

  {"16f73", PF7xSDIP, 4096, &profiles[7]},    //new package type
  {"16f74", PF7xSDIP, 4096, &profiles[7]},    //new package type
  {"16f76", PF7xSDIP, 8192, &profiles[7]},    //new package type
  {"16f77", PF7xSDIP, 8192, &profiles[7]},    //new package type
  {(int)NULL}
};


static void write_protocol(struct pic *pic, bit16 *buffer, bit16 count,
			   int eeprom);
static void write_protocol_12(struct pic *pic, bit16 *buffer, int count);
static void write_fuse_12bit(struct pic *pic, bit16 fuse);
static void read_pic_memory(struct pic *pic, bit16 *buffer, bit16 count, int eeprom);
static void send_count(bit16);
static void send_ctrl_byte(bit08);
static void send_tm1_2(bit08);
static void enter_config_area();
static void send_tm0();
static void send_tm1();
static void send_tm2();
static void send_silicon_command(bit08 cmd);
static void open_device(struct pic *);
static void close_device();
static void send_byte(bit08);
static void log_read(bit08);
static void log_write(bit08);
static void log_clear();
static void log_dump();
static void terminate();
static void fix_memory(struct pic *, bit16 *);
static void dump_memory(struct pic *, bit16 *);
static void kill_copy_protect();
static void skip_locations(struct pic *pic, bit16 count);
static void skip_7();

static bit08 get_byte();
static void get_bytes(bit08 *, bit16);
static bit16 get_word();
static void get_data(bit16 *, bit16);

static void send_byte(bit08);
static void send_word(bit16);
static void send_data(bit16 *, bit16);

static void timed_read(bit08 *, bit16);
static int core12(struct pic *pic);
static int open_tty(char *name) ;
static void print_calword(struct pic *pic, bit16 *buffer);


/*****************************************************************************
* 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) {
    printf("Got ping...");
  }
  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);
    }
  }

  printf("Trying for signon string\n");
  send_tm0();
  send_byte(0x0e);
  get_bytes(buffer, 16);
  printf("Got it!\n"); 

  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) {
  // printf("At read protocol\n");  Debug only
  close_device();

  send_ctrl_byte(pic->profile->ctrlread);

  //printf("Control byte sent.."); // jim robinson

  open_device(pic);

  // printf("Device open..");

  read_pic_memory(pic,buffer,count,eeprom);

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

}

/*---------------------------------------------------------*/
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);

}


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

  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 or14 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
}

/*------------------------------------------------------------*/

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

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

  default:
    return FALSE;
  }
}

/*------------------------------------------------------------*/


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];
}


/*---------------------------------------------------------------*/


/*****************************************************************************
*
* 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();
}

/*---------------------------------------------------------------------------*/
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;
}

/*****************************************************************************
*
* 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.
*
*
*****************************************************************************/


//WRITING 14-BIT CORE PARTS HERE.

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);


  /*NFE 19/05/01 */
  //Added this to set the flash program timing

  send_tm0();
  send_byte(0x01);
  send_byte(0x03);
  send_byte(pic->profile->FlashMS);  //New programming time.

  //End addition

  if (debugging)
    printf("RetryCount= %d OverPgm= %d Package = %1x \n",
	   pic->profile->RetryCount,
	   pic->profile->OverPgm, 
	   pic->package);

  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();
}

/*------------------------------------------------------------*/

static void write_protocol_12(struct pic *pic, 
			      bit16 *buffer, 
			      int count) {
  bit16 config;
  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)
    printf("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. */
}

/*****************************************************************************
* 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();
}


/*****************************************************************************
* 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();
}


/*****************************************************************************
* 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.
*
*****************************************************************************/
void write_config_isp(struct pic *pic, 
		      bit16 *buffer) {

  bit08 mpp = (pic->profile->flash)?0: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 LOCATION(S)
*
* 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);
  usleep(30000);
}

/*****************************************************************************
* send_silicon_command()
*
* This function sends a 6-bit SILICON command to the ISP device.
* The device must be opened first with the OPEN_DEVICE()
* function.
*
*****************************************************************************/

static void send_silicon_command(bit08 cmd) {
  send_tm2();
  send_byte(0x0c);
  send_byte(cmd);
}

/*****************************************************************************
* 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.
*
*****************************************************************************/



static void write_word(bit16 word) {
  send_tm2();
  send_byte(0x0e);
  send_word(word);
}

/*****************************************************************************
* 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.
*
*****************************************************************************/

static void delay() {
  send_tm2();
  send_byte(0x0d);
}

/*****************************************************************************
* 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 FUNCITONS
*
* 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.
*
*****************************************************************************/

static bit16 get_word() {
  bit16 val;

  val = get_byte() << 8;
  val |= get_byte();
  return val;
}


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

  while (count--) { // XXX: would this be better indicated with filling ### ?
    send_word(*buffer++);
    if (showProgess) {
      printf("\r%04x ", pgmcount++);
      fflush(stdout);
    } 
  }
  if (showProgess)
    printf("\n");
}

/*----------------------------------------------------*/
static void send_word(bit16 value) {
  send_byte(value >> 8);
  send_byte(value & 0xff);
}

/*----------------------------------------------------*/
static void send_ctrl_byte(bit08 ctrlbyte) {
  send_tm0();

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

/*-----------------------------------------------------*/
static void enter_config_area() {
  send_tm2();
  send_byte(0x09); /* ENTER CONFIG AREA "FIRMWARE" COMMAND */
}

/*------------------------------------------------------*/
/* 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');
}

/*-------------------------------------------------------*/
static void send_tm1() {
  send_byte(cmd0);
  send_byte('M');
  send_byte('1');
}

/*--------------------------------------------------------*/
static void send_tm2() {
  send_byte(cmd0);
  send_byte('M');
  send_byte('2');
}


/*---------------------------------------------------------*/
static void send_tm1_2(bit08 package) {
  if ((package == P18_12B) || (package == P28_12B))
    send_tm1();
  else
    send_tm2();
}

/*---------------------------------------------------------*/

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("You must create a symbolic link \n"
	   "between /dev/pic and the 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");
    exit(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");
    exit(1);
  }
}

/*----------------------------------------*/
static void send_byte(bit08 byte) {
  int modem_bits = 0;
  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 */

  /* NFE 19th May 2001  Removed usleep command. */

  //usleep(10000);     //(WAIT); /* wait a fixed time - needs this for some reason */

}

/*-----------------------------------------*/
static bit08 get_byte() {
  bit08 buf[1];

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

/*---------------------------------------------*/

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

/*---------------------------------------------------*/


static void get_data(bit16 *buffer, 
		     bit16 length) {
  unsigned int i,j,k;
  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
  }
}

/*----------------------------------------------------*/
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;

/*------------------------------------------------------------------*/
static void log_read(bit08 c) {
  if (log_count == 10000)
    return;
  log_type[log_count] = 0;
  log_buffer[log_count++] = c;
}

static void log_write(bit08 c) {
  if (log_count == 10000)
    return;
  log_type[log_count] = 1;
  log_buffer[log_count++] = c;
}

static void log_clear() {
  log_count = 0;
}


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

  if (debugging < 1)
    return;

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

/*------------------------------------------------------------*/

static void terminate(int error) {
  //log_dump();
  close_device();
  exit(error);
}

/*----------------------------------------------------------*/
/* 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");
}

/*----------------------------------------------------------*/
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 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) {
  int i,j;
  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 {
    printf("Calling Read_protocol now\n");
    
    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);
}

/*----------------------------------------------------------*/

#ifdef OLDBLANKCHECK
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

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); // This is not required NFE 19th May
			    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


/*-----------------------------------------------------------*/

static int hex(char c) {
  // Isn't this in glibc?
  if ((c >= '0') && (c <= '9'))
    return c - '0';
  if ((c >= 'a') && (c <= 'f'))
    return c - 'a' + 10;
  if ((c >= 'A') && (c <= 'F'))
    return c - 'A' + 10;
  printf("bad hex digit: '%c'\n", c);
  terminate(1);
}

/*------------------------------------------------------*/
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++);

  return ((checksum & 255) == 0)?count:-1;
}
/*-------------------------------------------------*/


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
}

/*------------------------------------------------------*/

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;
  }

}

/*-------------------------------------------------------*/

void fix_memory(struct pic *pic, bit16 *mem) {
  int i;
  for (i=0; i < MEMSIZE; i++)
    mem[i] &= pic->profile->initval;
}

/*------------------------------------------------------*/

static void dump_memory(struct pic *pic, 
			bit16 *mem) {
  int i;
  int j;
  
  if (debugging < 2)
    return;

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

/*------------------------------------------------------------------*/

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;
}


/*--------------------------------------------------------*/
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");
}

/*-------------------------------------------------------------------*/

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");
}

/*--------------------------------------------------------------*/
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");
}

/*--------------------------------------------------------------*/

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");
}

/*--------------------------------------------------------------*/

void verify_pic(struct pic *pic, 
		bit16 *buffer, 
		bit16 *cmpbuf, 
		int words) {
  bit16 verifymask = (pic->profile->mask << 8) | 0xff;
  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");
}

/*------------------------------------------------------*/

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");
}

/*---------------------------------------------------------------*/

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 function

   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.
*/


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 function

   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.
************************************************************/

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
  }
}

/*--------------------------------------------------------*/
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");
  }
}

/*--------------------------------------------------------*/
int main(int argc, char **argv) {
  int i;
  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("%s\n",version);

  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, "elvrRh?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 'e':
      external_isp = TRUE;
      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"
	     " [-l] list all the pic chips this program knows about\n"
	     " [-p prgmmrport] sets RS232C port name (default is /dev/pic)\n"
	     " [-e] enables 5 pin external programming connector\n"
	     " [-v] Verify chip with object file\n"
	     " [-z hexdata] sets config word (overrides object file value)\n"
	     " [-x hexdata] sets RC osc. calibration word (overrides object file value)\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"
	     " [-d] ... enables debugging\n"
	     " [objectfile] name of object file\n"
	     "Examples:\n"
	     " Program a chip : tm4 -c 16c71 -p /dev/ttyS0 -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);
  open_tty(port);
  log_clear();
  init();
  init_memory(pic, mem);
  init_memory(pic, cbuf);

  if(external_isp) {
    pic->package = P40_14B; //Override package and direct Vpp
    //to external 5 pin connector.
    printf("Using external 5 pin connector\n");
  }


  if (picread == 1) {
    read_pic_intel_hex(pic, filename);

    exit(0);
  }
  if(picread == 2) {
    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) {
    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) {
    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 configuraton memory

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

  log_dump(); //only active if debug enabled

  return 0;
}

/* END OF FILE*/
