/***************************************************************************
 * Copyright (c) 2025 10xEngineers
 *
 * 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                                                              */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

    .section .text
/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _tx_thread_context_save                            RISC-V32/GNU     */
/*                                                           6.4.x        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Akif Ejaz, 10xEngineers                                             */
/*    Wei-Chen Lai, National Cheng Kung University                        */
/*                                                                        */
/*  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                                                                */
/*                                                                        */
/**************************************************************************/
/* VOID   _tx_thread_context_save(VOID)
{  */
    .global  _tx_thread_context_save
_tx_thread_context_save:

    /* Upon entry to this routine, RA/x1 has been saved on the stack
       and the stack has been already allocated for the entire context:
       addi sp, sp, -32*4 (or -65*4)
       sw   ra, 28*4(sp)
    */

    sw      t0, 19*4(sp)                         // Store t0
    sw      t1, 18*4(sp)                         // Store t1

    /* Check for a nested interrupt.  */
    /* if (_tx_thread_system_state++)
    {  */

    la      t0, _tx_thread_system_state          // Pickup addr of system state var
    lw      t1, 0(t0)                            // Pickup system state
    addi    t1, t1, 1                            // Increment system state
    sw      t1, 0(t0)                            // Store system state
    li      t0, 1
    bgt     t1, t0, _tx_thread_nested_save       // If it's more than 1, nested interrupt

    /* First level interrupt, save the rest of the scratch registers and
       check for a thread to preempt.  */

    sw      t2, 17*4(sp)                         // Store t2
    sw      s0, 12*4(sp)                         // Store s0
    sw      a0, 27*4(sp)                         // Store a0
    sw      a1, 26*4(sp)                         // Store a1
    sw      a2, 25*4(sp)                         // Store a2
    sw      a3, 24*4(sp)                         // Store a3
    sw      a4, 23*4(sp)                         // Store a4
    sw      a5, 22*4(sp)                         // Store a5
    sw      a6, 21*4(sp)                         // Store a6
    sw      a7, 20*4(sp)                         // Store a7
    sw      t3, 16*4(sp)                         // Store t3
    sw      t4, 15*4(sp)                         // Store t4
    sw      t5, 14*4(sp)                         // Store t5
    sw      t6, 13*4(sp)                         // Store t6

    /* Save mstatus and skip FP state if FS is Off.  */
    csrr    t0, mstatus
    sw      t0, 29*4(sp)
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
    srli    t1, t0, 13
    andi    t1, t1, 0x3
    beqz    t1, _tx_thread_skip_fpu_save
#endif

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

    csrr    t0, mepc
    sw      t0, 30*4(sp)                         // Save it on the stack

    la      t1, _tx_thread_current_ptr           // Pickup address of current thread ptr
    lw      t2, 0(t1)                            // Pickup current thread pointer
    beqz    t2, _tx_thread_idle_system_save      // If NULL, idle system was interrupted

    /* Save the current thread's stack pointer and switch to the system stack.  */
    /* _tx_thread_current_ptr -> tx_thread_stack_ptr =  sp;
    sp = _tx_thread_system_stack_ptr;  */

    sw      sp, 8(t2)                            // Save stack pointer
    la      t0, _tx_thread_system_stack_ptr
    lw      sp, 0(t0)                            // Switch to system stack

    /* Call the ISR execution exit function if enabled.  */
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    call    _tx_execution_isr_enter              // Call the ISR execution enter function
#endif

    ret                                          // Return to ISR

_tx_thread_nested_save:

    /* Nested interrupt!  Just save the scratch registers and return to the ISR.  */

    sw      t2, 17*4(sp)                         // Store t2
    sw      s0, 12*4(sp)                         // Store s0
    sw      a0, 27*4(sp)                         // Store a0
    sw      a1, 26*4(sp)                         // Store a1
    sw      a2, 25*4(sp)                         // Store a2
    sw      a3, 24*4(sp)                         // Store a3
    sw      a4, 23*4(sp)                         // Store a4
    sw      a5, 22*4(sp)                         // Store a5
    sw      a6, 21*4(sp)                         // Store a6
    sw      a7, 20*4(sp)                         // Store a7
    sw      t3, 16*4(sp)                         // Store t3
    sw      t4, 15*4(sp)                         // Store t4
    sw      t5, 14*4(sp)                         // Store t5
    sw      t6, 13*4(sp)                         // Store t6

    /* Save mstatus and skip FP state if FS is Off.  */
    csrr    t0, mstatus
    sw      t0, 29*4(sp)
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
    srli    t1, t0, 13
    andi    t1, t1, 0x3
    beqz    t1, _tx_thread_skip_nested_fpu_save
#endif

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

    csrr    t0, mepc
    sw      t0, 30*4(sp)                         // Save it on stack

    /* Call the ISR execution exit function if enabled.  */
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    call    _tx_execution_isr_enter              // Call the ISR execution enter function
#endif

    ret                                          // Return to ISR

_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*4                               // Recover stack frame - with floating point enabled
#else
    addi    sp, sp, 32*4                               // Recover the reserved stack space
#endif
    ret                                                // Return to calling ISR
