/*
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)locore.s	7.13 (Berkeley) 04/10/90
 */

#include "../tahoe/mtpr.h"
#include "../tahoe/trap.h"
#include "../tahoe/psl.h"
#include "../tahoe/pte.h"
#include "../tahoe/cp.h"
#include "../tahoe/mem.h"
#include "../tahoe/SYS.h"

#include "../tahoemath/fp.h"

#include "errno.h"
#include "syscall.h"
#include "cmap.h"

	.set	HIGH,0x1f		# mask for total disable
	.set	NISP,3			# number of interrupt stack pages
	.set	SYSTEM,0xC0000000	# virtual address of system start
	.set	PPAGES,0x100000  	# possible pages in P0,P1, etc.

/* ACBL for non-negative '_add' */
#define ACBL(_limit,_add,_index,_displ) \
	addl2	_add,_index; \
	cmpl	_index,_limit; \
	bleq	_displ

/* _ACBL for negative '_add' */
#define _ACBL(_limit,_add,_index,_displ) \
	addl2	_add,_index; \
	cmpl	_index,_limit; \
	bgeq	_displ

#define	MOVC3(_srcaddr,_dstaddr,_len) \
	movl	_srcaddr,r0; \
	movl	_dstaddr,r1; \
	movl	_len,r2; \
	movblk

/* keep address of psl if coming from user mode */
#define CHECK_SFE(_delta) \
	bitl	$PSL_CURMOD,_delta(sp); \
	jeql	1f; \
	moval	_delta(sp),_user_psl; \
1:

/*
 * User structure is UPAGES at top of user space.
 */
	.globl	_u
	.set	_u,SYSTEM - UPAGES*NBPG

/*
 * Restart stack. Used on power recovery or panic.
 * Takes a core-dump and then halts.
 */ 
	.globl	_rsstk
	.globl	pwfl_stk	
_rsstk:
	.space	1024-8
pwfl_stk:
	.space	4
dumpflag:
	.space	4

	.globl	_intstack
_intstack:
	.space	NISP*NBPG
eintstack:

/*
 * Power failure storage block and
 * macros for saving and restoring.
 */
#define	POWERFAIL(id,longs) \
	.globl	pwfl_/**/id \
pwfl_/**/id: .space longs*4
	.data
	POWERFAIL(r0,	14)		# r0-r13
	POWERFAIL(sp,	1)		# r14
	POWERFAIL(SCBB,	1)		# system control block base
	POWERFAIL(SBR,	1)		# system pte base
	POWERFAIL(SLR,	1)		# system pte length
	POWERFAIL(P0BR,	1)		# p0 pte base
	POWERFAIL(P0LR,	1)		# p0 pte length
	POWERFAIL(P1BR,	1)		# p1 pte base
	POWERFAIL(P1LR,	1)		# p1 pte length
	POWERFAIL(P2BR,	1)		# p2 pte base
	POWERFAIL(P2LR,	1)		# p2 pte length
	POWERFAIL(IPL,	1)		# interrupt priority level
	POWERFAIL(DCK,	1)		# data cache key
	POWERFAIL(CCK,	1)		# code cache key
	POWERFAIL(PCBB,	1)		# process control block base
	POWERFAIL(ISP,	1)		# interrupt stack pointer
	POWERFAIL(KSP,	1)		# kernel mode stack pointer
	POWERFAIL(USP,	1)		# user mode stack pointer
	POWERFAIL(MME,	1)		# memory management enable
	POWERFAIL(PSL,	1)		# processor status longword

/*
 * Save current state in power fail storage block.
 */
#define	SAVEpwfl() \
	movpsl	pwfl_PSL	# Keeps all flags, etc. \
	storer	$0x3fff,pwfl_r0	# Saves r0-r13 \
	moval	0(sp),pwfl_sp	# Saves sp (=r14) \
	mfpr	$SBR,pwfl_SBR	# Save all re_loadable registers \
	mfpr	$SLR,pwfl_SLR \
	mfpr	$P0BR,pwfl_P0BR \
	mfpr	$P0LR,pwfl_P0LR \
	mfpr	$P1BR,pwfl_P1BR \
	mfpr	$P1LR,pwfl_P1LR \
	mfpr	$P2BR,pwfl_P2BR \
	mfpr	$P2LR,pwfl_P2LR \
	mfpr	$IPL,pwfl_IPL \
	mfpr	$MME,pwfl_MME \
	mfpr	$DCK,pwfl_DCK \
	mfpr	$CCK,pwfl_CCK \
	mfpr	$PCBB,pwfl_PCBB \
	mfpr	$ISP,pwfl_ISP \
	mfpr	$SCBB,pwfl_SCBB \
	mfpr	$KSP,pwfl_KSP \
	mfpr	$USP,pwfl_USP

/*
 * Restore state saved in power fail block and
 * jmp to location specified after (possibly)
 * enabling memory management.
 */
#define	RESTOREpwfl(loc) \
	loadr	$0x3fff,pwfl_r0	# Restore r0-r13 \
	movl	pwfl_sp,sp	# Restore sp (=r14) \
	mtpr	pwfl_SCBB,$SCBB \
	mtpr	pwfl_SBR,$SBR	# Restore all re_loadable registers \
	mtpr	pwfl_SLR,$SLR \
	mtpr	pwfl_P0BR,$P0BR \
	mtpr	pwfl_P0LR,$P0LR \
	mtpr	pwfl_P1BR,$P1BR \
	mtpr	pwfl_P1LR,$P1LR \
	mtpr	pwfl_P2BR,$P2BR \
	mtpr	pwfl_P2LR,$P2LR \
	mtpr	pwfl_IPL,$IPL \
	mtpr	pwfl_DCK,$DCK \
	mtpr	pwfl_CCK,$CCK \
	mtpr	pwfl_PCBB,$PCBB \
	mtpr	pwfl_ISP,$ISP \
	mtpr	pwfl_KSP,$KSP \
	mtpr	pwfl_USP,$USP \
\
	bicpsw	$0xff		# Restore PSW. \
	bispsw	pwfl_PSL+2	# Set original bits back (just in case..) \
# now go to mapped mode \
# Have to change PC to system addresses \
	mtpr	$1,$PACC	# Thoroughly clean up caches. \
	mtpr	$1,$PADC \
	mtpr	$1,$TBIA \
	mtpr	pwfl_MME,$MME  	# Restore MME. Last thing to be done. \
	jmp 	loc

/*
 * Do a dump.
 * Called by auto-restart.
 * May be called manually.
 */
	.align	2
	.text
	.globl	_Xdoadump
	.globl	_doadump
_Xdoadump:					# CP comes here after power fail
	RESTOREpwfl(*0f)			# restore state
_doadump:
	.word 0
0:	mtpr	$HIGH,$IPL
#define	_rsstkmap _Sysmap+12	# powerfail storage, scb, rsstk, int stack
	tstl	dumpflag			# dump only once!
	bneq	1f
	andl2	$~PG_PROT,_rsstkmap
	orl2	$PG_KW,_rsstkmap		# Make dump stack r/w
	mtpr	$0,$TBIA
	movl	$1,dumpflag
	movab	dumpflag,sp
	callf	$4,_dumpsys
1:
	halt

/*
 * Interrupt vector routines
 */ 
	.globl	_waittime
#define	SCBVEC(name) \
	.align 2; \
	.globl _X/**/name; \
_X/**/name
#define	PANIC(msg) \
	clrl _waittime; pushab 1f; callf $8,_panic; 1: .asciz msg
#define	PRINTF(n,msg) \
	pushab 1f; callf $(n+2)*4,_printf; MSG(msg)
#define	MSG(msg) .data; 1: .asciz msg; .text
/*
 * r0-r5 are saved across all faults and interrupts.
 * Routines below and those hidden in vbglue.s (device
 * interrupts) invoke the PUSHR/POPR macros to execute
 * this.  Also, certain stack frame offset calculations
 * use this, using the REGSPC definition (and FPSPC defined below).
 */
#define	REGSPC	6*4
#define	PUSHR	movab -REGSPC(sp),sp; storer $0x3f,(sp)
#define	POPR	loadr $0x3f,(sp); movab REGSPC(sp),sp

/*
 * Floating point state is saved across faults and
 * interrupts.  The state occupies 4 longwords on
 * the stack:
 *	precision indicator (single = 0/double = 1)
 *	double representation of accumulator
 *	save accumulator status flag (pcb_savacc)
 */
#define	FPSPC	(4*4)

#define SAVE_FPSTAT(_delta) \
	bitl	$PSL_DBL,_delta(sp); \
	beql	1f; \
	pushl	$1; \
	pushd; \
	jmp	2f; \
1:	pushl	$0; \
	pushl	$0; \
	stf	-(sp); \
2:	tstl	_u+PCB_SAVACC; \
	bneq	3f; \
	moval	0(sp),_u+PCB_SAVACC; \
	orl2	$2,8(sp);\
3:	pushl	$0;

#define REST_FPSTAT \
	tstl	(sp)+; \
	bitl	$2,8(sp);\
	beql	1f;\
	movl	$0,_u+PCB_SAVACC; \
1:	bitl	$1,8(sp); \
	beql	2f; \
	ldd	(sp); \
	jmp	3f; \
2:	ldf	(sp); \
3:	moval	12(sp),sp;

#define REST_ACC \
	tstl	_u+PCB_SAVACC; \
	beql	2f; \
	movl	_u+PCB_SAVACC,r1; \
	andl3	$(EXPMASK|SIGNBIT),(r1),-(sp); \
	cmpl	$0x80000000,(sp)+; \
	bneq	3f; \
	clrl	(r1); \
3:	bitl	$1,8(r1); \
	beql	1f; \
	ldd	(r1); \
	jmp	2f; \
1:	ldf	(r1); \
2:	;

	.data
nofault: .space	4			# bus error non-local goto label

	.text
SCBVEC(buserr):
	CHECK_SFE(12)
	SAVE_FPSTAT(12)
	incl	_intrcnt+I_BUSERR	# keep stats...
	pushl	r0			# must save
	andl3	24(sp),$ERRCD,r0	# grab pushed MER value
	cmpl	r0,$APE			# address parity error?
	jneq	1f
	halt	
1:	cmpl	r0,$VBE			# versabus error?
	jneq	2f
	halt
2:
	movl	(sp)+,r0		# restore r0 and...
	bitl	$PSL_CURMOD,4*4+3*4(sp)	# check if happened in user mode?
	jeql	3f			# yes, then shift stack up for trap...
	movl	12(sp),16(sp)		# sorry, no space for which-buss...
	movl	8(sp),12(sp)
	movl	4(sp),8(sp)
	movl	0(sp),4(sp)
	movl	$T_BUSERR,0(sp)		# push trap type code and...
	jbr	alltraps		# ...merge with all other traps
3:					# kernel mode, check to see if...
	tstl	nofault			# ...doing peek/poke?
	jeql	4f			# nofault set? if so, jump to it...
	movl	nofault,4*4+2*4(sp)	# ...setup for non-local goto
	clrl	nofault
	jbr	5f
4:
	PUSHR
	pushab	4*4+REGSPC(sp)		# address of bus error parameters
	callf	$8,_buserror
	POPR
5:
	REST_FPSTAT
	movab	8(sp),sp		# remove bus error parameters
	rei

SCBVEC(powfail):			# We should be on interrupt stack now.
	SAVEpwfl()			# save machine state
	moval	_Xdoadump-SYSTEM,_scb+SCB_DOADUMP
	halt

SCBVEC(stray):
	incl	_cnt+V_INTR		# add to statistics
	rei

#include "../net/netisr.h"
	.globl	_netisr
SCBVEC(netintr):
	CHECK_SFE(4)
	SAVE_FPSTAT(4); PUSHR
#include "imp.h"
#if NIMP > 0
	bbc	$NETISR_IMP,_netisr,1f;
	andl2	$~(1<<NETISR_IMP),_netisr
	callf	$4,_impintr;
1:
#endif
#ifdef INET
	bbc	$NETISR_IP,_netisr,1f	
	andl2	$~(1<<NETISR_IP),_netisr
	callf	$4,_ipintr	
1:
#endif
#ifdef NS
	bbc	$NETISR_NS,_netisr,1f	
	andl2	$~(1<<NETISR_NS),_netisr
	callf	$4,_nsintr	
1:
#endif
#ifdef ISO
	bbc	$NETISR_ISO,_netisr,1f	
	andl2	$~(1<<NETISR_ISO),_netisr
	callf	$4,_clnlintr	
1:
#endif
	incl	_cnt+V_SOFT
	POPR; REST_FPSTAT
	rei

SCBVEC(cnrint):
	CHECK_SFE(4)
	SAVE_FPSTAT(4); PUSHR; 
	pushl $CPCONS; callf $8,_cnrint;
	incl	_intrcnt+I_CNR
	incl	_cnt+V_INTR
	POPR; REST_FPSTAT;
	rei
SCBVEC(cnxint):
	CHECK_SFE(4)
	SAVE_FPSTAT(4); PUSHR; 
	pushl $CPCONS; callf $8,_cnxint;
	incl	_intrcnt+I_CNX
	incl	_cnt+V_INTR
	POPR; REST_FPSTAT;
	rei
SCBVEC(rmtrint):
	CHECK_SFE(4)
	SAVE_FPSTAT(4); PUSHR; 
	pushl $CPREMOT; callf $8,_cnrint;
	incl	_intrcnt+I_RMTR
	incl	_cnt+V_INTR
	POPR; REST_FPSTAT;
	rei
SCBVEC(rmtxint):
	CHECK_SFE(4)
	SAVE_FPSTAT(4); PUSHR; 
	pushl $CPREMOT; callf $8,_cnxint;
	incl	_intrcnt+I_RMTX
	incl	_cnt+V_INTR
	POPR; REST_FPSTAT;
	rei

#define PUSHPCPSL	pushl 4+FPSPC+REGSPC(sp); pushl 4+FPSPC+REGSPC(sp);

SCBVEC(hardclock):
	tstl	_clk_enable
	bneq	1f
	rei
1:
	CHECK_SFE(4)
	SAVE_FPSTAT(4); PUSHR
	PUSHPCPSL			# push pc and psl
	callf	$12,_hardclock		# hardclock(pc,psl)
	incl	_intrcnt+I_CLOCK
	incl	_cnt+V_INTR		## temp so not to break vmstat -= HZ
	POPR; REST_FPSTAT
	rei
SCBVEC(softclock):
	CHECK_SFE(4)
	SAVE_FPSTAT(4); PUSHR;
	PUSHPCPSL				# push pc and psl
	callf	$12,_softclock			# softclock(pc,psl)
	incl	_cnt+V_SOFT
	POPR; REST_FPSTAT
	rei

/*
 * Stray VERSAbus interrupt catch routines
 */
	.data
#define	PJ	.align 2; callf $4,_Xvstray
	.globl	_catcher
_catcher:
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ
	PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ;PJ

	.align	2
	.globl	_cold
_cold:	.long	0x3

	.text
SCBVEC(vstray):
	.word	0
	bbc	$0,_cold,2f		# system running?
	bbc	$1,_cold,1f		# doing autoconfig?
	jbr	3f			# random interrupt, ignore
1:
	mfpr	$IPL,r12		# ...setup br and cvec
	subl3	$_catcher+7,-8(fp),r11; shar $3,r11,r11
	addl2	$SCB_DEVBASE,r11
	jbr	3f
2:
	PUSHR
	subl3	$_catcher+7,-8(fp),r0; shar $3,r0,r0
	addl3	$SCB_DEVBASE,r0,-(sp);
	mfpr	$IPL,-(sp)
	PRINTF(2, "stray intr ipl %x vec %x\n")
	POPR
3:	moval	0f,-8(fp); ret		# pop callf frame...
0:	rei				# ...and return

/*
 * Trap and fault vector routines
 */ 
#define	TRAP(a)	pushl $T_/**/a; jbr alltraps

/*
 * Ast delivery (profiling and/or reschedule)
 */

SCBVEC(kspnotval):
	CHECK_SFE(4)
	pushl $0;
	SAVE_FPSTAT(8)
	TRAP(KSPNOTVAL)
SCBVEC(privinflt):
	CHECK_SFE(4)
	pushl $0;
	SAVE_FPSTAT(8)
	TRAP(PRIVINFLT)
SCBVEC(resopflt):
	CHECK_SFE(4)
	pushl $0;
	SAVE_FPSTAT(8)
	TRAP(RESOPFLT)
SCBVEC(resadflt):
	CHECK_SFE(4)
	pushl $0;
	SAVE_FPSTAT(8)
	TRAP(RESADFLT)
SCBVEC(bptflt):
	CHECK_SFE(4)
	pushl $0;
	SAVE_FPSTAT(8)
	TRAP(BPTFLT)
SCBVEC(kdbintr):
	CHECK_SFE(4);
	pushl $0;
	SAVE_FPSTAT(8);
	TRAP(KDBTRAP);
SCBVEC(tracep):
	CHECK_SFE(4)
	pushl $0;
	SAVE_FPSTAT(8)
	TRAP(TRCTRAP)
SCBVEC(alignflt):
#ifdef ALIGN
	bitl	$PSL_CURMOD,4(sp)
	jeql	align_excp		# Can't emulate for kernel mode !
	jbr	non_aligned		# Only emulated for user mode.
align_excp:
#else
	CHECK_SFE(4)
#endif
	pushl $0;
	SAVE_FPSTAT(8)
	TRAP(ALIGNFLT)
SCBVEC(arithtrap):
	CHECK_SFE(8)
	SAVE_FPSTAT(8)
	TRAP(ARITHTRAP)

SCBVEC(protflt):
	CHECK_SFE(12)
	bitl	$1,(sp)+
	jneq	segflt
	SAVE_FPSTAT(8)
	TRAP(PROTFLT)
segflt:
	SAVE_FPSTAT(8)
	TRAP(SEGFLT)

SCBVEC(fpm):			# Floating Point Emulation
#ifdef FPE
	CHECK_SFE(16)
	SAVE_FPSTAT(16)
	incl	_cnt+V_FPE	# count emulation traps
	callf	$4,_fpemulate
	REST_FPSTAT
#endif
	moval	8(sp),sp	# Pop operand
	tstl	(sp)		# Stack= PSL, PC, return_code
	jneq	_Xarithtrap	# If not OK, emulate F.P. exception
	movab	4(sp),sp	# Else remove return_code and
	rei

SCBVEC(sfexcep):
	CHECK_SFE(4)
	pushl $0
	SAVE_FPSTAT(8)
	TRAP(ASTFLT)

SCBVEC(transflt):
	CHECK_SFE(12)
	bitl	$2,(sp)+
	bneq	tableflt
pageflt:
	SAVE_FPSTAT(8)
	TRAP(PAGEFLT)
tableflt:
	SAVE_FPSTAT(8)
	TRAP(TABLEFLT)

#define REST_STACK	movab 4(sp), sp; REST_FPSTAT; movab 4(sp), sp

alltraps:
	mfpr	$USP,-(sp); 
	callf	$4,_trap;
	mtpr	(sp)+,$USP
	incl	_cnt+V_TRAP
	REST_STACK			# pop type, code, and fp stuff
	mtpr	$HIGH,$IPL		## dont go to a higher IPL (GROT)
	rei

SCBVEC(syscall):
	CHECK_SFE(8)
	SAVE_FPSTAT(8)
	pushl	$T_SYSCALL
	mfpr	$USP,-(sp);
	callf	$4,_syscall;
	mtpr	(sp)+,$USP
	incl	_cnt+V_SYSCALL
	REST_STACK			# pop type, code, and fp stuff
	mtpr	$HIGH,$IPL		## dont go to a higher IPL (GROT)
	rei

/*
 * System page table.
 *
 * Mbmap and Usrptmap are enlarged by CLSIZE entries
 * as they are managed by resource maps starting with index 1 or CLSIZE.
 */ 
#define	vaddr(x)	((((x)-_Sysmap)/4)*NBPG+SYSTEM)
#define	SYSMAP(mname, vname, npte)			\
_/**/mname:	.globl	_/**/mname;		\
	.space	(npte)*4;			\
	.globl	_/**/vname;			\
	.set	_/**/vname,vaddr(_/**/mname)
#define	ADDMAP(npte)	.space	(npte)*4

	.data
	.align	2
	SYSMAP(Sysmap	,Sysbase	,SYSPTSIZE	)
	SYSMAP(Forkmap	,forkutl	,UPAGES		)
	SYSMAP(Xswapmap	,xswaputl	,UPAGES		)
	SYSMAP(Xswap2map,xswap2utl	,UPAGES		)
	SYSMAP(Swapmap	,swaputl	,UPAGES		)
	SYSMAP(Pushmap	,pushutl	,UPAGES		)
	SYSMAP(Vfmap	,vfutl		,UPAGES		)
	SYSMAP(CMAP1	,CADDR1		,1		)
	SYSMAP(CMAP2	,CADDR2		,1		)
	SYSMAP(mmap	,vmmap		,1		)
	SYSMAP(alignmap	,alignutl	,1		)	/* XXX */
	SYSMAP(msgbufmap,msgbuf		,MSGBUFPTECNT	)
	SYSMAP(Mbmap	,mbutl		,NMBCLUSTERS*MCLBYTES/NBPG+CLSIZE )
#ifdef MFS
#include "../ufs/mfsiom.h"
	/*
	 * Used by the mfs_doio() routine for physical I/O
	 */
	SYSMAP(Mfsiomap	,mfsiobuf	,MFS_MAPREG )
#endif /* MFS */
#ifdef NFS
#include "../nfs/nfsiom.h"
	/*
	 * Used by the nfs_doio() routine for physical I/O
	 */
	SYSMAP(Nfsiomap	,nfsiobuf	,NFS_MAPREG )
#endif /* NFS */
	/*
	 * This is the map used by the kernel memory allocator.
	 * It is expanded as necessary by the special features
	 * that use it.
	 */
	SYSMAP(kmempt	,kmembase	,1000*CLSIZE 	)
#ifdef	GPROF
				ADDMAP( 600*CLSIZE	)
#endif
	/*
	 * Enlarge kmempt as needed for bounce buffers allocated
	 * by tahoe controllers.
	 */
#include "hd.h"
#if NHD > 0
				ADDMAP(	NHDC*(MAXPHYS/NBPG+CLSIZE) )
#endif
#include "dk.h"
#if NDK > 0
				ADDMAP(	NVD*(MAXPHYS/NBPG+CLSIZE) )
#endif
#include "yc.h"
#if NYC > 0
				ADDMAP(	NCY*(MAXPHYS/NBPG+CLSIZE) )
#endif
#include "mp.h"
				ADDMAP(	NMP*14		)
	SYSMAP(ekmempt	,kmemlimit	,0		)

	SYSMAP(VMEMbeg	,vmembeg	,0		)
	SYSMAP(VMEMmap	,vmem		,VBIOSIZE 	)
	SYSMAP(VMEMmap1	,vmem1		,0		)
#include "ace.h"
#if NACE > 0
				ADDMAP(	NACE*32	)
#endif
#if NHD > 0
				ADDMAP( NHDC )
#endif
#include "vx.h"
#if NVX > 0
				ADDMAP( NVX * 16384/NBPG )
#endif
	SYSMAP(VMEMend	,vmemend	,0		)

	SYSMAP(VBmap	,vbbase		,CLSIZE		)
#if NHD > 0
				ADDMAP(	NHDC*(MAXPHYS/NBPG+CLSIZE) )
#endif
#if NDK > 0
				ADDMAP(	NVD*(MAXPHYS/NBPG+CLSIZE) )
#endif
#if NYC > 0
				ADDMAP(	NCY*(MAXPHYS/NBPG+CLSIZE) )
#endif
				ADDMAP(	NMP*14		)
	SYSMAP(eVBmap	,vbend		,0		)

	SYSMAP(Usrptmap	,usrpt		,USRPTSIZE+CLSIZE )
eSysmap:
	.globl	_Syssize
	.set	_Syssize,(eSysmap-_Sysmap)/4

	.text
/*
 * Initialization
 *
 * IPL 0x1f; MME 0; scbb, pcbb, sbr, slr, isp, ksp not set
 */
	.align	2
	.globl	start
start:
	.word	0
/* set system control block base and system page table params */
	mtpr	$_scb-SYSTEM,$SCBB
	mtpr	$_Sysmap-SYSTEM,$SBR
	mtpr	$_Syssize,$SLR
/* double map the kernel into the virtual user addresses of phys mem */
	mtpr	$_Sysmap,$P0BR
	mtpr	$_Syssize,$P0LR
	mtpr	$_Sysmap,$P1BR			# against Murphy
	mtpr	$_Syssize,$P1LR
/* set ISP */
	movl	$_intstack-SYSTEM+NISP*NBPG,sp	# still physical
	mtpr	$_intstack+NISP*NBPG,$ISP
/* count up memory; _physmem contains limit */
	clrl	r7
	shll	$PGSHIFT,_physmem,r8
	decl	r8
1:	pushl	$1; pushl r7; callf $12,_badaddr; tstl r0; bneq 9f
	ACBL(r8,$64*1024,r7,1b)
9:
/* clear memory from kernel bss and pages for proc 0 u. and page table */
	movab	_edata,r6; andl2 $~SYSTEM,r6
	movab	_end,r5; andl2 $~SYSTEM,r5
#ifdef KADB
	subl2	$4,r5
1:	clrl	(r6); ACBL(r5,$4,r6,1b)		# clear just bss
	addl2	$4,r5
	bbc	$6,r11,0f			# check RB_KDB
	andl3	$~SYSTEM,r9,r5			# skip symbol & string tables
	andl3	$~SYSTEM,r9,r6
#endif
0:	orl3	$SYSTEM,r5,r9			# convert to virtual address
	addl2	$NBPG-1,r9			# roundup to next page
	addl2	$(UPAGES*NBPG)+NBPG+NBPG,r5
1:	clrl	(r6); ACBL(r5,$4,r6,1b)
/* trap(), syscall(), and fpemulate() save r0-r12 in the entry mask */
	orw2	$0x01fff,_trap
	orw2	$0x01fff,_syscall
#ifdef FPE
	orw2	$0x01fff,_fpemulate
#endif
	orw2	$0x01ffc,_panic			# for debugging (no r0|r1)
	callf	$4,_fixctlrmask			# setup for autoconfig
/* initialize system page table: scb and int stack writeable */
	clrl	r2
	movab	eintstack,r1 
	andl2	$~SYSTEM,r1
	shrl 	$PGSHIFT,r1,r1			# r1-page number of eintstack
/* make 1st page processor storage read/only, 2nd read/write */
	orl3	$PG_V|PG_KR,r2,_Sysmap[r2]; incl r2;
	orl3	$PG_V|PG_KW,r2,_Sysmap[r2]; incl r2;
/* other parts of the system are read/write for kernel */
1:	orl3	$PG_V|PG_KW,r2,_Sysmap[r2];	# data:kernel write+phys=virtual
	aoblss r1,r2,1b
/* make rsstk read-only as red zone for interrupt stack */
	andl2	$~PG_PROT,_rsstkmap
	orl2	$PG_V|PG_KR,_rsstkmap
/* make kernel text space read-only */
	movab	_etext+NBPG-1,r1
	andl2	$~SYSTEM,r1
	shrl 	$PGSHIFT,r1,r1
1:	orl3	$PG_V|PG_KR,r2,_Sysmap[r2]
	aoblss r1,r2,1b
/* make kernel data, bss, read-write */
	andl3	$~SYSTEM,r9,r1
	shrl 	$PGSHIFT,r1,r1
1:	orl3	$PG_V|PG_KW,r2,_Sysmap[r2]
	aoblss r1,r2,1b
/* go to mapped mode, have to change both pc and sp to system addresses */
	mtpr	$1,$TBIA
	mtpr	$1,$PADC			# needed by HW parity&ECC logic
	mtpr	$1,$PACC			# just in case
	mtpr 	$1,$MME
	movab	SYSTEM(sp),sp
	jmp 	*$0f
0:
/* disable any interrupts */
	movl	$0,_intenable
/* init mem sizes */
	shrl	$PGSHIFT,r7,_physmem
/* setup context for proc[0] == scheduler */
	andl3	$~SYSTEM,r9,r6			# convert to physical
	andl2	$~(NBPG-1),r6			# make page boundary
/* setup page table for proc[0] */
	shrl	$PGSHIFT,r6,r3			# r3 = btoc(r6)
	orl3	$PG_V|PG_KW,r3,_Usrptmap	# init first upt entry
	incl	r3				# r3 - next page
	movab	_usrpt,r0			# r0 - first user page
	mtpr	r0,$TBIS
/* init p0br, p0lr */
	mtpr	r0,$P0BR			# no p0 for proc[0]
	mtpr	$0,$P0LR
	mtpr	r0,$P1BR			# no p1 either
	mtpr	$0,$P1LR
/* init p2br, p2lr */
	movab	NBPG(r0),r0
	movl	$PPAGES-UPAGES,r1
	mtpr	r1,$P2LR
	moval	-4*PPAGES(r0),r2
	mtpr	r2,$P2BR
/* setup mapping for UPAGES of _u */
	clrl	r2
	movl 	$SYSTEM,r1
	addl2 	$UPAGES,r3
	jbr 2f
1:	decl	r3
	moval	-NBPG(r1),r1	# r1 = virtual add of next (downward) _u page
	subl2	$4,r0		# r0 = pte address
	orl3	$PG_V|PG_URKW,r3,(r0)
	mtpr	r1,$TBIS
2:	aobleq	$UPAGES,r2,1b
/* initialize (slightly) the pcb */
	movab	UPAGES*NBPG(r1),PCB_KSP(r1)	# KSP starts at end of _u
	movl	r1,PCB_USP(r1)			# USP starts just below _u
	mfpr	$P0BR,PCB_P0BR(r1)
	mfpr	$P0LR,PCB_P0LR(r1)
	mfpr	$P1BR,PCB_P1BR(r1)
	mfpr	$P1LR,PCB_P1LR(r1)
	mfpr	$P2BR,PCB_P2BR(r1)
	mfpr	$P2LR,PCB_P2LR(r1)
	movl	$CLSIZE,PCB_SZPT(r1)		# init u.u_pcb.pcb_szpt
	movl	r9,PCB_R9(r1)			# r9 obtained from boot
	movl	r10,PCB_R10(r1)			# r10 obtained from boot
	movl	r11,PCB_R11(r1)			# r11 obtained from CP on boot
	movab	1f,PCB_PC(r1)			# initial pc
	clrl	PCB_PSL(r1)			# kernel mode, ipl=0
	shll	$PGSHIFT,r3,r3
	mtpr	r3,$PCBB			# first pcbb (physical)
/* go to kernel mode */
	ldpctx
	rei					# Actually next instruction:
/* put signal trampoline code in u. area */
1:	movab	sigcode,r0
	movab	_u+PCB_SIGC,r1
	movl	$19,r2
	movblk
/* save boot device in global _bootdev */
	movl	r10,_bootdev
/* save reboot flags in global _boothowto */
	movl	r11,_boothowto
#ifdef KADB
/* save end of symbol & string table in global _bootesym */
	subl3	$NBPG-1,r9,_bootesym
#endif
/* calculate firstaddr, and call main() */
	andl3	$~SYSTEM,r9,r0
	shrl	$PGSHIFT,r0,-(sp)
	addl2	$UPAGES+1,(sp)			# first physical unused page
	callf 	$8,_main
/* proc[1] == /etc/init now running here in kernel mode; run icode */
	pushl	$PSL_CURMOD			# User mode PSL
	pushl $0				# PC = 0 (virtual now)
	rei

/*
 * Mask for saving/restoring registers on entry to
 * a user signal handler.  Space for the registers
 * is reserved in sendsig, so beware if you want
 * to change the mask.
 */
#define	SIGREGS	(R0|R1|R2|R3|R4|R5)
	.align	2
	.globl	sigcode
sigcode:
	storer	$SIGREGS,16(sp)	# save volatile registers
	calls	$4*3+4,*12(sp)	# params pushed by sendsig for handler
	loadr	$SIGREGS,4(sp)	# restore volatile registers
	movab	24(sp),fp	# use parameter list set up in sendsig
	kcall	$SYS_sigreturn	# cleanup mask and onsigstack
	halt			# sigreturn does not return!

	.globl	_icode
	.globl	_initflags
	.globl	_szicode
/*
 * Icode is copied out to process 1 to exec /etc/init.
 * If the exec fails, process 1 exits.
 */
	.align	2
_icode:
	/* try /sbin/init */
	pushab	b`argv1-l0(pc)
l0:	pushab	b`init1-l1(pc)
l1:	pushl	$2
	movab	(sp),fp
	kcall	$SYS_execv
	/* try /etc/init */
	pushab	b`argv2-l2(pc)
l2:	pushab	b`init2-l3(pc)
l3:	pushl	$2
	movab	(sp),fp
	kcall	$SYS_execv
	/* give up */
	pushl	r0
	pushl	$1
	movab	(sp),fp
	kcall	$SYS_exit

init1:	.asciz	"/sbin/init"
init2:	.asciz	"/etc/init"
	.align	2
_initflags:
	.long	0
argv1:	.long	init1+6-_icode
	.long	_initflags-_icode
	.long	0
argv2:	.long	init2+5-_icode
	.long	_initflags-_icode
	.long	0
_szicode:
	.long	_szicode-_icode

/*
 * Primitives
 */ 

/*
 * badaddr(addr, len)
 *	see if access addr with a len type instruction causes a machine check
 *	len is length of access (1=byte, 2=short, 4=long)
 *	r0 = 0 means good(exists); r0 =1 means does not exist.
 */
ENTRY(badaddr, R3|R4)
	mfpr	$IPL,r1
	mtpr	$HIGH,$IPL
	movl	_scb+SCB_BUSERR,r2
	movl	4(fp),r3
	movl	8(fp),r4
	movab	9f,_scb+SCB_BUSERR
	bbc	$0,r4,1f; tstb	(r3)
1:	bbc	$1,r4,1f; tstw	(r3)
1:	bbc	$2,r4,1f; tstl	(r3)
1:	clrl	r0
2:	movl	r2,_scb+SCB_BUSERR
	mtpr	r1,$IPL
	ret

/*
 * wbadaddr(addr, len, value)
 *	see if write of value to addr with a len type instruction causes
 *	a machine check
 *	len is length of access (1=byte, 2=short, 4=long)
 *	r0 = 0 means good(exists); r0 =1 means does not exist.
 */
ENTRY(wbadaddr, R3|R4)
	mfpr	$IPL,r1
	mtpr	$HIGH,$IPL
	movl	_scb+SCB_BUSERR,r2
	movl	4(fp),r3
	movl	8(fp),r4
	movab	9f,_scb+SCB_BUSERR
	bbc	$0,r4,1f; movb	15(fp), (r3)
1:	bbc	$1,r4,1f; movw	14(fp), (r3)
1:	bbc	$2,r4,1f; movl	12(fp), (r3)
1:	movl	$30000,r0		# delay for error interrupt
1:	decl	r0
	jneq	1b
2:	movl	r2,_scb+SCB_BUSERR	# made it w/o machine checks; r0 is 0
	mtpr	r1,$IPL
	ret

	.align	2
9:				# catch buss error (if it comes)
	andl3	4(sp),$ERRCD,r0
	cmpl	r0,$APE
	jneq	1f
	halt			# address parity error
1:	cmpl	r0,$VBE
	jneq	1f
	halt			# Versabus error
1:
	movl	$1,r0		# Anything else = bad address
	movab	8(sp),sp	# discard buss error trash
	movab	2b,(sp)		# new program counter on stack.
	rei

/*
 * badcyaddr(addr)
 *	see if access tape master controller addr causes a bus error
 *	r0 = 0: no error; r0 = 1: timeout error.
 */
ENTRY(badcyaddr, 0)
	mfpr	$IPL,r1
	mtpr	$HIGH,$IPL
	clrl	r2
	movab	2f,nofault
	movob	$-1, *4(fp)
1:	aobleq	$1000, r2, 1b
	clrl	nofault			# made it w/o bus error
	clrl	r0
	jbr	3f
2:	movl	$1,r0
3:	mtpr	r1,$IPL
	ret

/*
 * peek(addr)
 *	fetch word and catch any bus error
 */
ENTRY(peek, 0)
	mfpr	$IPL,r1
	mtpr	$0x18,$IPL	# not reentrant
	movl	4(fp),r2
	movab	1f,nofault
	movw	(r2),r0
	clrl	nofault
	andl2	$0xffff,r0
	jbr	2f
1:	movl	$-1,r0		# bus error
2:	mtpr	r1,$IPL
	ret

/*
 * poke(addr, val)
 *	write word and catch any bus error
 */
ENTRY(poke, R3)
	mfpr	$IPL,r1
	mtpr	$0x18,$IPL	# not reentrant
	movl	4(fp),r2
	movl	8(fp),r3
	clrl	r0
	movab	1f,nofault
	movw	r3,(r2)
	clrl	nofault
	jbr	2f
1:	movl	$-1,r0		# bus error
2:	mtpr	r1,$IPL
	ret

/*
 * Copy a potentially overlapping block of memory.
 *
 * ovbcopy(src, dst, count)
 *	caddr_t src, dst; unsigned count;
 */
ENTRY(ovbcopy, R3|R4)
	movl	4(fp),r0
	movl	8(fp),r1
	movl	12(fp),r2
	cmpl	r0,r1
	bgtru	1f			# normal forward case
	beql	2f			# equal, nothing to do
	addl2	r2,r0			# may be overlapping
	cmpl	r0,r1
	bgtru	3f
	subl2	r2,r0			# normal forward case
1:
	movblk
2:
	ret
3:
	addl2	r2,r1			# overlapping, must do backwards
	subl3	r0,r1,r3
	movl	r2,r4
	jbr	5f
4:
	subl2	r3,r0
	subl2	r3,r1
	movl	r3,r2
	movblk
	subl2	r3,r0
	subl2	r3,r1
	subl2	r3,r4
5:
	cmpl	r4,r3
	jgtr	4b
	movl	r4,r2
	subl2	r2,r0
	subl2	r2,r1
	movblk
	ret

/*
 * Copy a null terminated string from the user address space into
 * the kernel address space.
 *
 * copyinstr(fromaddr, toaddr, maxlength, &lencopied)
 */
ENTRY(copyinstr, 0)
	movl	12(fp),r5		# r5 = max length
	jlss	5f
	movl	8(fp),r4		# r4 = kernel address
	movl	4(fp),r0		# r0 = user address
	andl3	$(NBPG*CLSIZE-1),r0,r2	# r2 = bytes on first page
	subl3	r2,$(NBPG*CLSIZE),r2
1:
	cmpl	r5,r2			# r2 = min(bytes on page, length left);
	jgeq	2f
	movl	r5,r2
2:
	prober	$1,(r0),r2		# bytes accessible?
	jeql	5f
	subl2	r2,r5			# update bytes left count
	movl	r2,r3			# r3 = saved count
	movl	r0,r1
	cmps3				# check for null
	tstl	r2
	jneq	3f
	subl2	r3,r0			# back up r0
	movl	r4,r1
	movl	r3,r2
	movblk				# copy in next piece
	movl	r1,r4
	movl	$(NBPG*CLSIZE),r2	# check next page
	tstl	r5			# run out of space?
	jneq	1b
	movl	$ENOENT,r0		# set error code and return
	jbr	6f
3:
	tstl	16(fp)			# return length?
	beql	4f
	subl3	r5,12(fp),r5		# actual len = maxlen - unused pages
	subl2	r2,r5			#	- unused on this page
	addl3	$1,r5,*16(fp)		#	+ the null byte
4:
	movl	r4,r1
	subl3	r2,r3,r2		# calc char cnt
	subl2	r2,r0			# back up r0
	incl	r2			# add on null byte
	movblk				# copy last piece
	clrl	r0
	ret
5:
	movl	$EFAULT,r0
6:
	tstl	16(fp)
	beql	7f
	subl3	r5,12(fp),*16(fp)
7:
	ret

/*
 * Copy a null terminated string from the kernel
 * address space to the user address space.
 *
 * copyoutstr(fromaddr, toaddr, maxlength, &lencopied)
 */
ENTRY(copyoutstr, 0)
	movl	12(fp),r5		# r5 = max length
	jlss	5f
	movl	4(fp),r0		# r0 = kernel address
	movl	8(fp),r4		# r4 = user address
	andl3	$(NBPG*CLSIZE-1),r4,r2	# r2 = bytes on first page
	subl3	r2,$(NBPG*CLSIZE),r2
1:
	cmpl	r5,r2			# r2 = min(bytes on page, length left);
	jgeq	2f
	movl	r5,r2
2:
	probew	$1,(r4),r2		# bytes accessible?
	jeql	5f
	subl2	r2,r5			# update bytes left count
	movl	r2,r3			# r3 = saved count
	movl	r0,r1
/*
 * This is a workaround for a microcode bug that causes
 * a trap type 9 when cmps3/movs3 touches the last byte
 * on a valid page immediately followed by an invalid page.
 */
#ifdef good_cmps3
	cmps3				# check for null
	tstl	r2
	jneq	3b
#else
	decl	r2
	beql	9f			# cannot handle case of r2 == 0!
	cmps3				# check for null up to last byte
9:
	incl	r2
	cmpl	$1,r2			# get to last byte on page?
	bneq	3b
	tstb	(r0)			# last byte on page null?
	beql	3b
	incl	r0			# not null, so bump pointer
#endif not good_cmps3
	subl2	r3,r0			# back up r0
	movl	r4,r1
	movl	r3,r2
	movblk				# copy out next piece
	movl	r1,r4
	movl	$(NBPG*CLSIZE),r2	# check next page
	tstl	r5			# run out of space?
	jneq	1b
	movl	$ENOENT,r0		# set error code and return
	jbr	6b
5:
	clrl	*$0		# this should never execute, if it does
	movl	$EFAULT,r0	#  save me a core dump (mkm - 9/87)
6:
	tstl	16(fp)
	beql	7f
	subl3	r5,12(fp),*16(fp)
7:
	ret


/*
 * Copy a null terminated string from one point to another in
 * the kernel address space.
 *
 * copystr(fromaddr, toaddr, maxlength, &lencopied)
 */
ENTRY(copystr, 0)
	movl	12(fp),r3		# r3 = max length
	jlss	5b
	movl	4(fp),r0		# r0 = src address
	movl	8(fp),r4		# r4 = dest address
	clrl	r5			# r5 = bytes left
	movl	r3,r2			# r2 = max bytes to copy
	movl	r0,r1
	cmps3				# check for null
	tstl	r2
	jneq	3b
	subl2	r3,r0			# back up r0
	movl	r4,r1
	movl	r3,r2
	movblk				# copy next piece
	movl	$ENOENT,r0		# set error code and return
	jbr	6b

/*
 * Copy a block of data from the user address space into
 * the kernel address space.
 *
 * copyin(fromaddr, toaddr, count)
 */
ENTRY(copyin, 0)
	movl	12(fp),r0		# copy length
	blss	9f
	movl	4(fp),r1		# copy user address
	cmpl	$(CLSIZE*NBPG),r0	# probing one page or less ?
	bgeq	2f			# yes
1:
	prober	$1,(r1),$(CLSIZE*NBPG)	# bytes accessible ?
	beql	9f			# no
	addl2	$(CLSIZE*NBPG),r1	# incr user address ptr
	_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r0,1b)	# reduce count and loop
2:
	prober	$1,(r1),r0		# bytes accessible ?
	beql	9f			# no
	MOVC3(4(fp),8(fp),12(fp))
	clrl	r0
	ret
9:
	movl	$EFAULT,r0
	ret

/*
 * Copy a block of data from the kernel 
 * address space to the user address space.
 *
 * copyout(fromaddr, toaddr, count)
 */
ENTRY(copyout, 0)
	movl	12(fp),r0		# get count
	blss	9b
	movl	8(fp),r1		# get user address
	cmpl	$(CLSIZE*NBPG),r0	# can do in one probew?
	bgeq	2f			# yes
1:
	probew	$1,(r1),$(CLSIZE*NBPG)	# bytes accessible?
	beql	9b			# no 
	addl2	$(CLSIZE*NBPG),r1	# increment user address
	_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r0,1b)	# reduce count and loop
2:
	probew	$1,(r1),r0		# bytes accessible?
	beql	9b			# no
	MOVC3(4(fp),8(fp),12(fp))
	clrl	r0
	ret

/*
 * savectx is like setjmp but saves all registers.
 * Called before swapping out the u. area, restored by resume()
 * below.
 */
ENTRY(savectx, 0)
	movl	4(fp),r2
	storer	$0x1ff8,(r2); addl2 $40,r2	# r3-r12
	movl	(fp),(r2); addl2 $4,r2		# fp
	movab	8(fp),(r2); addl2 $4,r2		# sp
	movl	-8(fp),(r2)			# pc
	clrl	r0
	ret

#ifdef KADB
/*
 * C library -- reset, setexit -- XXX
 *
 *	reset(x)
 * will generate a "return" from
 * the last call to
 *	setexit()
 * by restoring r2 - r12, fp
 * and doing a return.
 * The returned value is x; on the original
 * call the returned value is 0.
 */
ENTRY(setexit, 0)
	movab	setsav,r0
	storer	$0x1ffc, (r0)
	movl	(fp),44(r0)		# fp
	moval	4(fp),48(r0)		# sp
	movl	-8(fp),52(r0)		# pc
	clrl	r0
	ret

ENTRY(reset, 0)
	movl	4(fp),r0	# returned value
	movab	setsav,r1
	loadr	$0x1ffc,(r1)
	movl	44(r1),fp
	movl	48(r1),sp
	jmp 	*52(r1)

	.data
	.align	2
setsav:	.space	14*4
	.text
#endif

	.globl	_whichqs
	.globl	_qs
	.globl	_cnt

	.globl	_noproc
	.comm	_noproc,4
	.globl	_runrun
	.comm	_runrun,4
/*
 * The following primitives use the fancy TAHOE instructions.
 * _whichqs tells which of the 32 queues _qs
 * have processes in them.  setrq puts processes into queues, remrq
 * removes them from queues.  The running process is on no queue,
 * other processes are on a queue related to p->p_pri, divided by 4
 * actually to shrink the 0-127 range of priorities into the 32 available
 * queues.
 */

/*
 * setrq(p), using fancy TAHOE instructions.
 *
 * Call should be made at spl8(), and p->p_stat should be SRUN
 */
ENTRY(setrq, 0)
	movl	4(fp),r0
	tstl	P_RLINK(r0)		## firewall: p->p_rlink must be 0
	beql	set1			##
	pushab	set3			##
	callf	$8,_panic		##
set1:
	movzbl	P_PRI(r0),r1		# put on queue which is p->p_pri / 4
	shar	$2,r1,r1
	shal	$1,r1,r2
	moval	_qs[r2],r2
	insque	(r0),*4(r2)		# at end of queue
	shal	r1,$1,r1
	orl2	r1,_whichqs		# mark queue non-empty
	ret

set3:	.asciz	"setrq"

/*
 * remrq(p), using fancy TAHOE instructions
 *
 * Call should be made at spl8().
 */
ENTRY(remrq, 0)
	movl	4(fp),r0
	movzbl	P_PRI(r0),r1
	shar	$2,r1,r1
	bbs	r1,_whichqs,rem1
	pushab	rem3			# it wasn't recorded to be on its q
	callf	$8,_panic
rem1:
	remque	(r0)
	bneq	rem2			# q not empty yet
	shal	r1,$1,r1
	mcoml	r1,r1
	andl2	r1,_whichqs		# mark queue empty
rem2:
	clrl	P_RLINK(r0)		## for firewall checking
	ret

rem3:	.asciz	"remrq"

/*
 * Masterpaddr is the p->p_addr of the running process on the master
 * processor.  When a multiprocessor system, the slave processors will have
 * an array of slavepaddr's.
 */
	.globl	_masterpaddr
	.data
	.align	2
_masterpaddr: .long	0

	.text
sw0:	.asciz	"swtch"

/*
 * When no processes are on the runq, swtch branches to idle
 * to wait for something to come ready.
 */
	.globl  Idle
Idle: idle:
	movl	$1,_noproc
	mtpr	$0,$IPL			# must allow interrupts here
1:
	tstl	_whichqs		# look for non-empty queue
	bneq	sw1
	brb	1b

badsw:	pushab	sw0
	callf	$8,_panic
	/* NOTREACHED */

	.align	2
/*
 * swtch(), using fancy tahoe instructions
 */
ENTRY(swtch, 0)
	movl	(fp),fp			# prepare for rei
	movl	(sp),4(sp)		# saved pc
	tstl	(sp)+
	movpsl	4(sp)
	incl	_cnt+V_SWTCH
sw1:	ffs	_whichqs,r0		# look for non-empty queue
	blss	idle			# if none, idle
	mtpr	$0x18,$IPL		# lock out all so _whichqs==_qs
	bbc	r0,_whichqs,sw1		# proc moved via interrupt
	shal	$1,r0,r1
	moval	_qs[r1],r1
	movl	(r1),r2			# r2 = p = highest pri process
	remque	*(r1)
	bvs	badsw			# make sure something was there
	bneq	sw2
	shal	r0,$1,r1
	mcoml	r1,r1
	andl2	r1,_whichqs		# no more procs in this queue
sw2:
	clrl	_noproc
	clrl	_runrun
#ifdef notdef
	tstl	P_WCHAN(r2)		## firewalls
	bneq	badsw			##
	cmpb	P_STAT(r2),$SRUN	##
	bneq	badsw			##
#endif
	clrl	P_RLINK(r2)		##
	movl	*P_ADDR(r2),r0
#ifdef notdef
	cmpl	r0,_masterpaddr		# resume of current proc is easy
	beql	res0
#endif
	movl	r0,_masterpaddr
	shal	$PGSHIFT,r0,r0		# r0 = pcbb(p)
	brb	swresume

/*
 * resume(pf)
 */
ENTRY(resume, 0)
	shal	$PGSHIFT,4(fp),r0	# r0 = pcbb(pf)
	movl	(fp),fp			# prepare for rei
	movl	(sp)+,4(sp)		# saved pc
	tstl	(sp)+
	movpsl	4(sp)
swresume:
	mtpr	$0x18,$IPL		# no interrupts, please
	movl	_CMAP2,_u+PCB_CMAP2	# yech
	REST_ACC			# restore original accumulator
	svpctx
	mtpr	r0,$PCBB
	ldpctx
	movl	_u+PCB_CMAP2,_CMAP2	# yech
	mtpr	$_CADDR2,$TBIS
res0:
	movl	_u+U_PROCP,r2		# r2 = u.u_procp
	tstl	P_CKEY(r2)		# does proc have code key?
	bneq	1f
	callf	$4,_getcodekey		# no, give him one
	movl	_u+U_PROCP,r2		# r2 = u.u_procp
	movl	r0,P_CKEY(r2)
1:
	tstl	P_DKEY(r2)		# does proc have data key?
	bneq	1f
	callf	$4,_getdatakey		# no, give him one
	movl	_u+U_PROCP,r2		# r2 = u.u_procp
	movl	r0,P_DKEY(r2)
1:
	mtpr	P_CKEY(r2),$CCK		# set code cache key
	mtpr	P_DKEY(r2),$DCK		# set data cache key
	tstl	_u+PCB_SSWAP
	bneq	res1
	rei
res1:					# restore alternate saved context
	movl	_u+PCB_SSWAP,r2
	clrl	_u+PCB_SSWAP
	loadr	$0x3ff8,(r2); addl2 $44,r2	# restore r3-r13 (r13=fp)
	movl	(r2),r1; addl2 $4,r2	# fetch previous sp ...
	movab	(sp),r0			# ... and current sp and
	cmpl	r1,r0			# check for credibility,
	bgequ	1f			# if further up stack ...
	pushab 2f; callf $8,_panic	# ... panic
	/*NOTREACHED*/
1:					# sp ok, complete return
	movl	r1,sp			# restore sp
	pushl	$PSL_PRVMOD		# kernel mode, ipl 0
	pushl	(r2)			# return address
	rei
2:	.asciz	"ldctx"

/*
 * {fu,su},{byte,word}
 */
ENTRY(fuword, 0)
	movl	4(fp), r1
	prober	$1,(r1),$4		# check access
	beql	fserr			# page unreadable
	bitl	$1,r1			# check byte alignment
	bneq	2f			# odd, do byte-word-byte
	bitl	$2,r1			# check word alignment
	bneq	1f			# odd, do in 2 words
	movl	(r1),r0			# move longword
	ret
1:
	movw	(r1),r0			# move two words
	shal	$16,r0,r0
	movzwl	2(r1),r1		# orw2 sign extends
	orl2	r1,r0
	ret
2:
	movb	(r1),r0			# move byte-word-byte
	shal	$24,r0,r0
	movzwl	1(r1),r2		# orw2 sign extends
	shal	$8,r2,r2
	movzbl	3(r1),r1		# orb2 sign extends
	orl2	r2,r1
	orl2	r1,r0
	ret
fserr:
	mnegl	$1,r0
	ret

ENTRY(fubyte, 0)
	prober	$1,*4(fp),$1
	beql	fserr
	movzbl	*4(fp),r0
	ret

ENTRY(suword, 0)
	movl	4(fp), r0
	probew	$1,(r0),$4		# check access
	beql	fserr			# page unwritable
	bitl	$1,r0			# check byte alignment
	bneq	1f			# odd byte boundary
	bitl	$2,r0			# check word alignment
	beql	2f			# longword aligned
	movw	8(fp),(r0)		# move two words
	movw	10(fp),2(r0)
	jbr	3f
1:
	movb	8(fp),(r0)
	movb	9(fp),1(r0)
	movb	10(fp),2(r0)
	movb	11(fp),3(r0)
	jbr	3f
2:
	movl	8(fp),(r0)
3:
	clrl	r0
	ret

ENTRY(subyte, 0)
	probew	$1,*4(fp),$1
	beql	fserr
	movb	11(fp),*4(fp)
	clrl	r0
	ret

/*
 * Copy 1 relocation unit (NBPG bytes)
 * from user virtual address to physical address
 */
ENTRY(copyseg, 0)
	orl3	$PG_V|PG_KW,8(fp),_CMAP2
	mtpr	$_CADDR2,$TBIS	# invalidate entry for copy 
	MOVC3(4(fp),$_CADDR2,$NBPG)
	ret

/*
 * Clear a page of memory.  The page frame is specified.
 *
 * clearseg(pf);
 */
ENTRY(clearseg, 0)
	orl3	$PG_V|PG_KW,4(fp),_CMAP1	# Maps to virtual addr CADDR1
	mtpr	$_CADDR1,$TBIS
	movl	$255,r0				# r0 = limit
	clrl	r1				# r1 = index of cleared long
1:
	clrl	_CADDR1[r1]
	aobleq	r0,r1,1b
	ret

/*
 * Check user mode read/write access.
 *
 * useracc(addr, count, mode)
 *	caddr_t addr; int count, mode;
 * mode = 0	write access
 * mode = 1	read access
 */
ENTRY(useracc, 0)
	movl	$1,r2			# r2 = 'user mode' for probew/probew
probes:
	movl	4(fp),r0		# get va
	movl	8(fp),r1		# count
	tstl	12(fp)			# test for read access ?
	bneq	userar			# yes
	cmpl	$(CLSIZE*NBPG),r1	# can we do it in one probe ?
	bgeq	uaw2			# yes
uaw1:
	probew	r2,(r0),$(CLSIZE*NBPG)
	beql	uaerr			# no access
	addl2	$(CLSIZE*NBPG),r0
	_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r1,uaw1)
uaw2:
	probew	r2,(r0),r1
	beql	uaerr
	movl	$1,r0
	ret
userar:
	cmpl	$(CLSIZE*NBPG),r1
	bgeq	uar2
uar1:
	prober	r2,(r0),$(CLSIZE*NBPG)
	beql	uaerr
	addl2	$(CLSIZE*NBPG),r0
	_ACBL($(CLSIZE*NBPG+1),$(-CLSIZE*NBPG),r1,uar1)
uar2:
	prober	r2,(r0),r1
	beql	uaerr
	movl	$1,r0
	ret
uaerr:
	clrl	r0
	ret

/*
 * Check kernel mode read/write access.
 *
 * kernacc(addr, count, mode)
 *	caddr_t addr; int count, mode;
 * mode = 0	write access
 * mode = 1	read access
 */
ENTRY(kernacc, 0)
	clrl	r2		# r2 = 0 means kernel mode probe.
	jbr	probes		# Dijkstra would get gastric distress here.

/*
 * addupc - increment some histogram counter
 *	in the profiling buffer
 *
 * addupc(pc, prof, delta)
 *	long pc; short delta; struct uprof *prof;
 * 
 * struct uprof {		# profile arguments 
 * 	short	*r_base;	# buffer base 
 * 	unsigned pr_size;	# buffer size 
 * 	unsigned pr_off;	# pc offset 
 * 	unsigned pr_scale;	# pc scaling 
 * }
 */
ENTRY(addupc, 0)
	movl	8(fp),r2		# r2 points to structure
	subl3	8(r2),4(fp),r0		# r0 = PC - lowpc
	jlss	9f			# PC < lowpc , out of range !
	shrl	$1,r0,r0		# the unit is words
	shrl	$1,12(r2),r1		# ditto for scale
	emul	r1,r0,$0,r0
	shrq	$14,r0,r0
	tstl	r0			# too big
	jneq	9f
	cmpl	r1,4(r2)		# Check buffer overflow
	jgequ	9f
	probew	$1,*0(r2)[r1],$2	# counter accessible?
	jeql	9f
	shrl	$1,r1,r1		# make r1 word index
	addw2	14(fp),*0(r2)[r1]
9:	ret

/*
 * scanc(size, cp, table, mask)
 */
ENTRY(scanc, R3|R4)
	movl	8(fp),r0		# r0 = cp
	addl3	4(fp),r0,r2		# end = &cp[size]
	movl	12(fp),r1		# r1 = table
	movb	19(fp),r4		# r4 = mask
	decl	r0			# --cp
	jbr	0f			# just like Fortran...
1:					# do {
	movzbl	(r0),r3	
	bitb	r4,(r1)[r3]		#	if (table[*cp] & mask)
	jneq	2f			#		break;
0:	aoblss	r2,r0,1b		# } while (++cp < end);
2:
	subl3	r0,r2,r0; ret		# return (end - cp);

/*
 * skpc(mask, size, cp)
 */
ENTRY(skpc, 0)
	movl	12(fp),r0		# r0 = cp
	addl3	8(fp),r0,r1		# r1 = end = &cp[size];
	movb	7(fp),r2		# r2 = mask
	decl	r0			# --cp;
	jbr	0f
1:					# do
	cmpb	(r0),r2			#	if (*cp != mask)
	jneq	2f			#		break;
0:	aoblss	r1,r0,1b		# while (++cp < end);
2:
	subl3	r0,r1,r0; ret		# return (end - cp);

/*
 * locc(mask, size, cp)
 */
ENTRY(locc, 0)
	movl	12(fp),r0		# r0 = cp
	addl3	8(fp),r0,r1		# r1 = end = &cp[size]
	movb	7(fp),r2		# r2 = mask
	decl	r0			# --cp;
	jbr	0f
1:					# do
	cmpb	(r0),r2			#	if (*cp == mask)
	jeql	2f			#		break;
0:	aoblss	r1,r0,1b		# while (++cp < end);
2:
	subl3	r0,r1,r0; ret		# return (end - cp);

#ifdef ALIGN
#include "../tahoealign/align.h"

	.globl	_alignment
/*
 * There's an intimate relationship between this piece of code
 * and the alignment emulation code (especially the layout
 * of local variables in alignment.c! Don't change unless
 * you update both this, alignment.h and alignment.c !!
 */
non_aligned:
	orb2	$EMULATEALIGN,_u+U_EOSYS
	incl	_cnt+V_TRAP
	incl	_cnt+V_ALIGN		# count emulated alignment traps
	moval	4(sp),_user_psl
	SAVE_FPSTAT(4)			# Also zeroes out ret_exception !
	pushl	$0			# ret_addr
	pushl	$0			# ret_code
	mfpr	$USP,-(sp)		# user sp
	callf	$4,_alignment		# call w/o parms so regs may be modified
	/*
	 * We return here after a successful emulation or an exception.
	 * The registers have been restored and we must not alter them
	 * before returning to the user.
	 */
2:	mtpr	(sp)+,$USP		# restore user sp
	tstl	8(sp)			# Any exception ?
	bneq	got_excp		# Yes, reflect it back to user.
	moval	8(sp),sp		# pop 2 zeroes pushed above
	REST_FPSTAT
	xorb2	$EMULATEALIGN,_u+U_EOSYS

	bitl	$PSL_T,4(sp)		# check for trace bit set
	beql	9f
	CHECK_SFE(4)
	pushl $0
	SAVE_FPSTAT(8)
	TRAP(TRCTRAP)
9:	rei

got_excp:				# decode exception
	casel	8(sp),$ILL_ADDRMOD,$ALIGNMENT
	.align	1
L1:
	.word	ill_addrmod-L1
	.word	ill_access-L1
	.word	ill_oprnd-L1
	.word	arithmetic-L1
	.word	alignment-L1
	brw	alignment		# default - shouldn't come here at all !

ill_addrmod:				# No other parameters. Set up stack as
	moval	8(sp),sp		# the HW would do it in a real case.
	REST_FPSTAT
	jbr	_Xresadflt
ill_oprnd:
	moval	8(sp),sp
	REST_FPSTAT
	jbr	_Xresopflt
alignment:
	moval	8(sp),sp
	REST_FPSTAT
	jbr	align_excp	# NB: going to _Xalignflt would cause loop
ill_access:
	/*
	 * Must restore accumulator w/o modifying sp and w/o using
	 * registers.  Solution: copy things needed by REST_FPSTAT.
	 */
	pushl	20(sp)			# The flags longword
	pushl	20(sp)			# acc_low
	pushl	20(sp)			# acc_high
	pushl	20(sp)			# ret_exception ignored by REST_FPSTAT 
	REST_FPSTAT			# Back where we were with the sp !
	movl	(sp),16(sp)		# code for illegal access
	movl	4(sp),20(sp)		# original virtual address
	moval	16(sp),sp		# Just like the HW would set it up
	jbr	_Xprotflt
arithmetic:				# same trickery as above
	pushl	20(sp)			# The flags longword
	pushl	20(sp)			# acc_low
	pushl	20(sp)			# acc_high
	pushl	20(sp)			# ret_exception ignored by REST_FPSTAT 
	REST_FPSTAT			# Back where we were with the sp !
	movl	(sp),20(sp)		# code for arithmetic exception
	moval	20(sp),sp		# Just like the HW would set it up
	jbr	_Xarithtrap
#endif
