;/*
; * File      : context.S
; * This file is part of RT-Thread RTOS
; * COPYRIGHT (C) 2006, RT-Thread Development Team
; *
; * The license and distribution terms for this file may be
; * found in the file LICENSE in this distribution or at
; * http://www.rt-thread.org/license/LICENSE
; *
; * Change Logs:
; * Date           Author       Notes
; * 2009-01-17     Bernard      first version
; */

;/**
; * @addtogroup STM32
; */
;/*@{*/

NVIC_INT_CTRL   EQU     0xE000ED04               ; interrupt control state register
NVIC_SYSPRI2    EQU     0xE000ED20               ; system priority register (2)
NVIC_PENDSV_PRI EQU     0x00FF0000               ; PendSV priority value (lowest)
NVIC_PENDSVSET  EQU     0x10000000               ; value to trigger PendSV exception

	AREA |.text|, CODE, READONLY, ALIGN=2
	THUMB
	REQUIRE8
	PRESERVE8

	IMPORT rt_thread_switch_interrput_flag
	IMPORT rt_interrupt_from_thread
	IMPORT rt_interrupt_to_thread

;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
rt_hw_interrupt_disable    PROC
	EXPORT  rt_hw_interrupt_disable
	MRS		r0, PRIMASK
	CPSID   I
	BX		LR
	ENDP

;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROC
	EXPORT  rt_hw_interrupt_enable
	MSR		PRIMASK, r0
	BX      LR
	ENDP

;/*
; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
; * r0 --> from
; * r1 --> to
; */
rt_hw_context_switch    PROC
	EXPORT rt_hw_context_switch

	; set rt_thread_switch_interrput_flag to 1
	LDR 	r2, =rt_thread_switch_interrput_flag
	LDR 	r3, [r2]
	MOV 	r3, #1
	STR 	r3, [r2]

	LDR 	r2, =rt_interrupt_from_thread	; set rt_interrupt_from_thread
	STR 	r0, [r2]
	LDR 	r2, =rt_interrupt_to_thread		; set rt_interrupt_to_thread
	STR 	r1, [r2]

    LDR     r0, =NVIC_INT_CTRL      ; trigger the PendSV exception (causes context switch)
    LDR     r1, =NVIC_PENDSVSET
	STR     r1, [r0]
	; CPSIE   I                       ; enable interrupts at processor level
	BX      LR
	ENDP

; r0 --> swith from thread stack
; r1 --> swith to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
rt_hw_pend_sv	PROC
	EXPORT rt_hw_pend_sv
	; clear rt_thread_switch_interrput_flag to 0
	LDR		r0, =rt_thread_switch_interrput_flag
	MOV		r1, #0x00
	STR		r1, [r0]

	LDR		r0, =rt_interrupt_from_thread
	LDR		r1, [r0]
	CBZ		r1, swtich_to_thread    ; skip register save at the first time

	MRS     r1, psp                 ; get from thread stack pointer
	STMFD	r1!, {r4 - r11}			; push r4 - r11 register
	LDR		r0, [r0]
	STR		r1, [r0]				; update from thread stack pointer

swtich_to_thread
	LDR		r1, =rt_interrupt_to_thread
	LDR		r1, [r1]
	LDR		r1, [r1]				; load thread stack pointer 

	LDMFD	r1!, {r4 - r11}			; pop r4 - r11 register
	MSR		psp, r1					; update stack pointer

	ORR     lr, lr, #0x04
	BX		lr
	ENDP

;/*
; * void rt_hw_context_switch_to(rt_uint32 to);
; * r0 --> to
; */
rt_hw_context_switch_to    PROC
	EXPORT rt_hw_context_switch_to
	LDR		r1, =rt_interrupt_to_thread
	STR		r0, [r1]

	; set from thread to 0
	LDR		r1, =rt_interrupt_from_thread
	MOV		r0, #0x0
	STR		r0, [r1]

	; set the PendSV exception priority
    LDR     r0, =NVIC_SYSPRI2
    LDR     r1, =NVIC_PENDSV_PRI
    STR     r1, [r0]

    LDR     r0, =NVIC_INT_CTRL      ; trigger the PendSV exception (causes context switch)
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]
	
    CPSIE   I                       ; enable interrupts at processor level
	
	; never reach here!
	ENDP

;/*
; * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to)
; * {
; *		if (rt_thread_switch_interrput_flag == 1)
; * 	{
; *			rt_interrupt_to_thread = to;
; * 	}
; * 	else
; * 	{
; *			rt_thread_switch_interrput_flag = 1;
; * 		rt_interrupt_from_thread = from;
; * 		rt_interrupt_to_thread = to;
; * 	}
; * }
; */
rt_hw_context_switch_interrupt    PROC
	EXPORT rt_hw_context_switch_interrupt
	LDR 	r2, =rt_thread_switch_interrput_flag
	LDR 	r3, [r2]
	CMP 	r3, #1
	BEQ 	_reswitch
	MOV 	r3, #1							; set rt_thread_switch_interrput_flag to 1
	STR 	r3, [r2]
	LDR 	r2, =rt_interrupt_from_thread	; set rt_interrupt_from_thread
	STR 	r0, [r2]
_reswitch
	LDR 	r2, =rt_interrupt_to_thread		; set rt_interrupt_to_thread
	STR 	r1, [r2]
	BX		lr
	ENDP

rt_hw_interrupt_thread_switch	PROC
	EXPORT rt_hw_interrupt_thread_switch
	LDR		r0, =rt_thread_switch_interrput_flag
	LDR		r1, [r0]
	CBZ		r1, _no_switch

	; clear rt_thread_switch_interrput_flag to 0
	; MOV		r1, #0x00
	; STR		r1, [r0]

	; trigger context switch
    LDR     r0, =NVIC_INT_CTRL      ; trigger the PendSV exception (causes context switch)
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]

_no_switch
	BX		lr

	ENDP

	END