view packages/hal/powerpc/arch/current/src/vectors.S @ 0:3111d98ba7b3 ecos-v1_1-release

Initial commit of eCos version 1.1
author jlarmour
date Tue, 11 May 1999 11:16:07 +0000
parents
children 443894e2e912
line wrap: on
line source

##=============================================================================
##
##      vectors.S
##
##      PowerPC exception vectors
##
##=============================================================================
#####COPYRIGHTBEGIN####
#
# -------------------------------------------
# The contents of this file are subject to the Cygnus eCos Public License
# Version 1.0 (the "License"); you may not use this file except in
# compliance with the License.  You may obtain a copy of the License at
# http://sourceware.cygnus.com/ecos
# 
# 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 Cygnus Operating System, released
# September 30, 1998.
# 
# The Initial Developer of the Original Code is Cygnus.  Portions created
# by Cygnus are Copyright (C) 1998 Cygnus Solutions.  All Rights Reserved.
# -------------------------------------------
#
#####COPYRIGHTEND####
##=============================================================================
#######DESCRIPTIONBEGIN####
##
## Author(s):   nickg
## Contributors:        nickg
## Date:        1997-10-16
## 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####
##
##=============================================================================

#include <pkgconf/hal.h>

#ifdef CYGPKG_KERNEL
#include <pkgconf/kernel.h>     // CYGPKG_KERNEL_INSTRUMENT
#endif
        
#include "cyg/hal/ppc.inc"

#define FUNC_START(name)        \
        .type name,@function;   \
        .globl name;            \
name:   

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

        .extern cyg_instrument

        .extern hal_hardware_init

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

#ifdef CYG_HAL_STARTUP_ROM      
#define CYG_MSR 0x00003042
#endif
#ifdef CYG_HAL_STARTUP_RAM      
#define CYG_MSR 0x00003002
#endif

#==============================================================================
# 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.
# Note:  Save some extra convenience registers (13-15)

#ifdef CYGDBG_HAL_COMMON_INTERRUPTS_SAVE_MINIMUM_CONTEXT

#define MAX_SAVE_REG    15

#else

#define MAX_SAVE_REG    31
        
#endif                  
        
#==============================================================================
# Start by defining the exceptions vectors that must be placed at
# locations 0xFFF00000 and 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. In RAM loaded configurations, this code may
# be placed at 0x00000000 during loading.
        
        .section ".vectors","ax"

#------------------------------------------------------------------------------
# Unused first vector.

rom_vectors:    
        lwi     r3,_start
        mtlr    r3
        blr
                
        .byte   0x11
        .byte   0x22
        .byte   0x33
        .byte   0x44
        
#------------------------------------------------------------------------------
# Reset vector. 

        .p2align 8
        .globl  reset_vector    

reset_vector:
        lwi     r3,_start
        mtlr    r3
        blr
        
#------------------------------------------------------------------------------
# Macro for generating an exception vector service routine
        
        .macro  exception_vector name
        .p2align 8
        .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@L          # load low half of vector address
        srwi    r5,r5,6                         # shift right by 6      
        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@L          # reload low half of vector address     
        blr                                     # go to common code
        .endm
        
#------------------------------------------------------------------------------
# Define the exception vectors.

        # These are the architecture defined vectors that
        # are always present.
                
        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

        # There are some additional vectors defined
        # on various implementations.

#ifdef CYG_HAL_POWERPC_MP860
                
        # MP860 vectors

        exception_vector        software_emu
        exception_vector        instruction_tlb_miss
        exception_vector        data_tlb_miss
        exception_vector        instruction_tlb_error
        exception_vector        data_tlb_error
        exception_vector        reserved_01500
        exception_vector        reserved_01600
        exception_vector        reserved_01700
        exception_vector        reserved_01800
        exception_vector        reserved_01900
        exception_vector        reserved_01A00
        exception_vector        reserved_01B00
        exception_vector        data_breakpoint
        exception_vector        instruction_breakpoint
        exception_vector        peripheral_breakpoint
        exception_vector        NMI_port

rom_vectors_end:        
#endif

#==============================================================================
# Real startup code. We jump here from the various reset vectors to set up the
# world.
        
        .text   
        .globl  _start
        
_start: 
        # Set up MSR
        lwi     r3,CYG_MSR
        mtmsr   r3

#if defined(CYG_HAL_POWERPC_MP860) && !defined(CYG_HAL_POWERPC_SIM)
        # Disable special 860 "development support" which suppresses
        # trace exceptions. The CPU seems to hang, not executing from
        # offset 0x1e00(?) as expected.
        li      r3,0
        mtspr   der, r3
#endif

        # Set up global offset table
        lwi     r2,_GLOBAL_OFFSET_TABLE_

        # set up time base register to zero
        xor     r3,r3,r3
        mtspr   284,r3
        xor     r4,r4,r4
        mtspr   285,r4

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

        # Call platform specific hardware initialization
        bl      hal_hardware_init
        
        # optional:     initialize BAT registers

#ifdef 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
        
1:
        lwz     r0,0(r3)                # get word from ROM
        stw     r0,0(r4)                # store in RAM
        addi    r3,r3,4                 # increment by 1 word
        addi    r4,r4,4                 # increment by 1 word
        cmplw   r4,r5                   # compare
        blt     1b                      # loop if not yet done
2:
        
#ifdef CYG_HAL_POWERPC_COPY_VECTORS
        # Copy RAM exception vectors from ROM to RAM    
        lwi     r3,rom_vectors          # r3 = rom start
        lwi     r4,0                    # r4 = ram start
        lwi     r5,rom_vectors_end      # r5 = rom end

        cmplw   r3,r5                   # skip if no vectors
        beq     2f
        
1:
        lwz     r0,0(r3)                # get word from ROM
        stw     r0,0(r4)                # store in RAM
        addi    r3,r3,4                 # increment by 1 word
        addi    r4,r4,4                 # increment by 1 word
        cmplw   r3,r5                   # compare
        blt     1b                      # loop if not yet done
2:
#endif
        
#endif
        
        # clear BSS

        lwi     r3,__bss_start  # r3 = start
        lwi     r4,_end         # r4 = end
        li      r0,0            # r0 = 0
        cmplw   r3,r4           # skip if no bss
        beq     2f
        
1:
        stw     r0,0(r3)        # store zero
        addi    r3,r3,4         # increment by 1 word
        cmplw   r3,r4           # compare
        blt     1b              # loop if not yet done
2:

        # set up stack to call cyg_start
        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,-ppc_stack_frame_size(sp)    # create new stack frame
                                                # for the call to __eabi
                                                # and cyg_start

#ifdef CYG_KERNEL_USE_INIT_PRIORITY

        .extern cyg_hal_invoke_constructors
        bl      cyg_hal_invoke_constructors     # call c++ constructors

#endif

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        .extern set_debug_traps
        bl      set_debug_traps
#endif
                
        .extern cyg_start
        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

        .extern __default_exception_vsr
__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

        subi    r1,r1,ppc_exception_decrement
                                        # leave space for registers and
                                        # a safety margin

        # First, save away some registers
        stw     r3,ppcreg_vector(r1)    # stash vector
        stw     r4,ppcreg_cr(r1)        # stash CR
        stw     r5,ppcreg_lr(r1)        # stash LR
        
        mfspr   r3,sprg1                # save original R3
        stw     r3,ppcreg_regs+3*4(r1)
        mfspr   r4,sprg2                # save original R4
        stw     r4,ppcreg_regs+4*4(r1)
        mfspr   r5,sprg3                # save original R5
        stw     r5,ppcreg_regs+5*4(r1)

        stw     r0,ppcreg_regs(r1)      # save R0
        stw     r2,ppcreg_regs+2*4(r1)  # save R2
        
        mr      r3,r1                   # recreate original R1
        addi    r3,r3,ppc_exception_decrement
        stw     r3,ppcreg_regs+1*4(r1)  # and save it in state
        
        # Save registers 6..31
        .set    _reg,6
        .rept   MAX_SAVE_REG+1-6
        stw     _reg,(ppcreg_regs+_reg*4)(r1)
        .set    _reg,_reg+1
        .endr

        # get remaining CPU registers
        mfxer   r3
        mfctr   r5
        mfdar   r6
        mfdsisr r7
        mfpvr   r8
        mfsrr0  r9
        mfsrr1  r10

        # and store them
        stw     r3,ppcreg_xer(r1)
        stw     r5,ppcreg_ctr(r1)
        stw     r6,ppcreg_dar(r1)
        stw     r7,ppcreg_dsisr(r1)
        stw     r8,ppcreg_pvr(r1)
        stw     r9,ppcreg_pc(r1)
        stw     r10,ppcreg_msr(r1)
        
        # 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,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,-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 exception_handler
        b       exception_handler               # call C code, r3 = registers

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


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

        .extern __default_interrupt_vsr
__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    r1,r1,ppc_exception_decrement
                                        # leave space for registers and
                                        # a safety margin

        stw     r3,ppcreg_vector(r1)    # stash vector
        stw     r4,ppcreg_cr(r1)        # stash CR
        stw     r5,ppcreg_lr(r1)        # stash LR

        mfspr   r3,sprg1                # save original R3
        stw     r3,ppcreg_regs+3*4(r1)
        mfspr   r4,sprg2                # save original R4
        stw     r4,ppcreg_regs+4*4(r1)
        mfspr   r5,sprg3                # save original R5
        stw     r5,ppcreg_regs+5*4(r1)

        stw     r0,ppcreg_regs(r1)      # save R0
        stw     r2,ppcreg_regs+2*4(r1)  # save R2

        mr      r3,r1                   # recreate original R1
        addi    r3,r3,ppc_exception_decrement
        stw     r3,ppcreg_regs+1*4(r1)  # and save it in state
        
        # Save registers 6..31
        .set    _reg,6
        .rept   MAX_SAVE_REG+1-6
        stw     _reg,(ppcreg_regs+_reg*4)(r1)
        .set    _reg,_reg+1
        .endr

        # get remaining CPU registers
        mfxer   r3
        mfctr   r5
        mfsrr0  r6
        mfsrr1  r7
        
        # and store them
        stw     r3,ppcreg_xer(r1)
        stw     r5,ppcreg_ctr(r1)
        stw     r6,ppcreg_pc(r1)
        stw     r7,ppcreg_msr(r1)
                
        # 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 _18Cyg_Scheduler_Base.sched_lock
        lwi     r3,_18Cyg_Scheduler_Base.sched_lock
        lwz     r4,0(r3)
        addi    r4,r4,1
        stw     r4,0(r3)
#endif
        
        mr      r13,sp                          # R13 = 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    r13,-4(sp)                      # save old SP on stack

#endif
        
        subi    sp,sp,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,-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,ppcreg_vector(r13)           # arg1 = vector address
        srwi    r4,r4,8                         # arg1 = vector number
        xor     r5,r5,r5                        # arg2 = 0
        bl      cyg_instrument                  # call instrument function
        
#endif

#ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT
        .extern cyg_hal_gdb_isr
        lwz     r3,ppcreg_vector(r13)           # retrieve vector number
        cmpwi   r3,0x0500                       # if external, call HAL
        bne     1f                              # GDB ISR which will check
        bl      cyg_hal_gdb_isr                 # for serial receive irq.
        cmpwi   r3,0x0000                       # Call ISR proper?
        beq     2f                              # (r3 is 0 when skipping
                                                #  to avoid DSR call)
1:
#endif

        lwz     r3,ppcreg_vector(r13)           # retrieve vector number
        srwi    r15,r3,11                       # isolate bit 11 of vector id
        slwi    r15,r15,2                       # convert to word offset

        srwi    r3,r3,8                         # R3 = vector no argument

        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
        
        mtctr   r6                              # put isr address in ctr

        bctrl                                   # branch to ctr reg and link

#ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT
        # If interrupt was caused by GDB, the ISR call above
        # is skipped by jumping here.
2:
#endif
        
#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,ppc_stack_frame_size*2(sp)   # sp = *sp

        subi    sp,sp,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,-ppc_stack_frame_size(sp)    # create new stack frame
                                                # where C code can save LR
#endif  

#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT

        # We only need to call _interrupt_end() when there is a kernel
        # present to do any tidying up.
        
        # 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 r13 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

        lwz     r6,ppcreg_msr(r13)              # restore msr, including
        mtmsr   r6                              # interrupt enable

        mr      r5,r13                          # arg3 = saved register dump
                        
        .extern interrupt_end
        bl      interrupt_end                   # call into C to finish off 
#endif

restore_state:  
        # All done, restore CPU state and continue

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

        # get sprs we want to restore
        lwz     r3,ppcreg_cr(sp)
        lwz     r4,ppcreg_pc(sp)
        lwz     r5,ppcreg_msr(sp)
        lwz     r6,ppcreg_xer(sp)
        lwz     r7,ppcreg_lr(sp)
        lwz     r8,ppcreg_ctr(sp)


        # stuff some of them into the CPU
        mtxer   r6
        mtlr    r7
        mtctr   r8

        # restore R0 and R2
        lwz     r0,ppcreg_regs(sp)
        lwz     r2,ppcreg_regs+2*4(sp)
        
        # restore registers 6..31
        .set    _reg,6
        .rept   MAX_SAVE_REG+1-6
        lwz     _reg,(ppcreg_regs+_reg*4)(r1)
        .set    _reg,_reg+1
        .endr

        # Here all the registers are loaded except
        # r1 = ppcregs
        # 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.

        mtcr    r3                      # set ccr
        
        lwi     r3,0x00003002           # do rest with ints disabled
        mtmsr   r3
        
        mtsrr0  r4                      # load old pc
        mtsrr1  r5                      # load old msr
        
        lwz     r3,ppcreg_regs+3*4(r1)  # load r3 value
        lwz     r4,ppcreg_regs+4*4(r1)  # load r4 value
        lwz     r5,ppcreg_regs+5*4(r1)  # load r5 value
        lwz     r1,ppcreg_regs+1*4(r1)  # restore r1
                
        sync                            # settle things down
        isync   
        rfi                             # and return

#------------------------------------------------------------------------------
# This table contains a VSR pointer for each defined exception.
# All of these except 5 and 9 point to the __default_exception_vsr
# above, 5 and 9 point to the __default_interrupt_vsr. Entries in
# this table may be changed using Cyg_Interrupt::set_vsr().
        
        .data

        .globl  hal_vsr_table
hal_vsr_table:
        # Architecture defined vectors
        .long   __default_exception_vsr         # reserved
        .long   __default_exception_vsr         # system reset
        .long   __default_exception_vsr         # machine check
        .long   __default_exception_vsr         # data storage
        .long   __default_exception_vsr         # instruction storage

        .long   __default_interrupt_vsr         # external interrupt

        .long   __default_exception_vsr         # alignment
        .long   __default_exception_vsr         # program
        .long   __default_exception_vsr         # floating point unavailable

        .long   __default_interrupt_vsr         # decrementer

        .long   __default_exception_vsr         # reserved 0x0a00
        .long   __default_exception_vsr         # reserved 0x0b00
        .long   __default_exception_vsr         # system call
        .long   __default_exception_vsr         # trace
        .long   __default_exception_vsr         # floating point assist
        .long   __default_exception_vsr         # reserved 0x0f00

#ifdef CYG_HAL_POWERPC_MP860    

        # Implementation defined vectors
        .long   __default_exception_vsr         # software emulation
        .long   __default_exception_vsr         # instruction TLB miss
        .long   __default_exception_vsr         # data TLB miss
        .long   __default_exception_vsr         # instruction TLB error
        .long   __default_exception_vsr         # data TLB error
        .long   __default_exception_vsr         # reserved 0x1500
        .long   __default_exception_vsr         # reserved 0x1600
        .long   __default_exception_vsr         # reserved 0x1700
        .long   __default_exception_vsr         # reserved 0x1800
        .long   __default_exception_vsr         # reserved 0x1900
        .long   __default_exception_vsr         # reserved 0x1a00
        .long   __default_exception_vsr         # reserved 0x1b00
        .long   __default_exception_vsr         # data breakpoint
        .long   __default_exception_vsr         # instruction breakpoint
        .long   __default_exception_vsr         # peripheral breakpoint
        .long   __default_exception_vsr         # non maskable development port
#endif

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

        .extern hal_default_isr

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

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

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

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

	.balign 16
__interrupt_stack_base:
	.rept CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE
	.byte 0
	.endr
	.balign 16
__interrupt_stack:
        
        .long   0,0,0,0,0,0,0,0 

#------------------------------------------------------------------------------

#ifndef CYG_KERNEL_USE_INIT_PRIORITY

# Head of __init function used for static constructors
        .section ".init.head","ax"
        .align 2
FUNC_START(__init)
        stwu 1,-8(1)
        mflr 0
        stw 0,12(1)

# Head of __fini function used for static destructors
        .section ".fini.head","ax"
        .align 2
FUNC_START(__fini)
        stwu 1,-8(1)
        mflr 0
        stw 0,12(1)
                
        
# Tail of __init used for static constructors
        .section ".init.tail","ax"
        lwz 0,12(1)
        mtlr 0
        addi 1,1,8
        blr

# Tail of __fini used for static destructors
        .section ".fini.tail","ax"
        lwz 0,12(1)
        mtlr 0
        addi 1,1,8
        blr
        
#endif
                                
#------------------------------------------------------------------------------
# end of vectors.S