/*
 * File      : context_gcc.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
 * 2011-12-17     nl1031       first implementation for MicroBlaze.
 *
 */

#include "microblaze.inc"

	.text
    	.globl	rt_interrupt_enter
    	.globl	rt_interrupt_leave

/*
 * rt_base_t rt_hw_interrupt_disable()
 * copy from ucos-ii
 */

	.globl	rt_hw_interrupt_disable
	.ent	rt_hw_interrupt_disable
	.align	2
rt_hw_interrupt_disable:
    	ADDIK   r1, r1, -4
    	SW      r4, r1, r0

    	MFS     r3, RMSR
    	ANDNI   r4, r3, IE_BIT
    	MTS     RMSR, r4

    	LW      r4, r1, r0
    	ADDIK   r1, r1, 4

    	AND     r0, r0, r0             /* NO-OP - pipeline flush                             */
    	AND     r0, r0, r0             /* NO-OP - pipeline flush                             */
    	AND     r0, r0, r0             /* NO-OP - pipeline flush                             */

    	RTSD    r15, 8
    	AND     r0, r0, r0
	.end	rt_hw_interrupt_disable

/*
 * void rt_hw_interrupt_enable(rt_base_t level)
 * copy from ucos-ii
 */
	.globl	rt_hw_interrupt_enable
	.ent	rt_hw_interrupt_enable
	.align	2
rt_hw_interrupt_enable:
    	RTSD    r15, 8
    	MTS     rMSR, r5               		/* Move the saved status from r5 into rMSR            */
	.end	rt_hw_interrupt_enable

/*
 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to)
 * r5 --> from
 * r6 --> to
 */

	.globl rt_interrupt_from_thread
	.globl rt_interrupt_to_thread
	.globl rt_hw_context_switch
	.ent rt_hw_context_switch
	.align	2
rt_hw_context_switch:
	PUSH_ALL
    	MFS     r3,  RMSR                   /* save the MSR */
    	SWI     r3,  r1, STACK_RMSR
	SWI		r1,  r5, 0					/* store sp in preempted tasks TCB */
	LWI		r1,  r6, 0					/* get new task stack pointer */

    	LWI     r3,  r1, STACK_RMSR
    	ANDI    r3,  r3, IE_BIT
    	BNEI    r3,  rt_hw_context_switch_ie /*if IE bit set,should be use RTID (return from interrupt). */

    	LWI     r3,  r1, STACK_RMSR
    	MTS     RMSR,r3
	POP_ALL
    	ADDIK   r1,  r1, STACK_SIZE
    	RTSD    r15, 8
    	AND     r0,  r0, r0

rt_hw_context_switch_ie:

    	LWI     r3,  r1, STACK_RMSR
    	ANDNI   r3,  r3, IE_BIT         	/* clear IE bit, prevent interrupt occur immediately*/
    	MTS     RMSR,r3
    	LWI     r3,  r1, STACK_R03
	POP_ALL
    	ADDIK   r1,  r1, STACK_SIZE
    	RTID    r14, 0                    	/* IE bit will be set automatically */
    	AND     r0,  r0, r0
	.end	rt_hw_context_switch

/*
 * void rt_hw_context_switch_to(rt_uint32 to)
 * r5 --> to
 */
	.globl 	rt_hw_context_switch_to
	.ent   	rt_hw_context_switch_to
	.align	2
rt_hw_context_switch_to:
	LWI		r1,	 r5, 0 					/* get new task stack pointer */
    	LWI     r3,  r1, STACK_RMSR
    	ANDNI   r3,  r3, IE_BIT         	/* clear IE bit, prevent interrupt occur immediately*/
    	MTS     RMSR,r3
	POP_ALL
    	ADDIK   r1,  r1, STACK_SIZE
    	RTID    r14, 0                    	/* IE bit will be set automatically */
    	AND     r0,  r0, r0

	.end 	rt_hw_context_switch_to

/*
 * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to)
 */
	.globl 	rt_thread_switch_interrupt_flag
	.globl 	rt_hw_context_switch_interrupt
	.ent rt_hw_context_switch_interrupt
	.align	2
rt_hw_context_switch_interrupt:
	LA	r3,	r0, rt_thread_switch_interrupt_flag
	LWI	r4, r3, 0					/* load rt_thread_switch_interrupt_flag into r4 */

	ANDI	r4, r4, 1
	BNEI	r4, _reswitch				/* if rt_thread_switch_interrupt_flag = 1 */

	ADDIK	r4, r0, 1					/* set rt_thread_switch_interrupt_flag to 1 */
	SWI	r4, r3, 0

	LA	r3, r0, rt_interrupt_from_thread /* set rt_interrupt_from_thread */
	SWI	r5, r3, 0 					/* rt_interrupt_from_thread = from */
_reswitch:
	LA	r3, r0, rt_interrupt_to_thread	/* set rt_interrupt_to_thread */
	SWI	r6, r3, 0 					/* rt_interrupt_to_thread = to */
    	RTSD    r15, 8
    	AND     r0, r0, r0
	.end rt_hw_context_switch_interrupt


    .globl     	_interrupt_handler
	.section .text
	.align 	2
	.ent _interrupt_handler
	.type _interrupt_handler, @function

_interrupt_handler:
	PUSH_ALL
    	MFS     r3,  RMSR
    	ORI     r3,  r3, IE_BIT
    	SWI     r3,  r1, STACK_RMSR         	/* push MSR    */

    	BRLID   r15, rt_interrupt_enter
    	AND     r0,  r0, r0

    	BRLID   r15, rt_hw_trap_irq
    	AND     r0,  r0, r0

    	BRLID   r15, rt_interrupt_leave
    	AND     r0,  r0, r0

	/*
	 * if rt_thread_switch_interrupt_flag set, jump to
	 * rt_hw_context_switch_interrupt_do and don't return
	 */
	LA	r3,  r0, rt_thread_switch_interrupt_flag
	LWI	r4,  r3, 0

	ANDI	r4,  r4, 1
	BNEI 	r4,  rt_hw_context_switch_interrupt_do

    	LWI     r3,  r1, STACK_RMSR
    	ANDNI   r3,  r3, IE_BIT
    	MTS     RMSR,r3
	POP_ALL
    	ADDIK   r1,  r1, STACK_SIZE

    	RTID    r14, 0
    	AND     r0,  r0, r0

/*
 * void rt_hw_context_switch_interrupt_do(rt_base_t flag)
 */
rt_hw_context_switch_interrupt_do:
	SWI	r0,  r3, 0 						/* clear rt_thread_switch_interrupt_flag */

	LA	r3,  r0, rt_interrupt_from_thread
	LW	r4,  r0, r3
	SWI	r1,  r4, 0						/* store sp in preempted tasks's TCB */

	LA	r3,  r0, rt_interrupt_to_thread
	LW	r4,  r0, r3
	LWI	r1,  r4, 0						/* get new task's stack pointer */

    	LWI     r3,  r1, STACK_RMSR
    	ANDI    r3,  r3, IE_BIT
    	BNEI    r3,  return_with_ie     	/*if IE bit set,should be use RTID (return from interrupt). */

    	LWI     r3,  r1, STACK_RMSR
    	MTS     RMSR,r3
	POP_ALL
    	ADDIK   r1,  r1, STACK_SIZE
    	RTSD    r15, 8
    	AND     r0,  r0, r0

return_with_ie:

    	LWI     r3,  r1, STACK_RMSR
    	ANDNI   r3,  r3, IE_BIT         	/* clear IE bit, prevent interrupt occur immediately*/
    	MTS     RMSR,r3
    	LWI     r3,  r1, STACK_R03
	POP_ALL
    	ADDIK   r1,  r1, STACK_SIZE
    	RTID    r14, 0                    	/* IE bit will be set automatically */
    	AND     r0,  r0, r0

.end _interrupt_handler