##=============================================================================
##
##	vectors.S
##
##	MIPS exception vectors
##
##=============================================================================
#####COPYRIGHTBEGIN####
#                                                                          
# -------------------------------------------                              
# The contents of this file are subject to the Red Hat eCos Public License 
# Version 1.1 (the "License"); you may not use this file except in         
# compliance with the License.  You may obtain a copy of the License at    
# http://www.redhat.com/                                                   
#                                                                          
# Software distributed under the License is distributed on an "AS IS"      
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
# License for the specific language governing rights and limitations under 
# the License.                                                             
#                                                                          
# The Original Code is eCos - Embedded Configurable Operating System,      
# released September 30, 1998.                                             
#                                                                          
# The Initial Developer of the Original Code is Red Hat.                   
# Portions created by Red Hat are                                          
# Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
# All Rights Reserved.                                                     
# -------------------------------------------                              
#                                                                          
#####COPYRIGHTEND####
##=============================================================================
#######DESCRIPTIONBEGIN####
##
## Author(s): 	nickg
## 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
 
	.section ".reset_vector","ax"

	# Reset vector at 0xBFC00000
	
FUNC_START(reset_vector)

#ifndef CYG_HAL_STARTUP_RAM
	# Decide whether this is an NMI, cold or warm boot.

	mfc0	k0,status		# get status reg
	lui	k1,0x0008		# isolate NMI bit
	and	k1,k1,k0		
	beqz	k1,1f			# skip if zero
	nop

	lar	k1,__nmi_entry		# jump to ROM nmi code
	jalr	k1
	nop
1:
	lui	k1,0x0010		# isolate soft reset bit
	and	k1,k1,k0		
	beqz	k1,2f			# skip if zero
	nop

	lar	k1,__warm_start		# jump to ROM warm_start code
	jr	k1
	nop
2:
	la	k0,INITIAL_CONFIG0	# Set up config0 register
	mtc0	k0,config0		# to disable cache
#endif	
	lar	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_diag_init
	hal_mmu_init
	hal_fpu_init
	hal_memc_init
	hal_intc_init
	hal_cache_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

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

#if defined(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:	b	9b		# Loop if we return
	nop			# (delay slot)
	
FUNC_END(_start	)


##-----------------------------------------------------------------------------
		
FUNC_START(__warm_start)

	## The following is debug code left in here for now in case it
	## proves useful in the near future.	
#if 0
	move	s0,t0
	move	s1,a1
	
#	hal_diag_init

	hal_diag_writec '$'
	mfc0	a0,$30			# get ErrorEPC
	lar	k0,hal_diag_ai_write_hex8
	jalr	k0
	nop
	hal_diag_writec '-'	
	move	a0,s0
	jalr	k0
	nop
	hal_diag_writec '-'	
	move	a0,s1
	jalr	k0
	nop
1:	
	b	1b
	nop
#endif
	
	# At present we treat this like an NMI. 
	b	__nmi_entry
	nop
	
FUNC_END(__warm_start)		

##-----------------------------------------------------------------------------
	
FUNC_START(__nmi_entry)		

	# Clear exception state
	hal_cpu_except_enable
	
	# Move the ErrorEPC register to the EPC register so that the
	# default exception handler saves the right PC value.
	mfc0	k0,$30
	nop; nop; nop;
	mtc0	k0,epc
	nop; nop; nop;	

#if (INITIAL_SR & 0x00400000) == 0
	# Taking this exception will have set the BEV bit to 1.
	# If we normally run with it zero, we must clear it here.
	mfc0	k0,status	
	la	k1,0xFFBFFFFF
	and	k0,k0,k1
	mtc0	k0,status
#endif	

	la	k0,34*4
	la	k1,hal_vsr_table	# Get VSR table
	lw	k1,34*4(k1)		# load NMI vector
	jr	k1			# go there
	nop				# (delay slot)
	
FUNC_END(__nmi_entry)

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

	sw	k0,mipsreg_vector(sp)	# store vector

	# store GPRs
	.set	noat
	sgpr	0,sp
	sgpr	1,sp
	sgpr	2,sp
	sgpr	3,sp
	sgpr	4,sp
	sgpr	5,sp
	sgpr	6,sp
	sgpr	7,sp
	sgpr	8,sp
	sgpr	9,sp
	sgpr	10,sp
	sgpr	11,sp
	sgpr	12,sp
	sgpr	13,sp
	sgpr	14,sp
	sgpr	15,sp
	sgpr	16,sp
	sgpr	17,sp
	sgpr	18,sp
	sgpr	19,sp
	sgpr	20,sp
	sgpr	21,sp
	sgpr	22,sp
	sgpr	23,sp
	sgpr	24,sp
	sgpr	25,sp
#	sgpr	26,sp	# == K0
#	sgpr	27,sp	# == K1
	sgpr	28,sp	# == GP
#	sgpr	29,sp	# == SP
	sgpr	30,sp	# == FP
	sgpr	31,sp	# == RA
	.set	at
	
	mfhi	a0
	mflo	a1
	shi	a0,sp
	slo	a1,sp

	# K1 contains original SP
	ssp	k1,sp			# store in reg dump	
	
	# save remaining machine state registers	
	mfc0	t0,cause
	mfc0	t1,status
	mfc0	t2,cachectrl
	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_cachectrl(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	cyg_hal_exception_handler
	j	cyg_hal_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
	.set	noat
	sgpr	0,sp
	sgpr	1,sp
	sgpr	2,sp
	sgpr	3,sp
	sgpr	4,sp
	sgpr	5,sp
	sgpr	6,sp
	sgpr	7,sp
	sgpr	8,sp
	sgpr	9,sp
	sgpr	10,sp
	sgpr	11,sp
	sgpr	12,sp
	sgpr	13,sp
	sgpr	14,sp
	sgpr	15,sp
	sgpr	16,sp
	sgpr	17,sp
	sgpr	18,sp
	sgpr	19,sp
	sgpr	20,sp
	sgpr	21,sp
	sgpr	22,sp
	sgpr	23,sp
	sgpr	24,sp
	sgpr	25,sp
#	sgpr	26,sp	# == K0
#	sgpr	27,sp	# == K1
	sgpr	28,sp	# == GP
#	sgpr	29,sp	# == SP
	sgpr	30,sp	# == FP
	sgpr	31,sp	# == RA
	.set	at
	
	mfhi	a0
	mflo	a1
	shi	a0,sp
	slo	a1,sp

	# K1 contains original SP
	ssp	k1,sp			# store in reg dump	
		
	mfc0	t1,status
	mfc0	t2,cachectrl
	mfc0	t3,epc
	
	sw	t1,mipsreg_sr(sp)
	sw	t2,mipsreg_cachectrl(sp)
	sw	t3,mipsreg_pc(sp)

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

	# 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,-8			# space for old SP 
						# (8 to keep dword alignment!)
	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

	# Here, s2 contains the number of the interrupt being serviced,
	# we need to derive from that the vector number to call in the ISR
	# table.
	
	hal_intc_translate s2,s1
	
	# Here s1 is the number of the vector to be called and s2 is
	# the number of the interrupt being serviced. 

	hal_diag_intr_start
		
#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
	 move	a2,s2				# a2 = interrupt number
#endif

#if defined(CYGDBG_HAL_MIPS_DEBUG_GDB_CTRLC_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
	la	v0,hal_saved_interrupt_state
	sw	s0,0(v0)
	
#endif
	
	sll	s1,s1,2				# s1 = byte offset of vector

	hal_cpu_except_enable			# reenable exceptions
			
	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

	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	s2,v0
		
	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 
	 move	a0,v0				# put ISR result in arg0
	
	move	v0,s2				# return value from isr
#endif
				
restore_state:
	move	k0,v0

	# 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_cachectrl(sp)
	lhi	t1,sp
	llo	t2,sp

	mtc0	t0,cachectrl
	mthi	t1
	mtlo	t2

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


#if defined(CYGSEM_HAL_USE_ROM_MONITOR_CygMon)

        # If we have a Cygmon that wants to listen to network interrupts, then
        # the return code from the earlier call to hal_default_isr() will
        # have been negative to indicate this. So we jump into Cygmon here
        # because Cygmon requires the processor state to be the same as when
        # the interrupt was taken, but with k0 as the exception number.
        
	bgez	k0,1f
	nop
	# Check for new cygmon
	sw	k0,(mipsreg_regs+26*4)(sp)	# save k0
	la	k1,0x80000100 + 41*4		# New cygmon "magic" id
	lw	k1,0(k1)
	lui	k0,0x55aa
	ori	k0,0x4321
	bne	k0,k1,1f
			
	# Need to let cygmon handle this
	la	k1,0x80000100 + 39*4		# stub entry vector
	lw	k0,(mipsreg_regs+26*4)(sp)	# restore k0
	lw	k1,0(k1)
	lw	sp,(mipsreg_regs+29*4)(sp)	# restore SP
	sll	k0,1				# clear bit 31.
	jr	k1
        srl	k0,1
    1:
#endif
	lw	k0,mipsreg_pc(sp)		# K0 = return PC
	lw	k1,mipsreg_sr(sp)		# K1 = saved SR
	lsp	sp,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
	nop
FUNC_END(__gccmain)
	
##-----------------------------------------------------------------------------
## Interrupt Stack.
## Used during intialization and for executing ISRs.
	
	.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	
	
##-----------------------------------------------------------------------------
## 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


