//-----------------------------------------------------------------------------------------
// startup.c
//
//alternate startup vector table/startup code to replace c runtime object file, and

//library functions from libgcc.a
//
//purpose- to have all startup code in one file/one location that can be modified as
//needed
//
//there are symbols in c runtime object file that are not defined here
//
//linker option --nostartfiles is needed to prevent linking in the crt*.o oject file,
//which is what this replaces in part
//
//the functions from libgcc.a for setting up ram, are prevented from being linked in
//by providing the functions here, which are naked (empty, no code)
//-----------------------------------------------------------------------------------------
#include <avr/io.h>
#include <avr/pgmspace.h>
//-----------------------------------------------------------------------------------------
//defines
//-----------------------------------------------------------------------------------------
#define NAKED __attribute__((naked))
#define VECTORS __attribute__((naked,noinline,section(".vectors")))
#define V(num) __attribute__((weak,alias("__bad_interrupt"))) void __vector_##num(void)
#define NL "\n\t"
#define WDR() asm("wdr")
//-----------------------------------------------------------------------------------------
//linker symbols
//-----------------------------------------------------------------------------------------
extern void* __bss_end;
extern void* __data_load_start;
extern void* __data_load_end;
extern void* __data_start;
//-----------------------------------------------------------------------------------------
//prevent these functions from being pulled in (libgcc.a) by creating empty functions
//-----------------------------------------------------------------------------------------
NAKED void __do_copy_data(){}
NAKED void __do_clear_bss(){}
//-----------------------------------------------------------------------------------------
//function prototypes, vectors are weak with alias to __bad_interrupt
//-----------------------------------------------------------------------------------------
int main(void);
V(1); V(2); V(3); V(4); V(5); V(6); V(7); V(8);
V(9); V(10);V(11);V(12);V(13);V(14);V(15);V(16);
V(17);V(18);V(19);V(20);V(21);V(22);V(23);V(24);
V(25);V(26);V(27);V(28);V(29);V(30);V(31);V(32);
V(33);V(34);V(35);V(36);V(37);V(38);V(39);V(40);
V(41);V(42);V(43);V(44);V(45);V(46);V(47);V(48);
V(49);V(50);V(51);V(52);V(53);V(54);V(55);V(56);
//-----------------------------------------------------------------------------------------
//vector table + startup code + exit code
//-----------------------------------------------------------------------------------------
VECTORS void __vectors(void){
    asm(
    ".irp d,,1,2,3,4,5"             NL              // tens
    ".irp c,0,1,2,3,4,5,6,7,8,9"    NL              // ones (dc=0-59, max set below)
    ".if \\d\\c < %0"               NL              // if < max irq #
    ".if %1"                        NL
    "jmp __vector_\\d\\c"           NL              // jmp to __vector_dc
    ".else"                         NL
    "rjmp __vector_\\d\\c"          NL              // rjmp to __vector_dc
    ".endif"                        NL
    ".endif"                        NL
    ".endr"                         NL
    ".endr"                         NL
    "__vector_0:"                   NL
    "clr __zero_reg__"              NL              // clear r1
    "out __SREG__,__zero_reg__"     NL              // SREG=0 (cli)
    :
    : "M"(FLASHEND>8192?_VECTORS_SIZE/4:_VECTORS_SIZE/2),
      "M"(FLASHEND>8192?1:0)
    );

    SP=RAMEND;                                      // set stack
    uint8_t* r = (uint8_t*)&__data_start;           // set start of data (ram)
    uint16_t ef = (uint16_t)&__data_load_end;       // set end of data (flash)
    uint8_t* er = (uint8_t*)&__bss_end;             // set end of bss (ram)
    uint8_t data;
    #ifdef RAMPZ
        uint32_t f;
        asm("ldi %C0,hlo8(%1)"          NL
            "ldi %B0,hi8(%1)"           NL
            "ldi %A0,lo8(%1)"           NL
            : "=d" (f)
            : "p" (&__data_load_start)
        );
        #define READ_FLASH pgm_read_byte_far
    #else
        uint16_t f = (uint16_t)&__data_load_start;
        #define READ_FLASH pgm_read_byte
    #endif
    while(r!=er){                                   // while not at end of bss (ram)
        data=0;                                     // assume bss, data = 0
        if((uint16_t)f!=ef){ data = READ_FLASH(f++); }      // if flash, data = read flash
        *r++=data;                                  // data-> ram
        WDR();                                      // clear watchdog
    }
    main();                                         // call main
    SREG=0;                                         // exit from main, cli
    while(1);                                       // then stop
}
//-----------------------------------------------------------------------------------------
//bad interrupts will call __vectors (reset vector), this can be changed as needed
//-----------------------------------------------------------------------------------------
NAKED void __bad_interrupt(void){ __vectors(); }