/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation 
 * 
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 * 
 * SPDX-License-Identifier: MIT
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** ThreadX Component                                                     */
/**                                                                       */
/**   Thread                                                              */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#include "tx_port.h"

    .section .text
/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _tx_thread_context_save                            RISC-V64/GNU     */
/*                                                           6.2.1        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Scott Larson, Microsoft Corporation                                 */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function saves the context of an executing thread in the       */
/*    beginning of interrupt processing.  The function also ensures that  */
/*    the system stack is used upon return to the calling ISR.            */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    ISRs                                                                */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  03-08-2023      Scott Larson            Initial Version 6.2.1         */
/*                                                                        */
/**************************************************************************/
/* VOID   _tx_thread_context_save(VOID)
{  */
    .global      _tx_thread_context_save
_tx_thread_context_save:

    /* Upon entry to this routine, it is assumed that interrupts are locked
       out and the interrupt stack fame has been allocated and x1 (ra) has
       been saved on the stack. */

    STORE   x5, 19*REGBYTES(sp)                         // First store t0 and t1
    STORE   x6, 18*REGBYTES(sp)

    la      x5, _tx_thread_system_state                 // Pickup address of system state
    LOAD    x6, 0(x5)                                   // Pickup system state

    /* Check for a nested interrupt condition.  */
    /* if (_tx_thread_system_state++)
    {  */
    beqz    x6, _tx_thread_not_nested_save              // If 0, first interrupt condition
    addi    x6, x6, 1                                   // Increment the interrupt counter
    STORE   x6, 0(x5)                                   // Store the interrupt counter

    /* Nested interrupt condition.
       Save the reset of the scratch registers on the stack and return to the
       calling ISR.  */

    STORE   x7,  17*REGBYTES(sp)                        // Store t2
    STORE   x8,  12*REGBYTES(sp)                        // Store s0
    STORE   x10, 27*REGBYTES(sp)                        // Store a0
    STORE   x11, 26*REGBYTES(sp)                        // Store a1
    STORE   x12, 25*REGBYTES(sp)                        // Store a2
    STORE   x13, 24*REGBYTES(sp)                        // Store a3
    STORE   x14, 23*REGBYTES(sp)                        // Store a4
    STORE   x15, 22*REGBYTES(sp)                        // Store a5
    STORE   x16, 21*REGBYTES(sp)                        // Store a6
    STORE   x17, 20*REGBYTES(sp)                        // Store a7
    STORE   x28, 16*REGBYTES(sp)                        // Store t3
    STORE   x29, 15*REGBYTES(sp)                        // Store t4
    STORE   x30, 14*REGBYTES(sp)                        // Store t5
    STORE   x31, 13*REGBYTES(sp)                        // Store t6
    csrr    t0, mepc                                    // Load exception program counter
    STORE   t0, 30*REGBYTES(sp)                         // Save it on the stack

    /* Save floating point scratch registers.  */
#if defined(__riscv_float_abi_single)
    fsw     f0, 31*REGBYTES(sp)                         // Store ft0
    fsw     f1, 32*REGBYTES(sp)                         // Store ft1
    fsw     f2, 33*REGBYTES(sp)                         // Store ft2
    fsw     f3, 34*REGBYTES(sp)                         // Store ft3
    fsw     f4, 35*REGBYTES(sp)                         // Store ft4
    fsw     f5, 36*REGBYTES(sp)                         // Store ft5
    fsw     f6, 37*REGBYTES(sp)                         // Store ft6
    fsw     f7, 38*REGBYTES(sp)                         // Store ft7
    fsw     f10,41*REGBYTES(sp)                         // Store fa0
    fsw     f11,42*REGBYTES(sp)                         // Store fa1
    fsw     f12,43*REGBYTES(sp)                         // Store fa2
    fsw     f13,44*REGBYTES(sp)                         // Store fa3
    fsw     f14,45*REGBYTES(sp)                         // Store fa4
    fsw     f15,46*REGBYTES(sp)                         // Store fa5
    fsw     f16,47*REGBYTES(sp)                         // Store fa6
    fsw     f17,48*REGBYTES(sp)                         // Store fa7
    fsw     f28,59*REGBYTES(sp)                         // Store ft8
    fsw     f29,60*REGBYTES(sp)                         // Store ft9
    fsw     f30,61*REGBYTES(sp)                         // Store ft10
    fsw     f31,62*REGBYTES(sp)                         // Store ft11
    csrr    t0, fcsr
    STORE   t0, 63*REGBYTES(sp)                         // Store fcsr
#elif defined(__riscv_float_abi_double)
    fsd     f0, 31*REGBYTES(sp)                         // Store ft0
    fsd     f1, 32*REGBYTES(sp)                         // Store ft1
    fsd     f2, 33*REGBYTES(sp)                         // Store ft2
    fsd     f3, 34*REGBYTES(sp)                         // Store ft3
    fsd     f4, 35*REGBYTES(sp)                         // Store ft4
    fsd     f5, 36*REGBYTES(sp)                         // Store ft5
    fsd     f6, 37*REGBYTES(sp)                         // Store ft6
    fsd     f7, 38*REGBYTES(sp)                         // Store ft7
    fsd     f10,41*REGBYTES(sp)                         // Store fa0
    fsd     f11,42*REGBYTES(sp)                         // Store fa1
    fsd     f12,43*REGBYTES(sp)                         // Store fa2
    fsd     f13,44*REGBYTES(sp)                         // Store fa3
    fsd     f14,45*REGBYTES(sp)                         // Store fa4
    fsd     f15,46*REGBYTES(sp)                         // Store fa5
    fsd     f16,47*REGBYTES(sp)                         // Store fa6
    fsd     f17,48*REGBYTES(sp)                         // Store fa7
    fsd     f28,59*REGBYTES(sp)                         // Store ft8
    fsd     f29,60*REGBYTES(sp)                         // Store ft9
    fsd     f30,61*REGBYTES(sp)                         // Store ft10
    fsd     f31,62*REGBYTES(sp)                         // Store ft11
    csrr    t0, fcsr
    STORE   t0, 63*REGBYTES(sp)                         // Store fcsr
#endif

#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    call    _tx_execution_isr_enter                     // Call the ISR execution enter function
#endif

    ret                                                 // Return to calling ISR

_tx_thread_not_nested_save:
    /* }  */

    /* Otherwise, not nested, check to see if a thread was running.  */
    /* else if (_tx_thread_current_ptr)
    {  */
    addi    x6, x6, 1                                   // Increment the interrupt counter
    STORE   x6, 0(x5)                                   // Store the interrupt counter

    /* Not nested: Find the user thread that was running and load our SP */

    LOAD    x5, _tx_thread_current_ptr                  // Pickup current thread pointer
    beqz    x5, _tx_thread_idle_system_save             // If NULL, idle system was interrupted

    /* Save the standard scratch registers.  */

    STORE   x7,  17*REGBYTES(sp)                        // Store t2
    STORE   x8,  12*REGBYTES(sp)                        // Store s0
    STORE   x10, 27*REGBYTES(sp)                        // Store a0
    STORE   x11, 26*REGBYTES(sp)                        // Store a1
    STORE   x12, 25*REGBYTES(sp)                        // Store a2
    STORE   x13, 24*REGBYTES(sp)                        // Store a3
    STORE   x14, 23*REGBYTES(sp)                        // Store a4
    STORE   x15, 22*REGBYTES(sp)                        // Store a5
    STORE   x16, 21*REGBYTES(sp)                        // Store a6
    STORE   x17, 20*REGBYTES(sp)                        // Store a7
    STORE   x28, 16*REGBYTES(sp)                        // Store t3
    STORE   x29, 15*REGBYTES(sp)                        // Store t4
    STORE   x30, 14*REGBYTES(sp)                        // Store t5
    STORE   x31, 13*REGBYTES(sp)                        // Store t6

    csrr    t0, mepc                                    // Load exception program counter
    STORE   t0, 30*REGBYTES(sp)                         // Save it on the stack

    /* Save floating point scratch registers.  */
#if defined(__riscv_float_abi_single)
    fsw     f0, 31*REGBYTES(sp)                         // Store ft0
    fsw     f1, 32*REGBYTES(sp)                         // Store ft1
    fsw     f2, 33*REGBYTES(sp)                         // Store ft2
    fsw     f3, 34*REGBYTES(sp)                         // Store ft3
    fsw     f4, 35*REGBYTES(sp)                         // Store ft4
    fsw     f5, 36*REGBYTES(sp)                         // Store ft5
    fsw     f6, 37*REGBYTES(sp)                         // Store ft6
    fsw     f7, 38*REGBYTES(sp)                         // Store ft7
    fsw     f10,41*REGBYTES(sp)                         // Store fa0
    fsw     f11,42*REGBYTES(sp)                         // Store fa1
    fsw     f12,43*REGBYTES(sp)                         // Store fa2
    fsw     f13,44*REGBYTES(sp)                         // Store fa3
    fsw     f14,45*REGBYTES(sp)                         // Store fa4
    fsw     f15,46*REGBYTES(sp)                         // Store fa5
    fsw     f16,47*REGBYTES(sp)                         // Store fa6
    fsw     f17,48*REGBYTES(sp)                         // Store fa7
    fsw     f28,59*REGBYTES(sp)                         // Store ft8
    fsw     f29,60*REGBYTES(sp)                         // Store ft9
    fsw     f30,61*REGBYTES(sp)                         // Store ft10
    fsw     f31,62*REGBYTES(sp)                         // Store ft11
    csrr    t0, fcsr
    STORE   t0, 63*REGBYTES(sp)                         // Store fcsr
#elif defined(__riscv_float_abi_double)
    fsd     f0, 31*REGBYTES(sp)                         // Store ft0
    fsd     f1, 32*REGBYTES(sp)                         // Store ft1
    fsd     f2, 33*REGBYTES(sp)                         // Store ft2
    fsd     f3, 34*REGBYTES(sp)                         // Store ft3
    fsd     f4, 35*REGBYTES(sp)                         // Store ft4
    fsd     f5, 36*REGBYTES(sp)                         // Store ft5
    fsd     f6, 37*REGBYTES(sp)                         // Store ft6
    fsd     f7, 38*REGBYTES(sp)                         // Store ft7
    fsd     f10,41*REGBYTES(sp)                         // Store fa0
    fsd     f11,42*REGBYTES(sp)                         // Store fa1
    fsd     f12,43*REGBYTES(sp)                         // Store fa2
    fsd     f13,44*REGBYTES(sp)                         // Store fa3
    fsd     f14,45*REGBYTES(sp)                         // Store fa4
    fsd     f15,46*REGBYTES(sp)                         // Store fa5
    fsd     f16,47*REGBYTES(sp)                         // Store fa6
    fsd     f17,48*REGBYTES(sp)                         // Store fa7
    fsd     f28,59*REGBYTES(sp)                         // Store ft8
    fsd     f29,60*REGBYTES(sp)                         // Store ft9
    fsd     f30,61*REGBYTES(sp)                         // Store ft10
    fsd     f31,62*REGBYTES(sp)                         // Store ft11
    csrr    t0, fcsr
    STORE   t0, 63*REGBYTES(sp)                         // Store fcsr
#endif

    /* Save the current stack pointer in the thread's control block.  */
    /* _tx_thread_current_ptr -> tx_thread_stack_ptr =  sp;  */

    /* Switch to the system stack.  */
    /* sp =  _tx_thread_system_stack_ptr;  */

    LOAD    t1, _tx_thread_current_ptr                  // Pickup current thread pointer
    STORE   sp, 2*REGBYTES(t1)                          // Save stack pointer

#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    /* _tx_execution_isr_enter is called with thread stack pointer */
    call    _tx_execution_isr_enter                     // Call the ISR execution enter function
#endif


    LOAD    sp, _tx_thread_system_stack_ptr             // Switch to system stack
    ret                                                 // Return to calling ISR

    /* }
    else
    {  */

_tx_thread_idle_system_save:


#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    call    _tx_execution_isr_enter                     // Call the ISR execution enter function
#endif

    /* Interrupt occurred in the scheduling loop.  */

    /* }
}  */
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
    addi    sp, sp, 65*REGBYTES                         // Recover stack frame - with floating point enabled
#else
    addi    sp, sp, 32*REGBYTES                         // Recover the reserved stack space
#endif
    ret                                                 // Return to calling ISR
