;/*
; * FreeRTOS Kernel V10.5.1
; * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
; *
; * SPDX-License-Identifier: MIT
; *
; * Permission is hereby granted, free of charge, to any person obtaining a copy of
; * this software and associated documentation files (the "Software"), to deal in
; * the Software without restriction, including without limitation the rights to
; * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
; * the Software, and to permit persons to whom the Software is furnished to do so,
; * subject to the following conditions:
; *
; * The above copyright notice and this permission notice shall be included in all
; * copies or substantial portions of the Software.
; *
; * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
; * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
; * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
; * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
; * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; *
; * https://www.FreeRTOS.org
; * https://github.com/FreeRTOS
; *
; */

#include "porthardware.h"

; Declare all extern symbols here - including any ISRs that are referenced in
; the vector table.

; ISR functions
; -------------
EXTERN TICK_INT

; Functions used by scheduler
; ---------------------------
EXTERN vTaskSwitchContext
EXTERN pxCurrentTCB
EXTERN xTaskIncrementTick
EXTERN uxCriticalNesting

; Functions implemented in this file
; ----------------------------------
PUBLIC vPortYield
PUBLIC vPortYieldFromTick
PUBLIC vPortYieldFromISR
PUBLIC vPortStart

; Interrupt vector table.
; -----------------------
;
; For simplicity the RTOS tick interrupt routine uses the __task keyword.
; As the IAR compiler does not permit a function to be declared using both
; __task and __interrupt, the use of __task necessitates that the interrupt
; vector table be setup manually.
;
; To write an ISR, implement the ISR function using the __interrupt keyword
; but do not install the interrupt using the "#pragma vector=ABC" method.
; Instead manually place the name of the ISR in the vector table using an
; ORG and jmp instruction as demonstrated below.
; You will also have to add an EXTERN statement at the top of the file.

    ASEG

        ORG TICK_INT_vect                   ; Vector address
            jmp TICK_INT                    ; ISR

    RSEG CODE

CLR_INT MACRO  FLAG_REG, FLAG_MASK
        st -y, r16
        ldi r16, FLAG_MASK
        sts FLAG_REG, r16
        ld r16, y+
        
        ENDM

; Saving and Restoring a Task Context and Task Switching
; ------------------------------------------------------
;
; The IAR compiler does not fully support inline assembler, so saving and
; restoring a task context has to be written in an asm file.
;
; vPortYield() and vPortYieldFromTick() are usually written in C.  Doing
; so in this case would required calls to be made to portSAVE_CONTEXT() and
; portRESTORE_CONTEXT().  This is dis-advantageous as the context switch
; function would require two extra jump and return instructions over the
; WinAVR equivalent.
;
; To avoid this I have opted to implement both vPortYield() and
; vPortYieldFromTick() in this assembly file.  For convenience
; portSAVE_CONTEXT and portRESTORE_CONTEXT are implemented as macros.

portSAVE_CONTEXT MACRO
    st  -y, r0          ; First save the r0 register - we need to use this.
    in  r0, SREG        ; Obtain the SREG value so we can disable interrupts...
    cli                 ; ... as soon as possible.
    st  -y, r0          ; Store the SREG as it was before we disabled interrupts.

    in  r0, RAMPZ
    st  -y, r0

    in  r0, SPL         ; Next store the hardware stack pointer.  The IAR...
    st  -y, r0          ; ... compiler uses the hardware stack as a call stack ...
    in  r0, SPH         ; ...  only.
    st  -y, r0

    st  -y, r1          ; Now store the rest of the registers.  Dont store the ...
    st  -y, r2          ; ... the Y register here as it is used as the software
    st  -y, r3          ; stack pointer and will get saved into the TCB.
    st  -y, r4
    st  -y, r5
    st  -y, r6
    st  -y, r7
    st  -y, r8
    st  -y, r9
    st  -y, r10
    st  -y, r11
    st  -y, r12
    st  -y, r13
    st  -y, r14
    st  -y, r15
    st  -y, r16
    st  -y, r17
    st  -y, r18
    st  -y, r19
    st  -y, r20
    st  -y, r21
    st  -y, r22
    st  -y, r23
    st  -y, r24
    st  -y, r25
    st  -y, r26
    st  -y, r27
    st  -y, r30
    st  -y, r31

    lds r0, uxCriticalNesting
    st  -y, r0                  ; Store the critical nesting counter.

    lds r26, pxCurrentTCB       ; Finally save the software stack pointer (Y ...
    lds r27, pxCurrentTCB + 1   ; ... register) into the TCB.
    st  x+, r28
    st  x+, r29

    ENDM


portRESTORE_CONTEXT MACRO
    lds r26, pxCurrentTCB
    lds r27, pxCurrentTCB + 1       ; Restore the software stack pointer from ...
    ld  r28, x+                     ; the TCB into the software stack pointer (...
    ld  r29, x+                     ; ... the Y register).

    ld  r0, y+
    sts uxCriticalNesting, r0

    ld  r31, y+                     ; Restore the registers down to R0.  The Y
    ld  r30, y+                     ; register is missing from this list as it
    ld  r27, y+                     ; has already been restored.
    ld  r26, y+
    ld  r25, y+
    ld  r24, y+
    ld  r23, y+
    ld  r22, y+
    ld  r21, y+
    ld  r20, y+
    ld  r19, y+
    ld  r18, y+
    ld  r17, y+
    ld  r16, y+
    ld  r15, y+
    ld  r14, y+
    ld  r13, y+
    ld  r12, y+
    ld  r11, y+
    ld  r10, y+
    ld  r9, y+
    ld  r8, y+
    ld  r7, y+
    ld  r6, y+
    ld  r5, y+
    ld  r4, y+
    ld  r3, y+
    ld  r2, y+
    ld  r1, y+

    ld  r0, y+                      ; The next thing on the stack is the ...
    out SPH, r0                     ; ... hardware stack pointer.
    ld  r0, y+
    out SPL, r0

    ld  r0, y+
    out RAMPZ, r0

    ld  r0, y+                      ; Next there is the SREG register.
    out SREG, r0

    ld  r0, y+                      ; Finally we have finished with r0, so restore r0.

    ENDM



; vPortYield(), vPortYieldFromTick() and vPortYieldFromISR()
; -------------------------------------
;
; Manual and preemptive context switch functions respectively.
; The IAR compiler does not fully support inline assembler,
; so these are implemented here rather than the more usually
; place of within port.c.

vPortYield:
    portSAVE_CONTEXT                ; Save the context of the current task.
    call vTaskSwitchContext         ; Call the scheduler.
    portRESTORE_CONTEXT             ; Restore the context of whichever task the ...
    ret                             ; ... scheduler decided should run.

vPortYieldFromTick:
    CLR_INT INT_FLAGS, INT_MASK     ; Clear tick interrupt flag

    portSAVE_CONTEXT                ; Save the context of the current task.    
    call xTaskIncrementTick         ; Call the timer tick function.
    tst r16
    breq SkipTaskSwitch
    call vTaskSwitchContext         ; Call the scheduler.

SkipTaskSwitch:
    portRESTORE_CONTEXT             ; Restore the context of whichever task the ...
    reti                            ; ... scheduler decided should run.

vPortYieldFromISR:
    portSAVE_CONTEXT                ; Save the context of the current task.
    call vTaskSwitchContext         ; Call the scheduler.
    portRESTORE_CONTEXT             ; Restore the context of whichever task the ...
    reti                            ; ... scheduler decided should run.

; vPortStart()
; ------------
;
; Again due to the lack of inline assembler, this is required
; to get access to the portRESTORE_CONTEXT macro.

vPortStart:
    portRESTORE_CONTEXT
    ret

; Just a filler for unused interrupt vectors.
vNoISR:
    reti

    END
