/*	$NetBSD: locore.s,v 1.170 2014/03/15 12:11:23 tsutsui Exp $	*/

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1982, 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*-
 * Copyright (C) 1993	Allen K. Briggs, Chris P. Caputo,
 *			Michael L. Finch, Bradley A. Grantham, and
 *			Lawrence A. Kesteloot
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the Alice Group.
 * 4. The names of the Alice Group or any of its members may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 * from: Utah $Hdr: locore.s 1.58 91/04/22$
 *
 *	@(#)locore.s	7.11 (Berkeley) 5/9/91
 */

#include "opt_compat_netbsd.h"
#include "opt_compat_svr4.h"
#include "opt_compat_sunos.h"
#include "opt_ddb.h"
#include "opt_fpu_emulate.h"
#include "opt_kgdb.h"
#include "opt_lockdebug.h"
#include "opt_fpsp.h"
#include "opt_m68k_arch.h"

#include "assym.h"

#include <machine/asm.h>
#include <machine/trap.h>

/*
 * This is for kvm_mkdb, and should be the address of the beginning
 * of the kernel text segment (not necessarily the same as kernbase).
 */
	.text
GLOBAL(kernel_text)

#include <mac68k/mac68k/vectors.s>
#include <mac68k/mac68k/macglobals.s>

/*
 * Initialization
 */

	.data
| Scratch memory.  Careful when messing with these...
ASLOCAL(longscratch)
	.long	0
ASLOCAL(longscratch2)
	.long	0
ASLOCAL(pte_tmp)			| for get_pte()
	.long	0 
GLOBAL(macos_crp1)
	.long	0
GLOBAL(macos_crp2)
	.long	0
GLOBAL(macos_tc)
	.long	0
GLOBAL(macos_tt0)
	.long	0
GLOBAL(macos_tt1)
	.long	0
GLOBAL(bletch)
	.long	0

BSS(esym,4)

ASENTRY_NOPROFILE(start)
	movw	#PSL_HIGHIPL,%sr	| no interrupts.  ever.
	lea	_ASM_LABEL(tmpstk),%sp	| give ourselves a temporary stack

	movl	#CACHE_OFF,%d0
	movc	%d0,%cacr		| clear and disable on-chip cache(s)

	/* Initialize source/destination control registers for movs */
	movql	#FC_USERD,%d0		| user space
	movc	%d0,%sfc		|   as source
	movc	%d0,%dfc		|   and destination of transfers

	/*
	 * Some parameters provided by MacOS
	 *
	 * LAK: This section is the new way to pass information from the booter
	 * to the kernel.  At A1 there is an environment variable which has
	 * a bunch of stuff in ascii format, "VAR=value\0VAR=value\0\0".
	 */
	movl	%a1,%sp@-		| Address of buffer
	movl	%d4,%sp@-		| Some flags... (mostly not used)
	jbsr	_C_LABEL(getenvvars)	| Parse the environment buffer
	addql	#8,%sp

	/* Determine MMU/MPU from what we can test empirically */
	movl	#0x200,%d0		| data freeze bit
	movc	%d0,%cacr		|   only exists on 68030
	movc	%cacr,%d0		| read it back
	tstl	%d0			| zero?
	jeq	Lnot68030		| yes, we have 68020/68040

	movl	#CACHE_OFF,%d0		| disable and clear both caches
	movc	%d0,%cacr
	lea	_C_LABEL(mmutype),%a0	| no, we have 68030
	movl	#MMU_68030,%a0@		| set to reflect 68030 PMMU
	lea	_C_LABEL(cputype),%a0
	movl	#CPU_68030,%a0@		| and 68030 MPU
	jra	Lstart1

Lnot68030:
	bset	#31,%d0			| data cache enable bit
	movc	%d0,%cacr		|   only exists on 68040
	movc	%cacr,%d0		| read it back
	tstl	%d0			| zero?
	beq	Lis68020		| yes, we have 68020

	movql	#CACHE40_OFF,%d0	| now turn it back off
	movc	%d0,%cacr		|   before we access any data
	.word	0xf4f8			| cpusha bc ;push and invalidate caches
	lea	_C_LABEL(mmutype),%a0
	movl	#MMU_68040,%a0@		| Reflect 68040 MMU
	lea	_C_LABEL(cputype),%a0
	movl	#CPU_68040,%a0@		| and 68040 MPU
	jra	Lstart1

Lis68020:
	movl	#CACHE_OFF,%d0		| disable and clear cache
	movc	%d0,%cacr
	lea	_C_LABEL(mmutype),%a0	| Must be 68020+68851
	movl	#MMU_68851,%a0@		| Reflect 68851 PMMU
	lea	_C_LABEL(cputype),%a0
	movl	#CPU_68020,%a0@		| and 68020 MPU

Lstart1:
	/*
	 * Now that we know what CPU we have, initialize the address error
	 * and bus error handlers in the vector table:
	 *
	 *	vectab+8	bus error
	 *	vectab+12	address error
	 */
	lea	_C_LABEL(cputype),%a0
	lea	_C_LABEL(vectab),%a2
#if defined(M68040)
	cmpl	#CPU_68040,%a0@		| 68040?
	jne	1f			| no, skip
	movl	#_C_LABEL(buserr40),%a2@(8)
	movl	#_C_LABEL(addrerr4060),%a2@(12)
	jra	Lstart2
1:
#endif
#if defined(M68020) || defined(M68030)
	cmpl	#CPU_68040,%a0@		| 68040?
	jeq	1f			| yes, skip
	movl	#_C_LABEL(busaddrerr2030),%a2@(8)
	movl	#_C_LABEL(busaddrerr2030),%a2@(12)
	jra	Lstart2
1:
#endif
	/* Config botch; no hope. */
	movl	_C_LABEL(MacOSROMBase),%a1 | Load MacOS ROMBase
	jra	Ldoboot1

Lstart2:
	jbsr	_C_LABEL(setmachdep)	| Set some machine-dep stuff
	jbsr	_C_LABEL(consinit)	| XXX Should only be if graybar on

/*
 * Figure out MacOS mappings and bootstrap NetBSD
 */
	lea	_C_LABEL(macos_tc),%a0	| get current %TC
	cmpl	#MMU_68040,_C_LABEL(mmutype) | check to see if 68040
	jeq	Lget040TC

	pmove	%tc,%a0@
	jra	Lstart3

Lget040TC:
#if 0
	movl	_C_LABEL(current_mac_model),%a1	 | if an AV Mac, save current
	cmpl	#MACH_CLASSAV,%a1@(CPUINFO_CLASS) | %TC so internal video will
	jne	LnotAV				 | get configured
#endif
	.long	0x4e7a0003		| movc %tc,%d0
	jra	LsaveTC
LnotAV:	
	movql	#0,%d0			| otherwise,
	.long	0x4e7b0003		| movc %d0,%tc ;Disable MMU
LsaveTC:	
	movl	%d0,%a0@

Lstart3:
	movl	%a0@,%sp@-		| get Mac OS mapping, relocate video,
	jbsr	_C_LABEL(bootstrap_mac68k) |   bootstrap pmap, et al.
	addql	#4,%sp

	/*
	 * Set up the vector table, and race to get the MMU
	 * enabled.
	 */
	movl	#_C_LABEL(vectab),%d0	| set Vector Base Register
	movc	%d0,%vbr
	
	movl	_C_LABEL(Sysseg),%a1	| system segment table addr
	addl	_C_LABEL(load_addr),%a1	| Make it physical addr
	cmpl	#MMU_68040,_C_LABEL(mmutype)
	jne	Lenablepre040MMU	| if not 040, skip

	movql	#0,%d0
	.long	0x4e7b0003		| movc %d0,%tc   ;Disable MMU
	.long	0x4e7b0004		| movc %d0,%itt0 ;Disable itt0
	.long	0x4e7b0005		| movc %d0,%itt1 ;Disable itt1
	.long	0x4e7b0006		| movc %d0,%dtt0 ;Disable dtt0
	.long	0x4e7b0007		| movc %d0,%dtt1 ;Disable dtt1
	movl	%a1,%d1
	.word	0xf518			| pflusha
	.long	0x4e7b1807		| movc %d1,%srp

#if defined(DJMEMCMAX)
	movl	%a3,%sp@-
	cmp	#MACH_MACC610,_C_LABEL(machineid)
	jra	Ldjmemc610
	cmp	#MACH_MACQ610,_C_LABEL(machineid)
	jra	Ldjmemc610
	cmp	#MACH_MACC650,_C_LABEL(machineid)
	jra	Ldjmemccfg
	cmp	#MACH_MACQ650,_C_LABEL(machineid)
	jra	Ldjmemccfg
	cmp	#MACH_MACQ800,_C_LABEL(machineid)
	jra	Ldjmemccfg

	jra	Lnodjmemc
       
Ldjmemccfg:
	movl	#0x50f0e00c,%a0
	movl	%a0@,%d0	| determine where RAM SIMMs start
	andl	#0x000000FF,%d0
	addl	#0x10,%d0	| bank 3 start
	addl	#0x10,%d0	| bank 4 start

	movl	#0x50f0e014,%a0
	movl	%d0,%a0@+	| bank 4
	addl	#0x10,%d0
	movl	%d0,%a0@+	| bank 5
	addl	#0x10,%d0

	movl	%d0,%a0@+	| bank 6
	addl	#0x10,%d0
	movl	%d0,%a0@+	| bank 7
	addl	#0x10,%d0
	movl	%d0,%a0@+	| bank 8
	addl	#0x10,%d0
	movl	%d0,%a0@+	| bank 9
	addl	#0x10,%d0
	jra	Ldjmemctop

Ldjmemc610:
	movl	#0x50f0e00c,%a0
	movl	%a0@,%d0	| determine where RAM SIMMs start
	andl	#0x000000FF,%d0
	addl	#0x10,%d0	| bank 3 start

	movl	#0x50f0e014,%a0
	movl	%d0,%a0@+	| bank 4
	addl	#0x10,%d0
	movl	%d0,%a0@+	| bank 5
	movl	%d0,%a0@+	| bank 6
	addl	#0x10,%d0
	movl	%d0,%a0@+	| bank 7
	movl	%d0,%a0@+	| bank 8
	addl	#0x10,%d0
	movl	%d0,%a0@+	| bank 9

Ldjmemctop:
	movl	#0x50F0E02C,%a0
	movl	%d0,%a0@	| memtop

	| preserve ~512KB beyond 4MB just in case
	movl	#0x400000,%a0
	movl	#0x9000000,%a2
	movl	#0xFFFF,%d0
L1stbankcopy1:
	movl	%a0@+,%a2@+
	dbra	%d0,L1stbankcopy1
	movl	#0xFFFF,%d0
L1stbankcopy2:
	movl	%a0@+,%a2@+
	dbra	%d0,L1stbankcopy2

	lea	_ASM_LABEL(Lsetup1stbank),%a0
	movl	#0x8800000,%a2	| Pick a location that should be in bank 4
	movl	#0x64,%d0
Ldjcopy:
	movl	%a0@+,%a2@+
	dbra	%d0,Ldjcopy

	movl	#0x8800000,%a0
	lea	_ASM_LABEL(Ldjmemcdone),%a2
	jmp	%a0@

Lsetup1stbank:
	| now configure banks 2 & 3
	movl	#0x50f0e00c,%a0
	movl	%a0@,%d0	| determine where RAM SIMMs start
	andl	#0x000000FF,%d0
	movl	%d0,%a0@+
	addl	#0x10,%d0	| bank 3 start
	movl	%d0,%a0@

	| and return to where we came from.
	jmp	%a2@

Ldjmemcdone:
	movl	#0x400000,%a2
	movl	#0x9000000,%a0
	movl	#0xFFFF,%d0
Lcopyback1:
	movl	%a0@+,%a2@+
	dbra	%d0,Lcopyback1
	movl	#0xFFFF,%d0
Lcopyback2:
	movl	%a0@+,%a2@+
	dbra	%d0,Lcopyback2

Lnodjmemc:
	movl	%sp@+,%a3
#endif

#if PGSHIFT == 13
	movl	#0xc000,%d0
#else
	movl	#0x8000,%d0
#endif
	.long	0x4e7b0003		| movc %d0,%tc   ;Enable MMU
	movl	#CACHE40_ON,%d0
	movc	%d0,%cacr		| turn on both caches
	jra	Lloaddone

Lenablepre040MMU:
	tstl	_C_LABEL(mmutype)	| TTx instructions will break 68851
	jgt	LnokillTT

	lea	_ASM_LABEL(longscratch),%a0 | disable TTx registers on 68030
	movl	#0,%a0@
	.long	0xf0100800		| movl %a0@,%tt0
	.long	0xf0100c00		| movl %a0@,%tt1

LnokillTT:
	lea	_C_LABEL(protorp),%a0
	movl	#0x80000202,%a0@	| nolimit + share global + 4 byte PTEs
	movl	%a1,%a0@(4)		| + segtable address
	pmove	%a0@,%srp		| load the supervisor root pointer
	movl	#0x80000002,%a0@	| reinit upper half for CRP loads
	pflusha
	lea	_ASM_LABEL(longscratch),%a2
#if PGSHIFT == 13
	movl	#0x82d08b00,%a2@	| value to load %TC with
#else
	movl	#0x82c0aa00,%a2@	| value to load %TC with
#endif
	pmove	%a2@,%tc		| load it

Lloaddone:

/*
 * Should be running mapped from this point on
 */
	lea	_ASM_LABEL(tmpstk),%sp	| temporary stack
/* call final pmap setup */
	jbsr	_C_LABEL(pmap_bootstrap_finalize)
/* set kernel stack, user SP, lwp0, and initial pcb */
	movl	_C_LABEL(lwp0uarea),%a1	| get lwp0 uarea
	lea	%a1@(USPACE-4),%sp	|   set kernel stack to end of area
	movl	#USRSTACK-4,%a2
	movl	%a2,%usp		| init %USP

/* flush TLB and turn on caches */
	cmpl	#MMU_68040,_C_LABEL(mmutype) | 68040?
	jeq	Ltbia040		| yes, cache already on
	pflusha
	movl	#CACHE_ON,%d0
	movc	%d0,%cacr		| clear cache(s)
#ifdef __notyet__
	tstl	_C_LABEL(ectype)
	jeq	Lnocache0
					| Enable external cache here
#endif
	jra	Lnocache0

Ltbia040:
	.word	0xf518			| pflusha

Lnocache0:
/* Final setup for call to main(). */
	jbsr	_C_LABEL(mac68k_init)

/*
 * Create a fake exception frame so that cpu_lwp_fork() can copy it.
 * main() nevers returns; we exit to user mode from a forked process
 * later on.
 */
	clrw	%sp@-			| vector offset/frame type
	clrl	%sp@-			| PC - filled in by "execve"
	movw	#PSL_USER,%sp@-		| in user mode
	clrl	%sp@-			| stack adjust count and padding
	lea	%sp@(-64),%sp		| construct space for D0-D7/A0-A7
	lea	_C_LABEL(lwp0),%a0	| save pointer to frame
	movl	%sp,%a0@(L_MD_REGS)	|   in lwp0.l_md.md_regs

	jra	_C_LABEL(main)		| main()
	PANIC("main() returned")
	/* NOTREACHED */

/*
 * Trap/interrupt vector routines
 */ 
#include <m68k/m68k/trap_subr.s>

/*
 * Use common m68k bus error and address error handlers.
 */
	.data
GLOBAL(mac68k_a2_fromfault)
	.long	0
GLOBAL(m68k_fault_addr)
	.long	0

#include <m68k/m68k/busaddrerr.s>

/*
 * FP exceptions.
 */
ENTRY_NOPROFILE(fpfline)
#if defined(M68040)
	cmpl	#FPU_68040,_C_LABEL(fputype) | 68040 FPU?
	jne	Lfp_unimp		| no, skip FPSP
	cmpw	#0x202c,%sp@(6)		| format type 2?
	jne	_C_LABEL(illinst)	| no, not an FP emulation
Ldofp_unimp:
#ifdef FPSP
	jmp	_ASM_LABEL(fpsp_unimp)	| yes, go handle it
#endif
Lfp_unimp:
#endif /* M68040 */
#ifdef FPU_EMULATE
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-		| save registers
	moveq	#T_FPEMULI,%d0		| denote as FP emulation trap
	jra	_ASM_LABEL(fault)	| do it
#else
	jra	_C_LABEL(illinst)
#endif

ENTRY_NOPROFILE(fpunsupp)
#if defined(M68040)
	cmpl	#FPU_68040,_C_LABEL(fputype) | 68040 FPU?
	jne	_C_LABEL(illinst)	| no, treat as illinst
#ifdef FPSP
	jmp	_ASM_LABEL(fpsp_unsupp)	| yes, go handle it
#endif
Lfp_unsupp:
#endif /* M68040 */
#ifdef FPU_EMULATE
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-		| save registers
	moveq	#T_FPEMULD,%d0		| denote as FP emulation trap
	jra	_ASM_LABEL(fault)	| do it
#else
	jra	_C_LABEL(illinst)
#endif

/*
 * Handles all other FP coprocessor exceptions.
 * Note that since some FP exceptions generate mid-instruction frames
 * and may cause signal delivery, we need to test for stack adjustment
 * after the trap call.
 */
ENTRY_NOPROFILE(fpfault)
	clrl	%sp@-		| stack adjust count
	moveml	#0xFFFF,%sp@-	| save user registers
	movl	%usp,%a0	| and save
	movl	%a0,%sp@(FR_SP)	|   the user stack pointer
	clrl	%sp@-		| no VA arg
	movl	_C_LABEL(curpcb),%a0 | current pcb
	lea	%a0@(PCB_FPCTX),%a0 | address of FP savearea
	fsave	%a0@		| save state
#if defined(M68040) || defined(M68060)
	/* always null state frame on 68040, 68060 */
	cmpl	#FPU_68040,_C_LABEL(fputype)
	jge	Lfptnull
#endif
	tstb	%a0@		| null state frame?
	jeq	Lfptnull	| yes, safe
	clrw	%d0		| no, need to tweak BIU
	movb	%a0@(1),%d0	| get frame size
	bset	#3,%a0@(0,%d0:w) | set exc_pend bit of BIU
Lfptnull:
	fmovem	%fpsr,%sp@-	| push %fpsr as code argument
	frestore %a0@		| restore state
	movl	#T_FPERR,%sp@-	| push type arg
	jra	_ASM_LABEL(faultstkadj) | call trap and deal with stack cleanup

/*
 * Other exceptions only cause four and six word stack frame and require
 * no post-trap stack adjustment.
 */

ENTRY_NOPROFILE(badtrap)
	moveml	#0xC0C0,%sp@-		| save scratch regs
	movw	%sp@(22),%sp@-		| push exception vector info
	clrw	%sp@-
	movl	%sp@(22),%sp@-		| and PC
	jbsr	_C_LABEL(straytrap)	| report
	addql	#8,%sp			| pop args
	moveml	%sp@+,#0x0303		| restore regs
	jra	_ASM_LABEL(rei)		| all done

ENTRY_NOPROFILE(trap0)
	clrl	%sp@-			| pad SR to longword
	moveml	#0xFFFF,%sp@-		| save user registers
	movl	%usp,%a0		| save %USP
	movl	%a0,%sp@(FR_SP)		|   in the savearea
	movl	%d0,%sp@-		| push syscall number
	jbsr	_C_LABEL(syscall)	| handle it
	addql	#4,%sp			| pop syscall arg
	tstl	_C_LABEL(astpending)
	jne	.Lrei2
	tstb	_C_LABEL(ssir)
	jeq	.Ltrap1
	movw	#SPL1,%sr
	tstb	_C_LABEL(ssir)
	jne	.Lsir1
.Ltrap1:
	movl	%sp@(FR_SP),%a0		| grab and restore
	movl	%a0,%usp		|   %USP
	moveml	%sp@+,#0x7FFF		| restore most registers
	addql	#8,%sp			| pop SSP and align word
	rte

/*
 * Trap 12 is the entry point for the cachectl "syscall" (both HP-UX & BSD)
 *	cachectl(command, addr, length)
 * command in %d0, addr in %a1, length in %d1
 */
ENTRY_NOPROFILE(trap12)
	movl	_C_LABEL(curlwp),%a0
	movl	%a0@(L_PROC),%sp@-	| push proc pointer
	movl	%d1,%sp@-		| push length
	movl	%a1,%sp@-		| push addr
	movl	%d0,%sp@-		| push command
	jbsr	_C_LABEL(cachectl1)	| do it
	lea	%sp@(16),%sp		| pop args
	jra	_ASM_LABEL(rei)		| all done

/*
 * Trace (single-step) trap.  Kernel-mode is special.
 * User mode traps are simply passed on to trap().
 */
ENTRY_NOPROFILE(trace)
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-
	moveq	#T_TRACE,%d0

	| Check PSW and see what happen.
	|   T=0 S=0	(should not happen)
	|   T=1 S=0	trace trap from user mode
	|   T=0 S=1	trace trap on a trap instruction
	|   T=1 S=1	trace trap from system mode (kernel breakpoint)

	movw	%sp@(FR_HW),%d1		| get PSW
	notw	%d1			| XXX no support for T0 on 680[234]0
	andw	#PSL_TS,%d1		| from system mode (T=1, S=1)?
	jeq	Lkbrkpt			| yes, kernel breakpoint
	jra	_ASM_LABEL(fault)	| no, user-mode fault

/*
 * Trap 15 is used for:
 *	- GDB breakpoints (in user programs)
 *	- KGDB breakpoints (in the kernel)
 *	- trace traps for SUN binaries (not fully supported yet)
 * User mode traps are simply passed to trap().
 */
ENTRY_NOPROFILE(trap15)
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-
	moveq	#T_TRAP15,%d0
	movw	%sp@(FR_HW),%d1		| get PSW
	andw	#PSL_S,%d1		| from system mode?
	jne	Lkbrkpt			| yes, kernel breakpoint
	jra	_ASM_LABEL(fault)	| no, user-mode fault

Lkbrkpt: | Kernel-mode breakpoint or trace trap. (%d0=trap_type)
	| Save the system %sp rather than the user %usp.
	movw	#PSL_HIGHIPL,%sr	| lock out interrupts
	lea	%sp@(FR_SIZE),%a6	| Save stack pointer
	movl	%a6,%sp@(FR_SP)		|  from before trap

	| If were are not on tmpstk switch to it.
	| (so debugger can change the stack pointer)
	movl	%a6,%d1
	cmpl	#_ASM_LABEL(tmpstk),%d1
	jls	Lbrkpt2			| already on tmpstk
	| Copy frame to the temporary stack
	movl	%sp,%a0			| %a0=src
	lea	_ASM_LABEL(tmpstk)-96,%a1 | %a1=dst
	movl	%a1,%sp			| %sp=new frame
	moveq	#FR_SIZE,%d1
Lbrkpt1:
	movl	%a0@+,%a1@+
	subql	#4,%d1
	bgt	Lbrkpt1

Lbrkpt2:
	| Call the trap handler for the kernel debugger.
	| Do not call trap() to do it, so that we can
	| set breakpoints in trap() if we want.  We know
	| the trap type is either T_TRACE or T_BREAKPOINT.
	| If we have both DDB and KGDB, let KGDB see it first,
	| because KGDB will just return 0 if not connected.
	| Save args in %d2, %a2
	movl	%d0,%d2			| trap type
	movl	%sp,%a2			| frame ptr
#ifdef KGDB
	| Let KGDB handle it (if connected)
	movl	%a2,%sp@-		| push frame ptr
	movl	%d2,%sp@-		| push trap type
	jbsr	_C_LABEL(kgdb_trap)	| handle the trap
	addql	#8,%sp			| pop args
	cmpl	#0,%d0			| did kgdb handle it?
	jne	Lbrkpt3			| yes, done
#endif
#ifdef DDB
	| Let DDB handle it
	movl	%a2,%sp@-		| push frame ptr
	movl	%d2,%sp@-		| push trap type
	jbsr	_C_LABEL(kdb_trap)	| handle the trap
	addql	#8,%sp			| pop args
#if 0	/* not needed on hp300 */
	cmpl	#0,%d0			| did ddb handle it?
	jne	Lbrkpt3			| yes, done
#endif
#endif
	/* Sun 3 drops into PROM here. */
Lbrkpt3:
	| The stack pointer may have been modified, or
	| data below it modified (by kgdb push call),
	| so push the hardware frame at the current %sp
	| before restoring registers and returning.

	movl	%sp@(FR_SP),%a0		| modified %sp
	lea	%sp@(FR_SIZE),%a1	| end of our frame
	movl	%a1@-,%a0@-		| copy 2 longs with
	movl	%a1@-,%a0@-		| ... predecrement
	movl	%a0,%sp@(FR_SP)		| %sp = h/w frame
	moveml	%sp@+,#0x7FFF		| restore all but %sp
	movl	%sp@,%sp		| ... and %sp
	rte				| all done

/* Use common m68k sigreturn */
#include <m68k/m68k/sigreturn.s>

/*
 * Interrupt handlers.
 *
 * Most 68k-based Macintosh computers
 *
 *      Level 0:        Spurious: ignored
 *      Level 1:        VIA1 (clock, ADB)
 *      Level 2:        VIA2 (NuBus, SCSI)
 *      Level 3:
 *      Level 4:        Serial (SCC)
 *      Level 5:
 *      Level 6:
 *      Level 7:        Non-maskable: parity errors, RESET button
 *
 * On the Q700, Q900 and Q950 in "A/UX mode": this should become:
 *
 *	Level 0:        Spurious: ignored
 *	Level 1:        Software
 *	Level 2:        VIA2 (except ethernet, sound)
 *	Level 3:        Ethernet
 *	Level 4:        Serial (SCC)
 *	Level 5:        Sound
 *	Level 6:        VIA1
 *	Level 7:        NMIs: parity errors, RESET button, YANCC error
 *
 * On the 660AV and 840AV:
 *
 *	Level 0:        Spurious: ignored
 *	Level 1:        VIA1 (clock, ADB)
 *	Level 2:        VIA2 (NuBus, SCSI)
 *	Level 3:        PSC device interrupt
 *	Level 4:        PSC DMA and serial
 *	Level 5:        ???
 *	Level 6:        ???
 *	Level 7:        NMIs: parity errors?, RESET button
 */

ENTRY_NOPROFILE(spurintr)
	addql	#1,_C_LABEL(intrcnt)+0
	INTERRUPT_SAVEREG
	CPUINFO_INCREMENT(CI_NINTR)
	INTERRUPT_RESTOREREG
	jra	_ASM_LABEL(rei)

ENTRY_NOPROFILE(intrhand)
	INTERRUPT_SAVEREG
	movw	%sp@(22),%sp@-		| push exception vector info
	clrw	%sp@-
	jbsr	_C_LABEL(intr_dispatch)	| call dispatch routine
	addql	#4,%sp
	INTERRUPT_RESTOREREG
	jra	_ASM_LABEL(rei)		| all done

ENTRY_NOPROFILE(lev7intr)
	addql	#1,_C_LABEL(intrcnt)+16
	clrl	%sp@-			| pad %SR to longword
	moveml	#0xFFFF,%sp@-		| save registers
	movl	%usp,%a0		| and save
	movl	%a0,%sp@(FR_SP)		|   the user stack pointer
	jbsr	_C_LABEL(nmihand)	| call handler
	movl	%sp@(FR_SP),%a0		| restore
	movl	%a0,%usp		|   %USP
	moveml	%sp@+,#0x7FFF		| and remaining registers
	addql	#8,%sp			| pop SSP and align word
	jra	_ASM_LABEL(rei)

/* 
 * We could tweak rtclock_intr and gain 12 cycles on the 020 and 030 by
 * saving the status register directly to the stack, but this would lose
 * badly on the 040.  Aligning the stack takes 10 more cycles than this
 * code does, so it's a good compromise.
 */
ENTRY_NOPROFILE(rtclock_intr)
	movl	%d2,%sp@-		| save %d2
	movw	%sr,%d2			| save SPL
	movw	_C_LABEL(ipl2psl_table)+IPL_CLOCK*2,%sr
					| raise SPL to splclock()
	movl	%a6@,%a1		| unwind to frame in intr_dispatch
	lea	%a1@(28),%a1		| push pointer to interrupt frame
	movl	%a1,%sp@-			| 28 = 16 for regs in intrhand,
					|    + 4 for args to intr_dispatch
					|    + 4 for return address to intrhand
					|    + 4 for value of %A6
	jbsr	_C_LABEL(hardclock)	| call generic clock int routine
	addql	#4,%sp			| pop param
	jbsr	_C_LABEL(mrg_VBLQueue)	| give programs in the VBLqueue a chance
	addql	#1,_C_LABEL(intrcnt)+32	| record a clock interrupt
	INTERRUPT_SAVEREG
	CPUINFO_INCREMENT(CI_NINTR)
	INTERRUPT_RESTOREREG
	movw	%d2,%sr			| restore SPL
	movl	%sp@+,%d2		| restore %d2
	rts				| go back from whence we came

/*
 * Emulation of VAX REI instruction.
 *
 * This code deals with checking for and servicing ASTs
 * (profiling, scheduling) and software interrupts (network, softclock).
 * We check for ASTs first, just like the VAX.  To avoid excess overhead
 * the T_ASTFLT handling code will also check for software interrupts so we
 * do not have to do it here.  After identifing that we need an AST we
 * drop the IPL to allow device interrupts.
 *
 * This code is complicated by the fact that sendsig may have been called
 * necessitating a stack cleanup.
 */

ASENTRY_NOPROFILE(rei)
	tstl	_C_LABEL(astpending)	| AST pending?
	jeq	.Lchksir		| no, go check for SIR
.Lrei1:
	btst	#5,%sp@			| yes, are we returning to user mode?
	jne	.Lchksir		| no, go check for SIR
	movw	#PSL_LOWIPL,%sr		| lower SPL
	clrl	%sp@-			| stack adjust
	moveml	#0xFFFF,%sp@-		| save all registers
	movl	%usp,%a1		| including
	movl	%a1,%sp@(FR_SP)		|    %USP
.Lrei2:
	clrl	%sp@-			| VA == none
	clrl	%sp@-			| code == none
	movl	#T_ASTFLT,%sp@-		| type == async system trap
	pea	%sp@(12)		| fp == address of trap frame
	jbsr	_C_LABEL(trap)		| go handle it
	lea	%sp@(16),%sp		| pop value args
	movl	%sp@(FR_SP),%a0		| restore %USP
	movl	%a0,%usp		|   from save area
	movw	%sp@(FR_ADJ),%d0	| need to adjust stack?
	jne	.Laststkadj		| yes, go to it
	moveml	%sp@+,#0x7FFF		| no, restore most user regs
	addql	#8,%sp			| toss %SP and stack adjust
	rte				| and do real RTE
.Laststkadj:
	lea	%sp@(FR_HW),%a1		| pointer to HW frame
	addql	#8,%a1			| source pointer
	movl	%a1,%a0			| source
	addw	%d0,%a0			|  + hole size = dest pointer
	movl	%a1@-,%a0@-		| copy
	movl	%a1@-,%a0@-		|  8 bytes
	movl	%a0,%sp@(FR_SP)		| new SSP
	moveml	%sp@+,#0x7FFF		| restore user registers
	movl	%sp@,%sp		| and our %SP
	rte				| and do real RTE
.Lchksir:
	tstb	_C_LABEL(ssir)		| SIR pending?
	jeq	.Ldorte			| no, all done
	movl	%d0,%sp@-		| need a scratch register
	movw	%sp@(4),%d0		| get SR
	andw	#PSL_IPL7,%d0		| mask all but IPL
	jne	.Lnosir			| came from interrupt, no can do
	movl	%sp@+,%d0		| restore scratch register
.Lgotsir:
	movw	#SPL1,%sr		| prevent others from servicing int
	tstb	_C_LABEL(ssir)		| too late?
	jeq	.Ldorte			| yes, oh well...
	clrl	%sp@-			| stack adjust
	moveml	#0xFFFF,%sp@-		| save all registers
	movl	%usp,%a1		| including
	movl	%a1,%sp@(FR_SP)		|    %USP
.Lsir1:
	clrl	%sp@-			| VA == none
	clrl	%sp@-			| code == none
	movl	#T_SSIR,%sp@-		| type == software interrupt
	pea	%sp@(12)		| fp == address of trap frame
	jbsr	_C_LABEL(trap)		| go handle it
	lea	%sp@(16),%sp		| pop value args
	movl	%sp@(FR_SP),%a0		| restore
	movl	%a0,%usp		|   %USP
	moveml	%sp@+,#0x7FFF		| and all remaining registers
	addql	#8,%sp			| pop %SP and stack adjust
	rte
.Lnosir:
	movl	%sp@+,%d0		| restore scratch register
.Ldorte:
	rte				| real return

/*
 * Use common m68k sigcode.
 */
#include <m68k/m68k/sigcode.s>
#ifdef COMPAT_SUNOS
#include <m68k/m68k/sunos_sigcode.s>
#endif
#ifdef COMPAT_SVR4
#include <m68k/m68k/svr4_sigcode.s>
#endif

/*
 * Primitives
 */ 

/*
 * Use common m68k support routines.
 */
#include <m68k/m68k/support.s>

/*
 * Use common m68k process/lwp switch and context save subroutines.
 */
#define FPCOPROC	/* XXX: Temp. Reqd. */
#include <m68k/m68k/switch_subr.s>

#if defined(M68040)
ENTRY(suline)
	movl	%sp@(4),%a0		| address to write
	movl	_C_LABEL(curpcb),%a1	| current pcb
	movl	#Lslerr,%a1@(PCB_ONFAULT) | where to return to on a fault
	movl	%sp@(8),%a1		| address of line
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	moveq	#0,%d0			| indicate no fault
	jra	Lsldone
Lslerr:
	moveq	#-1,%d0
Lsldone:
	movl	_C_LABEL(curpcb),%a1	| current pcb
	clrl	%a1@(PCB_ONFAULT) 	| clear fault address
	rts
#endif

ENTRY(ecacheon)
	rts

ENTRY(ecacheoff)
	rts

/*
 * Load a new user segment table pointer.
 */
ENTRY(loadustp)
	movl	%sp@(4),%d0		| new USTP
	moveq	#PGSHIFT,%d1
	lsll	%d1,%d0			| convert to addr
#if defined(M68040)
	cmpl	#MMU_68040,_C_LABEL(mmutype) | 68040?
	jne	LmotommuC		| no, skip
	.word	0xf518			| pflusha
	.long	0x4e7b0806		| movec %d0, URP
	rts
LmotommuC:
#endif
	pflusha				| flush entire TLB
	lea	_C_LABEL(protorp),%a0	| CRP prototype
	movl	%d0,%a0@(4)		| stash USTP
	pmove	%a0@,%crp		| load root pointer
	movl	#CACHE_CLR,%d0
	movc	%d0,%cacr		| invalidate cache(s)
	rts

/*
 * Set processor priority level calls.  Most are implemented with
 * inline asm expansions.  However, spl0 requires special handling
 * as we need to check for our emulated software interrupts.
 */

ALTENTRY(splnone, _spl0)
ENTRY(spl0)
	moveq	#0,%d0
	movw	%sr,%d0			| get old SR for return
	movw	#PSL_LOWIPL,%sr		| restore new SR
	tstb	_C_LABEL(ssir)		| software interrupt pending?
	jeq	.Lspldone		| no, all done
	subql	#4,%sp			| make room for RTE frame
	movl	%sp@(4),%sp@(2)		| position return address
	clrw	%sp@(6)			| set frame type 0
	movw	#PSL_LOWIPL,%sp@	| and new SR
	jra	.Lgotsir		| go handle it
.Lspldone:
	rts

/*
 * delay() - delay for a specified number of microseconds
 * _delay() - calibrator helper for delay()
 *
 * Notice that delay_factor is scaled up by a factor of 128 to avoid loss
 * of precision for small delays.  As a result of this we need to avoid
 * overflow.
 *
 * The branch target for the loops must be aligned on a half-line (8-byte)
 * boundary to minimize cache effects.  This guarantees both that there
 * will be no prefetch stalls due to cache line burst operations and that
 * the loops will run from a single cache half-line.
 */
	.align	8			| align to half-line boundary
					| (use nop instructions if necessary!)
ALTENTRY(_delay, _delay)
ENTRY(delay)
	movl	%sp@(4),%d0		| get microseconds to delay
	cmpl	#0x40000,%d0		| is it a "large" delay?
	bls	.Ldelayshort		| no, normal calculation
	movql	#0x7f,%d1		| adjust for scaled multipler (to
	addl	%d1,%d0			|   avoid overflow)
	lsrl	#7,%d0
	mulul	_C_LABEL(delay_factor),%d0 | calculate number of loop iterations
	bra	.Ldelaysetup		| go do it!
.Ldelayshort:
	mulul	_C_LABEL(delay_factor),%d0 | calculate number of loop iterations
	lsrl	#7,%d0			| adjust for scaled multiplier
.Ldelaysetup:
	jeq	.Ldelayexit		| bail out if nothing to do
	movql	#0,%d1			| put bits 15-0 in %d1 for the
	movw	%d0,%d1			|   inner loop, and move bits
	movw	#0,%d0			|   31-16 to the low-order word
	subql	#1,%d1			|   of %d0 for the outer loop
	swap	%d0
.Ldelay:
	tstl	_C_LABEL(delay_flag)	| this never changes for delay()!
	dbeq	%d1,.Ldelay		|   (used only for timing purposes)
	dbeq	%d0,.Ldelay
	addqw	#1,%d1			| adjust end count and
	swap	%d0			|    return the longword result
	orl	%d1,%d0
.Ldelayexit:
	rts

/*
 * Handle the nitty-gritty of rebooting the machine.
 * Basically we just turn off the MMU and jump to the appropriate ROM routine.
 * Note that we must be running in an address range that is mapped one-to-one
 * logical to physical so that the PC is still valid immediately after the MMU
 * is turned off.  We have conveniently mapped the last page of physical
 * memory this way.
 */
ENTRY_NOPROFILE(doboot)
#if defined(M68040)
	cmpl	#MMU_68040,_C_LABEL(mmutype) | 68040?
	jeq	Lnocache5		| yes, skip
#endif
	movl	#CACHE_OFF,%d0
	movc	%d0,%cacr		| disable on-chip cache(s)
Lnocache5:
	movl	_C_LABEL(maxaddr),%a0	| last page of physical memory
	lea	Lbootcode,%a1		| start of boot code
	lea	Lebootcode,%a3		| end of boot code
Lbootcopy:
	movw	%a1@+,%a0@+		| copy a word
	cmpl	%a3,%a1			| done yet?
	jcs	Lbootcopy		| no, keep going
#if defined(M68040)
	cmpl	#MMU_68040,_C_LABEL(mmutype) | 68040?
	jne	LmotommuE		| no, skip
	.word	0xf4f8			| cpusha bc
LmotommuE:
#endif
	movl	_C_LABEL(maxaddr),%a0
	jmp	%a0@			| jump to last page

Lbootcode:
	lea	%a0@(0x800),%sp		| physical %SP in case of NMI
	movl	_C_LABEL(MacOSROMBase),%a1 | Load MacOS ROMBase

#if defined(M68040)
	cmpl	#MMU_68040,_C_LABEL(mmutype) | 68040?
	jne	LmotommuF		| no, skip
	movl	#0,%d0
	movc	%d0,%cacr		| caches off
	.long	0x4e7b0003		| movc %d0,%tc (disable MMU)
	jra	Ldoboot1
LmotommuF:
#endif
	lea	_ASM_LABEL(longscratch),%a3
	movl	#0,%a3@			| value for pmove to %TC (turn off MMU)
	pmove	%a3@,%tc		| disable MMU

Ldoboot1:
	lea	%a1@(0x90),%a1		| offset of ROM reset routine
	jmp	%a1@			| and jump to ROM to reset machine
Lebootcode:

/*
 * u_long ptest040(void *addr, u_int fc);
 *
 * ptest040() does an 040 PTESTR (addr) and returns the 040 MMUSR iff
 * translation is enabled.  This allows us to find the physical address
 * corresponding to a MacOS logical address for get_physical().
 * sar  01-oct-1996
 */
ENTRY_NOPROFILE(ptest040)
#if defined(M68040)
	.long	0x4e7a0003		| movc %tc,%d0
	andw	#0x8000,%d0
	jeq	Lget_phys1		| MMU is disabled
	movc	%dfc,%d1		| Save %DFC
	movl	%sp@(8),%d0		| Set FC for ptestr
	movc	%d0,%dfc
	movl	%sp@(4),%a0		| logical address to look up
	.word	0xf568			| ptestr (%a0)
	.long	0x4e7a0805		| movc %mmusr,%d0
	movc	%d1,%dfc		| Restore %DFC
	rts
Lget_phys1:
#endif
	movql	#0,%d0			| return failure
	rts

/*
 * LAK: (7/24/94) This routine was added so that the
 *  C routine that runs at startup can figure out how MacOS
 *  had mapped memory.  We want to keep the same mapping so
 *  that when we set our MMU pointer, the PC doesn't point
 *  in the middle of nowhere.
 *
 * long get_pte(void *addr, unsigned long pte[2], unsigned short *psr)
 *
 *  Takes "addr" and looks it up in the current MMU pages.  Puts
 *  the PTE of that address in "pte" and the result of the
 *  search in "psr".  "pte" should be 2 longs in case it is
 *  a long-format entry.
 *
 *  One possible problem here is that setting the TT register
 *  may screw something up if we access user data space in a
 *  called function or in an interrupt service routine.
 *
 *  Returns -1 on error, 0 if pte is a short-format pte, or
 *  1 if pte is a long-format pte.
 *
 *  Be sure to only call this routine if the MMU is enabled.  This
 *  routine is probably more general than it needs to be -- it
 *  could simply return the physical address (replacing
 *  get_physical() in machdep).
 *
 *  "gas" does not understand the %tt0 register, so we must hand-
 *  assemble the instructions.
 */
ENTRY_NOPROFILE(get_pte)
	subql	#4,%sp		| make temporary space

	lea	_ASM_LABEL(longscratch),%a0
	movl	#0x00ff8710,%a0@ | Set up FC 1 r/w access
	.long	0xf0100800	| pmove %a0@,%tt0

	movl	%sp@(8),%a0	| logical address to look up
	movl	#0,%a1		| clear in case of failure
	ptestr	#FC_USERD,%a0@,#7,%a1 | search for logical address
	pmove	%psr,%sp@	| store processor status register
	movw	%sp@,%d1
	movl	%sp@(16),%a0	| where to store the %psr
	movw	%d1,%a0@	| send back to caller
	andw	#0xc400,%d1	| if bus error, exceeded limit, or invalid
	jne	get_pte_fail1	| leave now
	tstl	%a1		| check address we got back
	jeq	get_pte_fail2	| if 0, then was not set -- fail

	movl	%a1,%d0
	movl	%d0,_ASM_LABEL(pte_tmp)	| save for later

	| send first long back to user
	movl	%sp@(12),%a0	| address of where to put pte
	movsl	%a1@,%d0	|
	movl	%d0,%a0@	| first long

	andl	#3,%d0		| dt bits of pte
	cmpl	#1,%d0		| should be 1 if page descriptor
	jne	get_pte_fail3	| if not, get out now

	movl	%sp@(16),%a0	| addr of stored %psr
	movw	%a0@,%d0	| get %psr again
	andw	#7,%d0		| number of levels it found
	addw	#-1,%d0		| find previous level
	movl	%sp@(8),%a0	| logical address to look up
	movl	#0,%a1		| clear in case of failure

	cmpl	#0,%d0
	jeq	pte_level_zero
	cmpl	#1,%d0
	jeq	pte_level_one
	cmpl	#2,%d0
	jeq	pte_level_two
	cmpl	#3,%d0
	jeq	pte_level_three
	cmpl	#4,%d0
	jeq	pte_level_four
	cmpl	#5,%d0
	jeq	pte_level_five
	cmpl	#6,%d0
	jeq	pte_level_six
	jra	get_pte_fail4	| really should have been one of these...

pte_level_zero:
	| must get CRP to get length of entries at first level
	lea	_ASM_LABEL(longscratch),%a0 | space for two longs
	pmove	%crp,%a0@	| save root pointer
	movl	%a0@,%d0	| load high long
	jra	pte_got_parent
pte_level_one:
	ptestr	#FC_USERD,%a0@,#1,%a1 | search for logical address
	pmove	%psr,%sp@	| store processor status register
	movw	%sp@,%d1
	jra	pte_got_it
pte_level_two:
	ptestr	#FC_USERD,%a0@,#2,%a1 | search for logical address
	pmove	%psr,%sp@	| store processor status register
	movw	%sp@,%d1
	jra	pte_got_it
pte_level_three:
	ptestr	#FC_USERD,%a0@,#3,%a1 | search for logical address
	pmove	%psr,%sp@	| store processor status register
	movw	%sp@,%d1
	jra	pte_got_it
pte_level_four:
	ptestr	#FC_USERD,%a0@,#4,%a1 | search for logical address
	pmove	%psr,%sp@	| store processor status register
	movw	%sp@,%d1
	jra	pte_got_it
pte_level_five:
	ptestr	#FC_USERD,%a0@,#5,%a1 | search for logical address
	pmove	%psr,%sp@	| store processor status register
	movw	%sp@,%d1
	jra	pte_got_it
pte_level_six:
	ptestr	#FC_USERD,%a0@,#6,%a1 | search for logical address
	pmove	%psr,%sp@	| store processor status register
	movw	%sp@,%d1

pte_got_it:
	andw	#0xc400,%d1	| if bus error, exceeded limit, or invalid
	jne	get_pte_fail5	| leave now
	tstl	%a1		| check address we got back
	jeq	get_pte_fail6	| if 0, then was not set -- fail

	movsl	%a1@,%d0		| get pte of parent
	movl	%d0,_C_LABEL(macos_tt0)	| XXX for later analysis (kill me)
pte_got_parent:
	andl	#3,%d0		| dt bits of pte
	cmpl	#2,%d0		| child is short-format descriptor
	jeq	short_format
	cmpl	#3,%d0		| child is long-format descriptor
	jne	get_pte_fail7

	| long_format -- we must go back, change the tt, and get the
	|  second long.  The reason we didn't do this in the first place
	|  is that the first long might have been the last long of RAM.

	movl	_ASM_LABEL(pte_tmp),%a1	| get address of our original pte
	addql	#4,%a1		| address of ite second long

	| send second long back to user
	movl	%sp@(12),%a0	| address of where to put pte
	movsl	%a1@,%d0	|
	movl	%d0,%a0@(4)	| write in second long

	movql	#1,%d0		| return long-format
	jra	get_pte_success

short_format:
	movql	#0,%d0		| return short-format
	jra	get_pte_success

get_pte_fail:
	movql	#-1,%d0		| return failure

get_pte_success:
	lea	_ASM_LABEL(longscratch),%a0 | disable tt
	movl	#0,%a0@
	.long	0xf0100800	| pmove %a0@,%tt0

	addql	#4,%sp		| return temporary space
	rts

get_pte_fail1:
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail2:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail3:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail4:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail5:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail6:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail7:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail8:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail9:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail
get_pte_fail10:
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jbsr	_C_LABEL(printstar)
	jra	get_pte_fail

/*
 * Misc. global variables.
 */
	.data
GLOBAL(sanity_check)
	.long	0x18621862	| this is our stack overflow checker.

	.space	4 * PAGE_SIZE
ASLOCAL(tmpstk)

GLOBAL(machineid)
	.long	0		| default to 320

GLOBAL(mmutype)
	.long	MMU_68851	| default to 68851 PMMU

GLOBAL(cputype)
	.long	CPU_68020	| default to 68020 CPU

#ifdef __notyet__
GLOBAL(ectype)
	.long	EC_NONE		| external cache type, default to none
#endif

GLOBAL(fputype)
	.long	FPU_68882	| default to 68882 FPU

GLOBAL(protorp)
	.long	0,0		| prototype root pointer

GLOBAL(intiolimit)
	.long	0		| KVA of end of internal IO space

GLOBAL(load_addr)
	.long	0		| Physical address of kernel

ASLOCAL(lastpage)
	.long	0		| LAK: to store the addr of last page in mem

GLOBAL(MacOSROMBase)
	.long	0x40800000
GLOBAL(mac68k_vrsrc_cnt)
	.long	0
GLOBAL(mac68k_vrsrc_vec)
	.word	0, 0, 0, 0, 0, 0

#ifdef DEBUG
ASGLOBAL(fulltflush)
	.long	0

ASGLOBAL(fullcflush)
	.long	0
#endif

/* interrupt counters -- leave some space for overriding the names */

GLOBAL(intrnames)
	.asciz	"spur    "
	.asciz	"via1    "
	.asciz	"via2    "
	.asciz	"unused1 "
	.asciz	"scc     "
	.asciz	"unused2 "
	.asciz	"unused3 "
	.asciz	"nmi     "
	.asciz	"clock   "
GLOBAL(eintrnames)
	.even

GLOBAL(intrcnt)
	.long	0,0,0,0,0,0,0,0,0
GLOBAL(eintrcnt)
