/************************************************************************/ /* */ /* 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 #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