/*----------------------------------------------------------------------*
* AVRPP - AVR Parallel Programming Controller *
* *
* R0.46 (C)ChaN, 2015 Haftmann 2017 *
*----------------------------------------------------------------------*
* R0.31 Nov 11, '04 Migration from MS-DOS based AVRXP R0.25 *
* R0.32 Feb 02, '05 mega406 *
* R0.33 Feb 11, '05 90PWM2/3 *
* R0.34 Feb 15, '05 tiny25/45/85 *
* R0.35 Mar 12, '05 mega640/1280/1281/2560/2561 *
* R0.36 Aug 10, '05 tiny25/45/85 *
* R0.37 Jan 30, '06 90CAN32/64/128, -q switch *
* R0.38 Mar 15, '06 ATmega644, Fixed number of cals for tiny2313 *
* R0.39 Mar 18, '07 ATmega164P/324P/644P, ATtiny261/461/861 *
* R0.40 Aug 08, '07 ATmega48P/88P/168P/328P *
* R0.41 Dec 7, '08 ATmega325P/3250P/324PA, AT90PWM216/316 *
* R0.42 Feb 8, '10 ATtiny43U/48/88/87/167 *
* R0.42T1 Jul 9, '10 Tomkiewicz: linux, inpout32 *
* R0.43 Jul 21, '10 Supported Flash/EEPROM/Fuse combined hex files *
* R0.43b Sep 2, '10 Added -ff switch, ATtiny4313 *
* R0.43d Dec 23, '12 ATtiny1634 *
* R0.44 Jun 13, '15 ATmega48PB/88PB/168PB, ATtiny828 *
* R0.45 Jul 11, '17 ELF, Signature check, TPI, MessageBox *
* R0.46 Apr 2, '19 fuse.txt inclusion+highlight, EESAVE check *
* R0.46 July 1, '19 command-line write to flash/eeprom *
* R0.47 Dec 2, '19 corrected ELF loading: Now use Program Header *
* R0.48 Dec 5, '19 corrected hexdump screen output *
* R0.49 Jan 20, '21 corrected R0.47: -ff command writes all fuses *
* R0.50 Apr 3, '22 evaluate ELF .note.gnu.avr.deviceinfo section *
* R0.51 May 12, '22 ATmega328PB *
*----------------------------------------------------------------------*/
#include "avrpp.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef _WIN32
# include <io.h> // isatty(), fileno()
#else
# include <sys/ioctl.h>
# include <unistd.h>
#endif
const DEVPROP *Device; // Pointer to the current AVR property
static struct{
byte Fuses; // number of fuse bytes (1..3 = low,high,ext)
byte FuseMask[3]; // valid bits in up to 3 fuse bytes
}DevInfo;
/*-----------------------------------------------------------------------
Global variables (initialized by load_commands())
-----------------------------------------------------------------------*/
byte*CodeBuff; // Program code R/W buffer
byte*DataBuff; // EEPROM data R/W buffer
byte CaliBuf[4]; // Calibration bytes read buffer
byte FuseBuf[3]; // Fuse bytes read buffer
byte SignBuf[3]; // Device signature read buffer
bool outredir; // stdout stream redirected
byte termwidth=80; // width of terminal/console window, for hex/ascii dump
byte linebytes=32; // HEX line length, in output bytes (1,2,4,..,128)
/*---------- Command Parameters ------------*/
static struct{
byte fHelp; // -h (basic help), -m (more info), -d (device list)
byte fRead; // -r Read command and flags
// Bit 0: -rp Read program memory
// 1: -re Read EEPROM
// 2: (unused)
// 3: -ri Get info
// 4: -rl Read low fuse
// 5: -rh Read high fuse
// 6: -rx Read extended fuse
// 7: -rc Read calibration byte(s)
byte fOpt; // Bit 0: -e Erase device
// 1: -v Verify only
// 2: -c Copy calibration bytes into end of flash
// 3: -s Require signature in HEX/ELF file for comparison
// 4: Check signature
// 5: -w Pause before exiting program
byte fWrFuse; // Bit 0: -fl{bin} Write low fuse
// 1: -fh{bin} Write high fuse
// 2: -fx{bin} Write extended fuse
// 3: -l<bin> Write lock bits
// 4: Take default value, otherwise Fuse[0] for low fuse
// 5: Take default value, otherwise Fuse[1] for high fuse
// 6: Take default value, otherwise Fuse[2] for extended fuse
// 7: -ff Take fuse values from file
dword CodeSize;// Loaded program code size (.hex)
dword DataSize;// Loaded EEPROM data size (.eep)
byte Fuse[4]; // fuse bytes to be written {Low,High,Extended,Lock}
byte Sign[3]; // signature bytes to be checked
unsigned crystal_MHz;
}Cmd;
//--- Hardware Control ---
PORTPROP CtrlPort;
//--- Messages ---
//Indented by 7 spaces to make tabulators appearing same way in editor and shell output
const char MesUsage[]=
"AVRPP - AVR Parallel Programming tool 2022-05-12 http://elm-chan.org/\n\n"
"Write code and/or data : <elf/hex file> [<hex file>] ...\n"
"Verify code and/or data : -v <elf/hex file> [<hex file>] ...\n"
"Read code, data or fuse : -r[pef]*\n"
"Write fuse byte : -f{l|h|x}<binary>\n"
"Reset fuse byte : -f{l|h|x}\n"
"Load fuse from hex file : -ff may load lock byte too\n"
"Lock device : -l[<binary>]\n"
"Erase device : -e automatically before flashing\n"
"Copy calibration bytes : -c\n"
"Parallel port [-p1] : -p<n> hexadecimal port address may be given\n";
const char MesMore[]=
"More options : -m\n";
const char MesDevices[]=
"List supported AVRs : -d\n";
const char MoreInfo[]=
"Delay port I/O in microseconds : -i<us> default:none\n"
"Use inpout32.dll : -i\n"
"Check file's chip signature : -s if present, chip signature is checked\n"
"Short way for 8/14 pin device : -8 skip checking HVPP devices\n"
"Short for ATtiny15 : -5\n"
"Short way for 6 pin device : -6 skip ckecking HVPP and HVSP devices\n"
"Oscillator frequency : -f=<MHz> for StartUpTimer calculation\n"
"Set/modify flash/eeprom content : -w[f|e]adr=u8u8'str'u8 or u16,u16,'str',u16\n";
const char Devices[]=
"Supported AVRs:\n"
"AT90S 1200,2313,2323,2333,2343,4414,4433,4434,8515,8535\n"
"ATtiny 4,5,9,10,11,12,13,15,20,22,24,25,26,28,40,43U,\n"
" 44,45,48,84,85,87,88,167,261,441,461,841,861,2313,1634\n"
"ATmega 8,16,32,48(P),64,88(P),103,128,161,162,163,164P,165,168(P),169,323,\n"
" 324P(A),325(P),329,3250P,328(P)(B),406,603,640,644(P),1284P,645/649,\n"
" 1280,1281,2560,2561,3250/3290,6450/6490,8515,8535\n"
"AT90CAN32,64,128, AT90PWM 2,3,216,316\n";
static dword FlashSize(const DEVPROP*Device) {
byte b=Device->FlashPageSHL,s=Device->FlashPageS;
return b<16?1<<(b+s):b<<s; // in one case, ATmega406, is b==80
}
// Output the device information
static void output_deviceinfo() {
putchar('\n');
printf("Device Signature = 1E-%02x-%02X",Device->Sign[0],Device->Sign[1]);
putchar('\n');
dword s=FlashSize(Device);
dword p=1<<Device->FlashPageS;
printf("Flash Memory Size = %d bytes",s);
if (p>1) printf(" (%d bytes x %d pages)",p,s/p);
putchar('\n');
if (Device->EepromSizeS) {
s=1<<Device->EepromSizeS;
p=1<<Device->EepromPageS;
printf("EEPROM Size\t = %d bytes",s);
if (p>1) printf(" (%d bytes x %d pages)",p,s/p);
putchar('\n');
}
}
// Win32 Console vs. DOS/Linux ANSI sequences for output colorization
// Windows 10 can support ANSI sequences too, important for underline etc. later
#ifdef _WIN32
static HANDLE hStdOut;
#else
static void colorize(int c) {printf("\33[1;%dm",c);}
#endif
static void cyan() {
#ifdef _WIN32
SetConsoleTextAttribute(hStdOut,FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
#else
colorize(36);
#endif
}
static void green() {
#ifdef _WIN32
SetConsoleTextAttribute(hStdOut,FOREGROUND_GREEN|FOREGROUND_INTENSITY);
#else
colorize(32);
#endif
}
static void yellow() {
#ifdef _WIN32
SetConsoleTextAttribute(hStdOut,FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY);
#else
colorize(33);
#endif
}
static void hilite() {
#ifdef _WIN32
SetConsoleTextAttribute(hStdOut,FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
#else
fputs("\33[1m",stdout);
#endif
}
static void normal() {
#ifdef _WIN32
SetConsoleTextAttribute(hStdOut,FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
#else
fputs("\33[m",stdout);
#endif
}
// Output a fuse byte and description
// NEW: No dependency to fuse.txt anymore
// Highlighted non-default fuse bits
// Current fuse selection shown, in cyan
static void put_fuseval(int i) {
const FUSEPROP&f=FuseProps[Device->fusepropidx];
const byte*p=f.ids+(i<<3);
static const char*head[]={"Low: ","High:","Ext: "};
fputs(head[i], stdout);
byte val=FuseBuf[i];
byte def=f.FuseDefault[i];
for (int n=8;--n>=0;) {
if ((val^def)&0x80) hilite();
putchar(p[n]? val & 0x80 ? '1' : '0' : '-');
if ((val^def)&0x80) normal();
val<<=1; def<<=1;
}
putchar('\n');
for (int w,b=0;b<8;b+=w) {
w=1;
byte j=p[b]; // load fuse type
if (j) {
int c;
for (c=b+1;c<8;c++) if (p[c]==j) ++w; else break; // count (adjanced) bits for fuse type <j>
for (c=0;c<5;c++) putchar(' ');
// BUGBUG: Depending on terminal font, Windows accepts UTF-8 characters or OEM characters for line drawing.
#ifdef _WIN32
for (c=8;--c>=b+w;) putchar(p[c]?0xB3:' ');
putchar(0xC0);
for (;--c>=b;) putchar(0xC1);
for (;--c>=-2;) putchar(0xC4);
#else
for (c=8;--c>=b+w;) fputs(p[c]?"│":" ",stdout);
fputs("â””",stdout);
for (;--c>=b;) fputs("â”´",stdout);
for (;--c>=-2;) fputs("─",stdout);
#endif
putchar(' ');
const char*q=FuseNames[j]; // string: "Name <space> Description with [|] alternatives"
while (*q!=' ') putchar(*q++); // name of the fuse
if (w>1) {
putchar('[');
putchar('0'+w); // bit width of fuse cluster
putchar(']');
}
putchar(':');
const char*qb=strchr(q,'['); // begin of "[a|b|c]" selection
if (qb) {
const char*qw=qb+1; // begin of first word ("a")
const char*qe=strchr(qw,']'); // end of selection
if (qe) {
val=(~FuseBuf[i]>>b)&(1<<w)-1; // fuse cluster, inverted
const char*qn;
do{
qn=strchr(qw,'|');
if (!qn || qn>qe) qn=qe; // end of word
if (!val) { // zero reached?
while(q!=qb) putchar(*q++); // before "["
cyan();
while(qw!=qn) putchar(*qw++); // the right selection
normal();
q=qe+1; // after "]"
goto e;
}
qw=qn+1;
--val; // count down possibilities
}while (*qn!=']');
}
}
// TODO: Calculate SUT (StartUpTimer) fuse values.
// Practical values heavily depend on CKSEL fuse settings and crystal frequency,
// so having a clue about crystal frequency (-rq Mhz) would be helpful
e: puts(q);
}
}
}
// Output fuse bytes and calibration byte
static void output_fuse(byte fRead) {
MESS("\n");
for (int i=0; i<DevInfo.Fuses; i++) if (fRead&0x10<<i) put_fuseval(i);
/* Output calibration values */
const FUSEPROP&f=FuseProps[Device->fusepropidx];
if (fRead&0x80) if (f.Cals) {
fputs("Cal:", stdout);
for (int n=0; n<f.Cals; n++)
printf(" %d", CaliBuf[n]);
putchar('\n');
}
}
// Store bytes into data buffer
// addr = Physical Memory Address (linearized)
void store_buffer(const byte*dat,dword count,dword addr) {
if (addr>=BASE_FLASH && addr+count<=BASE_FLASH+MAX_FLASH) {
addr-=BASE_FLASH;
memcpy(CodeBuff+addr,dat,count);
addr+=count;
if (Cmd.CodeSize<addr) Cmd.CodeSize=addr;
return;
}
if (addr>=BASE_EEPROM && addr+count<=BASE_EEPROM+MAX_EEPROM) {
addr-=BASE_EEPROM;
memcpy(DataBuff+addr,dat,count);
addr+=count;
if (Cmd.DataSize<addr) Cmd.DataSize=addr;
return;
}
if (addr>=BASE_SIGNATURE && addr+count<=BASE_SIGNATURE+MAX_SIGNATURE) {
addr-=BASE_SIGNATURE;
for(;count;addr++,count--) Cmd.Sign[2-addr]=dat[addr];
// WinAVR aka avr-gcc signature array is reversed, for unknown reason
Cmd.fOpt|=0x10;
return;
}
if (!(Cmd.fWrFuse&0x80)) return; // load from file?
if (addr>=BASE_LOCK && addr+count<=BASE_LOCK+MAX_LOCK) {
addr=3;
goto locklikefuse;
}
if (addr>=BASE_FUSE && addr+count<=BASE_FUSE+MAX_FUSE) {
addr-=BASE_FUSE;
locklikefuse:
memcpy(Cmd.Fuse+addr,dat,count);
Cmd.fWrFuse|=((1<<count)-1)<<addr;
return;
}
}
// Single-byte version
void store_buffer(byte b,dword a) {store_buffer(&b,1,a);}
// Either load ELF or HEX file, return error line number
static int32 loadfile(const byte*fdata,int32 fsize) {
if (loadelf(fdata,fsize)) {
Deviceinfo di;
if (di.load(fdata,fsize)) {
di.check();
}
return 0;
}
return loadhex((const char*)fdata,fsize);
}
// Put an Intel Hex data block to file
static void put_hexline (
const byte*buffer, // pointer to data buffer
word ofs, // block offset address
byte count, // data byte count
byte type=0 // block type
) {
printf(":%02X%04X%02X",count,ofs,type); // Byte count, Offset address and Record type
byte sum = count + (ofs >> 8) + ofs + type;
for (byte b=0;b<count;b++) { // Data bytes
printf("%02X",*buffer);
sum += *buffer++;
}
printf("%02X\n",(byte)-sum); // Check sum
}
static byte memAnd(const byte*buf,int len,byte start=0xFF) {
if (len) do start&=*buf++; while(--len);
return start;
}
static int findFirstProgrammed(const byte*buf,int len) {
int i; for(i=0;i<len;i++) if (buf[i]!=0xFF) break; return i;
}
// Returns index beyond last non-0xFF byte in buffer.
static int findLastProgrammed(const byte*buf,int len) {
while (--len>=0) if (buf[len]!=0xFF) break;
return ++len;
}
// Output data buffer in Intel Hex format to file
static void put_hex(
const byte*buf, // pointer to data buffer
int siz, // number of bytes to be output
int adr=0 // start address (32 bit)
) {
int len = findLastProgrammed(buf,siz);
while (len) {
if (adr && !(adr&0xFFFF)) { // New 64K segment?
byte data[2]={0,byte(adr>>16)}; // 0x10000 will be "0001"
put_hexline(data,0,2,4);
}
int l=linebytes;
if (l>len) l=len; // full or possibly partial data block
if ((adr+l-1^adr)&0xFFFF0000) l=-adr&0xFFFF; // don't straddle 64K boundary
if (memAnd(buf,l)!=0xFF) put_hexline(buf,adr,l); // Don't emit erased memory
buf+=l;
adr+=l;
len-=l;
}
}
// Display Hex/ASCII dump data line to console, colored
static void output_hex (
const byte*buffer, // pointer to data buffer
int adr, // address to show
int count, // data byte count, must be even when wordwise set
int alen, // address field width
bool wordwise
) {
cyan(); // Address
printf("%0*X",alen,adr);
green(); // Hex Dump
for (int b=0;b<count;b+=1+wordwise) {
putchar(' ');
if (wordwise) printf("%04X",*(word*)(buffer+b));
else printf("%02X",buffer[b]);
}
int spaces=2+(wordwise?((linebytes-count)>>1)*5:(linebytes-count)*3);
if (spaces<1) spaces=1;
do putchar(' '); while (--spaces);
yellow(); // ASCII dump (no UTF-8 detection yet)
for (int i=0;i<count;i++) {
byte c=*buffer++;
putchar(c>=' '&&c<0x7F?c:'.');
}
normal();
putchar('\n');
}
// Display (Flash or) EEPROM data to console, colored
static void output_data(
const byte*buf, // pointer to data buffer
int siz, // number of bytes to be output
bool wordwise=false // normally in bytes
) {
int len = findLastProgrammed(buf,siz);
if (wordwise && len&1) len++; // round up length to be even
int adr = findFirstProgrammed(buf,len);
adr&=~(linebytes-1); // round down to start on “even” start address
if (!len) {puts("== Empty =="); return;}
len-=adr; buf+=adr;
printf("== Fill: %d %% ==\n",len*100/siz);
// Calculate space needed for address column, based on device's size, not fill
int alen=0; for(--siz;siz;siz>>=4) alen++;
while (len) {
int l=linebytes;
if (l>len) l=len; // full or possibly partial data block
output_hex(buf,adr,l,alen,wordwise);
buf+=l;
adr+=l;
len-=l;
}
}
static void output_code(const byte*buf,int siz) {
output_data(buf,siz,true); // later: disassembly!
}
static bool initBuffers() {
if (!CodeBuff) {
CodeBuff=new byte[MAX_FLASH];
if (!CodeBuff) {
fprintf(stderr,"memory allocation (%d) failed.\n",MAX_FLASH);
return false;
}
memset(CodeBuff,0xFF,MAX_FLASH);
}
if (!DataBuff) {
DataBuff=new byte[MAX_EEPROM];
if (!DataBuff) {
fprintf(stderr,"memory allocation (%d) failed.\n",MAX_EEPROM);
return false;
}
memset(DataBuff,0xFF,MAX_EEPROM);
}
return true;
}
/*-----------------------------------------------------------------------
Device control functions
-----------------------------------------------------------------------*/
// get address of memory region in von-neumannized TPI address space
static word nvmAddress(adsp src, word addr) {
// FLASH EEPROM FUSE LOCK SIGNAT CALIBS
static word offsets[]={0x4000, 0x2000, 0x3F40, 0x3F00, 0x3FC0, 0x3F80};
if (src==FUSE && addr==3) {src=LOCK; addr=0;}
return addr+offsets[src&7];
}
// set address for TPI devices
static bool nvmSetAddress(word lin) {
return tpiSend(0x68,LOBYTE(lin)) // SSTPRL, Send Store Pointer Low
&& tpiSend(0x69,HIBYTE(lin)); // SSTPRH, Send Store Pointer High
}
// Read a byte from device
// !! Data is expected to be read consecutive, starting from address 0
static byte read_byte (adsp src, // read from.. FLASH/EEPROM/SIGNATURE/CALIBS/FUSE
dword adr) // byte address
{
byte s;
switch (CtrlPort.Mode) {
case 0: switch (src) { // Parallel Mode
case FLASH :
if (adr & 1) return rcv_byte(BS_1);
adr >>= 1;
if (!adr) set_byte(XA_1, C_RD_PRG);
if (!(adr&0xFFFF) && FlashSize(Device)>128*1024) set_byte(BS_2, (byte)(adr >> 16));
if (!LOBYTE(adr)) set_byte(BS_1,HIBYTE(adr));
set_byte(0, (byte)adr);
return rcv_byte(0);
case EEPROM :
if (!adr) set_byte(XA_1, C_RD_EEP);
if (!(adr&0xFF)) set_byte(BS_1, (byte)(adr >> 8));
set_byte(0, (byte)adr);
return rcv_byte(0);
case SIGNATURE :
set_byte(XA_1, C_RD_SIG);
set_byte(0, (byte)adr);
return rcv_byte(0);
case CALIBS :
set_byte(XA_1, C_RD_SIG);
set_byte(0, (byte)adr);
return rcv_byte(BS_1);
case FUSE :
set_byte(XA_1, C_RD_FB);
switch (adr) {
case 2 : s = XA_1 | BS_2; break;
case 1 : s = XA_1 | BS_2 | BS_1; break;
default : s = DevInfo.Fuses ? 0 : BS_1;
}
return rcv_byte(s);
}break;
case 1:
case 2: switch (src) { // HVS Mode
case FLASH :
if (!adr) xfer8(I_LDCMD, C_RD_PRG);
if (adr&1) {
xfer8(I_RDLH1, 0);
return xfer8(I_RDLH2, 0);
}
if (!(adr&0x1FF)) xfer8(I_LDAH, (byte)(adr >> 9));
xfer8(I_LDAL, (byte)(adr >> 1));
xfer8(I_RDLL1, 0);
return xfer8(I_RDLL2, 0);
case EEPROM :
if (!adr) xfer8(I_LDCMD, C_RD_EEP);
if (!(adr&0xFF)) xfer8(I_LDAH, (byte)(adr >> 8));
xfer8(I_LDAL, (byte)adr);
xfer8(I_RDLL1, 0);
return xfer8(I_RDLL2, 0);
case SIGNATURE :
xfer8(I_LDCMD, C_RD_SIG);
xfer8(I_LDAL, (byte)adr);
xfer8(I_RDLL1, 0);
return xfer8(I_RDLL2, 0);
case CALIBS :
xfer8(I_LDCMD, C_RD_SIG);
xfer8(I_LDAL, (byte)adr);
xfer8(I_RDLH1, 0);
return xfer8(I_RDLH2, 0);
case FUSE :
xfer8(I_LDCMD, C_RD_FB);
switch (adr) {
case 2 : /* Extended */
xfer8(I_RDHL1, 0);
return xfer8(I_RDHL2, 0);
case 1 : /* High */
xfer8(I_RDHH1, 0);
return xfer8(I_RDHH2, 0);
default : /* Low */
if (DevInfo.Fuses) {
xfer8(I_RDLL1, 0);
return xfer8(I_RDLL2, 0);
}
xfer8(I_RDLH1, 0);
return xfer8(I_RDLH2, 0);
}
}break;
case 3: {
word lin=nvmAddress(src,word(adr));
if (!adr) nvmSetAddress(lin);
return tpiRecv(0x24); // SLD+, Send Load and Increment (Pointer)
}break;
}
return 0xFF;
}
// Write a byte into memory, for non-paged memory areas
// !! Data is expected to be written consecutive, starting from address 0
static int write_byte (
adsp dst, // write to.. FLASH/EEPROM
dword adr, // byte address
byte wd // data to be written
) {
switch (CtrlPort.Mode) {
case 0: switch (dst) { /* Parallel Mode */
case FLASH :
if (!(adr&1)) {
if (!adr) set_byte(XA_1, C_WR_PRG);
if (!(adr&0x1FF)) set_byte(BS_1, (byte)(adr >> 9));
set_byte(0, (byte)(adr >> 1));
}
if (wd == 0xFF) return 1; /* Skip if the value is 0xFF */
set_byte(XA_0, wd);
stb_wr((byte)(adr & 1 ? BS_1 : 0), 0);
break;
case EEPROM :
if (!adr) set_byte(XA_1, C_WR_EEP);
if (!(adr&0xFF)) set_byte(BS_1, (byte)(adr >> 8));
set_byte(0, (byte)adr);
set_byte(XA_0, wd);
stb_wr(0, 0);
break;
}break;
case 1:
case 2: switch (dst) { /* HVS mode */
case FLASH :
if (!adr) xfer8(I_LDCMD, C_WR_PRG);
if (!(adr&1)) {
if (!(adr&0x1FF)) xfer8(I_LDAH, (byte)(adr >> 9));
xfer8(I_LDAL, (byte)(adr >> 1));
if (wd == 0xFF) return 1; /* Skip if the value is 0xFF */
xfer8(I_LDDL, wd);
xfer8(I_WRLL1, 0);
xfer8(I_WRLL2, 0);
}else{
if (wd == 0xFF) return 1; /* Skip if the value is 0xFF */
xfer8(I_LDDH, wd);
xfer8(I_WRLH1, 0);
xfer8(I_WRLH2, 0);
}
break;
case EEPROM :
if (!adr) xfer8(I_LDCMD, C_WR_EEP);
if (!(adr&0xFF)) xfer8(I_LDAH, (byte)(adr >> 8));
xfer8(I_LDAL, (byte)adr);
xfer8(I_LDDL, wd);
xfer8(I_WRLL1, 0);
xfer8(I_WRLL2, 0);
break;
}break;
case 3: { // TPI mode
word lin=nvmAddress(dst,word(adr));
if (dst && !adr) { // erase before write
if (!tpiSend(0xF3,0x14) // SOUT(1aa1aaaa) NVMCMD(0x33): Section Erase
|| !nvmSetAddress(lin+1)
|| !tpiSend(0x60,0) // SST+, Send Store and Increment (Pointer)
|| !wait_ready())
return false;
}
if (!(adr&1)) {
tpiSend(0xF3,0x1D); // SOUT(1aa1aaaa) NVMCMD(0x33): Word_Write
}
if (!adr) nvmSetAddress(lin);
tpiSend(0x64,wd); // SST+, Send Store and Increment (Pointer)
if (!(adr&1)) return 1; // don't wait for completion
}break;
}
return wait_ready(); /* Wait for end of internal process */
}
// Write a page into memory
static int write_page (
adsp dst, /* write to.. FLASH/EEPROM */
dword adr, /* byte address (must be page boundary) */
const byte *wd // pointer to the page data
) {
int n;
switch (CtrlPort.Mode) {
case 0: switch (dst) { /* Parallel Mode */
case FLASH: {
byte d=0xFF; /* Skip page if all data in the page are 0xFF */
for (n=0; n<1<<Device->FlashPageS; n++) d&=wd[n];
if (d == 0xFF) return 1;
set_byte(XA_1, C_WR_PRG);
for (n=0; n<1<<Device->FlashPageS; n+=2) {
set_byte(0, (byte)((adr + n) >> 1));
set_byte(XA_0, wd[n]);
set_byte(XA_0 | BS_1, wd[n+1]);
stb_pagel();
}
if (FlashSize(Device)>(128*1024)) set_byte(BS_2, (byte)(adr >> 17));
set_byte(BS_1, (byte)(adr >> 9));
stb_wr(0, 0);
}break;
case EEPROM: {
set_byte(XA_1, C_WR_EEP);
int e = 1<<Device->EepromPageS;
for (n = 0; n < e; n++) {
set_byte(0, (byte)(adr + n));
set_byte(XA_0, wd[n]);
stb_pagel();
}
set_byte(BS_1, (byte)(adr >> 8));
stb_wr(0, 0);
}break;
}break;
case 1:
case 2: switch (dst) { /* HVS mode */
case FLASH: {
byte d=0xFF; /* Skip page if all data in the page are 0xFF */
int e = 1<<Device->FlashPageS;
for (n=0; n < e; n++) d&=wd[n];
if (d == 0xFF) return 1;
xfer8(I_LDCMD, C_WR_PRG);
for (n=0; n < e; n+=2) {
xfer8(I_LDAL, (byte)((adr + n) >> 1));
xfer8(I_LDDL, wd[n]);
xfer8(I_LDDH, wd[n+1]);
xfer8(I_PSTH1, 0);
xfer8(I_PSTH2, 0);
}
xfer8(I_LDAH, (byte)(adr >> 9));
xfer8(I_WRLL1, 0);
xfer8(I_WRLL2, 0);
}break;
case EEPROM: {
xfer8(I_LDCMD, C_WR_EEP);
int e = 1<<Device->EepromPageS;
for (n=0; n < e; n++) {
xfer8(I_LDAL, (byte)(adr + n));
xfer8(I_LDDL, wd[n]);
xfer8(I_PSTL1, 0);
xfer8(I_PSTL2, 0);
}
xfer8(I_LDAH, (byte)(adr >> 8));
xfer8(I_WRLL1, 0);
xfer8(I_WRLL2, 0);
}break;
}break;
}
return wait_ready(); /* Wait for end of internal process */
}
/* Write Fuse or Lock byte */
static int write_fuselock (
int dst, /* write to... F_LOCK/F_LOW/F_HIGH/F_EXTEND */
byte val /* byte value to be written */
) {
const FUSEPROP&f=FuseProps[Device->fusepropidx];
switch (CtrlPort.Mode) {
case 0: /* Parallel Mode */
if (dst==3) { // Device Lock byte
set_byte(XA_1, C_WR_LB);
set_byte(XA_0, val);
stb_wr(0, 0);
}else{
set_byte(XA_1, C_WR_FB);
set_byte(XA_0, val);
switch (dst) {
case 0: // Fuse Low byte
stb_wr(0, f.FuseWait);
break;
case 1: // Fuse High byte
stb_wr(BS_1, f.FuseWait);
break;
case 2: // Fuse Extend byte
stb_wr(XA_1 | BS_2, f.FuseWait);
}
}break;
case 1:
case 2: /* HVS mode */
if (dst==3) { // Device Lock byte
xfer8(I_LDCMD, C_WR_LB);
xfer8(I_LDDL, val);
xfer8(I_WRLL1, 0);
xfer8(I_WRLL2, 0);
}else{
xfer8(I_LDCMD, C_WR_FB);
xfer8(I_LDDL, val);
switch (dst) {
case 0: // Fuse Low byte
xfer8(I_WRLL1, 0);
delay_ms(f.FuseWait);
xfer8(I_WRLL2, 0);
break;
case 1: // Fuse High byte
xfer8(I_WRLH1, 0);
xfer8(I_WRLH2, 0);
break;
case 2: // Fuse Extended byte
xfer8(I_WRHL1, 0);
xfer8(I_WRHL2, 0);
break;
}
}break;
case 3:{
if (dst==3) {
write_byte(LOCK,0,val);
return write_byte(LOCK,1,0xFF);
}else{
write_byte(FUSE,0,val);
return write_byte(FUSE,1,0xFF);
}
}break;
}
return wait_ready(); /* Wait for end of internal process */
}
// Chip erase
static int erase_memory() {
const FUSEPROP&f=FuseProps[Device->fusepropidx];
switch (CtrlPort.Mode) {
case 0: { /* Parallel Mode */
set_byte(XA_1, C_ERASE);
stb_wr(0, f.EraseWait);
}break;
case 1:
case 2: { /* HVS mode */
xfer8(I_LDCMD, C_ERASE);
xfer8(I_WRLL1, 0);
xfer8(I_WRLL2, 0);
delay_ms(f.EraseWait);
if (!Device->EepromPageS) xfer8(I_LDCMD, C_NOP);
}break;
case 3: {
tpiSend(0xF3,0x10); // SOUT(1aa1aaaa) NVMCMD(0x33): Chip_Erase
nvmSetAddress(nvmAddress(FLASH,1));
tpiSend(0x60,0xFF); // SST, Send Store (Pointer): Dummy Write
}break;
}
return wait_ready();
}
/* Initialize control port */
static bool initialize_port() {
// Open interface port and check status
switch (open_ifport()) { // Main result code (error status)
case RES_DRVFAIL:
#ifdef WIN32
MESS(CtrlPort.inpout32?"INPOUT32":"GIVEIO");
MESS(" initialization failed.\n");
if (!CtrlPort.inpout32) MESS ("Try -i switch using InpOut32.dll!\n");
#else
MESS("ioperm() failed. Use 'sudo' to access hardware!\n");
#endif
break;
case RES_BADENV:
MESS("Unsupported environment (mostly too old hardware or operating system).\n");
break;
case RES_NOPORT:
fprintf(stderr, "No LPT port at 0x%X.\n",CtrlPort.PortAddr);
#ifdef WIN32
MESS("Refer to Device Manager (run 'devmgmt.msc') for correct port address!\n");
#endif
break;
case RES_NOADAPTER:
fprintf(stderr, "Programmer is not attached on LPT port 0x%X.\n", CtrlPort.PortAddr);
break;
case RES_OPENED:
return true;
}
return false;
}
#ifndef WIN32
enum querytype {MB_OK,MB_OKCANCEL,MB_ABORTRETRYIGNORE,MB_YESNOCANCEL,MB_YESNO,MB_RETRYCANCEL,
MB_ICONHAND=0x10,MB_ICONQUESTION=0x20,MB_ICONEXCLAMATION=0x30,MB_ICONASTERISK=0x40};
enum queryresult {IDNULL,IDOK,IDCANCEL,IDABORT,IDRETRY,IDIGNORE,IDYES,IDNO};
#endif
// Normally, let the user hit ENTER.
// However, this isn't possible for most IDEs (Notepad++, Programmers Notepad) output window.
// Therefore, if stdin is not connected to console, use MessageBox()
static int query(const char*msg,int type) {
#ifdef WIN32
if (outredir) {
return MessageBox(0,msg,"avrpp",type);
}
#endif
MESS(msg);
switch (int c=getchar()) {
case '\r':
case '\n': return IDOK;
case 'n': return IDNO;
case 'y': return IDYES;
case 'i': return IDIGNORE;
case 'r': return IDRETRY;
case 'a': return IDABORT;
case 27: return IDCANCEL;
default: return c;
}
}
static const DEVPROP*findSignature(byte sign[3]) {
if (sign[0]!=0x1E) return 0; // Must be ATMEL
for (const DEVPROP*dev=DevLst;dev->Name[0];dev++)
if (!memcmp(sign+1,dev->Sign,2)) return dev;
return 0;
}
// Initialize control port and detect device type
static int init_devices() {
dword adr;
const char *const DetMode[] = {"PAR","HVS","HVS15","TPI"};
/* Execute initialization if not initialized yet */
if (Device) return 0;
if (!initialize_port()) return RC_INIT;
const char*name="a device"; // Give a hint that the desired chip name is already known,
const DEVPROP*dev=findSignature(Cmd.Sign); // either by given signature in ELF/HEX file,
if (dev) name=dev->Name; // or by .note.gnu.avr.deviceinfo section in ELF file
char buf[128];
sprintf(buf,"Put %s%s on the socket and type Enter...",dev?"AT":"",name);
if (query(buf,MB_OK|MB_ICONASTERISK)!=IDOK) return RC_FAIL;
for (; CtrlPort.Mode<4; CtrlPort.Mode++) {
power_on();
// read device signature
for (adr=0; adr<3; adr++) SignBuf[adr] = read_byte(SIGNATURE, adr);
// search device table
if (Device=findSignature(SignBuf)) goto found;
power_off();
fprintf(stderr, "%s->Unknown device (%02X-%02X-%02X).\n",
DetMode[CtrlPort.Mode],SignBuf[0],SignBuf[1],SignBuf[2]);
delay_ms(50);
}
return RC_DEV; // Failed to detect device type
found: // Show the device name
fprintf(stderr, "%s->Detected device is AT%s.\n",
DetMode[CtrlPort.Mode],Device->Name);
// Fill DevInfo structure with information
const FUSEPROP&f = FuseProps[Device->fusepropidx];
for (int i=0; i<24; i++) if (f.ids[i]) {
DevInfo.FuseMask[i>>3]|=1<<(i&7);
DevInfo.Fuses=(i>>3)+1;
}
return 0;
}
/* Read fuse bytes and calibration bytes into buffer */
static void read_fusecal(byte fRead) {
for (int i=0;i<DevInfo.Fuses;i++)
if (fRead&0x10<<i) FuseBuf[i]=read_byte(FUSE,i);
const FUSEPROP&f = FuseProps[Device->fusepropidx];
if (fRead&0x80) for (int a=0;a<f.Cals;a++)
CaliBuf[a]=read_byte(CALIBS,a);
}
/*-----------------------------------------------------------------------
Programming functions
-----------------------------------------------------------------------*/
// Get bit number for EESAVE bit for this device
static int find_eesave(byte id=3) { // ID=3 is EESAVE bit
const FUSEPROP&f = FuseProps[Device->fusepropidx];
const byte*p=f.ids;
const byte*q=(const byte*)memchr(p,id,3<<3);
if (q) return int(q-p); // This AVR has EESAVE bit there
return -1;
}
static int erase_avr() {
// Check the need for erasing EEPROM
// 1. If any fuse given, reset EESAVE fuse first
if (Cmd.fWrFuse&0x07) { // Fuse given either by command line or by -ff and valid file data
int bit=find_eesave();
if (bit>=0) {
int byt=bit>>3; bit&=7;
read_fusecal(0x10<<byt); // Read only the one needed fuse byte
if (!(FuseBuf[byt]&1<<bit)) { // EESAVE programmed (= zero)
MESS("Reset EESAVE...");
FuseBuf[byt]|=1<<bit;
write_fuselock(byt,FuseBuf[byt]);
}
}
}
MESS("Erase..."); // Erase device before programming
return erase_memory();
}
// -e command
static int erase_device() {
int rc;
if (rc=init_devices()) return rc;
if (!erase_avr()) {
MESS("Failed.\n");
return RC_FAIL;
}
MESS("Erased.\n");
return 0;
}
// -r command
static int read_device(byte fRead) {
dword adr,siz;
int rc;
if (rc=init_devices()) return rc;
if (fRead&8 && !outredir) output_deviceinfo();
if (fRead&1) { // -rp : read program memory
siz=FlashSize(Device);
MESS("Read Flash...");
if (!CodeBuff) CodeBuff=new byte[siz];
for (adr = 0; adr < siz; adr++)
CodeBuff[adr] = read_byte(FLASH, adr);
MESS("Okay.\n");
if (outredir) put_hex(CodeBuff,siz);
else output_code(CodeBuff,siz);
}
if (fRead&2) { // -re : read eeprom
siz=1U<<Device->EepromSizeS;
if (siz==1) {
MESS("No EEPROM.\n");
}else{
MESS("Read EEPROM...");
if (!DataBuff) DataBuff=new byte[siz];
for (adr = 0; adr < siz; adr++)
DataBuff[adr] = read_byte(EEPROM, adr);
MESS("Okay.\n");
if (outredir) put_hex(DataBuff,siz,fRead==2?0:0x810000); // Don't emit address for single section
else output_data(DataBuff,siz);
}
}
if (fRead&0xF0) { // -rf : read fuses and cals
read_fusecal(fRead);
if (outredir) put_hex(FuseBuf,DevInfo.Fuses,0x820000);
else output_fuse(fRead);
}
if (outredir) {
if (fRead&8) {
byte data[]={Device->Sign[1],Device->Sign[0],0x1E};
put_hex(data,3,0x840000);
}
put_hexline(NULL,0,0,1); // End block
}
return 0;
}
static bool check_signature() {
if (Cmd.fOpt&0x10 // HEX/ELF file didn't contain signature (old style): Don't check
&& memcmp(Cmd.Sign,SignBuf,3)) { // File Signature matches Device Signature: Okay
char buf[128];
sprintf(buf,"File signature (AT%s) doesn't match to device signature (AT%s)! Continue?",
findSignature(Cmd.Sign)->Name,
findSignature(SignBuf)->Name);
return query(buf,MB_YESNO|MB_ICONQUESTION)==IDYES;
}else return true;
}
// TODO: Memory sizes will not be checked for now.
bool Deviceinfo::check() {
if (Cmd.fOpt&0x10) { // ELF file contains signature? Compare!
const char*a=findSignature(Cmd.Sign)->Name;
if (!strcasecmp(a,device_name)) return true; // good
char buf[128];
sprintf(buf,"File signature (AT%s) doesn't match to File DeviceInfo section (AT%s)!",
a,device_name);
return query(buf,MB_OK|MB_ICONEXCLAMATION),false;
}else{ // ELF file doesn't contain signature: Take from table
for (const DEVPROP*dev=DevLst;dev->Name[0];dev++) {
if (!strcasecmp(dev->Name,device_name)) { // name found
Cmd.fOpt|=0x10;
Cmd.Sign[0]=0x1E; // Atmel
Cmd.Sign[1]=dev->Sign[0];
Cmd.Sign[2]=dev->Sign[1];
return true;
}
}
char buf[128];
sprintf(buf,"File DeviceInfo section contains unknown chip name (AT%s)!",
device_name);
return query(buf,MB_OK|MB_ICONEXCLAMATION),false;
}
}
/* .hex files write command */
static int write_flash() {
dword adr;
byte rd;
int rc, n;
const FUSEPROP&f = FuseProps[Device->fusepropidx];
if (rc = init_devices()) return rc;
MESS("Flash: ");
if (Cmd.CodeSize > FlashSize(Device)) {
MESS("error: program size > memory size.\n");
return RC_FAIL;
}
Cmd.CodeSize = FlashSize(Device);
if (!check_signature()) return RC_FAIL;
if (!(Cmd.fOpt&0x02)) { // -v : Skip programming process when verify mode
if (!erase_avr()) {
MESS("Failed.\n");
return RC_FAIL;
}
if (Cmd.fOpt&0x04 && f.Cals) { // -c : Copy calibration bytes
read_fusecal(0x80);
for (n = 0; n < f.Cals; n++)
CodeBuff[FlashSize(Device)-1-n] = CaliBuf[n];
Cmd.CodeSize=FlashSize(Device);
}
MESS("Write...");
if (Device->FlashPageS) { /* Write flash in page mode */
for (adr = 0; adr < Cmd.CodeSize; adr += 1<<Device->FlashPageS) {
if (!write_page(FLASH, adr, &CodeBuff[adr])) {
MESS("Failed.\n");
return RC_FAIL;
}
}
}else{ /* Write flash in byte-by-byte mode */
for (adr = 0; adr < Cmd.CodeSize; adr++) {
if (!write_byte(FLASH, adr, CodeBuff[adr])) {
MESS("Failed.\n");
return RC_FAIL;
}
}
}
}
MESS("Verify...");
for (adr = 0; adr < Cmd.CodeSize; adr++) {
rd = read_byte(FLASH, adr);
if (rd != CodeBuff[adr]) {
fprintf(stderr, "Failed at %04X:%02X-%02X\n", adr, CodeBuff[adr], rd);
return RC_FAIL;
}
}
MESS("Okay.\n");
return 0;
}
// .eep files write command
static int write_eeprom() {
dword adr;
byte rd;
int rc;
if (rc = init_devices()) return rc;
if (!Device->EepromSizeS) return 0;
MESS("EEPROM: ");
// Check whether EESAVE fuse is set
int bit=find_eesave();
if (bit>=0) { // This AVR has EESAVE bit, get it
int byt=bit>>3; bit&=7;
read_fusecal(0x10<<byt); // Read only the one needed fuse byte
if (!(FuseBuf[byt]>>bit&1)) { // EESAVE programmed (= zero)
// There are at least four solutions:
// 1. Ignore and end up in verify error except no bit changes from 0 to 1
// 2. Skip overwriting EEPROM (no error)
// 3. Bail out with error
// 4. Erase chip with EESAVE cleared beforehand
MESS("Skip programming as EESAVE fuse is programmed.\n");
return 0;
}
}
if (Cmd.DataSize > 1U<<Device->EepromSizeS) {
MESS("error: data size > memory size.\n");
return RC_FAIL;
}
if (!(Cmd.fOpt&0x02)) { // -v : Skip programming process when verify mode
MESS("Write...");
if (Device->EepromPageS) { /* Write flash in page mode */
for (adr = 0; adr < Cmd.DataSize; adr += 1<<Device->EepromPageS) {
if (!write_page(EEPROM, adr, DataBuff+adr)) {
MESS("Failed.\n");
return RC_FAIL;
}
}
}else{ /* Write flash in byte-by-byte mode */
for (adr = 0; adr < Cmd.DataSize; adr++) {
if (!write_byte(EEPROM, adr, DataBuff[adr])) {
MESS("Failed.\n");
return RC_FAIL;
}
}
}
}
MESS("Verify...");
for (adr = 0; adr < Cmd.DataSize; adr++) {
rd = read_byte(EEPROM, adr);
if (rd != DataBuff[adr]) {
fprintf(stderr, "Failed at %04X:%02X-%02X\n", adr, DataBuff[adr], rd);
return RC_FAIL;
}
}
MESS("Okay.\n");
return 0;
}
/* -f{l|h|x}, -l command */
static int write_fuse() {
int rc=init_devices();
if (rc) return rc;
const FUSEPROP&f = FuseProps[Device->fusepropidx];
for (int i=0; i<DevInfo.Fuses; i++) if (Cmd.fWrFuse&1<<i) {
MESS("Fuse(");
fputc("lhx"[i],stderr);
MESS("): ");
if (Cmd.fWrFuse&0x10<<i) Cmd.Fuse[i] = f.FuseDefault[i];
if (!(Cmd.fOpt&0x02)) {
MESS("Write...");
write_fuselock(i,(byte)(Cmd.Fuse[i] | ~DevInfo.FuseMask[i]));
}
MESS("Verify...");
byte d = read_byte(FUSE,i);
if ((d ^ Cmd.Fuse[i]) & DevInfo.FuseMask[i]) {
MESS("Failed.\n");
return RC_FAIL;
}
MESS("Okay.\n");
}
if (Cmd.fWrFuse&8 && !(Cmd.fOpt&0x02)) {
MESS("Lock byte: Write...");
write_fuselock(3,Cmd.Fuse[3] ? Cmd.Fuse[3] : f.LockDefault);
MESS("Okay.\n");
}
return 0;
}
/*---------------------
Command line analysis
---------------------*/
static dword mystrtoul(const char*&s,byte radix=0) {
switch (*s) {
case '#': radix=10; ++s; break;
case '$': radix=16; ++s; break;
}
return strtoul(s,const_cast<char**>(&s),radix);
}
static byte strtobyte(const char*&s, byte radix, byte&chars) {
char buf[4];
*(dword*)buf=*(dword*)s;
buf[chars]=0; // chars must be less than 4
char*e;
byte b=(byte)strtoul(buf,&e,radix);
chars=byte(e-buf);
s+=chars;
return b;
}
static int process_argv(const char*cp) {
if (*cp=='-') { // Command switches...
cp++;
switch (tolower(*cp++)) {
case 'e': Cmd.fOpt|=0x01; break; // -e
case 'v': Cmd.fOpt|=0x02; break; // -v
case 'c': Cmd.fOpt|=0x04; break; // -c
case 'r' : /* -r[peiflhxc]+ */
if (!*cp) Cmd.fRead = 0xFF; // read all by default
else while (*cp) switch (tolower(*cp++)) {
case 'p': Cmd.fRead|=0x01; break;
case 'e': Cmd.fRead|=0x02; break;
case 'i': Cmd.fRead|=0x08; break; // -ri (info only)
case 'l': Cmd.fRead|=0x10; break;
case 'h': Cmd.fRead|=0x20; break;
case 'x': Cmd.fRead|=0x40; break;
case 'c': Cmd.fRead|=0x80; break;
case 'f': Cmd.fRead|=0xF0; break;
default: return RC_SYNTAX;
}break;
case 'f': { /* -f{l|h|x}[<bin>] */
int i=-1;
switch (tolower(*cp++)) {
case '=': Cmd.crystal_MHz=mystrtoul(cp); break;
case 'f': Cmd.fWrFuse|=0x80; break; // -ff = take fuses from file
case 'l': i=0; break; // -fl[<bin>] = define low fuse
case 'h': i=1; break; // -fh[<bin>] = define high fuse
case 'x': i=2; break; // -fx[<bin>] = define extended fuse
case 0: --cp;
case 'd': Cmd.fWrFuse|=0x77; break; // -fd = set all fuses to default
default: return RC_SYNTAX;
}
if (i>=0) {
Cmd.fWrFuse|=(*cp?1:0x11)<<i;
if (*cp) {
Cmd.Fuse[i]=(byte)mystrtoul(cp,2);
}
}
}break;
case 'l': { /* -l[<bin>] */
Cmd.fWrFuse|=0x08;
Cmd.Fuse[3] = (byte)mystrtoul(cp,2);
}break;
case 'p': { /* -p<hex> */
unsigned ln=mystrtoul(cp,16);
if ((ln >= 1 && ln <= 3) || ln >= 0x100)
CtrlPort.PortAddr=(word)ln;
else return RC_SYNTAX;
}break;
case 'i': if (*cp=='=') {cp++; goto num;}
if ('1'<=*cp && *cp<='9') { // -i100 = microseconds between every OUT command
num:unsigned r=mystrtoul(cp);
if (r) CtrlPort.iodelay=r;
else return RC_SYNTAX;
}else CtrlPort.inpout32=true; break;
case 'h': Cmd.fHelp|=1; break;
case 'm': Cmd.fHelp|=3; break;
case 'd': Cmd.fHelp|=4; break;
case 'w': if (*cp) { // -wf, -we, -wabsadr=databytes/string: Write flash/eeprom bytes
if (!initBuffers()) return RC_FILE;
if (Cmd.fRead) {
int rc=init_devices();
if (rc) return rc;
dword adr,siz;
if (Cmd.fRead&1) { // -rp : modify program memory
siz=FlashSize(Device);
MESS("Read Flash...");
for (adr = 0; adr < siz; adr++) CodeBuff[adr] = read_byte(FLASH, adr);
MESS("Okay.\n");
}
if (Cmd.fRead&2) {// -re : modify eeprom
siz=1U<<Device->EepromSizeS;
MESS("Read EEPROM...");
for (adr = 0; adr < siz; adr++) DataBuff[adr] = read_byte(EEPROM, adr);
MESS("Okay.\n");
}
if (Cmd.fRead&0xF0) { // -rf : read fuses and cals
read_fusecal(Cmd.fRead);
}
Cmd.fRead=0;
}
dword a=BASE_FLASH; // 0
switch (*cp) {
case 'e': a=BASE_EEPROM; nobreak;
case 'f': ++cp; break;
}
a+=mystrtoul(cp,16);
byte asciimode=0;
bool straighthex=!strchr(cp,','); // no delimiters means concatenated hex bytes
for(;;) switch (char c=*cp++) {
case 0: return 0;
case '\'':
case '"': if (asciimode==c) asciimode=0; else asciimode=c; nobreak;
default: if (asciimode) {
if (c=='\\') switch (c=*cp++) {
case 0: return RC_SYNTAX;
case 'r': c=10; break;
case 'n': c=13; break;
case 'a': c=7; break;
case 'e': c=27; break;
case 'b': c=8; break;
case 't': c=9; break;
case 'x': {byte chars=2; c=strtobyte(cp,16,chars); if (!chars) return RC_SYNTAX;} break;
default: if ('0'<=c && c<='7') {byte chars=3; c=strtobyte(cp,8,chars);}
}
store_buffer(c,a++);
}else switch (c) {
case ',':
case '=': break; // ignore delimiters
default: --cp;
if (straighthex) {
byte chars=2;
c=strtobyte(cp,16,chars);
if (!chars) return RC_SYNTAX;
store_buffer(c,a++);
}else{
byte len=2;
const char*savecp=cp;
dword v=mystrtoul(cp);
if (savecp==cp) return RC_SYNTAX;
switch (_tolower(*cp)) {
case 'l': len=4; cp++; break;
case 'w': cp++; break;
case 'b': len=1; cp++; break;
}
switch (*cp) {
case 0: break;
case ',':++cp; break;
default: return RC_SYNTAX;
}
if (v>dword((1UL<<(len<<3))-1)) return RC_SYNTAX; // too large for size suffix
do{
store_buffer((byte)v,a++);
v>>=8;
}while(--len);
}
}
}
}else Cmd.fOpt|=0x20; break; // -w (pause before exit)
case '8': CtrlPort.Mode=1; break; // -8: detect device as 8-pin / 14-pin serial-only device
case '5': CtrlPort.Mode=2; break; // -5: detect device as tn15
case '6': CtrlPort.Mode=3; break; // -6: detect 6-pin ATtiny4,5,9,10 with “Tiny Programming Interface” TPI
case 'q': CtrlPort.Quick=true; break; // -q (quick power-up)
case 's': Cmd.fOpt|=0x08; break; // -s (require chip signature in HEX/ELF file)
default: return RC_SYNTAX; // invalid command
}
if (*cp) return RC_SYNTAX; // option trails garbage
}else{ // HEX/ELF File (Chip Write command)
FILE *f=fopen(cp,"rb"); // TODO: UTF-8 file names (Windows)
if (!f) {
fprintf(stderr, "%s : Unable to open.\n", cp);
return RC_FILE;
}
fseek(f,0,SEEK_END);
int32 fsize=ftell(f);
rewind(f);
byte*fdata=new byte[fsize];
if (!fdata) {
fprintf(stderr,"memory allocation (%d) failed.\n",fsize);
return RC_FILE;
}
if (fread(fdata,1,fsize,f)!=(size_t)fsize) {
fprintf(stderr,"%s : File access failure.\n",cp);
return RC_FILE;
}
fclose(f);
if (!initBuffers()) return RC_FILE;
// .eep files are read as EEPROM data, others are read as program code
dword ln;
if (strstr(cp,".EEP") || strstr(cp,".eep")) {
ln=loadhex((const char*)fdata,fsize,BASE_EEPROM);
}else{
ln=loadfile(fdata,fsize); // All other .hex or .elf files may contain multiple sections
}
free(fdata);
if (ln) {
fprintf(stderr, "%s (%d) : Hex format error.\n", cp, ln);
return RC_FILE;
}
}
return 0;
}
static int load_commands(char *argv[]) {
// Process INI file as command line parameters
FILE*f=open_cfgfile(INIFILE);
if (f) {
char s[260];
s[elemof(s)-1]=0;
while (fgets(s,elemof(s)-1,f)) {
char *e=s+strlen(s);
while (e!=s) {
if ((byte)*--e>0x20) break;
*e=0; // trim right (newline and spaces/tabs)
}
if (*s) break; // halt on first empty line
int ret;
if (ret=process_argv(s)) return ret;
}
fclose(f);
}
// Process command line parameters
while (*++argv) {
int ret;
if (ret=process_argv(*argv)) return ret;
}
return 0;
}
/* Terminate process */
static void terminate() {
close_ifport();
Device = NULL;
if (Cmd.fOpt&0x20) { // Pause
MESS("\n");
query("Type Enter to exit!",MB_OK|MB_ICONASTERISK);
}
}
int _cdecl main (int argc, char*argv[]) {
outredir=!isatty(fileno(stdout));
#ifdef WIN32
hStdOut=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO sbi;
GetConsoleScreenBufferInfo(hStdOut,&sbi);
termwidth=(byte)sbi.dwSize.X;
#else
winsize ws;
ioctl(fileno(stdout),TIOCGWINSZ,&ws);
termwidth=(byte)ws.ws_col;
#endif
if (!outredir && termwidth<118) linebytes=16;
int rc=load_commands(argv);
if (rc==RC_SYNTAX) Cmd.fHelp|=1;
if (Cmd.fHelp&1) {
MESS(MesUsage);
if (!(Cmd.fHelp&2)) MESS(MesMore);
if (!(Cmd.fHelp&4)) MESS(MesDevices);
}
if (Cmd.fHelp&2) MESS(MoreInfo);
if (Cmd.fHelp&4) MESS(Devices);
if (Cmd.fHelp) return rc;
if (Cmd.fWrFuse&0x80 && !(Cmd.fWrFuse&0x0F))
MESS("'-ff' given but file doesn't contain fuse definition!\n");
if (Cmd.fOpt&0x08 && !(Cmd.fOpt&0x10))
MESS("'-s' given but file doesn't contain chip signature!\n");
if (Cmd.fOpt&0x10) {
// preselect right programming interface (to save time or command-line parameter)
const DEVPROP*dev=findSignature(Cmd.Sign);
if (!dev) {
MESS("Chip signature in file is unknown!\n");
}else{
byte mode=0;
const FUSEPROP&f = FuseProps[dev->fusepropidx];
for(byte m=f.PgmType;!(m&1);mode++,m>>=1);
CtrlPort.Mode=mode;
}
}
// Read device and terminate if -r{p|e|f} command is specified
if (Cmd.fRead) {
rc = read_device(Cmd.fRead);
terminate();
return rc;
}
// Erase device if -e command is specified
if (Cmd.fOpt&0x01) rc = erase_device();
// Write to device if any file is loaded
if (Cmd.CodeSize) {
if (rc = write_flash()) {
terminate();
return rc;
}
}
if (Cmd.DataSize) {
if (rc = write_eeprom()) {
terminate();
return rc;
}
}
// Write fuse,lock if -f{l|h|x}, -l are specified
if (Cmd.fWrFuse&0x0F) {
if (rc = write_fuse()) {
terminate();
return rc;
}
}
if (!Device) MESS(MesUsage);
terminate();
return 0;
}
// Bisher war die 64-Bit-Version an msvcrt90.dll gebunden, was bei Windows 10 problematisch ist!
// Irrtum 2021 bemerkt und auf mainCRTStartup() zurückgeschwenkt
#ifdef _WIN64
#if 0
#pragma bss_seg(".CRT$XCA")
static void* Anfang; // Keine Initialisierung
#pragma bss_seg(".CRT$XCZ")
static void* Ende;
#pragma bss_seg() // zurückschalten zu ".bss"
extern "C" _CRTIMP void _cdecl _initterm(void*&,void*&);
#endif
extern "C" _CRTIMP int _cdecl __getmainargs(int&,char**&,char**&,int,int&);
void mainCRTStartup() {
#if 0
_initterm(Anfang,Ende);
#endif
int argc,newmode;
char**argv,**envp;
__getmainargs(argc,argv,envp,false,newmode);
UINT e=main(argc,argv);
ExitProcess(e);
}
#endif
Detected encoding: UTF-8 | 0
|