aboutsummaryrefslogtreecommitdiff
path: root/avr/core.S
blob: da35058682939fe848c17a7715add8ff2147ae4c (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/************************************************************************/
/*                                                                      */
/*  File:           avr/core.s                                          */
/*  Description:    cpu dependent (or optimized) part of kernel         */
/*  Version:        1.0                                                 */
/*  Author:         Otto Mattik                                         */
/*                                                                      */
/*  (C)Copyright Otto Mattik 2014-2021.                                 */
/*                                                                      */
/*  This file is a part of 'armen' (a tiny operating system).           */
/*  'armen' is distributed under the CeCILL-V2.1 licence. For more      */
/*  details about this licence, please visit the website cecill.info    */
/*                                                                      */
/************************************************************************/

#ifndef F_CPU
  #error "cpu clock frequency is not defined"
#endif

#if (F_CPU == 16000000UL)
  #define CLOCK_PRESCALE    3
  #define CLOCK_TICKS       250
#elif (F_CPU == 12000000UL)
  #define CLOCK_PRESCALE    3
  #define CLOCK_TICKS       187
#elif (F_CPU == 8000000UL)
  #define CLOCK_PRESCALE    3
  #define CLOCK_TICKS       125
#elif (F_CPU == 4000000UL)
  #define CLOCK_PRESCALE    3
  #define CLOCK_TICKS       62
#else
  #error "no parameters for the clock frequency"
#endif

#define SPH     0x3E
#define SPL     0x3D
#define SREG    0x3F

#include <avr/io.h>

#ifdef __AVR_HAVE_JMP_CALL__
  #define JMP   jmp
  #define CALL  call
#else
  #define JMP   rjmp
  #define CALL  rcall
#endif

    .text

    /*
     * here we go ...
     * in:
     * out:
    */ 
    .global armen_start
armen_start:
    cli
    /*
     * initialize timer 0 to 1kHz (timer 1 if tiny85)
    */
#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny85V__)
    ldi r24, _BV(CTC1)|_BV(CS13)|CLOCK_PRESCALE
    sts TCCR1, r24              ; CTC mode + prescaler
    ldi r24, CLOCK_TICKS
    sts OCR1A, r24              ; counter A
    ldi r24, _BV(OCIE1A)
    sts TIMSK, r24              ; enable comparator
#else
    ldi r24, _BV(WGM01)
    sts TCCR0A, r24             ; CTC mode
    ldi r24, CLOCK_PRESCALE
    sts TCCR0B, r24             ; prescaler
    ldi r24, CLOCK_TICKS
    sts OCR0A, r24              ; counter A
    ldi r24, _BV(OCIE0A)
  #ifdef TIMSK
    sts TIMSK, r24              ; enable comparator
  #else
    sts TIMSK0, r24             ; enable comparator
  #endif
#endif
    /*
     * initialize the os
    */
    ldi r23, hi8(RAMEND)
    ldi r22, lo8(RAMEND)        ; arg2 = end of ram (stack)
    ldi r25, hi8(__bss_end)
    ldi r24, lo8(__bss_end)     ; arg1 = end of bss (heap)
    CALL _sys_init              ; see core.c
    sei
    ret

    /*
     * clock (1 ms)
     * in:
     * out:
    */
    .global TIMER0_COMPA_vect
TIMER0_COMPA_vect:
    CALL _save_context
    push r24
    push r25
    CALL _sys_clock             ; see core.c
    pop r25
    pop r24
.L1:
    CALL _scheduler
.L2:
    /*
     * restore context
    */
    pop r0
    out SREG, r0
    pop r0
    pop r1
    pop r2
    pop r3
    pop r4
    pop r5
    pop r6
    pop r7
    pop r8
    pop r9
    pop r10
    pop r11
    pop r12
    pop r13
    pop r14
    pop r15
    pop r16
    pop r17
    pop r18
    pop r19
    pop r20
    pop r21
    pop r22
    pop r23
    pop r24
    pop r25
    pop r26
    pop r27
    pop r28
    pop r29
    pop r30
    pop r31
    reti                        ; works well because is same as ret (for stack)
                                ; differ only in global interrupt flag

    /*
     * call the scheduler
     * in:
     * out:
    */
    .global _schedule
_schedule:
    CALL _save_context
    JMP .L1

    /*
     * set initial thread context
     * in:
     *   r24/r25 entry point (WARNING: must be in first flash 128 kb)
     *   r22/r23 thread call parameter
     *   r20/r21 stack pointer
     * out:
     *   new SP
    */
    .global _init_context
_init_context:
    push ZH
    push ZL
    push r17
    push r16
    ldi r16, 33                 ; size for all general + SREG registers
#ifdef __AVR_3_BYTE_PC__
    subi r20, 38
#else
    subi r20, 36                ; add 2 PC size for ret(i) and _thread_exit
#endif
    sbci r21, 0
    mov ZH, r21
    mov ZL, r20                 ; top of new stack
    clr r17
.L3:
    st Z+, r17                  ; set all registers to 0
    subi r16, 1
    brne .L3
#ifdef __AVR_3_BYTE_PC__
    st Z+, r17
#endif
    st Z+, r25
    st Z+, r24                  ; entry point for ret(i)
    ldi r25, hi8(_thread_exit)
    ldi r24, lo8(_thread_exit)
#ifdef __AVR_3_BYTE_PC__
    st Z+, r17
#endif
    st Z+, r25
    st Z+, r24                  ; entry point for _thread_exit
    cpi r22, 0
    cpc r23, r17
    breq .L4
    mov ZH, r21
    mov ZL, r20
    std Z+24, r22
    std Z+25, r23               ; set parameter in r24/r25 location
.L4:
    mov r25, r21
    mov r24, r20
    subi r24, 1
    sbci r25, 0                 ; new SP value
    pop r16
    pop r17
    pop ZL
    pop ZH
    ret

    /*
     * save all general + SREG registers
     * in:
     * out:
     *   new SP
    */
    .global _save_context
_save_context:
#ifdef __AVR_3_BYTE_PC__
    pop r1                      ; bits 17-22 of PC
#endif
    push YH
    push YL
    push r27
    push r26
    push r25                    ; registers sets in the right place
    in r25, SREG                ; save SREG
    in YH, SPH
    in YL, SPL
    adiw YL, 6
    ld r27, Y+
    ld r26, Y+                  ; save PC for return
    st -Y, r31
    st -Y, r30                  ; set Z instead of PC in stack
    push r24
    push r23
    push r22
    push r21
    push r20
    push r19
    push r18
    push r17
    push r16
    push r15
    push r14
    push r13
    push r12
    push r11
    push r10
    push r9
    push r8
    push r7
    push r6
    push r5
    push r4
    push r3
    push r2
#ifdef __AVR_3_BYTE_PC__
    clr r2
    push r2
#else
    push r1
#endif
    push r0
    push r25                    ; store SREG
    in r25, SPH
    in r24, SPL                 ; return new SP
    push r26
    push r27                    ; restore PC (context: 32+1+(2|3) bytes)
#ifdef __AVR_3_BYTE_PC__
    push r1
#endif
    ret

    /*
     * switch thread context (called by scheduler)
     * in:
     *   r24/r25 new stack pointer
     * out:
     *   no return
    */
    .global _restore_context
_restore_context:
#ifdef __AVR_3_BYTE_PC__
    pop r0
    pop r0
#endif
    pop r0
    pop r0                          ; PC stacked at _restore_context call
    pop r0
    pop r0                          ; PC stacked at _scheduler call
    out SPH, r25
    out SPL, r24
    JMP .L2

    /*
     * disable interruptions
     * in:
     * out:
     *   interruption flag
    */
    .global _disable_intr
_disable_intr:
    in r24, SREG
    cli
    andi r24, 0x80
    ret

    /*
     * enable interruptions
     * in:
     *   r24 interruption flag
     * out:
    */
    .global _enable_intr
_enable_intr:
    cpi r24, 0
    breq .L5
    sei
.L5:
    ret

    /*
     * delay loops
     * in:
     *   r24/r25 count of microseconds
     * out:
    */
    .global delay
delay:
#if (F_CPU > 12000000UL)
    nop
    nop
    nop
    nop                             ; 4 cycles
#endif
#if (F_CPU > 8000000UL)
    nop
    nop
    nop
    nop                             ; 4 cycles
#endif
#if (F_CPU > 4000000UL)
    nop
    nop
    nop
    nop                             ; 4 cycles
#endif
    sbiw r24, 1                     ; 2 cycles
    brne delay                      ; 2 cycles or 1 if false
    ret

    /*
     * this thread is elected when all others are suspend
     * it do nothing (set cpu in sleep mode ???)
     * in:
     * out:
    */ 
    .global _idle
_idle:
    rjmp _idle

    /*
     * test and set a mutex (atomic operation)
     * in:
     *   r24/r25 address of mutex (byte)
     * out:
     *   previous state of mutex
    */
    .global test_and_set
test_and_set:
    push ZH
    push ZL
    mov ZH, r25
    mov ZL, r24
#ifdef XMEGA
    ldi r24, 1
    xch Z, r24
#else
    ldi r25, 1
    cli
    ld r24, Z
    st Z, r25
    sei
#endif
    pop ZL
    pop ZH
    ret                             ; r24 = previous state of mutex