/* 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", ¤tAddr);
}
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", ¤tAddr);
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
Vorgefundene Kodierung: UTF-8 | 0
|