/** @file reentrant_irq.h
*   @brief macro to make IRQ functions reentrant
*   @date 1/12/2015
*   @version 01.00.00
*
*/

#ifndef REENTRANT_IRQ_H_
#define REENTRANT_IRQ_H_

/*! **************************************************************************
* \brief __REENTRANT_IRQ__ - Wrapper to make a IRQ interruptible.
*
* Can be used to prefix a IRQ function with the code necessary to make it interruptable.
* It will create an Assembly function with the name "FuncName" and a declare a C function of the type void __CLevel"FuncName"(void) (without ").
* The user has to handle the VIM, enable and disable IRQ himself within the C function.
*
* \param FuncName - Name of the Function
*
******************************************************************************/

#define __REENTRANT_IRQ__(FuncName) \
	__asm(  " .def " #FuncName "       ; Export Symbol\n" \
			" .arm                     ; Assemble in ARM UAL Syntax\n" \
			" .align 4                 ; Aligne to word size\n" \
			" .armfunc " #FuncName "   ; Declare an ASM function\n" \
			"                          ; Start ASM Handler to manage IRQ Nesting\n" \
			#FuncName":                ; Symbol\n" \
		 	" 	 	 	 	 	       ; TI ASM Step 1\n" \
		    " SUB      LR, LR, #4      ; construct the return address\n" \
		    " SRSDB    #31!            ; Save LR_irq and SPSR_irq to System mode stack\n" \
		    "                          ; TI ASM Step 2:\n" \
		    " CPS      #31             ; Switch to System mode\n" \
            "                          ; TI ASM Step 3:\n" \
		    " PUSH     {R0-R3, R12}    ; Store AAPCS registers\n" \
		    " .if __TI_VFPV3D16_SUPPORT__ = 1 ; If VFPV3D16 is used\n" \
		    " FMRX      R12, FPSCR\n" \
		    " STMFD     SP!, {R12}\n" \
		    " FMRX      R12, FPEXC\n" \
		    " STMFD     SP!, {R12}\n" \
		    " FSTMDBD   SP!, {D0-D7}\n" \
		    " .endif\n" \
            "                          ; TI ASM Step 4\n" \
            "                          ; Align stack to a 64 Bit boundary\n" \
            " AND      R3, SP, #4      ; Calculate Stack adjustment to 64bit boundary\n" \
            " SUB      SP, SP, R3      ; Adjust System Stack\n" \
            " PUSH     {R3, LR}        ; Put Stack adjustment and System Mode LR on Stack\n" \
            "                          ; TI ASM Step 5\n" \
            " BL   __CLevel"#FuncName" ; Branch to C Level Interrupt Function\n" \
            "                          ; TI ASM Step 6\n" \
            "                          ; Undo stack alignment\n" \
            " POP      {R3, LR}        ; Get Stack adjustment and System Mode LR from Stack\n" \
            " ADD      SP, SP, R3      ; Undo System Stack adjustment\n" \
            "                          ; TI ASM Step 7\n" \
            " .if __TI_VFPV3D16_SUPPORT__ = 1 ; If VFPV3D16 is used\n" \
            " FLDMIAD   SP!, {D0-D7}   \n" \
            " LDMFD     SP!, {R12}     \n" \
            " FMXR      FPEXC, R12     \n" \
            " LDMFD     SP!, {R12}     \n" \
            " FMXR      FPSCR, R12     \n" \
            " .endif                   \n" \
            " POP      {R0-R3, R12}    ; Restore AAPCS registers\n" \
            "                          ; TI ASM Step 7\n" \
            " RFEIA    SP!             ; Return using RFE from System mode stack\n" \
			"\n");                     /* End of ASM Function */ \
	void __CLevel##FuncName(void)      /* Modify Function name for C Level Interrupt Function */

#endif /* REENTRANT_IRQ_H_ */
