4AvrAscii will now be bad jump resistant and
spm safe

/* bootloader reset address */
int main(void){

__asm__ __volatile__ (

"bl_addr: \n\t" //will be bootloader reset address
//used for an rjmp reference, as .text
//section starts at bootloader address

//-------------------------------------------------------------------
//first three instructions will cause an exit from the bootloader
//if the watchdog flag is set
//
//the fourth instruction sets up to enable ivsel change, the fifth
//sets ivce in MCUCR or GIFR
//
//any 'errant' jump into the bootloader to any of the first five
//instructions will still have to pass the PORF=1 test which takes
//place below the change enable instruction
//-------------------------------------------------------------------

"in r0,%[flags] \n\t" //get flags into r0
"sbrc r0,%[wdrf] \n\t" //skip if wdrf=0,
"rjmp bl_addr+%[bls] \n\t" //else exit (wdrf=1)
"ldi r31,%[ivce] \n\t" //r31=(1<<ivce),
"out %[irqctrl],r31 \n\t" //enable iv change

//-------------------------------------------------------------------
//any 'errant' jump into the bootloader anywhere below this line
//will not cause ivsel to be set
//-------------------------------------------------------------------

"in r0,%[flags] \n\t" //clk+1, get flags into r0 again
"ldi r31,1<<%[ivsel] \n\t" //clk+2, r31=ivsel
"sbrc r0,%[porf] \n\t" //clk+3, if porf=0 skip next
"out %[irqctrl],r31 \n\t" //clk+4, ivsel=1

//-------------------------------------------------------------------
//now check if the change occurred, if not, porf was 0
//will leave bootloader with i/o registers untouched, including
//SREG, (GIFR or MCUCR will be 0 though, which IS the inital value
//of those registers), r0 will =flags,r31 will =irq_ctrl register
//-------------------------------------------------------------------

"in r31,%[irqctrl] \n\t" //get ivsel into r31
"sbrs r31,%[ivsel] \n\t" //if ivsel=1, skip next
"rjmp bl_addr+%[bls] \n\t" //else ivsel=0, change didn't work
:
:
[flags] "I" (_SFR_IO_ADDR(RESET_FLAGS)),
[irqctrl] "I" (_SFR_IO_ADDR(IRQ_CONTROL)),
[wdrf] "I" (WDRF),
[porf] "I" (PORF),
[ivce] "M" (1<<IVCE),
[ivsel] "I" (IVSEL),
[bls] "i" (BL_SIZE_BYTES)
);

/*
.....the rest of bootloader code
*/

}

/*-----------------------------------------------------------------------------------------------------------
check if we got into the bootloader through the 'front door' (ivsel will =1)
-----------------------------------------------------------------------------------------------------------*/

void safeT_check(void){
__asm__ __volatile__ (
"in r24,%[irqctrl] \n\t" //get ivsel register
"sbrs r24,%[ivsel] \n\t" //if ivsel=1, skip next
"rjmp bl_addr+%[bls] \n\t" //else ivsel=0, exit bootloader
"ret \n\t" //done
::
[irqctrl] "I" (_SFR_IO_ADDR(IRQ_CONTROL)),
[ivsel] "I" (IVSEL),
[bls] "i" (BL_SIZE_BYTES)
);
}



/*-----------------------------------------------------------------------------------------------------------
SPM function

r1:r0 will have to be setup before this function is called (page fill needs data in r1:r0)
r25:r24 will have the Z address, r22 will have the spm command

spm will fail if IVSEL=0 (and exit bootloader)
spm will fail on any 'errant' jump into the bootloader (assuming IVSEL=0)
spm will fail on any 'errant' jump into the bootloader (assuming spm register already setup) as an
rjmp/rcall takes 2 cycles, so if it 'lands' on anthing other than 'spm', it will have not met the 4 cycle
requirement, or it will exit the bootloader if it 'lands' on the rjmp instruction

so, in order for an 'errant' jump into the bootloader to do an spm, it will either have had to set IVSEL,
OR setup spm register, then 'jump' exactly on the spm instruction

this function could be used in an application if wanted- save sreg,cli,get IVSEL=1,setup r1:r0 if needed,
call this function,get IVSEL=0,restore sreg

if cmd=__BOOT_PAGE_FILL, no spm wait or rww enable will take place- spm wait not needed for page fill, and
enable of rww not wanted as it will erase the page buffer
-----------------------------------------------------------------------------------------------------------*/

void do_spm(uint16_t addr,uint8_t cmd){ //r25:r24=addr,r22=cmd,(r1,r0 on your own if needed)

__asm__ __volatile__ (
"movw r30,r24 \n\t" //put addr into Z
"cpi r22,%[bpf] \n\t" //if page fill,
"breq 2f \n\t" //no rww enable wanted, just spm
"rcall 2f \n\t" //call spm, return to next instruction

"1: \n\t"
"lds r0,%[spmreg] \n\t" //get spm register
"sbrc r0,%[spmen] \n\t" //if spmen=0, skip next
"rjmp 1b \n\t" //else check again
"ldi r22,%[rwwen] \n\t" //enable rww section

"2: \n\t"
"sts %[spmreg],r22 \n\t" //load spm command
"in r22,%[irqctrl] \n\t" //clk+1, check if ivsel=1
"sbrs r22,%[ivsel] \n\t" //clk+2+3, skip next if ivsel=1
"rjmp bl_addr+%[bls] \n\t" //else ivsel=0, exit bootloader
"spm \n\t" //clk+4, one spm in bootloader, this is it
"clr r1 \n\t" //need to clear r1 in case was page fill
"ret \n\t" //ret (if page fill), or back to rww enable
::
[bpf] "M" (__BOOT_PAGE_FILL),
[spmreg] "i" (_SFR_MEM_ADDR(__SPM_REG)),
[spmen] "M" (SPMEN),
[rwwen] "M" (__BOOT_RWW_ENABLE),
[irqctrl] "I" (_SFR_IO_ADDR(IRQ_CONTROL)),
[ivsel] "I" (IVSEL),
[bls] "i" (BL_SIZE_BYTES)
);
}