// #========================================================================
// #
// #    vectors.S
// #
// #    ARM exception vectors
// #
// #========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2009 Free Software Foundation, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
// #========================================================================
// ######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_:

// Call thumb function from ARM mode, return to ARM
// mode afterwards
#define THUMB_CALL(_r_, _l_, _f_)                \
        ldr     _r_,=_f_+1                      ;\
        mov     lr,pc                           ;\
        bx      _r_                             ;\
        .pool                                   ;\
        .code   16                              ;\
        .thumb_func                             ;\
        ldr     _r_,=_l_  ## f                  ;\
        bx      _r_                             ;\
        .pool                                   ;\
        .code   32                              ;\
 _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_                           ;\
        .type _name_, function                  ;\
_name_:                                         ;\
        ldr     _r_,=_name_ ## _ARM             ;\
        bx      _r_                             ;\
        .code   32                              ;\
_name_ ## _ARM:

#else

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

// Call ARM function
#define THUMB_CALL(_r_, _l_, _f_) \
        bl      _f_

// 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_; \
        .type _name_, function ;\
_name_: 

#endif

        

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

// CYGHWR_HAL_ROM_VADDR is used when compiling for a different location
// from the base of ROM.  hal_platform_setup.h might define it.  For
// example, if flash is from 0x50000000 upwards (as on SA11x0), and we are
// to execute at 0x50040000, then we want the reset vector to point to
// 0x0004pqrs - the unmapped ROM address of the code - rather than
// 0x0000pqrs, which is the offset into our flash block.
// 
// But usually it's not defined, so the behaviour is the obvious.

#ifndef UNMAPPED        
#ifdef CYGHWR_HAL_ARM_HAS_MMU
# ifndef CYGHWR_HAL_ROM_VADDR
#  define CYGHWR_HAL_ROM_VADDR __exception_handlers
# endif
# define UNMAPPED(x) ((x)-CYGHWR_HAL_ROM_VADDR)
#else
# define UNMAPPED(x) (x)
#endif
#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 the vector table base (by default at
//   location 0x0000) at startup time.
//

#ifndef CYGHWR_HAL_VECTOR_TABLE_BASE
# define CYGHWR_HAL_VECTOR_TABLE_BASE     0x0
#endif  /* ifdef CYGHWR_HAL_VECTOR_TABLE_BASE */

        .code   32
        .section ".vectors","ax"

// This macro allows platforms to add their own code at the very start of
// the image.  This may be required in some circumstances where eCos ROM 
// based code does not run immediately upon reset and/or when some sort of
// special header is required at the start of the image.        
#ifdef PLATFORM_PREAMBLE
        PLATFORM_PREAMBLE
#endif
                        
        .global __exception_handlers
__exception_handlers:
#ifdef CYGSEM_HAL_ROM_RESET_USES_JUMP
// Assumption:  ROM code has these vectors at the hardware reset address.
// A simple jump removes any address-space dependencies [i.e. safer]
        b       reset_vector                    // 0x00
#else        
        ldr     pc,.reset_vector                // 0x00
#endif        
        ldr     pc,.undefined_instruction       // 0x04
        ldr     pc,.software_interrupt          // 0x08 start && software int
        ldr     pc,.abort_prefetch              // 0x0C
        ldr     pc,.abort_data                  // 0x10
#ifdef CYGNUM_HAL_ARM_VECTOR_0x14
        .word   CYGNUM_HAL_ARM_VECTOR_0x14
#else
        .word   0                               // unused
#endif
        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
#ifdef CYGSEM_HAL_ARM_PID_ANGEL_BOOT         
PTR(start) // This is copied to 0x28 for bootup // 0x40
#endif        
           // 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
	// what, if anything, hal_dram_type means is up to the platform
        .globl  hal_dram_type
hal_dram_type:  
        .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
        
#ifdef CYGHWR_HAL_ARM_ICE_THREAD_SUPPORT
        .balign 16      // Should be at 0x50
ice_thread_vector:
        .long   0       // Must be 'MICE'             
        .long   0       // Pointer to thread support vector
        .long   0       // eCos executing flag
        .long   0       // Must be 'GDB '
#endif // CYGHWR_HAL_ARM_ICE_THREAD_SUPPORT
        .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
        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]

#ifdef CYGHWR_HAL_ARM_ICE_THREAD_SUPPORT        
        ldr     r2,=ice_thread_vector
        sub     r2,r2,r1        // compute fixed (low memory) address
        ldr     r3,=0x4D494345  // 'MICE'
        str     r3,[r2],#4
        ldr     r3,=hal_arm_ice_thread_handler
        str     r3,[r2],#4
        mov     r3,#1
        str     r3,[r2],#4
        ldr     r3,=0x47444220  // 'GDB '
        str     r3,[r2],#4
#endif // CYGHWR_HAL_ARM_ICE_THREAD_SUPPORT

#if defined(CYGSEM_HAL_ARM_PID_ANGEL_BOOT)
// 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
        .type   start,function
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
        ldr     r0,=CYGHWR_HAL_VECTOR_TABLE_BASE           // 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,#HAL_ARM_SWI_VECTOR_ADDR]   // software interrupt
        str     r2,[r0,#HAL_ARM_SWI_VECTOR_ADDR]
10:
        ldr     r2,[r1,#HAL_ARM_IRQ_VECTOR]   // IRQ
        str     r2,[r0,#HAL_ARM_IRQ_VECTOR]
        ldr     r2,[r1,#HAL_ARM_IRQ_VECTOR_ADDR]
        str     r2,[r0,#HAL_ARM_IRQ_VECTOR_ADDR]
        ldr     r2,[r1,#HAL_ARM_FIQ_VECTOR]   // FIQ
        str     r2,[r0,#HAL_ARM_FIQ_VECTOR]
        ldr     r2,[r1,#HAL_ARM_FIQ_VECTOR_ADDR]
        str     r2,[r0,#HAL_ARM_FIQ_VECTOR_ADDR]
        ldr     r2,[r1,#HAL_ARM_PREFETCH_VECTOR]   // abort (prefetch)
        str     r2,[r0,#HAL_ARM_PREFETCH_VECTOR]
        ldr     r2,[r1,#HAL_ARM_PREFETCH_VECTOR_ADDR]   
        str     r2,[r0,#HAL_ARM_PREFETCH_VECTOR_ADDR]
        ldr     r2,[r1,#HAL_ARM_ABORT_VECTOR]   // abort (data)
        str     r2,[r0,#HAL_ARM_ABORT_VECTOR]
        ldr     r2,[r1,#HAL_ARM_ABORT_VECTOR_ADDR]
        str     r2,[r0,#HAL_ARM_ABORT_VECTOR_ADDR]

        LED 4

#if defined(CYG_HAL_STARTUP_ROM) || defined(CYG_HAL_STARTUP_ROMRAM)
        // Set up reset vector
        ldr     r0,=CYGHWR_HAL_VECTOR_TABLE_BASE
        ldr     r1,.__exception_handlers
# ifndef CYGSEM_HAL_KEEP_RESET_VECTOR
        ldr     r2,[r1,#HAL_ARM_RESET_VECTOR]    // reset vector instruction
        str     r2,[r0,#HAL_ARM_RESET_VECTOR]
#  ifndef CYGSEM_HAL_ROM_RESET_USES_JUMP // if using jump, reset vector address is not referenced
        ldr     r2,=warm_reset
        str     r2,[r0,#HAL_ARM_RESET_VECTOR_ADDR]
#  endif
# endif
        // 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_UNDEF_MODE)
        msr     cpsr,r0
        ldr     sp,.__exception_stack
#ifndef CYGOPT_HAL_ARM_FIQ_DISABLE
        mov     r0,#(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_FIQ_MODE)
        msr     cpsr,r0
        ldr     sp,.__fiq_stack
#endif
        
        // 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
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
        // use interrupt stack for system initialization since it's bigger 
        // than the "startup" stack in this configuration                                
        ldr     sp,.__interrupt_stack
#else        
        ldr     sp,.__startup_stack
#endif        

        // clear BSS
        ldr     r1,.__bss_start
        ldr     r2,.__bss_end
        mov     r0,#0
        cmp     r1,r2
        beq     2f
1:      str     r0,[r1],#4
        cmp     r2,r1
        bhi     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

#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

        LED 2
        
        // Run through static constructors
        bl      cyg_hal_invoke_constructors

        LED 1
        
        // This starts up the eCos kernel
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
        ldr     r1,=__startup_stack
        mov     sp,r1
#endif        
        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 non-user context [mode]
//             except in case of standalone app. running in user mode
//             (CYGOPT_HAL_ARM_WITH_USER_MODE should have been defined)
//
        .code   32
undefined_instruction:
        ldr     sp,.__undef_exception_stack     // get good stack
        stmfd   sp!,{r0-r5}                     // save some supervisor regs
        mrs     r1,spsr
        tst     r1,#CPSR_THUMB_ENABLE
        subeq   r0,lr,#4                // PC at time of interrupt (ARM)
        subne   r0,lr,#2                // PC at time of interrupt (thumb)
        mov     r2,#CYGNUM_HAL_EXCEPTION_ILLEGAL_INSTRUCTION
        mov     r3,sp
        b       call_exception_handler

        .code   32
software_interrupt:
        stmfd   sp!,{r8}
        ldr     r8,.__undef_exception_stack     // get good stack
        stmfd   r8!,{r0-r5}                     // save some supervisor regs
        mov     r3,r8
        ldmfd   sp!,{r8}
        mrs     r1,spsr
        tst     r1,#CPSR_THUMB_ENABLE
        subeq   r0,lr,#4                // PC at time of SWI (ARM)
        subne   r0,lr,#2                // PC at time of SWI (thumb)
        mov     r2,#CYGNUM_HAL_EXCEPTION_INTERRUPT
        b       call_exception_handler

        .code   32
abort_prefetch:
        ldr     sp,.__undef_exception_stack     // get good stack
        stmfd   sp!,{r0-r5}                     // save some supervisor regs
        sub     r0,lr,#4                        // PC at time of interrupt
        mrs     r1,spsr
        mov     r2,#CYGNUM_HAL_EXCEPTION_CODE_ACCESS
        mov     r3,sp
        b       call_exception_handler

        .code   32
abort_data:
        ldr     sp,.__undef_exception_stack     // get good stack
        stmfd   sp!,{r0-r5}                     // save some supervisor regs
        sub     r0,lr,#4                        // PC at time of interrupt
        mrs     r1,spsr
        mov     r2,#CYGNUM_HAL_EXCEPTION_DATA_ACCESS
        mov     r3,sp
        b       call_exception_handler
        
//
// Dispatch an exception handler.

        .code   32
call_exception_handler:
        //
        // On Entry:
        //
        // r4,r5 = scratch
        // r3 = pointer to temp save area
        // r2 = vector number
        // r1 = exception psr
        // r0 = exception pc
        // 
        // [r3+20]: exception r5
        // [r3+16]: exception r4
        // [r3+12]: exception r3
        // [r3+8] : exception r2
        // [r3+4] : exception r1
        // [r3]   : exception r0
        
        mrs     r4,cpsr                 // switch to Supervisor Mode
        bic     r4,r4,#CPSR_MODE_BITS
        orr     r4,r4,#CPSR_SUPERVISOR_MODE
        msr     cpsr,r4

        mov     r5,sp                   // save original svc sp
        mov	r4,lr                   // and original svc lr
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        // Make sure we use the GDB stack.
        ldr     sp,.__GDB_stack
        cmp     r5,sp                   // already on GDB stack?
        bhi     10f     
        ldr     r4,.__GDB_stack_base            
        cmp     r5,r4
        movhi   sp,r5
10:
#endif
        //
        // r5 holds original svc sp, current sp is stack to use
        // r4 holds original svc lr, which must also be preserved
        //

        stmfd   sp!,{r0-r2,r4,r5}       // push svc_sp, svc_lr, vector, psr, pc
        
#ifdef CYGOPT_HAL_ARM_WITH_USER_MODE
        // did exception occur in user mode ?
        and     r2, r1, #CPSR_MODE_BITS
        cmp     r2, #CPSR_USER_MODE
        bne     1f
        stmfd   sp, {r8-r12, sp, lr}^   // get user mode regs
	nop
        sub     sp, sp, #4*7
        bal     2f
1:
#endif
        // switch to pre-exception mode to get banked regs
        mov     r0,sp                   // r0 survives mode switch
        mrs     r2,cpsr                 // Save current psr for return
        orr     r1,r1,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        bic     r1,r1,#CPSR_THUMB_ENABLE
        msr     cpsr,r1
        stmfd   r0!,{r8-r12,sp,lr}
        msr     cpsr,r2                 // back to svc mode
        mov     sp,r0                   // update stack pointer
2:
        // now save pre-exception r0-r7 on current stack
        ldmfd   r3,{r0-r5}
        stmfd   sp!,{r0-r7}

        // SP needs fixing if exception occured in SVC mode.
        // The original SVC LR is still in place so that 
        // does not need to be fixed here.
        ldr     r1,[sp,#armreg_cpsr]
        and     r1,r1,#CPSR_MODE_BITS
        cmp     r1,#CPSR_SUPERVISOR_MODE
        ldreq   r1,[sp,#armreg_svcsp]
        streq   r1,[sp,#armreg_sp]

#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
        mov     r0,sp
        ldr     r1,.__dump_procs
        ldr     r2,[sp,#armreg_vector]
        ldr     r1,[r1,r2,lsl #2]
        THUMB_MODE(r9,10)
        mov     lr,pc
        mov     pc,r1
#else
        THUMB_MODE(r9,10)
#endif

        // call exception handler
        mov     r0,sp
        bl      exception_handler

#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
        mov     r0,sp
        bl      cyg_hal_report_exception_handler_returned
#endif

        ARM_MODE(r1,10)

        //
        // Return from exception
        //
return_from_exception:

        ldr     r0,[sp,#armreg_cpsr]

        // return to supervisor mode is simple
        and     r1,r0,#CPSR_MODE_BITS
        cmp     r1,#CPSR_SUPERVISOR_MODE

#ifndef CYGOPT_HAL_ARM_PRESERVE_SVC_SPSR
        msr     spsr,r0
        ldmeqfd sp,{r0-r14,pc}^
#else
        // we must take care of not corrupting the current (svc)
        // spsr which happens to be also the pre-exception spsr
        bne     1f
        tst     r0, #CPSR_THUMB_ENABLE
        
        // when returning to thumb/svc mode, there is no easy way to preserve
        // spsr. It is possible to do so, but would add a lot of instructions.
        // The purpose of CYGOPT_HAL_ARM_PRESERVE_SVC_SPSR is to allow stepping
        // through SWI exception handling code, so not preserving spsr in this
        // case should be okay.
        msrne   spsr,r0
        ldmnefd sp,{r0-r14,pc}^
        
        // we are returning to arm/svc mode thus we must restore the
        // pre-exception cpsr before returning to interrupted code
        msr     cpsr, r0
        ldmfd   sp, {r0-r14, pc}
1:
        // we are not returning to svc mode thus we can safely restore
        // svc spsr
        msr     spsr, r0
#endif

#ifdef CYGOPT_HAL_ARM_WITH_USER_MODE
        // are we returning to user mode ?
        and     r2, r1, #CPSR_MODE_BITS
        cmp     r2, #CPSR_USER_MODE
        add     r2, sp, #armreg_r8
        bne     1f
        ldmfd   r2, {r8-r14}^           // restore user mode regs
	nop
        bal     2f
1:
#else
        add     r2, sp, #armreg_r8
#endif
        //
        // return to other non-user modes is a little trickier
        //

        // switch to pre-exception mode and restore r8-r14
        mrs     r1,cpsr
        orr     r0,r0,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        bic     r0,r0,#CPSR_THUMB_ENABLE
        msr     cpsr,r0
        ldmfd   r2,{r8-r14}
        msr     cpsr, r1        // back to svc mode

2:	
        // move sp,lr and pc for final load
        ldr     r0,[sp,#armreg_svcsp]
        str     r0,[sp,#armreg_r8]
        ldr     r0,[sp,#armreg_svclr]	
        str     r0,[sp,#armreg_r9]
        ldr     r0,[sp,#armreg_pc]
        str     r0,[sp,#armreg_r10]

        // restore r0-r7,sp,lr and return from exception
        ldmfd   sp,{r0-r7,sp,lr,pc}^

#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
__dump_procs:
        .word  0    // placeholder for reset
        .word  cyg_hal_report_undefined_instruction
        .word  cyg_hal_report_software_interrupt
        .word  cyg_hal_report_abort_prefetch
        .word  cyg_hal_report_abort_data
        .word  0    // reserved
#endif


// Handle device interrupts
// This is slightly more complicated than the other exception handlers because
// it needs to interface with the kernel (if present).
// Assumption: can get here from any mode, including user mode
// (spurious interrupt while standalone app. is running in user mode)

        .code   32
FIQ:
#ifndef CYGOPT_HAL_ARM_FIQ_DISABLE
        // If we are not disabling FIQs as part of eCos, these will be under
        // user control. In theory the user should have installed their own
        // FIQ VSR before enabling them and we should never come here. If
        // we do there is nothing eCos itself can do because we are outside
        // the scope of the system. The default action here, then, is to simply
        // mask FIQs and return.
        mrs     r8,spsr                 // CPSR at time of interrupt
        orr	r8,r8,#CPSR_FIQ_DISABLE
        msr	spsr,r8
        subs	pc,lr,#4
#else        
        // We can get here from any non-user mode.
        mrs     r8,spsr                 // CPSR at time of interrupt
        and     r9,r8,#CPSR_MODE_BITS   // isolate pre-interrupt mode
        cmp	r9,#CPSR_IRQ_MODE
        bne	1f
        // If FIQ interrupted IRQ mode, just return with FIQ disabled.
        // The common interrupt handling takes care of the rest.
        orr	r8,r8,#CPSR_FIQ_DISABLE
        msr	spsr,r8
        subs	pc,lr,#4
    1:
        // If FIQ interrupted other non-user mode, switch to IRQ mode and
        // fall through to IRQ handler.
        ldr     sp,.__exception_stack   // get good stack to save lr and spsr
        stmdb   sp,{r8,lr}
        mov     r8,#CPSR_IRQ_MODE|CPSR_FIQ_DISABLE|CPSR_IRQ_DISABLE
        msr     cpsr,r8			// switch to IRQ mode
        ldr     sp,.__exception_stack   // get regs saved in FIQ mode
        ldmdb	sp,{sp,lr}
        msr     spsr,sp

        // now it looks like we got an IRQ instead of an FIQ except that
        // FIQ is disabled so we don't recurse.
#endif        
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
        stmfd   sp!,{r0-r5}             // save some supervisor regs
        sub     r0,lr,#4                // PC at time of interrupt
        mrs     r1,spsr
        mov     r2,#CYGNUM_HAL_VECTOR_IRQ
        mov     r3,sp
	
        mrs     r4,cpsr                 // switch to Supervisor Mode

#ifdef CYGOPT_HAL_ARM_FIQ_DISABLE        
        // Due to a small chance of contention with FIQ,
        // we should disable FIQ while still in IRQ mode and then
        // change to SVC mode. This only applies when eCos is masking
        // FIQ alongside IRQ, otherwise FIQs are outside eCos' control
        // and the FIQ to IRQ translation doesn't happen.
        orr     r4,r4,#CPSR_FIQ_DISABLE        
        msr     cpsr,r4
#endif        
        
        bic     r4,r4,#CPSR_MODE_BITS
        // When handling an IRQ we must disable FIQ unless the current 
        // mode in CPSR is IRQ. If we were to get a FIQ while in another 
        // mode, the FIQ handling code would transform the FIQ into an 
        // IRQ and call the non-reentrant IRQ handler again. As a result, 
        // for example, the stack pointer would be set to the beginning 
        // of the exception_stack clobbering the registers we have just 
        // saved.
        // However, if we are allowing the user to handle their own
        // FIQs, we don't need to do that, since FIQ will not be
        // translated to IRQ.
        orr     r4,r4,#CPSR_SUPERVISOR_MODE|CPSR_INTR_MASK
        msr     cpsr,r4

        mov     r5,sp                   // save original svc sp
	mov	r4,lr			// save original svc lr
        stmfd   sp!,{r0-r2,r4,r5}       // push svc_sp, svc_lr, vector, psr, pc
		
#ifdef CYGOPT_HAL_ARM_WITH_USER_MODE
        // did exception occur in user mode ?
        and     r2, r1, #CPSR_MODE_BITS
        cmp     r2, #CPSR_USER_MODE
        bne     1f
        stmfd   sp, {r8-r12, sp, lr}^   // get user mode regs
	nop
        sub     sp, sp, #4*7
        bal     2f
1:
#endif
        // switch to pre-exception mode to get banked regs
        mov     r0,sp                   // r0 survives mode switch
        mrs     r2,cpsr                 // Save current psr for return
        orr     r1,r1,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE
        bic     r1,r1,#CPSR_THUMB_ENABLE
        msr     cpsr,r1
        stmfd   r0!,{r8-r12,sp,lr}
        msr     cpsr,r2                 // back to svc mode
        mov     sp,r0                   // update stack pointer
	
2:
        // now save pre-exception r0-r7 on current stack
        ldmfd   r3,{r0-r5}
        stmfd   sp!,{r0-r7}

        // sp needs fixing if exception occured in SVC mode.
        ldr     r1,[sp,#armreg_cpsr]
        and     r1,r1,#CPSR_MODE_BITS
        cmp     r1,#CPSR_SUPERVISOR_MODE
        ldreq   r1,[sp,#armreg_svcsp]
        streq   r1,[sp,#armreg_sp]

        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

        cmp     r0,#CYGNUM_HAL_INTERRUPT_NONE   // spurious interrupt
        bne     10f

#ifdef  CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS
        // Acknowledge the interrupt
        THUMB_CALL(r1,12,hal_interrupt_acknowledge)
#else
        mov     r0,v6                   // register frame
        THUMB_CALL(r1,12,hal_spurious_IRQ)
#endif // CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS
        b       spurious_IRQ
        
10:     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

spurious_IRQ:           

#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
        // 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.

                              // don't run this for spurious interrupts!
        cmp     v1,#CYGNUM_HAL_INTERRUPT_NONE
        beq     17f
        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
        ARM_MODE(r1,10)
17:

//      mov     r0,sp
//      bl      show_frame_out

	// return from IRQ is same as return from exception
	b	return_from_exception

#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_INTR_MASK
        bic     r5,r4,#CPSR_INTR_MASK
        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_INTR_MASK
        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,#CPSR_INTR_MASK   // mask interrupts
        msr     cpsr,r1
        bx      lr                      // exit, _old_ in r0        

FUNC_START_ARM(hal_enable_interrupts, r1)
        mrs     r0,cpsr                 // current state
        bic     r1,r0,#CPSR_INTR_MASK   // mask interrupts
        msr     cpsr,r1
        bx      lr                      // exit
        
FUNC_START_ARM(hal_restore_interrupts, r1)
        mrs     r1,cpsr                 // current state
        bic     r1,r1,#CPSR_INTR_MASK   // mask out interrupts
        and     r0,r0,#CPSR_INTR_MASK   // keep only interrupts
        orr     r1,r1,r0                // mask interrupts
        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__

#ifndef CYGOPT_HAL_ARM_FIQ_DISABLE

// Functions for controlling FIQ independently of IRQ.
// These are only present if we are not manipulating FIQs
// alongside IRQs in the standard interrupt control macros.
        
FUNC_START_ARM(hal_disable_FIQ, r1)
        mrs     r0,cpsr                 // current state
        orr     r1,r0,#CPSR_FIQ_DISABLE // mask FIQs
        msr     cpsr,r1
        bx      lr                      // exit, _old_ in r0        

FUNC_START_ARM(hal_enable_FIQ, r1)
        mrs     r0,cpsr                 // current state
        bic     r1,r0,#CPSR_FIQ_DISABLE // mask FIQs
        msr     cpsr,r1
        bx      lr                      // exit
        
FUNC_START_ARM(hal_restore_FIQ, r1)
        mrs     r1,cpsr                 // current state
        bic     r1,r1,#CPSR_FIQ_DISABLE // mask out FIQs
        and     r0,r0,#CPSR_FIQ_DISABLE // keep only FIQs
        orr     r1,r1,r0                // mask FIQs
        msr     cpsr,r1
        bx      lr                      // exit        

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

#endif
        
// 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(__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
#ifdef  CYGHWR_HAL_ARM_DUMP_EXCEPTIONS
PTR(__dump_procs)
#endif
#ifndef CYGOPT_HAL_ARM_FIQ_DISABLE        
PTR(__fiq_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:
        .balign 4
        .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
__exception_stack:
        .rept   32
        .long   0
        .endr
__undef_exception_stack:

#ifndef CYGOPT_HAL_ARM_FIQ_DISABLE
        .balign 16
__fiq_stack_base: 
        .rept   CYGNUM_HAL_ARM_FIQ_STACK_SIZE
        .byte   0
        .endr
        .balign 16        
__fiq_stack:
#endif
        
// 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 CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE // rather than 1k
        .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
