##==========================================================================
##
##      vectors.S
##
##      PowerPC exception vectors
##
##==========================================================================
## ####ECOSGPLCOPYRIGHTBEGIN####                                            
## -------------------------------------------                              
## This file is part of eCos, the Embedded Configurable Operating System.   
## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2007, 2008 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, jskov
## Contributors: nickg, jskov
## Date:         1999-02-20
## Purpose:      PowerPC 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####
##
##==========================================================================

#===========================================================================
#
#       The PowerPC exception handling has changed as of version 1.3.1.
#       The primary motivation for rewriting the code was to bring it more
#       in line with the other HALs, in particular to allow a RAM application
#       to cleanly take over only a subset of vectors from a running ROM
#	monitor.
#
#       GDB stubs (and CygMon, should it be ported to PPC) copies
#       exception vector entry handler code to address 0. These vector entry
#       handlers (defined by the exception_vector macro below) compute
#       a vector index into the hal_vsr_table, fetch the pointer, and
#       jump to the HAL vector service routine (VSR).
#
#       The hal_vsr_table is located immediately after the vector
#       handlers (at address 0x3000), allowing RAM applications to
#       change VSRs as necessary, while still keeping desired ROM
#       monitor functionality available for debugging.
#
#       ROM applications can still be configured to leave the vector entry
#       handlers at 0xff000000, but there is at the moment no
#       provision for reclaiming the reserved vector space in RAM to
#       application usage.
#
#       RAM applications can also be configured to provide exception
#       handlers which are copied to address 0 on startup, thus taking
#       full control of the target.
#
#
#       Default configuration is for RAM applications to rely on an
#       existing ROM monitor to provide debugging functionality, and
#       for ROM applications to copy vectors to address 0.
#
#
#       Unfortunately the new exception scheme is not compatible with the
#       old scheme. Stubs and applications must be compiled using the same
#       scheme (i.e., old binaries will not run with new stubs, and new
#       binaries will not run with old stubs).
#
#===========================================================================

#include <pkgconf/hal.h>

#ifdef CYGPKG_KERNEL
#include <pkgconf/kernel.h>     // CYGPKG_KERNEL_INSTRUMENT
#endif

#define CYGARC_HAL_COMMON_EXPORT_CPU_MACROS
#include <cyg/hal/ppc_regs.h>

#===========================================================================
                
//        .file   "vectors.S"
        
        .extern hal_interrupt_data
        .extern hal_interrupt_handlers
        .extern hal_interrupt_objects
        .extern hal_vsr_table

        .extern cyg_hal_invoke_constructors
        .extern cyg_instrument
        .extern cyg_start
        .extern hal_IRQ_init
        .extern hal_MMU_init
        .extern hal_enable_caches
        .extern hal_hardware_init
        .extern initialize_stub

        .extern __bss_start
        .extern __bss_end
        .extern __sbss_start
        .extern __sbss_end

#===========================================================================
# MSR initialization value
# zero all bits except:
# FP = floating point available
# ME = machine check enabled
# IP = vectors at 0xFFFxxxxx (ROM startup only)
# IR = instruction address translation
# DR = data address translation
# RI = recoverable interrupt

#define CYG_MSR_COMMON (MSR_FP | MSR_ME | MSR_RI)

#if (CYGHWR_HAL_POWERPC_VECTOR_BASE == 0xfff00000)
# define IP_BIT MSR_IP
#else
# define IP_BIT 0
#endif

#ifdef CYGHWR_HAL_POWERPC_ENABLE_MMU
# define IR_DR_BITS (MSR_IR | MSR_DR)
#else
# define IR_DR_BITS 0
#endif

#define CYG_MSR (CYG_MSR_COMMON | IP_BIT | IR_DR_BITS)

# Include variant macros after MSR definition.        
#include <cyg/hal/arch.inc>
#include <cyg/hal/ppc_offsets.inc>


#===========================================================================
# If the following option is enabled, we only save registers up to R12.
# The PowerPC ABI defines registers 13..31 as callee saved and thus we do
# not need to save them when calling C functions.

#ifdef CYGDBG_HAL_COMMON_INTERRUPTS_SAVE_MINIMUM_CONTEXT
# define MAX_SAVE_REG    12
#else
# define MAX_SAVE_REG    31
#endif                  
        

#if defined(CYGHWR_HAL_POWERPC_NEED_VECTORS)

#===========================================================================
# Start by defining the exceptions vectors that must be placed at
# locations 0xFFF00000 or 0x00000000. The following code will normally
# be located at 0xFFF00000 in the ROM. It may optionally be copied out
# to 0x00000000 if we want to use the RAM vectors. For this reason this code
# MUST BE POSITION INDEPENDENT.
        
        .section ".vectors","ax"

#---------------------------------------------------------------------------
# Macros for generating an exception vector service routine

# Reset vector macro

        .macro  reset_vector name
        .p2align CYGHWR_HAL_POWERPC_VECTOR_ALIGNMENT
        .globl  __exception_\name
__exception_\name:
#ifdef CYGSEM_HAL_POWERPC_RESET_USES_JUMP        
        bl      _start               
#else
        lwi     r3,_start
        mtlr    r3
        blr
#endif                

        .endm
        
# Generic vector macro
                
        .macro  exception_vector name
        .p2align CYGHWR_HAL_POWERPC_VECTOR_ALIGNMENT
        .globl  __exception_\name
__exception_\name:
        mtspr   SPRG1,r3                     # stash some work registers away
        mtspr   SPRG2,r4                    
        mtspr   SPRG3,r5                    
        mfcr    r4                           # stash CR
        li      r5,__exception_\name-rom_vectors       # load low half of vector addr
        srwi    r5,r5,(CYGHWR_HAL_POWERPC_VECTOR_ALIGNMENT-2) # shift right by alignment-2
        lwi     r3,hal_vsr_table             # table base
        lwzx    r3,r3,r5                     # address of vsr
        mflr    r5                           # save link register
        mtlr    r3                           # put vsr address into it
        li      r3,__exception_\name-rom_vectors       # reload low half of vector addr
        blr                                  # go to common code
        .endm
        
#---------------------------------------------------------------------------
# Define the exception vectors.

// Some platforms won't let us put the vector code just where we want
// This macro introduces some lattitude in vector placement
                        
#ifdef CYG_HAL_FUDGE_VECTOR_ALIGNMENT        
        hal_fudge_vector_alignment
#endif

        hal_fixed_vectors
        
rom_vectors:
        # These are the architecture defined vectors that
        # are always present.
#ifdef CYG_HAL_RESERVED_VECTOR_00000
        hal_reserved_vector_00000                
#else
        exception_vector        reserved_00000
#endif        
        reset_vector            reset
        exception_vector        machine_check
        exception_vector        data_storage
        exception_vector        instruction_storage
        exception_vector        external
        exception_vector        alignment
        exception_vector        program
        exception_vector        floatingpoint_unavailable
        exception_vector        decrementer
        exception_vector        reserved_00a00
        exception_vector        reserved_00b00
        exception_vector        system_call
        exception_vector        trace
        exception_vector        floatingpoint_assist
        exception_vector        reserved_00f00

#if defined(CYGHWR_HAL_POWERPC_BOOK_E)
        # Book E defines some extra vectors
        exception_vector        critical_input
        exception_vector        ap_unavailable
        exception_vector        interval_timer
        exception_vector        watchdog
        exception_vector        data_tlb_miss
        exception_vector        instruction_tlb_miss
        exception_vector        debug        
#endif
        
        # Variants may define extra vectors.
        hal_extra_vectors

rom_vectors_end:   

#else //  CYGHWR_HAL_POWERPC_NEED_VECTORS

        # When vectors are not included this is the primary entry point.
        .globl  __exception_reset
__exception_reset:
        lwi     r3,_start
        mtlr    r3
        blr
        
#endif //  CYGHWR_HAL_POWERPC_NEED_VECTORS



#===========================================================================
# Real startup code. We jump here from the various reset vectors to set up
# the world.
        
        .text   
        .globl  _start
_start:
        # Initialize CPU to a post-reset state, ensuring the ground doesn''t
        # shift under us while we try to set things up.
        hal_cpu_init

        # Set up global offset table
        lwi     r2,_GLOBAL_OFFSET_TABLE_

        # set up time base register to zero
        xor     r3,r3,r3
        mtspr   TBL_W,r3
        xor     r4,r4,r4
        mtspr   TBU_W,r4

        # Call platform specific hardware initialization
        # This may include memory controller initialization. It is not
        # safe to access RAM until after this point.
        bl      hal_hardware_init	# this is platform dependent
        .globl  _hal_hardware_init_done
_hal_hardware_init_done:

        hal_vectors_init
        
        # set up stack
        lwi     sp,__interrupt_stack
        mtspr   SPRG0,sp        # save in sprg0 for later use

        # Set up exception handlers and VSR table, taking care not to
        # step on any ROM monitor''s toes.
        hal_mon_init        

#if defined(CYG_HAL_STARTUP_ROM)
        # Copy data from ROM to ram
        lwi     r3,__rom_data_start     # r3 = rom start
        lwi     r4,__ram_data_start     # r4 = ram start
        lwi     r5,__ram_data_end       # r5 = ram end

        cmplw   r4,r5                   # skip if no data
        beq     2f
        sub     r5,r5,r4                # compute number of words to copy
        srwi    r5,r5,2
        mtctr   r5
        subi    r3,r3,4
        subi    r4,r4,4
1:      lwzu    r0,4(r3)                # get word from ROM
        stwu    r0,4(r4)                # store in RAM
        bdnz    1b
2:
#endif

        # clear BSS
        lwi     r3,__bss_start  # r3 = start
        lwi     r4,__bss_end    # r4 = end
        li      r0,0            # r0 = 0
        cmplw   r3,r4           # skip if no bss
        beq     2f
        sub     r4,r4,r3        # compute number of words to clear
        srwi    r4,r4,2
        mtctr   r4
        subi    r3,r3,4
        
1:      stwu    r0,4(r3)        # store zero & increment pointer
        bdnz    1b
2:

        # clear SBSS
        lwi     r3,__sbss_start # r3 = start
        lwi     r4,__sbss_end   # r4 = end
        cmplw   r3,r4           # skip if no sbss
        beq     2f
        sub     r4,r4,r3        # compute number of words to clear
        srwi    r4,r4,2
        mtctr   r4
        subi    r3,r3,4
        
1:      stwu    r0,4(r3)        # store zero & increment pointer
        bdnz    1b
2:

        # It is now safe to call C functions which may rely on initialized
        # data.
        
        # Set up stack for calls to C code.
        subi    sp,sp,12                        # make space on stack
        li      r0,0
        stw     r0,0(sp)                        # clear back chain
        stw     r0,8(sp)                        # zero return pc
        stwu    sp,-CYGARC_PPC_STACK_FRAME_SIZE(sp) # create new stack frame

        # Variant HALs may need to do something special before we continue
        bl      hal_variant_init

        # Platform initialization
        bl      hal_platform_init
        
        # MMU and cache are controlled by the same option since caching
        # on the PPC [typically] does not make sense without the MMU to mark 
        # regions which should not be cached.
#ifdef CYGHWR_HAL_POWERPC_ENABLE_MMU
        # Initialize MMU.
        bl      hal_MMU_init

        # Enable MMU (if desired) so we can safely enable caches.
        lwi     r3,CYG_MSR              # interrupts enabled later
        sync
        mtmsr   r3
        sync
	
        # Enable caches
        bl      hal_enable_caches
#endif // CYGHWR_HAL_POWERPC_ENABLE_MMU

        # set up platform specific interrupt environment
        bl      hal_IRQ_init

        # call c++ constructors
        bl      cyg_hal_invoke_constructors

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        bl      initialize_stub
#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
        
        bl      cyg_start                       # call cyg_start
9:      
        b       9b              # if we return, loop

#---------------------------------------------------------------------------
# This code handles the common part of all exception handlers.
# It saves the machine state onto the stack  and then calls
# a "C" routine to do the rest of the work. This work may result
# in thread switches, and changes to the saved state. When we return
# here the saved state is restored and execution is continued.
        
        .text

        .globl cyg_hal_default_exception_vsr
cyg_hal_default_exception_vsr:
        
        # We come here with all register containing their
        # pre-exception values except:
        # R3    = ls 16 bits of vector address
        # R4    = saved CR
        # R5    = saved LR
        # LR    = VSR address
        # SPRG1 = old R3
        # SPRG2 = old R4
        # SPRG3 = old R5
        # SRR0  = old PC
        # SRR1  = old MSR and the exception cause (the POW state is lost!)

        subi    sp,sp,CYGARC_PPC_EXCEPTION_DECREMENT
                                        # leave space for registers and
                                        # a safety margin
#ifdef CYGPKG_HAL_POWERPC_PPC40x
// The caches on this processor are always enabled when the MMU is on
// (and disabled when off).  Thus we need to be careful about cache
// polution and staleness when changing the MMU state.
// At this point, the MMU is off due to the exception.  We need to
// flush the part of the cache which may be touched before the MMU
// is reenabled so that memory will be consistent when that happens.
// Of course, this is complicated by the fact that there are no "free"
// registers at this point in the code.
        dcbf    0,sp                    // Flushes first line
        stw     r3,0(sp)                // This is now safe
        li      r3,CYGARC_PPCREG_VECTOR // Flush lines which will be changed
        dcbf    r3,sp                   
        li      r3,CYGARC_PPCREG_CR
        dcbf    r3,sp                   
        li      r3,CYGARC_PPCREG_LR
        dcbf    r3,sp              
        lwz     r3,0(sp)                // Restore register
#endif        

        # First, save away some registers
        stw     r3,CYGARC_PPCREG_VECTOR(sp)    # stash vector
        stw     r4,CYGARC_PPCREG_CR(sp)        # stash CR
        stw     r5,CYGARC_PPCREG_LR(sp)        # stash LR

        # Save exception PC and MSR
        mfsrr0  r3
        stw     r3,CYGARC_PPCREG_PC(sp)
        mfsrr1  r3
        stw     r3,CYGARC_PPCREG_MSR(sp)
        
cyg_hal_default_exception_vsr_common:   
        
#ifdef CYGDBG_HAL_POWERPC_FRAME_WALLS
        # Mark this frame as an Exception frame
        lwi     r3,0xDDDDDDE0
        stw     r3,CYGARC_PPCREG_WALL_HEAD(sp)
        lwi     r3,0xDDDDDDE1
        stw     r3,CYGARC_PPCREG_WALL_TAIL(sp)
#endif

        # Enable MMU & interrupt/FPU environment (as configured)
        lwi     r3,CYG_MSR
        sync
        mtmsr   r3
        sync
                
        mfspr   r3,SPRG1                # save original R3
        stw     r3,CYGARC_PPCREG_REGS+3*4(sp)
        mfspr   r4,SPRG2                # save original R4
        stw     r4,CYGARC_PPCREG_REGS+4*4(sp)
        mfspr   r5,SPRG3                # save original R5
        stw     r5,CYGARC_PPCREG_REGS+5*4(sp)

        stw     r0,CYGARC_PPCREG_REGS+0*4(sp)  # save R0
        stw     r2,CYGARC_PPCREG_REGS+2*4(sp)  # save R2
        
        mr      r3,sp                   # recreate original SP
        addi    r3,r3,CYGARC_PPC_EXCEPTION_DECREMENT
        stw     r3,CYGARC_PPCREG_REGS+1*4(sp)  # and save it in state
        
        # Save registers r6..r12/r31
        .set    _reg,6
        .rept   MAX_SAVE_REG+1-6
        stw     _reg,(CYGARC_PPCREG_REGS+_reg*4)(sp)
        .set    _reg,_reg+1
        .endr

        # Save registers used in vsr (r14+r15)
        stw     r14,(CYGARC_PPCREG_REGS+14*4)(sp)
        stw     r15,(CYGARC_PPCREG_REGS+15*4)(sp)
        
        # get remaining family CPU registers
        mfxer   r3
        mfctr   r4
        # and store them
        stw     r3,CYGARC_PPCREG_XER(sp)
        stw     r4,CYGARC_PPCREG_CTR(sp)

        # Save variant registers
        hal_variant_save sp

        # Save FPU registers
        hal_fpu_save sp

        # The entire CPU state is now stashed on the stack,
        # call into C to do something with it.

        mr      r3,sp                           # R3 = register dump
        
        subi    sp,sp,CYGARC_PPC_STACK_FRAME_SIZE # make a null frame

        li      r0,0                            # R0 = 0
        stw     r0,0(sp)                        # backchain = 0
        stw     r0,8(sp)                        # return pc = 0
        
        stwu    sp,-CYGARC_PPC_STACK_FRAME_SIZE(sp) # create new stack frame
                                                # where C code can save LR

        lwi     r5,restore_state                # get return link
        mtlr    r5                              # to link register

        .extern cyg_hal_exception_handler
        b       cyg_hal_exception_handler       # call C code, r3 = registers

        # When the call returns it will go to restore_state below.

#if defined(CYGHWR_HAL_POWERPC_BOOK_E)        
##--------------------------------------------------------------------------
## Critical exception handling code.
##
## Book E processors save the old PC and MSR in CSRR0/1 for certain
## exceptions. This VSR saves the values from these registers before
## branching to the common VSR code.
        
        .globl cyg_hal_default_critical_exception_vsr
cyg_hal_default_critical_exception_vsr: 
        
        # We come here with all register containing their
        # pre-exception values except:
        # R3    = ls 16 bits of vector address
        # R4    = saved CR
        # R5    = saved LR
        # LR    = VSR address
        # SPRG1 = old R3
        # SPRG2 = old R4
        # SPRG3 = old R5
        # CSRR0 = old PC
        # CSRR1 = old MSR and the exception cause

        subi    sp,sp,CYGARC_PPC_EXCEPTION_DECREMENT
                                        # leave space for registers and
                                        # a safety margin

        # First, save away some registers
        stw     r3,CYGARC_PPCREG_VECTOR(sp)    # stash vector
        stw     r4,CYGARC_PPCREG_CR(sp)        # stash CR
        stw     r5,CYGARC_PPCREG_LR(sp)        # stash LR

        # Save exception PC and MSR
        mfspr   r3,58
        stw     r3,CYGARC_PPCREG_PC(sp)
        mfspr   r3,59
        stw     r3,CYGARC_PPCREG_MSR(sp)

        b cyg_hal_default_exception_vsr_common

#endif        
        
##--------------------------------------------------------------------------
## The following macros are defined depending on whether the Interrupt
## system is using isr tables or chaining, and depending on the interrupt
## controller in the system.

#ifndef CYGPKG_HAL_POWERPC_INTC_DEFINED

## This is the simple version. No interrupt controller, CYGARC_PPCREG_VECTOR 
## is updated with the decoded interrupt vector. Isr tables/chaining
## use same interrupt decoder.
## Bit 21 biffers between decrementer (0) and external (1).

        # decode the interrupt
	.macro  hal_intc_decode dreg,state
        lwz     \dreg,CYGARC_PPCREG_VECTOR(\state) # retrieve vector number,
        rlwinm  \dreg,\dreg,22,31,31               # isolate bit 21 and update
        stw     \dreg,CYGARC_PPCREG_VECTOR(\state) # vector in state frame.
        slwi    \dreg,\dreg,2                      # convert to word offset.
        .endm                              

#endif // CYGPKG_HAL_POWERPC_INTC_DEFINED

#---------------------------------------------------------------------------
# Common interrupt handling code.

        .globl cyg_hal_default_interrupt_vsr
cyg_hal_default_interrupt_vsr:

        # We come here with all register containing their
        # pre-exception values except:
        # R3    = ls 16 bits of vector address
        # R4    = saved CR
        # R5    = saved LR
        # LR    = VSR address
        # SPRG1 = old R3
        # SPRG2 = old R4
        # SPRG3 = old R5
        # SRR0  = old PC
        # SRR1  = old MSR


        subi    sp,sp,CYGARC_PPC_EXCEPTION_DECREMENT
                                        # leave space for registers and
                                        # a safety margin
#ifdef CYGPKG_HAL_POWERPC_PPC40x
// The caches on this processor are always enabled when the MMU is on
// (and disabled when off).  Thus we need to be careful about cache
// polution and staleness when changing the MMU state.
// At this point, the MMU is off due to the exception.  We need to
// flush the part of the cache which may be touched before the MMU
// is reenabled so that memory will be consistent when that happens.
// Of course, this is complicated by the fact that there are no "free"
// registers at this point in the code.
        dcbf    0,sp                    // Flushes first line
        stw     r3,0(sp)                // This is now safe
        li      r3,CYGARC_PPCREG_VECTOR // Flush lines which will be changed
        dcbf    r3,sp                   
        li      r3,CYGARC_PPCREG_CR
        dcbf    r3,sp                   
        li      r3,CYGARC_PPCREG_LR
        dcbf    r3,sp              
        lwz     r3,0(sp)                // Restore register
#endif        
              
        stw     r3,CYGARC_PPCREG_VECTOR(sp)    # stash vector
        stw     r4,CYGARC_PPCREG_CR(sp)        # stash CR
        stw     r5,CYGARC_PPCREG_LR(sp)        # stash LR

        # Save exception PC and MSR
        mfsrr0  r3
        stw     r3,CYGARC_PPCREG_PC(sp)
        mfsrr1  r3
        stw     r3,CYGARC_PPCREG_MSR(sp)
        
cyg_hal_default_interrupt_vsr_common:   
                
#ifdef CYGDBG_HAL_POWERPC_FRAME_WALLS
        # Mark this frame as an interrupt frame
        lwi     r3,0xDDDDDD10
        stw     r3,CYGARC_PPCREG_WALL_HEAD(sp)
        lwi     r3,0xDDDDDD11
        stw     r3,CYGARC_PPCREG_WALL_TAIL(sp)
#endif

        # Enable MMU & interrupt/FPU environment as configured
        lwi     r3,CYG_MSR
        sync
        mtmsr   r3
        sync

        mfspr   r3,SPRG1                # save original R3
        stw     r3,CYGARC_PPCREG_REGS+3*4(sp)
        mfspr   r4,SPRG2                # save original R4
        stw     r4,CYGARC_PPCREG_REGS+4*4(sp)
        mfspr   r5,SPRG3                # save original R5
        stw     r5,CYGARC_PPCREG_REGS+5*4(sp)

        stw     r0,CYGARC_PPCREG_REGS+0*4(sp)  # save R0
        stw     r2,CYGARC_PPCREG_REGS+2*4(sp)  # save R2

        mr      r3,sp                   # recreate original SP
        addi    r3,r3,CYGARC_PPC_EXCEPTION_DECREMENT
        stw     r3,CYGARC_PPCREG_REGS+1*4(sp)  # and save it in state
        
        # Save registers r6..r12/r31
        .set    _reg,6
        .rept   MAX_SAVE_REG+1-6
        stw     _reg,(CYGARC_PPCREG_REGS+_reg*4)(sp)
        .set    _reg,_reg+1
        .endr

        # Save registers used in vsr (r14+r15)
        stw     r14,CYGARC_PPCREG_REGS+14*4(sp)
        stw     r15,CYGARC_PPCREG_REGS+15*4(sp)

        # get remaining family CPU registers
        mfxer   r3
        mfctr   r4

        # and store them
        stw     r3,CYGARC_PPCREG_XER(sp)
        stw     r4,CYGARC_PPCREG_CTR(sp)
                
        # Save variant registers
        hal_variant_save sp

        # Save FPU registers
        hal_fpu_save sp

        # The entire CPU state is now stashed on the stack,
        # increment the scheduler lock and call the ISR
        # for this vector.

#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT                 
        .extern cyg_scheduler_sched_lock
        lwi     r3,cyg_scheduler_sched_lock
        lwz     r4,0(r3)
        addi    r4,r4,1
        stw     r4,0(r3)
#endif
        
        mr      r14,sp                          # r14 = register dump
        
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK 
        lwi     r3,__interrupt_stack            # stack top
        lwi     r4,__interrupt_stack_base       # stack base
        sub.    r5,sp,r4                        # sp - base
        blt     1f                              # if < 0 - not on istack
        sub.    r5,r3,sp                        # top - sp
        bgt     2f                              # if > 0 - already on istack

1:      mr      sp,r3                           # switch to istack

2:      stwu    r14,-4(sp)                      # save old SP on stack

#endif
        
        subi    sp,sp,CYGARC_PPC_STACK_FRAME_SIZE # make a null frame

        li      r0,0                            # R0 = 0
        stw     r0,0(sp)                        # backchain = 0
        stw     r0,8(sp)                        # return pc = 0
        
        stwu    sp,-CYGARC_PPC_STACK_FRAME_SIZE(sp) # create new stack frame
                                                # where C code can save LR
        
#if defined(CYGPKG_KERNEL_INSTRUMENT) && defined(CYGDBG_KERNEL_INSTRUMENT_INTR)

        lwi     r3,0x0301                       # r3 = type = INTR,RAISE
        lwz     r4,CYGARC_PPCREG_VECTOR(r14)    # arg1 = vector address
        srwi    r4,r4,8                         # arg1 = vector number
        xor     r5,r5,r5                        # arg2 = 0
        bl      cyg_instrument                  # call instrument function
        
#endif

        hal_intc_decode r15,r14                # get table index

#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
	lwi	r3,hal_saved_interrupt_state
	stw	r14,0(r3)
	
#endif

#ifdef CYGSEM_HAL_COMMON_INTERRUPTS_ALLOW_NESTING

#ifdef CYGPKG_HAL_POWERPC_MPC8xx
        # The CPM controller allows nested interrupts. However,
        # it sits on the back of the SIU controller which has no
        # HW support for this. In effect, SW masking of lower
        # priority IRQs in the SIU would be required for this to work.
#endif

#endif

        lwz     r3,CYGARC_PPCREG_VECTOR(r14)    # retrieve decoded vector #

        lwi     r6,hal_interrupt_handlers       # get interrupt handler table
        lwzx    r6,r6,r15                       # load routine pointer

        lwi     r4,hal_interrupt_data           # get interrupt data table
        lwzx    r4,r4,r15                       # load data pointer
                                                # R4 = data argument
        mr      r5,r14                          # R5 = saved registers        
        
        mtctr   r6                              # put isr address in ctr

        bctrl                                   # branch to ctr reg and link

#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.
        # Since we have arranged for the top of stack location to
        # contain the sp we need to go back to here, just pop it off
        # and put it in SP.

        
        lwz     sp,CYGARC_PPC_STACK_FRAME_SIZE*2(sp) # sp = *sp

        subi    sp,sp,CYGARC_PPC_STACK_FRAME_SIZE # make a null frame

        li      r0,0                            # R0 = 0
        stw     r0,0(sp)                        # backchain = 0
        stw     r0,8(sp)                        # return pc = 0
        
        stwu    sp,-CYGARC_PPC_STACK_FRAME_SIZE(sp) # create new stack frame
                                                # where C code can save LR
#endif  

        # On return r3 bit 1 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.

        # Note that r14 and r15 are defined to be preserved across
        # calls by the calling convention, so they still contain
        # the register dump and the vector number respectively.

        lwi     r4,hal_interrupt_objects        # get interrupt object table
        lwzx    r4,r4,r15                       # load object pointer
        mr      r5,r14                          # arg3 = saved register dump

        .extern interrupt_end
        bl      interrupt_end                   # call into C to finish off 

restore_state:  
        # All done, restore CPU state and continue

        # retrieve CPU state pointer
        addi    sp,sp,CYGARC_PPC_STACK_FRAME_SIZE*2

        # Restore FPU registers
        hal_fpu_load sp

        # Restore variant registers
        hal_variant_load sp

        # get sprs we want to restore
        # stuff some of them into the CPU
        lwz     r3,CYGARC_PPCREG_XER(sp)
        lwz     r4,CYGARC_PPCREG_LR(sp)
        lwz     r5,CYGARC_PPCREG_CTR(sp)
        mtxer   r3
        mtlr    r4
        mtctr   r5

        # Restore registers used in vsr (r14+r15)
        lwz     r14,CYGARC_PPCREG_REGS+14*4(r1)
        lwz     r15,CYGARC_PPCREG_REGS+15*4(r1)

        # restore registers r6..r12/r31
        .set    _reg,6
        .rept   MAX_SAVE_REG+1-6
        lwz     _reg,(CYGARC_PPCREG_REGS+_reg*4)(sp)
        .set    _reg,_reg+1
        .endr

        hal_cpu_int_disable
        
        # restore R0 and R2
        lwz     r0,CYGARC_PPCREG_REGS+0*4(sp)
        lwz     r2,CYGARC_PPCREG_REGS+2*4(sp)

        # Here all the registers are loaded except
        # sp = HAL_SavedRegisters
        # r3 = ccr
        # r4 = srr0 = pc
        # r5 = srr1 = msr
        #
        # We have to disable interrupts while srr0 and
        # srr1 are loaded, since another interrupt will
        # destroy them.

        lwz     r3,CYGARC_PPCREG_CR(sp)
        lwz     r4,CYGARC_PPCREG_PC(sp)
        lwz     r5,CYGARC_PPCREG_MSR(sp)
        mtcr    r3                      # set ccr
        mtsrr0  r4                      # load old pc
        mtsrr1  r5                      # load old msr
        
#ifdef CYGDBG_HAL_POWERPC_FRAME_WALLS
        # Mark this frame as (almost) dead.
        lwi     r3,0xDDDDDDD0
        stw     r3,CYGARC_PPCREG_WALL_HEAD(sp)
        lwi     r3,0xDDDDDDD1
        stw     r3,CYGARC_PPCREG_WALL_TAIL(sp)
#endif

        lwz     r3,CYGARC_PPCREG_REGS+3*4(sp)  # load r3 value
        lwz     r4,CYGARC_PPCREG_REGS+4*4(sp)  # load r4 value
        lwz     r5,CYGARC_PPCREG_REGS+5*4(sp)  # load r5 value
        lwz     sp,CYGARC_PPCREG_REGS+1*4(sp)  # restore sp
                
        sync                            # settle things down
        isync   
        rfi                             # and return

#if defined(CYGHWR_HAL_POWERPC_BOOK_E)                        
##---------------------------------------------------------------------------
## Critical interrupt handling code.
##
## Book E processors save the old PC and MSR in CSRR0/1 for certain
## exceptions. This VSR saves the values from these registers before
## branching to the common VSR code.

        .globl cyg_hal_default_interrupt_vsr
cyg_hal_default_critical_interrupt_vsr:

        # We come here with all register containing their
        # pre-exception values except:
        # R3    = ls 16 bits of vector address
        # R4    = saved CR
        # R5    = saved LR
        # LR    = VSR address
        # SPRG1 = old R3
        # SPRG2 = old R4
        # SPRG3 = old R5
        # CSRR0 = old PC
        # CSRR1 = old MSR


        subi    sp,sp,CYGARC_PPC_EXCEPTION_DECREMENT
                                        # leave space for registers and
                                        # a safety margin
              
        stw     r3,CYGARC_PPCREG_VECTOR(sp)    # stash vector
        stw     r4,CYGARC_PPCREG_CR(sp)        # stash CR
        stw     r5,CYGARC_PPCREG_LR(sp)        # stash LR

        # Save exception PC and MSR
        mfspr   r3,58                          # Get CSSR0
        stw     r3,CYGARC_PPCREG_PC(sp)
        mfspr   r3,59                          # Get CSSR1
        stw     r3,CYGARC_PPCREG_MSR(sp)

        b cyg_hal_default_interrupt_vsr_common
        
#endif
        
##-----------------------------------------------------------------------------
## Execute pending DSRs on the interrupt stack with interrupts enabled.
## Note: this can only be called from code running on a thread stack

#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
	.extern cyg_interrupt_call_pending_DSRs

FUNC_START(hal_interrupt_stack_call_pending_DSRs)
        # Change to interrupt stack, save state and set up stack for
        # calls to C code.
        mr      r3,sp
        lwi     r4,__interrupt_stack
        subi    r4,r4,24                        # make space on stack
        mr      sp,r4
        stw     r3,12(sp)                       # save old sp
        mfmsr   r3
        stw     r3,16(sp)                       # save old MSR
        mflr    r3
        stw     r3,20(sp)                       # save old LR

        li      r0,0
        stw     r0,0(sp)                        # clear back chain
        stw     r0,8(sp)                        # zero return pc

        hal_cpu_int_enable

        # Call into kernel which will execute DSRs
        stwu    sp,-CYGARC_PPC_STACK_FRAME_SIZE(sp)
        bl      cyg_interrupt_call_pending_DSRs
        addi    sp,sp,CYGARC_PPC_STACK_FRAME_SIZE

        lwz     r3,20(sp)                       # restore LR
        mtlr    r3
        lwz     r5,12(sp)                       # get SP from saved state
        lwz     r3,16(sp)                       # restore interrupt setting
        hal_cpu_int_merge r3,r4

        mr      sp,r5                           # restore stack pointer
        blr                                     # and return to caller
#endif		

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

	.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:
        
        .long   0,0,0,0,0,0,0,0 

#---------------------------------------------------------------------------
# end of vectors.S
