/*
 * Copyright (c) 2020-2020, Bluetrum Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020/11/18     greedyhao    The first version.
 */

#include "ab32vg1.h"

.global  rt_interrupt_from_thread
.global  rt_interrupt_to_thread
.global  rt_cur_thread_sp
.global  rt_thread_switch_interrupt_flag
.global  rt_interrupt_nest

/*
 * rt_base_t rt_hw_interrupt_disable();
 * a0 -> return
 */
    .global rt_hw_interrupt_disable
rt_hw_interrupt_disable:
    lw      a5, PICCON(zero)
    mv      a0, a5
    andi    a5, a5, -2
    sw      a5, PICCON(zero)
    ret

/*
 * void rt_hw_interrupt_enable(rt_base_t level);
 * a0 -> level
 */
    .global rt_hw_interrupt_enable
rt_hw_interrupt_enable:
    andi    a0, a0, 1
    beqz    a0,enable_int_ret
    lw      a5, PICCON(zero)
    ori     a5, a5, 1
    sw      a5,PICCON(zero)
enable_int_ret:
    ret

/* Macro for saving task context */
.macro save_context
    addi    sp, sp, -124

    /* Save Context */
    sw      x1, 0(sp)
    sw      x4, 4(sp)
    sw      x5, 8(sp)
    sw      x6, 12(sp)
    sw      x7, 16(sp)
    sw      x8, 20(sp)
    sw      x9, 24(sp)
    sw      x10, 28(sp)
    sw      x11, 32(sp)
    sw      x12, 36(sp)
    sw      x13, 40(sp)
    sw      x14, 44(sp)
    sw      x15, 48(sp)
    sw      x16, 52(sp)
    sw      x17, 56(sp)
    sw      x18, 60(sp)
    sw      x19, 64(sp)
    sw      x20, 68(sp)
    sw      x21, 72(sp)
    sw      x22, 76(sp)
    sw      x23, 80(sp)
    sw      x24, 84(sp)
    sw      x25, 88(sp)
    sw      x26, 92(sp)
    sw      x27, 96(sp)
    sw      x28, 100(sp)
    sw      x29, 104(sp)
    sw      x30, 108(sp)
    sw      x31, 112(sp)

    lw      a5, EPC(zero)                                       //Saves current program counter (EPC) as task program counter
    sw      a5, 116(sp)
    lw      a5, EPICCON(zero)
    sw      a5, 120(sp)

    sw      sp, rt_cur_thread_sp, a4                            //store sp in preempted tasks tcb
    .endm


/* Macro for restoring task context */
.macro restore_context

    la      a5, rt_cur_thread_sp
    lw      sp, 0(a5)                                           //get new task stack pointer

    /* Load task program counter  EPC*/
    lw      a5, 116(sp)
    sw      a5, EPC(zero)
    lw      a5, 120(sp)
    sw      a5, EPICCON(zero)

    /* Restore registers,
       Skip global pointer because that does not change */
    lw      x1, 0(sp)
    lw      x4, 4(sp)
    lw      x5, 8(sp)
    lw      x6, 12(sp)
    lw      x7, 16(sp)
    lw      x8, 20(sp)
    lw      x9, 24(sp)
    lw      x10, 28(sp)
    lw      x11, 32(sp)
    lw      x12, 36(sp)
    lw      x13, 40(sp)
    lw      x14, 44(sp)
    lw      x15, 48(sp)
    lw      x16, 52(sp)
    lw      x17, 56(sp)
    lw      x18, 60(sp)
    lw      x19, 64(sp)
    lw      x20, 68(sp)
    lw      x21, 72(sp)
    lw      x22, 76(sp)
    lw      x23, 80(sp)
    lw      x24, 84(sp)
    lw      x25, 88(sp)
    lw      x26, 92(sp)
    lw      x27, 96(sp)
    lw      x28, 100(sp)
    lw      x29, 104(sp)
    lw      x30, 108(sp)
    lw      x31, 112(sp)

    addi    sp, sp, 124
    mret
    .endm

/*
 * void rt_hw_context_switch_to(rt_uint32 to);
 * r0 --> to
 */
    .globl rt_hw_context_switch_to
rt_hw_context_switch_to:
    sw      zero, rt_interrupt_from_thread, a4                      /*set from thread to 0*/
    sw      a0, rt_interrupt_to_thread, a4                          /*set rt_interrupt_to_thread*/

    sb      zero, rt_interrupt_nest, a4                             /*rt_interrupt_nest = 0*/

    li      a5, 1
    sw      a5, rt_thread_switch_interrupt_flag, a4                 // rt_thread_switch_interrupt_flag = 1;

    SWINT                                                           /*kick soft interrupt*/
    lw      a5, PICCON(zero)                                        /*enable interrupt*/
    ori     a5, a5, 1
    sw      a5, PICCON(zero)
    ret

/*
 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
 * a0 --> from
 * a1 --> to
 */
    .globl rt_hw_context_switch
rt_hw_context_switch:
    sw      a0, rt_interrupt_from_thread, a4                        /*set rt_interrupt_from_thread*/
    sw      a1, rt_interrupt_to_thread, a4                          /*set rt_interrupt_to_thread*/
    li      a5, 1
    sw      a5, rt_thread_switch_interrupt_flag, a4                 /*rt_thread_switch_interrupt_flag = 1*/
    SWINT                                                           /*kick soft interrupt*/
    ret

 /*
 * void rt_switch_to_thread(void);
 * r0 --> to
 */
    .globl rt_switch_to_thread
rt_switch_to_thread:
    lw     a0, rt_interrupt_from_thread
    lw     a5, rt_cur_thread_sp
    sw     a5, 0(a0)

    lw     a0, rt_interrupt_to_thread
    lw     a5, 0(a0)
    sw     a5, rt_cur_thread_sp, a4
    ret

/*
 * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to);
 * a0 --> from
 * a1 --> to
 */
    .global rt_hw_context_switch_interrupt
rt_hw_context_switch_interrupt:
    lw      a5, rt_thread_switch_interrupt_flag
    bnez    a5, _reswitch
    li      a5, 0x01
    sw      a5, rt_thread_switch_interrupt_flag, a4
    sw      a0, rt_interrupt_from_thread, a4                      	/*set rt_interrupt_from_thread*/
_reswitch:
    sw      a1, rt_interrupt_to_thread, a4                        	/*set rt_interrupt_to_thread*/
    ret

    //软中断服务
    .global rt_soft_isr
rt_soft_isr:
    li      a5, 0x4                                       			// PICPND = BIT(IRQ_SW_VECTOR); 清软中断Pending
    sw      a5, PICPND(zero)
    ret

    .globl low_prio_irq
low_prio_irq:
    save_context

    lw      a5, rt_interrupt_nest
    bnez    a5, _low_prio_irq_exit

    lw      a5, cpu_irq_comm_hook
    jalr    a5

    lw      a5, rt_thread_switch_interrupt_flag
    beqz    a5, _low_prio_irq_exit                                  // if (rt_thread_switch_interrupt_flag)
    jal     rt_switch_to_thread
    sw      zero, rt_thread_switch_interrupt_flag, a4               // rt_thread_switch_interrupt_flag = 0;

    _low_prio_irq_exit:
    restore_context

