Logo Search packages:      
Sourcecode: picp version File versions

picstart-isp.c

//-----------------------------------------------------------------------------
//
//    PICSTART Plus programming interface
//
//-----------------------------------------------------------------------------
//
//    Cosmodog, Ltd.
//    415 West Huron Street
//    Chicago, IL   60610
//    http://www.cosmodog.com
//
//-----------------------------------------------------------------------------
//
//    this interface to the PICSTART Plus programmer is provided in an effort
//    to make the PICSTART (and thus, the PIC family of microcontrollers) useful
//    and accessible across multiple platforms, not just one particularly well-
//    known, unstable one.
//
//-----------------------------------------------------------------------------
//
//    Copyright (C) 1999-2002 Cosmodog, Ltd.
// Copyright (c) 2004 Jeffery L. Post
//
// Please send bug reports for version 0.6.0 or higher to
// j_post <AT> pacbell <DOT> net.
//
//    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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
//-----------------------------------------------------------------------------
//
// revision history
//
//    0.2  (10 Sept. 1999) -- first real release
//
//    0.2a (11 Sept. 1999)
//          removed timebits.h from includes.h (wasn't actually used anywhere)
//
//    0.3  (12 Sept. 1999)
//          cleaned up a couple of mistakes in the usage text
//          added support for many more parts (everything supported by MPLAB)
//
//    0.4  (1 Oct. 1999)
//          cleaned up some argument handling (better, more generalized)
//          broke picstart.c up into smaller modules
//          added support for -wo flag (only works on single-word calibration spaces)
//          added oscillator calibration space size checking
//          added dump of device info if invoked without any flags
//
//    0.4a (28 Oct. 1999)
//          improved hex record parsing
//          added timeout to InitDevice() to allow the PICSTART to respond to DTR
//
//    0.4b (14 July 2000)
//          added support for several additional parts (culled from MPLAB 5.00.00)
//          added extended device profile (or something) to initialization -- newer
//                versions of MPLAB send more stuff at device init; these are now sent
//                by picp as well
//
//    0.4c (14 July, 2000)
//          added support for PIC18C442 and PIC18C452
//
//    0.4d (20 February, 2001)
//          changed CMD_REQUEST_ACK to CMD_REQUEST_MODEL (actually asking for programmer
//                model, not just an acknowledge)
//          dropped baud rates over 115200 from serial.c, they aren't useful here and
//                were limiting cross compatibility
//          added bool as a typedef so picp can be compiled under straight c instead of c++
//          changed Makefile so picp compiles using gcc instead of c++
//          modified OpenDevice() and CloseDevice() to save the previous serial port
//                settings and restore them on exit
//          made some improvements to ConfigureDevice() courtesy Brian Chamberlain
//
//    0.5 (31 October, 2002)
//          added support for more parts (current as of MPLAB 5.40.00 -- thanks Mike!)
//          increased character timeout to 1 second from 1/2 second (helps to eliminate
//                false "no response from PICSTART" errors)
//          added support for extended linear address records in Intel hex files.  this
//                used to fail silently and prevented programming if any byte address in
//                the file exceeded 0x8000 (like certain configuration words)
//          corrected size of 'startAddr' and 'curAddr' (were 16 bit, now need to be 32 bit)
//          added warning if segment address record is encountered in Intel hex files.
//                such records are now ignored; previously they caused the parser to fail
//                silently.
//          added test for buffer overrun when reading hex files.  this was absent before,
//                and writing files larger than 8K bytes would fail.  now hex files may be
//                arbitrarily large.
//          added extended linear address record generation to output when reading device
//          replaced some ioctl calls in serial.c with wrapper functions which makes it
//                more portable (now builds under OS X without modification) -- thanks to
//                Ahmi Wolf.
//          fixed error reporting in main() when you specify an illegal part number or
//                serial port
//          reorganized some of the code.
//          added support for V3 of the PS+.
//          added support for the 16F627.
//
//    0.5a (2 November 2002)
//          updated to match MPLAB 5.70.40 (PICSTART Plus firmware 3.00.40)
//          added all parts from MPLAB 5.70.40
//
//    0.5b (3 November 2002)
//          fixed bug which crept back into DoWritePgm() which messed up config word
//          increased BUFFERSIZE to 128k (was 8k)
//
//    0.5c (12 November 2002)
//          added signal handing; if any signal is received reset the PICSTART and exit
//          added libc++ to Makefile to eliminate link errors in OS X and Red Hat 8.0
//
//    0.5d (12 June 2003)
//          added erase flash command
//          fixed segfault in WritePgmRange()
//
// 0.6.0 (14 April 2004) - Jeff Post
//          Added ID location programming.
//          Added compatibility with Warp-13 programmer.
//
// 0.6.1 (18 May 2004) - Jeff Post
//          Added read/write/erase data (eeprom) space.
//          Added erase ID locations and erase configuration bits.
//          Modified DoEraseFlash to explicitly program blank any area that did not
//                get blanked by the CMD_ERASE_FLASH command.
//
// 0.6.2 (15 June 2004) - Jeff Post
//          Added routine to check if a Warp-13 programmer is connected. NOTE: the Warp-13
//                firmware may not support this feature in future versions.
//          Added serial comm line debug option (-c). NOTE: the -c option MUST be the first
//                option on the command line (eg: picp -c /dev/ttyS1 16f84 -ri). If used, the -c
//                option will record all bytes sent/received to/from the PS+ or Warp-13 programmer
//                in a file named 'picpcomm.log'. This file is cumulative--each time it is used
//                it will append data to the log file. The log file can grow quite large unless
//                you delete it occasionally. To report problems with picp, use the -c option to
//                record the comm line data in picpcomm.log, put it in a zip file, and email it
//                to me at j_post <AT> pacbell <DOT> net. Please do not send humungously large
//                log files--try to limit the log file to the minimum that shows the problem.
//          Added code to change character timeout for 18xxx PICs from 1 sec to 5 sec.
//          Fixed dumb bug that forced even number of words when writing program space.
//
//          Thanks to Alexandre Nunes for the following:
//          Add working support for PIC18F252, PIC18F452:
//                - Support for configuration bits with size > 1 word;
//                - Added some data to the device structure to supply
//                   some information not detected from the headers;
//                - Support for word/multiword address alignment (at least 18Fxx2 requires 8 bytes);
//                - Picstart addressing seems to be in byte fashion, not word (size is in words);
//
//                - Modified atoi_base to support octal (JLP).
//
//          Fixed endian issue with configuration data in hex file.
//          Handle comment lines in Intel hex file.
//          Fixed cfgsize error in DoReadCfg.
//          Fixed extended info in PIC_DEFINITION of 16f818/9.
//          Multiple "failed to verify" messages suppressed.
//
//-----------------------------------------------------------------------------
//
// Special thanks to Todd Squires, Brian Chamberlain, Scott Johnston, Mike
//  Crowe, Jim Gasbarro, Tim Voght, Ahmi Wolf, and Antonio Augusto Todo Bom Neto
//  for various suggestions and contributions
//
// Many thanks to Jim Robertson (Newfound Electronics) and Wayne Patterson for
// their excellent support and advice in developing version 0.6.0 and higher.
// Thanks to Alexandre Nunes for his modifications for 18xxx devices.
//
// Thanks to Joost Versteegh of Amsterdam, and Rodney Brooks of MIT for their
// help in debugging problems with 18F chips. Special thanks to Dr. Brooks for
// providing hardware with an 18f458 with which to test ISP programming. The
// Makefile now also builds a version of picp (picp-isp) for programming
// 18f devices via in-circuit serial programming with Warp-13.
//
//-----------------------------------------------------------------------------
//
//  TODO:
//
//          verify program space
//          verify data space
//          erase oscillator calibration (probably unnecessary since none of the current flash devices have calibration space)
//          support osc. calibration space sizes other than 1 (PIC14000 only?)
//          seems to have trouble verifying oscillator calibration when stringing several operations together
//          seems to have a problem with 16C63A (but not 16C63, which MPLAB appears to treat as identical)
//          need to be able to write motorola S records
//
//-----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include    <time.h>

#include "atoi_base.h"
#include "parse.h"
#include "serial.h"
#include "picdev.h"
#include "record.h"

#define TIMEOUT_1_SECOND      1000000                 // 1 second time to wait for a character before giving up (in microseconds)
#define TIMEOUT_5_SECOND      5000000                 // 5 second time to wait for a character before giving up (in microseconds)
#define BUFFERSIZE            0x20000                 // buffer bigger than anyone should need

#define MAXNAMESLEN           80                            // max number of characters on a line when reporting device names

#define     OLD_PICDEV_DEFXSIZE     16

#define     MAX_EEPROM_DATA_SIZE    (8 * 1024)  // maximum size of any device's data memory
#define     MAX_CFG_SIZE      8           // maximum number of words for configuration bits

// COMMANDS (sent to PICSTART)
//
#define CMD_BLANK_CHECK             0x42        // 'B' blank check (0x0f = F not blank, 0x17 = not blank, 0x10 = blank?)
#define CMD_WRITE_PGM               0x51        // 'Q' write program memory
#define CMD_READ_PGM                      0x54        // 'T' read program memory
#define CMD_READ_OSC                      0x63        // 'c' read oscillator calibration memory
#define CMD_READ_DATA               0x64        // 'd' read data (EEPROM) memory
#define CMD_READ_ID                       0x65        // 'e' read ID locations
#define CMD_READ_CFG                      0x66        // 'f' request configuration bits
#define CMD_WRITE_CFG               0x67        // 'g' write configuration bits
#define CMD_WRITE_ID                      0x68        // 'h' write ID locations
#define CMD_WRITE_DATA              0x69        // 'i' write data memory
#define CMD_WRITE_OSC               0x71        // 'q' write oscillator calibration memory
#define CMD_LOAD_INFO               0x81        // send processor-specific info
#define CMD_LOAD_EXT_INFO           0x82        // send more processor-specific info (this is new as of 0.4b)
#define CMD_REQUEST_MODEL           0x88        // request programmer model
#define CMD_REQUEST_VERSION   0x8d        // request firmware version
#define CMD_SET_ADDR                      0x8e        // set address range (start address, size)
#define CMD_ERASE_FLASH             0x8f        // send erase flash device command

#define PIC_ACK                           0xab        // response from picstart plus to model request (only programmer supported)

#define MIN_MAJORVER    3                             // picstart version number detected must be at least this high
#define MIN_MIDVER      00
#define MIN_MINORVER    40

#define MINIMUM_VER           (MIN_MAJORVER*65536 + MIN_MIDVER*256 + MIN_MINORVER)

#define HASH_WIDTH_DEFAULT          40                // default width of the status bar

#define     Warp13      w13_ver[0]              // Will be non-zero if Warp-13 connected

// Prototypes

static bool DoErasePgm(const PIC_DEFINITION *picDevice);
static bool DoEraseData(const PIC_DEFINITION *picDevice);
static bool DoEraseConfigBits(const PIC_DEFINITION *picDevice);
static bool DoEraseIDLocs(const PIC_DEFINITION *picDevice);
static unsigned short int     GetPgmSize(const PIC_DEFINITION *picDevice);
static unsigned short int     GetDataSize(const PIC_DEFINITION *picDevice);

// Struct definitions

typedef struct
{
      unsigned char major;
      unsigned char middle;
      unsigned char minor;
} VERSION;

typedef unsigned short int SIZEFNCT(const PIC_DEFINITION *);

typedef struct
{
      unsigned char     mask;
      SIZEFNCT                *sizef;                       // pointer to function to return the size of the space (0=no function)
      char                    *name;
} BLANK_MSG;

// Data

static const char versionString[] = "0.6.2 ISP";      // this program's version number

static const BLANK_MSG blankList[] =
{
      {BLANK_PGM,       &GetPgmSize,      "program memory"},
      {BLANK_CFG,       0,                      "configuration bits"},
      {BLANK_ID,        0,                      "ID locations"},
      {BLANK_DATA,      &GetDataSize,     "data memory"},
      {0,0,""},
};

static char *programName, *deviceName, *picName;

static VERSION          PICversion;

FILE  *comm_debug;
int   comm_debug_count = 0;
bool  writingProgram = false;
bool  sendCommand = false;

static bool             verboseOutput;
static bool             ignoreVerfErr;
static int              serialDevice;
static unsigned int                 CharTimeout = TIMEOUT_1_SECOND;     // default 1 second timeout
static unsigned short int     readConfigBits[16];     // config bits read back from device
static unsigned int                 hashWidth;                    // width of status bar (0 = none)
static int  oldFirmware = false;
static unsigned char    w13_ver[16];      // This will be filled in if a Warp-13 programmer is connected
static unsigned int     w13version = 0;

static unsigned char eepromData[MAX_EEPROM_DATA_SIZE];

//  status bar handling
static unsigned short int     hashMod, hashNum;

//-----------------------------------------------------------------------------
// reset the PICSTART Plus by lowering then raising DTR
static void ResetPICSTART()
{
      SetDTR(serialDevice, false);              // lower DTR to reset PICSTART Plus
      usleep(1000000/4);                                    // sleep for a quarter second (to let PS+ reset)
      SetDTR(serialDevice, true);               // raise DTR
}

//-----------------------------------------------------------------------------
// process signals (any signal will cause us to exit)
static void SigHandler(int sig)
{
      fprintf(stderr, "exiting...\n");
      ResetPICSTART();
      exit(0);
}

//-----------------------------------------------------------------------------
// Find the requested PIC device from the list of supported devices.
// If the user didn't prepend 'PIC' to the device name skip the
// first three letters of the devices when comparing.  This allows
// both PIC16C505 or 16C505 to refer to the same part.
const static PIC_DEFINITION *GetPICDefinition(char *name)
{
      int   idx;
      int   offset;

      idx = 0;

      while (name[idx])                               // ensure it's all upper case
      {
            name[idx] = toupper(name[idx]);
            idx++;
      }

      offset = 0;                                           // assume no PIC at the front

      if (strncmp(name, "PIC", 3) == 0)               // if the user's argument has 'PIC' at the front,
            offset = 3;                                     // skip the first three letters of the user's argument

      idx = 0;

      while (deviceList[idx])
      {
            if (strcmp(deviceList[idx]->name, name + offset) == 0)
                  break;

            idx++;
      }

      if (deviceList[idx] && !(strncmp(deviceList[idx]->name, "18", 2)))
            CharTimeout = TIMEOUT_5_SECOND;

      return(deviceList[idx]);            // return 0 if no match
}

//-----------------------------------------------------------------------------
// return the size of the program space of the specified device (in words)
static unsigned short int GetPgmSize(const PIC_DEFINITION *picDevice)
{
      return(picDevice->def[PD_PGM_SIZEH] * 256 + picDevice->def[PD_PGM_SIZEL]);
}

//-----------------------------------------------------------------------------
// return the size of the data space of the specified device (in bytes)
static unsigned short int GetDataSize(const PIC_DEFINITION *picDevice)
{
      return(picDevice->def[PD_DATA_SIZEH] * 256 + picDevice->def[PD_DATA_SIZEL]);
}

//-----------------------------------------------------------------------------
// return the start address of the data space of the specified device
static unsigned short int GetDataStart(const PIC_DEFINITION *picDevice)
{
      return (picDevice->eeaddr) ? picDevice->eeaddr :
            (unsigned) (picDevice->def[PD_DATA_ADDRH] * 256 +
             picDevice->def[PD_DATA_ADDRL]);
}

// return the ID Locations area size, in words.
static unsigned int GetIDSize(const PIC_DEFINITION *picDevice)
{
      return picDevice->def[PD_ID_SIZE];
}

// return the ID Locations area start addr.
static unsigned int GetIDAddr(const PIC_DEFINITION *picDevice)
{
      return picDevice->idaddr ? picDevice->idaddr / 2:
            (unsigned) picDevice->def[PD_ID_ADDRH] * 256 + picDevice->def[PD_ID_ADDRL];
}

//-----------------------------------------------------------------------------
// return the size of the oscillator calibration space of the specified device (in words)
static unsigned short int GetOscCalSize(const PIC_DEFINITION *picDevice)
{
      return(picDevice->def[PD_CLK_SIZEH] * 256 + picDevice->def[PD_CLK_SIZEL]);
}

//-----------------------------------------------------------------------------
// return the start address of the oscillator calibration space of the specified device
static unsigned short int GetOscCalStart(const PIC_DEFINITION *picDevice)
{
      return(picDevice->def[PD_CLK_ADDRH] * 256 + picDevice->def[PD_CLK_ADDRL]);
}

//-----------------------------------------------------------------------------
// return the start address of the configuration bits, in words.
static unsigned int GetConfigStart(const PIC_DEFINITION *picDevice)
{
      return picDevice->cfgmem ?
            picDevice->cfgmem / 2: (unsigned)
            (picDevice->def[PD_CFG_ADDRH] * 256 +
            picDevice->def[PD_CFG_ADDRL]);
}

// return the size of the configuration bits, in words.
static unsigned int GetConfigSize(const PIC_DEFINITION *picDevice)
{
      return picDevice->def[PD_CFG_SIZE];
}

// Get the alignment interval for recording purposes (in words).
static unsigned int GetWordAlign(const PIC_DEFINITION *picDevice)
{
  return picDevice->wordalign ? picDevice->wordalign : 0; // No align by default
}

// Get the word width for this device
static unsigned short int GetWordWidth(const PIC_DEFINITION *picDevice)
{
      return (picDevice->def[PD_PGM_WIDTHH]) << 8 | picDevice->def[PD_PGM_WIDTHL];
}

//-----------------------------------------------------------------------------
//    send a message to the PICSTART, wait for a specified number of bytes to be returned
//  If false is returned, there was a timeout, or some other error
static bool SendMsg(const unsigned char *cmdBuff, unsigned int cmdBytes, unsigned char *rtnBuff, unsigned int rtnBytes)
{
      bool  fail;
      int   numRead;
      int   bytesRemaining;
      unsigned int      i;

      fail = false;

      if (cmdBytes)
      {
            write(serialDevice, &cmdBuff[0], cmdBytes);                 // send out the command

            if (comm_debug)
            {
                  if (!writingProgram)
                  {
                        fprintf(comm_debug, "\n");
                        comm_debug_count = 0;
                  }

                  for (i=0; i<cmdBytes; i++)
                  {
                        fprintf(comm_debug, " O-0x%02x", cmdBuff[i]);
                        comm_debug_count++;

                        if (comm_debug_count >= 8)
                        {
                              comm_debug_count = 0;
                              fprintf(comm_debug, "\n");
                        }
                  }
            }
      }

      bytesRemaining = rtnBytes;

      while (bytesRemaining && !fail)
      {
            numRead = ReadBytes(serialDevice, &rtnBuff[rtnBytes - bytesRemaining], bytesRemaining, CharTimeout);

            if (numRead < 0)
            {
                  fprintf(stderr,"error %d, %s\n", errno, strerror(errno));
                  fail = true;
            }
            else if (numRead == 0)        // timed out
                  fail = true;

            // subtract bytes read in, don't allow to underflow (shouldn't happen)
            bytesRemaining = (bytesRemaining > numRead) ? bytesRemaining - numRead : 0;
      }

      return(!fail);
}

//-----------------------------------------------------------------------------
//    send a message to the PICSTART, wait for each byte to be returned
//  If false is returned, there was a timeout, or some other error
static bool SendMsgWait(const unsigned char *cmdBuff, unsigned int cmdBytes, unsigned char *rtnBuff, unsigned int rtnBytes)
{
      bool  fail;
      int   numRead;
      unsigned int      i;
      int   bytesRemaining;

      fail = false;
      bytesRemaining = rtnBytes;

      if (comm_debug && !writingProgram)
      {
            comm_debug_count = 0;
            fprintf(comm_debug, "\n");
      }

      for (i=0; i<cmdBytes && bytesRemaining && !fail; i++)
      {
            write(serialDevice, &cmdBuff[i], 1);                        // send out the command

            if (comm_debug)
            {
                  fprintf(comm_debug, " O-0x%02x", cmdBuff[i]);
                  comm_debug_count++;

                  if (comm_debug_count >= 8)
                  {
                        comm_debug_count = 0;
                        fprintf(comm_debug, "\n");
                  }
            }

            numRead = ReadBytes(serialDevice, &rtnBuff[i], 1, CharTimeout);

            if (numRead < 0)
            {
                  fprintf(stderr,"error %d, %s\n", errno, strerror(errno));
                  fail = true;
            }
            else if (numRead == 0)        // timed out
                  fail = true;

            // subtract bytes read in, don't allow to underflow (shouldn't happen)
            bytesRemaining = (bytesRemaining > numRead) ? bytesRemaining - numRead : 0;

            if (fail)
                  break;
      }

      return(!fail);
}

//
// This routine will check if a Warp-13 programmer is connected.
//
// NOTE: This feature of the Warp-13 programmer is based on the
// TM4 protocol, and MAY NOT BE SUPPORTED IN FUTURE VERSIONS of
// the Warp-13 firmware.
//
// If a Picstart Plus is connected, it will echo back the four
// bytes sent, but otherwise ignore them.
//
// If a Warp-13 is connected, it will respond to the four byte
// command with a single byte of 0x01. Then we send the next four
// byte command and the Warp-13 will respond with 16 bytes of
// data. Bluepole returns "BP" in the first two bytes, Redback
// returns "RX". Bytes at offset 3-7 are the version code for
// the Warp-13 firmware, but the exact interpretation of the
// meaning of these five bytes aren't known to me at this time.
//

static void check_warp13(void)
{
      int   i;
      unsigned int      to;
      unsigned char     bfr[16];

      to = CharTimeout;                               // save normal character timeout, since this
      CharTimeout = TIMEOUT_1_SECOND;     // should always be done with short timeout

      w13_ver[0] = 0;
      bfr[0] = '+';
      bfr[1] = 'M';
      bfr[2] = '0';
      bfr[3] = 7;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nChecking for Warp-13");
            comm_debug_count = 0;
      }

      for (i=4; i<16; i++)
      {
            bfr[i] = 0;
            w13_ver[i] = 0;
      }

      SendMsg(bfr, 4, bfr, 4);      // expect a timeout here, so don't check return value

      if (bfr[0] != 1)                    // Warp-13 not connected
      {
            CharTimeout = to;             // restore character timeout
            return;
      }

      bfr[0] = '+';                             // Warp-13 responded, so send
      bfr[1] = 'M';                             // firmware request command
      bfr[2] = '0';
      bfr[3] = 0x0e;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nGetting Warp-13 version info");
            comm_debug_count = 0;
      }

      if (SendMsg(bfr, 4, bfr, 16))
      {
            for (i=0; i<16; i++)                // save Warp-13 data for later use
                  w13_ver[i] = bfr[i];

            w13version = (unsigned int) bfr[3] << 24 | bfr[4] << 16 | bfr[5] << 8 | bfr[6];
            fprintf(stderr, "Warp-13 %c%c %02x.%02x.%02x.%02x.%02x\n",
                  bfr[0], bfr[1], bfr[3], bfr[4], bfr[5], bfr[6], bfr[7]);
      }

      CharTimeout = to;             // restore character timeout
}

//-----------------------------------------------------------------------------
//    ask what type of programmer is attached (if any)
//
// This is where MPLAB tries to identify what type of programmer is
// attached and selects the appropriate protocol based on the response.
// We just check for Picstart Plus response code and fail if we don't get it.
static bool DoGetProgrammerType()
{
      bool                    succeed;
      unsigned char     theBuffer[1];
      unsigned int      retryCount;

      theBuffer[0] = CMD_REQUEST_MODEL;
      retryCount = 5;
      succeed = false;

      do
      {
            if (comm_debug)
            {
                  fprintf(comm_debug, "\nGet programmer type");
                  comm_debug_count = 0;
            }

            if (SendMsg(theBuffer, 1, theBuffer, 1))
            {
                  if (theBuffer[0] == PIC_ACK)        // see if PICSTART Plus
                        succeed = true;
                  else
                        fprintf(stderr, "%02X\n", theBuffer[0]);
            }

            retryCount--;
      } while(!succeed && retryCount);

      return(succeed);
}

//-----------------------------------------------------------------------------
// request version from the PICSTART
static bool DoGetVersion()
{
      bool                    fail;
      unsigned char     theBuffer[4];

      fail = false;
      theBuffer[0] = CMD_REQUEST_VERSION;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nGet version");
            comm_debug_count = 0;
      }

      if (SendMsg(theBuffer, 1, theBuffer, 4))
      {
            if (theBuffer[0] != CMD_REQUEST_VERSION)
                  fail = true;
            else
            {
                  PICversion.major = theBuffer[1];
                  PICversion.middle = theBuffer[2];
                  PICversion.minor = theBuffer[3];
            }
      }
      else
      {
            fprintf(stderr, "failed to read PICSTART version number\n");
            fail = true;
      }

      return(!fail);
}

//-----------------------------------------------------------------------------
//    get the version from the PICSTART and display it
static void DoShowVersion()
{
      fprintf(stdout,"PICSTART Plus firmware version %d.%02d.%02d\n", PICversion.major, PICversion.middle, PICversion.minor);
}

//-----------------------------------------------------------------------------
// send a "set range" command to the PS+
static bool SetRange(const PIC_DEFINITION *picDevice, unsigned int start, unsigned int length)
{
      unsigned char     rangeBuffer[6], rtnBuffer[6];
      int                     size;
      bool                    error = false;

      if (GetWordWidth(picDevice) == 0xffff)
            start *= 2;       // For these devices, addressing is done in octets.

      rangeBuffer[0] = CMD_SET_ADDR;

      if (!oldFirmware)
      {
            size = 6;
            rangeBuffer[1] = (start >> 16) &0xff;
            rangeBuffer[2] = (start >> 8) & 0xff;
            rangeBuffer[3] = (start >> 0) & 0xff;
            rangeBuffer[4] = (length >> 8) & 0xff;
            rangeBuffer[5] = (length >> 0) & 0xff;
      }
      else
      {
            size = 5;
            rangeBuffer[1] = 0;
            rangeBuffer[2] = 0;
            rangeBuffer[3] = (length >> 8) & 0xff;
            rangeBuffer[4] = (length >> 0) & 0xff;
      }

      if (comm_debug)
      {
            fprintf(comm_debug, "\nSet Range");
            comm_debug_count = 0;
            sendCommand = true;
      }

      if (SendMsgWait(rangeBuffer, size, rtnBuffer, size))
      {
            if (memcmp(rangeBuffer, rtnBuffer, size) == 0)  // read back result and see if it looks correct
                  return(true);
            else
                  fprintf(stderr,"echoback from set range command incorrect\n");
      }
      else
            fprintf(stderr,"failed to send set range command\n");

      return (!error);
}

// Read configuration bits and compare against bits in picDevice->defx.
// If they match, return true, else return false.

bool DoConfigBlankCheck(const PIC_DEFINITION *picDevice)
{
      int                                 i, size;
      const unsigned char     *cfgbits;
      unsigned char                 cfgdata[MAX_CFG_SIZE * 2];
      bool                                fail = false;

      size = GetConfigSize(picDevice) * 2;

      if (size > MAX_CFG_SIZE * 2)
            return false;

      cfgbits = picDevice->defx;
      cfgdata[0] = CMD_READ_CFG;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nRead Configuration bits");
            comm_debug_count = 0;
            sendCommand = true;
      }

      if (SendMsg(cfgdata, 1, cfgdata, size + 2))
      {
            if ((cfgdata[0] != CMD_READ_CFG) || (cfgdata[size + 1] != 0))
                  fail = true;
            else
            {
                  for (i=0; i<size; i++)
                  {
                        fail = !(cfgdata[i + 1] == cfgbits[i]);

                        if (fail)
                              break;
                  }
            }
      }

      return (!fail);
}

//-----------------------------------------------------------------------------
// Check device for blank
static bool DoBlankCheck(const PIC_DEFINITION *picDevice, unsigned char blankMode)
{
      bool                    fail;
      unsigned char     theBuffer[3];
      int                     idx;

      idx = 0;
      fail = false;
      theBuffer[0] = CMD_BLANK_CHECK;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nBlank Check");
            comm_debug_count = 0;
      }

      if (SendMsg(theBuffer, 1, theBuffer, 2))
      {
            theBuffer[1] &= blankMode;                      // look only at what we were asked to look at

            if (!verboseOutput)
                  fprintf(stdout, "0x%02x\n", theBuffer[1]);                  // quiet mode will just show the return code
            else
            {
                  while (blankList[idx].mask)                                             // report anything that isn't blank
                  {
                        if (blankMode & blankList[idx].mask)                        // check only what we were asked
                        {
                              if (!blankList[idx].sizef || (blankList[idx].sizef(picDevice) > 0))
                              {
                                    if (blankMode & blankList[idx].mask == BLANK_CFG)
                                    {
                                          fprintf(stdout, "%s: ", blankList[idx].name);

                                          if (!DoConfigBlankCheck(picDevice))       // compare values to defx
                                          {
                                                fprintf(stdout, "not blank\n");
                                                fail = true;                                                // config bits not blank
                                          }
                                          else
                                                fprintf(stdout, "blank\n");
                                    }
                                    else
                                    {
                                          fprintf(stdout, "%s: ", blankList[idx].name);

                                          if (theBuffer[1] & blankList[idx].mask)   // if a bit is set,
                                          {
                                                fprintf(stdout, "not blank\n");
                                                fail = true;                                                      // something wasn't blank
                                          }
                                          else
                                                fprintf(stdout, "blank\n");
                                    }
                              }
                        }

                        idx++;
                  }
            }
      }
      else
      {
            fprintf(stderr, "failed to send blank check command\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Read eeprom data
static bool DoReadData(const PIC_DEFINITION *picDevice, FILE *theFile)
{
      bool                    fail;
      unsigned char     theBuffer[1024];
      unsigned short int      size, start;

      size = GetDataSize(picDevice);
      start = GetDataStart(picDevice);

      if (!size)
      {
            fprintf(stderr, "Device %s has no eeprom data!\n", picName);
            return false;
      }

      fail = false;
      theBuffer[0] = CMD_READ_DATA;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nRead Data");
            comm_debug_count = 0;
            sendCommand = true;
      }

      if (SendMsg(theBuffer, 1, eepromData, size + 2))
      {                                               // ask it to fill the buffer (plus the command plus a terminating zero)
            WriteHexRecord(theFile, &eepromData[1], start, size); // write hex records to selected stream
      }
      else
      {
            fprintf(stderr, "failed to send read data command\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Write eeprom data
static bool DoWriteData(const PIC_DEFINITION *picDevice, FILE *theFile)
{
      int                     i;
      bool                    fail, fileDone;
      unsigned short int      size, start, count;
      unsigned int      startAddr, curAddr, nextAddr;
      unsigned char     data;

      size = GetDataSize(picDevice);
      start = GetDataStart(picDevice);

      if (!size)
      {
            fprintf(stderr, "Device %s has no eeprom data!\n", picName);
            return false;
      }

      for (i=0; i<size; i++)
            eepromData[i] = 0xff;

      fail = fileDone = false;

      InitParse();                                                                  // get ready to start reading the hex file

      fileDone = !GetNextByte(theFile, &nextAddr, &data);   // get a byte and the initial address

      while (!fileDone && !fail)
      {
            startAddr = nextAddr;                                       // the first address of the new block
            curAddr = startAddr;
            eepromData[1] = data;                                       // the first data byte of the new block
            count = 2;                                                              // number of bytes waiting to be sent

            while ((!(fileDone = !GetNextByte(theFile, &nextAddr, &data)) &&  (count < size + 1)))
            {                                                                                   // get next byte
                  if (curAddr + 1 == nextAddr)                    // use this byte now only if it's sequential
                  {
                        eepromData[count] = data;
                        count++;
                        curAddr++;

                        if (curAddr > size)
                        {
                              fail = true;
                              break;
                        }
                  }
                  else
                        break;                                                      // non-sequential, write this buffer then start another
            }
      }

      if (!fail)
      {
            writingProgram = true;
            eepromData[0] = CMD_WRITE_DATA;                 // set command in eepromData buffer

            if (comm_debug)
            {
                  fprintf(comm_debug, "\nWrite Data\n");
                  comm_debug_count = 0;
                  sendCommand = true;
            }

            if (!SendMsgWait(eepromData, size + 1, eepromData, size + 2))
            {
                  fprintf(stderr, "failed to send write data command\n");
                  fail = true;
            }
      }

      writingProgram = false;
      return(!fail);
}

//--------------------------------------------------------------------
// initialize a status bar, given the number
// of operations expected
static void InitHashMark(unsigned short int numOps, unsigned short int hashWidth)
{
      hashNum = 0;

      if (hashWidth)                                        // if width = zero do nothing (no bar)
      {
            if (hashWidth <= numOps)                        // if it will create less than 1 mark per operation
                  hashMod = numOps / hashWidth;       // mod is the number of operations divided by the bar width
            else
                  hashMod = 1;                              // don't allow the bar to be longer than the number of operations
      }
      else
            hashMod = 0;                                    // no bar, no mod
}

//--------------------------------------------------------------------
// advance the status bar if the current number
// of operations warrants it
static void ShowHashMark(unsigned short int curOps)
{
      if (verboseOutput && hashMod && (curOps / hashMod > hashNum) )
      {
            fprintf(stdout, "#");
            fflush(stdout);                                 // be sure it gets displayed right away
            hashNum++;
      }
}

//--------------------------------------------------------------------
// finish up a status bar
static void UnInitHashMark()
{
      if (verboseOutput && hashMod)
            fprintf(stdout, "\n");
}

//--------------------------------------------------------------------
// read oscillator calibration
static bool DoReadOscCal(const PIC_DEFINITION *picDevice)
{
      bool                                fail;
      unsigned char                 *theBuffer;
      unsigned short int      size;
      int                                 idx;

      fail = false;
      size = GetOscCalSize(picDevice);

      if (size)
      {
            if (SetRange(picDevice, GetOscCalStart(picDevice), size))
            {
                        // get a buffer this big plus one char for the command and a 0 at the end
                  if ((theBuffer = (unsigned char *) malloc(size + 2)))
                  {
                        theBuffer[0] = CMD_READ_OSC;

                        if (comm_debug)
                        {
                              fprintf(comm_debug, "\nRead OSC Calibration");
                              comm_debug_count = 0;
                              sendCommand = true;
                        }

                              // ask it to fill the buffer (plus the command plus a terminating zero)
                        if (SendMsg(theBuffer, 1, theBuffer, size + 2))
                        {
                              fprintf(stdout, "oscillator calibration: ");

                              for (idx=1; idx < size + 1; idx+=2)
                              {
                                    if ((((idx - 1) / 2) & 8) == 0)
                                          fprintf(stdout, "\n");

                                    fprintf(stdout, " 0x%02x%02x", theBuffer[idx], theBuffer[idx + 1]);
                              }

                              fprintf(stdout, "\n");
                        }
                        else
                        {
                              fprintf(stderr,"failed to send read osc command\n");
                              fail = true;
                        }
                  }
                  else
                  {
                        fprintf(stderr, "failed to malloc\n");
                        fail = true;
                  }
            }
            else
                  fail = true;
      }
      else
      {
            fprintf(stderr, "device %s has no oscillator calibration space\n", picDevice->name);
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// write oscillator calibration
static bool DoWriteOscCalBits(const PIC_DEFINITION *picDevice, unsigned short int oscCalBits)
{
      bool                                fail;
      unsigned char                 theBuffer[3], rtnBuffer[4];
      unsigned short int      size;                                           // size of the calibration space

      fail = false;
      size = GetOscCalSize(picDevice);          // size of the calibration space

      if (size == 1)
      {
            theBuffer[0] = CMD_WRITE_OSC;
            theBuffer[1] = (oscCalBits >> 8) & 0xff;
            theBuffer[2] = oscCalBits & 0xff;

            if (comm_debug)
            {
                  fprintf(comm_debug, "\nWrite OSC Calibration");
                  comm_debug_count = 0;
                  sendCommand = true;
            }

            if (SendMsg(theBuffer, 3, rtnBuffer, 4))
            {
                  if (theBuffer[0] != rtnBuffer[0] || theBuffer[1] != rtnBuffer[1] || theBuffer[2] != rtnBuffer[2] || rtnBuffer[3] != 0)
                  {
                        fail = true;
                        fprintf(stderr, "failed to verify while writing oscillator calibration data\n");
                  }
            }
            else
            {
                  fprintf(stderr, "failed to send write osc command\n");
                  fail = true;
            }
      }
      else if (size == 0)
      {
            fprintf(stderr, "device %s has no oscillator calibration space\n", picDevice->name);
            fail = true;
      }
      else
      {
            fprintf(stderr, "oscillator calibration space sizes other than 1 not supported yet\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// read configuration bits
static bool DoReadCfg(const PIC_DEFINITION *picDevice, bool verbose)
{
      bool                    fail;
      int                     i, j, cfgsize;
      unsigned char     theBuffer[MAX_CFG_SIZE * 2];

      cfgsize = GetConfigSize(picDevice) * 2;

      if (cfgsize > MAX_CFG_SIZE * 2)
      {
            fprintf(stdout, "Configuration size exceeds maximum\n");
            return false;
      }

      fail = false;
      theBuffer[0] = CMD_READ_CFG;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nRead Configuration bits");
            comm_debug_count = 0;
            sendCommand = true;
      }

      if (SendMsg(theBuffer, 1, theBuffer, cfgsize + 2))
      {
            if ((theBuffer[0] != CMD_READ_CFG) || (theBuffer[cfgsize + 1] != 0))
            {
                  fprintf(stderr, "failed to read configuration bits\n");
                  fail = true;
            }
            else
            {
                  for (i=0, j=0; i<cfgsize; i++, j+= 2)
                        readConfigBits[i] = theBuffer[j + 1] * 256 + theBuffer[j + 2];

                  if (verbose)
                  {
                        if (verboseOutput)
                              fprintf(stdout, "configuration bits:");

                        for (i=0; i < cfgsize / 2; i++)
                              fprintf(stdout, " 0x%04x", readConfigBits[i]);
                        fprintf(stdout, "\n");
                  }
            }
      }
      else
      {
            fprintf(stderr, "failed to send read configuration command\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// erase the program space of a part that can be erased (PIC16Fxx, etc)
static bool DoErasePgm(const PIC_DEFINITION *picDevice)
{
      bool                                fail;
      unsigned char                 theBuffer[4], rtnBuffer[2];
      unsigned short int      size;                         // size of the device's program memory (in bytes)
      int                                 byteCnt;
      unsigned char                 high, low;

      fail = false;
      size = GetPgmSize(picDevice) * 2;         // get the size
      InitHashMark(size, hashWidth);

      if (SetRange(picDevice, 0, size / 2))     // erase the whole program space
      {
            writingProgram = true;
            theBuffer[0] = CMD_WRITE_PGM;
            high = picDevice->def[PD_PGM_WIDTHH];
            low = picDevice->def[PD_PGM_WIDTHL];

            if (comm_debug)
                  fprintf(comm_debug, "\nErase Program (write pgm cmd)\n");

            if (SendMsg(theBuffer, 1, rtnBuffer, 1))  // send the command, watch for it to bounce back
            {
                  if (comm_debug)
                  {
                        fprintf(comm_debug, "\n");
                        comm_debug_count = 0;
                  }

                  if (*rtnBuffer == CMD_WRITE_PGM)
                  {
                        theBuffer[0] = high;
                        theBuffer[1] = low;
                        byteCnt = 0;

                        while ((byteCnt < size) && !fail)
                        {
                              if (SendMsg(theBuffer, 2, rtnBuffer, 2))  // write the bytes, ignore the return value (check results later)
                              {
                                    byteCnt += 2;
                                    ShowHashMark(byteCnt);
                              }
                              else
                              {
                                    fprintf(stderr, "failed to send write program command\n");
                                    fail = true;
                              }
                        }

                        UnInitHashMark();

                        if (SendMsg(theBuffer, 0, rtnBuffer, 1))              // eat the trailing zero
                        {
                              if (*rtnBuffer == 0)
                              {
                                    if (!DoBlankCheck(picDevice, BLANK_PGM))  // make sure it is now blank
                                    {
                                          fprintf(stderr, "failed to erase program space\n");
                                          fail = true;
                                    }
                              }
                              else
                              {
                                    fprintf(stderr, "bad return result\n");
                                    fail = true;                                                // fail if it's not zero
                              }
                        }
                        else
                        {
                              fprintf(stderr, "failed to read trailing 0\n");
                              fail = true;                                                      // didn't echo everthing back like it should have
                        }
                  }
                  else
                  {
                        fprintf(stderr, "echoback did not look correct\n");
                        fail = true;
                  }
            }
            else
            {
                  fprintf(stderr, "failed to send write program command\n");
                  fail = true;                                                            // didn't echo everthing back like it should have
            }
      }
      else        // set range failed
            fail = true;

      writingProgram = false;
      return(!fail);
}

//--------------------------------------------------------------------
// erase the data space of a part that can be erased (PIC16Fxx, etc)
static bool DoEraseData(const PIC_DEFINITION *picDevice)
{
      bool                                fail;
      unsigned char                 theBuffer[4], rtnBuffer[2];
      unsigned short int      size;                   // size of the device's data memory (in bytes)
      int                                 byteCnt;

      fail = false;
      size = GetDataSize(picDevice);            // get the size

      if (!size)
      {
            fprintf(stderr, "Device %s has no eeprom data!\n", picName);
            return false;
      }

      if (SetRange(picDevice, 0, size))                     // erase the whole data space
      {
            theBuffer[0] = CMD_WRITE_DATA;

            if (comm_debug)
            {
                  fprintf(comm_debug, "\nErase Data (write data cmd)");
                  comm_debug_count = 0;
            }

            if (SendMsg(theBuffer, 1, rtnBuffer, 1))  // send the command, watch for it to bounce back
            {
                  if (*rtnBuffer == CMD_WRITE_DATA)
                  {
                        if (comm_debug)
                        {
                              fprintf(comm_debug, "\n");
                              writingProgram = true;
                              comm_debug_count = 0;
                        }

                        theBuffer[0] = 0xff;                                  // send as all 1's
                        byteCnt = 0;

                        while ((byteCnt < size) && !fail)
                        {
                                    // write the bytes, ignore the return value (check results later)
                              if (SendMsgWait(theBuffer, 1, rtnBuffer, 1))
                                    byteCnt++;
                              else
                              {
                                    fprintf(stderr, "failed to send write data command\n");
                                    fail = true;
                              }
                        }

                        if (SendMsg(theBuffer, 0, rtnBuffer, 1))                    // eat the trailing zero
                        {
                              if (*rtnBuffer == 0)
                              {
                                    if (!DoBlankCheck(picDevice, BLANK_DATA))       // make sure it is now blank
                                    {
                                          fprintf(stderr, "failed to erase data space\n");
                                          fail = true;
                                    }
                              }
                              else
                              {
                                    fprintf(stderr, "bad return result\n");
                                    fail = true;                                                // fail if it's not zero
                              }
                        }
                        else
                        {
                              fprintf(stderr, "failed to read trailing 0\n");
                              fail = true;                                                      // didn't echo everthing back like it should have
                        }
                  }
                  else
                  {
                        fprintf(stderr, "echoback did not look correct\n");
                        fail = true;
                  }
            }
            else
            {
                  fprintf(stderr, "failed to send write data command\n");
                  fail = true;                                                            // didn't echo everthing back like it should have
            }
      }
      else        // set range failed
            fail = true;

      writingProgram = false;
      return(!fail);
}

//--------------------------------------------------------------------
// Execute a ERASE FLASH DEVICE operation
// CMD_ERASE_FLASH does not erase data memory,
// and may not do anything except remove code protection.
// So, after issuing CMD_ERASE_FLASH and doing a blank check,
// if anything is reported non-blank, we will explicitly
// program it blank and then do a blank check again.
static bool DoEraseFlash(const PIC_DEFINITION *picDevice)
{
      bool                    fail;
      unsigned char     theBuffer[3];

      fail = false;
      theBuffer[0] = CMD_ERASE_FLASH;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nErase Flash");
            comm_debug_count = 0;
      }

      if (SendMsg(theBuffer, 1, theBuffer, 2))
      {
            if ((theBuffer[0] != CMD_ERASE_FLASH) || (theBuffer[1] != 0))
            {
                  fprintf(stderr, "failed to erase flash device\n");
                  fail = true;
            }
            else
            {
                  theBuffer[0] = CMD_BLANK_CHECK;                 // make sure everything is now blank

                  if (comm_debug)
                  {
                        fprintf(comm_debug, "\nBlank Check");
                        comm_debug_count = 0;
                  }

                  if (SendMsg(theBuffer, 1, theBuffer, 2))
                  {
                        theBuffer[1] &= (BLANK_PGM | BLANK_CFG | BLANK_ID | BLANK_DATA);

                        if (theBuffer[1])       // something didn't get erased
                        {
                              if (theBuffer[1] & BLANK_CFG)       // explicitly write the configuration bits
                                    DoEraseConfigBits(picDevice);

                              if (theBuffer[1] & BLANK_ID)        // explicitly write ID locations
                                    DoEraseIDLocs(picDevice);

                              if (theBuffer[1] & BLANK_DATA)      // explicitly write data memory
                                    DoEraseData(picDevice);

                              if (theBuffer[1] & BLANK_PGM)       // explicitly write program memory
                                    DoErasePgm(picDevice);

                              if (!DoBlankCheck(picDevice, BLANK_DATA))       // make sure it is now blank
                              {
                                    fprintf(stderr, "failed to erase flash device\n");
                                    fail = true;
                              }
                        }
                  }
                  else
                  {
                        fprintf(stderr, "failed to send blank check command\n");
                        fail = true;
                  }
            }
      }
      else
      {
            fprintf(stderr, "failed to send erase command\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Write device's configuration bits
static bool DoWriteConfigBits(const PIC_DEFINITION *picDevice, unsigned char *cfgbits, unsigned int cfgsize)
{
      bool                    fail;
      unsigned char     theBuffer[3], rtnBuffer[3];
      unsigned int      i, j;

      if ((cfgsize > (j = GetConfigSize(picDevice) * 2)) || !cfgsize || (cfgsize & 1))
      {
            fprintf(stderr, "Invalid request of size %u to write configuration"
                  " bits.\nThis device configuration space size is %u\n",
                  cfgsize, j);
            return false;
      }

      fail = false;
      theBuffer[0] = CMD_WRITE_CFG;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nWrite Configuration bits");
            comm_debug_count = 0;
      }

      if (!SendMsg(theBuffer, 1, rtnBuffer, 1) || rtnBuffer[0] != theBuffer[0])
      {
            fprintf(stderr, "Error sending Write Configuration bits command\n");
            fail = true;
      }

      if (!fail)
      {
            for (i=0; i < cfgsize && !fail; )
            {
                  theBuffer[0] = cfgbits[i++];
// 18f ISP requires single byte at a time
//                theBuffer[1] = cfgbits[i++];

//                if (!SendMsg(theBuffer, 2, rtnBuffer, 2)/* || memcmp(theBuffer, rtnBuffer, 2)*/)
                  if (!SendMsg(theBuffer, 1, rtnBuffer, 1)/* || memcmp(theBuffer, rtnBuffer, 2)*/)
                  {
                        fprintf(stderr, "failed to verify while writing configuration bits\n");
                        fail = true;
                  }
            }

            if (!fail)
            {
// 18f ISP requires single byte at a time
//                for (i = 0 ;  i < (j - cfgsize); i += 2)
                  for (i = 0 ;  i < (j - cfgsize); i++)
                  {
                        theBuffer[0] = 0xff;
//                      theBuffer[1] = 0xff;

// 18f ISP requires single byte at a time
//                      if (!SendMsg(theBuffer, 2, rtnBuffer, 2) /*|| memcmp(theBuffer, rtnBuffer, 2)*/)
                        if (!SendMsg(theBuffer, 1, rtnBuffer, 1) /*|| memcmp(theBuffer, rtnBuffer, 2)*/)
                        {
                              fprintf(stderr, "failed to verify while writing spare configuration bits\n");
                              fail = true;
                        }
                  }

                  if (!fail)
                  {
                        if (!SendMsg(theBuffer, 0, rtnBuffer, 1) || rtnBuffer[0] != 0)
                        {
                              fprintf(stderr, "failed to verify after writing configuration bits\n");
                              fail = true;
                        }
                  }
            }
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Erase device's configuration bits
// [TODO] Erase config bits is not correct for all devices.

static bool DoEraseConfigBits(const PIC_DEFINITION *picDevice)
{
      bool                    fail;
      unsigned int      i, size;
      unsigned char     theBuffer[64];
      const unsigned char     *cfgbits;

      size = GetConfigSize(picDevice) * 2;
      cfgbits = picDevice->defx;

      for (i=0; i<size; i++)
            theBuffer[i] = *cfgbits++;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nErase Configuration (write cfg cmd)");
            comm_debug_count = 0;
      }

      fail = !DoWriteConfigBits(picDevice, theBuffer, size);

      if (!fail && !DoBlankCheck(picDevice, BLANK_CFG))     // make sure it is now blank
      {
            fprintf(stderr, "failed to erase configuration bits\n");
            fail = true;
      }

      return(fail);
}

//--------------------------------------------------------------------
static bool DoWriteIDLocs(const PIC_DEFINITION *picDevice, unsigned char *idlocs, unsigned int idsize)
{
      unsigned int      i, j;
      bool                    fail;
      unsigned char     theBuffer[3], rtnBuffer[3];

      if (idsize > (j = GetIDSize(picDevice) * 2) || !idsize || (idsize & 1))
      {
            fprintf(stderr, "Invalid request of size %u to write ID Locations.\n"
                  "This device ID Location size is %u\n",
                  idsize, j);
            return false;
      }

      fail = false;

      theBuffer[0] = CMD_WRITE_ID;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nWrite ID Locations");
            comm_debug_count = 0;
      }

      if (!SendMsg(theBuffer, 1, rtnBuffer, 1) || rtnBuffer[0] != theBuffer[0])
      {
            fprintf(stderr, "Error sending Write ID Locations command\n");
            fail = true;
      }

      if (!fail)
      {
            for (i=0; i<idsize && !fail; )
            {
                  theBuffer[1] = idlocs[i++];
                  theBuffer[0] = idlocs[i++];

                  if (!SendMsg(theBuffer, 2, rtnBuffer, 2) || memcmp(theBuffer, rtnBuffer, 2))
                  {
                        fprintf(stderr,"failed to verify while writing ID locations\n");
                        fail = true;
                  }
            }

            if (!fail && !SendMsg(theBuffer, 0, rtnBuffer, 1) || rtnBuffer[0] != 0)
            {
                  fprintf(stderr, "failed to verify after writing ID locations\n");
                  fail = true;
            }
      }

      return(!fail);
}

//--------------------------------------------------------------------
static bool DoEraseIDLocs(const PIC_DEFINITION *picDevice)
{
      int                     i, size;
      unsigned char     bitshi, bitslo, *theBuffer;
      bool                    fail = false;

      size = GetIDSize(picDevice) * 2;
      theBuffer = (unsigned char *) malloc(size);

      if (!theBuffer)
      {
            fprintf(stderr, "failed to allocate buffer\n");
            fail = true;                        // failed to malloc
      }
      else
      {
            bitshi = picDevice->def[PD_DATA_WIDTHH];
            bitslo = picDevice->def[PD_DATA_WIDTHL];

            for (i=0; i<size; i++)
            {
                  if (i & 1)
                        theBuffer[i] = bitshi;
                  else
                        theBuffer[i] = bitslo;
            }

            if (comm_debug)
            {
                  fprintf(comm_debug, "\nErase ID Locations (write ID cmd)");
                  comm_debug_count = 0;
            }

            fail = !DoWriteIDLocs(picDevice, theBuffer, size);

            if (!fail && !DoBlankCheck(picDevice, BLANK_ID))      // make sure it is now blank
            {
                  fprintf(stderr, "failed to erase ID locations\n");
                  fail = true;
            }
      }

      return(!fail);
}

//--------------------------------------------------------------------
// copy buffer to device, starting at word address startAddr_w, running for size_w words
//  DOES NOT boundary-check range -- will attempt to write outside of device's memory
//  Returns true if okay, false if failed
//  Verify error counts as failure only if failOnVerf = true
static bool WritePgmRange(const PIC_DEFINITION *picDevice, unsigned short int startAddr_w, unsigned short int size_w, unsigned char *buffer)
{
      bool                    fail, verifyFail;
      unsigned char     temp, cmdBuffer[2];
      int                     idx;

      fail = verifyFail = false;

      if (SetRange(picDevice, startAddr_w, size_w))
      {
            idx = 0;

            while (idx < (size_w * 2))
            {
                  temp = buffer[idx + 1];
                  buffer[idx + 1] = buffer[idx];                        // swap byte order (make it little endian)
                  buffer[idx] = temp;
                  idx += 2;
            }

            cmdBuffer[0] = CMD_WRITE_PGM;                               // add in the command

            if (comm_debug)
            {
                  fprintf(comm_debug, "\nWrite Program");
                  comm_debug_count = 0;
                  sendCommand = true;
            }

            if (SendMsg(cmdBuffer, 1 ,cmdBuffer, 1))        // send the command, watch for it to bounce back
            {
                  if (*cmdBuffer == CMD_WRITE_PGM)
                  {
                        writingProgram = true;
                        idx = 0;

                        while (!fail && (idx < (size_w * 2)))
                        {
// 18f ISP requires single byte at a time, so wait after each byte
//                            if (SendMsg(&buffer[idx], 2, cmdBuffer, 2))
                              if (SendMsgWait(&buffer[idx], 2, cmdBuffer, 2))
                              {
                                    if ((buffer[idx] != cmdBuffer[0]) || (buffer[idx + 1] != cmdBuffer[1]))
                                          verifyFail = true;                                    // didn't get back what we sent

                                    idx += 2;
                                    ShowHashMark(idx);
                              }
                              else
                              {
                                    fprintf(stderr, "failed to send write program data\n");
                                    fail = true;
                              }
                        }

                        if (!fail)
                        {
                              if (SendMsg(buffer, 0, cmdBuffer, 1))           // eat the trailing zero
                              {
                                    if (verifyFail)
                                    {
                                          if (!ignoreVerfErr)
                                                fprintf(stderr, "failed to verify while writing to program space\n");
                                          else                          // report it but don't fail on it
                                                fprintf(stderr, "Warning: failed to verify while writing to program space\n");
                                    }
                              }
                              else
                              {
                                    fprintf(stderr, "failed to get trailing 0\n");
                                    fail = true;
                              }
                        }
                  }
                  else
                  {
                        fprintf(stderr, "write program command did not echo back as expected\n");
                        fail = true;
                  }
            }
            else
            {
                  fprintf(stderr, "failed to send write program command\n");
                  fail = true;
            }
      }
      else        // set range failed
            fail = true;

      writingProgram = false;
      return(!(fail || (!ignoreVerfErr && verifyFail)));
}

//--------------------------------------------------------------------
// write the program space of the passed device
static bool DoWritePgm(const PIC_DEFINITION *picDevice, FILE *theFile)
{
      bool                    fail, fileDone;
      unsigned char     *theBuffer;
      unsigned int      i, j, startAddr, curAddr, nextAddr, size, align, pgmsize;
      unsigned char     data;
      unsigned int      devCfgAddr, devIDAddr;

      fail = fileDone = false;
      align = GetWordAlign(picDevice) * 2;
      pgmsize = GetPgmSize(picDevice) * 2;

      if ((theBuffer = (unsigned char *) malloc(BUFFERSIZE)))
      {
            InitHashMark(pgmsize, hashWidth);   // go to too much effort to set the width
            InitParse();                                                // get ready to start reading the hex file
            fileDone = !GetNextByte(theFile, &nextAddr, &data);   // get a byte and the initial address

            while (!fileDone && !fail)
            {
                  startAddr = nextAddr;   // the first address of the new block
                  curAddr = startAddr;
                  size = 0;                           // number of bytes waiting to be sent

                  if (align && (startAddr % align) && startAddr < pgmsize) // assuming program addr starts at zero.
                  {
                        if (startAddr < align)
                        {
                              fprintf(stderr, "Error: Wrong addressing "
                                    "on hex file (unaligned address "
                                    "at position 0x%X). Wrong processor type?\n",
                                     startAddr);
                                     fail = true;
                                     break;
                        }

                        for (i=0; i < startAddr % align; i++)
                              theBuffer[size++] = 0xff;

                        startAddr -= startAddr % align;
                  }

                  theBuffer[size++] = data;           // the first data byte of the new block

                  while ((!(fileDone = !GetNextByte(theFile, &nextAddr, &data)) &&  (size < BUFFERSIZE)))   // get next byte
                  {
                        if (size >= BUFFERSIZE)
                        {
                              fail = true;
                              break;
                        }

                        if (curAddr + 1 == nextAddr)  // use this byte now only if it's sequential
                        {
                              curAddr++;
                              theBuffer[size++] = data;
                        }
                        else
                              break;                        // non-sequential, write this buffer then start another
                  }

                  if (fail)
                        break;

                  if (align && (startAddr < pgmsize) && (size % align)) // take care of unaligned/incomplete writes.
                  {
                        j = align - (size % align);

                        for (i=0; i<j; i++)
                              theBuffer[size++] = 0xff;
                  }
                  else if (!align && (size & 1))                  // Don't allow odd sizes
                        theBuffer[size++] = 0xff;

                  // check if config address is embedded in range
                  devCfgAddr = GetConfigStart(picDevice) * 2;     // address of device's config memory
                  devIDAddr  = GetIDAddr(picDevice) * 2;          // address of id locations.

                  if (startAddr == devCfgAddr)              // at config bits address?
                  {
                        if (size <= GetConfigSize(picDevice) * 2) // must not be greater than this
                        {                                                           // inside configuration space, write as configuration
                              for (j=0; j<size; j += 2)     // DoWriteConfigBits needs big endian, so swap bytes
                              {
                                    data = theBuffer[j];
                                    theBuffer[j] = theBuffer[j+1];
                                    theBuffer[j+1] = data;
                              }

                              fail = !DoWriteConfigBits(picDevice, theBuffer, size);
                        }
                        else
                        {
                              fprintf(stderr,"Configuration data not written: size is %u bytes (device's limit is %u)\n",
                                    size, GetConfigSize(picDevice));
                        }
                  }
                  else
                  {                 // if not config bits try to write where ever it lands
                        fail = !WritePgmRange(picDevice, startAddr / 2, size / 2, theBuffer);
                  }
            }

            UnInitHashMark();
            free(theBuffer);
      }
      else
      {
            fprintf(stderr, "failed to malloc %d bytes\n", BUFFERSIZE);
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Read the program space of the passed device
static bool DoReadPgm(const PIC_DEFINITION *picDevice, FILE *theFile)
{
      bool                    fail;
      unsigned char     temp, *theBuffer;
      unsigned int      size;                               // size of the device's program memory (in bytes)
      unsigned int      idx;

      fail = false;
      size = GetPgmSize(picDevice) * 2;

      if (DoReadCfg(picDevice, false))
      {
            if (~readConfigBits[0] & picDevice->cpbits)
                  fprintf(stderr, "Warning: device is code protected: configuration bits = 0x%04x\n", readConfigBits[0]);

            if (SetRange(picDevice, 0, size / 2))     // size in words
            {
                              // get a buffer this big plus one char for the command and a 0 at the end
                  if ((theBuffer = (unsigned char *) malloc(size + 2)))
                  {
                        theBuffer[0] = CMD_READ_PGM;

                        if (comm_debug)
                        {
                              fprintf(comm_debug, "\nRead Program");
                              comm_debug_count = 0;
                              sendCommand = true;
                        }

                                          // ask it to fill the buffer (plus the command plus a terminating zero)
                        if (SendMsg(theBuffer, 1, theBuffer, size + 2))
                        {
                              // DEBUG shouldn't need to swap byte order here but we do

                              for (idx=1; idx < size + 1; idx += 2)
                              {
                                    temp = theBuffer[idx + 1];
                                    theBuffer[idx + 1] = theBuffer[idx];      // swap byte order (make it little endian)
                                    theBuffer[idx] = temp;
                              }

                              WriteHexRecord(theFile, &theBuffer[1], 0, size);      // write hex records to selected stream
                        }
                        else
                        {
                              fprintf(stderr, "failed to send read program command\n");
                              fail = true;
                        }

                        free(theBuffer);
                  }
                  else
                  {
                        fprintf(stderr, "failed to allocate buffer\n");
                        fail = true;                        // failed to malloc
                  }
            }
            else        // set range failed
                  fail = true;
      }
      else
      {
            fprintf(stderr, "failed to read config bits\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Read the device ID locations
static bool DoReadID(const PIC_DEFINITION *picDevice)
{
      bool                    fail;
      unsigned int      i, size;
      unsigned char     theBuffer[32];

      size = GetIDSize(picDevice) * 2;

      if (!size)
      {
            fprintf(stderr, "Reading ID Locations is not supported for this device.\n");
            return false;
      }

      fail = false;
      theBuffer[0] = CMD_READ_ID;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nRead ID Locations");
            comm_debug_count = 0;
            sendCommand = true;
      }

      if (SendMsg(theBuffer, 1, theBuffer, size + 2))
      {
            if ((theBuffer[0] == CMD_READ_ID) && (theBuffer[size + 1] == 0))
            {
                  if (verboseOutput)
                        fprintf(stdout, "ID locations: ");  // if in quiet mode, only the values will be returned

                  for (i=0; i<size; i+= 2)
                        fprintf(stdout, "0x%02x%02x ", theBuffer[i + 1], theBuffer[i + 2]);

                  fprintf(stdout, "\n");
            }
            else
            {
                  fprintf(stderr, "failed to read ID locations\n");
                  fail = true;
            }
      }
      else
      {
            fprintf(stderr, "failed to send read ID command\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// initialize for specified part, return true if succeeded
static bool DoInitPIC(const PIC_DEFINITION *picDevice)
{
      int                     idx;
      bool                    fail;
      unsigned char     theBuffer[3];
      unsigned char     cmdBuffer[PICDEV_DEFSIZE + 1];
      unsigned char     extCmdBuffer[PICDEV_DEFXSIZE + 1];

      fail = false;
      theBuffer[0] = CMD_LOAD_INFO;

      if (comm_debug)
      {
            fprintf(comm_debug, "\nLoad Processor Info");
            comm_debug_count = 0;
      }

            // send load processor info command, wait for command to echo back
      if (SendMsg(theBuffer, 1, theBuffer, 1))
      {
            if (theBuffer[0] == CMD_LOAD_INFO)
            {
                  memcpy(cmdBuffer, picDevice->def, PICDEV_DEFSIZE);    // copy definition into the new buffer
                  cmdBuffer[PICDEV_DEFSIZE] = 0;                                          // initialize the checksum

                              // calculate the checksum, store as last byte in buffer
                  for (idx = 0; idx < PICDEV_DEFSIZE; idx++)
                        cmdBuffer[PICDEV_DEFSIZE] += cmdBuffer[idx];

                              // send whole buffer including checksum
                  if (SendMsg(cmdBuffer, PICDEV_DEFSIZE + 1,theBuffer, 1))
                  {
                        if (theBuffer[0] == 0)              // zero = checksum okay (data received okay)
                        {
                              theBuffer[0] = CMD_LOAD_EXT_INFO;

                              if (comm_debug)
                              {
                                    fprintf(comm_debug, "\nLoad Extended Configuration");
                                    comm_debug_count = 0;
                              }

                                    // send load extended processor info command, wait for command to echo back
                              if (SendMsg(theBuffer, 1, theBuffer, 1))
                              {
                                    if (theBuffer[0] == CMD_LOAD_EXT_INFO)
                                    {
                                                // copy definition into the new buffer
                                          memcpy(extCmdBuffer, picDevice->defx, PICDEV_DEFXSIZE);
                                          extCmdBuffer[PICDEV_DEFXSIZE] = 0;        // initialize the checksum

                                                // calculate the checksum, store as last byte in buffer
                                          for (idx=0; idx<PICDEV_DEFXSIZE; idx++)
                                                extCmdBuffer[PICDEV_DEFXSIZE] += extCmdBuffer[idx];

                                                // send whole buffer including checksum
                                          if (SendMsg(extCmdBuffer, PICDEV_DEFXSIZE + 1, theBuffer, 1))
                                          {
                                                if (theBuffer[0] != 0)  // zero = checksum okay (data received okay)
                                                {
                                                      theBuffer[0] = CMD_LOAD_EXT_INFO;

                                                      if (comm_debug)
                                                      {
                                                            fprintf(comm_debug, "\nLoad Extended Configuration - old firmware");
                                                            comm_debug_count = 0;
                                                      }

                                                            // send load extended processor info command, wait for command to echo back
                                                      if (SendMsg(theBuffer, 1, theBuffer, 1))
                                                      {
                                                            if (theBuffer[0] == CMD_LOAD_EXT_INFO)
                                                            {
                                                                        // copy definition into the new buffer
                                                                  memcpy(extCmdBuffer, picDevice->defx, OLD_PICDEV_DEFXSIZE);
                                                                  extCmdBuffer[OLD_PICDEV_DEFXSIZE] = 0;     // initialize the checksum

                                                                        // calculate the checksum, store as last byte in buffer
                                                                  for (idx=0; idx<OLD_PICDEV_DEFXSIZE; idx++)
                                                                  {
                                                                        extCmdBuffer[OLD_PICDEV_DEFXSIZE] += extCmdBuffer[idx];
                                                                  }

                                                                        // send whole buffer including checksum
                                                                  if (SendMsg(extCmdBuffer, OLD_PICDEV_DEFXSIZE + 1, theBuffer, 1))
                                                                  {
                                                                        if (theBuffer[0] != 0)  // zero = checksum okay (data received okay)
                                                                        {
                                                                              fprintf(stderr, "bad result from PICSTART: 0x%x\n", theBuffer[0]);
                                                                              fail = true;
                                                                        }
                                                                        else
                                                                              oldFirmware = true;
                                                                  }
                                                            }
                                                      }
                                                }
                                          }
                                          else
                                          {
                                                fprintf(stderr, "failed to send extended device data\n");
                                                fail = true;
                                          }
                                    }
                                    else
                                    {
                                          fprintf(stderr, "echoback did not look correct\n");
                                          fail = true;
                                    }
                              }
                              else
                              {
                                    fprintf(stderr, "failed to send extended device definition\n");
                                    fail = true;
                              }
                        }
                        else
                        {
                              fprintf(stderr, "checksum failure\n");
                              fail = true;                                                                  // didn't receive zero, fail
                        }
                  }
                  else
                  {
                        fprintf(stderr, "failed to send device definition\n");
                        fail = true;
                  }
            }
            else
            {
                  fprintf(stderr, "echoback did not look correct\n");
                  fail = true;
            }
      }
      else
      {
            fprintf(stderr, "failed to send load info command\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// find out if the next argument is a flag (not preceeded by '-').
// if it is, return a pointer to it and advance to the next argument, otherwise
//    return null.
static char *GetNextFlag(int *argc, char **argv[])
{
      if (*argc && (***argv != '-'))      // if the next argument isn't preceeded by a '-'
      {
            (*argv)++;                                      // advance to next argument
            (*argc)--;
            return(*(*argv - 1));               // but return pointer to this one
      }

      return(NULL);
}

//--------------------------------------------------------------------
// Do all the things that the command line is asking us to do
static bool DoTasks(int *argc, char **argv[], const PIC_DEFINITION *picDevice, char *flags)
{
      bool                    fail = false;
      char                    *fileName = (char *) 0;
      FILE                    *theFile = stdout;
      unsigned char     blankMode, *cbfr;
      unsigned int      i, count, count2, *ibfr;
      unsigned int      oscCalBits;

      switch (*flags)
      {
            case 'b':                                             // blank check
                  flags++;

                  if (*flags)
                  {
                        blankMode = 0;

                        while (*flags)
                        {
                              switch (*flags)
                              {
                                    case 'p':
                                          blankMode |= BLANK_PGM;
                                          break;

                                    case 'c':
                                          blankMode |= BLANK_CFG;
                                          break;

                                    case 'i':
                                          blankMode |= BLANK_ID;
                                          break;

                                    case 'd':
                                          blankMode |= BLANK_DATA;
                                          break;

                                    default:
                                          break;                        // ignore undefined flags
                              }

                              flags++;
                        }
                  }
                  else                          // no mode flags means check them all
                        blankMode = BLANK_PGM | BLANK_CFG | BLANK_ID | BLANK_DATA;

                  fail = !DoBlankCheck(picDevice, blankMode);
                  break;

            case 'e':                                             // erase
                  flags++;

                  if (*flags)
                  {
                        while (*flags && !fail)
                        {
                              switch (*flags)
                              {
                                    case 'p':
                                          fail = !DoErasePgm(picDevice);
                                          break;

                                    case 'c':
                                          DoEraseConfigBits(picDevice);
                                          break;

                                    case 'i':
                                          DoEraseIDLocs(picDevice);
                                          break;

                                    case 'd':
                                          fail = !DoEraseData(picDevice);
                                          break;

                                    case 'o':   // [TODO] erase oscillator calibration
                                          fprintf(stderr, "erase oscillator calibration not implemented yet\n");
                                                // DEBUG can osc cal be erased/written on flash parts?
                                          break;

                                    case 'f':
                                          fail = !DoEraseFlash(picDevice);
                                          break;

                              }

                              flags++;
                        }
                  }
                  else
                        fprintf(stderr, "specify one or more regions to erase (p|c|i|d|o)\n");
                  break;

            case 'r':                                             // read
                  flags++;

                  if (*flags)
                  {
                        while (*flags && !fail)
                        {
                              switch (*flags)
                              {
                                    case 'p':
                                          if ((fileName = GetNextFlag(argc, argv)))
                                                theFile = fopen(fileName, "w");
                                          else
                                                theFile = stdout;

                                          if (theFile)
                                          {
                                                fail = !DoReadPgm(picDevice, theFile);          // read program data, write to stream

                                                if (theFile != stdout)        // if we wrote it to a file,
                                                      fclose(theFile);              // close the file
                                          }
                                          else
                                                fprintf(stderr, "unable to open output file: '%s'\n", fileName);

                                          break;

                                    case 'c':
                                          fail = !DoReadCfg(picDevice, true); // read configuration bits, display them
                                          break;

                                    case 'i':
                                          fail = !DoReadID(picDevice);  // read ID locations
                                          break;

                                    case 'd':
                                          if ((fileName = GetNextFlag(argc, argv)))
                                                theFile = fopen(fileName, "w");
                                          else
                                                theFile = stdout;

                                          if (theFile)
                                          {
                                                fail = !DoReadData(picDevice, theFile);   // read data memory

                                                if (theFile != stdout)        // if we wrote it to a file,
                                                      fclose(theFile);              // close the file
                                          }
                                          else
                                                fprintf(stderr, "unable to open output file: '%s'\n", fileName);

                                          break;

                                    case 'o':
                                          fail = !DoReadOscCal(picDevice);
                                          break;
                              }

                              flags++;
                        }
                  }
                  else
                        fprintf(stderr, "specify one or more regions to read (p|c|i|d|o)\n");

                  break;

            case 'w':                                             // write
                  flags++;

                  if (*flags)
                  {
                        switch (*flags)
                        {
                              case 'p':
                                    if ((fileName = GetNextFlag(argc, argv)))
                                          theFile = fopen(fileName, "r");
                                    else
                                          theFile = stdin;

                                    if (theFile)
                                    {
                                          fail = !DoWritePgm(picDevice, theFile);

                                          if (theFile != stdin)                           // if we read it from a file,
                                                fclose(theFile);                                // close the file
                                    }
                                    else
                                          fprintf(stderr, "unable to open input file: '%s'\n", fileName);

                                    break;

                              case 'c':
                                    if ((fileName = GetNextFlag(argc, argv))) // 'fileName' is actually the next argument
                                    {
                                          i = GetConfigSize(picDevice) * 2;

                                          if (!i)
                                          {
                                                fprintf(stderr, "Writing to configuration bits is not supported for this device.\n");
                                                fail = true;
                                                break;
                                          }

                                          count = i / sizeof(unsigned short int);
                                          ibfr = (unsigned int *) alloca(i * sizeof(unsigned int));
                                          cbfr = (unsigned char *) alloca(i * sizeof(unsigned int) * 2);

                                          if (!ibfr || !cbfr)
                                          {
                                                fprintf(stderr, "Error allocating memory");
                                                fail = true;
                                                break;
                                          }

                                          for (count2=0; count2<count; count2++)
                                          {
                                                fail = !atoi_base(fileName, (unsigned int *) &ibfr[count2]);

                                                if (fail)
                                                      break;

                                                fileName = GetNextFlag(argc, argv);
                                          }

                                          if (fail)
                                          {
                                                fprintf(stderr, "Error parsing supplied configuration bits.\n");
                                                fail = true;
                                                break;
                                          }

// [TODO] the following may not be correct for 18xxx devices

                                          for (count = count2 = 0; count < i; count++, count2 += 2)
                                          {
                                                cbfr[count2+0] = (ibfr[count] >> 8)  & 0xff;
                                                cbfr[count2+1] = (ibfr[count] >> 0)  & 0xff;
                                          }

                                          fail = DoWriteConfigBits(picDevice, cbfr, count);
                                    }
                                    else
                                    {
                                          fprintf(stderr, "Missing argument to Write Configuration Bits command.\n");
                                          fail = true;
                                    }

                                    break;

                              case 'i':
                                    if ((fileName = GetNextFlag(argc, argv)))
                                    {
                                          i = GetIDSize(picDevice) * 2;
                                          count = i / sizeof(unsigned short int);
                                          ibfr = (unsigned int *) alloca(i * sizeof(unsigned int));
                                          cbfr = (unsigned char *) alloca(i * sizeof(unsigned int) * 2);

                                          if (!ibfr || !cbfr)
                                          {
                                                fprintf(stderr, "Error allocating memory");
                                                fail = true;
                                                break;
                                          }

                                          for (count2=0; count2<count; count2++)
                                          {
                                                fail = !atoi_base(fileName, (unsigned int *) &ibfr[count2]);

                                                if (fail)
                                                      break;

                                                fileName = GetNextFlag(argc, argv);
                                          }

                                          if (fail)
                                          {
                                                fprintf(stderr, "Error parsing supplied ID Locations data.\n");
                                                fail = true;
                                                break;
                                          }

// [TODO] the following may not be correct for 18xxx devices

                                          for (count = count2 = 0; count < i; count++, count2 += 2)
                                          {
                                                cbfr[count2 + 1] = (ibfr[count] >> 8)  & 0xff;
                                                cbfr[count2 + 0] = (ibfr[count] >> 0)  & 0xff;
                                          }

                                          fail = DoWriteIDLocs(picDevice, cbfr, count);
                                    }
                                    else
                                    {
                                          fprintf(stderr, "Missing argument to Write ID locations command.\n");
                                          fail = true;
                                    }

                                    break;

                              case 'd':
                                    if ((fileName = GetNextFlag(argc, argv)))
                                          theFile = fopen(fileName, "r");
                                    else
                                          theFile = stdin;

                                    if (theFile)
                                    {
                                          fail = !DoWriteData(picDevice, theFile);

                                          if (theFile != stdin)                           // if we read it from a file,
                                                fclose(theFile);                                // close the file
                                    }
                                    else
                                          fprintf(stderr, "unable to open input file: '%s'\n", fileName);

                                    break;

                              case 'o':
                                    if ((fileName = GetNextFlag(argc, argv))) // 'fileName' is actually the next argument
                                    {
                                          fail = !atoi_base(fileName, &oscCalBits);

                                          if (!fail)
                                          {
                                                if (oscCalBits < 0x10000)
                                                      fail = !DoWriteOscCalBits(picDevice, (unsigned short int) oscCalBits);
                                                else
                                                {
                                                      fprintf(stderr, "Value out of range: '%s'\n", fileName);
                                                      fail = true;
                                                }
                                          }
                                          else
                                                fprintf(stderr, "Unable to interpret '%s' as a numerical value\n", fileName);
                                    }
                                    else
                                          fprintf(stderr, "write oscillator calibration must be followed by a numerical value\n");

                                    break;

                              default:
                                    fprintf(stderr, "must specify a region to write\n");
                                    break;
                        }
                  }
                  else
                        fprintf(stderr, "specify a region to write (p|c|i|d|o)\n");

                  break;

            case 'v':   // [TODO]                     // verify
                  fprintf(stderr, "DEBUG verify is not implemented yet\n");
                  // DEBUG verify device goes here
                  break;

            default:
                  break;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Initialize the serial port
// Once the device is opened and locked, this sets up the port, and makes sure the handshake looks good.
static bool InitDevice(int serialDevice, unsigned int baudRate, unsigned char dataBits, unsigned char stopBits, unsigned char parity)
{
      bool                                fail;                               // haven't failed (yet)
      bool                                CTS, DCD;
      unsigned short int      ctsTimeOut;

      fail = false;

      if (ConfigureDevice(serialDevice, baudRate, dataBits, stopBits, parity, false))     // set up the device
      {
            if (ConfigureFlowControl(serialDevice, false))  // no flow control at the moment (raise RTS)
            {
                  ResetPICSTART();
                  ctsTimeOut = 100;                                     // allow about 100 ms (0.1 sec) for CTS to show up

                  do
                  {
                        GetDeviceStatus(serialDevice, &CTS, &DCD);      // see if CTS is true

                        if (CTS)
                              break;                                                // break out if it is

                        usleep(1000);                                         // wait 1 ms (more or less), try again
                  }
                  while (ctsTimeOut--);

                  if (!CTS)
                  {
                        fprintf(stderr, "programmer not detected (CTS is false)\n");
                        fail = true;                                          // didn't see CTS, assume device is not present or not ready, fail
                  }
                  else
                        ConfigureFlowControl(serialDevice, true); // looks ok to use flow control, so allow it

                  FlushBytes(serialDevice);                                   // get rid of any pending data
            }
            else
            {
                  fprintf(stderr, "could not configure flow control\n");
                  fail = true;
            }
      }
      else
      {
            fprintf(stderr, "could not configure device parameters\n");
            fail = true;
      }

      return(!fail);
}

//--------------------------------------------------------------------
// Show a starting address and size
static void ShowStartSize(unsigned short int start, unsigned short int size)
{
      if (size)
            fprintf(stdout, "    0x%04x-0x%04x (0x%04x word%c)\n", start, start + size - 1, size, ((size != 1) ? 's' : '\0'));
      else
            fprintf(stdout, "    none\n");
}

//--------------------------------------------------------------------
// Report information about the passed device
static void ShowDeviceInfo(const PIC_DEFINITION *picDevice)
{
      fprintf(stdout, "device name: %s\n", picDevice->name);                        // show the name

      fprintf(stdout, "  program space:\n");                                              // show range of program space
      ShowStartSize(0, GetPgmSize(picDevice));

      fprintf(stdout, "  data space:\n");                                                 // show range of data space, if any
      ShowStartSize(GetDataStart(picDevice), GetDataSize(picDevice));

      fprintf(stdout, "  oscillator calibration space:\n");                   // show range of calibration space, if any
      ShowStartSize(GetOscCalStart(picDevice), GetOscCalSize(picDevice));

      fprintf(stdout, "  configuration bits:\n");
      fprintf(stdout, "    address: 0x%x, size: %u words\n",
            GetConfigStart(picDevice), GetConfigSize(picDevice)); // show address of configuration bits
      fprintf(stdout, "    protect mask:  0x%04x\n", picDevice->cpbits);      // mask of code protect bits
      fprintf(stdout, "    watchdog mask: 0x%04x\n", picDevice->wdbit);       // mask of watchdog enable bit
}

//--------------------------------------------------------------------
//    display all supported devices
static void ShowDevices()
{
      int idx = 0;
      int length = 0;
      int thisLength;

      fprintf(stdout, "supported devices:\n");

      while (deviceList[idx])
      {
            thisLength = strlen(deviceList[idx]->name);     // length of this device's name
            length += thisLength;                                             // add to length of this line

            if (length + 2 < MAXNAMESLEN)                               // ensure there's room for the space and comma, too
                  fprintf(stdout, "%s", deviceList[idx]->name);   // put it on this line
            else
            {
                  fprintf(stdout, "\n%s", deviceList[idx]->name); // put it on the next line
                  length = thisLength;
            }

            idx++;

            if (deviceList[idx])                                                    // if more devices are in the list,
            {
                  fprintf(stdout, ", ");                                      // add a comma and a space
                  length += 2;
            }
            else
                  fprintf(stdout, "\n");                                      // or newline if it is the last one
      }
}

//--------------------------------------------------------------------
// some thorny issues still exist in specifying arguments:
//
//    1) should write cause a blank check before writing?  should it program anyway if no bits that should be 1 are 0?
//    2) several different writes are needed (program, ID, data, config)
//    3) likewise, several different reads are needed
//    4) need to be able to specify a range of addresses to read, instead of reading the entire device
//    5) should erase be an option?  several different erases? erase plus a set of flags to indicate what to erase?

//--------------------------------------------------------------------
// tell the user how to use this program
static void Usage()
{
      fprintf(stdout, "%s: version %s, (c) 2000-2004 Cosmodog, Ltd. (http://www.cosmodog.com)\n"
                  " and Jeff Post (http://home.pacbell.net/theposts/picmicro)\n", programName, versionString);
      fprintf(stdout, "Usage: %s ttyname devtype [-h] [-q] [-v] [-s [size]] [-b|-r|-w|-e][pcidof]\n", programName);
      fprintf(stdout, " where:\n");
      fprintf(stdout, "  ttyname is the serial device the PICSTART is attached to (e.g., /dev/ttyS0)\n");
      fprintf(stdout, "  devtype is the pic device to be used (12C508, 16C505, etc.)\n");
      fprintf(stdout, "  -h shows this help\n");
      fprintf(stdout, "  -s [size] shows a hash mark status bar of length [size] while erasing/writing\n");
      fprintf(stdout, "  -q sets quiet mode (excess messages supressed)\n");
      fprintf(stdout, "  -r initiates a read (Intel Hex record format)\n");
      fprintf(stdout, "  -b blank checks the requested region or regions\n");
      fprintf(stdout, "  -f ignores verify errors while writing\n");
      fprintf(stdout, "  -w writes to the requested region\n");
      fprintf(stdout, "  -e erases the requested region (flash parts only)\n");
      fprintf(stdout, "  -v shows PICSTART Plus version number\n");
      fprintf(stdout, "    p [filename] = program memory, optionally reading/writing filename\n");
      fprintf(stdout, "    c [val] = configuration bits (val is a numeric word value when writing)\n");
      fprintf(stdout, "    i [val] = ID locations\n");
      fprintf(stdout, "    d [filename] = data memory, optionally reading/writing filename\n");
      fprintf(stdout, "    o [val] = oscillator calibration space\n");
      fprintf(stdout, "    f = entire flash device (only applies to -e, erase)\n");
      fprintf(stdout, "  filename is an optional input or output file (default is stdin/stdout)\n");
      fprintf(stdout, "\n");
      fprintf(stdout, "Flags are operated on in order, from left to right.  If any operation fails,\n");
      fprintf(stdout, "further execution is aborted.  Thus, a part can be blank checked and programmed\n");
      fprintf(stdout, "with a single command, e.g.:\n");
      fprintf(stdout, "        %s /dev/ttyS0 16c505 -bp -wp program.hex \n", programName);
      fprintf(stdout, "This example will blank check the program memory of a PIC16C505 then write the\n");
      fprintf(stdout, "contents of the file program.hex to the program memory only if the blank check\n");
      fprintf(stdout, "succeeded.\n");
      fprintf(stdout, "The -wc, -wi, and -wo options must be followed by a numeric argument which\n");
      fprintf(stdout, "represents the value.  The number may be binary (preceeded by 0b or 0B), hex\n");
      fprintf(stdout, "(preceeded by 0x or 0X), or decimal (anything else).\n\n");
      ShowDevices();
}

//--------------------------------------------------------------------
// Program PICs through a serial port
int main(int argc,char *argv[])
{
      bool                    fail;
      unsigned int      baudRate;
      unsigned char     dataBits, stopBits, parity;
      bool                    done;
      char                    *flags;
      time_t                  tp;
      struct tm         *date_time;
      int                     year;
      const PIC_DEFINITION    *picDevice = 0;

      comm_debug = NULL;
      signal(SIGINT, SigHandler);                           // set up a signal handler

      programName = *argv++;                                      // name of the application
      argc--;

      fail = false;
      verboseOutput = true;                                       // be verbose unless told otherwise
      hashWidth = false;                                                // don't show hask marks by default
      ignoreVerfErr = false;                                      // by default stop on verify errors

      if (argc > 2)                                                           // need at least four arguments to do anything
      {
            if ((!strcmp(argv[0], "-c")) || (!strcmp(argv[0], "-C")))   // if first argument is '-c', debug comm line
            {
                  comm_debug = fopen("picpcomm.log", "a");
                  argc--;
                  argv++;

                  if (comm_debug)
                  {
                        time(&tp);                                            // get current time
                        date_time = localtime(&tp);         // convert to hr/min/day etc
                        year = date_time->tm_year;

                        while (year > 100)
                              year -= 100;

                        fprintf(comm_debug, "\nPicp %s comm debug file opened %02d/%02d/%02d %02d:%02d\n",
                              versionString,
                              date_time->tm_mon + 1,
                              date_time->tm_mday, year,
                              date_time->tm_hour, date_time->tm_min);
                  }
            }

            deviceName = *argv++;                                             // name of the device (probably)
            argc--;
            picName = *argv++;                                                      // name of the PIC type (probably)
            argc--;

            if (comm_debug)
                  fprintf(comm_debug, "PIC %s\n", picName);

            if ((picDevice = GetPICDefinition(picName)))                // locate the PIC type (0 = none found)
            {
                  done = false;

                  if (OpenDevice(deviceName, &serialDevice))            // open the serial device
                  {
                        baudRate = 19200;
                        dataBits = 8;
                        stopBits = 1;
                        parity = 0;

                        if (InitDevice(serialDevice, baudRate, dataBits, stopBits, parity))     // initialize the serial port
                        {
                              check_warp13();               // test if Warp-13 programmer is connected

                              if (DoGetProgrammerType())                                        // ask what kind of programmer is attached, fail if none or one we don't support
                              {
                                    if (DoGetVersion())                                               // get the version number of the PICSTART
                                    {
                                          if (DoInitPIC(picDevice))                             // try to load up the parameters for this device
                                          {
                                                while (argc && !fail)                           // do as long as we can read some more
                                                {
                                                      flags = *argv++;                                // get this argument, point to the next
                                                      argc--;

                                                      if (*flags == '-')                              // see if it's a flag
                                                      {
                                                            flags++;                                              // it is, skip the dash

                                                            switch (*flags)
                                                            {
                                                                  case 'v':
                                                                        DoShowVersion();
                                                                        break;

                                                                  case 'f':
                                                                        ignoreVerfErr = true;   // force, ignore verify error
                                                                        break;

                                                                  case 'q':
                                                                        verboseOutput = false;  // inhibit display of messages
                                                                        break;

                                                                  case 'h':
                                                                        Usage();                                  // give help
                                                                        break;

                                                                  case 's':
                                                                        if (argc && **argv != '-')          // if the next argument isn't preceeded by a '-'
                                                                        {
                                                                              fail = !atoi_base(*argv, &hashWidth);     // try to read the next argument as a number
                                                                              argv++;                                         // skip to the next argument
                                                                              argc--;

                                                                              if (fail)
                                                                                    fprintf(stderr, "Unable to interpret '%s' as a numerical value\n", *argv);
                                                                        }
                                                                        else
                                                                              hashWidth = HASH_WIDTH_DEFAULT;     // turn hash marks on to the default width

                                                                        break;

                                                                  case 'b':
                                                                  case 'r':
                                                                  case 'w':
                                                                  case 'e':
                                                                        fail = !DoTasks(&argc, &argv, picDevice, flags);      // do the requested operation
                                                                        break;

                                                                  case '\0':                                // ignore a stray dash
                                                                        break;

                                                                  default:
                                                                        fprintf(stderr, "bad argument: '%s'\n", *(argv - 1)); // back up, show the trouble spot
                                                                        break;
                                                            }
                                                      }
                                                }
                                          }
                                          else        // DoInitPIC failed
                                                fprintf(stderr, "failed to initialize %s\n", picDevice->name);
                                    }
                                    else
                                          fprintf(stderr, "failed to obtain PICSTART firmware version number\n");
                              }
                              else
                                    fprintf(stderr, "failed to connect to PICSTART Plus\n");
                        }
                        else
                              fprintf(stderr, "failed to set up the serial port\n");

                        CloseDevice(serialDevice);
                  }
                  else
                        fprintf(stderr, "failed to open device '%s'\n", deviceName);
            }
            else
            {
                  fprintf(stderr, "unrecognized PIC device type: '%s'\n", picName);       // don't know that one;
                  ShowDevices();                                                                                  // give a helpful list of supported devices
            }
      }
      else
      {
            if (argc == 1)
                  flags = argv[0];
            else
                  flags = NULL;

            if ((argc == 1 && (picDevice = GetPICDefinition(argv[0]))) ||           // locate the PIC type (0 = none found)
                  (argc == 2 && (picDevice = GetPICDefinition(argv[1]))))                 //  (be forgiving about arg position)
            {
                  ShowDeviceInfo(picDevice);
            }
            else if (flags && flags[0] == '-' && (flags[1] == 'v' || flags[1] == 'V'))
            {
                  fprintf(stdout, "\n%s: version %s, (c) 2000-2004 Cosmodog, Ltd. (http://www.cosmodog.com)\n"
                        " and Jeff Post (http://home.pacbell.net/theposts/picmicro)\n\n", programName, versionString);
            }
            else
            {
                  Usage();
                  fail = true;
            }
      }

      if (comm_debug)
      {
            fprintf(comm_debug, "\n");
            fflush(comm_debug);
            fclose(comm_debug);
      }

      return(fail);     // return 0 if okay (not failed)
}

// end of file

Generated by  Doxygen 1.6.0   Back to index