Source file: /~heha/hs/bl/msp430-usbbsl.zip/src/fileio.cpp

/* Part of msp430-usbbsl:
 * My command-line firmware download tool with hex-file support
 *
 */

#define _CRT_SECURE_NO_WARNINGS
#include "fileio.h"

/********************************
 * string <-> line in text file *
 ********************************/

int LINEFILE::getf(FILE*f) {
 char linebuf[MAX_CHAR_PER_LINE];
 if (!fgets(linebuf,sizeof(linebuf),f)) {
  if (feof(f)) return 0;		// code for EOF
  return -3;
 }
 return get(linebuf);		// returns >=1 on success
}

int LINEFILE::putf(FILE*f) {
 char linebuf[MAX_CHAR_PER_LINE];
 int e=put(linebuf);
 if (e<0) return e;
 return fputs(linebuf,f);
}

#ifdef _M_IX86

// swap bytes in word
WORD _declspec(naked) _fastcall sbw(WORD cx) {_asm{
	xchg	ecx,eax
	xchg	ah,al
	ret
}}

// swap bytes in dword
DWORD _declspec(naked) _fastcall sbd(DWORD ecx) {_asm{
	xchg	ecx,eax
	bswap	eax
	ret
}}

#else
#pragma intrinsic(_byteswap_ushort,_byteswap_ulong)
inline WORD sbw(WORD cx) {return _byteswap_ushort(cx);}
inline DWORD sbd(DWORD ecx) {return _byteswap_ulong(ecx);}
#endif
// machine specific endianness swapping (here X86)
#define BEW(x) sbw(x)	// big endian WORD (do swap)
#define LEW(x) (x)	// little endian WORD (do not swap)
#define BED(x) sbd(x)	// big endian DWORD (do swap)
#define LED(x) (x)	// little endian DWORD (do not swap)

/***********
 * helpers *
 ***********/

// returns nibble value, or outside 0..15 on error
char ToNibble(char c) {
 if (c>='@') c&=~' ';		// upcase character (maybe?)
 c-='A';
 if (c<0) {
  if (-7<c) return c;		// error
  c+=7;
 }
 return c+10;			// converted digit '0'->0, 'A'->10, 'F'->15, 'Z'->36
}

bool ToByte(const char*s,BYTE*b) {
 char n1=ToNibble(s[0]);
 if (n1&0xF0) return false;
 char n2=ToNibble(s[1]);
 if (n2&0xF0) return false;
 *b=n1<<4|n2;
 return true;
}

/**************************************
 * INTEL HEX RECORD encoding/decoding *
 **************************************/

int IHEXRECORD::get(const char*s) {
 if (*s++!=':') return IHEX_LINE_NO_COLON;	// Line must start with ':'
 int i=sizeof(all);
 BYTE cs=0, *p=all;
 do{
  if (!ToByte(s,p)) break;			// read hex numbers until end of line, or buffer full
  cs+=*p;
  s+=2;
  p++;
 }while (--i);
 i=sizeof(all)-i;				// Number of bytes read (same as "p-all")
 if (i<5) return IHEX_LINE_TOO_SHORT;		// Must be at least 10 hexadecimal characters
 if ((BYTE)*s>=' ') return IHEX_LINE_GARBAGE;	// unusable characters (should be '\n')
 if (i!=len+5) return IHEX_LINE_GARBAGE;	// too long or too short
 if (cs) return IHEX_WRONG_CHECKSUM;
 return i;
}

int IHEXRECORD::put(char*s) {
 if (len>sizeof(data)) return IHEX_LINE_GARBAGE; // Some programming error
 char*s0=s;
 *s++=':';
 int i=len+5;				// total number of bytes
 BYTE cs=0, *p=all;
 do{
  if (i==1) *p=-cs; else cs+=*p;	// poke checksum, or calculate checksum
  s+=sprintf(s,"%02X",*p);		// should return 2
  p++;
 }while (--i);
 *s++='\n';				// new line
 *s=0;
 return int(s-s0);				// positive: count of characters written
}

/***********************************
 * MOTOROLA SREC encoding/decoding *
 ***********************************/

int SRECRECORD::get(const char*s) {
 if (*s++!='S') return SREC_LINE_NO_S;	// Line must start with capital 'S' (name!)
 if ((rectype=ToNibble(*s++))&0xF0) return SREC_LINE_GARBAGE;
 int i=sizeof(all)-1;
 BYTE cs=0, *p=all+1;
 do{
  if (!ToByte(s,p)) break;			// read hex numbers until end of line, or buffer full
  cs+=*p;
  s+=2;
  p++;
 }while (--i);
 i=int(p-all);					// Number of bytes read
 if (i<5) return SREC_LINE_TOO_SHORT;		// Must be at least 9 hexadecimal characters
 if ((BYTE)*s>=' ') return SREC_LINE_GARBAGE;	// unusable characters (should be '\n')
 if (i!=len+2) return SREC_LINE_GARBAGE;	// too long or too short
 if (cs) return SREC_WRONG_CHECKSUM;
 return i;
}

int SRECRECORD::put(char*s) {
 if (len>sizeof(data)+1) return SREC_LINE_GARBAGE; // Some programming error
 char*s0=s;
 int i=len+2;				// total number of bytes
 BYTE cs=0, *p=all;
 do{
  if (i==1) *p=-cs; else cs+=*p;	// poke checksum, or calculate checksum
  s+=sprintf(s,"%02X",*p);		// should return 2
  p++;
 }while (--i);
 *s++='\n';				// new line
 *s=0;
 *s0='S';
 return int(s-s0);				// positive: count of characters written
}

/********************************************
 * Texas Instruments TEXT encoding/decoding *
 ********************************************/

int TEXTRECORD::get(const char*s) {
 switch (*s) {
  case '@': len='@'; addr=strtoul(s,(char**)&s,16); break;
  case 'q': len=0; addr=strtoul(s,(char**)&s,16); break;	// start address?
  default: {
   BYTE *p=data;
   int i=sizeof(data);
   do{
    DWORD d=strtoul(s,(char**)&s,16);
    if (d>>8) return TEXT_LINE_GARBAGE;	// too large number
    *p=LOBYTE(d);
    p++;
    if ((BYTE)*s<' ') break;
   }while(--i);
   len=int(p-data);
  }
 }
 if ((BYTE)*s>=' ') return TEXT_LINE_GARBAGE;	// unusable characters (should be '\n')
 return len;
}

int TEXTRECORD::put(char*s) {
 char*s0=s;
 switch (len) {
  case '@': sprintf(s,"@%05X",addr); break;
  case 0: sprintf(s,data[4]?"q%05X":"q",addr); break;	// EOF
  default: {
   int i=len;					// total number of bytes
   if (i>sizeof(data)) return TEXT_LINE_GARBAGE; // Some programming error
   BYTE *p=data;
   do{
    s+=sprintf(s,"%02X",*p);
    p++;
    if (i!=1) *s++=' ';
   }while(--i);
  }
 }
 *s++='\n';
 *s=0;
 return int(s-s0);				// positive: count of characters written
}

/**********************
 * INTEL HEX FILE I/O *
 **********************/

int IHEXFILEIO::Open(PCTSTR FileName,bool wr) {
 write=wr;
 havestart=false;
 start=linaddr=segaddr=hr.head=hr.cs=0;
 f=_tfopen(FileName,wr?T("w"):T("r"));
 return f?0:-2;
}

// Automatically concatenate the up-to-16-byte hex records to larger blocks up to <siz>,
// but can also dissect record data.
// Returns the number of bytes filled into <buf>, 0 at end of file
// Returns the target address (for the first byte) to <adr>
// Negative return values indicate errors.
int IHEXFILEIO::Read(BYTE*buf, int siz, DWORD&adr) {
 int ret=0;
 bool first=true;
 DWORD expect;
 while (siz) {
  while (hr.len==hr.cs) {// Used-up (i.e. emptied-out) hex record? Skip empty records.
rep:
   int e = hr.getf(f);	// read-in hex record, and return error code
   if (!e) break;	// bail out on EOF
   if (e<0) return e;	// invalid hex file structure (unrecoverable)
   hr.cs=0;		// Checksum is not needed, so use it as read-byte counter
   switch (hr.rectype) {
    case 0: break;	// data record, then take address
    case 1: if (hr.len) return IHEX_LINE_GARBAGE;
	    if (!havestart) start=BEW(hr.addr);
	    havestart=true;
	    goto rep;
    case 2: if (hr.addr || hr.len!=2 || linaddr) return IHEX_LINE_GARBAGE;
            segaddr=BEW(*(WORD*)hr.data)<<4;
	    goto rep;
    case 3: if (hr.addr || hr.len!=4 || havestart) return IHEX_LINE_GARBAGE;
	    start=(BEW(*(WORD*)hr.data)<<4)+BEW(*(WORD*)(hr.data+2));
	    havestart=true;
	    goto rep;
    case 4: if (hr.addr || hr.len!=2 || segaddr) return IHEX_LINE_GARBAGE;
            linaddr=BEW(*(WORD*)hr.data)<<16;
	    goto rep;
    case 5: if (hr.addr || hr.len!=4 || havestart) return IHEX_LINE_GARBAGE;
	    start=BED(*(DWORD*)hr.data);
	    havestart=true;
	    goto rep;
    default: return IHEX_LINE_GARBAGE;
   }
   addr=BEW(hr.addr)+segaddr+linaddr;	// build the linear address
  }
  if (first) adr=addr;
  else if (addr!=expect) break;		// bail out for address gaps
  first=false;
  int l=hr.len-hr.cs;
  if (l>siz) l=siz;
  if (!l) break;			// EOF
  memcpy(buf,hr.data+hr.cs,l);
  buf+=l;
  siz-=l;
  hr.cs+=l;
  addr+=l;
  expect=addr;
  ret+=l;
 }
 return ret;
}

// Dissects <buf> into 16-byte hex records
// but can also concatenate smaller quantities to full hex records.
int IHEXFILEIO::Write(const BYTE*buf, int siz, DWORD adr) {
 int ret=0;
 while (siz) {
  if (addr!=adr				// non-contiguous?
   || hr.len==MAX_WRITE_BYTES_PER_LINE	// or full?
   || hr.len && !(addr&0xFFFF)) {	// or full 64KB page?
   if (hr.len) {
    int e = hr.putf(f);	// flush accumulated hex record
    if (e<0) return e;
    hr.len=0;		// empty-out hex record
   }
   addr=adr;		// set address (if non-contiguous)
  }
  if (!hr.len) {
   if (linaddr!=(adr&0xFFFF0000)) {
    linaddr=adr&0xFFFF0000;
    hr.head=0x04000002;	// set header for record type 4
    *(WORD*)hr.data=BEW(HIWORD(adr));
    int e = hr.putf(f);
    if (e<0) return e;
    hr.head=0;		// clear header
   }
   hr.addr=BEW(LOWORD(addr));	// fill in new address
  }
  int l=MAX_WRITE_BYTES_PER_LINE-hr.len;	// bytes that fit into record
  if (l>siz) l=siz;
  int topageend=0x10000-LOWORD(addr);	// remaining space in this 64 KB page
  if (l>topageend) l=topageend;		// don't cross 64 KB page boundaries!
  memcpy(hr.data+hr.len,buf,l);
  buf+=l;
  siz-=l;
  hr.len+=l;
  addr=adr+=l;
  ret+=l;
 }
 return ret;
}

int IHEXFILEIO::Close() {
 int e;
 if (f) {
  if (write) {
   if (hr.len) {
    e = hr.putf(f);			// flush collected data
    if (e<0) return e;
   }
   if (HIWORD(start)) {
    hr.head=0x05000004;
    *(DWORD*)hr.data=BED(start);
   }else{
    hr.head=0x01000000|(BEW(LOWORD(start))<<8);
   }
   e = hr.putf(f);			// write EOF hex record
   if (e<0) return e;
  }
  if (fclose(f)) return -2;
 }
 f=NULL;
 return 0;
}

/**************************
 * MOTOROLA SREC FILE I/O *
 **************************/

int SRECFILEIO::Open(PCTSTR FileName,bool wr) {
 write=wr;
 havestart=false;
 start=hr.head=0;
 addrlen=2;
 f=_tfopen(FileName,wr?T("w"):T("r"));
 return f?0:-2;
}

//TODO

int SRECFILEIO::Read(BYTE*buf, int siz, DWORD&adr) {
 return -1;
}

int SRECFILEIO::Write(const BYTE*buf, int siz, DWORD adr) {
 return -1;
}

int SRECFILEIO::Close() {
 if (fclose(f)) return -2;
 return 0;
}

/***********************************
 * Texas Instruments TEXT FILE I/O *
 ***********************************/

int TEXTFILEIO::Open(PCTSTR FileName,bool wr) {
 write=wr;
 havestart=false;
 start=0;
 f=_tfopen(FileName,wr?T("w"):T("r"));
 return f?0:-2;
}

int TEXTFILEIO::Read(BYTE*buf, int siz, DWORD&adr) {
 return -1;
}

int TEXTFILEIO::Write(const BYTE*buf, int siz, DWORD adr) {
 return -1;
}

int TEXTFILEIO::Close() {
 if (fclose(f)) return -2;
 return 0;
}

/*************************************************
 * ELF (Executable and Linkable Format) file I/O *
 *************************************************/

int ELFFILEIO::Open(PCTSTR FileName,bool wr) {
 filemap=0;
 if (wr) return -2;
 FILE*f=_tfopen(FileName,T("rb"));
 if (!f) return -2;
 BYTE*map=new BYTE[256*1024];	// no fseek() allowed, so allocate large address space
 if (!map) return -2;
 DWORD flen=(DWORD)fread(map,1,256*1024,f);	// read up to 256 Kbyte data
 fclose(f);
 map=(BYTE*)realloc(map,flen);		// reduce memory consumption
 if (!map) return -2;
 if (flen<sizeof(ELFHDR)+sizeof(Proghdr32)) {	// Must have a minimum size
  delete[] map;
  return -2;
 }
 filemap=map;
 write=false;
 havestart=true;
 ELFHDR&h=*reinterpret_cast<ELFHDR*>(map);
 start=h.entry;
 phti=dataindex=0;
 return 0;
}

int ELFFILEIO::Read(BYTE*buf, int siz, DWORD&adr) {
 BYTE*map=filemap;
 if (!map) return -2;
 ELFHDR&h=*reinterpret_cast<ELFHDR*>(map);
 for(;phti<h.phnum;++phti) {
  Proghdr32&ph=*reinterpret_cast<Proghdr32*>(map+h.phoff+h.phentsize*phti);
  if (ph.type!=1) continue;	// Not loadable: skip
  int ll=ph.filesz;
  if (dataindex>=ll) {dataindex=0; continue;}	// take next loadable section
  ll-=dataindex;	// not-yet-read length
  if (ll>siz) ll=siz;	// limit to available data
  memcpy(buf,map+ph.offset+dataindex,ll);
  adr=ph.paddr+dataindex;	// Deliver target address for data chunk
  dataindex+=ll;
  return ll;	// bail out search-for-data loop
 }
 return 0;	// End-of-file reached
}

int ELFFILEIO::Close() {
 delete[] filemap;
 return 0;
}

/************************
 * Binary dump file I/O *
 ************************/

int BINFILEIO::Open(PCTSTR FileName,bool wr) {
 f=_tfopen(FileName,wr?T("wb"):T("rb"));
 if (!f) return -2;
 write=wr;
 havestart=false;
 start=0;
 return 0;	// The user must set addr to the base address before!
}

int BINFILEIO::Read(BYTE*buf, int siz, DWORD&adr) {
 int e=(int)fread(buf,1,siz,f);
 if (!e && !feof(f)) return -2;
 adr=addr;
 addr+=e;
 return e;
}

int BINFILEIO::Write(const BYTE*buf, int siz, DWORD adr) {
 if (adr<addr) return -1;		// cannot move backward here
 while (adr>addr) {
  if (fputc(0xFF,f)!=1) return -2;	// fill gaps with 0xFF
  adr++;
 }
 int e=(int)fwrite(buf,1,siz,f);	// write buffer content
 if (e!=siz) return -2;
 addr+=e;
 return e;
}

int BINFILEIO::Close() {
 if (fclose(f)) return -2;
 return 0;
}

/*********************
 * File name is data *
 *********************/

int NOFILEIO::Open(PCTSTR FileName,bool wr) {
 write=wr;
 havestart=false;
 start=0;
 return 0;
}

// input comes from file name
int NOFILEIO::Read(BYTE*buf, int siz, DWORD&adr) {
 adr=addr;
 return -1;
}

// output to stdout
int NOFILEIO::Write(const BYTE*buf, int siz, DWORD adr) {
 return -1;
}

int NOFILEIO::Close() {
 return 0;
}

/*************************************************
 * Data in a resource (internal use for RAM BSL) *
 *************************************************/
// Structure of RCDATA resoruce: Multiple chunks of:
// DWORD loadaddr
// DWORD datalen
// BYTE data[datalen]
// The last chunk with datalen==0 contains the start address for <loadaddr>

int RESIO::Open(PCTSTR FileName,bool wr) {
 write=wr;
 hRes=FindResource(0,FileName,RT_RCDATA);
 if (!hRes) return -2;
 pRes.vp=LockResource(LoadResource(0,hRes));
 if (!pRes.vp) return -2;
 rest=0;
 havestart=false;
 return 0;
}

int RESIO::Read(BYTE*buf, int siz, DWORD&adr) {
 if (siz<=0) return -1;
 if (!rest) {
  addr=*pRes.dp++;	// fetch address (always in host byte order)
  rest=*pRes.lp++;	// fetch length (0 for EOF)
  if (!rest && addr!=(DWORD)-1) {
   start=addr;
   havestart=true;
  }
 }
 adr=addr;
 if (siz>rest) siz=rest;	// take sub-block
 memcpy(buf,pRes.bp,siz);
 addr+=siz;
 pRes.bp+=siz;
 rest-=siz;
 return siz;
}

int RESIO::Close() {
 if (hRes && FreeResource(hRes)) hRes=0;
 pRes.vp=NULL;
 return 0;
}

#if 0

static FILE* file;
static unsigned int currentAddr = NO_DATA_READ;


/*******************************************************************************
*Function:    endTI_TextWrite
*Description: Writes the final 'q' to a TI TEXT file and closes it
*Parameters: 
*             none
*Returns:
*             none
*******************************************************************************/
void endTI_TextWrite()
{
  fprintf(file,"q\n");
   fclose( file );
  closeTI_Text();
}

/*******************************************************************************
*Function:    moreDataToRead
*Description: checks whether an end-of-file was hit during read
*Parameters: 
*             none
*Returns:
*             1                     if an EOF has not been hit
*             0                     if an EOF has been hit
*******************************************************************************/
int moreDataToRead()
{
  return !(currentAddr == TXT_EOF);
}

void initializeDataToRead()
{
  currentAddr = NO_DATA_READ;
}
/*******************************************************************************
*Function:    writeTI_TextFile
*Description: writes a block of data in TI TEXT format to the current file
*Parameters: 
*             DataBlock data        The DataBlock structure to write
*Returns:
*             none
*******************************************************************************/
/*
void writeTI_TextFile( DataBlock data )
{

  unsigned int bytesWritten = 0;
  if( (currentAddr == NO_DATA_READ) || (currentAddr != data.startAddr) )
  {
    fprintf(file, "@%05X\n", data.startAddr);
	currentAddr = data.startAddr;
  }
  for( bytesWritten = 0; bytesWritten < data.numberOfBytes; bytesWritten++,currentAddr++ )
  {
    fprintf(file, "%02X", data.data[bytesWritten]);
	if( ((bytesWritten+1)%16 == 0) || (bytesWritten+1 == data.numberOfBytes) )
	{
      fprintf(file, "\n");
	} // if
	else
	{
      fprintf(file, " ");
	}
  }
}
*/
/*******************************************************************************
*Function:    writeTI_TextFile
*Description: writes a block of data in TI TEXT format to the current file
*Parameters: 
*             int length                 The address of bytes
*             unsigned char *data        The array to write
*             int length                 The amount of bytes
*Returns:
*             none
*******************************************************************************/
void writeTI_TextFile( int addr, unsigned char *data, int length )
{
  int i;
   fprintf(file, "@%05X", addr);
  for( i = 0; i < length; i++)
  {
    if( i%16 == 0 )
	{
      fprintf(file, "\n");
	}
	else
	{
      fprintf(file, " ");
	}

    fprintf(file, "%02X", data[i]);
	/*
	if( i == 0 )
	{
      fprintf(file, " ");
    }
	else if( (i%16 == 0) || (i == length-1) || (i == 15))
	{
      fprintf(file, "\n");
	}
	else
	{
      fprintf(file, " ");
	}
	*/
  }
  fprintf(file, "\n");

}
/*******************************************************************************
*Function:    v
*Description: reads a certain amount of bytes from a TI TEXT file
*Parameters: 
*             int bytesToRead       The maximum number of bytes to read from the file
*Returns:
*             A Datablock structure with the requested bytes
*******************************************************************************/
DataBlock readTI_TextFile(int bytesToRead)
{
  DataBlock returnBlock;
  int bytesRead = 0;
  char string[50];
  int status;
  if( currentAddr == NO_DATA_READ )
  {
    fgets( string, sizeof string, file );
    sscanf(&string[1], "%x\n", &currentAddr);
  }
  returnBlock.startAddr = currentAddr;
  do
  {
	int stringLength=0;
	int stringPosition=0;
    status = (int)fgets( string, sizeof string, file );
	stringLength = strlen( string );
	if( status == 0 )
	{
      currentAddr = EOF;
	}
    else if( string[0] == '@' )
	{
      sscanf(&string[1], "%x\n", &currentAddr);
	  status = 0;
	}
	else if ( string[0] == 'q' || string[0] == 'Q' )
	{
      status = 0;
	  currentAddr = EOF;
	}
    else
	{
	  for( stringPosition = 0; stringPosition < (stringLength-3); stringPosition+=3 )
	  {
        sscanf( &string[stringPosition], "%2x", &returnBlock.data[bytesRead] );
	    bytesRead++;
		currentAddr++;
	  }
	}
  }
  while( (status != 0) && (bytesRead < bytesToRead) );
  returnBlock.numberOfBytes = bytesRead;
  return returnBlock;
}

#endif
Detected encoding: ASCII (7 bit)2