/*===========================================================================
//
//	vec_xvsr.S
//
//	SPARClite vectors: exception vector service routine
//
//===========================================================================
//####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): 	 hmt
// Contributors: hmt
// Date:	 1999-02-20
// Purpose:	 SPARClite vector code
// Description:	 see vectors.S; this is the default vector service routine
//               for exceptions.
//
//####DESCRIPTIONEND####
//
//=========================================================================*/

!-----------------------------------------------------------------------------

	.file	"vec_xvsr.S"

!----------------------------------------------------------------------------

#include <pkgconf/system.h>
#include <pkgconf/hal.h>

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

!------------------------------------------------------------------------

#include <cyg/hal/vectors.h>

#define DELAYS_AFTER_WRPSR_SAME_WINDOW
#define DELAYS_AFTER_WRWIM

!------------------------------------------------------------------------

	.text

!---------------------------------------------------------------------------
! default exception handler VSR, which calls the appropriate ISR after
! interrupt masking - much the same as the interrupt VSR but does not lock
! scheduler or call interrupt_end().

	.global hal_default_exception_vsr
hal_default_exception_vsr:
	! here,locals have been set up as follows:
	! %l0 = psr (with this CWP/window-level in it)
	! %l1 = pc
	! %l2 = npc
	! %l3 = vector number (16-25 for traps)
	! and we are in our own register window, though it is likely that
	! the next one will need to be saved before we can use it:
	! ie. this one is the invalid register window.

	! must establish a safe stack before re-enabling interrupts + traps
	and	%l0, __WINBITS, %l7	! CWP extracted
	mov	1, %l6
	sll	%l6, %l7, %l6		! 1 << CWP
	rd	%wim, %l5
	cmp	%l5, %l6		! are they the same?
	bne	1f			! No, so the stack is OK as is.

	! now do by hand an overflow trap, effectively
	mov	%g1, %l7		! (DELAY SLOT)
	srl	%l5, 1, %l5
	sll	%l6, __WINSIZE-1, %l6
	or	%l6, %l5, %g1		! new WIM in %g1 so we can get it
					! within the save:
	save				! Slip into next window
	mov	%g1, %wim		! Install the new wim
					! (invalidates current window!)
#ifdef DELAYS_AFTER_WRWIM
	nop
	nop
	nop
#endif

	std	%l0, [%sp + 0 * 4]	! save L & I registers
	std	%l2, [%sp + 2 * 4]
	std	%l4, [%sp + 4 * 4]
	std	%l6, [%sp + 6 * 4]

	std	%i0, [%sp + 8 * 4]
	std	%i2, [%sp + 10 * 4]
	std	%i4, [%sp + 12 * 4]
	std	%i6, [%sp + 14 * 4]

	restore				! Go back to trap window.
	mov	%l7, %g1		! Restore %g1

1:	! now save away the regs we must preserve
	sub     %fp, 32 * 4, %sp
	! save a maximal context regardless: see also
	! CYGDBG_HAL_COMMON_INTERRUPTS_SAVE_MINIMUM_CONTEXT
	std	%l0, [%sp + 0 * 4]	! save L & I registers
	std	%l2, [%sp + 2 * 4]
	std	%l4, [%sp + 4 * 4]
	std	%l6, [%sp + 6 * 4]

	std	%i0, [%sp + 8 * 4]
	std	%i2, [%sp + 10 * 4]
	std	%i4, [%sp + 12 * 4]
	std	%i6, [%sp + 14 * 4]

	st	%g1, [%sp + 17 * 4]	! save G registers
	std	%g2, [%sp + 18 * 4]
	std	%g4, [%sp + 20 * 4]
	std	%g6, [%sp + 22 * 4]

	! no point whatsoever in saving O registers

	! and save the CWP in %g0 save place
	st	%l0, [%sp + 16 * 4]

	sub	%sp, 24 * 4, %sp	! fresh frame including
					! arg spill area for callees

	! do not lock the scheduler

	! HELP_GDB_WITH_BACKTRACE
	mov	%i7, %l5		! preserve it JIC
	mov	%l1, %i7		! bogus return link here

	! and we must preserve the Y register (multiply/divide auxiliary)
	! over these calls; we will keep it in %l4 which is otherwise unused.
	rd	%y, %l4

	! Now we can reenable traps (preserving interrupt level)
	or	%l0, 0x0e0, %l7		! set ET (+S,PS), preserve PIL
	wr	%l7, %psr		! and enable!
#ifdef DELAYS_AFTER_WRPSR_SAME_WINDOW
	nop
	nop
	nop
#endif
	! now call the XSR and so on with the appropriate args:
	! ie.
	! isr_retcode = (*(hal_interrupt_handlers[ vector ]))
	!                   ( vector, hal_interrupt_data[ vector ], stackp );

	! from hal_arch.h
	!// ISR tables
	!CYG_ADDRESS    hal_interrupt_handlers[CYGNUM_HAL_ISR_COUNT];
	!CYG_ADDRWORD   hal_interrupt_data[CYGNUM_HAL_ISR_COUNT];
	!CYG_ADDRESS    hal_interrupt_objects[CYGNUM_HAL_ISR_COUNT];        

	mov	%l3, %o0
	sll	%l3, 2, %l3		! %l3 to a word offset
	sethi	%hi(hal_interrupt_data), %l7
	or	%l7, %lo(hal_interrupt_data), %l7
	ld	[ %l7 + %l3 ], %o1	! data
	add	%sp, 24 * 4, %o2	! stackpointer of saved regset

	sethi	%hi(hal_interrupt_handlers), %l7
	or	%l7, %lo(hal_interrupt_handlers), %l7
	ld	[ %l7 + %l3 ], %l6
	call	%l6
	nop

	! do not call _interrupt_end()

	! restore the Y register having done our callouts to C
	wr	%l4, %y

	! We can reinstall the original CWP here; even if interrupt_end()
	! performed a reschedule (ie. yield/resume pair) we will be in the
	! same window.  The window is preserved by reschedule precisely
	! because it is impossible atomically to disable traps here without
	! involving a CWP living in a register for a time when other
	! interrupts may occur.

	! disable traps (using the saved psr is fastest way)
	wr	%l0, %psr	! restores flags, disables traps, same PIL.
#ifdef DELAYS_AFTER_WRPSR_SAME_WINDOW
	nop
	nop
	nop
#endif
	! HELP_GDB_WITH_BACKTRACE
	mov	%l5, %i7	! restore (unused) return link

	! and restore other saved regs
	add	%sp, 24 * 4, %sp	! undo fresh frame
	! restore a maximal context regardless: see also
	! CYGDBG_HAL_COMMON_INTERRUPTS_SAVE_MINIMUM_CONTEXT
	ldd	[%sp + 0 * 4], %l0	! restore L & I registers
	ldd	[%sp + 2 * 4], %l2	! to support the handler
	ldd	[%sp + 4 * 4], %l4	! having messed with them.
	ldd	[%sp + 6 * 4], %l6

	ldd	[%sp + 8 * 4], %i0
	ldd	[%sp + 10 * 4], %i2
	ldd	[%sp + 12 * 4], %i4
	ldd	[%sp + 14 * 4], %i6

	ld	[%sp + 17 * 4], %g1	! and G registers
	ldd	[%sp + 18 * 4], %g2
	ldd	[%sp + 20 * 4], %g4
	ldd	[%sp + 22 * 4], %g6
	! no point whatsoever in loading back O registers.

	! Now test for window underflow here and fix up if needs be.
	!
	! Why?  interrupt_end() might have yielded us, when only
	! its own frame was restored; its own return to us caused a
	! window underflow trap, as would our return to interruptee
	! unless we deal with it now.

	add	%l0, 1, %l7		! interruptee~s CWP plus noise
	and	%l7, __WINBITS, %l7	! CWP only
	mov	1, %l6
	sll	%l6, %l7, %l6		! 1 << CWP
	rd	%wim, %l5
	cmp	%l5, %l6		! are they the same?
	bne	2f			! No, so the stack is OK as is.

	! now do by hand an underflow trap, effectively
	sll	%l5, 1, %l5		! Rotate wim left
	srl	%l6, __WINSIZE-1, %l6
	wr	%l5, %l6, %wim
#ifdef DELAYS_AFTER_WRWIM
	nop				! are these delays needed?
	nop				! (following restore uses wim)
	nop
#endif
	restore				! Interruptee~s window
	ldd	[%sp + 0 * 4], %l0	! restore L & I registers
	ldd	[%sp + 2 * 4], %l2
	ldd	[%sp + 4 * 4], %l4
	ldd	[%sp + 6 * 4], %l6

	ldd	[%sp + 8 * 4], %i0
	ldd	[%sp + 10 * 4], %i2
	ldd	[%sp + 12 * 4], %i4
	ldd	[%sp + 14 * 4], %i6
	save				! Back to trap window

2:	! restore the condition codes, PSR and PIL and return from trap.
	wr	%l0, %psr	! restores flags, disables traps, and old PIL
#ifdef DELAYS_AFTER_WRPSR_SAME_WINDOW
	nop
	nop
	nop
#endif
	jmpl	%l1,  %g0
	rett	%l2


! end of vec_xvsr.S
