/* 
 * dspicdmp.c -- utility for dumping the contents of a dsPIC 
 *            -- chip to a COFF file using a jdm-style serial 
 *            -- port programmer
 *
 * homer reid -- 1/2005
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <ctype.h>
#include "dspicprg.h"

/***************************************************/
/* Usage message ***********************************/
/***************************************************/
char *Usage=
 "\nusage: dspicdmp [options]\n\n"
 "   Options:\n"
 "     -c xx, --chip=xx      specify dsPIC model [pic30f2010]\n"
 "     -f xx, --hexfile=xx   specify name of output file[dspicdmp.hex]\n"
 "     -h, --help            print this message\n"
 "     -p xx, --portbase=xx  specify serial port base [0x3F8]\n\n";

/***************************************************/
/* Process command-line arguments. *****************/
/***************************************************/
struct option Options[] =
{ { "chip",        1, 0, 'c' },
  { "hexfile",     1, 0, 'f' },
  { "help",        0, 0, 'h' },
  { "portbase",    1, 0, 'p' },
  { 0,0,0,0 }
};

void ProcessOptions(int argc, char *argv[])
{ int i;
  char c, buffer[100];
  char *HexFileName="dspicdmp.hex";

  while((c=getopt_long(argc, argv, "c:f:p:h", Options, 0)))
   {
     if (c==-1)
      break;

     switch(c)
      {
        /* look for chip in list of supported chips */
        case 'c':
         WhichChip=0;
         for (i=0; SupportedChips[i].Name!=0; i++)
          if ( !strcasecmp(optarg,SupportedChips[i].Name) )
           { WhichChip=&(SupportedChips[i]);
             break;
           };
         if (!WhichChip)
          { fprintf(stderr,"unknown chip specification %s\n",optarg);
            fprintf(stderr," supported chips: \n");
            for (i=0; SupportedChips[i].Name!=0; i++)
             fprintf(stderr,"  %s\n",SupportedChips[i].Name);
            exit(1);
          };
         break;

        case 'f':
         HexFileName=optarg;
         break;

        case 'p':
         PortBase=atoi(optarg);
         if ( PortBase<=0 || PortBase >= 0x400 )
          ErrExit("invalid port base %X",PortBase);
         break;

        case 'h':
        default:
         printf(Usage);
         exit(1);

      }; /* switch(c);  */
   }; /* while (c= ... ) */

  /*
   * Try to open output file.
   */
  if ((HexFile=fopen(HexFileName,"r")))
   { fclose(HexFile);
     fprintf(stderr,"File %s exists; overwrite (y/n)? ",HexFileName);
     fgets(buffer,100,stdin);
     if ( tolower(buffer[0]) !='y' ) exit(1);
   };
  if ( !(HexFile=fopen(HexFileName,"w")) )
   ErrExit("could not open file %s for writing",HexFileName);

}

/***************************************************/
/* main routine ************************************/
/***************************************************/
int main(int argc, char *argv[])
{
  DWORD Address, AddressHigh, MaxAddress;

  BYTE UBuf[16],PBuf[12];


  /* 
   * Process command-line options.
   */
  ProcessOptions(argc,argv);

  /* 
   * Initialize hardware and enter STDP mode.
   */
  HWInit();
  EnterSTDPMode();

  /*
   * Read all of code memory, 4 program words 
   * (12 bytes) at a time, and write to hex file.
   */
  bzero(UBuf,16);
  AddressHigh=0;
  WriteELARecord(0);
  MaxAddress=2*WhichChip->ProgramWords;
  for (Address=0; Address<MaxAddress; Address+=8)
   {
     /* 
      * emit an ELA record to the hex file whenever 
      * the upper word of the address increments
      */
     if ( (Address&0xFFFF0000) != AddressHigh )
      { AddressHigh=Address&0xFFFF0000;
        WriteELARecord( Address );
      };

     STDPSeq(STDP_READCODE,Address,PBuf,0);
     CodeUnpack(PBuf,UBuf); /* unpack weird byte ordering */
     WriteDataRecord(Address&0xFFFF,UBuf);
   };

  /* 
   * Read all of data eeprom and write to hex file.
   * Note that the pic30-hex file format stores 
   * each two-byte data word as a four-byte dword
   * (with upper two msbs both zero).
   */
  bzero(UBuf,16);
  /* the upper word of the address for data eeprom */
  /* is always 0x007F for all dsPIC devices        */
  WriteELARecord(0x007F0000);
  MaxAddress=0x10000;
  Address=MaxAddress - 2*WhichChip->DataWords;
  for ( ; Address<MaxAddress; Address+=8)
   {
     STDPSeq(STDP_READDATA,Address,PBuf,0);
     DataUnpack(PBuf,UBuf);
     WriteDataRecord(Address&0xFFFF,UBuf);
   };

  /* 
   * Read configuration memory and write to hex
   * file.
   */
  WriteELARecord(0x00F80000);
  STDPSeq(STDP_READCFG,0,PBuf,0);
  DataUnpack(PBuf,UBuf);
  WriteDataRecord(0,UBuf);
  DataUnpack(PBuf+8,UBuf);
  WriteDataRecord(8,UBuf);

  WriteEOFRecord();
  ExitSTDPMode();
  fclose(HexFile);
  printf("Thank you for your support.\n");
  return 0;

}