// #========================================================================
// #
// #    vectors.S
// #
// #    ARM exception vectors
// #
// #========================================================================
// ####COPYRIGHTBEGIN####
//                                                                          
// -------------------------------------------                              
// The contents of this file are subject to the Red Hat eCos Public License 
// Version 1.1 (the "License"); you may not use this file except in         
// compliance with the License.  You may obtain a copy of the License at    
// http://www.redhat.com/                                                   
//                                                                          
// Software distributed under the License is distributed on an "AS IS"      
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
// License for the specific language governing rights and limitations under 
// the License.                                                             
//                                                                          
// The Original Code is eCos - Embedded Configurable Operating System,      
// released September 30, 1998.                                             
//                                                                          
// The Initial Developer of the Original Code is Red Hat.                   
// Portions created by Red Hat are                                          
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
// All Rights Reserved.                                                     
// -------------------------------------------                              
//                                                                          
// ####COPYRIGHTEND####
// #========================================================================
// ######DESCRIPTIONBEGIN####
// #
// # Author(s):     nickg, gthomas
// # Contributors:  nickg, gthomas
// # Date:          1999-02-20
// # Purpose:       ARM exception vectors
// # Description:   This file defines the code placed into the exception
// #                vectors. It also contains the first level default VSRs
// #                that save and restore state for both exceptions and
// #                interrupts.
// #
// #####DESCRIPTIONEND####
// #
// #========================================================================

#include <pkgconf/hal.h>
#include <pkgconf/hal_arm.h>
#ifdef CYGPKG_KERNEL  // no CDL yet
#include <pkgconf/kernel.h>
#else
# undef CYGFUN_HAL_COMMON_KERNEL_SUPPORT
# undef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
#endif
#include <cyg/hal/hal_platform_setup.h>

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
// The CDL should enforce this
#undef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
#endif

#include "arm.inc"

#ifdef __thumb__
// Switch to thumb mode
#define THUMB_MODE(_r_, _l_)                     \
        ldr     _r_,=_l_ ## f+1                 ;\
        bx      _r_                             ;\
        .pool                                   ;\
        .code   16                              ;\
        .thumb_func                             ;\
 _l_:

// Switch to ARM mode
#define ARM_MODE(_r_, _l_)                       \
        ldr     _r_,=_l_ ## f                   ;\
        bx      _r_                             ;\
        .pool                                   ;\
        .code   32                              ;\
 _l_:

// Function definition, start executing body in ARM mode
#define FUNC_START_ARM(_name_, _r_)              \
        .code   16                              ;\
        .thumb_func                             ;\
        .globl _name_                           ;\
_name_:                                         ;\
        ldr     _r_,=_name_ ## _ARM             ;\
        bx      _r_                             ;\
        .code   32                              ;\
_name_ ## _ARM:

#else

// Switch to thumb mode
#define THUMB_MODE(_r_, _l_)

// Switch to ARM mode
#define ARM_MODE(_r_, _l_)

// Function definition, start executing body in ARM mode
#define FUNC_START_ARM(_name_, _r_) \
        .globl _name_; \
_name_: 

#endif

        

#define PTR(name)               \
.##name: .word  name

#ifdef CYGHWR_HAL_ARM_HAS_MMU
#define UNMAPPED(x) ((x)-__exception_handlers)
#else
#define UNMAPPED(x) (x)
#endif
                                
#define UNMAPPED_PTR(name)              \
.##name: .word  UNMAPPED(name)


        .file   "vectors.S"


// CYGHWR_LED_MACRO can be defined in hal_platform_setup.h. It's free to
// use r0+r1. Argument is in "\x" - cannot use macro arguments since the
// macro may contain #-chars and use of arguments cause these to be 
// interpreted as CPP stringify operators.
// See example in PID hal_platform_setup.h.
#ifndef CYGHWR_LED_MACRO
#define CYGHWR_LED_MACRO
#endif
        
.macro LED x
    CYGHWR_LED_MACRO
.endm


//==========================================================================
// Hardware exception vectors.
//   This entire section will be copied to location 0x0000 at startup time.
//
        .code   32
        .section ".vectors","ax"
        .global __exception_handlers
__exception_handlers:
        ldr     pc,.reset_vector                // 0x00
        ldr     pc,.undefined_instruction       // 0x04
        ldr     pc,.software_interrupt          // 0x08 start && software int
        ldr     pc,.abort_prefetch              // 0x0C
        ldr     pc,.abort_data                  // 0x10
        .word   0                               // unused
        ldr     pc,.IRQ                         // 0x18
        ldr     pc,.FIQ                         // 0x1C

// The layout of these pointers should match the vector table above since
// they are copied in pairs.
        .global vectors
vectors:
UNMAPPED_PTR(reset_vector)                      // 0x20
PTR(undefined_instruction)                      // 0x24
PTR(software_interrupt)                         // 0x28
PTR(abort_prefetch)                             // 0x2C
PTR(abort_data)                                 // 0x30
        .word   0                               // 0x34
PTR(IRQ)                                        // 0x38
PTR(FIQ)                                        // 0x3c
PTR(start) // This is copied to 0x28 for bootup // 0x40
           // location 0x40 is used for storing DRAM size if known
           // for some platforms.
        
//
// "Vectors" - fixed location data items
//    This section contains any data which might be shared between
// an eCos application and any other environment, e.g. the debug
// ROM.                        
//
        .section ".fixed_vectors"
        // Interrupt/exception VSR pointers
        .globl  hal_vsr_table
hal_vsr_table:
        .rept   8               
        .long   0
        .endr

        .globl  hal_dram_size
hal_dram_size:  
        .long   0        

        .balign 16
#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT
	// Vectors used to communicate between eCos and ROM environments
        .globl  hal_virtual_vector_table
hal_virtual_vector_table:
        .rept   CYGNUM_CALL_IF_TABLE_SIZE
        .long   0
        .endr
#endif
        
        .balign 32
        
// Other vectors - this may include "fixed" locations
#ifdef PLATFORM_VECTORS
        PLATFORM_VECTORS
#endif
                        
        .text   
// Startup code which will get the machine into supervisor mode
        .global reset_vector
        .type   reset_vector,function
reset_vector:
        PLATFORM_SETUP1         // Early stage platform initialization
                                // which can set DRAM size at 0x40
                                // see <cyg/hal/hal_platform_setup.h>

        // Come here to reset board
warm_reset:                 

#if defined(CYG_HAL_STARTUP_RAM) && \
    !defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS)
        mrs     r7,cpsr                 // move back to IRQ mode
        and     r7,r7,#CPSR_MODE_BITS
        cmp     r7,#CPSR_SUPERVISOR_MODE
        beq     start
#endif

        // We cannot access any LED registers until after PLATFORM_SETUP1
        LED 7

        mov     r0,#0           // move vectors
        // We cannot perform a store until after PLATFORM_SETUP1
#ifndef CYGPKG_HAL_ARM_EBSA285  // EBSA285 sets the DRAM size above
#ifndef CYGPKG_HAL_ARM_SA11X0   // SA11x0 also uses this
        ldr     r1,=hal_dram_size
        str     r0, [r1]        // DRAM size to zero => unknown
#endif
#endif

        ldr     r1,=__exception_handlers
#ifndef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        // Wait with this if stubs are included (see further down).
        ldr     r2,[r1,#0x04]   // undefined instruction
        str     r2,[r0,#0x04]
        ldr     r2,[r1,#0x24]   
        str     r2,[r0,#0x24]
#endif
        ldr     r2,[r1,#0x08]   // software interrupt
        str     r2,[r0,#0x08]


#if defined(CYG_HAL_STARTUP_RAM)
// Ugly hack to get into supervisor mode
        ldr     r2,[r1,#0x40]
        str     r2,[r0,#0x28]

        LED 6
                
        swi                     // switch to supervisor mode
#endif        

// =========================================================================
// Real startup code. We jump here from the reset vector to set up the world.
        .globl  start
start:  

        LED 5

#if defined(CYG_HAL_STARTUP_RAM) && \
    !defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS)
// If we get restarted, hang here to avoid corrupting memory
        ldr     r0,.init_flag
        ldr     r1,[r0]
1:      cmp     r1,#0
        bne     1b
        ldr     r1,init_done
        str     r1,[r0]
#endif

        // Reset software interrupt pointer
        mov     r0,#0           // move vectors
        ldr     r1,.__exception_handlers
#if defined(CYG_HAL_STARTUP_RAM) && \
    !defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS)
        cmp     r7,#CPSR_SUPERVISOR_MODE
        beq     10f
#endif
        ldr     r2,[r1,#0x28]   // software interrupt
        str     r2,[r0,#0x28]
10:
        ldr     r2,[r1,#0x18]   // IRQ
        str     r2,[r0,#0x18]
        ldr     r2,[r1,#0x38]
        str     r2,[r0,#0x38]
        ldr     r2,[r1,#0x1C]   // FIQ
        str     r2,[r0,#0x1C]
        ldr     r2,[r1,#0x3C]
        str     r2,[r0,#0x3C]
        ldr     r2,[r1,#0x0C]   // abort (prefetch)
        str     r2,[r0,#0x0C]
        ldr     r2,[r1,#0x2C]   
        str     r2,[r0,#0x2C]
        ldr     r2,[r1,#0x10]   // abort (data)
        str     r2,[r0,#0x10]
        ldr     r2,[r1,#0x30]
        str     r2,[r0,#0x30]

        LED 4

#if defined(CYG_HAL_STARTUP_ROM)
        // Set up reset vector
        mov     r0,#0
        ldr     r1,.__exception_handlers
        ldr     r2,[r1,#0x00]    // reset vector intstruction
        str     r2,[r0,#0x00]
        ldr     r2,=warm_reset
        str     r2,[r0,#0x20]
        // Relocate [copy] data from ROM to RAM
        ldr     r3,.__rom_data_start
        ldr     r4,.__ram_data_start
        ldr     r5,.__ram_data_end
        cmp     r4,r5           // jump if no data to move
        beq     2f
        sub     r3,r3,#4        // loop adjustments
        sub     r4,r4,#4
1:      ldr     r0,[r3,#4]!     // copy info
        str     r0,[r4,#4]!
        cmp     r4,r5
        bne     1b
2:
#endif

        // initialize interrupt/exception environments
        ldr     sp,.__startup_stack
        mov     r0,#(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_IRQ_MODE)
        msr     cpsr,r0
        ldr     sp,.__exception_stack
        mov     r0,#(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_FIQ_MODE)
        msr     cpsr,r0
        ldr     sp,.__FIQ_exception_stack
        mov     r0,#(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_UNDEF_MODE)
        msr     cpsr,r0
        ldr     sp,.__exception_stack

        // initialize CPSR (machine state register)
        mov     r0,#(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_SUPERVISOR_MODE)
        msr     cpsr,r0

        // Note: some functions in LIBGCC1 will cause a "restore from SPSR"!!
        msr     spsr,r0

        // initialize stack
        ldr     sp,.__startup_stack

        // clear BSS
        ldr     r1,.__bss_start
        ldr     r2,.__bss_end
        mov     r0,#0
        cmp     r1,r2
        beq     2f
1:      str     r0,[r1],#4
        cmp     r1,r2
        bne     1b
2:

        // Run kernel + application in THUMB mode
        THUMB_MODE(r1,10)

        LED 3
        
        // Call platform specific hardware initialization
        bl      hal_hardware_init

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        bl      initialize_stub

        // Now that stub is initialized, change vector. It is possible
        // to single-step through most of the init code, except the below.
        // Put a breakpoint at the call to cyg_hal_invoke_constructors to
        // pass over this bit (s-s depends on internal state in the stub).
#endif

#if defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS) || \
    defined(CYGIMP_HAL_PROCESS_ALL_EXCEPTIONS)
        mov     r0,#0           // move vectors
        ldr     r1,=__exception_handlers
        ldr     r2,[r1,#0x04]   // undefined instruction
        str     r2,[r0,#0x04]
        ldr     r2,[r1,#0x24]   
        str     r2,[r0,#0x24]
#endif

	// FIXME: The line with the thumb check is a hack, allowing
	// the farm to run test. Problem is that virtual vector table
	// API needs to be ARM/Thumb consistent. Will fix later.
#if !defined(__thumb__) || defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS)
#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \
    || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
        .extern hal_ctrlc_isr_init
        bl      hal_ctrlc_isr_init
#endif
#endif
        LED 2
        
        // Run through static constructors
        bl      cyg_hal_invoke_constructors

        LED 1
        
        // This starts up the eCos kernel
        bl      cyg_start
_start_hang:
        b       _start_hang
        .code   32
        
        .global reset_platform
        .type   reset_platform,function
reset_platform:         
#ifdef CYGSEM_HAL_ROM_MONITOR
        // initialize CPSR (machine state register)
        mov     r0,#(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_SUPERVISOR_MODE)
        msr     cpsr,r0
        b       warm_reset
#else
        mov     r0,#0
        mov     pc,r0           // Jump to reset vector        
#endif                   

init_done:
        .long   0xDEADB00B

//
// Exception handlers
// Assumption: get here from a Supervisor context [mode]
//
        .code   32
undefined_instruction:
        ldr     sp,.__undef_exception_stack     // get good stack
        stmfd   sp!,{r0,fp,ip,lr}               
        mrs     r0,spsr
        stmfd   sp!,{r0}
        mov     ip,sp                   // save SP which will vanish with
                                        //   mode switch
        mrs     r0,cpsr                 // switch to Supervisor Mode
        bic     r0,r0,#CPSR_MODE_BITS
        orr     r0,r0,#CPSR_SUPERVISOR_MODE
        msr     cpsr,r0                 // sp,lr are now old values
        mov     fp,sp
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        ldr     sp,.__GDB_stack
        cmp     fp,sp                   // already on GDB stack?
        bhi     10f     
        ldr     r0,.__GDB_stack_base            
        cmp     fp,r0
        bls     10f                     // no - switch to GDB stack
                                        //      (already in sp)
        mov     sp,fp                   // yes - no switch
10:
#endif
        sub     sp,sp,#ARMREG_SIZE      // make space for frame
        stmea   sp,{r0-r10,fp}          // save immediately visible registers
        ldmfd   ip,{r0-r4}              // saved registers
        // Adjust PC according to CPU mode
        tst     r0,#CPSR_THUMB_ENABLE
        subeq   r4,r4,#4                // PC at time of interrupt (ARM)
        subne   r4,r4,#2                // PC at time of interrupt (thumb)
        str     r0,[sp,#armreg_cpsr]    // CPSR at time of interrupt
        str     r1,[sp,#armreg_r0]      // saved R0
        str     r2,[sp,#armreg_fp]      // saved FP
        str     r3,[sp,#armreg_ip]      // saved IP
        str     r4,[sp,#armreg_pc]      // PC at time of interrupt
        str     lr,[sp,#armreg_lr]      // LR at time of interrupt
        add     r0,ip,#ARMREG_SIZE
        str     fp,[sp,#armreg_sp]      // SP at time of interrupt
#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
        mov     r0,sp
        bl      cyg_hal_report_undefined_instruction
#endif
        mov     v1,#CYGNUM_HAL_EXCEPTION_ILLEGAL_INSTRUCTION
        b       call_exception_handler

        .code   32
software_interrupt:
        sub     sp,sp,#ARMREG_SIZE+16   // make space for frame
        stmea   sp,{r0-r10,fp}          // save immediately visible registers
        mrs     r0,spsr
        // Adjust PC according to CPU mode
        tst     r0,#CPSR_THUMB_ENABLE
        subeq   r3,lr,#4                // PC at time of interrupt (ARM)
        subne   r3,lr,#2                // PC at time of interrupt (thumb)
        str     r0,[sp,#armreg_cpsr]    // CPSR at time of interrupt
        str     ip,[sp,#armreg_ip]      // saved IP
        str     r3,[sp,#armreg_pc]      // PC at time of interrupt
        str     lr,[sp,#armreg_lr]      // LR at time of interrupt
        add     r0,sp,#ARMREG_SIZE+16
        str     r0,[sp,#armreg_sp]      // SP at time of interrupt
#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
        mov     r0,sp
        bl      cyg_hal_report_software_interrupt
#endif
        mov     v1,#CYGNUM_HAL_EXCEPTION_INTERRUPT
        b       call_exception_handler

        .code   32
abort_prefetch:
        ldr     sp,.__undef_exception_stack     // get good stack
        sub     lr,lr,#4                // PC at time of interrupt
        stmfd   sp!,{r0,fp,ip,lr}               
        mrs     r0,spsr
        stmfd   sp!,{r0}
        mov     ip,sp                   // save SP which will vanish with
                                        //   mode switch
        mrs     r0,cpsr                 // switch to Supervisor Mode
        bic     r0,r0,#CPSR_MODE_BITS
        orr     r0,r0,#CPSR_SUPERVISOR_MODE
        msr     cpsr,r0                 // sp,lr are now old values
        mov     fp,sp
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        ldr     sp,.__GDB_stack
        cmp     fp,sp                   // already on GDB stack?
        bhi     10f     
        ldr     r0,.__GDB_stack_base            
        cmp     fp,r0
        bls     10f                     // no - switch to GDB stack
                                        //      (already in sp)
        mov     sp,fp                   // yes - no switch
10:
#endif
        sub     sp,sp,#ARMREG_SIZE      // make space for frame
        stmea   sp,{r0-r10,fp}          // save immediately visible registers
        ldmfd   ip,{r0-r4}              // saved registers
        str     r0,[sp,#armreg_cpsr]    // CPSR at time of interrupt
        str     r1,[sp,#armreg_r0]      // saved R0
        str     r2,[sp,#armreg_fp]      // saved FP
        str     r3,[sp,#armreg_ip]      // saved IP
        str     r4,[sp,#armreg_pc]      // PC at time of interrupt
        str     lr,[sp,#armreg_lr]      // LR at time of interrupt
        add     r0,ip,#ARMREG_SIZE
        str     fp,[sp,#armreg_sp]      // SP at time of interrupt
#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
        mov     r0,sp
        bl      cyg_hal_report_abort_prefetch
#endif
        mov     v1,#CYGNUM_HAL_EXCEPTION_CODE_ACCESS
        b       call_exception_handler

        .code   32
abort_data:
        ldr     sp,.__undef_exception_stack     // get good stack
        sub     lr,lr,#4                // PC at time of interrupt
        stmfd   sp!,{r0,fp,ip,lr}               
        mrs     r0,spsr
        stmfd   sp!,{r0}
        mov     ip,sp                   // save SP which will vanish with
                                        //   mode switch
        mrs     r0,cpsr                 // switch to Supervisor Mode
        bic     r0,r0,#CPSR_MODE_BITS
        orr     r0,r0,#CPSR_SUPERVISOR_MODE
        msr     cpsr,r0                 // sp,lr are now old values
        mov     fp,sp
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        ldr     sp,.__GDB_stack
        cmp     fp,sp                   // already on GDB stack?
        bhi     10f     
        ldr     r0,.__GDB_stack_base            
        cmp     fp,r0
        bls     10f                     // no - switch to GDB stack
                                        //      (already in sp)
        mov     sp,fp                   // yes - no switch
10:
#endif
        sub     sp,sp,#ARMREG_SIZE      // make space for frame
        stmea   sp,{r0-r10,fp}          // save immediately visible registers
        ldmfd   ip,{r0-r4}              // saved registers
        str     r0,[sp,#armreg_cpsr]    // CPSR at time of interrupt
        str     r1,[sp,#armreg_r0]      // saved R0
        str     r2,[sp,#armreg_fp]      // saved FP
        str     r3,[sp,#armreg_ip]      // saved IP
        str     r4,[sp,#armreg_pc]      // PC at time of interrupt
        str     lr,[sp,#armreg_lr]      // LR at time of interrupt
        add     r0,ip,#ARMREG_SIZE
        str     fp,[sp,#armreg_sp]      // SP at time of interrupt
#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
        mov     r0,sp
        bl      cyg_hal_report_abort_data
#endif
        mov     v1,#CYGNUM_HAL_EXCEPTION_DATA_ACCESS
        b       call_exception_handler

        
//
// Dispatch an exception handler.

        .code   32
call_exception_handler:
        str     v1,[sp,#armreg_vector]
        mov     r0,sp

        THUMB_MODE(r9,10)

        bl      exception_handler
#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
        mov     r0,sp
        bl      cyg_hal_report_exception_handler_returned
#endif

        ARM_MODE(r1,10)

// Restore [interrupted] context
        mov     ip,sp                   // get stack pointer

        mrs     r0,cpsr                 // move back to IRQ mode
        orr     r0,r0,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        msr     cpsr,r0
        nop
        nop

        ldr     lr,[ip,#armreg_lr]
        ldr     sp,[sp,#armreg_sp]
                        
        bic     r0,r0,#CPSR_MODE_BITS
        orr     r0,r0,#CPSR_IRQ_MODE
        msr     cpsr,r0

        mov     sp,ip
        ldr     lr,[sp,#armreg_pc]
        ldr     r0,[sp,#armreg_cpsr]
        ldr     ip,[sp,#armreg_ip]
        msr     spsr,r0
        ldmfd   sp,{r0-r10,fp}

        movs    pc,lr                   // restore PC from LR, CPSR from SPSR

// Handle device interrupts
// This is slightly more complicated than the other exception handlers because
// it needs to interface with the kernel (if present).

        .code   32
FIQ:
        // Note: I use this exception stack while saving the context because
        // the current SP does not seem to be always valid in this CPU mode.
        ldr     sp,.__FIQ_exception_stack       // get good stack
        sub     lr,lr,#4                // PC at time of interrupt
        stmfd   sp!,{r0,fp,ip,lr}               
        mrs     r0,spsr
        stmfd   sp!,{r0}
        b       handle_IRQ_or_FIQ

IRQ:
        // Note: I use this exception stack while saving the context because
        // the current SP does not seem to be always valid in this CPU mode.
        ldr     sp,.__exception_stack   // get good stack
        sub     lr,lr,#4                // PC at time of interrupt
        stmfd   sp!,{r0,fp,ip,lr}               
        mrs     r0,spsr
        stmfd   sp!,{r0}

handle_IRQ_or_FIQ:              
        mov     ip,sp                   // save SP which will vanish with
                                        //   mode switch
        mrs     r0,cpsr                 // switch to Supervisor Mode
        bic     r0,r0,#CPSR_MODE_BITS
        orr     r0,r0,#CPSR_SUPERVISOR_MODE|CPSR_FIQ_DISABLE|CPSR_IRQ_DISABLE
        msr     cpsr,r0                 // sp,lr are now old values
        mov     fp,sp                   // save old SP
        sub     sp,sp,#ARMREG_SIZE      // make space for frame
        stmea   sp,{r0-r10,fp}          // save immediately visible registers
        ldmfd   ip,{r0-r4}              // saved registers
        str     r0,[sp,#armreg_cpsr]    // CPSR at time of interrupt
        str     r1,[sp,#armreg_r0]      // saved R0
        str     r2,[sp,#armreg_fp]      // saved FP
        str     r3,[sp,#armreg_ip]      // saved IP
        str     r4,[sp,#armreg_pc]      // PC at time of interrupt
        str     lr,[sp,#armreg_lr]      // LR at time of interrupt
        str     fp,[sp,#armreg_sp]      // SP at time of interrupt
        mov     v6,sp                   // Save pointer to register frame

//      mov     r0,sp
//      bl      _show_frame_in

#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
        // Switch to interrupt stack
        ldr     r2,.irq_level           // current number of nested interrupts
        ldr     r0,[r2]
        add     r1,r0,#1
        str     r1,[r2]                 // if was zero, switch stacks
        cmp     r0,#0
        moveq   r1,sp                   // save old stack pointer
        ldreq   sp,.__interrupt_stack
        stmeqfd sp!,{r1}
10:
#endif

        // The entire CPU state is now stashed on the stack,
        // increment the scheduler lock and handle the interrupt

#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT                 
        .extern cyg_scheduler_sched_lock
        ldr     r3,.cyg_scheduler_sched_lock
        ldr     r4,[r3]
        add     r4,r4,#1
        str     r4,[r3]
#endif

        THUMB_MODE(r3,10)

        mov     r0,v6
        bl      hal_IRQ_handler         // determine interrupt source
        mov     v1,r0                   // returned vector #

#if defined(CYGPKG_KERNEL_INSTRUMENT) && \
    defined(CYGDBG_KERNEL_INSTRUMENT_INTR)
        ldr     r0,=RAISE_INTR          // arg0 = type = INTR,RAISE
        mov     r1,v1                   // arg1 = vector
        mov     r2,#0                   // arg2 = 0
        bl      cyg_instrument          // call instrument function
#endif

        ARM_MODE(r0,10)

        mov     r0,v1                   // vector #

#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \
    || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
        // If we are supporting Ctrl-C interrupts from GDB, we must squirrel
        // away a pointer to the save interrupt state here so that we can
        // plant a breakpoint at some later time.

       .extern  hal_saved_interrupt_state
        ldr     r2,=hal_saved_interrupt_state
        str     v6,[r2]
#endif

#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS     
        cmp     r0,#0                   // ignore spurious interrupts
        beq     spurious_IRQ
#endif // CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS        
        ldr     r1,.hal_interrupt_data
        ldr     r1,[r1,v1,lsl #2]       // handler data
        ldr     r2,.hal_interrupt_handlers
        ldr     v3,[r2,v1,lsl #2]       // handler (indexed by vector #)
        mov     r2,v6                   // register frame (this is necessary
                                        // for the ISR too, for ^C detection)

#ifdef __thumb__
        ldr     lr,=10f
        bx      v3                      // invoke handler (thumb mode)
        .pool
        .code   16
        .thumb_func
IRQ_10T:
10:     ldr     r2,=15f
        bx      r2                      // switch back to ARM mode
        .pool
        .code   32
15:
IRQ_15A:
#else
        mov     lr,pc                   // invoke handler (call indirect
        mov     pc,v3                   // thru v3)
#endif

#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS     
spurious_IRQ:           
#endif // CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS  

#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
        // If we are returning from the last nested interrupt, move back
        // to the thread stack. interrupt_end() must be called on the
        // thread stack since it potentially causes a context switch.
        ldr     r2,.irq_level
        ldr     r3,[r2]
        subs    r1,r3,#1
        str     r1,[r2]
        ldreq   sp,[sp]         // This should be the saved stack pointer
#endif
#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT
        // The return value from the handler (in r0) will indicate whether a 
        // DSR is to be posted. Pass this together with a pointer to the
        // interrupt object we have just used to the interrupt tidy up routine.

        ldr     r1,.hal_interrupt_objects
        ldr     r1,[r1,v1,lsl #2]
        mov     r2,v6           // register frame

        THUMB_MODE(r3,10)

        bl      interrupt_end   // post any bottom layer handler
                                // threads and call scheduler
#endif

        ARM_MODE(r1,10)

//      mov     r0,sp
//      bl      show_frame_out

        mov     ip,sp                   // get stack pointer

        mrs     r0,cpsr                 // move back to IRQ mode
        orr     r0,r0,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        msr     cpsr,r0
        nop
        nop

        ldr     lr,[ip,#armreg_lr]
        ldr     sp,[sp,#armreg_sp]
                        
        bic     r0,r0,#CPSR_MODE_BITS
        orr     r0,r0,#CPSR_IRQ_MODE
        msr     cpsr,r0

        mov     sp,ip
        ldr     lr,[sp,#armreg_pc]
        ldr     r0,[sp,#armreg_cpsr]
        ldr     ip,[sp,#armreg_ip]
        msr     spsr,r0
        ldmfd   sp,{r0-r10,fp}
        movs    pc,lr                   // restore PC from LR, CPSR from SPSR

#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
// Execute pending DSRs the interrupt stack
// Note: this can only be called from code running on a thread stack
FUNC_START_ARM(hal_interrupt_stack_call_pending_DSRs, r1)
        stmfd   sp!,{r4,r5,lr}
        // Disable interrupts
        mrs     r4,cpsr                 // disable IRQ's
        orr     r2,r4,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        bic     r5,r4,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        msr     cpsr,r2
        // Switch to interrupt stack
        mov     r3,sp                   // save old stack pointer
        ldr     sp,.__interrupt_stack
        stmfd   sp!,{r3}                // stored at top of interrupt stack
        ldr     r2,.irq_level           // current number of nested interrupts
        ldr     r3,[r2]
        add     r3,r3,#1                // bump nesting level
        str     r3,[r2]
        msr     cpsr,r5                 // enable interrupts

        THUMB_MODE(r1,20)

        bl      cyg_interrupt_call_pending_DSRs


        ARM_MODE(r1,22)

        // Disable interrupts
        mrs     r1,cpsr                 // disable IRQ's
        orr     r2,r1,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        msr     cpsr,r2

        // Move back to the thread stack.
        ldr     r2,.irq_level
        ldr     r3,[r2]
        sub     r3,r3,#1                // decrement nesting level
        str     r3,[r2]
        ldr     sp,[sp]                 // This should be the saved stack pointer
        msr     cpsr,r4                 // restore interrupts to original state

#ifdef __thumb__
        ldmfd   sp!,{r4,r5,lr}          // return
        bx      lr
#else
        ldmfd   sp!,{r4,r5,pc}          // return
#endif // __thumb__
#endif // CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
        
// Thumb-only support functions
#ifdef __thumb__

FUNC_START_ARM(hal_disable_interrupts, r1)
        mrs     r0,cpsr                 // current state
        orr     r1,r0,#0xC0             // mask both FIQ and IRQ
        msr     cpsr,r1
        bx      lr                      // exit, _old_ in r0        

FUNC_START_ARM(hal_enable_interrupts, r1)
        mrs     r0,cpsr                 // current state
        bic     r1,r0,#0xC0             // mask both FIQ and IRQ
        msr     cpsr,r1
        bx      lr                      // exit
        
FUNC_START_ARM(hal_restore_interrupts, r1)
        mrs     r1,cpsr                 // current state
        bic     r1,r1,#0xC0             // mask out FIQ/IRQ bits
        and     r0,r0,#0xC0             // keep only FIQ/IRQ
        orr     r1,r1,r0                // mask both FIQ and IRQ
        msr     cpsr,r1
        bx      lr                      // exit        

FUNC_START_ARM(hal_query_interrupts, r1)
        mrs     r0,cpsr                 // current state
        bx      lr                      // exit, state in r0

#endif // __thumb__

// Dummy/support functions

        .global __gccmain
        .global _psr
        .global _sp

#ifdef __thumb__
        .code   16
        .thumb_func
__gccmain:
        bx      lr

        .code   16
        .thumb_func
_psr:
        ARM_MODE(r1,10)
        mrs     r0,cpsr
        bx      lr

        .code   16
        .thumb_func
_sp:
        mov     r0,sp
        bx      lr
#else
__gccmain:
        mov     pc,lr   

_psr:
        mrs     r0,cpsr
        mov     pc,lr

_sp:
        mov     r0,sp
        mov     pc,lr
#endif                


//
// Pointers to various objects.
//
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
PTR(__GDB_stack_base)
PTR(__GDB_stack)
#endif
PTR(__startup_stack)
PTR(__FIQ_exception_stack)
PTR(__exception_stack)
PTR(__undef_exception_stack)
PTR(__bss_start)
PTR(__bss_end)
PTR(_end)
PTR(__rom_data_start)
PTR(__ram_data_start)
PTR(__ram_data_end)
PTR(hal_interrupt_handlers)
PTR(hal_interrupt_data)
PTR(hal_interrupt_objects)
PTR(__exception_handlers)
PTR(init_flag)
#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT
PTR(cyg_scheduler_sched_lock)
#endif
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
PTR(irq_level)
PTR(__interrupt_stack)
#endif

//
// Identification - useful to find out when a system was configured
_eCos_id:
        .asciz  "eCos : " __DATE__


// -------------------------------------------------------------------------
// Interrupt vector tables.
// These tables contain the isr, data and object pointers used to deliver
// interrupts to user code.

// Despite appearances, their sizes are not #defines, but .equ symbols
// generated by magic without proper dependencies in arm.inc
// Recompiling will not DTRT without manual intervention.

        .data

init_flag:
        .long   0

        .extern hal_default_isr

        .globl  hal_interrupt_handlers
hal_interrupt_handlers:
        .rept   CYGNUM_HAL_ISR_COUNT
        .long   hal_default_isr
        .endr

        .globl  hal_interrupt_data
hal_interrupt_data:
        .rept   CYGNUM_HAL_ISR_COUNT
        .long   0
        .endr

        .globl  hal_interrupt_objects
hal_interrupt_objects:
        .rept   CYGNUM_HAL_ISR_COUNT
        .long   0
        .endr

// -------------------------------------------------------------------------
// Temporary interrupt stack
        
        .section ".bss"

// Small stacks, only used for saving information between CPU modes
__exception_stack_base: 
        .rept   32
        .long   0
        .endr
__FIQ_exception_stack:
        .rept   32
        .long   0
        .endr
__exception_stack:
        .rept   32
        .long   0
        .endr
__undef_exception_stack:

// Runtime stack used during all interrupt processing
#ifndef CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE
#define CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE 4096
#endif
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
        .balign 16
        .global cyg_interrupt_stack_base
cyg_interrupt_stack_base:
__interrupt_stack_base:
        .rept CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE
        .byte 0
        .endr
        .balign 16
        .global cyg_interrupt_stack
cyg_interrupt_stack:
__interrupt_stack:
irq_level:
        .long   0
#endif

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        .balign 16
__GDB_stack_base:
        .rept 0x400
        .byte 0
        .endr
__GDB_stack:
#endif
        .balign 16
__startup_stack_base:
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
        .rept 512
#else
        .rept CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE
#endif
        .byte 0
        .endr
        .balign 16
__startup_stack:

#ifdef PLATFORM_EXTRAS
#include PLATFORM_EXTRAS
#endif                                

// --------------------------------------------------------------------------
//  end of vectors.S
