!	@(#)subr.s 1.21 87/09/11 SMI
!	Copyright (c) 1986 by Sun Microsystems, Inc.

	.seg	"text"
	.align	4

#include <machine/asm_linkage.h>
#include <machine/psl.h>
#include <machine/mmu.h>
#include <machine/enable.h>
#include "/sys/sun4/assym.s"

/*
 * General assembly language routines.
 */

/*
 * Macro to raise processor priority level.
 * Avoid dropping processor priority if already at high level.
 */
#define	RAISE(level) \
	mov	%psr, %o0; \
	and	%o0, PSR_PIL, %g1; \
	cmp	%g1, (level << PSR_PIL_BIT); \
	bl,a	1f; \
	andn	%o0, PSR_PIL, %g1; \
	retl; \
	nop; \
1:	or	%g1, (level << PSR_PIL_BIT), %g1; \
	mov	%g1, %psr; \
	nop; \
	retl; \
	nop;

#define SETPRI(level) \
	mov	%psr, %o0; \
	andn	%o0, PSR_PIL, %g1; \
	or	%g1, (level << PSR_PIL_BIT), %g1; \
	mov	%g1, %psr; \
	nop; \
	retl; \
	nop;

	ENTRY(splimp)
	RAISE(6)

	ENTRY(splnet)
	RAISE(1)

	ENTRY(splie)
	RAISE(6)

	ENTRY(splclock)
	RAISE(10)

	ENTRY(splzs)
	SETPRI(12)

	ENTRY(spl7)
	SETPRI(13)

	ENTRY(spl8)
	SETPRI(15)

	ENTRY2(spl6,spl5)
	SETPRI(12)

	ENTRY(spl4)
	SETPRI(8)

	ENTRY(spl3)
	SETPRI(6)

	ENTRY(spl2)
	SETPRI(4)

	ENTRY2(spl1,splsoftclock)
	SETPRI(2)

	ENTRY(spl0)
	SETPRI(0)

	ENTRY(spl
	and	%o0, PSR_PIL, %g1
	mov	%psr, %o0
	andn	%o0, PSR_PIL, %g2
	or	%g2, %g1, %g2
	mov	%g2, %psr
	nop
	retl
	nop

/*
 * splr is like splx but will only raise the priority and never drop it
 */
	ENTRY(splr)
	mov	%psr, %o1		! find current priority
	and	%o1, PSR_PIL, %o2
	and	%o0, PSR_PIL, %o0
	cmp	%o2, %o0		! if cur pri < new pri, set new pri
	bl	1f
	andn	%o1, PSR_PIL, %o1	! generate new psr value
	mov	%o2, %o0		! set return value (old pri)
	retl
	.empty				! next instruction ok in delay slot
1:
	or	%o1, %o0, %o1
	mov	%o1, %psr		! set new pri
	nop				! allow for 3 cycle delay
	retl				! return value is new pri
	nop

/*
 * Setjmp and longjmp implement non-local gotos using state vectors
 * type label_t.
 *
 * setjmp(lp)
 * label_t *lp;
 */
	ENTRY(setjmp)
	st	%o7, [%o0 + L_PC]	! save return address
	st	%sp, [%o0 + L_SP]	! save stack ptr
	retl
	clr	%o0			! return 0

/*
 * longjmp(lp)
 * label_t *lp;
 */
	ENTRY(longjmp)
	call	_flush_windows		! flush all but this window
	nop
	ld	[%o0 + L_PC], %i7	! restore return addr
	ld	[%o0 + L_SP], %fp	! restore sp for dest on foreign stack
	ret				! return 1
	restore	%g0, 1, %o0		! takes underflow, switches stacks

/*
 * Enable and disable video.
 */
	ENTRY(setvideoenable)
	tst	%o0
	bnz	_on_enablereg
	mov	ENA_VIDEO, %o0

	b,a	_off_enablereg

/*
 * Enable and disable DVMA.
 */
	ENTRY(enable_dvma)
	b	_on_enablereg
	mov	ENA_SDVMA, %o0		! enable system DVMA

	ENTRY(disable_dvma)
	b	_off_enablereg
	mov	ENA_SDVMA, %o0		! disable system DVMA

/*
 * Turn on a bit in the system enable register.
 * on_enablereg((u_char)bit)
 */
	ENTRY(on_enablereg)
	mov	%psr, %o3
	or	%o3, PSR_PIL, %g1	! spl hi to lock enable reg update
	mov	%g1, %psr
	nop; nop;			! psr delay
	sethi	%hi(_enablereg), %o1	! software copy of enable register
	ldub	[%o1 + %lo(_enablereg)], %g1 ! get software copy
	set	ENABLEREG, %o2		! address of real version in hardware
	bset	%o0, %g1		! turn on bit
	stb	%g1, [%o1 + %lo(_enablereg)] ! update software copy
	stba	%g1, [%o2]ASI_CTL	! write out new enable register
	stb	%g1, [%o1 + %lo(_enablereg)] ! update soft copy, for cache off
	mov	%o3, %psr		! restore psr
	nop				! psr delay
	retl
	nop

/*
 * Turn off a bit in the system enable register.
 * off_enablereg((u_char)bit)
 */
	ENTRY(off_enablereg)
	mov	%psr, %o3
	or	%o3, PSR_PIL, %g1	! spl hi to lock enable reg update
	mov	%g1, %psr
	nop; nop;			! psr delay
	sethi	%hi(_enablereg), %o1	! software copy of enable register
	ldub	[%o1 + %lo(_enablereg)], %g1 ! get software copy
	set	ENABLEREG, %o2		! address of real version in hardware
	bclr	%o0, %g1		! turn off bit
	stb	%g1, [%o1 + %lo(_enablereg)] ! update software copy
	stba	%g1, [%o2]ASI_CTL	! write out new enable register
	mov	%o3, %psr		! restore psr
	nop				! psr delay
	retl
	nop

/*
 * Read the ID prom.
 * This is mapped from IDPROMBASE for IDPROMSIZE bytes in the
 * ASI_CTL address space for byte access only.
 */
	ENTRY(getidprom)
	set	IDPROMBASE, %g1
	clr	%g2
1:
	lduba	[%g1 + %g2]ASI_CTL, %g7	! get id prom byte
	add	%g2, 1, %g2		! interlock
	stb	%g7, [%o0]		! put it out
	cmp	%g2, IDPROMSIZE		! done yet?
	bne,a	1b
	add	%o0, 1, %o0		! delay slot
	retl				! leaf routine return
	nop				! delay slot

/*
 * if a() calls b() calls caller(),
 * caller() returns return address in a().
 */
	ENTRY(caller)
	retl
	mov	%i7, %o0

/*
 * if a() calls callee(), callee() returns the
 * return address in a();
 */
	ENTRY(callee)
	retl
	mov	%o7, %o0

/*
 * insque(entryp, predp)
 *
 * Insert entryp after predp in a doubly linked list.
 */
	ENTRY(_insque)
	ld	[%o1], %g1		! predp->forw
	st	%o1, [%o0 + 4]		! entryp->back = predp
	st	%g1, [%o0]		! entryp->forw = predp->forw
	st	%o0, [%o1]		! predp->forw = entryp
	retl
	st	%o0, [%g1 + 4]		! predp->forw->back = entryp

/*
 * remque(entryp)
 *
 * Remove entryp from a doubly linked list
 */
	ENTRY(_remque)
	ld	[%o0], %g1		! entryp->forw
	ld	[%o0 + 4], %g2		! entryp->back
	st	%g1, [%g2]		! entryp->back = entryp->forw
	retl
	st	%g2, [%g1 + 4]		! entryp->forw = entryp->back

/*
 * scanc(length, string, table, mask)
 *
 * VAX scanc instruction.
 */
	ENTRY(scanc)
	tst	%o0
	bz	1f			! check length
	clr	%o4
0:
	ldub	[%o1 + %o4], %g1	! get next byte in string
	cmp	%o4, %o0		! interlock slot, index < length ?
	ldub	[%o2 + %g1], %g1	! get corresponding table entry
	bge	1f			! interlock slot
	btst	%o3, %g1		! apply the mask
	bz,a	0b
	inc	%o4			! delay slot, increment index
1:
	retl				! return(length - index)
	sub	%o0, %o4, %o0

