// 
// int_asm.S - assembly language interrupt utility routines
//
// $Id: //depot/rel/Foxhill/dot.8/Xtensa/OS/hal/int_asm.S#1 $

// Copyright (c) 2003-2010 Tensilica Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <xtensa/coreasm.h>


#if XCHAL_HAVE_INTERRUPTS
/*  Offsets of XtHalVPriState structure members (Xthal_vpri_state variable):  */
#define XTHAL_VPRI_VPRI_OFS		0x00
#define XTHAL_VPRI_LOCKLEVEL_OFS	0x01
#define XTHAL_VPRI_LOCKVPRI_OFS		0x02
#define XTHAL_VPRI_PAD0_OFS		0x03
#define XTHAL_VPRI_ENABLED_OFS		0x04
#define XTHAL_VPRI_LOCKMASK_OFS		0x08
#define XTHAL_VPRI_PAD1_OFS		0x0C
#define XTHAL_VPRI_ENABLEMAP_OFS	0x10
#define XTHAL_VPRI_RESOLVEMAP_OFS	(0x10+0x40*(XCHAL_NUM_INTLEVELS+1))
#define XTHAL_VPRI_END_OFS		(0x10+0x40*(XCHAL_NUM_INTLEVELS*2+1))
#endif /* XCHAL_HAVE_INTERRUPTS */



//----------------------------------------------------------------------
// Access INTENABLE register from C
//----------------------------------------------------------------------

// unsigned xthal_get_intenable(void)
//
DECLFUNC(xthal_get_intenable)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	rsr.intenable	a2
# else
	movi	a2, 0	// if no INTENABLE (no interrupts), tell caller nothing is enabled
# endif
	abi_return
	endfunc



// void xthal_set_intenable(unsigned)
//
DECLFUNC(xthal_set_intenable)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	wsr.intenable	a2
# endif
	abi_return
	endfunc


//----------------------------------------------------------------------
// Access INTERRUPT, INTSET, INTCLEAR register from C
//----------------------------------------------------------------------



// unsigned xthal_get_interrupt(void)
//
DECLFUNC (xthal_get_interrupt)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	rsr.interrupt	a2
# else
	movi	a2, 0	// if no INTERRUPT (no interrupts), tell caller nothing is pending
# endif
	abi_return
	endfunc



DECLFUNC (xthal_get_intread)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	rsr.interrupt	a2
# else
	movi	a2, 0	// if no INTERRUPT (no interrupts), tell caller nothing is pending
# endif
	abi_return
	endfunc



// void xthal_set_intset(unsigned)
//
DECLFUNC(xthal_set_intset)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	wsr.intset	a2
# endif
	abi_return
	endfunc


// void xthal_set_intclear(unsigned)
//
DECLFUNC(xthal_set_intclear)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	wsr.intclear	a2
# endif
	abi_return
	endfunc



//----------------------------------------------------------------------
// Virtual PS.INTLEVEL support:
// allows running C code at virtual PS.INTLEVEL > 0
// using INTENABLE to simulate the masking that PS.INTLEVEL would do.
//----------------------------------------------------------------------



// unsigned xthal_get_vpri(void);

DECLFUNC(xthal_get_vpri)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	movi	a2, Xthal_vpri_state
	l8ui	a2, a2, XTHAL_VPRI_VPRI_OFS
# else
	movi	a2, 0	// no interrupts, report we're always at level 0
# endif
	abi_return
	endfunc


// unsigned xthal_set_vpri_nw(unsigned)
//
//  Must be called at PS.INTLEVEL <= 1.
//  Doesn't touch the stack (doesn't reference a1 at all).
//  Normally, PS should be restored with a6 after return from this call
//  (it isn't restored automatically because some exception handlers
//   want to keep ints locked for a while).
//
//  On entry:
//	a2 = new virtual interrupt priority (0x00 .. 0x1F)
//	a3-a6 = undefined
//	PS.INTLEVEL <= 1
//  On exit:
//	a2 = previous virtual interrupt priority (0x0F .. 0x1F, or 0 if no interrupts)
//	a3-a5 = clobbered
//	a6 = PS as it was on entry
//	PS.INTLEVEL = 1
//	!!!!!!!!! PS.WOE = 0 (but not if there are no interrupts; is this really needed???)
//	INTENABLE = updated according to new vpri

_SYM(xthal_set_vpri_nw)

# if XCHAL_HAVE_INTERRUPTS
	/*  Make sure a2 is in the range 0x0F .. 0x1F:  */
	movi	a3, 0x1F	// highest legal virtual interrupt priority
	sub	a4, a2, a3	// (a4 = newlevel - maxlevel)
	movgez	a2, a3, a4	// newlevel = maxlevel if (newlevel - maxlevel) >= 0
	movi	a3, 15		// lowest legal virtual interrupt priority
	sub	a4, a2, a3	// (a4 = newlevel - 15)
	movltz	a2, a3, a4	// newlevel = 15 if newlevel < 15

xthal_set_vpri_nw_common:
	movi	a4, Xthal_vpri_state	// address of vpri state structure

	/*
	 *  Lockout interrupts for exclusive access to virtual priority structure
	 *  while we examine and modify it.
	 *  Note that we accessed a4 and don't access any further than a6,
	 *  so we won't cause any spills, so we could leave WOE enabled (if it is),
	 *  but we clear it because that might be what the caller wants,
	 *  and is cleaner.
	 */
	//  Get PS and mask off INTLEVEL:
	rsil	a6, 1		// save a6 = PS, set PS.INTLEVEL = 1

	//  Clear PS.WOE.  (Can we get rid of this?!!!!!):
	movi	a3, ~0x00040000	// mask to...
	rsr.ps	a5		// get and save a6 = PS
//a2,a3,a4,a5,a6
	and	a5, a5, a3	// ... clear a5.WOE
	wsr.ps	a5		// clear PS.WOE
	rsync

//a2,a4,a6
	/*  Get mask of interrupts to be turned off at requested level:  */
	l32i	a5, a4, XTHAL_VPRI_ENABLED_OFS		// get the global mask
	addx4	a3, a2, a4	// a3 = a4 + a2*4  (index into enablemap[] array)
//a2,a3,a4,a5,a6
	l32i	a3, a3, XTHAL_VPRI_ENABLEMAP_OFS	// get the per-level mask
	and	a3, a5, a3	// new INTENABLE value according to new intlevel
	wsr.intenable	a3	// set it!
//a2,a4,a6

	l8ui	a5, a4, XTHAL_VPRI_VPRI_OFS	// previous virtual priority
	s8i	a2, a4, XTHAL_VPRI_VPRI_OFS	// new virtual priority

	//  Let the caller restore PS:
	//wsr.ps	a6			// restore PS.INTLEVEL
	//rsync

	mov	a2, a5		// return previous virtual intlevel

# else /* ! XCHAL_HAVE_INTERRUPTS */
xthal_set_vpri_nw_common:
#  if XCHAL_HAVE_EXCEPTIONS
	rsr.ps	a6	// return PS for caller to restore
#  else
	movi	a6, 0
#  endif
	movi	a2, 0	// no interrupts, report we're always at virtual priority 0
# endif /* XCHAL_HAVE_INTERRUPTS */
	ret
	endfunc



// unsigned xthal_set_vpri_intlevel_nw(unsigned);
//
//  Same as xthal_set_vpri_nw() except that it accepts
//  an interrupt level rather than a virtual interrupt priority.
//  This just converts intlevel to vpri and jumps to xthal_set_vpri_nw.

_SYM(xthal_set_vpri_intlevel_nw)
# if XCHAL_HAVE_INTERRUPTS
	movi	a3, 0x10
	movnez	a2, a3, a2	// a2 = (a2 ? 0x10 : 0)
	addi	a2, a2, 0x0F	// a2 += 0x0F
# endif
	j	xthal_set_vpri_nw_common	// set vpri to a2
	endfunc





// unsigned  xthal_set_vpri (unsigned newvpri);
//
//  Normal windowed call (PS.INTLEVEL=0 and PS.WOE=1 on entry and exit).
//  (PS.UM = 0 or 1)
//
//  Returns previous virtual interrupt priority
//  (0x0F .. 0x1F, or 0 if no interrupts).
//
//  On entry:
//	a2 = new virtual interrupt priority (0x00 .. 0x1F)
//  On exit:
//	a2 = previous vpri
//	INTENABLE = updated according to new vpri

DECLFUNC(xthal_set_vpri)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	/*  Make sure a2 is in the range 0x0F .. 0x1F:  */
	movi	a3, 0x1F	// highest legal virtual interrupt priority
	sub	a4, a2, a3	// (a4 = newlevel - maxlevel)
	movgez	a2, a3, a4	// newlevel = maxlevel if (newlevel - maxlevel) >= 0
	movi	a3, 15		// lowest legal virtual interrupt priority
	sub	a4, a2, a3	// (a4 = newlevel - 15)
	movltz	a2, a3, a4	// newlevel = 15 if newlevel < 15

xthal_set_vpri_common1:
	movi	a4, Xthal_vpri_state	// address of vpri state structure

	/*
	 *  Lockout interrupts for exclusive access to virtual priority structure
	 *  while we examine and modify it.
	 *  Note that we accessed a4 and don't access any further than a6,
	 *  so we won't cause any spills, so we can leave WOE enabled.
	 */
	//  Get PS and mask off INTLEVEL:
	rsil	a6, 1		// save a6 = PS, set PS.INTLEVEL = 1

	l8ui	a7, a4, XTHAL_VPRI_VPRI_OFS	// previous virtual priority (vpri)

	/*  Get mask of interrupts to be turned off at requested level:  */
	l32i	a5, a4, XTHAL_VPRI_ENABLED_OFS		// get the global mask
	addx4	a3, a2, a4	// a3 = a4 + a2*4  (index into enablemap[] array)
	l32i	a3, a3, XTHAL_VPRI_ENABLEMAP_OFS	// get the per-level mask
	s8i	a2, a4, XTHAL_VPRI_VPRI_OFS	// new virtual priority (in load-slot)
	and	a3, a5, a3	// new INTENABLE value according to new intlevel
	wsr.intenable	a3	// set it!

	wsr.ps	a6		// restore PS.INTLEVEL
	rsync

	mov	a2, a7		// return previous vpri

# else /* ! XCHAL_HAVE_INTERRUPTS */
	movi	a2, 0	// no interrupts, report we're always at virtual priority 0
# endif /* XCHAL_HAVE_INTERRUPTS */
	abi_return
	endfunc



// unsigned  xthal_set_vpri_intlevel (unsigned intlevel);
//
//  Equivalent to xthal_set_vpri(XTHAL_VPRI(intlevel,0xF)).
//  This just converts intlevel to vpri and jumps inside xthal_set_vpri.

DECLFUNC(xthal_set_vpri_intlevel)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	movi	a3, 0x10
	movnez	a2, a3, a2	// a2 = (a2 ? 0x10 : 0)
	addi	a2, a2, 0x0F	// a2 += 0x0F
	j	xthal_set_vpri_common1	// set vpri to a2
# else
	movi	a2, 0	// no interrupts, report we're always at virtual priority 0
	abi_return
# endif
	endfunc



// unsigned  xthal_set_vpri_lock (void);
//
//  Equivalent to xthal_set_vpri(0x1F);
//  Returns previous virtual interrupt priority.
//
DECLFUNC(xthal_set_vpri_lock)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	movi	a2, 0x1F		// lock at intlevel 1
	j	xthal_set_vpri_common1
# else
	movi	a2, 0	// no interrupts, report we're always at virtual priority 0
	abi_return
# endif
	endfunc




// unsigned xthal_get_intpending_nw(void)
//
//  Of the pending level-1 interrupts, returns
//  the bitmask of interrupts at the highest software priority,
//  and the index of the first of these.
//  It also disables interrupts of that software priority and lower
//  via INTENABLE.
//
//	On entry:
//		a0 = return PC
//		a1 = sp
//		a2-a6 = (available) (undefined)
//		PS.INTLEVEL = 1
//		PS.WOE = 0
//	On exit:
//		a0 = return PC
//		a1 = sp (NOTE: stack is untouched, a1 is never referenced)
//		a2 = index of first highest-soft-pri pending l1 interrupt (0..31), or -1 if none
//		a3 = bitmask of highest-soft-pri pending l1 interrupts (0 if none) (may be deprecated)
//		a4 = (clobbered)
//		a5 = new vpri (not typically used by caller? so might get deprecated...?)
//		a6 = old vpri (eg. to be saved as part of interrupt context's state)
//		INTENABLE = updated according to new vpri
//		INTERRUPT bit cleared for interrupt returned in a2 (if any), if software or edge-triggered or write-error
//		all others = preserved

_SYM(xthal_get_intpending_nw)
# if XCHAL_HAVE_INTERRUPTS
	// Give us one more register to play with
	//wsr.excsave1	a4

	// Figure out which interrupt to process

	/*
	Perform a binary search to find a mask of the interrupts that are
	ready at the highest virtual priority level.
	Xthal_vpri_resolvemap is a binary tree implemented within an array,
	sorted by priority: each node contains the set of interrupts in
	the range of priorities corresponding to the right half of its branch.
	The mask of enabled & pending interrupts is compared with each node to
	determine in which subbranch (left or right) the highest priority one is
	present.  After 4 such masks and comparisons (for 16 priorities), we have
	determined the priority of the highest priority enabled&pending interrupt.

	Table entries for intlevel 'i' are bitmasks defined as follows (map=Xthal_vpri_resolvemap[i-1]):
	    map[8+(x=0)]          = ints at pri x + 8..15 (8-15)
	    map[4+(x=0,8)]        = ints at pri x + 4..7  (4-7,12-15)
	    map[2+(x=0,4,8,12)]   = ints at pri x + 2..3  (2-3,6-7,10-11,14-15)
	    map[1+(x=0,2..12,14)] = ints at pri x + 1     (1,3,5,7,9,11,13,15)
	    map[0]                = 0  (unused; for alignment)
	*/

	rsr.interrupt	a4	// a4 = mask of interrupts pending, including those disabled
	rsr.intenable	a2	// a2 = mask of interrupts enabled
	movi	a3, Xthal_vpri_state
	and	a4, a2, a4	// a4 = mask of enabled interrupts pending
	beqz	a4, gipfail	// if none (can happen for spurious level-triggered interrupts,
				//  or ???), we're done

	mov	a5, a3
	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+8*4
	bnone	a2, a4, 1f
	addi	a5, a5, 8*4
1:	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+4*4
	bnone	a2, a4, 1f
	addi	a5, a5, 4*4
1:	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+2*4
	bnone	a2, a4, 1f
	addi	a5, a5, 2*4
1:	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+1*4
	bnone	a2, a4, 1f
	addi	a5, a5, 1*4
1:

#  if 0
	a5 = address of map ...
	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+8*4
	addi	a?, a5, 8*4
	and	a2, a2, a4
	movnez	a5, a?, a2
	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+4*4
	addi	a?, a5, 4*4
	and	a2, a2, a4
	movnez	a5, a?, a2
	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+2*4
	addi	a?, a5, 2*4
	and	a2, a2, a4
	movnez	a5, a?, a2
	l32i	a2, a5, XTHAL_VPRI_RESOLVEMAP_OFS+1*4
	addi	a?, a5, 1*4
	and	a2, a2, a4
	movnez	a5, a?, a2
#  endif

	//  Here:
	//	a3 = Xthal_vpri_state
	//	a5 = Xthal_vpri_state + softpri*4
	//	a4 = mask of enabled interrupts pending
	//	a2,a6 = available

	//  Lock interrupts during virtual priority data structure transaction:
	//rsil	a6, 1			// set PS.INTLEVEL = 1 (a6 ignored)
	//	a2,a6 = available

	//  The highest priority interrupt(s) in a4 is at softpri = (a5-a3) / 4.
	//  So interrupts in enablemap[1][softpri] are not in a4 (they are higher priority).
	//  The set of interrupts at softpri are:
	//	enablemap[1][softpri-1] - enablemap[1][softpri]
	//  So and'ing a4 with enablemap[1][softpri - 1] will give us
	//  the set of interrupts pending at the highest soft priority.
	//
	l32i	a2, a5, XTHAL_VPRI_ENABLEMAP_OFS + 16*4 - 4	// get enablemap[1][softpri-1]
	and	a4, a2, a4		// only keep interrupts of highest pri (softpri)

	//  a4 now has mask of pending interrupts at highest ready level (new vpri)

	//  Update INTENABLE for this new virtual priority
	l32i	a2, a5, XTHAL_VPRI_ENABLEMAP_OFS + 16*4	// get vpri-specific mask = enablemap[1][softpri]
	l32i	a6, a3, XTHAL_VPRI_ENABLED_OFS		// get global mask
	sub	a5, a5, a3		// a5 = softpri * 4 (for below; here for efficiency)
	and	a2, a2, a6				// and together
	wsr.intenable	a2		// disable interrupts at or below new vpri
	//	a2,a6 = available

	//  Update new virtual priority:
	l8ui	a6, a3, XTHAL_VPRI_VPRI_OFS		// get old vpri (returned)
	srli	a5, a5, 2		// a5 = softpri  (0..15)
	addi	a5, a5, 0x10		// a5 = 0x10 + softpri = new virtual priority
	s8i	a5, a3, XTHAL_VPRI_VPRI_OFS		// store new vpri (returned)

	//  Undo the temporary lock (if was at PS.INTLEVEL > 1):
	//rsil	a2, 1

	mov	a3, a4		// save for the caller (in case it wants it?)

	//  Choose one of the set of highest-vpri pending interrupts to process.
	//  For speed (and simplicity), use this simple two-instruction sequence
	//  to select the least significant bit set in a4.  This implies that
	//  interrupts with a lower interrupt number take precedence over those
	//  with a higher interrupt number (!!).
	//
	neg	a2, a4		// keep only the least-significant bit that is set...
	and	a4, a2, a4	// ... in a4

	//  Software, edge-triggered, and write-error interrupts are cleared by writing to the
	//  INTCLEAR pseudo-reg (to clear relevant bits of the INTERRUPT register).
	//  To simplify interrupt handlers (so they avoid tracking which type of
	//  interrupt they handle and act accordingly), clear such interrupts here.
	//  To avoid race conditions, the clearing must occur *after* we undertake
	//  to process the interrupt, and *before* actually handling the interrupt.
	//  Interrupt handlers may additionally clear the interrupt themselves
	//  at appropriate points if needed to avoid unnecessary interrupts.
	//
#define CLEARABLE_INTLEVEL1_MASK	(XCHAL_INTLEVEL1_MASK & XCHAL_INTCLEARABLE_MASK)
#  if CLEARABLE_INTLEVEL1_MASK != 0
	//movi	a2, CLEARABLE_INTLEVEL1_MASK
	//and	a2, a2, a4
	//wsr.intclear	a2
	wsr.intclear	a4	// no effect if a4 not a software or edge-triggered or write-error interrupt
#  endif

	//  Convert the single-bit interrupt mask to an interrupt number.
	//  (ie. compute log2 using either the NSAU instruction or a binary search)

	find_ms_setbit	a2, a4, a2, 0	// set a2 to index of lsbit set in a4 (0..31)
				// NOTE: assumes a4 != 0 (otherwise a2 is undefined[?])

	//	a2 has vector number (0..31)

	//rsr.excsave1	a4
	ret

gipfail:
	l8ui	a6, a3, XTHAL_VPRI_VPRI_OFS		// get old vpri
	mov	a5, a6					// is also new vpri (unchanged)
# else /* XCHAL_HAVE_INTERRUPTS */
	//  No interrupts configured!
	movi	a5, 0		// return zero new vpri
	movi	a6, 0		// return zero old vpri
# endif /* XCHAL_HAVE_INTERRUPTS */
	movi	a2, -1		// return bogus vector number (eg. can be quickly tested for negative)
	movi	a3, 0		// return zero bitmask of interrupts pending
	ret
	endfunc

// -----------------------------------------------------------------



// void xthal_vpri_lock()
//
// Used internally by the Core HAL to block interrupts of higher or equal
// priority than Xthal_vpri_locklevel during virtual interrupt operations.
//
DECLFUNC(xthal_vpri_lock)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
	rsil	a6, 1				// save a6 = PS, set PS.INTLEVEL = 1

	//     if( Xthal_vpri_level < Xthal_vpri_locklevel )
	//
	movi	a2, Xthal_vpri_state		// a2 := address of global var. Xthal_vpri_state
	//interlock
	l8ui	a3, a2, XTHAL_VPRI_VPRI_OFS	// a3 := Xthal_vpri_level == Xthal_vpri_state.vpri
	l8ui	a5, a2, XTHAL_VPRI_LOCKLEVEL_OFS  // a5 := Xthal_vpri_locklevel
	l32i	a4, a2, XTHAL_VPRI_ENABLED_OFS	// a4 := Xthal_vpri_enabled 
	bgeu	a3, a5, xthal_vpri_lock_done

	//  xthal_set_intenable( Xthal_vpri_enablemap[0][Xthal_vpri_locklevel] & Xthal_vpri_enabled );
	//
	addx4	a3, a5, a2			// a3 := a2 + a5*4  (index into enablemap[] array)
	l32i	a3, a3, XTHAL_VPRI_ENABLEMAP_OFS // a3 := Xthal_vpri_enablemap[0][Xthal_vpri_locklevel]
	//interlock
	and	a2, a4, a3
  	wsr.intenable	a2

xthal_vpri_lock_done:
	wsr.ps	a6				// restore PS.INTLEVEL
	rsync
# endif
	abi_return
	endfunc



// void xthal_vpri_unlock(void)
//
// Enable interrupts according to the current virtual interrupt priority.
// This effectively "unlocks" interrupts disabled by xthal_vpri_lock()
// (assuming the virtual interrupt priority hasn't changed).
//
DECLFUNC(xthal_vpri_unlock)
	abi_entry
# if XCHAL_HAVE_INTERRUPTS
        //
	//  This should be free of race-conditions.
	//
        //  xthal_set_intenable( Xthal_vpri_enablemap[0][Xthal_vpri_level] & Xthal_vpri_enabled );
	//
	movi	a2, Xthal_vpri_state		 // a2 := address of global var. Xthal_vpri_state
	//interlock
	l8ui	a3, a2, XTHAL_VPRI_VPRI_OFS      // a3 := Xthal_vpri_level == Xthal_vpri_state.vpri
	l32i	a4, a2, XTHAL_VPRI_ENABLED_OFS	 // a4 := Xthal_vpri_enabled 
	addx4	a3, a3, a2                       // a3 := a2 + a3*4  (index into enablemap[] array)
	l32i	a3, a3, XTHAL_VPRI_ENABLEMAP_OFS // a3 := Xthal_vpri_enablemap[0][Xthal_vpri_level]
	//interlock
	and	a2, a4, a3
  	wsr.intenable	a2
# endif
	abi_return
	endfunc


