.section .ivr, "ax", @progbits
.align 1
ivr:
jmp _init_cpu
jmp _ivr_int0
jmp _ivr_int1
jmp _ivr_pcint0
jmp _ivr_pcint1
jmp _ivr_pcint2
jmp _ivr_wdt
jmp _ivr_timer2_compa
jmp _ivr_timer2_compb
jmp _ivr_timer2_ovf
jmp _ivr_timer1_capt
jmp _ivr_timer1_compa
jmp _ivr_timer1_compb
jmp _ivr_timer1_ovf
jmp _ivr_timer0_compa
jmp _ivr_timer0_compb
jmp _ivr_timer0_ovf
jmp _ivr_spi
jmp _ivr_usart_rcx
jmp _ivr_usart_dre
jmp _ivr_usart_tcx
jmp _ivr_adc_rdy
jmp _ivr_eeprom_rdy
jmp _ivr_analogue_cmp
jmp _ivr_twi
jmp _ivr_spm_rdy
_ivr_undefined:
reti
;;; CPU flags initialisation
_init_cpu:
cli ;;; Disable interrupts (the application will reenable when it wants)
ldi r16,0x01 ;;; Sleep = IDLE mode, ENABLED
sts 0x50,r16 ;;; Write sleep control flag
eor r1, r1 ;;; The compiler runtime expects 0 in r1
;;; Clear the BSS region
_init_zero_bss:
ldi r31, hi8(__bss_start) ;;; load BSS start into Z
ldi r30, lo8(__bss_start)
ldi r29, hi8(__bss_end) ;;; load BSS end into Y
ldi r28, lo8(__bss_end)
sub r28, r30 ;;; Y = Y-Z (== length in bytes)
sbc r29, r31
rjmp _init_zero_bss_loop_check
_init_zero_bss_loop:
st Z+, r1 ;;; Store zero @Z and postincrement
subi r28, 1 ;;; Decrement counter Y
sbci r29, 0
_init_zero_bss_loop_check:
brne _init_zero_bss_loop ;;; Loop iff Y != 0
;;; Inititalise RAM
_init_copy_memory:
ldi r31, hi8(__data_load_start) ;;; data segment start into Z
ldi r30, lo8(__data_load_start)
ldi r29, hi8(__data_load_end) ;;; data segment end into Y
ldi r28, lo8(__data_load_end)
ldi r27, hi8(__data_start) ;;; destination address into X
ldi r26, lo8(__data_start)
sub r28, r30 ;;; Y = Y-Z (== length in bytes)
sbc r29, r31
rjmp _init_copy_memory_loop_check
_init_copy_memory_loop:
lpm r0, Z+ ;;; Load from @Z and incremenent pointer
st X+, r0 ;;; Store to @X, increment pointer
subi r28, 1 ;;; Decrement counter Y
sbci r29, 0
_init_copy_memory_loop_check:
brne _init_copy_memory_loop ;;; Loop iff Y != 0
;;; Now jump into the application
_init_runtime:
;;; @todo reset the stack pointer
jmp _oxide_boot ;;; Jump to the application entry point
;;; Utility function for writing to clock prescaler register
;;; We need to guarantee the second write happens within 4 cycles of
;;; the first, so use assembler for this
.global ccp_clkper_write
.type ccp_clkper_write, @function
ccp_clkper_write:
;;; void ccp_io_write(void *ioaddr, char value)
;;; R25 = addrhi
;;; R24 = addrlo
;;; R22 = value
;;; clobbers
;;; R27,R26 (X) = for value of location to write
;;; R23 = CLKPCE value
movw r26,r24 ;; Load addr into X
ldi r23, 0x80 ;; CLKPCE bit set (0b10000000)
st X, r23 ;; First write the change enable flag to @X
st X, r22 ;; Now write the actual value to @X
nop
nop
ret
;;; By default, start all interrupt handlers as doing a no-op
;;; return. Since these symbols are weakly linked, user code can
;;; override each one on demand.
.macro _ivr_default name target=_ivr_undefined
.weak \name
.set \name, \target
.endm
_ivr_default _ivr_int0
_ivr_default _ivr_int1
_ivr_default _ivr_pcint0
_ivr_default _ivr_pcint1
_ivr_default _ivr_pcint2
_ivr_default _ivr_wdt
_ivr_default _ivr_timer2_compa
_ivr_default _ivr_timer2_compb
_ivr_default _ivr_timer2_ovf
_ivr_default _ivr_timer1_capt
_ivr_default _ivr_timer1_compa
_ivr_default _ivr_timer1_compb
_ivr_default _ivr_timer1_ovf
_ivr_default _ivr_timer0_compa
_ivr_default _ivr_timer0_compb
_ivr_default _ivr_timer0_ovf
_ivr_default _ivr_spi
_ivr_default _ivr_usart_rcx
_ivr_default _ivr_usart_dre
_ivr_default _ivr_usart_tcx
_ivr_default _ivr_adc_rdy
_ivr_default _ivr_eeprom_rdy
_ivr_default _ivr_analogue_cmp
_ivr_default _ivr_twi
_ivr_default _ivr_spm_rdy