##=============================================================================
##
##	vectors.S
##
##	MIPS 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,1999 Cygnus Solutions.  All Rights Reserved.
# -------------------------------------------
#
#####COPYRIGHTEND####
##=============================================================================
#######DESCRIPTIONBEGIN####
##
## Author(s): 	nickg
## Contributors:	nickg
## Date:	1998-02-04
## Purpose:	MIPS 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/system.h>	
#include <pkgconf/hal.h>

#ifdef CYGPKG_KERNEL
# include <pkgconf/kernel.h>	
#endif
			
#include <cyg/hal/arch.inc>	
			
	.extern cyg_instrument

##-----------------------------------------------------------------------------
## Hardware supplied vectors
	
	.set	noreorder
	.set	noat
 
	.section ".reset_vector","ax"

	# Reset vector at 0xBFC00000
	
FUNC_START(reset_vector)
	la	v0,_start		# jump to start
	jr	v0
	nop				# (delay slot)
FUNC_END(reset_vector)
	
	.section ".debug_vector","ax"
		
	# Debug vector at 0xBFC00200
	
FUNC_START(debug_vector)
	la	k0,32
	la	k1,hal_vsr_table	# Get VSR table
	lw	k1,32*4(k1)		# load debug vector
	jr	k1			# go there
	nop				# (delay slot)
FUNC_END(debug_vector)

	.section ".other_vector","ax"

	# Common vector at 0x80000080 or 0xBFC00180
	
FUNC_START(other_vector)
	mfc0	k0,cause		# K0 = exception cause
	nop
	andi	k0,k0,0x7F		# isolate exception code
	la	k1,hal_vsr_table	# address of VSR table
	add	k1,k1,k0		# offset of VSR entry
	lw	k1,0(k1)		# k1 = pointer to VSR
	jr	k1			# go there
	nop				# (delay slot)
FUNC_END(other_vector)
	
	.section ".utlb_vector","ax"

FUNC_START(utlb_vector)
	la	k0,33
	la	k1,hal_vsr_table	# Get VSR table
	lw	k1,33*4(k1)		# load utlb vector
	jr	k1			# go there
	nop				# (delay slot)
FUNC_END(utlb_vector)

##-----------------------------------------------------------------------------
## Startup code
	
	.text
	
FUNC_START(_start)

	# Initialize hardware

	hal_cpu_init
	hal_mmu_init
	hal_fpu_init
	hal_memc_init
	hal_intc_init
	hal_cache_init
	hal_diag_init
	hal_timer_init
		
	# Load Global Pointer register.
	la	gp,_gp

	# load initial stack pointer
	la	a0,__interrupt_stack
	move	sp,a0

	hal_mon_init
		
#ifdef CYG_HAL_STARTUP_ROM
	# Copy data from ROM to RAM

	.extern	hal_copy_data
	jal	hal_copy_data
	nop

#endif
		
	# Zero BSS

	.extern hal_zero_bss
	jal	hal_zero_bss
	nop

	# Cal variant and platform HAL
	# initialization routines.

	.extern	hal_variant_init
	jal	hal_variant_init
	nop

	.extern	hal_platform_init
	jal	hal_platform_init
	nop
	
	# Call constructors
	.extern cyg_hal_invoke_constructors
	jal     cyg_hal_invoke_constructors
	nop

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        .extern initialize_stub
        jal     initialize_stub
        nop
#endif

	# Call cyg_start	
	
	.extern	cyg_start
	jal	cyg_start
	nop

9:	j	9b		# Loop if we return
	nop			# (delay slot)
	
FUNC_END(_start	)

##-----------------------------------------------------------------------------
## Default exception VSR.
## Saves machine state and calls external handling code.
	
FUNC_START(__default_exception_vsr)

	# We enter here with all of the CPU state still
	# in its registers except:
	# K0 = vector index
	# K1 = address of this function

		
	move	k1,sp			# K1 = original SP
	
#ifdef CYG_HAL_ROM_MONITOR
	# Switch to interrupt stack to handle exception
	la	sp,__interrupt_stack
#endif
			
	addi	sp,sp,-mips_exception_decrement
				# space for registers + safety margin

	mfc0	k0,cause
	andi	k0,k0,0x7F		# isolate exception code	
	sw	k0,mipsreg_vector(sp)	# store vector

	.set	noat
	# store GPRs
	sw	$0,(mipsreg_regs+0*4)(sp)
	sw	$1,(mipsreg_regs+1*4)(sp)
	sw	$2,(mipsreg_regs+2*4)(sp)
	sw	$3,(mipsreg_regs+3*4)(sp)
	sw	$4,(mipsreg_regs+4*4)(sp)
	sw	$5,(mipsreg_regs+5*4)(sp)
	sw	$6,(mipsreg_regs+6*4)(sp)
	sw	$7,(mipsreg_regs+7*4)(sp)
	sw	$8,(mipsreg_regs+8*4)(sp)
	sw	$9,(mipsreg_regs+9*4)(sp)
	sw	$10,(mipsreg_regs+10*4)(sp)
	sw	$11,(mipsreg_regs+11*4)(sp)
	sw	$12,(mipsreg_regs+12*4)(sp)
	sw	$13,(mipsreg_regs+13*4)(sp)
	sw	$14,(mipsreg_regs+14*4)(sp)
	sw	$15,(mipsreg_regs+15*4)(sp)
	sw	$16,(mipsreg_regs+16*4)(sp)
	sw	$17,(mipsreg_regs+17*4)(sp)
	sw	$18,(mipsreg_regs+18*4)(sp)
	sw	$19,(mipsreg_regs+19*4)(sp)
	sw	$20,(mipsreg_regs+20*4)(sp)
	sw	$21,(mipsreg_regs+21*4)(sp)
	sw	$22,(mipsreg_regs+22*4)(sp)
	sw	$23,(mipsreg_regs+23*4)(sp)
	sw	$24,(mipsreg_regs+24*4)(sp)
	sw	$25,(mipsreg_regs+25*4)(sp)
#	sw	$26,(mipsreg_regs+26*4)(sp)	# == K0
#	sw	$27,(mipsreg_regs+27*4)(sp)	# == K1
	sw	$28,(mipsreg_regs+28*4)(sp)	# == GP
#	sw	$29,(mipsreg_regs+29*4)(sp)	# == SP
	sw	$30,(mipsreg_regs+30*4)(sp)	# == FP
	sw	$31,(mipsreg_regs+31*4)(sp)	# == RA

	mfhi	a0
	mflo	a1
	sw	a0,mipsreg_hi(sp)
	sw	a1,mipsreg_lo(sp)

	# K1 contains original SP
	sw	k1,(mipsreg_regs+29*4)(sp)	# store in reg dump	
	
	# save remaining machine state registers	
	mfc0	t0,cause
	mfc0	t1,status
	mfc0	t2,cache
	mfc0	t3,badvr
	mfc0	t4,config
	mfc0	t5,prid
	mfc0	t6,epc
	
	sw	t0,mipsreg_cause(sp)
	sw	t1,mipsreg_sr(sp)
	sw	t2,mipsreg_cache(sp)
	sw	t3,mipsreg_badvr(sp)
	sw	t4,mipsreg_config(sp)
	sw	t5,mipsreg_prid(sp)
	sw	t6,mipsreg_pc(sp)

	hal_fpu_save sp
	
	# The machine state is now all saved on the stack.

	hal_diag_excpt_start
	
	# Load Global Pointer register.
	la	gp,_gp
	
	move	s0,sp				# save pointer to saved state

	addi	sp,sp,-mips_stack_frame_size	# make a null frame	

	# Need to set up back pointers etc. ???

	la	ra,restore_state		# load return address

	hal_cpu_except_enable			# reenable exceptions
		
	.extern	exception_handler
	j	exception_handler		# call C code
	move	a0,s0				# arg0 = register dump (delay slot)

	# When the exception handler returns, it will
	# go back to restore_state, below.
			
FUNC_END(__default_exception_vsr)
	
##------------------------------------------------------------------------------
## Default interrupt VSR.
## Saves machine state and calls appropriate ISR. When done, calls
## interrupt_end() to finish up and possibly reschedule.	

FUNC_START(__default_interrupt_vsr)

	
	# We enter here with all of the CPU state still
	# in its registers except:
	# K0 = vector index
	# K1 = address of this function

	move	k1,sp			# K1 = original SP
		
	addi	sp,sp,-mips_exception_decrement
				# space for registers + safety margin

	sw	k0,mipsreg_vector(sp)	# store vector

	# store GPRs
	sw	$0,(mipsreg_regs+0*4)(sp)
	sw	$1,(mipsreg_regs+1*4)(sp)
	sw	$2,(mipsreg_regs+2*4)(sp)
	sw	$3,(mipsreg_regs+3*4)(sp)
	sw	$4,(mipsreg_regs+4*4)(sp)
	sw	$5,(mipsreg_regs+5*4)(sp)
	sw	$6,(mipsreg_regs+6*4)(sp)
	sw	$7,(mipsreg_regs+7*4)(sp)
	sw	$8,(mipsreg_regs+8*4)(sp)
	sw	$9,(mipsreg_regs+9*4)(sp)
	sw	$10,(mipsreg_regs+10*4)(sp)
	sw	$11,(mipsreg_regs+11*4)(sp)
	sw	$12,(mipsreg_regs+12*4)(sp)
	sw	$13,(mipsreg_regs+13*4)(sp)
	sw	$14,(mipsreg_regs+14*4)(sp)
	sw	$15,(mipsreg_regs+15*4)(sp)
	sw	$16,(mipsreg_regs+16*4)(sp)
	sw	$17,(mipsreg_regs+17*4)(sp)
	sw	$18,(mipsreg_regs+18*4)(sp)
	sw	$19,(mipsreg_regs+19*4)(sp)
	sw	$20,(mipsreg_regs+20*4)(sp)
	sw	$21,(mipsreg_regs+21*4)(sp)
	sw	$22,(mipsreg_regs+22*4)(sp)
	sw	$23,(mipsreg_regs+23*4)(sp)
	sw	$24,(mipsreg_regs+24*4)(sp)
	sw	$25,(mipsreg_regs+25*4)(sp)
#	sw	$26,(mipsreg_regs+26*4)(sp)	# == K0
#	sw	$27,(mipsreg_regs+27*4)(sp)	# == K1
	sw	$28,(mipsreg_regs+28*4)(sp)	# == GP
#	sw	$29,(mipsreg_regs+29*4)(sp)	# == SP
	sw	$30,(mipsreg_regs+30*4)(sp)	# == FP
	sw	$31,(mipsreg_regs+31*4)(sp)	# == RA

	mfhi	a0
	mflo	a1
	sw	a0,mipsreg_hi(sp)
	sw	a1,mipsreg_lo(sp)

	# K1 contains original SP
	sw	k1,(mipsreg_regs+29*4)(sp)	# store in reg dump	
		
	mfc0	t1,status
	mfc0	t2,cache
	mfc0	t3,epc
	
	sw	t1,mipsreg_sr(sp)
	sw	t2,mipsreg_cache(sp)
	sw	t3,mipsreg_pc(sp)

	hal_fpu_save sp
		
	# The machine state is now all saved on the stack.

	hal_diag_intr_start

	# Load Global Pointer register.
	la	gp,_gp
	
#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT			
	.extern	cyg_scheduler_sched_lock
	la	v0,cyg_scheduler_sched_lock
	lw	a0,0(v0)
	addi	a0,a0,1
	sw	a0,0(v0)
#endif

	move	s0,sp				# save pointer to saved state
	
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK	

	la	a0,__interrupt_stack		# a0 = stack top
	la	a1,__interrupt_stack_base	# a1 = stack base
	sub	a3,sp,a1			# a3 = sp - base
	bltz	a3,1f				# if < 0 - not on istack
	nop					# delay slot
	sub	t0,a0,sp			# t0 = top - sp
	bgtz	t0,8f				# if > 0 - already on istack
	nop					# delay slot
1:	
	move	sp,a0				# switch to istack
8:
	addi	sp,sp,-4			# space for old SP
	sw	s0,0(sp)			# save old SP on stack
	
#endif
					
	subu	sp,sp,mips_stack_frame_size	# make a null frame	

	# Need to set up back pointers etc. ???

	# Decode external interrupt via interrupt controller

	hal_intc_decode	s2
	
#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN
	move	s1,zero				# Just vector zero is supported
#else			
	move	s1,s2				# Vector == interrupt number
#endif

	# Here s1 is the number of the vector to be called and s2 is
	# the number of the interrupt being serviced. 
	
#if defined(CYGPKG_KERNEL_INSTRUMENT) && defined(CYGDBG_KERNEL_INSTRUMENT_INTR)

	# Call cyg_instrument to record that this interrupt is being raised.
		
	li	a0,0x0301			# a0 = type = INTR,RAISE
	move	a1,s1				# a1 = vector number
	jal	cyg_instrument			# call instrument function
	xor	a2,a2,a2			# arg2 = 0 (delay slot)
#endif

	sll	s1,s1,2				# s1 = byte offset of vector
	
	la	t2,hal_interrupt_handlers	# handler table
	add	t2,t2,s1			# address of ISR ptr
	lw	t2,0(t2)			# ISR pointer

	la	a1,hal_interrupt_data		# data table
	add	a1,a1,s1			# address of data ptr
	lw	a1,0(a1)			# Data pointer

	move	a0,s2				# pass interrupt number

	move	a2,s0				# arg3 = saved register dump
		
	jalr	t2				# call ISR via t2
	nop					# (delay slot)

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

	
	lw	sp,mips_stack_frame_size(sp)	# sp = *sp
	subu	sp,sp,mips_stack_frame_size	# make a null frame		
#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 v0 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 s0, s1 and s2 are defined to be preserved across
	# calls by the calling convention, so they still contain
	# the register dump, the vector offset and the interrupt number
	# respectively.

	move	a0,v0				# put ISR result in arg0
		
	la	a1,hal_interrupt_objects	# interrupt object table
	add	a1,a1,s1			# address of object ptr
	lw	a1,0(a1)			# a1 = object ptr

	move	a2,s0				# arg3 = saved register dump
	
	.extern	interrupt_end
	jal	interrupt_end			# call into C to finish off 
	nop					# (delay slot)

#endif
				
restore_state:	

	# All done, restore CPU state and continue

	addi	sp,sp,mips_stack_frame_size	# retrieve CPU state ptr

	# Disable interrupts again while we restore state. 

	hal_diag_restore

	hal_fpu_load sp
	
	lw	t0,mipsreg_cache(sp)
	lw	t1,mipsreg_hi(sp)
	lw	t2,mipsreg_lo(sp)

	mtc0	t0,cache
	mthi	t1
	mtlo	t2

		# load GPRs
#	lw	$0,(mipsreg_regs+0*4)(sp)
	lw	$1,(mipsreg_regs+1*4)(sp)
	lw	$2,(mipsreg_regs+2*4)(sp)
	lw	$3,(mipsreg_regs+3*4)(sp)
	lw	$4,(mipsreg_regs+4*4)(sp)
	lw	$5,(mipsreg_regs+5*4)(sp)
	lw	$6,(mipsreg_regs+6*4)(sp)
	lw	$7,(mipsreg_regs+7*4)(sp)
	lw	$8,(mipsreg_regs+8*4)(sp)
	lw	$9,(mipsreg_regs+9*4)(sp)
	lw	$10,(mipsreg_regs+10*4)(sp)
	lw	$11,(mipsreg_regs+11*4)(sp)
	lw	$12,(mipsreg_regs+12*4)(sp)
	lw	$13,(mipsreg_regs+13*4)(sp)
	lw	$14,(mipsreg_regs+14*4)(sp)
	lw	$15,(mipsreg_regs+15*4)(sp)
	lw	$16,(mipsreg_regs+16*4)(sp)
	lw	$17,(mipsreg_regs+17*4)(sp)
	lw	$18,(mipsreg_regs+18*4)(sp)
	lw	$19,(mipsreg_regs+19*4)(sp)
	lw	$20,(mipsreg_regs+20*4)(sp)
	lw	$21,(mipsreg_regs+21*4)(sp)
	lw	$22,(mipsreg_regs+22*4)(sp)
	lw	$23,(mipsreg_regs+23*4)(sp)
	lw	$24,(mipsreg_regs+24*4)(sp)
	lw	$25,(mipsreg_regs+25*4)(sp)
#	lw	$26,(mipsreg_regs+26*4)(sp)	# == K0
#	lw	$27,(mipsreg_regs+27*4)(sp)	# == K1
	lw	$28,(mipsreg_regs+28*4)(sp)	# == GP
#	lw	$29,(mipsreg_regs+29*4)(sp)	# == SP
	lw	$30,(mipsreg_regs+30*4)(sp)	# == FP
	lw	$31,(mipsreg_regs+31*4)(sp)	# == RA

	lw	k0,mipsreg_pc(sp)		# K0 = return PC
	lw	k1,mipsreg_sr(sp)		# K1 = saved SR
	lw	sp,(mipsreg_regs+29*4)(sp)	# load SP

	# Invoke CPU specific mechanism for returning from this
	# exception
	
	hal_cpu_eret k0,k1
		
FUNC_END(__default_interrupt_vsr)

	hal_intc_decode_data

##-----------------------------------------------------------------------------
## 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)
	mfc0	t0,status			# get status register value
	la	v0,__interrupt_stack		# v0 = interrupt stack
	move	v1,sp				# v1 = original stack ptr
	move	sp,v0				# sp = interrupt stack
	addi	sp,sp,-32			# make a null frame
	sw	v1,16(sp)			# save old sp
	sw	ra,20(sp)			# save old ra
	sw	t0,24(sp)			# save old sr

	hal_cpu_int_enable

	jal	cyg_interrupt_call_pending_DSRs	# call back to kernel
	nop

	lw	a0,24(sp)			# get status reg

	hal_cpu_int_merge a0			# merge with current SR
	
	lw	ra,20(sp)			# restore ra
	lw	sp,16(sp)			# restore sp

	jr	ra				# go back
	nop					# delay slot
	
FUNC_END(hal_interrupt_stack_call_pending_DSRs)	
#endif		


// Short circuit in case any code tries to use "__gccmain()"
FUNC_START(__gccmain)
	jr	ra
FUNC_END(__gccmain)

	
##-----------------------------------------------------------------------------
## Interrupt Stack.
## Used during intialization and for executing ISRs.
	
	.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	
	
##-----------------------------------------------------------------------------
## VSR table.
## The main interrupt code indirects through here to find the VSR
## to execute for each architecture defined interrupt.
## This is only used for simulated targets, on real targets a fixed location VSR
## table is now allocated at 0x80000100.

#ifndef CYG_HAL_MIPS_VSR_TABLE_DEFINED
	
##	.section ".vsr_table","a"
	
	.data
			
	.globl	hal_vsr_table

hal_vsr_table:	
	.long	__default_interrupt_vsr	
	.rept	63
	.long	__default_exception_vsr	
	.endr

#endif	
	
#------------------------------------------------------------------------------
# Interrupt vector tables.
# These tables contain the isr, data and object pointers used to deliver
# interrupts to user code.
# hal_interrupt_level contains the interrupt level set by 
# HAL_INTERRUPT_CONFIGURE().
# This is a default set that provide support only for the 6 external
# interrupts in the status/cause registers. Platforms or boards are expected
# to define their own versions of these if they have their own interrupt mappings.

#ifndef CYG_HAL_MIPS_ISR_TABLES_DEFINED
	
	.extern hal_default_isr
	
	.data

	.globl	hal_interrupt_handlers
hal_interrupt_handlers:
	.long	hal_default_isr
	.long	hal_default_isr
	.long	hal_default_isr
	.long	hal_default_isr
	.long	hal_default_isr
	.long	hal_default_isr


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

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

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


