avr-oxide 0.2.2

An extremely simple Rusty operating system for AVR microcontrollers
.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