aboutsummaryrefslogtreecommitdiffhomepage
path: root/targets/avr.S
blob: 568ae6aed0953680184b7b6b1ec95a0307d83779 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
; This file provides common code across AVRs that cannot be implemented directly
; in Go.
; The reset vector is device-specific and is generated by tools/gen-device-avr.py.

; These definitions are necessary because LLVM does not yet know these register
; aliases. See: https://reviews.llvm.org/D96492
#define xl r26
#define xh r27
#define yl r28
#define yh r29
#define zl r30
#define zh r31

; Ugly hack until https://reviews.llvm.org/D137572 is merged.
#if !defined(__AVR_HAVE_ELPM__) && defined(__flash1)
#define __AVR_HAVE_ELPM__
#endif

; Startup code
.section .text.__vector_RESET
.global  __vector_RESET
__vector_RESET:
    clr  r1          ; r1 is expected to be 0 by the C calling convention

    ; Set up the stack pointer.
    ldi  xl, lo8(_stack_top)
    ldi  xh, hi8(_stack_top)
    out  0x3d, xl; SPL
    out  0x3e, xh; SPH

    ; Subtract one from the stack pointer, so it doesn't point in the .data section.
    push r0

    ; Initialize .data
init_data:
    ldi  xl, lo8(_sdata)
    ldi  xh, hi8(_sdata)
    ldi  yl, lo8(_edata)
    ldi  yh, hi8(_edata)
    ldi  zl, lo8(_sidata)
    ldi  zh, hi8(_sidata)
#ifdef __AVR_HAVE_ELPM__
    ldi  r16, hh8(_sidata) ; RAMPZ = hh8(_sidata)
    out  0x3b, r16
#endif
init_data_loop:
    cp   xl, yl         ; if x == y
    cpc  xh, yh
    breq init_data_end  ; goto main
#ifdef __AVR_HAVE_ELPM__
    elpm r0, Z+         ; r0 = *(z++)
#else
    lpm  r0, Z+         ; r0 = *(z++)
#endif
    st   X+, r0         ; *(x++) = r0
    rjmp init_data_loop ; goto init_data_loop
init_data_end:

    ; main will be placed right after here by the linker script so there's no
    ; need to jump.


; The only thing this WDT handler really does is disable itself, to get out of
; sleep mode.
.section .text.__vector_WDT
.global  __vector_WDT
__vector_WDT:
    push r16

    clr  r16
    wdr            ; Reset watchdog
    out  0x34, r16 ; Clear reset reason (MCUSR)

    ; part 1: set WDCE and WDE to enable editing WDTCSR
    lds  r16, 0x60 ; r16 = WDTCSR
    ori  r16, 0x18 ; r16 |= WDCE | WDE
    sts  0x60, r16 ; WDTCSR = r16

    ; part 2: within 4 clock cycles, set the new value for WDTCSR
    clr  r16
    sts  0x60, r16 ; WDTCSR = 0

    pop  r16
    reti