#! /bin/sh
#	@(#)TEST_LOCORE	1.1	10/31/94

# Test of assembler's ability to assemble the kernel's "locore.s".

FLAGS="$*"
CLEANUP="rm -f LOCORE.*"
AS=./as

${CLEANUP}

#=============================================================================
cat <<-'!EOF'  >LOCORE.assym.s
	#define	P_LINK 0x0
	#define	P_RLINK 0x4
	#define	P_ADDR 0x8
	#define	P_PRI 0xd
	#define	P_STAT 0xf
	#define	P_WCHAN 0x58
	#define	P_CTX 0x90
	#define	P_FLAG 0x24
	#define	P_PID 0x2c
	#define	PROCSIZE 0x98
	#define	SLOAD 0x1
	#define	SSLEEP 0x1
	#define	SRUN 0x3
	#define	SPTECHG_BIT 24
	#define	V_SWTCH 0x0
	#define	V_TRAP 0x4
	#define	V_SYSCALL 0x8
	#define	V_INTR 0xc
	#define	V_PDMA 0x10
	#define	FM_CTX 0x0
	#define	FM_SEGMENT 0x4
	#define	FM_PAGE 0x8
	#define	FM_PARTIAL 0xc
	#define	MSGBUFPTECNT 0x1
	#define	NMBCLUSTERS 0x100
	#define	L_PC 0x0
	#define	L_SP 0x4
	#define	U_LOFAULT 0x3714
	#define	U_ARG 0x329c
	#define	U_PROCP 0x3280
	#define	USIZE 0x3738
	#define	PCB_REGS 0x3000
	#define	PCB_PC 0x3000
	#define	PCB_SP 0x3004
	#define	PCB_PSR 0x3008
	#define	PCB_SSWAP 0x3020
	#define	PCB_P0LR 0x3010
	#define	PCB_WBUF 0x3028
	#define	PCB_SPBUF 0x31a8
	#define	PCB_WBCNT 0x31c0
	#define	PCB_UWM 0x3024
	#define	PCB_FPU_REGS 0x31c8
	#define	PCB_FPU_FSR 0x3248
	#define	PCB_FPU_Q 0x3250
	#define	PCB_FPU_QCNT 0x3260
	#define	PCB_FPU_EN 0x3261
	#define	PCB_FLAGS 0x3268
	#define	PCB_TEM_BIT 20
	#define	PCB_AEXC_BIT 5
	#define	AST_SCHED_BIT 31
	#define	ZSSIZE 0x34
	#define	ZS_ADDR 0x10
	#define	CTX_CONTEXT 0x8
	#define	CTX_PROCP 0x0
	#define	CTX_TIME 0x4
	#define	PSR_PIL_BIT 8
	#define	PG_S_BIT 29
	#define	REGSIZE 76
	#define	ROMP_PRINTF 0xffe81084
	#define	ROMP_ROMVEC_VERSION 0xffe810a4
	#define	ROMP_MEMORYBITMAP 0xffe810c8
	#define	ROMP_MEMAVAIL 0xffe810b8
	#define	EEPROM_PTE 0xe4079000
	#define	CLOCK_PTE 0xe4079800
	#define	MEMERR_PTE 0xe407a000
	#define	INTREG_PTE 0xe407a800
	#define	ECCREG_PTE 0xe407f8f0
	#define	ME_VADDR 0x4
	#define	PTE_SIZE 4
!EOF
cat <<-'!EOF'  >LOCORE.s
		.seg	"data"
		.asciz	"@(#)locore.s 1.62 87/08/05"

	!	Copyright (c) 1986 by Sun Microsystems, Inc.

	#include <sys/param.h>
	#include <sys/vmparam.h>
	#include <sys/errno.h>
	#include <netinet/in_systm.h>
	#include <sun4/asm_linkage.h>
	#include <sun4/buserr.h>
	#include <sun4/clock.h>
	#include <sun4/cpu.h>
	#include <sun4/diag.h>
	#include <sun4/enable.h>
	#include <sun4/intreg.h>
	#include <sun4/memerr.h>
	#include <sun4/eccreg.h>
	#include <sun4/eeprom.h>
	#include <sun4/mmu.h>
	#include <sun4/pcb.h>
	#include <sun4/psl.h>
	#include <sun4/pte.h>
	#include <sun4/reg.h>
	#include <sun4/trap.h>
	#include <sun4/scb.h>

	#include "LOCORE.assym.s"

	NW	= NWINDOW
	WINMASK	= ((1 << NWINDOW) - 1)
	PROT	= (PG_V | PG_W) >> PG_S_BIT
	P_INVAL	= (PG_V) >> PG_S_BIT

	!
	! MINFRAME and the register offsets in struct regs must add up to allow
	! double word loading of struct regs. PCB_WBUF must also be aligned.
	!
	#if (MINFRAME & 7) == 0 || (G2 & 1) == 0 || (O0 & 1) == 0
	ERROR - struct regs not aligned
	#endif
	#if (PCB_WBUF & 7)
	ERROR - pcb_wbuf not aligned
	#endif

		.seg	"data"
	/*
	 * Absolute external symbols.
	 * On the sun4 we put the message buffer and the pte's
	 * for proc 0 on the same page, since we have such a large page size.
	 * We set things up so that the first page of KERNELBASE is illegal
	 * to act as a redzone during copyin/copyout type operations. One of
	 * the reasons the message buffer is allocated in low memory to
	 * prevent being overwritten during booting operations (besides
	 * the fact that it is small enough to share a page with others).
	 */
		.global	_u, _DVMA, _msgbuf
	_u	= UADDR				! address of u area
	_DVMA	= DVMABASE			! address of DVMA area
	_msgbuf	= KERNELBASE + NBPG		! address of printf message buffer

	#if MSGBUFSIZE > (NBPG - (4 * UPAGES))
	ERROR - msgbuf too large
	#endif

	#if USIZE-KERNSTACK >= 4096
	ERROR - user area too large
	#endif

	/*
	 * Define some variables used by post-mortem debuggers
	 * to help them work on kernels with changing structures.
	 */
		.global UPAGES_DEBUG, KERNELBASE_DEBUG, VADDR_MASK_DEBUG
		.global PGSHIFT_DEBUG, SLOAD_DEBUG

	UPAGES_DEBUG		= UPAGES
	KERNELBASE_DEBUG	= KERNELBASE
	VADDR_MASK_DEBUG	= 0xffffffff
	PGSHIFT_DEBUG		= PGSHIFT
	SLOAD_DEBUG		= SLOAD

	/*
	 * The interrupt stack. This must be the first thing in the data
	 * segment (other than an sccs string) so that we don't stomp
	 * on anything important during interrupt handling. We get a
	 * red zone below this stack for free when the kernel text is
	 * write protected. The interrupt entry code assumes that the
	 * interrupt stack is at a lower address than
	 * both eintstack and the kernel stack in the u area.
	 */
	#define INTSTACKSIZE	0x3000

		.align	8
		.global intstack, eintstack
	intstack:				! bottom of interrupt stack
		.skip	INTSTACKSIZE
	eintstack:				! end (top) of interrupt stack

	/*
	 * System software page tables
	 */
	#define vaddr(x)	((((x)-_Sysmap)/4)*NBPG + KERNELBASE)
	#define SYSMAP(mname, vname, npte)	\
		.global	mname;			\
	mname:	.skip	(4*npte);		\
		.global	vname;			\
	vname = vaddr(mname);
		SYSMAP(_Sysmap	 ,_Sysbase	,SYSPTSIZE	)
		SYSMAP(_Usrptmap ,_usrpt	,USRPTSIZE	)
		SYSMAP(_Forkmap	 ,_forkutl	,UPAGES		)
	#ifdef VAC
		SYSMAP(_Swtchmap ,_swtchutl     ,UPAGES         )
	#endif VAC
		SYSMAP(_Xswapmap ,_xswaputl	,UPAGES		)
		SYSMAP(_Xswap2map,_xswap2utl	,UPAGES		)
		SYSMAP(_Swapmap	 ,_swaputl	,UPAGES		)
		SYSMAP(_Pushmap	 ,_pushutl	,UPAGES		)
		SYSMAP(_Vfmap	 ,_vfutl	,UPAGES		)
		SYSMAP(_CMAP1	 ,_CADDR1	,1		) ! local tmp
		SYSMAP(_CMAP2	 ,_CADDR2	,1		) ! local tmp
		SYSMAP(_mmap	 ,_vmmap	,1		)
		SYSMAP(_Mbmap	 ,_mbutl	,NMBCLUSTERS*CLSIZE)
		SYSMAP(_ESysmap	 ,_Syslimit	,0		) ! must be last

		.global	_Syssize
	_Syssize = (_ESysmap-_Sysmap)/4

	#ifdef SAS
		.global _availmem
	_availmem:
		.word	0
	#endif SAS

	/*
	 * Software copy of system enable register
	 * This is always atomically updated
	 */
		.global	_enablereg
	_enablereg:	.byte	0		! UNIX's system enable register

	/*
	 * Opcodes for instructions in PATCH macros
	 */
	#define MOVPSRL0	0xa1480000
	#define MOVL4		0xa8102000
	#define BA		0x10800000

	/*
	 * Trap vector macros.
	 */
	#define TRAP(H) \
		b (H); mov %psr,%l0; nop; nop;

	#define SYS_TRAP(T) \
		mov %psr,%l0; b sys_trap; mov (T),%l4; nop;

	#define BAD_TRAP	SYS_TRAP((. - _scb) >> 4);

	#define PATCH_ST(T, V) \
		set	_scb, %g1; \
		set	MOVPSRL0, %g2; \
		st	%g2, [%g1 + ((V)*16+0*4)]; \
		set	sys_trap, %g2; \
		sub	%g2, %g1, %g2; \
		sub	%g2, ((V)*16 + 4), %g2; \
		srl	%g2, 2, %g2; \
		set	BA, %g3; \
		or	%g2, %g3, %g2; \
		st	%g2, [%g1 + ((V)*16+1*4)]; \
		set	MOVL4 + (T), %g2; \
		st	%g2, [%g1 + ((V)*16+2*4)];

	#define PATCH_T(H, V) \
		set	_scb, %g1; \
		set	(H), %g2; \
		sub	%g2, %g1, %g2; \
		sub	%g2, (V)*16, %g2; \
		srl	%g2, 2, %g2; \
		set	BA, %g3; \
		or	%g2, %g3, %g2; \
		st	%g2, [%g1 + ((V)*16+0*4)]; \
		set	MOVPSRL0, %g2; \
		st	%g2, [%g1 + ((V)*16+1*4)];

	/*
	 * Trap vector table.
	 * This must be the first text in the boot image.
	 *
	 * When a trap is taken, we vector to KERNELBASE+(TT*16) and we have
	 * the following state:
	 *	2) traps are disabled
	 *	3) the previous state of PSR_S is in PSR_PS
	 *	4) the CWP has been incremented into the trap window
	 *	5) the previous pc and npc is in %l1 and %l2 respectively.
	 *
	 * Registers:
	 *	%l0 - %psr immediately after trap
	 *	%l1 - trapped pc
	 *	%l2 - trapped npc
	 *	%l3 - wim (sys_trap only)
	 *	%l4 - system trap number (sys_trap only)
	 *
	 * Note: UNIX receives control at vector 0 (trap)
	 */
		.seg	"text"
		.align	4

		.global _start, _scb
	_start:
	_scb:
		TRAP(start);				! 00
		SYS_TRAP(T_FAULT | T_TEXT_FAULT);	! 01
		SYS_TRAP(T_UNIMP_INSTR);		! 02
		SYS_TRAP(T_PRIV_INSTR);			! 03
		SYS_TRAP(T_FP_DISABLED);		! 04
		TRAP(window_overflow);			! 05
		TRAP(window_underflow);			! 06
		SYS_TRAP(T_ALIGNMENT);			! 07
		SYS_TRAP(T_FP_EXCEPTION);		! 08
		SYS_TRAP(T_FAULT | T_DATA_FAULT);	! 09
		BAD_TRAP;				! 0A tag_overflow
		BAD_TRAP; BAD_TRAP;			! 0B - 0C
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 0D - 10
		SYS_TRAP(T_INTERRUPT | 1);		! 11
		SYS_TRAP(T_INTERRUPT | 2);		! 12
		SYS_TRAP(T_INTERRUPT | 3);		! 13
		SYS_TRAP(T_INTERRUPT | 4);		! 14
		SYS_TRAP(T_INTERRUPT | 5);		! 15
		SYS_TRAP(T_INTERRUPT | 6);		! 16
		SYS_TRAP(T_INTERRUPT | 7);		! 17
		SYS_TRAP(T_INTERRUPT | 8);		! 18
		SYS_TRAP(T_INTERRUPT | 9);		! 19
		SYS_TRAP(T_INTERRUPT | 10);		! 1A
		SYS_TRAP(T_INTERRUPT | 11);		! 1B
		SYS_TRAP(T_INTERRUPT | 12);		! 1C
		SYS_TRAP(T_INTERRUPT | 13);		! 1D
	#ifdef GPROF
		TRAP(test_prof);			! 1E
	#else
		SYS_TRAP(T_INTERRUPT | 14);		! 1E
	#endif GPROF
		SYS_TRAP(T_INTERRUPT | 15);		! 1F

	/*
	 * The rest of the traps in the table up to 0x80 should 'never'
	 * be generated by hardware.
	 */
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 20 - 23
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 24 - 27
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 28 - 2B
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 2C - 2F
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 30 - 33
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 34 - 37
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 38 - 33
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 3C - 3F
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 40 - 43
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 44 - 47
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 48 - 4B
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 4C - 4F
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 50 - 53
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 54 - 57
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 58 - 5B
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 5C - 5F
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 60 - 63
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 64 - 67
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 68 - 6B
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 6C - 6F
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 70 - 73
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 74 - 77
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 78 - 7B
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 7C - 7F

	/*
	 * User generated traps
	 */
		SYS_TRAP(T_SYSCALL)			! 80 - system call
		SYS_TRAP(T_BREAKPOINT)			! 81 - user breakpoint
		SYS_TRAP(T_DIV0)			! 82 - divide by zero
		SYS_TRAP(T_FLUSH_WINDOWS)		! 83 - flush windows
		TRAP(clean_windows);			! 84 - clean windows
		BAD_TRAP;				! 85 - range check
		BAD_TRAP;				! 86
		BAD_TRAP;				! 87
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 88 - 8B
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 8C - 8F
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 90 - 93
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 94 - 97
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 98 - 9B
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 9C - 9F
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! A0 - A3
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! A4 - A7
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! A8 - AB
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! AC - AF
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! B0 - B3
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! B4 - B7
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! B8 - BB
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! BC - BF
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP;	! C0 - C3
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! C4 - C7
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! C8 - CB
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! CC - CF
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! D0 - D3
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! D4 - D7
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! D8 - DB
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! DC - DF
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! E0 - E3
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! E4 - E7
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! E8 - EB
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! EC - EF
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! F0 - F3
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! F4 - F7
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! F8 - FB
		BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! FC - FF

	/*
	 * System initialization
	 *
	 * Do a halfhearted job of setting up the mmu so that we can run out
	 * of the high address space. We do this by copying the pmeg numbers
	 * for the physical locations to the correct virtual locations.
	 * NOTE - Assumes that the real and virtual locations have the same
	 * segment offsets from 0 and KERNELBASE!!!
	 *
	 * We make the following assumptions about our environment
	 * as set up by the monitor:
	 *
	 *	- traps are disabled
	 *	- we are loaded at 0x4000
	 *	- we have enough memory mapped for the entire kernel + some more
	 *	- all pages are writable
	 *	- the last pmeg [SEGINV] has no valid pme's
	 *	- the next to the last pmeg has no valid pme's
	 *	- when the monitor's romp->v_memorybitmap points to a zero
	 *	    - each low segment i is mapped to use pmeg i
	 *	    - each page map entry i maps physical page i
	 *	- on systems w/ ecc memory, that the monitor has set the base
	 *	    addresses and enabled all the memory cards correctly
	 *
	 * We will set the protection properly in startup().
	 * Before we set up the new mapping and start running with the correct
	 * addresses, all of the code must be carefully written to be position
	 * independent code, since we are linked for running out of high addresses,
	 * but we get control running in low addresses.
	 */
		.global	start
	start:
		set	PSR_S|PSR_PIL, %g1	! setup psr, disable traps
		mov	%g1, %psr
		mov	0x02, %wim		! setup wim
		nop				! psr delay
		nop

		set	CONTEXTBASE, %g1	! setup kernel context (0)
		stba	%g0, [%g1]ASI_CTL

		/*
		 * Loop through the kernel segment maps starting at zero, copying the
		 * pmeg numbers to the correct virtual addresses, which start at
		 * KERNELBASE.
		 * NOTE: This loop won't work if the kernel size is greater than
		 * KERNELBASE.
		 */
		clr	%l0			! current virtual address
		set	KERNELBASE, %l1		! correct virtual address
		set	_end-1, %l2		! ending virtual address
		set	NBSG, %l3
	1:
		lduha	[%l0]ASI_SM, %g1
		stha	%g1, [%l1]ASI_SM
		add	%l1, %l3, %l1
		cmp	%l1, %l2
		ble,a	1b
		add	%l0, %l3, %l0		! delay slot

		/*
		 * The correct virtual addresses are now mapped. Do an absolute jump
		 * to set the pc to the correct virtual addresses.
		 */
		set	1f, %g1
		jmp	%g1
		nop
	1:
		/*
		 * Now we are running with correct addresses
		 * and can use non-position independent code.
		 */

	#ifdef SAS
		/*
		 * If we are in the simulator we now size memory by counting the
		 * valid segment maps.
		 */
		clr	%l0
		set	NBSG, %g2
	2:
		lduha	[%l0]ASI_SM, %g1
	#ifdef SUN4_260
		cmp	%g1, SEGINV
	#else
		cmp	%g1, 255
	#endif
		bne,a	2b
		add	%l0, %g2, %l0

		sethi	%hi(_availmem), %g1
		st	%l0, [%g1 + %lo(_availmem)]
	#endif SAS

		/*
		 * Use the pmeg after sysptsize for the u area pmeg.
		 */
		set	UPMEG, %g1		! use pmeg reserved for u area
		set	_u, %l0
		stha	%g1, [%l0]ASI_SM	! write segment map

		/*
		 * Since we are loaded at 0x4000 we use physical page 0
		 * for the u area. If UPAGES > 1 we also use pages after _end.
		 * NOTE: We assume the u area starts somewhere in the last segment
		 * of virtual memory and that u is word aligned.
		 * This code and uinit() are closely related.
		 * The second physical page is used for the msgbuf in its first 2k
		 * and the last few words hold the proc 0 uarea ptes.
		 */
	#if UPAGES > 1
		mov	UPAGES-1, %l1
		set	NBPG, %l3
		set	(_end+NBPG-1)-KERNELBASE, %l2 ! compute page after _end
		srl	%l2, PGSHIFT, %l2
		set	PG_V|PG_S|PG_W, %g1
		or	%l2, %g1, %l2		! add protections
	0:
		sta	%l2, [%l0]ASI_PM	! write page map
		add	%l0, %l3, %l0		! virtual address of next page of u
		deccc	%l1			! next to last UPAGE?
		bg,a	0b
		inc	%l2			! next physical page
	#endif UPAGES > 1
		set	PG_V|PG_S|PG_W|0, %g1	! pte for physical page 0
		sta	%g1, [%l0]ASI_PM	! write page map

		/*
		 * Clear u area.
		 */
		set	USIZE, %g1		! size of u area
	1:
		subcc	%g1, 4, %g1
		bnz	1b
		clr	[%l0 + %g1]

		/*
		 * get software copy of enable register
		 */
		set	ENABLEREG, %g1		! address of real version in hardware
		lduba	[%g1]ASI_CTL, %g1
		sethi	%hi(_enablereg), %g2	! software copy of enable register
		stb	%g1, [%g2 + %lo(_enablereg)] ! update software copy

		/*
		 * Now map in our own copies of the eeprom, clock, memory error
		 * register, and interrupt control register into the last virtual
		 * segment.
		 */
		set	EEPROM_ADDR, %g1	! map in eeprom
		set	EEPROM_PTE, %g2
		sta	%g2, [%g1]ASI_PM

		set	CLOCK_ADDR, %g1		! map in clock
		set	CLOCK_PTE, %g2
		sta	%g2, [%g1]ASI_PM

		set	MEMERR_ADDR, %g1	! map in memory error register
		set	MEMERR_PTE, %g2
		sta	%g2, [%g1]ASI_PM

		set	INTREG_ADDR, %g1	! map in interrupt control reg
		set	INTREG_PTE, %g2
		sta	%g2, [%g1]ASI_PM

		set	ECCREG_ADDR, %g1	! map in ecc regs
		set	ECCREG_PTE, %g2
		sta	%g2, [%g1]ASI_PM

		/*
		 * Setup trap base and make a kernel stack.
		 */
		set	_scb, %g1		! setup trap handler
		mov	%g1, %tbr

		mov	_u+KERNSTACK, %sp	! setup kernel stack pointer
		sub	%sp, SA(MINFRAME + REGSIZE), %sp
		mov	0, %fp

		/*
		 * Dummy up fake user registers on the stack.
		 */
		set	USRSTACK-WINDOWSIZE, %g1
		st	%g1, [%sp + MINFRAME + SP*4] ! user stack pointer
		set	PSL_USER, %l0
		st	%l0, [%sp + MINFRAME + PSR*4] ! psr
		set	USRTEXT, %g1
		st	%g1, [%sp + MINFRAME + PC*4] ! pc
		add	%g1, 4, %g1
		st	%g1, [%sp + MINFRAME + nPC*4] ! npc

		mov	%psr, %g1
		wr	%g1, PSR_ET, %psr	! enable traps

		/*
		 * Zero bss.
		 */
		set	_edata, %o0
		set	_end, %o1
		call	_bzero
		sub	%o1, %o0, %o1
		/*
		 * Patch vector 0 trap to "zero" if it happens again.
		 */
		PATCH_ST(T_ZERO, 0)

		/*
		 * Call main. We will return as process 1 (init).
		 */
		call	_main
		add	%sp, MINFRAME, %o0

		/*
		 * Proceed as if this was a normal user trap.
		 */
		b,a	sys_rtt			! fake return from trap

	/*
	 * Routines beyond this point are executed with the proper PC and do not
	 * have to be position independent.
	 */

	/*
	 * Generic system trap handler.
	 */
		.global	sys_trap
	sys_trap:
		!
		! Prepare to go to C (batten down the hatches).
		!
		mov	0x01, %l5		! CWM = 0x01 << CWP
		sll	%l5, %l0, %l5
		mov	%wim, %l3		! get WIM
		btst	PSR_PS, %l0		! test pS
		bz	st_user			! branch if user trap
		btst	%l5, %l3		! delay slot, compare WIM and CWM

		!
		! Trap from supervisor.
		! We can be either on the system stack or interrupt stack.
		!
		sub	%fp, MINFRAME+REGSIZE, %l7 ! save sys globals on stack
		SAVE_GLOBALS(%l7 + MINFRAME)
	#ifdef TRAPWINDOW
		!
		! store the window at the time of the trap into a static area.
		!
		set	_trap_window, %g1
		mov	%wim, %g2
		st	%g2, [%g1+96]
		mov	%psr, %g2
		restore
		st %o0, [%g1]; st %o1, [%g1+4]; st %o2, [%g1+8]; st %o3, [%g1+12]
		st %o4, [%g1+16]; st %o5, [%g1+20]; st %o6, [%g1+24]; st %o7, [%g1+28]
		st %l0, [%g1+32]; st %l1, [%g1+36]; st %l2, [%g1+40]; st %l3, [%g1+44]
		st %l4, [%g1+48]; st %l5, [%g1+52]; st %l6, [%g1+56]; st %l7, [%g1+60]
		st %i0, [%g1+64]; st %i1, [%g1+68]; st %i2, [%g1+72]; st %i3, [%g1+76]
		st %i4, [%g1+80]; st %i5, [%g1+84]; st %i6, [%g1+88]; st %i7, [%g1+92]
		mov	%g2, %psr
		nop; nop; nop;
		btst	%l5, %l3		! retest
	#endif TRAPWINDOW
		st	%fp, [%l7 + MINFRAME + SP*4] ! stack pointer
		st	%l0, [%l7 + MINFRAME + PSR*4] ! psr
		st	%l1, [%l7 + MINFRAME + PC*4] ! pc
		!
		! If we are in last trap window, all windows are occupied and
		! we must do window overflow stuff in order to do further calls
		!
		bz	st_have_window		! if ((CWM&WIM)==0) no overflow
		st	%l2, [%l7 + MINFRAME + nPC*4] ! npc
		b,a	st_sys_ovf

	st_user:
		!
		! Trap from user. Save user globals and prepare system stack.
		! Test whether the current window is the last available window
		! in the register file (CWM == WIM).
		!
		set	(_u+KERNSTACK-SA(MINFRAME+REGSIZE)), %l7 ! setup kernel stack
		SAVE_GLOBALS(%l7 + MINFRAME)
		SAVE_OUTS(%l7 + MINFRAME)
		st	%l0, [%l7 + MINFRAME + PSR*4] ! psr
		st	%l1, [%l7 + MINFRAME + PC*4] ! pc
		st	%l2, [%l7 + MINFRAME + nPC*4] ! npc
		!
		! If we are in last trap window, all windows are occupied and
		! we must do window overflow stuff in order to do further calls
		!
		bz	1f			! if ((CWM&WIM)==0) no overflow
		clr	[_u+PCB_WBCNT]		! delay slot, save buffer ptr = 0

		not	%l5, %l6		! UWM = ~CWM
		and	%l6, WINMASK, %l6
		b	st_user_ovf		! overflow
		srl	%l3, 1, %g1		! delay slot,WIM = %g1 = ror(WIM, 1, NW)

		!
		! Compute the user window mask (u.u_pcb.pcb_uwm), which is a mask of
		! window which contain user data. It is all the windows "between"
		! CWM and WIM.
		!
	1:
		subcc	%l3, %l5, %g1		! if (WIM >= CWM)
		bneg,a	2f			!    u.u_pcb.pcb_uwm = (WIM-CWM)&~CWM
		sub	%g1, 1, %g1		! else
	2:					!    u.u_pcb.pcb_uwm = (WIM-CWM-1)&~CWM
		bclr	%l5, %g1
		and	%g1, WINMASK, %g1
		st	%g1, [_u+PCB_UWM]

	st_have_window:
		!
		! The next window is open.
		!
		mov	%l7, %sp		! setup previously computed stack
		!
		! Process trap according to type
		!
		btst	T_INTERRUPT, %l4	! interrupt
		bnz	interrupt
		cmp	%l4, T_SYSCALL		! syscall
		be	syscall
		btst	T_FAULT, %l4		! fault
		bnz,a	fault
		bclr	T_FAULT, %l4
		cmp	%l4, T_FP_EXCEPTION	! floating point exception
		be	_fp_exception
		cmp	%l4, T_FP_DISABLED	! fpu is disabled
		be	_fp_disabled
		cmp	%l4, T_FLUSH_WINDOWS	! flush user windows to stack
		bne	1f
		wr	%l0, PSR_ET, %psr	! enable traps

		!
		! Flush windows trap.
		!
		call	_flush_user_windows	! flush user windows
		nop
		!
		! Don't redo trap instruction.
		!
		ld	[%sp + MINFRAME + nPC*4], %g1
		st	%g1, [%sp + MINFRAME + PC*4]  ! pc = npc
		add	%g1, 4, %g1
		b	sys_rtt
		st	%g1, [%sp + MINFRAME + nPC*4] ! npc = npc + 4

	1:
		!
		! All other traps. Call C trap handler.
		!
		mov	%l4, %o0		! trap(t, rp)
		call	_trap			! C trap handler
		add	%sp, MINFRAME, %o1
		b,a	sys_rtt			! return from trap
	/* end systrap */

	/*
	 * Sys_trap overflow handling.
	 * Psuedo subroutine returns to st_have_window.
	 */
	st_sys_ovf:
		!
		! Overflow from system.
		! Determine whether the next window is a user window.
		! If u.u_pcb.pcb_uwm has any bits set, then it is a user window
		! which must be saved.
		!
		ld	[_u+PCB_UWM], %l6	! if (u.u_pcb.pcb_uwm)
		tst	%l6			!	user window
		bnz	st_user_ovf
		srl	%l3, 1, %g1		! delay slot,WIM = %g1 = ror(WIM, 1, NW)
		!
		! Save supervisor window. Compute the new WIM and change current window
		! to the window to be saved.
		!
		sll	%l3, NW-1, %l3
		or	%l3, %g1, %g1
		save				! get into window to be saved
		mov	%g1, %wim		! install new WIM
		!
		! Save window on the stack.
		!
	st_stack_res:
		SAVE_WINDOW(%sp)
		b	st_have_window		! finished overflow processing
		restore				! delay slot, back to original window

	st_user_ovf:
		!
		! Overflow. Window to be saved is a user window.
		! Compute the new WIM and change the current window to the
		! window to be saved.
		!
		sll	%l3, NW-1, %l3
		or	%l3, %g1, %g1
		bclr	%g1, %l6		! turn off uwm bit for window
		st	%l6, [_u+PCB_UWM]	! we are about to save
		save				! get into window to be saved
		mov	%g1, %wim		! install new WIM
		!
		! We must check whether the user stack is resident where the window
		! will be saved, which is pointed to by the window's sp.
		! We must also check that the sp is aligned to a word boundary.
		! Normally, we would check the alignment, and then probe the top
		! and bottom of the save area on the stack. However we optimize
		! this by checking that both ends of the save area are within a
		! 4k unit (the biggest mask we can generate in one cycle), and
		! the alignment in one shot. This allows us to do one probe to
		! the page map. NOTE: this assumes a page size of at least 4k.
		!
		and	%sp, 0xfff, %g1
		add	%g1, (14*4), %g1
		andncc	%g1, 0xff8, %g0
		bz,a	st_sp_bot
		lda	[%sp]ASI_PM, %g1	! check for stack page resident
		!
		! Stack is either misaligned or crosses a 4k boundary.
		!
		btst	0x7, %sp		! test sp alignment
		bz	st_sp_top
		add	%sp, (14*4), %g1	! delay slot, check top of save area

	#ifdef PARTIAL_ALIGN
		.global	_partial_align
		.seg	"data"
		.align	4
	_partial_align:
		.word	0
		.seg	"text"

		sethi	%hi(_partial_align), %g1 ! check for partial align enabled
		ld	[%g1 + %lo(_partial_align)], %g1
		tst	%g1
		bz	st_stack_not_res
		.empty

		and	%sp, 0xfff, %g1
		add	%g1, (15*4), %g1
		andncc	%g1, 0xffc, %g0
		bz,a	stp_sp_bot
		lda	[%sp]ASI_PM, %g1	! check for stack page resident

		btst	0x3, %sp		! test sp alignment
		bz	stp_sp_top
		add	%sp, (15*4), %g1	! delay slot, check top of save area

		b,a	st_stack_not_res	! stack misaligned, catch it later

	stp_sp_top:
		lda	[%g1]ASI_PM, %g1	! get pme for this address
		srl	%g1, PG_S_BIT, %g1	! get vws bits
		cmp	%g1, PROT		! look for valid, writeable, user
		be,a	stp_sp_bot
		lda	[%sp]ASI_PM, %g1	! delay slot, check bottom of save area

		b,a	st_stack_not_res	! stack not resident, catch it later

	stp_sp_bot:
		srl	%g1, PG_S_BIT, %g1	! get vws bits
		cmp	%g1, PROT		! look for valid, writeable, user
		bne	st_stack_not_res
		nop

		SAVE_WINDOW_S(%sp)
		b	st_have_window		! finished overflow processing
		restore				! delay slot, back to original window
	#endif PARTIAL_ALIGN

		b,a	st_stack_not_res	! stack misaligned, catch it later

	st_sp_top:
		lda	[%g1]ASI_PM, %g1	! get pme for this address
		srl	%g1, PG_S_BIT, %g1	! get vws bits
		cmp	%g1, PROT		! look for valid, writeable, user
		be,a	st_sp_bot
		lda	[%sp]ASI_PM, %g1	! delay slot, check bottom of save area

		b,a	st_stack_not_res	! stack not resident, catch it later

	st_sp_bot:
		srl	%g1, PG_S_BIT, %g1	! get vws bits
		cmp	%g1, PROT		! look for valid, writeable, user
		be	st_stack_res
		nop				! extra nop in rare case

	st_stack_not_res:
		!
		! User stack is not resident, save in u area for processing in sys_rtt.
		!
		ld	[_u+PCB_WBCNT], %g1
		sll	%g1, 2, %g1		! convert to spbuf offset
		st	%sp, [%g1 + _u+PCB_SPBUF] ! save sp
		sll	%g1, 4, %g1		! convert wbcnt to pcb_wbuf offset
		SAVE_WINDOW(%g1 + _u+PCB_WBUF)
		srl	%g1, 6, %g1		! increment u.u_pcb.pcb_wbcnt
		add	%g1, 1, %g1
		st	%g1, [_u+PCB_WBCNT]
		b	st_have_window		! finished overflow processing
		restore				! delay slot, back to original window
	/* end sys_trap overflow */

	#ifdef STREAMS
		.global _qrunflag, _queueflag, _queuerun
	#endif STREAMS

	/*
	 * Return from sys_trap routine.
	 */
		.global	sys_rtt
	sys_rtt:
		!
		! Return from trap.
		!
		ld	[%sp + MINFRAME + PSR*4], %l0 ! get saved psr
		btst	PSR_PS, %l0		! test pS for return to supervisor
		bnz	sr_sup
		mov	%psr, %g1

	sr_user:
		!
		! Return to user. Turn off traps using the current CWP (because
		! we are returning to user). Test for streams actions.
		! Test for AST for resched. or prof.
		!
		and	%g1, PSR_CWP, %g1	! insert current CWP in old psr
		andn	%l0, PSR_CWP, %l0
		or	%l0, %g1, %l0
		mov	%l0, %psr		! install old psr, disable traps
		nop; nop; nop;			! psr delay
	#ifdef STREAMS
		sethi	%hi(_qrunflag), %g1
		ldub	[%g1 + %lo(_qrunflag)], %g1
		sethi	%hi(_queueflag), %l5	! interlock slot
		tst	%g1			! need to run stream queues?
		bz,a	3f			! no
		ld	[_u+PCB_P0LR], %g1	! delay slot, test for ast.
		ldub	[%l5 + %lo(_queueflag)], %g1
		tst	%g1			! already running queues?
		bnz,a	3f			! yes
		ld	[_u+PCB_P0LR], %g1	! delay slot, test for ast.
		!
		! Run the streams queues.
		!
		or	%l0, PSR_PIL, %l0	! spl hi
		mov	%l0, %psr		! change priority (IU bug)
		wr	%l0, PSR_ET, %psr	! turn on traps
		add	%g1, 1, %g1		! mark that we're running the queues
		call	_queuerun
		stb	%g1, [%l5 + %lo(_queueflag)]

		clrb	[%l5 + %lo(_queueflag)] ! done running queues
		b,a	sys_rtt

	3:
	#else STREAMS
		ld	[_u+PCB_P0LR], %g1
	#endif STREAMS
		srl	%g1, AST_SCHED_BIT, %g1
		btst	1, %g1
		bz,a	1f
		ld	[_u+PCB_WBCNT], %g3	! delay slot, user regs been saved?

		!
		! Let trap handle the AST.
		!
		mov	%l0, %psr		! in case of changed priority (IU bug)
		wr	%l0, PSR_ET, %psr	! turn on traps
		mov	T_AST, %o0
		call	_trap			! trap(T_AST, rp)
		add	%sp, MINFRAME, %o1
		b,a	sys_rtt

	1:
		!
		! If user regs have been saved to the window buffer we must clean it.
		!
		tst	%g3
		bz,a	2f
		ld	[_u+PCB_UWM], %l4	! delay slot, user windows in reg file?

		!
		! User regs have been saved into the u area.
		! Let trap handle putting them on the stack.
		!
		mov	%l0, %psr		! in case of changed priority (IU bug)
		wr	%l0, PSR_ET, %psr	! turn on traps
		mov	T_WIN_OVERFLOW, %o0
		call	_trap			! trap(T_WIN_OVERFLOW, rp)
		add	%sp, MINFRAME, %o1
		b,a	sys_rtt

	2:
		!
		! We must insure that the rett will not take a window underflow trap.
		!
		RESTORE_OUTS(%sp + MINFRAME)	! restore user outs
		tst	%l4
		bnz	sr_user_regs
		ld	[%sp + MINFRAME + PC*4], %l1 ! restore user pc

		!
		! The user has no windows in the register file.
		! Try to get one from his stack.
		!
		mov	%wim, %l3		! get wim
		sll	%l3, 1, %l4		! next WIM = rol(WIM, 1, NW)
		srl	%l3, NW-1, %l5
		or	%l5, %l4, %l5
		mov	%l5, %wim		! install it
		!
		! Normally, we would check the alignment, and then probe the top
		! and bottom of the save area on the stack. However we optimize
		! this by checking that both ends of the save area are within a
		! 4k unit (the biggest mask we can generate in one cycle), and
		! the alignment in one shot. This allows us to do one probe to
		! the page map. NOTE: this assumes a page size of at least 4k.
		!
		and	%fp, 0xfff, %g1
		add	%g1, (14*4), %g1
		andncc	%g1, 0xff8, %g0
		bz,a	sr_sp_bot
		lda	[%fp]ASI_PM, %g2	! check for stack page resident
		!
		! Stack is either misaligned or crosses a 4k boundary.
		!
		btst	0x7, %fp		! test fp alignment
		bz	sr_sp_top
		add	%fp, (14*4), %g1	! delay slot, check top of save area

	#ifdef PARTIAL_ALIGN
		sethi	%hi(_partial_align), %g1 ! check for partial align enabled
		ld	[%g1 + %lo(_partial_align)], %g1
		tst	%g1
		bz	srp_align
		.empty

		and	%fp, 0xfff, %g1
		add	%g1, (15*4), %g1
		andncc	%g1, 0xffc, %g0
		bz,a	srp_sp_bot
		lda	[%fp]ASI_PM, %g2	! check for stack page resident

		btst	0x3, %fp		! test fp alignment
		bz	srp_sp_top
		add	%fp, (15*4), %g1	! delay slot, check top of save area

		b,a	srp_align

	srp_sp_top:
		lda	[%g1]ASI_PM, %g2	! get pme for this address
		srl	%g2, PG_S_BIT, %g2	! get vws bits
		cmp	%g2, PROT		! look for valid, writeable, user
		be,a	srp_sp_bot
		lda	[%fp]ASI_PM, %g2	! delay slot, check bottom of save area

		b,a	sr_stack_not_res	! stack page not resident

	srp_sp_bot:
		srl	%g2, PG_S_BIT, %g2	! get vws bits
		cmp	%g2, PROT		! look for valid, writeable, user
		be,a	srp_stack_res
		restore

		b	sr_stack_not_res	! stack page not resident
		mov	%fp, %g1		! save fault address

	srp_stack_res:
		RESTORE_WINDOW_S(%sp)
		b	sr_user_regs
		save				! get back to original window

	srp_align:
	#endif PARTIAL_ALIGN

		!
		! A user underflow with a misaligned sp.
		! Fake a memory alignment trap.
		!
		mov	%l3, %wim		! restore old wim
		mov	%l0, %psr		! in case of changed priority (IU bug)
		wr	%l0, PSR_ET, %psr	! turn on traps
		mov	T_ALIGNMENT, %o0
		call	_trap			! trap(T_ALIGNMENT, rp)
		add	%sp, MINFRAME, %o1
		b,a	sys_rtt

	sr_sp_top:
		lda	[%g1]ASI_PM, %g2	! get pme for this address
		srl	%g2, PG_S_BIT, %g2	! get vws bits
		cmp	%g2, PROT		! look for valid, writeable, user
		be,a	sr_sp_bot
		lda	[%fp]ASI_PM, %g2	! delay slot, check bottom of save area

		b,a	sr_stack_not_res	! stack page not resident

	sr_sp_bot:
		srl	%g2, PG_S_BIT, %g2	! get vws bits
		cmp	%g2, PROT		! look for valid, writeable, user
		be,a	sr_stack_res
		restore				! get into window to be restored

		mov	%fp, %g1		! save fault address
	sr_stack_not_res:
		!
		! Restore area on user stack is not resident.
		! We punt and fake a page fault so that trap can bring the page in.
		!
		mov	%l3, %wim		! restore old wim
		mov	%l0, %psr		! in case of changed priority (IU bug)
		wr	%l0, PSR_ET, %psr	! enable traps
		add	%sp, MINFRAME, %o1
		mov	%g1, %o2
		mov	BE_INVALID, %o3		! was stack protected or invalid?
		btst	P_INVAL, %g2
		bnz,a	1f
		mov	BE_PROTERR, %o3
	1:
		call	_trap			! trap(T_DATA_FAULT, rp, addr, be)
		mov	T_DATA_FAULT, %o0

		b,a	sys_rtt

	sr_stack_res:
		!
		! Resident user window. Restore window from stack
		!
		RESTORE_WINDOW(%sp)
		save				! get back to original window

	sr_user_regs:
		!
		! User has at least one window in the register file.
		!
		ld	[_u+PCB_FLAGS], %l3
		ld	[%sp + MINFRAME + nPC*4], %l2 ! user npc
		btst	CLEAN_WINDOWS, %l3
		bz,a	3f
		mov	%l0, %psr		! install old PSR_CC

		!
		! Maintain clean windows.
		!
		mov	%wim, %g2		! put wim in global
		mov	0, %wim			! zero wim to allow saving
		mov	%l0, %g3		! put original psr in global
		b	2f			! test next window for invalid
		save
		!
		! Loop through windows past the trap window
		! clearing them until we hit the invlaid window.
		!
	1:
		clr	%l1			! clear the window
		clr	%l2
		clr	%l3
		clr	%l4
		clr	%l5
		clr	%l6
		clr	%l7
		clr	%o0
		clr	%o1
		clr	%o2
		clr	%o3
		clr	%o4
		clr	%o5
		clr	%o6
		clr	%o7
		save
	2:
		mov	%psr, %g1		! get CWP
		srl	%g2, %g1, %g1		! test WIM bit
		btst	1, %g1
		bz,a	1b			! not invalid window yet
		clr	%l0			! clear the window

		!
		! Clean up trap window.
		!
		mov	%g3, %psr		! back to trap window, restore PSR_CC
		mov	%g2, %wim		! restore wim
		nop; nop;			! psr delay
		RESTORE_GLOBALS(%sp + MINFRAME)	! restore user globals
		mov	%l1, %o6		! put pc, npc in unobtrusive place
		mov	%l2, %o7
		clr	%l0			! clear the rest of the window
		clr	%l1
		clr	%l2
		clr	%l3
		clr	%l4
		clr	%l5
		clr	%l6
		clr	%l7
		clr	%o0
		clr	%o1
		clr	%o2
		clr	%o3
		clr	%o4
		clr	%o5
		jmp	%o6			! return
		rett	%o7
	3:
		RESTORE_GLOBALS(%sp + MINFRAME)	! restore user globals
		jmp	%l1			! return
		rett	%l2
		.empty

	sr_sup:
		!
		! Returning to supervisor.
		! We will restore the trap psr. This has the effect of disabling
		! traps and changing the CWP back to the original trap CWP. This
		! completely restores the PSR so that if we get a trap between a
		! rdpsr and a wrpsr its OK. We only do this for supervisor return
		! since users can't manipulate the psr.
		!
	ld	[%sp + MINFRAME + SP*4], %fp ! get sys sp
		xor	%g1, %l0, %g1		! test for CWP change
		btst	PSR_CWP, %g1
		bz,a	sr_samecwp
		mov	%l0, %psr		! install old psr, disable traps
		!
		! The CWP will be changed. We must save sp and the ins
		! and recompute WIM. We know we need to restore the next
		! window in this case.
		!
		mov	%l0, %g3		! save old psr
		mov	%sp, %g4		! save sp, ins for new window
		std	%i0, [%sp +(8*4)]	! normal stack save area
		std	%i2, [%sp +(10*4)]
		std	%i4, [%sp +(12*4)]
		std	%i6, [%sp +(14*4)]
		mov	%g3, %psr		! old psr, disable traps, CWP, PSR_CC
		mov	0x4, %g1		! psr delay, compute mask for CWP + 2
		sll	%g1, %g3, %g1		! psr delay, won't work for NW == 32
		srl	%g1, NW, %g2		! psr delay
		or	%g1, %g2, %g1
		mov	%g1, %wim		! install new wim
		mov	%g4, %sp		! reestablish sp
		ldd	[%sp + (8*4)], %i0	! reestablish ins
		ldd	[%sp + (10*4)], %i2
		ldd	[%sp + (12*4)], %i4
		ldd	[%sp + (14*4)], %i6
		restore				! restore return window
		RESTORE_WINDOW(%sp)
		b	sr_out
		save

	sr_samecwp:
		!
		! There is no CWP change.
		! We must make sure that there is a window to return to.
		!
		mov	0x2, %g1		! compute mask for CWP + 1
		sll	%g1, %l0, %g1		! XXX won't work for NW == 32
		srl	%g1, NW, %g2
		or	%g1, %g2, %g1
		mov	%wim, %g2		! cmp with wim to check for underflow
		btst	%g1, %g2
		bz	sr_out
		mov	%l0, %psr		! install old PSR_CC
		!
		! No window to return to. Restore it.
		!
		sll	%g2, 1, %g1		! compute new WIM = rol(WIM, 1, NW)
		srl	%g2, NW-1, %g2
		or	%g1, %g2, %g1
		mov	%g1, %wim		! install it
		nop; nop; nop;			! wim delay
		restore				! get into window to be restored
		RESTORE_WINDOW(%sp)
		save				! get back to original window
	sr_out:
		RESTORE_GLOBALS(%sp + MINFRAME)	! restore system globals
		ld	[%sp + MINFRAME + PC*4], %l1 ! delay slot, restore sys pc
		ld	[%sp + MINFRAME + nPC*4], %l2 ! sys npc
		jmp	%l1			! return to trapped instruction
		rett	%l2
		.empty
	/* end sys_rtt*/

	/*
	 * System call handler.
	 */
	syscall:
		wr	%l0, PSR_ET, %psr	! enable traps (no priority change)
		nop				! psr delay
		call	_syscall		! syscall(rp)
		add	%sp, MINFRAME, %o0	! ptr to reg struct
		b,a	sys_rtt
	/* end syscall */

	/*
	 * Fault handler.
	 */
	fault:
	#ifdef notdef
	/* enable this code when MEMERR_ADDR fix is in */
		cmp	%l4, T_TEXT_FAULT	! text fault?
		be,a	1f
		mov	%l1, %o2		! pc is text fault address
	#endif notdef
		set	MEMERR_ADDR, %g1
		ld	[%g1 + ME_VADDR], %o2	! get data fault address in mem err reg
		ld	[%g1], %g2		! is this a memory error or a fault?
		btst	ER_INTR, %g2
		bz,a	1f			! memory error
		clr	[%g1 + ME_VADDR]	! clear fault address

		!
		! Memory error. Enable traps to let interrupt happen.
		!
		wr	%l0, PSR_ET, %psr
		nop				! psr delay
		b	sys_rtt			! return from trap
		nop
	1:
	/* XXX remove from here ... */
		cmp	%l4, T_TEXT_FAULT	! text fault?
		be,a	2f
		mov	%l1, %o2		! pc is text fault address
	2:
	/* ... to here when MEMERR_ADDR fix is in */
		set	BUSERRBASE, %g2		! get bus error reg before trap enable
		lduba	[%g2]ASI_CTL, %o3
		wr	%l0, PSR_ET, %psr	! enable traps (no priority change)
		mov	%l4, %o0		! trap(t, rp, addr, be)
		call	_trap			! C trap handler
		add	%sp, MINFRAME, %o1
		b,a	sys_rtt			! return from trap
	/* end fault */

	/*
	 * Interrupt vector table
	 */
		.seg	"data"
		.align	4
		!
		! all interrupts are vectored via the following table
		! we can't vector directly from the scb because
		! we haven't done window setup
		!
		.global _int_vector
	_int_vector:
		.word	_spurious	! level 0, should not happen
		.word	level1		! level 1, softint, interrupt enable register 1
		.word	(1<<1)		! level 2, vme level 1
		.word	(2<<1)		! level 3, vme level 2
		.word	level4		! level 4, SCSI or IE register 2
		.word	(3<<1)		! level 5, vme level 3
		.word	level6		! level 6, ethernet or IE register 3
		.word	(4<<1)		! level 7, vme level 4
		.word	_spurious	! level 8, unused
		.word	(5<<1)		! level 9, vme level 5
		.word	level10		! level 10, normal clock
		.word	(6<<1)		! level 11, vme level 6
	#ifdef SAS
		.word	_cnintr		! sas console interrupt
	#else
		.word	_zslevel12	! level 12, scc - serial i/o
	#endif SAS
		.word	(7<<1)		! level 13, vme level 7
		.word	_spurious	! kprof (not done here) / monitor nmi
		.word	memory_err	! level 15, memory error
		.seg	"text"

	/*
	 * Generic interrupt handler.
	 */
		.global	interrupt
	interrupt:
		set	eintstack, %g1		! on interrupt stack?
		cmp	%sp, %g1
		bgu,a	1f
		sub	%g1, SA(MINFRAME), %sp	! get on it.
	1:
		andn	%l0, PSR_PIL, %l5	! compute new psr with proper PIL
		and	%l4, T_INT_LEVEL, %l4
		sll	%l4, PSR_PIL_BIT, %g1
		or	%l5, %g1, %l0
		!
		! If we just took a memory error we don't want to turn interrupts
		! on just yet in case there is another memory error waiting in the
		! wings. So disable interrupts if the PIL is 15.
		!
		cmp	%g1, PSR_PIL
		bne	2f
		sll	%l4, 2, %l6		! convert level to word offset
		mov	IR_ENA_INT, %o0
		call	_set_intreg
		mov	0, %o1
	2:
		!
		! Get handler address for level.
		!
		set	_int_vector, %g1
		ld	[%g1 + %l6], %l3	! grab vector
		!
		! no code can exist at a low address (< 8192)
		! so we use this to weed out vme generated interrupts
		! the vector will be the vme level that we should
		! acknowledge for the current interrupt if it is a vme
		! interrupt otherwise we get an interrupt routine address
		!
		cmp	%l3, 16			! is this a vme generated interrupt?
		blu,a	vme_interrupt
		mov	(14<<PSR_PIL_BIT), %g1
		!
		! On board interrupt.
		! Due to a bug in the IU, we cannot increase the PIL and
		! enable traps at the same time. In effect, ET changes
		! slightly before the new PIL becomes effective.
		! So we write the psr twice, once to set the PIL and
		! once to set ET.
		!
		mov	%l0, %psr		! set level (IU bug)
		wr	%l0, PSR_ET, %psr	! enable traps
		nop
		call	%l3			! non-vme interrupt
		nop

	int_rtt:
		set	_cnt+V_INTR, %g2	! cnt.v_intr++
		ld	[%g2], %g1
		inc	%g1
		st	%g1, [%g2]
		b	sys_rtt			! restore previous stack pointer
		mov	%l7, %sp		! reset stack pointer
	/* end interrupt */

	/*
	 * VME interrupt. Do vectoring.
	 * The VME vector is read off the VME bus. If this fails (data fault),
	 * _trap will check the PC of the fault and jump to _spurious.
	 */
		.global	vme_interrupt, _vme_read_vector
	vme_interrupt:
		or	%l5, %g1, %l5		! spl 14
		mov	%l5, %psr		! set level (IU bug)
		wr	%l5, PSR_ET, %psr	! enable traps
		mov	-1, %l5			! bad vector value for _spurious
		set	VMEINTVECBASE, %g1	! get VME vector from the bus
		or	%g1, %l3, %g1		! create address to read in alt space
		or	%g1, 1, %g1		! a0 should always be a 1
	_vme_read_vector:
		lduba	[%g1]ASI_CTL, %l5	! read vector number, acknowledge int.
		wr	%l0, PSR_ET, %psr	! set proper interrupt level
		cmp	%l5, VEC_MAX		! check vector limits
		bg	_spurious
		subcc	%l5, VEC_MIN, %g3	! normalize vector
		bl	_spurious
		sll	%g3, 3, %g3		! scale vector
		set	_vme_vector, %g2
		ld	[%g2 + %g3], %g1	! get handler address
		add	%g2, 4, %g2		! generate address of arg ptr
		ld	[%g2 + %g3], %o0	! delay slot, get arg ptr
		call	%g1			! call handler
		ld	[%o0], %o0		! read arg ptr

		b,a	int_rtt			! restore previous stack pointer
	/* end vme_interrupt */

	/*
	 * Vme vectors are compatible with the sun3 family in which
	 * there were possible valid vectors from 64 to 255 inclusive.
	 * This requires 192 vectors, each vector is two words long
	 * the first word being the interrupt routine address and the
	 * second word is the arg.
	 *
	 * Vectors 0xC8-0xFF (200-255) are reserved for customer use.
	 */

	/*
	 * The vme vectoring uses the following table of routines
	 * and arguments. The values for the vectors in the following
	 * table are loaded by autoconf at boot time.
	 */
	#define ERRV	.word _spurious, 0

		.seg	"data"
		.align	4

		.global	_vme_vector
	_vme_vector:			! vector numbers
		ERRV; ERRV; ERRV; ERRV  ! 0x40 - 0x43  sc0  | sc?
		ERRV; ERRV; ERRV; ERRV  ! 0x44 - 0x47  future disk controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x48 - 0x4B  xyc0 | xyc1 | xyc?
		ERRV; ERRV; ERRV; ERRV  ! 0x4C - 0x4F  future disk controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x50 - 0x53  future disk controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x54 - 0x57  future disk controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x58 - 0x5B  future disk controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x5C - 0x5F  future disk controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x60 - 0x63  tm0  | tm1  | tm?
		ERRV; ERRV; ERRV; ERRV  ! 0x64 - 0x67  xtc0 | xtc1 | xtc?
		ERRV; ERRV; ERRV; ERRV  ! 0x68 - 0x6B  future tape controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x6C - 0x6F  future tape controllers
		ERRV; ERRV; ERRV; ERRV  ! 0x70 - 0x73  ec?
		ERRV; ERRV; ERRV; ERRV  ! 0x74 - 0x77  ie0 | ie1 | ie?
		ERRV; ERRV; ERRV; ERRV  ! 0x78 - 0x7B  future ethernet devices
		ERRV; ERRV; ERRV; ERRV  ! 0x7C - 0x7F  future ethernet devices
		ERRV; ERRV; ERRV; ERRV  ! 0x80 - 0x83  vpc0 | vpc1 | vpc?
		ERRV; ERRV; ERRV; ERRV  ! 0x84 - 0x87  vp?
		ERRV; ERRV; ERRV; ERRV  ! 0x88 - 0x8B  mti0 | mti1 | mti2 | mti3
		ERRV; ERRV; ERRV; ERRV  ! 0x8C - 0x8F  SunLink SCP (Systech DCP-8804)
		ERRV; ERRV; ERRV; ERRV  ! 0x90 - 0x93  Sun-3 zs0 (8 even vectors)
		ERRV; ERRV; ERRV; ERRV  ! 0x94 - 0x97  Sun-3 zs1 (8 odd vectors)
		ERRV; ERRV; ERRV; ERRV  ! 0x98 - 0x9B  Sun-3 zs0 (8 even vectors)
		ERRV; ERRV; ERRV; ERRV  ! 0x9C - 0x9F  Sun-3 zs1 (8 odd vectors)
		ERRV; ERRV; ERRV; ERRV  ! 0xA0 - 0xA3  future serial
		ERRV; ERRV; ERRV; ERRV  ! 0xA4 - 0xA7  pc0  | pc1  | pc2  | pc3
		ERRV; ERRV; ERRV; ERRV  ! 0xA8 - 0xAB  cg2 | future frame buffers
		ERRV; ERRV; ERRV; ERRV  ! 0xAC - 0xAF  gp1 | future graphics processors
		ERRV; ERRV; ERRV; ERRV  ! 0xB0 - 0xB3  sky0 | ?
		ERRV; ERRV; ERRV; ERRV  ! 0xB4 - 0xB7  SunLink / channel attach
		ERRV; ERRV; ERRV; ERRV  ! 0xB8 - 0xBB  (token bus) tbi0 | tbi1 | ?
		ERRV; ERRV; ERRV; ERRV  ! 0xBC - 0xBF  Reserved for Sun
		ERRV; ERRV; ERRV; ERRV  ! 0xC0 - 0xC3  Reserved for Sun
		ERRV; ERRV; ERRV; ERRV  ! 0xC4 - 0xC7  Reserved for Sun
		ERRV; ERRV; ERRV; ERRV  ! 0xC8 - 0xCB  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xCC - 0xCF  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xD0 - 0xD3  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xD4 - 0xD7  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xD8 - 0xDB  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xDC - 0xDF  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xE0 - 0xE3  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xE4 - 0xE7  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xE8 - 0xEB  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xEC - 0xEF  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xF0 - 0xF3  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xF4 - 0xF7  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xF8 - 0xFB  Reserved for User
		ERRV; ERRV; ERRV; ERRV  ! 0xFC - 0xFF  Reserved for User

		.seg	"text"
		.align	4
	/*
	 * Spurious trap... 'should not happen'
	 * %l4 - processor interrupt level
	 * %l3 - vme interrupt level << 1 (or interrupt handler address)
	 * %l5 - vme vector
	 */
		.global	_spurious
	_spurious:
		cmp	%l3, 16			! is this a vme generated interrupt?
		blu	spurious_vme
		nop

		!
		! Spurious on-board interrupt.
		!
		set	1f, %o0
		call	_printf
		mov	%l4, %o1
		b,a	int_rtt
		.seg	"data"
	1:	.asciz	"spurious interrupt at processor level %d\n"
		.seg	"text"

		!
		! Spurious VME interrupt.
		!
	spurious_vme:
		set	2f, %o0
		mov	%l4, %o1
		srl	%l3, 1, %o2
		call	_printf
		mov	%l5, %o3
		b,a	int_rtt
		.seg	"data"
	2:	.asciz	"spurious VME interrupt at processor level %d\nVME level %d, VME vector 0x%x\n"
		.seg	"text"
	/* end spurious */

	/*
	 * Macro for autovectored interrupts.
	 */
	#define IOINTR(LEVEL) \
		set	_level/**/LEVEL/**/_vector,%l5 /* get vector ptr */;\
	1:	ld	[%l5], %g1	/* get routine address (into g1) */;\
		call	.ptr_call	/* go there */;\
		add	%l5, 4, %l5	/* delay slot, next one to try */;\
		tst	%o0		/* success? */;\
		bz	1b		/* no, try next one */;\
		cmp	%o0, -1		/* was it spurious */;\
		be	2f		;\
		.empty			;\
		set	_level/**/LEVEL/**/_spurious,%l1 /* no, clr spurious cnt */;\
		clr	[%l1]		;\
	2:				;\
		b,a	int_rtt		/* done */

	/*
	 * Handle software interrupts
	 * Just call C routine softint - executes all accumulated softcalls
	 */
		.global	level1
	level1:
		mov	%psr, %g2
		or	%g2, PSR_PIL, %g1	! spl hi to protect intreg update
		mov	%g1, %psr
		nop; nop;			! psr delay
		set	INTREG_ADDR, %l1	! interrupt register address
		ldub	[%l1], %g1		! get current setting
		bclr	IR_SOFT_INT1, %g1	! reset level one int request bit
		stb	%g1, [%l1]		! turn off the level one interrupt
		mov	%g2, %psr		! splx
		nop				! psr delay
		call	_softint		! go do accumulated softcalls
		nop

		b,a	int_rtt
	/* end level1 */

	/*
	 * Level 4 interrupts, normally used by SCSI or interrupt register 2
	 */
	level4:
		IOINTR(4)

	/*
	 * Level 6 interrupts, normally used by Ethernet or interrupt register 3
	 */
	level6:
		IOINTR(6)

	/*
	 * LED data for idle pattern of diagnostic register.
	 * NOTE: pattern is sampled at 100hz.
	 */
	LEDTICKS =	33
	LEDPATCNT =	18

		.seg	"data"
	led:
		! LEDPAT
		.byte	0x7e, 0x7e
		.byte	0x7e, 0xbd
		.byte	0xbd, 0xbd
		.byte	0xdb, 0xdb
		.byte	0xdb, 0xe7
		.byte	0xe7, 0xe7
		.byte	0xdb, 0xdb
		.byte	0xdb, 0xbd
		.byte	0xbd, 0xbd
		! end of LEDPAT
		.byte	0		! LEDCNT current count of ticks
		.byte	0		! LEDPTR offset in pattern

	LEDPAT =	0
	LEDCNT =	18
	LEDPTR =	19

		.seg	"text"
	/*
	 * This code assumes that the real time clock interrupts 100 times
	 * per second, for SUN4 we call hardclock at that rate.
	 *
	 * If idle, update the LEDs with new values before calling hardclock so
	 * at least the user can tell that something is still running.
	 */
		.global	level10
	level10:
		mov	%psr, %g3
		or	%g3, PSR_PIL, %g1	! spl hi to protect intreg update
		mov	%g1, %psr
		nop; nop;			! psr delay
	#ifndef SAS
		set	CLOCK_ADDR+CLK_INTRREG, %l5 ! read CLOCK_ADDR->CLK_INTRRER
		ldub	[%l5], %g1		! to clear clock chip
	#endif !SAS
		set	INTREG_ADDR, %g1
		ldub	[%g1], %g2		! read interrupt register
		bclr	IR_ENA_CLK10, %g2
		stb	%g2, [%g1]		! reset interrupt
		bset	IR_ENA_CLK10, %g2
		stb	%g2, [%g1]		! re-enable interrupt
	#ifndef SAS
		!
		! Clear interrupt register again. If we lose
		! an interrupt we will resync later anyway.
		!
		ldub	[%l5], %g1
	#endif !SAS
		mov	%g3, %psr		! restore psr

		!
		! Check if executing in the idle loop.
		! _swtch immediately follows _idle in swtch.s.
		!
	#ifndef SAS
		set	led, %l5		! countdown to next update of LEDs
		set	_swtch, %g1		! end of idle loop
		cmp	%l1, %g1		! if pc >= swtch not in idle loop
		bgeu	4f			! no, don't update LEDs
		.empty				! next instruction ok in delay slot

		set	_idle, %g1		! address of idle loop
		cmp	%l1, %g1		! if pc < idle not in idle loop
		bgeu	1f			! yes, update LEDs
		nop
	4:
		ldub	[%l5 + LEDCNT], %g1
		subcc	%g1, 1, %g1
		bge,a	3f			! not zero, just call hardclock
		stb	%g1, [%l5 + LEDCNT]	! delay, write out new ledcnt
	1:
		mov	LEDTICKS, %g1
		stb	%g1, [%l5 + LEDCNT]

		ldub	[%l5 + LEDPTR], %g1
		ldub	[%l5 + %g1], %g2	! get LED pattern
		subcc	%g1, 1, %g1		! point to next one
		bneg,a	2f
		mov	LEDPATCNT-1, %g1
	2:
		stb	%g1, [%l5 + LEDPTR]	! update pattern pointer
		set	DIAGREG, %g1
		stba	%g2, [%g1]ASI_CTL	! write led pattern to LEDs
	#endif !SAS
	3:
		call	_syncfpu		! take any pending fp exceptions
		nop
		ld	[%l7 + MINFRAME + PC*4], %o0 ! pc
		call	_hardclock
		ld	[%l7 + MINFRAME + PSR*4], %o1 ! psr

		b,a	int_rtt			! return to restore previous stack
	/* end level10 */

	/*
	 * Level 14 interrupts can be caused by the clock when
	 * kernel profiling is enabled. It is handled immediately
	 * in the trap window.
	 */
	#ifdef GPROF
		.global	test_prof
	test_prof:
		sethi	%hi(CLOCK_ADDR+CLK_INTRREG), %l3 ! read CLOCK_ADDR->CLK_INTRRER
		ldub	[%l3 + %lo(CLOCK_ADDR+CLK_INTRREG)], %l3 ! to clear clock chip
		sethi	%hi(INTREG_ADDR), %l3
		ldub	[%l3 + %lo(INTREG_ADDR)], %l4 ! reset interrupt reg bit
		bclr	IR_ENA_CLK14, %l4
		stb	%l4, [%l3 + %lo(INTREG_ADDR)]
		bset	IR_ENA_CLK14, %l4	! reenable interrupt
		stb	%l4, [%l3 + %lo(INTREG_ADDR)]
		sethi	%hi(_mon_nmi_on), %l3	! see if profiling is enabled
		ldub	[%l3 + %lo(_mon_nmi_on)], %l3
		tst	%l3
		bz	kprof			! profiling on, do it.
		nop
		b	sys_trap		! do normal interrupt processing
		mov	(T_INTERRUPT | 14), %l4
	#endif GPROF

	/*
	 * Level 15 interrupts can only be caused by parity/ECC errors.
	 * This is fatal if the error is an incorrectable ECC or parity.
	 * If the error is correctable ECC, memerr will eventually return
	 * after logging some diagnostic information.
	 */
		.global	memory_err
	memory_err:
		set	MEMERR_ADDR, %g1	! get address of mem err reg
		ld	[%g1], %g2		! read it
		btst	ER_INTR, %g2		! valid memory interrupt pending?
		bz	1f
		nop
		call	_memerr			! sometimes returns
		nop
		b,a	3f
	1:
		sethi	%hi(2f), %o0		! print stray interrupt message
		call	_printf			! print a message to the console
		or	%o0, %lo(2f), %o0
	3:
		mov	IR_ENA_INT, %o0		! reenable interrupts
		call	_set_intreg
		mov	1, %o1
		b,a	int_rtt

		.seg	"data"
	2:	.asciz	"stray level 15 interrupt\n"
		.seg	"text"
	/* end level15 */

	/*
	 * Turn on a software interrupt (H/W level 1).
	 */
		ENTRY(siron)
		mov	IR_SOFT_INT1, %o0
		b	_set_intreg
		mov	1, %o1
	/* end siron */

	/*
	 * Enable and disable video interrupt.
	 * setintrenable(value)
	 *	int value;		0 = off, otherwise on
	 */
		ENTRY(setintrenable)
		mov	%o0, %o1
		b	_set_intreg
		mov	IR_ENA_VID8, %o0
	/* end setintrenable */

	/*
	 * Turn on or off bits in the interrupt register.
	 * We must lock out interrupts, since we don't have an atomic or/and to mem.
	 * set_intreg(bit, value)
	 *	int bit;		bit mask in interrupt reg
	 *	int flag;		0 = off, otherwise on
	 */
		ENTRY(set_intreg)
		mov	%psr, %g2
		or	%g2, PSR_PIL, %g1	! spl hi to protect intreg update
		mov	%g1, %psr
		nop;				! psr delay
		tst	%o1
		set	INTREG_ADDR, %o2	! interrupt register address
		ldub	[%o2], %g1		! read interrupt register
		bnz,a	1f
		bset	%o0, %g1		! on
		bclr	%o0, %g1		! off
	1:
		stb	%g1, [%o2]		! request a level 1 interrupt
		mov	%g2, %psr		! splx
		nop				! psr delay
		retl
		nop
	/* end set_intreg */

	/*
	 * return 1 if an interrupt is being serviced (on interrupt stack),
	 * otherwise return 0.
	 */
		ENTRY(intsvc)
		clr	%o0			! assume no
		set	eintstack, %g1
		cmp	%sp, %g1		! check if on int stack
		bleu,a	1f			! no, we annul the next instruction
		mov	1, %o0			! yes we are
	1:
		retl				! leaf routine return
		nop
	/* end intsvc */

	/*
	 * Flush all windows to memory, except for the one we entered in.
	 * We do this by doing NWINDOW-2 saves then the same number of restores.
	 * This leaves the WIM immediately before window entered in.
	 * This is used for context switching.
	 * NOTE: if NWINDOWS isn't right this will be undefined.
	 */
	#if NWINDOW == 7
		ENTRY(flush_windows)
		save	%sp, -WINDOWSIZE, %sp
		save	%sp, -WINDOWSIZE, %sp
		save	%sp, -WINDOWSIZE, %sp
		save	%sp, -WINDOWSIZE, %sp
		save	%sp, -WINDOWSIZE, %sp
		restore
		restore
		restore
		restore
		ret
		restore
	#endif

	/*
	 * flush user windows to memory.
	 */
		ENTRY(flush_user_windows)
		ld	[_u+PCB_UWM], %g1	! get user window mask
		tst	%g1			! do save until mask is zero
		bz	3f
		clr	%g2
	1:
		save	%sp, -WINDOWSIZE, %sp
		ld	[_u+PCB_UWM], %g1	! get user window mask
		tst	%g1			! do save until mask is zero
		bnz	1b
		add	%g2, 1, %g2
	2:
		subcc	%g2, 1, %g2		! restore back to orig window
		bnz	2b
		restore
	3:
		retl
		nop

	/*
	 * Throw out any user windows in the register file.
	 * Used by setregs (exec) to clean out old user.
	 * Used by sigcleanup to remove extraneous windows when returning from a
	 * signal.
	 */
		ENTRY(trash_user_windows)
		ld	[_u+PCB_UWM], %g1	! get user window mask
		tst	%g1
		bz	3f			! user windows?
		nop
		!
		! There are old user windows in the register file. We disable traps
		! and increment the WIM so that we don't overflow on these windows.
		! Also, this sets up a nice underflow when first returning to the
		! new user.
		!
		mov	%psr, %g4
		or	%g4, PSR_PIL, %g1	! spl hi to prevent interrupts
		mov	%g1, %psr
		nop; nop; nop;			! psr delay
		ld	[_u+PCB_UWM], %g1	! get user window mask
		b	2f
		clr	[_u+PCB_UWM]		! throw user windows away

	1:
		srl	%g2, 1, %g3		! next WIM = ror(WIM, 1, NW)
		sll	%g2, NW-1, %g2
		or	%g2, %g3, %g2
		mov	%g2, %wim		! install wim
		bclr	%g2, %g1		! clear bit from UWM
	2:
		tst	%g1			! more user windows?
		bnz,a	1b
		mov	%wim, %g2		! get wim

		mov	%g4, %psr		! enable traps
		nop				! psr delay
	3:
		retl
		clr	[_u+PCB_WBCNT]		! zero window buffer cnt

	/*
	 * Clean out register file.
	 */
	clean_windows:
		ld	[_u+PCB_FLAGS], %l4	! set CLEAN_WINDOWS in pcb_flags
		mov	%wim, %l3
		bset	CLEAN_WINDOWS, %l4
		st	%l4, [_u+PCB_FLAGS]
		srl	%l3, %l0, %l3		! test WIM bit
		btst	1, %l3
		bnz,a	cw_out			! invalid window, just return
		mov	%l0, %psr		! restore PSR_CC

		mov	%g1, %l5		! save some globals
		mov	%g2, %l6
		mov	%g3, %l7
		mov	%wim, %g2		! put wim in global
		mov	0, %wim			! zero wim to allow saving
		mov	%l0, %g3		! put original psr in global
		b	2f			! test next window for invalid
		save
		!
		! Loop through windows past the trap window
		! clearing them until we hit the invlaid window.
		!
	1:
		clr	%l1			! clear the window
		clr	%l2
		clr	%l3
		clr	%l4
		clr	%l5
		clr	%l6
		clr	%l7
		clr	%o0
		clr	%o1
		clr	%o2
		clr	%o3
		clr	%o4
		clr	%o5
		clr	%o6
		clr	%o7
		save
	2:
		mov	%psr, %g1		! get CWP
		srl	%g2, %g1, %g1		! test WIM bit
		btst	1, %g1
		bz,a	1b			! not invalid window yet
		clr	%l0			! clear the window

		!
		! Clean up trap window.
		!
		mov	%g3, %psr		! back to trap window, restore PSR_CC
		mov	%g2, %wim		! restore wim
		nop; nop;			! psr delay
		mov	%l5, %g1		! restore globals
		mov	%l6, %g2
		mov	%l7, %g3
		mov	%l2, %o6		! put npc in unobtrusive place
		clr	%l0			! clear the rest of the window
		clr	%l1
		clr	%l2
		clr	%l3
		clr	%l4
		clr	%l5
		clr	%l6
		clr	%l7
		clr	%o0
		clr	%o1
		clr	%o2
		clr	%o3
		clr	%o4
		clr	%o5
		clr	%o7
		jmp	%o6			! return to npc
		rett	%o6 + 4

	cw_out:
		nop				! psr delay
		jmp	%l2			! return to npc
		rett	%l2 + 4

	/*
	 * Get vector base register
	 */
		ENTRY(getvbr)
		retl				! leaf routine return
		mov	%tbr, %o0		! read trap base register

	/*
	 * Enter the monitor -- called from console abort
	 */
		ENTRY(montrap)
		save	%sp, -SA(MINFRAME), %sp	! get a new window
		call	_flush_windows		! flush windows to stack
		nop

	#ifdef SAS
		ta	255			! trap to siumlator
		nop
	#else
		call	%i0			! go to monitor
		nop

	#endif SAS
		ret
		restore
!EOF

#==============================================================================

print_flags_if_any() {
	case $# in
	0 )     ;; # no flags given
	1 )     echo -n " (with flag \"$*\")"  ;;
	* )     echo -n " (with flags \"$*\")" ;;
	esac
}

if  ${AS} -P -DLOCORE -Dsun4 -DGENERIC -DSUN4_110 -DSUN4_260 -DIPCSHMEM \
	-DIPCSEMAPHORE -DIPCMESSAGE -DNIT -DNFS -DUFS -DQUOTA -DSYSACCT \
	-DINET -DKERNEL -I. -Dsparc -o LOCORE.o  LOCORE.s \
	${FLAGS}  2>LOCORE.errs
then
	echo -n "****** SUCCESS:  LOCORE.S ASSEMBLES PROPERLY"
	print_flags_if_any ${FLAGS}
	echo "."
	${CLEANUP}
	exit 0
else
	set `wc -l LOCORE.errs`	# $1 gets Line-count
	LINES=$1
	if [ ${LINES} -le 3 ]
	then
		cat LOCORE.errs
	else
		head -3 LOCORE.errs
		echo  "...etc..."
	fi
	echo -n "****** FAILURE.  LOCORE does not assemble properly"
	print_flags_if_any ${FLAGS}
	echo "."
	${CLEANUP}
	exit 1
fi
