;
; Copyright (c) 2006-2022, RT-Thread Development Team
;
; SPDX-License-Identifier: Apache-2.0
;
; Change Logs:
; Date           Author       Notes
; 2018-09-01     xuzhuoyi     the first version.
; 2019-06-17     zhaoxiaowei  fix bugs of old c28x interrupt api.
; 2019-07-03     zhaoxiaowei  add _rt_hw_calc_csb function to support __rt_ffs.
; 2019-12-05     xiaolifan    add support for hardware fpu32
; 2022-06-21     guyunjie     trim pendsv (RTOSINT_Handler)
; 2022-08-24     guyunjie     fix bugs in context switching

    .ref   _rt_interrupt_to_thread
    .ref   _rt_interrupt_from_thread
    .ref   _rt_thread_switch_interrupt_flag

    .def   _RTOSINT_Handler
    .def   _rt_hw_get_st0
    .def   _rt_hw_get_st1
    .def   _rt_hw_calc_csb
    .def   _rt_hw_context_switch_interrupt
    .def   _rt_hw_context_switch
    .def   _rt_hw_context_switch_to
    .def   _rt_hw_interrupt_thread_switch
    .def   _rt_hw_interrupt_disable
    .def   _rt_hw_interrupt_enable

;workaround for importing fpu settings from the compiler
    .cdecls C,NOLIST
    %{
        #ifdef __TMS320C28XX_FPU32__
            #define __FPU32__ 1
        #else
            #define __FPU32__ 0
        #endif
    %}


RT_CTX_SAVE  .macro

    PUSH    AR1H:AR0H
    PUSH    XAR2
    PUSH    XAR3
    PUSH    XAR4
    PUSH    XAR5
    PUSH    XAR6
    PUSH    XAR7
    PUSH    XT
    PUSH    RPC

    .if __FPU32__
    PUSH    RB
    MOV32   *SP++, STF
    MOV32   *SP++, R0H
    MOV32   *SP++, R1H
    MOV32   *SP++, R2H
    MOV32   *SP++, R3H
    MOV32   *SP++, R4H
    MOV32   *SP++, R5H
    MOV32   *SP++, R6H
    MOV32   *SP++, R7H
    .endif

    .endm


RT_CTX_RESTORE  .macro

    .if __FPU32__
    MOV32   R7H, *--SP, UNCF
    MOV32   R6H, *--SP, UNCF
    MOV32   R5H, *--SP, UNCF
    MOV32   R4H, *--SP, UNCF
    MOV32   R3H, *--SP, UNCF
    MOV32   R2H, *--SP, UNCF
    MOV32   R1H, *--SP, UNCF
    MOV32   R0H, *--SP, UNCF
    MOV32   STF, *--SP
    POP     RB
    .endif

    POP     RPC
    POP     XT
    POP     XAR7
    POP     XAR6
    POP     XAR5
    POP     XAR4
    POP     XAR3
    POP     XAR2

    MOVZ    AR0 , @SP
    SUBB    XAR0, #6
    MOVL    ACC , *XAR0
    AND     ACC, #0xFFFF << 16
    MOV     AL, IER
    MOVL   *XAR0, ACC

    POP     AR1H:AR0H

    .endm


.text
    .newblock

;
; rt_base_t rt_hw_interrupt_disable();
;
    .asmfunc
_rt_hw_interrupt_disable:
    PUSH  ST1
    SETC  INTM,DBGM
    MOV   AL, *--SP
    LRETR
    .endasmfunc

;
; void rt_hw_interrupt_enable(rt_base_t level);
;
    .asmfunc
_rt_hw_interrupt_enable:
    MOV   *SP++, AL
    POP   ST1
    LRETR
    .endasmfunc

;
; void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
; ACC   --> from
; SP[4] --> to
;
    .asmfunc
_rt_hw_context_switch_interrupt:
    MOVL    XAR0, ACC
    MOVL    XAR4, *-SP[4]
    ; set rt_thread_switch_interrupt_flag to 1
    MOVL    XAR5, #_rt_thread_switch_interrupt_flag
    MOVL    ACC, *XAR5
    BF      _reswitch, NEQ                     ; ACC!=0
    MOVB    ACC, #1
    MOVL    *XAR5, ACC

    MOVL    XAR5, #_rt_interrupt_from_thread   ; set rt_interrupt_from_thread
    MOVL    *XAR5, XAR0

_reswitch:
    MOVL    XAR5, #_rt_interrupt_to_thread     ; set rt_interrupt_to_thread
    MOVL    *XAR5, XAR4

    LRETR
    .endasmfunc

;
; void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
; ACC   --> from
; SP[4] --> to
;
    .asmfunc
_rt_hw_context_switch:
    MOVL    XAR0, ACC
    MOVL    XAR4, *-SP[4]
    ; set rt_thread_switch_interrupt_flag to 1
    MOVL    XAR5, #_rt_thread_switch_interrupt_flag
    MOVL    ACC, *XAR5
    BF      _reswitch2, NEQ                    ; ACC!=0
    MOVB    ACC, #1
    MOVL    *XAR5, ACC

    MOVL    XAR5, #_rt_interrupt_from_thread   ; set rt_interrupt_from_thread
    MOVL    *XAR5, XAR0

_reswitch2:
    MOVL    XAR5, #_rt_interrupt_to_thread     ; set rt_interrupt_to_thread
    MOVL    *XAR5, XAR4

    TRAP    #16
    LRETR
    .endasmfunc

;
; * void rt_hw_context_switch_to(rt_uint32 to);
; * ACC --> to
;
    .asmfunc
_rt_hw_context_switch_to:
    ; get to thread
    MOVL    XAR1, #_rt_interrupt_to_thread
    MOVL    *XAR1, ACC

    ; set from thread to 0
    MOVL    XAR1, #_rt_interrupt_from_thread
    MOVL    XAR0, #0
    MOVL    *XAR1, XAR0

    ; set interrupt flag to 1
    MOVL    XAR1, #_rt_thread_switch_interrupt_flag
    MOVL    XAR0, #1
    MOVL    *XAR1, XAR0

    TRAP    #16

    ; never reach here!
    .endasmfunc

    .asmfunc
_RTOSINT_Handler:
    ; disable interrupt to protect context switch
    ; DINT ;this is done by hardware so not needed

    ; get rt_thread_switch_interrupt_flag
    MOVL    XAR0, #_rt_thread_switch_interrupt_flag
    MOVL    ACC, *XAR0
    BF      rtosint_exit, EQ         ; pendsv already handled

    ; clear rt_thread_switch_interrupt_flag to 0
    MOVL    XAR1, #0
    MOVL    *XAR0, XAR1

    MOVL    XAR0, #_rt_interrupt_from_thread
    MOVL    ACC, *XAR0
    BF      switch_to_thread, EQ     ; skip register save at the first time

    RT_CTX_SAVE                      ; push cpu registers

    MOVL    ACC, *XAR0
    MOVL    XAR0, ACC
    MOVZ    AR1, @SP                 ; get from thread stack pointer
    MOVL    *XAR0, XAR1              ; update from thread stack pointer

switch_to_thread:
    MOVL    XAR1, #_rt_interrupt_to_thread
    MOVL    ACC, *XAR1
    MOVL    XAR1, ACC
    MOVL    ACC, *XAR1
    MOV     @SP, AL                  ; load thread stack pointer

    RT_CTX_RESTORE                   ; pop cpu registers

rtosint_exit:
    ; do not restore interrupt here: to be restored according to the
    ; switched-to context during IRET (automaticlly by hardware)

    IRET
    .endasmfunc

    .asmfunc
_rt_hw_get_st0:
    PUSH    ST0
    POP     AL
    LRETR
    .endasmfunc

    .asmfunc
_rt_hw_get_st1:
    PUSH    ST1
    POP     AL
    LRETR
    .endasmfunc

; C28x do not have a build-in "__ffs" func in its C compiler.
; We can use the "Count Sign Bits" (CSB) instruction to make one.
; CSB will return the number of 0's minus 1 above the highest set bit.
; The count is placed in T. For example:
;    ACC        T     maxbit
; 0x00000001    30      0
; 0x00000010    26      4
; 0x000001FF    22      8
; 0x000001F0    22      8
    .asmfunc
_rt_hw_calc_csb:
    MOV     AH, #0
    CSB     ACC                   ; T = no. of sign bits - 1
    MOVU    ACC, T                ; ACC = no. of sign bits - 1
    SUBB    ACC, #30              ; ACC = ACC - 30
    ABS     ACC                   ; ACC = |ACC|
    lretr
    .endasmfunc

; compatible with old version
    .asmfunc
_rt_hw_interrupt_thread_switch:
    LRETR
    NOP
    .endasmfunc

.end