;************************************************************************
;*                                                                      *
;*  assembler definitions for enabling nested interrupts                *
;*                                                                      *
;************************************************************************

;************************************************************************
;* 1  Disclaimer                                                        *
;************************************************************************
;* __DISCLAIMER_START__                                                 *
;************************************************************************
;* (c) 2014-2016, Cypress Semiconductor Corporation
;* or a subsidiary of Cypress Semiconductor Corporation. All rights
;* reserved.
;*
;* This software, including source code, documentation and related
;* materials ( "Software" ), is owned by Cypress Semiconductor
;* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
;* and subject to worldwide patent protection (United States and foreign),
;* United States copyright laws and international treaty provisions.
;* Therefore, you may use this Software only as provided in the license
;* agreement accompanying the software package from which you
;* obtained this Software ( "EULA" ).
;*
;* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
;* non-transferable license to copy, modify, and compile the
;* Software source code solely for use in connection with Cypress' s
;* integrated circuit products. Any reproduction, modification, translation,
;* compilation, or representation of this Software except as specified
;* above is prohibited without the express written permission of Cypress.
;*
;* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
;* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
;* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
;* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
;* PARTICULAR PURPOSE. Cypress reserves the right to make
;* changes to the Software without notice. Cypress does not assume any
;* liability arising out of the application or use of the Software or any
;* product or circuit described in the Software. Cypress does not
;* authorize its products for use in any products where a malfunction or
;* failure of the Cypress product may reasonably be expected to result in
;* significant property damage, injury or death ( "High Risk Product" ). By
;* including Cypress' s product in a High Risk Product, the manufacturer
;* of such system or application assumes all risk of such use and in doing
;* so agrees to indemnify Cypress against all liability.
;************************************************************************
; __DISCLAIMER_END__                                                    *
;************************************************************************
;************************************************************************
;* 2  Contents                                                          *
;************************************************************************
;*  1  Disclaimer                                                       *
;*  2  Contents                                                         *
;*  3  History                                                          *
;*  4  Local definitions                                                *
;*  5  Assembler macro code                                             *
;************************************************************************
;* 3  History                                                           *
;************************************************************************
;* Version Date        Author   Description                             *
;* V0.01   2015-09-01  HS       Initial version for Traveo              *
;************************************************************************
;* 4  Local definitions                                                 *
;************************************************************************

;* define register address
ADDRESS_OF_IRC0_IRQR0  .equ    0xb0400b00
ADDRESS_OF_IRQST_nIRQ  .equ    0xb1100078
ADDRESS_OF_IRC0_IRQHC  .equ    0xb0400c48

;************************************************************************
;* 5  Assembler macro code                                              *
;************************************************************************
;* This macro can be used to define a nested IRQ handler.
;* Currently GHS comliler does not support IRQ nesting,
;* so it is necessary to code in assembly language to define nested IRQ handler.
;*
;* Parameters:
;*  irq_handler_name: IRQ handler name.This macro generates a hanlder named irq_handler_name.
;*  isr_name        : Interrup service routine name which called by the irq_handler_name.
;*  irq_num         : IRQ number
;*
;* How to use:
;*  Define hanlders using the steps explained below.
;*
;*  - At first, make new assembler file.
;*  - Next include this file from new file.
;*  - Then Define handlers using this macro.
;*  - Implement Interrupt service routine "isr_name".
;*    "isr_name" can be coded in C or assembly language.
;*  - Assign irq_handler_name to INTERRUPTS_IRQ_ISR_??? which is defined in interrupts.h.
;*
;* example:
;*      - In new file
;*          .include    "nested_interrupt.asm"
;*
;*          .text       ;* Declare code section. (Change section if you need.)
;*          .nothumb    ;* use ARM mode.
;*
;*          define_nested_irq_handler irq_handler_0, irq_isr_0, 0
;*
;*          END
;*
;*      - In any file
;*          // implement ISR
;*          void irq_isr_0(void)
;*          {
;*              // code to handle the IRQ
;*              // clear IRQ flag at source (peripheral or software interrupt)
;*          }
;*
;*      - In interrupts.h
;*        // assign INTERRUPTS_IRQ_ISR_0
;*        #define INTERRUPTS_IRQ_ISR_0        irq_handler_0  ///< IRQ#0
;*
.macro define_nested_irq_handler irq_handler_name, isr_name, irq_num

    .macrolocal IRQST_check_loop
    .export     irq_handler_name
    .import     isr_name

irq_handler_name:
    SUB     lr, lr, #4                              ;* Save LR_irq and SPS_irq on system stack
    SRSFD   sp!, #0x1f                              ;* 
    CPS     #0x1f                                   ;* Switch to system mode
    PUSH    {r0-r3, r12}                            ;* Store remaining AAPCS register on system stack
    FSTMDBD sp!, {d0-d7}
    FMRX    r1, fpscr
    PUSH    r1
    AND     r1, sp, #4                              ;* Ensure stack is 8-byte aligned
    SUB     sp, sp, r1                              ;* 
    PUSH    {r1, lr}                                ;* Save LR_sys and stack adjusted value

    CPSIE   i                                       ;* Enable IRQ

    ;* Dummy write to IRC0_IRQR0 (for enable IRQ)
    MOVW    r1, #(ADDRESS_OF_IRC0_IRQR0 & 0xFFFF)   ;* Load register address (lower 16bit)
    MOVT    r1, #(ADDRESS_OF_IRC0_IRQR0 >> 16)      ;* Load register address (upper 16bit)
    MOV     r0, #0                                  ;* Set IRQR to "0" (no effect)
    STR     r0, [r1]                                ;* 

    ;* Call interrupt service routine
    LDR     r1, =isr_name
    BLX     r1

    ;* Check IRQST
    MOVW    r1, #(ADDRESS_OF_IRQST_nIRQ & 0xFFFF)   ;* Load register address (lower 16bit)
    MOVT    r1, #(ADDRESS_OF_IRQST_nIRQ >> 16)      ;* Load register address (upper 16bit)
IRQST_check_loop:
    LDRB    r0, [r1]                                ;* Read register value
    CMP     r0, #0
    BEQ     IRQST_check_loop                        ;* Loop while nIRQ == 0

    ;* Clear hold bit
    MOVW    r0, #(irq_num & 0x1FF)                  ;* Set to IRQ number (valid range: 0 to 511)
    MOVW    r1, #(ADDRESS_OF_IRC0_IRQHC & 0xFFFF)   ;* Load register address (lower 16bit)
    MOVT    r1, #(ADDRESS_OF_IRC0_IRQHC >> 16)      ;* Load register address (upper 16bit)
    STR     r0, [r1]                                ;* Clear hold bit

    CPSID   i                                       ;* Disable IRQ

    POP     {r1, lr}                                ;* Restore LR_sys and unajust stack
    ADD     sp, sp, r1                              ;* 
    POP     r1
    FMXR    fpscr, r1
    FLDMIAD sp!, {d0-d7}                            ;* Restore AAPCS register
    POP     {r0-r3, r12}
    RFEFD   sp!                                     ;* Return from system stack mode

.endm