__ __  ___             ___              _ _ 
      / // / /   |_   _______/   |  __________(_|_)
     / // /_/ /| | | / / ___/ /| | / ___/ ___/ / /
    /__  __/ ___ | |/ / /  / ___ |(__  ) /__/ / / 
      /_/ /_/  |_|___/_/  /_/  |_/____/\___/_/_/  
     ____              _   _                 _                 ____            __    __
    | __ )  ___   ___ | |_| | ___   __ _  __| | ___ _ __      / __ )__  ______/ /___/ /_  __
    |  _ \ / _ \ / _ \| __| |/ _ \ / _` |/ _` |/ _ \ '__|    / __  / / / / __  / __  / / / /
    | |_) | (_) | (_) | |_| | (_) | (_| | (_| |  __/ |      / /_/ / /_/ / /_/ / /_/ / /_/ / 
    |____/ \___/ \___/ \__|_|\___/ \__,_|\__,_|\___|_|     /_____/\__,_/\__,_/\__,_/\__, /  
                                                                                   /____/
       __                       __                                                          
      / /  __ __  ______ ______/ /__  ____ _ 
     / _ \/ // / / __/ // / __/ __/ |/ /  ' \
    /_.__/\_, /  \__/\_,_/_/  \__/|___/_/_/_/
         /___/ 






add-on to 4AvrAscii256BabyBootloader

>read eeprom
>write eeprom using standard intel hex record
>read fuse/lock bytes



Download here ->
   4AvrAsciiBuddy.c
version 2007.12.12


//---------------------------------------------------------------------------------------------------------//
// __ __ ___ ___ _ _ //
// / // / / |_ _______/ | __________(_|_) //
// / // /_/ /| | | / / ___/ /| | / ___/ ___/ / / //
// /__ __/ ___ | |/ / / / ___ |(__ ) /__/ / / //
// /_/ /_/ |_|___/_/ /_/ |_/____/\___/_/_/ //
// ____ _ _ _ ____ __ __ //
// | __ ) ___ ___ | |_| | ___ __ _ __| | ___ _ __ / __ )__ ______/ /___/ /_ __ //
// | _ \ / _ \ / _ \| __| |/ _ \ / _` |/ _` |/ _ \ '__| / __ / / / / __ / __ / / / / //
// | |_) | (_) | (_) | |_| | (_) | (_| | (_| | __/ | / /_/ / /_/ / /_/ / /_/ / /_/ / //
// |____/ \___/ \___/ \__|_|\___/ \__,_|\__,_|\___|_| /_____/\__,_/\__,_/\__,_/\__, / //
// /____/ //
// __ __ //
// / / __ __ ______ ______/ /__ ____ _ //
// / _ \/ // / / __/ // / __/ __/ |/ / ' \ //
// /_.__/\_, / \__/\_,_/_/ \__/|___/_/_/_/ //
// /___/ //
// //
// Curt's ATmega88/ATmega168/ATmega16 4AvrAscii 256 BabyBootloader Buddy //
// EEPROM reading/writing and fuse/lock bits reading application add-on //
// //
// Copyright 2007 by Curt Van Maanen //
// //
// Version 2007.12.09 //
//---------------------------------------------------------------------------------------------------------//



/*-----------------------------------------------------------------------------------------------------------

3 sections to this file, in this order-
CONFIG - make your changes needed in this section
PROGRAM - program located here, no changes normally needed
HELP - help information section

CHECKLIST-

1. .text section is set to 0 (or removed from 'memory segments' option)
2. linker options set to -nostartfiles and -nodefaultlibs
3. -mshort-calls added to custom options
4. change whatever needed in the config section (see config section)


-----------------------------------------------------------------------------------------------------------*/



/*-----------------------------------------------------------------------------------------------------------
___ _ _ ___ _ _ _ ___ ___ ___
|_ _| \| |/ __| | | | | | \| __/ __|
| || .` | (__| |_| |_| | |) | _|\__ \
|___|_|\_|\___|____\___/|___/|___|___/

-----------------------------------------------------------------------------------------------------------*/
#include <avr/io.h>
#include <avr/pgmspace.h> // for reading from flash memory
#include <avr/boot.h> // for writing to flash memory
#include <avr/wdt.h> // watchdog stuff
#include <avr/eeprom.h> // eeprom stuff


//---------------------------------------------------------------------------------------------------------//
// ______ ______ .__ __. _______ __ _______ //
// / | / __ \ | \ | | | ____|| | / _____| //
// | ,----'| | | | | \| | | |__ | | | | __ //
// | | | | | | | . ` | | __| | | | | |_ | //
// | `----.| `--' | | |\ | | | | | | |__| | //
// \______| \______/ |__| \__| |__| |__| \______| //
// //
//---------------------------------------------------------------------------------------------------------//
// CONFIGURATION SECTION DEFINES (change defines as needed) //
//---------------------------------------------------------------------------------------------------------//



/*-----------------------------------------------------------------------------------------------------------
baud rate defines
page 197 of datasheet (mega88/168), 38,400 has little error, and works well with internal 8mhz clock
set to whatever you need for your clock and desired baud rate
high baud register is not used in bootloader, so no high cpu freq's with low baud rates
-----------------------------------------------------------------------------------------------------------*/

#define BAUD_LO_VALUE 12 // BAUD 38400 .2% error @ 8Mhz


/*-----------------------------------------------------------------------------------------------------------
set commands here for 'go', 'erase', 'eeprom read', and 'show fuses'
they have to be > 128
the command in hex will be the same as ascii-> for erase >:EE<enter>
-----------------------------------------------------------------------------------------------------------*/

#define EE_GO_COMMAND 0x99 // 'go' command, APP
#define EE_ERASE_COMMAND 0xEE // 'erase eeprom' command,:EE
#define EE_READ_COMMAND 0xEB // 'eeprom read' (:ER)
#define EE_SHOW_FUSES 0xCF // show fuses (:SF)



/*-----------------------------------------------------------------------------------------------------------
ATmega16 has UDR, ATmega88/168 has UDR0, so use defines here to get them the same
if you have an avr with udr1/udr2, change the number below to the desired usart number
-----------------------------------------------------------------------------------------------------------*/

#ifdef UDR // for avr's with just a 'plain' udr,
#define USART_NUM // leave this blank, nothing, empty
#else // for newer avr's, set usart number
#define USART_NUM 0 // usart number( here using usart0 )
#endif



//---------------------------------------------------------------------------------------------------------//
// .______ .______ ______ _______ .______ ___ .___ ___. //
// | _ \ | _ \ / __ \ / _____|| _ \ / \ | \/ | //
// | |_) | | |_) | | | | | | | __ | |_) | / ^ \ | \ / | //
// | ___/ | / | | | | | | |_ | | / / /_\ \ | |\/| | //
// | | | |\ \----.| `--' | | |__| | | |\ \----./ _____ \ | | | | //
// | _| | _| `._____| \______/ \______| | _| `._____/__/ \__\ |__| |__| //
// //
//---------------------------------------------------------------------------------------------------------//
// PROGRAM (normally no changes needed in this section) //
//---------------------------------------------------------------------------------------------------------//



/*-----------------------------------------------------------------------------------------------------------
___ ___ ___ ___ _ _ ___ ___
| \| __| __|_ _| \| | __/ __|
| |) | _|| _| | || .` | _|\__ \
|___/|___|_| |___|_|\_|___|___/

-----------------------------------------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------------------------------
concat macros, used in usart defines
-----------------------------------------------------------------------------------------------------------*/

#define MY_CONCAT3b(a,b,c) a ## b ## c // needed to expand USART_NUM
#define MY_CONCAT3(a,b,c) MY_CONCAT3b(a,b,c) // concat 3 'words'

/*-----------------------------------------------------------------------------------------------------------
undefine older usart names for mega64, so usart macros below work correctly for the mega64
-----------------------------------------------------------------------------------------------------------*/

#if defined (__AVR_ATmega64__)
#undef RXEN
#undef TXEN
#undef RXC
#undef TXC
#undef UDR
#undef UDRE
#undef FE
#undef DOR
#undef U2X
#endif

/*-----------------------------------------------------------------------------------------------------------
usart definitions
-----------------------------------------------------------------------------------------------------------*/

#define BAUD_RATE_REG_LO MY_CONCAT3(UBRR,USART_NUM,L)
#define USART_CONTROL_B MY_CONCAT3(UCSR,USART_NUM,B)
#define USART_STATUS_B MY_CONCAT3(UCSR,USART_NUM,B)
#define USART_CONTROL_A MY_CONCAT3(UCSR,USART_NUM,A)
#define USART_STATUS_A MY_CONCAT3(UCSR,USART_NUM,A)
#define RX_ENABLE __CONCAT(RXEN,USART_NUM)
#define TX_ENABLE __CONCAT(TXEN,USART_NUM)
#define RX_COMPLETED __CONCAT(RXC,USART_NUM)
#define TX_COMPLETED __CONCAT(TXC,USART_NUM)
#define RX_DATA __CONCAT(UDR,USART_NUM)
#define TX_DATA __CONCAT(UDR,USART_NUM)
#define TX_BUFFER_EMPTY __CONCAT(UDRE,USART_NUM)
#define FRAMING_ERROR __CONCAT(FE,USART_NUM)
#define OVERRUN_ERROR __CONCAT(DOR,USART_NUM)
#define USART_2X_SPEED __CONCAT(U2X,USART_NUM)

#define DISABLE_USART() USART_CONTROL_B = 0
#define ENABLE_USART() USART_CONTROL_B = ((1<<RX_ENABLE) | (1<<TX_ENABLE))
#define MY_RX_FLAGS ((1<<RX_COMPLETED)|(1<<FRAMING_ERROR) | (1<<OVERRUN_ERROR))

/*-----------------------------------------------------------------------------------------------------------
set maximum size of data bytes we can receive in an intel hex record
limit to 128 (rx_buffer needs to stay < 256)
-----------------------------------------------------------------------------------------------------------*/

#define MAX_EE_BYTES 128

/*-----------------------------------------------------------------------------------------------------------
make our own watchdog macro, without the irq bit save/restore
-----------------------------------------------------------------------------------------------------------*/

#define ENABLE_WDT(value) _WD_CONTROL_REG = (_BV(_WD_CHANGE_BIT) | _BV(WDE));\
_WD_CONTROL_REG = ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | \
_BV(WDE) | (value & 0x07)) )

/*-----------------------------------------------------------------------------------------------------------
reset flags register
-----------------------------------------------------------------------------------------------------------*/

#ifdef MCUCSR
#define RESET_FLAGS MCUCSR
#else
#define RESET_FLAGS MCUSR
#endif

/*-----------------------------------------------------------------------------------------------------------
IVSEL register
-----------------------------------------------------------------------------------------------------------*/

#ifdef GICR
#define IRQ_CONTROL GICR
#else
#define IRQ_CONTROL MCUCR
#endif

/*-----------------------------------------------------------------------------------------------------------
eeprom control register
-----------------------------------------------------------------------------------------------------------*/

#ifdef EEWE
#define EEPE EEWE
#define EEMPE EEMWE
#endif

/*-----------------------------------------------------------------------------------------------------------
functions in bootloader (can use absolute numbers here, since 'main' section starts at 0)
-----------------------------------------------------------------------------------------------------------*/

#define HEX2ASCII(byte) ( (void(*)(uint8_t h)) -8 )(byte)
#define SENDCHAR(byte) ( (void(*)(uint8_t c)) -6 )(byte)
#define BOOTLOADER() ( (void(*)(void)) -2 )()

/*-----------------------------------------------------------------------------------------------------------
___ _ _ _ _ ___ _____ ___ ___ _ _ ___ ___ ___ _____ ___ _______ _____ ___ ___
| __| | | | \| |/ __|_ _|_ _/ _ \| \| | | _ \ _ \/ _ \_ _/ _ \_ _\ \ / / _ \ __/ __|
| _|| |_| | .` | (__ | | | | (_) | .` | | _/ / (_) || || (_) || | \ V /| _/ _|\__ \
|_| \___/|_|\_|\___| |_| |___\___/|_|\_| |_| |_|_\\___/ |_| \___/ |_| |_| |_| |___|___/

-----------------------------------------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------------------------------
make main the first to run (.init9), and no extra compiler code (naked)
-----------------------------------------------------------------------------------------------------------*/

int main (void) __attribute__ ((naked,section (".init9")));
uint8_t read_ee (uint16_t addr);
void write_ee (uint16_t addr, uint8_t data);


/*-----------------------------------------------------------------------------------------------------------
___ _ ___ ___ _ _ __ ___ ___ ___ _ ___ _ ___ ___
/ __| | / _ \| _ ) /_\ | | \ \ / /_\ | _ \_ _| /_\ | _ ) | | __/ __|
| (_ | |_| (_) | _ \/ _ \| |__ \ V / _ \| /| | / _ \| _ \ |__| _|\__ \
\___|____\___/|___/_/ \_\____| \_/_/ \_\_|_\___/_/ \_\___/____|___|___/

-----------------------------------------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------------------------------
receive buffer to hold intel hex record (receive loop expects this to be an odd
number, used by nibble shifting, so if changed and is an even number, receive loop needs changing
-----------------------------------------------------------------------------------------------------------*/

struct {
uint8_t colon; // ':' (colon)
uint8_t LL; // LL - record length
uint8_t AAh; // AAxx - data byte address (byte)
uint8_t AAl; // xxAA - low byte of address
uint8_t TT; // TT - record type (0 or 1 used here)
uint8_t DD[MAX_EE_BYTES]; // DD - data bytes
uint8_t CC; // CC - used just for sizing of struct
uint8_t CR; // cr - used just for sizing of struct
}rx_buffer;



/*===========================================================================================================
__ __ _ ___ _ _
| \/ | /_\ |_ _| \| |
| |\/| |/ _ \ | || .` |
|_| |_/_/ \_\___|_|\_|

===========================================================================================================*/

int main(void){

/*-------------------------------------------------------------------------------------------------------
local variables of main()
r2-r17,r28,r29 are call saved registers, which means the compiler will save these if used in a function
all others may not survive between function calls, and the compiler may optimize a register variable
away if a function call is 'between' when the variable is used
-------------------------------------------------------------------------------------------------------*/

uint16_t address; // address to program/erase

register uint8_t recordlength asm("r4"); // store data length of record
register uint8_t checksum asm("r16"); // store accumulating checksum

register uint16_t temp_r16w asm("r16"); // store address for verify
register uint8_t temp_r16 asm("r16"); // used to get AAAA swapped into addressV
register uint8_t temp_r17 asm("r17"); // used to get AAAA swapped into addressV

register uint8_t index asm("r3"); // DDDD array index

register uint8_t temp_r24 asm("r24"); // r24, (use between calls only)


/*-------------------------------------------------------------------------------------------------------
clear reset flags, clear r1, set watchdog, set stack pointer, setup usart
-------------------------------------------------------------------------------------------------------*/

RESET_FLAGS = 0; // clear WDRF
asm volatile ("clr __zero_reg__"); // compiler wants r1 to be 0
ENABLE_WDT(WDTO_120MS); // enable watchdog, 1/8th sec timeout
SP=RAMEND; // set stack pointer
BAUD_RATE_REG_LO = BAUD_LO_VALUE; // set baud low (baud high not used)
ENABLE_USART(); // enable usart tx/rx macro

/*-------------------------------------------------------------------------------------------------------
the loop
-------------------------------------------------------------------------------------------------------*/

while(1){

goto _cr; // goto command prompt

/*---------------------------------------------------------------------------------------------------
error (checksum bad, bad eeprom address, erase/programming verify error, rx framing/overrun)
---------------------------------------------------------------------------------------------------*/

_error:
asm volatile("_err:");
SENDCHAR('!'); // notify that error happened
goto _cr; // skip _did_nothing, goto _cr

/*---------------------------------------------------------------------------------------------------
nothing was done, so show '?', command or record was not processed for some reason- first character
not a ':', cr was not the last character
---------------------------------------------------------------------------------------------------*/

_did_nothing:
SENDCHAR('?'); // means nothing done with input 'line'

/*---------------------------------------------------------------------------------------------------
command prompt
---------------------------------------------------------------------------------------------------*/

_cr:
DISABLE_USART(); // flush any LF, also prevent overrun
ENABLE_USART(); // enable again
SENDCHAR('\r'); // cr
SENDCHAR('#'); // eeprom prompt

/*---------------------------------------------------------------------------------------------------
process intel hex record
intel hex record >:LLAAAATTDD...DDCC Always starts with a ':'
LL = Record length (in bytes, of DD bytes)
AAAA = memory address (in bytes)
TT = record type (00 = data,01 = end of file, others not used)
DD = data byte , number of bytes = LL
CC = checksum (add all bytes, byte sum only, invert, then add 1)
CC = (!(LL + AA + AA + TT + DD...DD )) + 1, checksum total including checksum byte = 0

get the whole record here (input limited by size of rx_buffer)
if rx_buffer overrun, last character will not be cr, and will ignore record (back to command prompt)
cr has to be the last char (and breaks out of do loop)
any 'control' character (0x00-0x1F) breaks out of loop
---------------------------------------------------------------------------------------------------*/

asm volatile(

"ldi r17,%[bufsize] \n\t" // keep us inside the buffer
"ldi r16,0xFF \n\t" // default erase character
"sts %[AAh],r16 \n\t" // stored in AAh
"ldi r16,0x00 \n\t" // temp use
"ldi r28,lo8(%[bufptr]) \n\t" // Y pointer-> &rxbuffer
"ldi r29,hi8(%[bufptr]) \n\t"
"st Y,r16 \n\t" // clear first buffer position (colon)

/*-----------------------------------------------------------------------------------------------
get rx status, wait until RXCn, FEn or DORn flag set (any one or more)
also clear watchdog, since we could be here a while
if any flag set, drop out of do loop, read udr to either get data or to flush rx & reset flags,
then check if RXCn is the only flag set, if not, framing or overrun flag(s) set, so goto _error
-----------------------------------------------------------------------------------------------*/

"1: \n\t" // rx loop
"wdr \n\t" // reset watchdog
"lds r25,%[status] \n\t" // get usart status
"andi r25,%[flags] \n\t" // mask off unwanted bits
"breq 1b \n\t" // if no flags, try again

"lds r24,%[udr] \n\t" // read udr
"cpi r25,%[rxc] \n\t" // check if rxc is the only flag set
"brne _err \n\t" // no, buffer overrun or framing error

"cpi r24,' ' \n\t" // if rx_data >= ' '

/*-----------------------------------------------------------------------------------------------
if < ' ' (0x00-0x1F), its a 'control' character, done
-----------------------------------------------------------------------------------------------*/

"brcs 5f \n\t" // if not, done here

/*-----------------------------------------------------------------------------------------------
if >= ' ' (0x20-0xFF), echo it, subtract 7 if >= 0x40, mask off high nibble to leave 0x00-0x0F
'0' (0x30) converts to 0x00, '9' (0x39) to 0x09, 'A' (0x41) to 0x0A, 'F' (0x46) to 0x0F
ANY characters above 0x1F are converted to 0x00-0x0F
-----------------------------------------------------------------------------------------------*/

"rcall -6 \n\t" // echo back character (FLASHEND-7)
"cpi r24,0x40 \n\t" // if 'A'-'F' (0x41-0x46)
"brcs 2f \n\t" // if not, skip next
"subi r24,0x07 \n\t" // yes, subtract 7 ('A'=0x41->0x3A,etc)
"2: \n\t"
"andi r24,0x0F \n\t" // mask off bits3-7, make ALL 0x00-0x0F

/*-----------------------------------------------------------------------------------------------
rx_buffer is odd number length, so first character (':') will be stored right away as a
'complete' byte (that's why rx_buffer.colon had to be cleared)
the second character will be shifted left into the high nibble position, third character will
be added to second character, then saved, and so on
-----------------------------------------------------------------------------------------------*/

"sbrc r17,0 \n\t" // if odd number
"rjmp 3f \n\t" // jump ahead
"swap r24 \n\t" // else even, low nibble to high nibble
"st Y,r24 \n\t" // save it to buffer
"rjmp 4f \n\t" // check if still in buffer
"3: \n\t" // odd 'position'
"ld r25,Y \n\t" // get previous high nibble
"add r24,r25 \n\t" // merge high nibble + low nibble
"st Y+,r24 \n\t" // store it in buffer, inc Y pointer
"add r16,r24 \n\t" // add to checksum
"4: \n\t" // now check if still in buffer
"subi r17,0x01 \n\t" // buffer size - 1
"brne 1b \n\t" // if not 0, get another character
"5: \n\t" // done
::
[AAh] "i" (_SFR_MEM_ADDR(rx_buffer.AAh)),
[bufsize] "M" (sizeof(rx_buffer)),
[bufptr] "i" (&rx_buffer),
[status] "M" (_SFR_MEM_ADDR(USART_STATUS_A)),
[flags] "M" (MY_RX_FLAGS),
[udr] "M" (_SFR_MEM_ADDR(RX_DATA)),
[rxc] "M" (1<<RX_COMPLETED)

);

/*---------------------------------------------------------------------------------------------------
if last rx character was not cr, go back to command prompt
could be rx_buffer overrun or a 'control' character other than cr (like esc key)
---------------------------------------------------------------------------------------------------*/

if(temp_r24 != '\r'){ // if last char not cr
goto _did_nothing; // show that nothing was done
}

/*---------------------------------------------------------------------------------------------------
if first character was not ':', go back to command prompt
---------------------------------------------------------------------------------------------------*/

if(rx_buffer.colon != (':' & 0x0F)){ // if rx not ':' (0x3A & 0x0F = 0x0A)
goto _did_nothing; // show that nothing was done
}

/*---------------------------------------------------------------------------------------------------
store record length - LL (data byte count)
---------------------------------------------------------------------------------------------------*/

recordlength = rx_buffer.LL; // store recordlength

/*---------------------------------------------------------------------------------------------------
if LL is the 'go' command, rjmp back to bootloader (reset flags were already cleared before)
---------------------------------------------------------------------------------------------------*/

if(recordlength == EE_GO_COMMAND){ // back to bootloader
wdt_reset();
IRQ_CONTROL = (1<<IVCE); // enable ivsel change
IRQ_CONTROL = (1<<IVSEL); // ivsel=1
BOOTLOADER(); // jump to bootloader (jump table)
}

/*---------------------------------------------------------------------------------------------------
Erase Eeprom loop
if LL=EE_ERASE_COMMAND, erase eeprom
erase character will default to 0xFF, if you want some other character, include it after ther
erase command
---------------------------------------------------------------------------------------------------*/

if(recordlength == EE_ERASE_COMMAND){ // erase all eeprom
address = E2END+1; // start at end +1
while(address){ // until address == 0
wdt_reset(); // need to clear watchdog in this loop
address--; // dec address
write_ee(address,rx_buffer.AAh); // erase to desired character
if(read_ee(address) != rx_buffer.AAh){ // if not the same
goto _error; // write failed
}
}
goto _cr; // back to command prompt
}

/*---------------------------------------------------------------------------------------------------
EEprom Read
if LL=EE_READ_COMMAND, display eeprom
---------------------------------------------------------------------------------------------------*/

if(recordlength == EE_READ_COMMAND){ // read eeprom
for(address=0; address<=E2END; address++){ // loop through all eeprom
wdt_reset(); // reset watchdog in this loop
if (!(address & 0x1F)){ // every 32 bytes
SENDCHAR('\r'); // start a new line
}
if (!(address & 0x0F)){ // every 16 bytes
SENDCHAR(' '); // send a space
}
HEX2ASCII(read_ee(address)); // display eeprom byte
}
goto _cr; // back to command prompt
}

/*---------------------------------------------------------------------------------------------------
show fuses
if LL=EE_SHOW_FUSES, display fuses/lock byte values
---------------------------------------------------------------------------------------------------*/

if(recordlength == EE_SHOW_FUSES){ // show fuses
SENDCHAR('\r'); // new line
for(address=0; address<4; address++){ // loop through the 4 fuse/lock bytes
__SPM_REG = (1<<BLBSET) | (1<<SPMEN); // setup to read fuses
HEX2ASCII(pgm_read_byte(address)); // read fuse byte, send as ascii
}
goto _cr; // back to command prompt
}

/*---------------------------------------------------------------------------------------------------
if checksum on all bytes received not 0, checksum error (':' was added, so need to check if 0x0A)
---------------------------------------------------------------------------------------------------*/

if(checksum != 0x0A){ // if not 0 + 0x0A
goto _error; // checksum error
}

/*---------------------------------------------------------------------------------------------------
TT - Record type
if TT is 1, that is an EOF record, just goto command prompt
---------------------------------------------------------------------------------------------------*/

if(rx_buffer.TT == 1){ // if TT is eof record
goto _cr; // ok
}

/*---------------------------------------------------------------------------------------------------
now check if TT is NOT 0 (we only do data records which are type 0x00)
---------------------------------------------------------------------------------------------------*/

if(rx_buffer.TT != 0){ // if TT not 0
goto _did_nothing; // ignore
}

index = 0; // rx_buffer.DD array number

/*---------------------------------------------------------------------------------------------------
AAAA is in sram high byte in lower memory, low byte in higher memory
compiler needs uint16_t low byte in lower location, high byte in higher location
get the low AA byte, put it in the 'low' register pair of r17:r16, get the high AA byte,
put it in the 'high' register pair r17:r16, then copy the 'swapped' AAAA to 'address'
---------------------------------------------------------------------------------------------------*/

temp_r16 = rx_buffer.AAl; // get low AA
temp_r17 = rx_buffer.AAh; // get high AA
address = temp_r16w; // copy it to address

/*---------------------------------------------------------------------------------------------------
program eeprom loop
---------------------------------------------------------------------------------------------------*/

while(recordlength--){ // loop through data bytes in record
wdt_reset(); // in case of large records
if(address > E2END){ // make sure it is a valid address
goto _error; // invalid eeprom address
}
write_ee(address,rx_buffer.DD[index]); // write byte
if(read_ee(address++) != rx_buffer.DD[index++]){ // if not the same
goto _error; // write failure
}
}

/*---------------------------------------------------------------------------------------------------
done (back to command prompt)
---------------------------------------------------------------------------------------------------*/
} // while(1)
} // main()



/*===========================================================================================================
___ _ _ _ _ ___ _____ ___ ___ _ _ ___
| __| | | | \| |/ __|_ _|_ _/ _ \| \| / __|
| _|| |_| | .` | (__ | | | | (_) | .` \__ \
|_| \___/|_|\_|\___| |_| |___\___/|_|\_|___/

===========================================================================================================*/

/*-----------------------------------------------------------------------------------------------------------
read a byte from eeprom,
wait until eeprom ready, setup address, strobe read bit, return data
-----------------------------------------------------------------------------------------------------------*/

uint8_t read_ee(uint16_t addr){
eeprom_busy_wait();
EEAR = addr;
EECR |= (1<<EERE);
return EEDR;
}

/*-----------------------------------------------------------------------------------------------------------
write a byte to eeprom,
-----------------------------------------------------------------------------------------------------------*/

void write_ee(uint16_t addr, uint8_t data){
uint8_t prev_data;

prev_data = read_ee(addr); // get previous value in eeprom
if(prev_data == data){ // if no change, no need to write
return; // so just return
}

EECR &= ~((1<<EEPM1)|(1<<EEPM0)); // set to erase/write
if(data == 0xFF){ // if need 0xFF
EECR |= (1<<EEPM0); // set to erase only
}
else if(!((prev_data ^ data)& ~prev_data)){ // if no 0->1's needed
EECR |= (1<<EEPM1); // set to write only
}

EEDR = data; // setup data
EECR |= (1<<EEMPE); // enable write
EECR |= (1<<EEPE); // write
}



//---------------------------------------------------------------------------------------------------------//
// __ __ _______ __ .______ //
// | | | | | ____|| | | _ \ //
// | |__| | | |__ | | | |_) | //
// | __ | | __| | | | ___/ //
// | | | | | |____ | `----.| | //
// |__| |__| |_______||_______|| _| //
// //
//---------------------------------------------------------------------------------------------------------//

/*

simply upload this app, then run the 'go' command, which will then run this application

the command prompt will be-
#

any input that does not result in a command/record being processed, results in a ?
any errors result in a !

to erase all eeprom (to 0xFF), type the command :EE (erase eeprom)
to erase all eeprom to some other character, type the command :EE00 (00 will then be the erase character)
to read all eeprom, type the command :ER (eeprom read)
to show fuse bytes, type the command :SF (show fuses)
LFLBEFHF (low fuse LF, lock bits LB, extended fuse EF, high fuse HF)
to go back to the bootloader, type the command APP (same as :99)
to upload an eeprom hex file, just upload the eeprom hex file just like in the bootloader

IMPORTANT NOTE - the transmit line delay will need to be increased to 65ms when uploading eeprom hex
files, as each byte will take 3.5ms

if you have eeprom hex records larger than 16 data bytes, you will have to increase the delay even more

*/