
    //---------------------------------------------------------------------------------------------------------//
    //     __ __  ___             ___              _ _                                                         //
    //    / // / /   |_   _______/   |  __________(_|_)                                                        //
    //   / // /_/ /| | | / / ___/ /| | / ___/ ___/ / /                                                         //
    //  /__  __/ ___ | |/ / /  / ___ |(__  ) /__/ / /                                                          //
    //    /_/ /_/  |_|___/_/  /_/  |_/____/\___/_/_/                                                           //
    //   ____              _   _                 _                 ____            __    __                    //
    //  | __ )  ___   ___ | |_| | ___   __ _  __| | ___ _ __      / __ )__  ______/ /___/ /_  __               //
    //  |  _ \ / _ \ / _ \| __| |/ _ \ / _` |/ _` |/ _ \ '__|    / __  / / / / __  / __  / / / /               //
    //  | |_) | (_) | (_) | |_| | (_) | (_| | (_| |  __/ |      / /_/ / /_/ / /_/ / /_/ / /_/ /                //
    //  |____/ \___/ \___/ \__|_|\___/ \__,_|\__,_|\___|_|     /_____/\__,_/\__,_/\__,_/\__, /                 //
    //                                                                                 /____/                  //
    //     __                       __                                                                         //
    //    / /  __ __  ______ ______/ /__  ____ _                                                               //
    //   / _ \/ // / / __/ // / __/ __/ |/ /  ' \                                                              //
    //  /_.__/\_, /  \__/\_,_/_/  \__/|___/_/_/_/                                                              //
    //       /___/                                                                                             //
    //                                                                                                         //
    //  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);
                IRQ_CONTROL = (1<<IVSEL);
                BOOTLOADER();
            }

            /*---------------------------------------------------------------------------------------------------
             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

    */

