// Copyright (c) 2020, XMOS Ltd, All rights reserved

#include "rtos_support_rtos_config.h"

/* The FreeRTOS interrupt code calls vTaskSwitchContext.
Therfore it must be added to the rtos_isr group with the
rest of the ISR callback functions. */
.weak _fptrgroup.rtos_isr.nstackwords.group
.add_to_set _fptrgroup.rtos_isr.nstackwords.group, vTaskSwitchContext.nstackwords, vTaskSwitchContext

.globl kexcept
.align 128              /* align the kernel section to 128 bytes */
.type  kexcept,@function
.issue_mode dual
.cc_top kexcept.function, kexcept
kexcept:
  bu _DoException       /* This symbol is generated by the toolchain and */
                        /* provides graceful exception handling */

_yield:
 {set    sp,     r4                  /* Restore the task's SP to save the rest of its context. */
  get    r11,    id}                 /* Get the logical core ID into r11. */
  ldaw   r0,     dp[rtos_core_map]
  ldw    r0,     r0[r11]             /* Translate to the RTOS core ID into r0 */
  bu _yield_continue                 /* Skip the ulPortYieldRequired check and jump right to */
                                     /* the context save and switch. Also skips saving SPC */
                                     /* since the kcall handler has already saved it. */

.align 64
kcall:
  /* start saving the thread's context */
  extsp RTOS_SUPPORT_INTERRUPT_STACK_GROWTH
  stw    r1,     sp[9]
  stw    r11,    sp[19]

  /* kcall sets SPC to the instruction of the kcall rather than the next instruction */
  /* so we need to adjust the SPC value that we save to the stack: */
  stw    spc,    sp[1]   /* save the saved program counter onto the stack... */
  ldw    r1,     sp[1]   /* so that we can load it into r1 (which we have already saved). */
  add    r1,     r1,   4 /* Add 4 to the spc to make it point to the instruction after the kcall. */
 {stw    r1,     sp[1]   /* Now save it to the stack. */

  /* kcall uses the same common function as interrupt callbacks. */
  /* tell it to call _yield above. */
  ldap   r11,    _yield}
  mov    r1,     r11

  /* fall into rtos_interrupt_callback_common */

.globl rtos_interrupt_callback_common
rtos_interrupt_callback_common:
  /* This is the body of the RTOS _xcore_c_interrupt_callback_XXX functions. */
  /* r1 = interrupt_callback_t function */

  /* Save the thread's context onto the thread's stack. */
  /* The stack was extended for this by the wrapper function. */
  /* Begin only by saving some registers. The rest will be saved */
  /* later if vTaskSwitchContext() needs to be called. */
  /* DP and CP need to be saved because these are restored for the kernel ISR. */
  /* LR needs to be saved because it is clobbered when calling the callback. */
  /* r0-r3, and r11 need to be saved because the callback may clobber them. */
  /* r4 is saved because it is used here to hold the task SP. */

  stw    dp,     sp[5]
  stw    cp,     sp[6]
  stw    lr,     sp[7]
  stw    r0,     sp[8]
/*stw    r1,     sp[9]      already saved by the wrapper function. */
  stw    r2,     sp[10]
  stw    r3,     sp[11]
 {stw    r4,     sp[12]
/*stw    r11,    sp[19]     already saved by the wrapper function. */

  ldaw   r4,     sp[0]}  /* Get value of current stackpointer into r4. */

 {kentsp 0               /* switch to the kernel stack. */
                         /* The value 0 is safe to use since we don't need the SP */
                         /* that it saves to KSP[0]. We already have it in r4. */

  get    r11,    ed}     /* Get the event data... */
  ldw    dp,     sp[3]   /* (Restore CP and DP required for the RTOS ISR */
  ldw    cp,     sp[4]   /* in case the active thread has modified them.) */
 {mov    r0,     r11     /* ...into the first argument for the callback function, */
  bla    r1}             /* and call the callback function. */

 {set    sp,     r4      /* Restore the task's SP now. */

  get    r11,    id}                           /* Get the logical core ID into r11. */
  ldaw   r0,     dp[rtos_core_map]
  ldw    r0,     r0[r11]                       /* Translate to the RTOS core ID into r0. */
  ldaw   r2,     dp[ulPortYieldRequired]       /* Get the yield required array into r2. */
  ldw    r1,     r2[r0]                        /* Is a yield required for this core? */
 {bf     r1,     _freertos_restore_ctx_partial /* If not, restore the context now. */
  ldc    r1,     0}
  stw    r1,     r2[r0]                        /* Otherwise, clear the yield required flag. */

  /* Save the rest of the current task's context. */

  /* Save standard xs2 regs */
  stw    spc,    sp[1]
_yield_continue:
  stw    ssr,    sp[2]
  stw    sed,    sp[3]
  stw    et,     sp[4]
  stw    r5,     sp[13]
  stw    r6,     sp[14]
  stw    r7,     sp[15]
  stw    r8,     sp[16]
  stw    r9,     sp[17]
  stw    r10,    sp[18]
#if 1
  /* Save VPU status and headroom */
  vgetc  r11
 {stw    r11,    sp[20]
  /* Save VPU regs */
  ldaw   r11,    sp[21]}
 {vstr   r11[0]
  ldaw   r11,    sp[29]}
 {vstd   r11[0]
  ldaw   r11,    sp[37]}
  vstc   r11[0]
#endif
  ldw    r5,     dp[xcorePvtTCBContainer]
  ldw    r1,     r5[r0]            /* Get this core's current TCB pointer into r1. */
  stw    r4,     r1[0x0]           /* Save the current task's SP to the first */
                                   /* word (top of stack) in the current TCB. */

 {kentsp 0                         /* switch back to the kernel stack. */

  mov    r6,     r0}               /* copy the RTOS core ID into r6 so we don't lose it. */
  ldap   r11,    vTaskSwitchContext
  bla    r11             /* Finally call vTaskSwitchContext(core_id) now that the task's */
                         /* entire context is saved. Note the core id in r0 is the argument. */

//krestsp 0              /* unnecessary since KSP is already set and the SP */
                         /* is being restored next from the current TCB. */

.globl _freertos_restore_ctx
_freertos_restore_ctx:

  ldw    r0,     r5[r6]  /* get this core's current TCB pointer into r0 */
  ldw    r0,     r0[0x0] /* Get the top of the stack from the current TCB... */
  set    sp,     r0      /* into the stack pointer register. */

  /* Restore the current task's context */
#if 1
  /* Restore VPU regs */
  ldaw   r11,    sp[37]
 {vldc   r11[0]
  ldaw   r11,    sp[29]}
 {vldd   r11[0]
  ldaw   r11,    sp[21]}
  vldr   r11[0]
  /* Restore VPU status and headroom */
  ldw    r11,    sp[20]
  vsetc  r11
#endif
  /* Restore standard xs2 regs */
  ldw    spc,    sp[1]
  ldw    ssr,    sp[2]
  ldw    sed,    sp[3]
  ldw    et,     sp[4]
  ldw    r5,     sp[13]
  ldw    r6,     sp[14]
  ldw    r7,     sp[15]
  ldw    r8,     sp[16]
  ldw    r9,     sp[17]
  ldw    r10,    sp[18]
_freertos_restore_ctx_partial:
  ldw    dp,     sp[5]
  ldw    cp,     sp[6]
  ldw    lr,     sp[7]
  ldw    r0,     sp[8]
  ldw    r1,     sp[9]
  ldw    r2,     sp[10]
  ldw    r3,     sp[11]
  ldw    r4,     sp[12]
 {ldw    r11,    sp[19]

  /* shrink the stack by the size of the context just restored */
  ldaw   sp,     sp[RTOS_SUPPORT_INTERRUPT_STACK_GROWTH]}

  kret                   /* exit kernel mode and return to the thread */

.cc_bottom kexcept.function

