Source to machdep/ppc/lowmem_vectors.s


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * Low-memory exception vector code for PowerPC MACH
 *
 * These are the only routines that are ever run with
 * VM instruction translation switched off.
 *
 * The PowerPC is quite strange in that rather than having a set
 * of exception vectors, the exception handlers are installed
 * in well-known addresses in low memory. This code must be loaded
 * at ZERO in physical memory. We do this by putting this code
 * into a special segment (__VECTORS) and giving the segment
 * a -segaddr of zero in the 'ld' command line.
 *
 * When this code is loaded into memory, it is loaded at physical
 * address zero.
 *
 * This code handles all powerpc exceptions and is always entered
 * in supervisor mode with translation off. It saves the minimum
 * processor state before switching back on translation and
 * jumping to the approprate routine.
 *
 * Vectors from 0x100 to 0x2fff occupy 0x100 bytes each (64 instructions)
 *
 * We use some of this space to decide which stack to use, and where to
 * save the context etc, before	jumping to a generic handler.
 */

#include <assym.h>
#include <debug.h>
#include <cpus.h>
	
#include <mach_debug.h>
#include <machdep/ppc/asm.h>
#include <machdep/ppc/proc_reg.h>
#include <machdep/ppc/exception.h>
#include <mach/ppc/vm_param.h>
#include <machdep/ppc/nkdefs.h>
#include <ppc/pcb_flags.h>

/*	Kernel origin point. Keep this in sync with the makefiles!
	See MASTER.ppc and MAKEFILE.ppc!
*/
#define	RELOC	0x10000

/*
 * Define a couple of macros to make the Handler macro simpler
 */
#if	__STDC__
#define	LHL(x)	LCL(_handler ## x)
#define	LOC(y)	0x ## y
#else /* __STDC__ */
#define	LHL(x)	LCL(_handler/**/x)
#define	LOC(y)	0x/**/y
#endif /* __STDC__ */

/*
 * Almost all handlers do exactly the same thing:
 *     save r1-r3 into sprg1-3
 *     load r3 with a value indicating which trap
 *     load r2 with the per-processor save area pointer
 *     branch to the generic entry code.
 */
#define	HANDLER2(a,v,l)	LHL(a): 		@ \
			. = LOC(a) 		@ \
			mtsprg	3,	r3 	@ \
			mtsprg	2,	r2 	@ \
			li	r3,	v 	@ \
			mtsprg	1,	r1 	@ \
			mfsprg	r2,	0	@ \
			b	LCL(l)

#define	HANDLER(a,v)	HANDLER2(a, v, _exception_entry)
	
	


/*
 * Here are the vectors
 */
	.section __VECTORS, __interrupts
	.align	2

	.globl _ExceptionVectorsStart
_ExceptionVectorsStart:	/* Used if relocating the exception vectors */


/* 
 * System reset - call debugger
 */
HANDLER(100, EXC_RESET)


/*
 * Machine check (physical bus error) - call debugger
 */
HANDLER(200, EXC_MACHINE_CHECK)


/*
 * Data access - page fault, invalid memory rights for operation
 */
HANDLER2(300, EXC_DATA_ACCESS, _MMU)


/*
 * Instruction access - as for data access
 */
HANDLER2(400,EXC_INSTRUCTION_ACCESS, _MMU)


/*
 * External interrupt
 */
HANDLER(500,EXC_INTERRUPT)


/*
 * Alignment - many reasons
 */
HANDLER(600,EXC_ALIGNMENT)


/*
 * Program - floating point exception, illegal inst, priv inst, user trap
 */
HANDLER2(700, EXC_PROGRAM, _BLUE_FAST_TRAP)


/*
 * Program - floating point disabled, illegal inst, priv inst, user trap
 */
HANDLER(800,EXC_FP_UNAVAILABLE)


/*
 * Decrementer - DEC register has passed zero.
 */
HANDLER(900,EXC_DECREMENTER)


/*
 * I/O controller interface error - MACH does not use this
 * (601 only)
 */
HANDLER(a00,EXC_IO_ERROR)


/*
 * Reserved
 */
HANDLER(b00,EXC_RESERVED_0B)


/*
 * System call - generated by the sc instruction
 */
HANDLER(c00,EXC_SYSTEM_CALL)


/*
 * Trace - generated by single stepping
 * (603, 603e and 604)
 */
HANDLER(d00,EXC_TRACE)


/*
 * Floating point assist
 */
HANDLER(e00,EXC_FP_ASSIST)


/*
 * Performance monitoring interrupt (Not implemented yet)
 * (604)
 */
HANDLER(f00,EXC_PERFORMANCE_MON)


/*
 * Instruction translation miss - we inline this code.
 * (603, 603e only)
 *
 * Upon entry (done for us by the machine):
 *     srr0 :	 addr of instruction that missed
 *     srr1 :	 bits 0-3   = saved CR0
 *                    4     = lru way bit
 *                    16-31 = saved msr
 *     msr[tgpr] = 1  (so gpr0-3 become our temporary variables)
 *     imiss:	 ea that missed
 *     icmp :	 the compare value for the va that missed
 *     hash1:	 pointer to first hash pteg
 *     hash2:	 pointer to 2nd hash pteg
 *
 * Register usage:
 *     tmp0:	 saved counter
 *     tmp1:	 junk
 *     tmp2:	 pointer to pteg
 *     tmp3:	 current compare value
 *
 * This code is taken from the 603e User's Manual with
 * some bugfixes and minor improvements to save bytes and cycles
 */

L_handler1000:
	. = 0x1000

	mfspr	tmp2,	hash1
	mfctr	tmp0				/* use tmp0 to save ctr */
	mfspr	tmp3,	icmp

.L_imiss_find_pte_in_pteg:
	li	tmp1,	8			/* count */
	subi	tmp2,	tmp2,	8		/* offset for lwzu */
	mtctr	tmp1				/* count... */
	
.L_imiss_pteg_loop:
	lwz	tmp1,	8(tmp2)			/* check pte0 for match... */
	addi	tmp2,	tmp2,	8
	cmpw	CR0,	tmp1,	tmp3
#if 0
	bdnzf+	CR0+lt,	.L_imiss_pteg_loop
#else
	bc	0, 2,	.L_imiss_pteg_loop
#endif
	beq+	CR0,	.L_imiss_found_pte

	/* Not found in PTEG, we must scan 2nd then give up */

	andi.	tmp1,	tmp3,	MASK(PTE0_HASH_ID)
	bne-	.L_imiss_do_no_hash_exception		/* give up */

	mfspr	tmp2,	hash2
	ori	tmp3,	tmp3,	MASK(PTE0_HASH_ID)
	b	.L_imiss_find_pte_in_pteg

.L_imiss_found_pte:

	lwz	tmp1,	4(tmp2)				/* get pte1_t */
	andi.	tmp3,	tmp1,	MASK(PTE1_WIMG_GUARD)	/* Fault? */
	bne-	.L_imiss_do_prot_exception		/* Guarded - illegal */

	/* Ok, we've found what we need to, restore and rfi! */

	mtctr	tmp0					/* restore ctr */
	mfsrr1	tmp3
	mfspr	tmp0,	imiss
	mtcrf	0x80,	tmp3				/* Restore CR0 */
	mtspr	rpa,	tmp1				/* set the pte */
	ori	tmp1,	tmp1,	MASK(PTE1_REFERENCED)	/* set referenced */
	tlbli	tmp0
	sth	tmp1,	6(tmp2)
	rfi
	
.L_imiss_do_prot_exception:
	/* set up srr1 to indicate protection exception... */
	mfsrr1	tmp3
	andi.	tmp2,	tmp3,	0xffff
	addis	tmp2,	tmp2,	MASK(SRR1_TRANS_PROT) >> 16
	b	.L_imiss_do_exception
	
.L_imiss_do_no_hash_exception:
	/* clean up registers for protection exception... */
	mfsrr1	tmp3
	andi.	tmp2,	tmp3,	0xffff
	addis	tmp2,	tmp2,	MASK(SRR1_TRANS_HASH) >> 16
	
	/* And the entry into the usual instruction fault handler ... */
.L_imiss_do_exception:

	mtctr	tmp0					/* Restore ctr */
	mtsrr1	tmp2					/* Set up srr1 */
	mfmsr	tmp0					
	xoris	tmp0,	tmp0,	MASK(MSR_TGPR)>>16	/* no TGPR */
	mtcrf	0x80,	tmp3				/* Restore CR0 */
	mtmsr	tmp0					/* reset MSR[TGPR] */
	ba	0x400					/* Instr Access */
	
/*
 * Data load translation miss
 * (603, 603e only)
 *
 * Upon entry (done for us by the machine):
 *     srr0 :	 addr of instruction that missed
 *     srr1 :	 bits 0-3   = saved CR0
 *                    4     = lru way bit
 *                    5     = 1 if store
 *                    16-31 = saved msr
 *     msr[tgpr] = 1  (so gpr0-3 become our temporary variables)
 *     dmiss:	 ea that missed
 *     dcmp :	 the compare value for the va that missed
 *     hash1:	 pointer to first hash pteg
 *     hash2:	 pointer to 2nd hash pteg
 *
 * Register usage:
 *     tmp0:	 saved counter
 *     tmp1:	 junk
 *     tmp2:	 pointer to pteg
 *     tmp3:	 current compare value
 *
 * This code is taken from the 603e User's Manual with
 * some bugfixes and minor improvements to save bytes and cycles
 */

L_handler1100:
	. = 0x1100

	mfspr	tmp2,	hash1
	mfctr	tmp0				/* use tmp0 to save ctr */
	mfspr	tmp3,	dcmp

.L_dlmiss_find_pte_in_pteg:
	li	tmp1,	8			/* count */
	subi	tmp2,	tmp2,	8		/* offset for lwzu */
	mtctr	tmp1				/* count... */
	
.L_dlmiss_pteg_loop:
	lwz	tmp1,	8(tmp2)			/* check pte0 for match... */
	addi	tmp2,	tmp2,	8
	cmpw	CR0,	tmp1,	tmp3
#if 0
	bdnzf+	CR0+lt,	.L_dlmiss_pteg_loop
#else
	bc	0,2,	.L_dlmiss_pteg_loop
#endif
	beq+	CR0,	.L_dmiss_found_pte

	/* Not found in PTEG, we must scan 2nd then give up */

	andi.	tmp1,	tmp3,	MASK(PTE0_HASH_ID)	/* already at 2nd? */
	bne-	.L_dmiss_do_no_hash_exception		/* give up */

	mfspr	tmp2,	hash2
	ori	tmp3,	tmp3,	MASK(PTE0_HASH_ID)
	b	.L_dlmiss_find_pte_in_pteg

.L_dmiss_found_pte:

	lwz	tmp1,	4(tmp2)				/* get pte1_t */

	/* Ok, we've found what we need to, restore and rfi! */

	mtctr	tmp0					/* restore ctr */
	mfsrr1	tmp3
	mfspr	tmp0,	dmiss
	mtcrf	0x80,	tmp3				/* Restore CR0 */
	mtspr	rpa,	tmp1				/* set the pte */
	ori	tmp1,	tmp1,	MASK(PTE1_REFERENCED)	/* set referenced */
	tlbld	tmp0					/* load up tlb */
	sth	tmp1,	6(tmp2)				/* sth is faster? */
	rfi
	
	/* This code is shared with data store translation miss */
	
.L_dmiss_do_no_hash_exception:
	/* clean up registers for protection exception... */
	mfsrr1	tmp3
	/* prepare to set DSISR_WRITE_BIT correctly from srr1 info */
	rlwinm	tmp1,	tmp3,	9,	6,	6
	addis	tmp1,	tmp1,	MASK(SRR1_TRANS_NO_PTE) >> 16

	/* And the entry into the usual data fault handler ... */

	mtctr	tmp0					/* Restore ctr */
	andi.	tmp2,	tmp3,	0xffff			/* Clean up srr1 */
	mtsrr1	tmp2					/* Set srr1 */
	mtdsisr	tmp1
	mfspr	tmp2,	dmiss
	mtdar	tmp2
	mfmsr	tmp0
	xoris	tmp0,	tmp0,	MASK(MSR_TGPR)>>16	/* no TGPR */
	mtcrf	0x80,	tmp3				/* Restore CR0 */
	sync
	mtmsr	tmp0					/* reset MSR[TGPR] */
	ba	0x300					/* Data Access */
	
/*
 * Data store translation miss (similar to data load)
 * (603, 603e only)
 *
 * Upon entry (done for us by the machine):
 *     srr0 :	 addr of instruction that missed
 *     srr1 :	 bits 0-3   = saved CR0
 *                    4     = lru way bit
 *                    5     = 1 if store
 *                    16-31 = saved msr
 *     msr[tgpr] = 1  (so gpr0-3 become our temporary variables)
 *     dmiss:	 ea that missed
 *     dcmp :	 the compare value for the va that missed
 *     hash1:	 pointer to first hash pteg
 *     hash2:	 pointer to 2nd hash pteg
 *
 * Register usage:
 *     tmp0:	 saved counter
 *     tmp1:	 junk
 *     tmp2:	 pointer to pteg
 *     tmp3:	 current compare value
 *
 * This code is taken from the 603e User's Manual with
 * some bugfixes and minor improvements to save bytes and cycles
 */

L_handler1200:
	. = 0x1200

	mfspr	tmp2,	hash1
	mfctr	tmp0				/* use tmp0 to save ctr */
	mfspr	tmp3,	dcmp

.L_dsmiss_find_pte_in_pteg:
	li	tmp1,	8			/* count */
	subi	tmp2,	tmp2,	8		/* offset for lwzu */
	mtctr	tmp1				/* count... */
	
.L_dsmiss_pteg_loop:
	lwz	tmp1,	8(tmp2)			/* check pte0 for match... */
	addi	tmp2,	tmp2,	8
	cmpw	CR0,	tmp1,	tmp3
#if 0
	bdnzf+	CR0+lt,	.L_dsmiss_pteg_loop
#else
	bc	0,2,	.L_dsmiss_pteg_loop
#endif
	beq+	CR0,	.L_dsmiss_found_pte

	/* Not found in PTEG, we must scan 2nd then give up */

	andi.	tmp1,	tmp3,	MASK(PTE0_HASH_ID)	/* already at 2nd? */
	bne-	.L_dmiss_do_no_hash_exception		/* give up */

	mfspr	tmp2,	hash2
	ori	tmp3,	tmp3,	MASK(PTE0_HASH_ID)
	b	.L_dsmiss_find_pte_in_pteg

.L_dsmiss_found_pte:

	lwz	tmp1,	4(tmp2)				/* get pte1_t */
	andi.	tmp3,	tmp1,	MASK(PTE1_CHANGED)	/* unchanged, check? */
	beq-	.L_dsmiss_check_prot			/* yes, check prot */

.L_dsmiss_resolved:
	/* Ok, we've found what we need to, restore and rfi! */

	mtctr	tmp0					/* restore ctr */
	mfsrr1	tmp3
	mfspr	tmp0,	dmiss
	mtcrf	0x80,	tmp3				/* Restore CR0 */
	mtspr	rpa,	tmp1				/* set the pte */
	tlbld	tmp0					/* load up tlb */
	rfi
	
.L_dsmiss_check_prot:
	/* PTE is unchanged, we must check that we can write */
	rlwinm.	tmp3,	tmp1,	30,	0,	1	/* check PP[1] */
	bge-	.L_dsmiss_check_prot_user_kern
	andi.	tmp3,	tmp1,	1			/* check PP[0] */
	beq+	.L_dsmiss_check_prot_ok
	
.L_dmiss_do_prot_exception:
	/* clean up registers for protection exception... */
	mfsrr1	tmp3
	/* prepare to set DSISR_WRITE_BIT correctly from srr1 info */
	rlwinm	tmp1,	tmp3,	9,	6,	6
	addis	tmp1,	tmp1,	MASK(SRR1_TRANS_NO_PTE) >> 16

	/* And the entry into the usual data fault handler ... */
	mtctr	tmp0					/* Restore ctr */
	andi.	tmp2,	tmp3,	0xffff			/* Clean up srr1 */
	mtsrr1	tmp2					/* Set srr1 */
	mtdsisr	tmp1
	mfspr	tmp2,	dmiss
	mtdar	tmp2
	mfmsr	tmp0
	xoris	tmp0,	tmp0,	MASK(MSR_TGPR)>>16	/* no TGPR */
	mtcrf	0x80,	tmp3				/* Restore CR0 */
	sync
	mtmsr	tmp0					/* reset MSR[TGPR] */
	ba	0x300					/* Data Access */
	
/* NB - if we knew we were on a 603e we could test just the MSR_KEY bit */
.L_dsmiss_check_prot_user_kern:
	mfsrr1	tmp3
	andi.	tmp3,	tmp3,	MASK(MSR_PR)
	beq+	.L_dsmiss_check_prot_kern
	mfspr	tmp3,	dmiss				/* check user privs */
	mfsrin	tmp3,	tmp3				/* get excepting SR */
	andis.	tmp3,	tmp3,	0x2000			/* Test SR ku bit */
	beq+	.L_dsmiss_check_prot_ok
	b	.L_dmiss_do_prot_exception

.L_dsmiss_check_prot_kern:
	mfspr	tmp3,	dmiss				/* check kern privs */
	mfsrin	tmp3,	tmp3
	andis.	tmp3,	tmp3,	0x4000			/* Test SR Ks bit */
	bne-	.L_dmiss_do_prot_exception

.L_dsmiss_check_prot_ok:
	/* Ok, mark as referenced and changed before resolving the fault */
	ori	tmp1,	tmp1,	(MASK(PTE1_REFERENCED)|MASK(PTE1_CHANGED))
	sth	tmp1,	6(tmp2)
	b	.L_dsmiss_resolved
	
/*
 * Instruction address breakpoint
 * (603, 603e and 604)
 *
 */
HANDLER(1300,EXC_INSTRUCTION_BKPT)


/*
 * System management interrupt
 * (603, 603e and 604)
 */
HANDLER(1400,EXC_SYSTEM_MANAGEMENT)


/*
 * There is now a large gap of reserved traps
 */
HANDLER(1500,EXC_RESERVED_15)
HANDLER(1600,EXC_RESERVED_16)
HANDLER(1700,EXC_RESERVED_17)
HANDLER(1800,EXC_RESERVED_18)
HANDLER(1900,EXC_RESERVED_19)
HANDLER(1a00,EXC_RESERVED_1A)
HANDLER(1b00,EXC_RESERVED_1B)
HANDLER(1c00,EXC_RESERVED_1C)
HANDLER(1d00,EXC_RESERVED_1D)
HANDLER(1e00,EXC_RESERVED_1E)
HANDLER(1f00,EXC_RESERVED_1F)


/*
 * Run mode/ trace exception - single stepping on 601 processors
 * (601 only)
 */
HANDLER(2000,EXC_RUNMODE_TRACE)


/*
 * There is another large gap of reserved traps
 */
HANDLER(2100,EXC_RESERVED_21)
HANDLER(2200,EXC_RESERVED_22)
HANDLER(2300,EXC_RESERVED_23)
HANDLER(2400,EXC_RESERVED_24)
HANDLER(2500,EXC_RESERVED_25)
HANDLER(2600,EXC_RESERVED_26)
HANDLER(2700,EXC_RESERVED_27)
HANDLER(2800,EXC_RESERVED_28)
HANDLER(2900,EXC_RESERVED_29)
HANDLER(2a00,EXC_RESERVED_2A)
HANDLER(2b00,EXC_RESERVED_2B)
HANDLER(2c00,EXC_RESERVED_2C)
HANDLER(2d00,EXC_RESERVED_2D)
HANDLER(2e00,EXC_RESERVED_2E)
HANDLER(2f00,EXC_RESERVED_2F)

/* Remember that at 0x3000 is just past the end of the trap table */
	. = 0x3000





/*
 * L_exception_entry(type)
 *
 * This is the common exception handling routine called by any
 * type of system exception. (except 603 translation miss exceptions)
 *
 * Entry:	via a system exception handler, thus interrupts off, VM off.
 *              Entry r1-r3 have been saved in sprg1-3. 
 *              r2 - byte offset into per_proc_info of this CPU
 *		r3 - contains the exception number.
 *
 * Exit:	srr0 and srr1 saved in per_proc_info structure
 *              r3 (supplied) saved in per_proc_info structure
 *              cr            saved in per_proc_info structure
 *              original r1-3 saved in sprg1-3.
 *                 r1 - is scratch
 *                 r2 - byte offset into per_proc_info of this CPU
 *                 r3  -contains exception info as for entry
 *
 *              The exception handler is entered with
 *              VM on, interrupts still switched off
 */

		
L_exception_entry:

	/* Save SRR0 and SRR1 plus cr and r3 into PER_PROC structure */
	
	stw	r3,	PP_SAVE_EXCEPTION_TYPE(r2)
	mfsrr0	r1
	mfsrr1	r3
	stw	r1,	PP_SAVE_SRR0(r2)
	stw	r3,	PP_SAVE_SRR1(r2)
	mfdar	r1
	mfdsisr	r3
	stw	r1,	PP_SAVE_DAR(r2)
	stw	r3,	PP_SAVE_DSISR(r2)
	mfcr	r1
	stw	r1,	PP_SAVE_CR(r2)
	
.L_Common:
	/* Remap the kernel using seg reg 0, and I/O via seg reg 5 */

	lis	r3,	(KERNEL_SEG_REG0_VALUE >> 16)
#if PPC_SID_KERNEL != 0
		/* if PPC_SID_KERNEL == 0, then bottom 16 bits are 0,
		 * so save an instruction - big deal
		 */
	ori	r3,	r3,	(KERNEL_SEG_REG0_VALUE & 0xFFFF)
#endif
	mtsr	sr0,	r3		/* Kernel SR0 */
	ori	r1,	r3,	1	/* Kernel SR1 */
	mtsr	sr1,	r1
	ori	r1,	r3,	2
	mtsr	sr2,	r1
	ori	r1,	r3,	3
	mtsr	sr3,	r1
		
	/* jump into main handler code switching on VM at the same time */

	/* We assume kernel data is mapped contiguously in physical
	 * memory, otherwise we need to switch on (at least) virtual data.
	 */
	lwz	r3,	PP_SAVE_EXCEPTION_TYPE(r2)
	lwz	r1,	PP_PHYS_EXCEPTION_HANDLERS(r2)
	lwzx	r1,	r1,	r3
	lwz	r2,	PP_VIRT_PER_PROC(r2)
	mtsrr0	r1
		
	li	r1,	MSR_SUPERVISOR_INT_OFF
	mtsrr1	r1
	rfi		/* into the exception handler with VM on */






/*
 * exception_exit(sr0,srr0,srr1)
 *
 * This is the trampoline code used when exiting into a foreign
 * address space.
 *
 * NMGS TODO - can't we skip exception_exit by relying on translations
 * NMGS TODO   even after we've loaded sr0? Docs aren't clear. Using
 * NMGS TODO   1-1 kernel text mapping would definately avoid this need.
 *
 * Entry :	 entry via rfi, MSR = MSR_VM_OFF
 *               r1-3 saved in sprg1-3
 * 		 r1 = user's sr0 - used to construct sr1 too
 *               r2 = user's srr0 (instruction pointer)
 *               r3 = user's srr1 (msr)
 *
 * Exit :	 this routine restores the users' space and rfis.
 */
	
	.data
	.align	ALIGNMENT
	.globl	EXT(exception_exit)
EXT(exception_exit):
	.long	exception_exit_fn	/* phys addr of fn */


	.section __VECTORS, __interrupts
	.align	2
	
exception_exit_fn:

	mtsrr0	r2
	mtsrr1	r3

	mtsr	sr0,	r1		/* Restore user space SR0 */
	ori	r2,	r1,	1
	mtsr	sr1,	r2		/* Restore user space SR1 */
	ori	r2,	r1,	2
	mtsr	sr2,	r2
	ori	r2,	r1,	3
	mtsr	sr3,	r2
		
	mfsprg	r1,	1
	mfsprg	r2,	2
	mfsprg	r3,	3
	rfi


/*
** Blue Box Fast Trap entry
**
** Entry:	VM switched OFF
**		Interrupts OFF
**		Data/Inst Relocation OFF
**              r1-r3 have been saved in sprg1-3. 
**              r2 - ptr to per_proc_info of this CPU
**		r3 - contains the exception number.
**
**
*/
	.align 5
L_BLUE_FAST_TRAP:
	mfcr	r1			
	stw	r1,	PP_SAVE_CR(r2)
	/* Check for Trap program exception */
	mfsrr1	r1			
	mtcrf	0x70,	r1		
//	bt+	12,	EmulateUnimplementedInstructions
//	bt	13,	EmulatePrivilegedInstructions
	bf	14,	1f

	/* check for bluebox enabled */
	lwz	r1,	PP_CPU_DATA(r2)
	lwz	r1,	CPU_FLAGS(r1)
	rlwinm.	r1,r1,0,PCB_BB_BIT,PCB_BB_BIT
	beq	1f	/* exit if bb not enabled */


	/* Now save some state for working room */
	stw	r4,	PP_SAVE_R4(r2)
	stw	r5,	PP_SAVE_R5(r2)
	stw	r6,	PP_SAVE_R6(r2)
	stw	r7,	PP_SAVE_R7(r2)

	/* Turn on Data Reloction to get instruction */
	mfmsr	r1
	ori	r5,	r1,	MASK(MSR_DR)
	mtmsr	r5
	isync

	/* read in offending instruction */	
	mfsrr0	r6
	lwz	r7,	0(r6)

	/* turn Data Relocation back off*/
	mtmsr	r1
	isync	

	/* check the trap instruction */
	xoris	r6,	r7,	0xFFF
	cmplwi	cr7,	r6,	17
	bge	cr7,	2f

	/*
	** Once here
	**	r1-r3 -> sprg1-3
	**	r4-r7 -> PP_SAVE_R4-7
	**	cr    -> PP_SAVE_CR
	**	r2    -> Per Processor Info Block
	**	r3    -> exception type
	*/ 
	stw	r0,	PP_SAVE_R0(r2)

	mfspr	r0,	srr0
	stw	r0,	PP_SAVE_SRR0(r2)

	mfspr	r0,	srr1
	stw	r0,	PP_SAVE_SRR1(r2)

	mfsprg	r0,	1
	stw	r0,	PP_SAVE_R1(r2)

	mfsprg	r0,	2
	stw	r0,	PP_SAVE_R2(r2)

	mfsprg	r0,	3
	stw	r0,	PP_SAVE_R3(r2)

	stw	r8,	PP_SAVE_R8(r2)
	stw	r9,	PP_SAVE_R9(r2)
	stw	r10,	PP_SAVE_R10(r2)
	stw	r11,	PP_SAVE_R11(r2)
	stw	r12,	PP_SAVE_R12(r2)

	/*
	** functions 0-15 -> Call
	**             16 -> Exit
	*/
	cmplwi	cr7,	r6,	16
	beq	cr7,	.L_ExitPseudoKernel

/*QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
 * void                 CallPseudoKernel        ( void )
 *
 * This op provides a means of invoking the BlueBox PseudoKernel from a
 * system (68k) or native (PPC) context while changing BlueBox interruption
 * state atomically. As an added bonus, this op clobbers only r0 while leaving
 * the rest of PPC user state registers intact.
 *
 * This op is invoked as follows:
 *      li r0, kCallPseudoKernelNumber  // load this op's firmware call number
 *      sc                              // invoke CallPseudoKernel
 *      dc.l    CallPseudoKernelDescriptorPtr   // static pointer to CallPseudoK
ernelDescriptor
 *
 * NOTE: The CallPseudoKernelDescriptor and the word pointed to by
 * intControlAddr must be locked, else this op will crash the kernel.
 *
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ*/

        // word following the twi is the descriptor's address
        lwz     r3,	PP_SAVE_SRR0(r2)

        lwz     r10,	PP_SAVE_CR(r2)       // setup r10 with CR

	/* Turn on Data Relocation */
	mtmsr	r5
	isync

        lwz     r3,     4(r3)           // get descriptor's address

        lwz     r11,    CPKD_INTCONTROLADDR(r3)
        lwz     r4,     CPKD_PC(r3)
        lwz     r6,     CPKD_NEWSTATE(r3)
        lwz     r7,     CPKD_INTSTATEMASK(r3)
        lwz     r8,     0(r11)          // get current interruption control word
//	lwz     r5,     CPKD_GPR0(r3)
        lwz     r12,    CPKD_SYSCONTEXTSTATE(r3)
        andc    r9,	r8,	r7	// remove current state
        and     r8,	r8,	r7	// extract current state
        cmplw   r8,	r12		// test for entry from system context
        or      r9,	r9,	r6	// insert new state
        bne     .L_CallFromAlternateContext

.L_CallFromSystemContext:
        lwz     r6,     CPKD_INTCR2SHIFT(r3)
        lwz     r7,     CPKD_INTCR2MASK(r3)
        srw     r10,	r10,	r6	// position live CR2 from cr register as required
        andc    r9,	r9,	r7	// remove old backup CR2
        and     r10,	r10,	r7	// mask live CR2
        or      r9,	r9,	r10	// insert CR2 into backup CR2
        b       .L_CallContinue

.L_CallFromAlternateContext:
.L_CallContinue:
        stw     r9,     0(r11)		 // update interruption control word

	/* turn Data Relocation back off*/
	mtmsr	r1
	isync	

        /* introduce new pc and gr0 contents */
        lwz     r6,	PP_SAVE_SRR1(r2)
        stw     r4,	PP_SAVE_SRR0(r2)
//	stw     r5,	PP_SAVE_R0(r2)

	// insert updated fe0, fe1, se, and be bits into user msr
        rlwimi  r6,	r6,	0,	MSR_FE1_BIT,	MSR_FE0_BIT
	/* Disable FPU */
	rlwinm	r6,	r6,	0,	MSR_FP_BIT+1,	MSR_FP_BIT-1

        /* zero single step and branch step control in user msr */
        stw     r6,	PP_SAVE_SRR1(r2) // update user msr

.L_BlueBoxCommonExit:
	/*
	** Restore State for Exit
	*/
	lwz	r4,	PP_SAVE_CR(r2)
	stwcx.	r4,	0,	r2		/* clear existing reservation */
	mtcr	r4				/* update cr, it is live */

	/* the trampoline code takes r1-r3 from sprg1-3, and uses r1-3
	 * as arguments */
	lwz	r0,	PP_SAVE_R1(r2)
	mtsprg	1,	r0

	lwz	r0,	PP_SAVE_R2(r2)
	mtsprg	2,	r0

	lwz	r0,	PP_SAVE_R3(r2)
	mtsprg	3,	r0

	lwz	r0,	PP_SAVE_R0(r2)

	lwz	r4,	PP_SAVE_R4(r2)
	lwz	r5,	PP_SAVE_R5(r2)
	lwz	r6,	PP_SAVE_R6(r2)
	lwz	r7,	PP_SAVE_R7(r2)
	lwz	r8,	PP_SAVE_R8(r2)
	lwz	r9,	PP_SAVE_R9(r2)
	lwz	r10,	PP_SAVE_R10(r2)
	lwz	r11,	PP_SAVE_R11(r2)
	lwz	r12,	PP_SAVE_R12(r2)


	/*
	** Setup parameters for exit code
	*/
	mfsr	r1,	sr0
	lwz	r3,	PP_SAVE_SRR1(r2)	/* load the last register... */
	lwz	r2,	PP_SAVE_SRR0(r2)	/* For trampoline */

	b	exception_exit_fn


2:
	lwz	r7,	PP_SAVE_R7(r2)
	lwz	r6,	PP_SAVE_R6(r2)
	lwz	r5,	PP_SAVE_R5(r2)
	lwz	r4,	PP_SAVE_R4(r2)
1:
	lwz	r1,	PP_SAVE_CR(r2)
	mtcr	r1
	b	L_exception_entry



/*QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
 * void ExitPseudoKernel ( ExitPseudoKernelDescriptorPtr exitDescriptor )
 *
 * This op provides a means of exiting from the BlueBox PseudoKernel to a
 * user context while changing the BlueBox interruption state atomically.
 * It also allows all of the user state PPC registers to be loaded.
 *
 * This op is invoked as follows:
 *      lwz r3, ExitPseudoKernelDescriptorPtr
 *      li r0, kCallPseudoKernelNumber  // load this op's firmware call number
 *      sc                              // invoke CallPseudoKernel
 *
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ*/
.L_ExitPseudoKernel:
	/* Turn on Data Relocation */
	mtmsr	r5
	isync

	/* start of actual routine */

	//mfspr	r9,	srr1
        lwz     r9,	PP_SAVE_SRR1(r2)

	//mfsprg	r3,	3		// restore r3, it is exitdescptr
	lwz	r3,	PP_SAVE_R3(r2)

	lwz	r8,	EPKD_CR(r3)

	lwz	r11,	EPKD_INTCONTROLADDR(r3)
	lwz	r4,	EPKD_PC(r3)
	lwz	r7,	EPKD_NEWSTATE(r3)
	lwz	r10,	EPKD_INTSTATEMASK(r3)
	lwz	r5,	0(r11)	// get current interruption control word
	lwz	r0,	EPKD_SYSCONTEXTSTATE(r3)
	andc	r12,	r5,	r10	// remove current state
	cmplw	r7,	r0		// test for exit to system context
	or	r12,	r12,	r7	// insert new state
	lwz	r0,	EPKD_MSRUPDATE(r3)
	beq	.L_ExitToSystemContext

.L_ExitToAlternateContext:
	lwz	r5,	EPKD_INTPENDINGMASK(r3)
	lwz	r6,	EPKD_INTPENDINGPC(r3)
	and.	r7,	r12,	r5	// test for pending 'rupt in backup cr2
	beq	.L_ExitUpdateRuptControlWord	//   and enter alternate context if none pending
	mr	r4,	r6		// otherwise, introduce entry abort pc
	b	.L_ExitNoUpdateRuptControlWord	//   and prepare to reenter pseudokernel

.L_ExitToSystemContext:
	lwz	r5,	EPKD_INTCR2SHIFT(r3)
	lwz	r6,	EPKD_INTCR2MASK(r3)		
	slw	r7,	r12,	r5	// position backup cr2
	and	r7,	r7,	r6	//   and mask it
	or	r8,	r8,	r7	//   then or it into the live cr2
											// ...fall through into system context

.L_ExitUpdateRuptControlWord:
	rlwimi	r9,	r0,	0,	MSR_FE0_BIT,	MSR_FE1_BIT
	/* Disable FPU */
	rlwinm	r9,	r9,	0,	MSR_FP_BIT+1,	MSR_FP_BIT-1
		// insert updated fe0, fe1, se, and be bits into user msr
	stw	r12,	0(r11)		// update interruption control word 
.L_ExitNoUpdateRuptControlWord:
	lwz	r5,	EPKD_GPR0(r3)
	lwz	r6,	EPKD_SP(r3)
	lwz	r7,	EPKD_GPR3(r3)

	/* turn Data Relocation back off */
	mtmsr	r1
	isync
											// load caller's new register content

	stw	r4,	PP_SAVE_SRR0(r2)
	stw	r5,	PP_SAVE_R0(r2)
	stw	r6,	PP_SAVE_R1(r2)
	stw	r7,	PP_SAVE_R3(r2)
	stw	r8,	PP_SAVE_CR(r2)
	stw	r9,	PP_SAVE_SRR1(r2)

	b	.L_BlueBoxCommonExit

#if 1
/*
** EmulateUnimplementedInstructions
**
** Entry	r1-r3 have been saved in sprg1-3. 
**		r2 - byte offset into per_proc_info of this CPU
**		r3 - contains the exception number.
**              cr   saved in per_proc_info structure
**
*/
EmulatePrivilegedInstructions:
EmulateUnimplementedInstructions:
	stw	r0,PP_SAVE_R0+( 0*4)(r2)
	stmw	r4,PP_SAVE_R0+( 4*4)(r2)

	//la	UnimpMQptr,RegMQ(ContextPtr)
	//lwz	ContextPtr,XCP_GPR_0_31+(ContextPtr*4)(KernelDataPtr)


#if CountExceptions
//	Assume that the instruction will be emulated, so increment the count.
	//lwz	DataTemp,NI+EmulatedUnimpInstCount(KernelDataPtr)
	//lwz	MemProcPtr,MemProcBasePtr(KernelDataPtr)
	//addi	DataTemp,DataTemp,1
	//stw	DataTemp,NI+EmulatedUnimpInstCount(KernelDataPtr)
#else
	//lwz	MemProcPtr,MemProcBasePtr(KernelDataPtr)
#endif

	mfmsr	MSR_Disabled
	//ori	MSR_Enabled,MSR_Disabled,msr_dr
	ori	MSR_Enabled,MSR_Disabled,MASK(MSR_DR)

	mtmsr	MSR_Enabled		// enable Data Relocation
	isync
	lwz	MemInstr,0(SavedSRR0)	// read the faulty instruction
	mtmsr	MSR_Disabled		// disable data relocation
	isync

	//rlwinm DataTemp,MemInstr,6,0x000003F		// get Opcode field
	rlwinm	DataTemp,MemInstr,6,26,31	// get Opcode field
	cmpwi	cr6,DataTemp,9
	cmpwi	cr0,DataTemp,22
	cmpwi	cr1,DataTemp,31

	//lwz	MemDataH,UnimpDefaults(KernelDataPtr)
	lis	MemDataH,0xFFFF
	ori	MemDataH,MemDataH,0xFFFF

	//rlwinm	MemDataL,MemCtxFlags,b_CtxFlagEmulatePowerCompatible-b_EmulatePOWERmaskLSB,1<<(31-b_EmulatePOWERmaskLSB)
	rlwinm	MemDataL,MemCtxFlags,b_CtxFlagEmulatePowerCompatible-b_EmulatePOWERmaskLSB,14,14)
	neg		MemDataL,MemDataL
	//rlwimi	MemDataL,MemCtxFlags,b_CtxFlagEmulateOptionalInstr-b_EmulateOptional,1<<(31-b_EmulateOptional)
	rlwimi	MemDataL,MemCtxFlags,b_CtxFlagEmulateOptionalInstr-b_EmulateOptional,16,16)
	or		MemDataL,MemDataL,MemDataH
	//rlwimi	MemDataL,MemInstr,0,0x000007FF
	rlwimi	MemDataL,MemInstr,0,21,31

	//rlwimi	MemCtxFlags,MemCtxFlags,(b_CtxFlagStepTraceEnabled-b_CtxFlagTracePending)&0x1F,1<<(31-b_CtxFlagTracePending)
	rlwimi	MemCtxFlags,MemCtxFlags,(b_CtxFlagStepTraceEnabled-b_CtxFlagTracePending)&0x1F,b_CtxFlagTracePending,b_CtxFlagTracePending)

	//rlwinm	UnimpRSRT,MemInstr,13+0,0x0000007C
	rlwinm	UnimpRSRT,MemInstr,13+0,25,29
	//rlwinm	UnimpRA,MemInstr,13+5,0x0000007C
	rlwinm	UnimpRA,MemInstr,13+5,25,29
//	001001 rt-rt ra-ra si-si si-si si-si s		dozi		rt,ra,si
	beq	cr6,EmulateDOZI

	mtcrf	0x3F,MemDataL
	//rlwinm	UnimpRB,MemInstr,13+10,0x0000007C
	rlwinm	UnimpRB,MemInstr,13+10,25,29
//	010110 rs-rs ra-ra rb-rb mb-mb me-me .		rlmi[.]		ra,rs,rb,mb,me
	beq	cr0,EmulateRLMI

	bne	cr1,EmulateOp31done
	//rlwinm	MemDataL,MemInstr,2,0x000000F8		// low 5 bits of XO field * 8
	rlwinm	MemDataL,MemInstr,2,24,28		// low 5 bits of XO field * 8
	add	MemDataL,MemDataL,MemProcPtr
//	lwz	MemDataH,EmulateOp31Lookup+0-MemProcBase(MemDataL)
	//rlwinm	DataTemp,MemInstr,26,0x0000001F		// high 5 bits of XO field
	rlwinm	DataTemp,MemInstr,26,27,31	// high 5 bits of XO field
//	lwz	MemDataL,EmulateOp31Lookup+4-MemProcBase(MemDataL)
	rotlw.	MemDataH,MemDataH,DataTemp		// see if supported XO field
	add	MemDataL,MemDataL,MemProcPtr		// compute routine address
	mtlr	MemDataL

	bltlr				// dispatch to supported proc
EmulateOp31done:

//	100001 rt-rt ra-ra d---d d---d d---d d	lwzu		rt,d(ra)
//	100011 rt-rt ra-ra d---d d---d d---d d	lbzu		rt,d(ra)
//	100101 rs-rs ra-ra d---d d---d d---d d	stwu		rs,d(ra)
//	100111 rs-rs ra-ra d---d d---d d---d d	stbu		rs,d(ra)
//	101001 rt-rt ra-ra d---d d---d d---d d	lhzu		rt,d(ra)
//	101011 rt-rt ra-ra d---d d---d d---d d	lhau		rt,d(ra)
//	101101 rs-rs ra-ra d---d d---d d---d d	sthu		rs,d(ra)
//	101110 rt-rt ra-ra d---d d---d d---d d	lmw			rt,d(ra)
//	110001 ft-ft ra-ra d---d d---d d---d d	lfsu		ft,d(ra)
//	110011 ft-ft ra-ra d---d d---d d---d d	lfdu		ft,d(ra)
//	110101 fs-fs ra-ra d---d d---d d---d d	stfsu		fs,d(ra)
//	110111 fs-fs ra-ra d---d d---d d---d d	stfdu		fs,d(ra)
	ble	cr1,EmulateILLEGAL	// primary opcode <= 31 are illegal
	lis	MemDataH,0x55565500>>16	// mask for valid primary opcodes
	ori	MemDataH,MemDataH,0x55565500&0xFFFF
	rotlw.	MemDataH,MemDataH,DataTemp			// see if this opcode is legal
	blt	EmulatePowerMemoryInvalidForm		// if legal opcode, must be invalid form

EmulateILLEGAL:
EmulateDisabledPowerMQ:
EmulateDisabledPowerRTC:
EmulateDisabledPowerDEC:
EmulateDisabledPowerComplex:
EmulateDisabledInvalidSPR:
EmulateDisabledPowerCLCS:
EmulateDisabledPowerMemory:
EmulateDisabledOptional:
	mtcrf	0x70,SavedSRR1		// test SRR1 flags for original cause
	li	Tmp1,ecInvalidInstr	// invalid instruction
	bf	13,GenerateInvalidInstructionException	// If not privileged, use ecInvalidInstr
EmulatePRIVILEGED:
	mtcrf	0x0F,SavedSRR1		// test SRR1 flags for PR bit
	li	Tmp1,ecInvalidInstr	// invalid instruction
//	bf	b_msr_pr,GenerateInvalidInstructionException	// If privileged, use ecInvalidInstr
	li	Tmp1,ecPrivilegedInstr	// privileged instruction
GenerateInvalidInstructionException:
#if CountExceptions
//	The instruction was really invalid, so decrement the emulated instruction count.
	//lwz		Tmp2,NI+EmulatedUnimpInstCount(KernelDataPtr)
	//lmw		r14,XCP_GPR_0_31+(14*4)(KernelDataPtr)
	subi	Tmp2,Tmp2,1
	//stw		Tmp2,NI+EmulatedUnimpInstCount(KernelDataPtr)
#else
	//lmw		r14,XCP_GPR_0_31+(14*4)(KernelDataPtr)
#endif
//	lwz		ContextPtr,ActiveContextPtr(KernelDataPtr)
//	lwz		ContextStateFlags,ActiveCtxFlags(KernelDataPtr)
	b		GenerateException					// call the exception handler


EmulateOp31Lookup:
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 00
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 01
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 02
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 03
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 04
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 05
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 06
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 07
//	011111 rt-rt ra-ra rb-rb 01000 01000 .	doz[.]		rt,ra,rb
//	011111 rt-rt ra-ra xxxxx 01011 01000 .	abs[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 01111 01000 .	nabs[.]		rt,ra
//	011111 rt-rt ra-ra rb-rb 11000 01000 .	dozo[.]		rt,ra,rb
//	011111 rt-rt ra-ra xxxxx 11011 01000 .	abso[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 11111 01000 .	nabso[.]	rt,ra
	.long	0x00910091,EmulateDOZ_ABS_NABS-MemProcBase		// 08
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 09
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 0A
//	011111 rt-rt ra-ra rb-rb 00011 01011 .	mul[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01010 01011 .	div[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01011 01011 .	divs[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 10011 01011 .	mulo[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11010 01011 .	divo[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11011 01011 .	divso[.]	rt,ra,rb
	.long	0x10301030,EmulateMUL_DIV_DIVS-MemProcBase		// 0B
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 0C
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 0D
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 0E
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 0F
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 10
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 11
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 12
//	011111 rt-rt 00000 xxxxx 01010 10011 x	mfmq		rt
//	011111 rt-rt 00001 xxxxx 01010 10011 x	mfxer		rt
//	011111 rt-rt 00010 xxxxx 01010 10011 x	mfspr		rt,0x2
//	011111 rt-rt 00011 xxxxx 01010 10011 x	mfspr		rt,0x3
//	011111 rt-rt 00100 xxxxx 01010 10011 x	mfrtcu		rt
//	011111 rt-rt 00101 xxxxx 01010 10011 x	mfrtcl		rt
//	011111 rt-rt 00110 xxxxx 01010 10011 x	mfdec		rt
//	011111 rt-rt 00111 xxxxx 01010 10011 x	mfspr		rt,0x7
//	011111 rt-rt 01000 xxxxx 01010 10011 x	mflr		rt
//	011111 rt-rt 01001 xxxxx 01010 10011 x	mfctr		rt
//	011111 rt-rt 01010 xxxxx 01010 10011 x	mfspr		rt,0xA
//	011111 rt-rt 01011 xxxxx 01010 10011 x	mfspr		rt,0xB
//	011111 rt-rt 01100 xxxxx 01010 10011 x	mfspr		rt,0xC
//	011111 rt-rt 01101 xxxxx 01010 10011 x	mfspr		rt,0xD
//	011111 rt-rt 01110 xxxxx 01010 10011 x	mfspr		rt,0xE
//	011111 rt-rt 01111 xxxxx 01010 10011 x	mfspr		rt,0xF
//	011111 rt-rt 11111 01000 01010 10011 x	mfspr		rt,287
//	011111 rt-rt 11000 11101 01010 10011 x	mfspr		rt,952
//	011111 rt-rt 11001 11101 01010 10011 x	mfspr		rt,953
//	011111 rt-rt 11010 11101 01010 10011 x	mfspr		rt,954
//	011111 rt-rt 11011 11101 01010 10011 x	mfspr		rt,955
//	011111 rt-rt 11100 11101 01010 10011 x	mfspr		rt,956
//	011111 rt-rt 11101 11101 01010 10011 x	mfspr		rt,957
//	011111 rt-rt 11110 11101 01010 10011 x	mfspr		rt,958
//	011111 rt-rt 11111 11101 01010 10011 x	mfspr		rt,959
//	011111 rt-rt 01100 01000 01011 10011 x	mftb		rt
//	011111 rt-rt 01101 01000 01011 10011 x	mftbu		rt
//	011111 rs-rs 00000 xxxxx 01110 10011 x	mtmq		rs
//	011111 rs-rs 00001 xxxxx 01110 10011 x	mtxer		rs
//	011111 rs-rs 00010 xxxxx 01110 10011 x	mtspr		0x2,rs
//	011111 rs-rs 00011 xxxxx 01110 10011 x	mtspr		0x3,rs
//	011111 rs-rs 00100 xxxxx 01110 10011 x	mtspr		0x4,rs
//	011111 rs-rs 00101 xxxxx 01110 10011 x	mtspr		0x5,rs
//	011111 rs-rs 00110 xxxxx 01110 10011 x	mtspr		0x6,rs
//	011111 rs-rs 00111 xxxxx 01110 10011 x	mtspr		0x7,rs
//	011111 rs-rs 01000 xxxxx 01110 10011 x	mtlr		rs
//	011111 rs-rs 01001 xxxxx 01110 10011 x	mtctr		rs
//	011111 rs-rs 01010 xxxxx 01110 10011 x	mtspr		0xA,rs
//	011111 rs-rs 01011 xxxxx 01110 10011 x	mtspr		0xB,rs
//	011111 rs-rs 01100 xxxxx 01110 10011 x	mtspr		0xC,rs
//	011111 rs-rs 01101 xxxxx 01110 10011 x	mtspr		0xD,rs
//	011111 rs-rs 01110 xxxxx 01110 10011 x	mtspr		0xE,rs
//	011111 rs-rs 01111 xxxxx 01110 10011 x	mtspr		0xF,rs
//	011111 rs-rs 11000 11101 01110 10011 x	mtspr		952,rs
//	011111 rs-rs 11001 11101 01110 10011 x	mtspr		953,rs
//	011111 rs-rs 11010 11101 01110 10011 x	mtspr		954,rs
//	011111 rs-rs 11011 11101 01110 10011 x	mtspr		955,rs
//	011111 rs-rs 11100 11101 01110 10011 x	mtspr		956,rs
//	011111 rs-rs 11101 11101 01110 10011 x	mtspr		957,rs
//	011111 rs-rs 11110 11101 01110 10011 x	mtspr		958,rs
//	011111 rs-rs 11111 11101 01110 10011 x	mtspr		959,rs
//	011111 rt-rt ra-ra xxxxx 10000 10011 x	clcs		rt,ra
	.long	0x00328000,EmulateMFSPR_MFTB_MTSPR_CLCS-MemProcBase	// 13
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 14
//	011111 rt-rt ra-ra rb-rb 01000 10101 .	lscbx[.]	rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 10000 10101 x	lswx		rt,ra,rb
//	011111 rt-rt ra-ra nb-nb 10010 10101 x	lswi		rt,ra,nb
	.long	0x0080A000,EmulatePowerMemoryInvalidForm-MemProcBase	// 15
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 16
//	011111 rt-rt ra-ra rb-rb 00001 10111 x	lwzux		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 00011 10111 x	lbzux		rt,ra,rb
//	011111 rs-rs ra-ra rb-rb 00101 10111 x	stwux		rs,ra,rb
//	011111 rs-rs ra-ra rb-rb 00111 10111 x	stbux		rs,ra,rb
//	011111 rt-rt ra-ra rb-rb 01001 10111 x	lhzux		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01011 10111 x	lhaux		rt,ra,rb
//	011111 rs-rs ra-ra rb-rb 01101 10111 x	sthux		rs,ra,rb
//	011111 ft-ft ra-ra rb-rb 10001 10111 x	lfsux		ft,ra,rb
//	011111 ft-ft ra-ra rb-rb 10011 10111 x	lfdux		ft,ra,rb
//	011111 fs-fs ra-ra rb-rb 10101 10111 x	stfsux		fs,ra,rb
//	011111 fs-fs ra-ra rb-rb 10111 10111 x	stfdux		fs,ra,rb
//	011111 fs-fs ra-ra rb-rb 11110 10111 x	stfiwx		fs,ra,rb
	.long	0x55545502,EmulateUpdateMemOps_STFIWX-MemProcBase	// 17
//	011111 rs-rs ra-ra rb-rb 00100 11000 .	slq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 00101 11000 .	sliq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00110 11000 .	sllq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 00111 11000 .	slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10100 11000 .	srq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 10101 11000 .	sriq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10110 11000 .	srlq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 10111 11000 .	srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 11100 11000 .	sraq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 11101 11000 .	sraiq[.]	ra,rs,sh
	.long	0x0F000F0C,EmulateSHIFTQ-MemProcBase			// 18
//	011111 rs-rs ra-ra rb-rb 00100 11001 .	sle[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 00110 11001 .	sleq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10000 11001 .	rrib[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10100 11001 .	sre[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11001 .	sreq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 11100 11001 .	srea[.]		ra,rs,rb
	.long	0x0A008A08,EmulateRRIB_SHIFTE-MemProcBase		// 19
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 1A
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 1B
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 1C
//	011111 rs-rs ra-ra rb-rb 00000 11101 .	maskg[.]	ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10000 11101 .	maskir[.]	ra,rs,rb
	.long	0x80008000,EmulateMASKG_MASKIR-MemProcBase		// 1D
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 1E
	.long	0x00000000,EmulateILLEGAL-MemProcBase			// 1F


EmulateUpdateRT_cr0_xer_mq:
	stw	MemDataH,0(UnimpMQptr)	// update MQ in context block
EmulateUpdateRT_cr0_xer:								// update XER (live XER already updated)
EmulateUpdateRT_cr0:
	bf	31,EmulateUpdateRT	// test the Rc bit
	mfcr	DataTemp		// get current CR0
	//rlwimi	SavedCR,DataTemp,0,0xF0000000	// update saved CR0
	rlwimi	SavedCR,DataTemp,0,0,3	// update saved CR0
EmulateUpdateRT:
	stwx	MemDataL,KernelDataPtr,UnimpRT	// update RT
	b	CompleteMemOpDone	// instruction emulation complete


EmulateDOZ_ABS_NABS:
//	           1 11111 11112 22222 22223 3
//	012345 67890 12345 67890 12345 67890 1
//	------ ----- ----- ----- ----- ----- -
//	011111 rt-rt ra-ra rb-rb 01000 01000 .		doz[.]		rt,ra,rb
//	011111 rt-rt ra-ra xxxxx 01011 01000 .		abs[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 01111 01000 .		nabs[.]		rt,ra
//	011111 rt-rt ra-ra rb-rb 11000 01000 .		dozo[.]		rt,ra,rb
//	011111 rt-rt ra-ra xxxxx 11011 01000 .		abso[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 11111 01000 .		nabso[.]	rt,ra
	bf	b_EmulatePowerComplex,EmulateDisabledPowerComplex
	lwzx	UnimpRA,KernelDataPtr,UnimpRA		// get RA
	bf	24,EmulateDOZ
EmulateABS_NABS:
//	011111 rt-rt ra-ra xxxxx 01011 01000 .		abs[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 01111 01000 .		nabs[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 11011 01000 .		abso[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 11111 01000 .		nabso[.]	rt,ra
	bt	21,EmulateABSO_NABSO
//	011111 rt-rt ra-ra xxxxx 01011 01000 .		abs[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 01111 01000 .		nabs[.]		rt,ra
	mr.	MemDataL,UnimpRA	// get operand, update cr0
//	crxor	23,23,lt		// exchange abs with nabs if negative
	crxor	23,23,lt_b		// exchange abs with nabs if negative
	bf	23,EmulateUpdateRT_cr0	// return unchanged result
	neg.	MemDataL,UnimpRA	// negate operand, update cr0
	b	EmulateUpdateRT_cr0	// return negated result


EmulateABSO_NABSO:
//	011111 rt-rt ra-ra xxxxx 11011 01000 .		abso[.]		rt,ra
//	011111 rt-rt ra-ra xxxxx 11111 01000 .		nabso[.]	rt,ra
	li	MemDataL,0
	addo.	MemDataL,UnimpRA,MemDataL	// get operand, clear OV, cr0
//	crxor	23,23,lt		// exchange abs with nabs if negative
	crxor	23,23,lt_b		// exchange abs with nabs if negative
	bf	23,EmulateUpdateRT_cr0_xer	// return unchanged result
	nego.	MemDataL,UnimpRA	// negate operand, update OV, cr0
	b	EmulateUpdateRT_cr0_xer	// return negated result


EmulateDOZI:
//	001001 rt-rt ra-ra si-si si-si si-si s		dozi		rt,ra,si
	mtcrf	0x3F,MemDataL
	bf		b_EmulatePowerComplex,EmulateDisabledPowerComplex
	lwzx	UnimpRA,KernelDataPtr,UnimpRA		// get RA
	extsh	UnimpRB,MemInstr					// get SI
	cmpw	cr1,UnimpRB,UnimpRA					// see if SI < RA
	sub	MemDataL,MemDataL,MemDataL			// assume zero
	blt	cr1,EmulateUpdateRT					// return zero when difference < zero
	sub	MemDataL,UnimpRB,UnimpRA			// compute difference
	b		EmulateUpdateRT						// return difference when >= zero


EmulateDOZ:
//	011111 rt-rt ra-ra rb-rb 01000 01000 .		doz[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11000 01000 .		dozo[.]		rt,ra,rb
	lwzx	UnimpRB,KernelDataPtr,UnimpRB		// get RB
	bt		21,EmulateDOZO
//	011111 rt-rt ra-ra rb-rb 01000 01000 .		doz[.]		rt,ra,rb
	cmpw	cr1,UnimpRB,UnimpRA					// see if RB < RA
	sub.	MemDataL,MemDataL,MemDataL			// assume zero
	blt		cr1,EmulateUpdateRT_cr0				// return zero when difference < zero
	sub.	MemDataL,UnimpRB,UnimpRA			// compute difference
	b		EmulateUpdateRT_cr0					// return difference when >= zero


EmulateDOZO:
//	011111 rt-rt ra-ra rb-rb 11000 01000 .		dozo[.]		rt,ra,rb
	cmpw	cr1,UnimpRB,UnimpRA					// see if RB < RA
	subo.	MemDataL,MemDataL,MemDataL			// assume zero
	blt		cr1,EmulateUpdateRT_cr0_xer			// return zero when difference < zero
	subo.	MemDataL,UnimpRB,UnimpRA			// compute difference
	b		EmulateUpdateRT_cr0_xer				// return difference when >= zero


EmulateMUL_DIV_DIVS:
//	           1 11111 11112 22222 22223 3
//	012345 67890 12345 67890 12345 67890 1
//	------ ----- ----- ----- ----- ----- -
//	011111 rt-rt ra-ra rb-rb 00011 01011 .		mul[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01010 01011 .		div[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01011 01011 .		divs[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 10011 01011 .		mulo[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11010 01011 .		divo[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11011 01011 .		divso[.]	rt,ra,rb
	bf		b_EmulatePowerMQ,EmulateDisabledPowerMQ
	lwzx	UnimpRB,KernelDataPtr,UnimpRB		// get RB
	lwzx	UnimpRA,KernelDataPtr,UnimpRA		// get RA
	bf		22,EmulateMUL
//	011111 rt-rt ra-ra rb-rb 01010 01011 .		div[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01011 01011 .		divs[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11010 01011 .		divo[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11011 01011 .		divso[.]	rt,ra,rb
	cmpwi	cr1,UnimpRB,0						// anything / 00000000 is undefined
	bt		25,EmulateDIVS
//	011111 rt-rt ra-ra rb-rb 01010 01011 .		div[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11010 01011 .		divo[.]		rt,ra,rb

	lwz		UnimpMQtmp,0(UnimpMQptr)			// get MQ from context block
	srwi	MemDataL,UnimpMQtmp,31				// get Sign(MQ)
	add.	MemDataL,MemDataL,UnimpRA			// see if RA=Sign(MQ)
	bne		EmulateDIV							// perform complex 64 bit divide when RA<>Sign(MQ)
	mr		UnimpRA,UnimpMQtmp					// perform 32 bit DIVS (MQ/RB) when RA=Sign(MQ)

EmulateDIVS:
//	011111 rt-rt ra-ra rb-rb 01011 01011 .		divs[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 11011 01011 .		divso[.]	rt,ra,rb
	cmpwi	cr0,UnimpRB,-1						// 80000000 / FFFFFFFF is undefined
	bt		21,EmulateDIVSO
//	011111 rt-rt ra-ra rb-rb 01011 01011 .		divs[.]		rt,ra,rb
	beq		cr0,EmulateDIVS_neg_one				// handle RA / FFFFFFFF (just negate)
	beq		cr1,EmulateDIVS_zero				// handle RA / 00000000
	divw	MemDataL,UnimpRA,UnimpRB			// quotient <- RA / RB
EmulateDIVS_rem_calc:
	mullw	MemDataH,MemDataL,UnimpRB			// quotient * RB
	sub.	MemDataH,UnimpRA,MemDataH			// remainder <- RA - (quotient * RB)
	b		EmulateUpdateRT_cr0_xer_mq
EmulateDIVS_neg_one:
	neg		MemDataL,UnimpRA					// quotient <- -RA
	sub.	MemDataH,UnimpRA,UnimpRA			// remainder <- 00000000
	b		EmulateUpdateRT_cr0_xer_mq

EmulateDIVSO:
//	011111 rt-rt ra-ra rb-rb 11011 01011 .		divso[.]	rt,ra,rb
	divwo	MemDataL,UnimpRA,UnimpRB			// quotient <- RA / RB, compute OV bit
	beq		cr0,EmulateDIVS_neg_one				// handle RA / FFFFFFFF (just negate)
	bne		cr1,EmulateDIVS_rem_calc			// compute remainder, unless RA / 00000000
EmulateDIVS_zero:
	//rlwinm	DataTemp,UnimpRA,2,0x00000002		// quotient <- 00000001 when RA < 0
	rlwinm	DataTemp,UnimpRA,2,30,30// quotient <- 00000001 when RA < 0
	subi	MemDataL,DataTemp,1					// quotient <- FFFFFFFF when RA >= 0
	mr.		MemDataH,UnimpRA					// remainder <- RA
	b		EmulateUpdateRT_cr0_xer_mq


EmulateDIV:
	mfxer	UnimpXERtmp			// save XER CA bit
	beq	cr1,EmulateDIV_zero		// handle RA / 00000000

	cmpwi	cr0,UnimpRB,0			// test sign of RB
	cmpwi	cr1,UnimpRA,0			// test sign of RA
//	crxor	(cr1*4)+so,(cr0*4)+lt,(cr1*4)+lt// compute sign of quotient
	crxor	(rcr1*4)+so_b,(rcr0*4)+lt_b,(rcr1*4)+lt_b// compute sign of quotient
	bge	cr0,EmulateDIV_denom_ok		// compute abs (RB)
	neg	UnimpRB,UnimpRB			// UnimpRB <- abs (32 bit denom)
EmulateDIV_denom_ok:
	bge	cr1,EmulateDIV_numer_ok		// compute abs (RA||MQ)
	subfic	UnimpMQtmp,UnimpMQtmp,0		// UnimpRA/UnimpMQtmp <- abs (64 bit numerator)
	subfze	UnimpRA,UnimpRA
EmulateDIV_numer_ok:	
	cmplw	UnimpRA,UnimpRB			// see if abs(quotient) fits in 32 bits
	bge	EmulateDIV_initial_ovfl		// handle overflow

#if	0
//	Slower, but smaller divide algorithm
	mfctr	DataTemp			// save the CTR register
	li	MemDataL,32			// loop for 32 bits
	mtctr	MemDataL			// setup the counter
EmulateDIV_shift:
	slwi	UnimpRA,UnimpRA,1		// shift 64 bit remainder
	//rlwimi	UnimpRA,UnimpMQtmp,1,0x00000001		// shift in MSB of low 32 bits
	rlwimi	UnimpRA,UnimpMQtmp,1,31,31	// shift in MSB of low 32 bits
	cmplw	UnimpRA,UnimpRB			// Q = remainder >= denominator
	slwi	UnimpMQtmp,UnimpMQtmp,1		// shift in the quotient bit (assume Q=0)
	blt	EmulateDIV_q0			// branch when Q=0
EmulateDIV_q1:
	sub	UnimpRA,UnimpRA,UnimpRB		// subtract denom from remainder
	ori	UnimpMQtmp,UnimpMQtmp,1		// set the quotient bit (Q=1)
EmulateDIV_q0:
	bdnz	EmulateDIV_shift		// loop for 32 bits
	mtctr	DataTemp			// restore the CTR register
	mr	MemDataH,UnimpRA		// MemDataH <- remainder
#else
//	about 1.5x faster, but bigger divide algorithm
	cntlzw	MemDataL,UnimpRB		// MemDataL <- denominator shift amount
	xor	UnimpRA,UnimpRA,UnimpMQtmp	// merge numerator halves for left shift
	slw	UnimpRB,UnimpRB,MemDataL	// UnimpRB <- normalized denom
	rotlw	UnimpRA,UnimpRA,MemDataL	// rotate numer.high xor numer.low
	slw	UnimpMQtmp,UnimpMQtmp,MemDataL	// shift numer.low
	xor		UnimpRA,UnimpRA,UnimpMQtmp// UnimpRA/UnimpMQtmp <- left shifted numer.high/low

	srwi	DataTemp,UnimpRB,16	// get upper 16 bits of normalized denom
	divwu	MemDataH,UnimpRA,DataTemp	// compute first 16 bits of quotient estimate
	mullw	DataTemp,MemDataH,DataTemp	// quotient * upper(denom)
	sub		UnimpRA,UnimpRA,DataTemp// compute remainder using upper 16 bits of denom
	slwi	UnimpRA,UnimpRA,16		// shift remainder left by 16 bit
	//rlwimi	UnimpRA,UnimpMQtmp,16,0x0000FFFF
	rlwimi	UnimpRA,UnimpMQtmp,16,16,31
	slwi	UnimpMQtmp,UnimpMQtmp,16
	//rlwinm	DataTemp,UnimpRB,0,0x0000FFFF		// get lower 16 bits of normalized denom
	rlwinm	DataTemp,UnimpRB,0,16,31// get lower 16 bits of normalized denom
	mullw	DataTemp,MemDataH,DataTemp			// quotient * lower(denom)
	subfc	UnimpRA,DataTemp,UnimpRA			// compute remainder using lower 16 bits of denom
	subfe.	DataTemp,DataTemp,DataTemp			// compute borrow
	add		UnimpMQtmp,UnimpMQtmp,MemDataH		// shift in quotient
	bge		EmulateDIV_compute_low
EmulateDIV_correct_high:
	addc	UnimpRA,UnimpRA,UnimpRB
	addze.	DataTemp,DataTemp
	subi	UnimpMQtmp,UnimpMQtmp,1
	blt		EmulateDIV_correct_high

EmulateDIV_compute_low:
	srwi	DataTemp,UnimpRB,16					// get upper 16 bits of normalized denom
	divwu	MemDataH,UnimpRA,DataTemp			// compute next 16 bits of quotient estimate
	mullw	DataTemp,MemDataH,DataTemp			// quotient * upper(denom)
	sub		UnimpRA,UnimpRA,DataTemp			// compute remainder using upper 16 bits of denom
	slwi	UnimpRA,UnimpRA,16					// shift remainder left by 16 bit
	//rlwimi	UnimpRA,UnimpMQtmp,16,0x0000FFFF
	rlwimi	UnimpRA,UnimpMQtmp,16,16,31
	slwi	UnimpMQtmp,UnimpMQtmp,16
	//rlwinm	DataTemp,UnimpRB,0,0x0000FFFF		// get lower 16 bits of normalized denom
	rlwinm	DataTemp,UnimpRB,0,16,31		// get lower 16 bits of normalized denom
	mullw	DataTemp,MemDataH,DataTemp			// quotient * lower(denom)
	subfc	UnimpRA,DataTemp,UnimpRA			// compute remainder using lower 16 bits of denom
	subfe.	DataTemp,DataTemp,DataTemp			// compute borrow
	add		UnimpMQtmp,UnimpMQtmp,MemDataH		// shift in quotient
	bge		EmulateDIV_low_done
EmulateDIV_correct_low:
	addc	UnimpRA,UnimpRA,UnimpRB
	addze.	DataTemp,DataTemp
	subi	UnimpMQtmp,UnimpMQtmp,1
	blt		EmulateDIV_correct_low
EmulateDIV_low_done:
	srw		MemDataH,UnimpRA,MemDataL			// MemDataH <- remainder
#endif

EmulateDIV_correct_signs:
	mr.		MemDataL,UnimpMQtmp					// MemDataL <- quotient
	bge		cr1,EmulateDIV_r_done
	neg		MemDataH,MemDataH
EmulateDIV_r_done:
	bns		cr1,EmulateDIV_q_done
	neg.	MemDataL,MemDataL
EmulateDIV_q_done:
	bf		21,EmulateDIV_xer_done
//	crxor	(cr0*4)+lt,(cr0*4)+lt,(cr1*4)+so
	crxor	(rcr0*4)+lt_b,(rcr0*4)+lt_b,(rcr1*4)+so_b
	//rlwinm	UnimpXERtmp,UnimpXERtmp,0,0xBFFFFFFF// clear OV bit
	rlwinm	UnimpXERtmp,UnimpXERtmp,0,2,0	// clear OV bit
	bge		EmulateDIV_xer_done
	oris	UnimpXERtmp,UnimpXERtmp,0xC000		// set SO and OV bits
EmulateDIV_xer_done:
	mtxer	UnimpXERtmp							// restore XER
	mr.		MemDataH,MemDataH
	b		EmulateUpdateRT_cr0_xer_mq

EmulateDIV_initial_ovfl:
//	I can't figure out the 601 algorithm for undefined overflow quo/rem.
//	Instead, return the values that would be computed for divide by zero.
EmulateDIV_zero:
	bf	21,EmulateDIV_zero_xer_done
	oris	UnimpXERtmp,UnimpXERtmp,0xC000		// set SO and OV bits
EmulateDIV_zero_xer_done:
	mtxer	UnimpXERtmp				// restore XER
	not	MemDataL,UnimpRA
	srwi	DataTemp,UnimpRA,31
	mr.	MemDataH,UnimpMQtmp
	add	MemDataL,DataTemp,MemDataL
	b	EmulateUpdateRT_cr0_xer_mq


EmulateMUL:
//	011111 rt-rt ra-ra rb-rb 00011 01011 .		mul[.]		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 10011 01011 .		mulo[.]		rt,ra,rb
	mulhw	MemDataL,UnimpRA,UnimpRB			// RT <- low 32 bits of product
	bt		21,EmulateMULO
//	011111 rt-rt ra-ra rb-rb 00011 01011 .		mul[.]		rt,ra,rb
	mullw.	MemDataH,UnimpRA,UnimpRB			// MQ <- high 32 bits of product
	b		EmulateUpdateRT_cr0_xer_mq

EmulateMULO:
//	011111 rt-rt ra-ra rb-rb 10011 01011 .		mulo[.]		rt,ra,rb
	mullwo.	MemDataH,UnimpRA,UnimpRB	// MQ <- high 32 bits of product (update OV)
	b		EmulateUpdateRT_cr0_xer_mq


EmulateMFSPR_MFTB_MTSPR_CLCS:
//	           1 11111 11112 22222 22223 3
//	012345 67890 12345 67890 12345 67890 1
//	------ ----- ----- ----- ----- ----- -
//	011111 rt-rt 00000 xxxxx 01010 10011 x		mfmq		rt
//	011111 rt-rt 00001 xxxxx 01010 10011 x		mfxer		rt
//	011111 rt-rt 00010 xxxxx 01010 10011 x		mfspr		rt,0x2
//	011111 rt-rt 00011 xxxxx 01010 10011 x		mfspr		rt,0x3
//	011111 rt-rt 00100 xxxxx 01010 10011 x		mfrtcu		rt
//	011111 rt-rt 00101 xxxxx 01010 10011 x		mfrtcl		rt
//	011111 rt-rt 00110 xxxxx 01010 10011 x		mfdec		rt
//	011111 rt-rt 00111 xxxxx 01010 10011 x		mfspr		rt,0x7
//	011111 rt-rt 01000 xxxxx 01010 10011 x		mflr		rt
//	011111 rt-rt 01001 xxxxx 01010 10011 x		mfctr		rt
//	011111 rt-rt 01010 xxxxx 01010 10011 x		mfspr		rt,0xA
//	011111 rt-rt 01011 xxxxx 01010 10011 x		mfspr		rt,0xB
//	011111 rt-rt 01100 xxxxx 01010 10011 x		mfspr		rt,0xC
//	011111 rt-rt 01101 xxxxx 01010 10011 x		mfspr		rt,0xD
//	011111 rt-rt 01110 xxxxx 01010 10011 x		mfspr		rt,0xE
//	011111 rt-rt 01111 xxxxx 01010 10011 x		mfspr		rt,0xF
//	011111 rt-rt 11111 01000 01010 10011 x		mfspr		rt,287
//	011111 rt-rt 11000 11101 01010 10011 x		mfspr		rt,952
//	011111 rt-rt 11001 11101 01010 10011 x		mfspr		rt,953
//	011111 rt-rt 11010 11101 01010 10011 x		mfspr		rt,954
//	011111 rt-rt 11011 11101 01010 10011 x		mfspr		rt,955
//	011111 rt-rt 11100 11101 01010 10011 x		mfspr		rt,956
//	011111 rt-rt 11101 11101 01010 10011 x		mfspr		rt,957
//	011111 rt-rt 11110 11101 01010 10011 x		mfspr		rt,958
//	011111 rt-rt 11111 11101 01010 10011 x		mfspr		rt,959
//	011111 rt-rt 01100 01000 01011 10011 x		mftb		rt
//	011111 rt-rt 01101 01000 01011 10011 x		mftbu		rt
//	011111 rs-rs 00000 xxxxx 01110 10011 x		mtmq		rs
//	011111 rs-rs 00001 xxxxx 01110 10011 x		mtxer		rs
//	011111 rs-rs 00010 xxxxx 01110 10011 x		mtspr		0x2,rs
//	011111 rs-rs 00011 xxxxx 01110 10011 x		mtspr		0x3,rs
//	011111 rs-rs 00100 xxxxx 01110 10011 x		mtspr		0x4,rs
//	011111 rs-rs 00101 xxxxx 01110 10011 x		mtspr		0x5,rs
//	011111 rs-rs 00110 xxxxx 01110 10011 x		mtspr		0x6,rs
//	011111 rs-rs 00111 xxxxx 01110 10011 x		mtspr		0x7,rs
//	011111 rs-rs 01000 xxxxx 01110 10011 x		mtlr		rs
//	011111 rs-rs 01001 xxxxx 01110 10011 x		mtctr		rs
//	011111 rs-rs 01010 xxxxx 01110 10011 x		mtspr		0xA,rs
//	011111 rs-rs 01011 xxxxx 01110 10011 x		mtspr		0xB,rs
//	011111 rs-rs 01100 xxxxx 01110 10011 x		mtspr		0xC,rs
//	011111 rs-rs 01101 xxxxx 01110 10011 x		mtspr		0xD,rs
//	011111 rs-rs 01110 xxxxx 01110 10011 x		mtspr		0xE,rs
//	011111 rs-rs 01111 xxxxx 01110 10011 x		mtspr		0xF,rs
//	011111 rs-rs 11000 11101 01110 10011 x		mtspr		952,rs
//	011111 rs-rs 11001 11101 01110 10011 x		mtspr		953,rs
//	011111 rs-rs 11010 11101 01110 10011 x		mtspr		954,rs
//	011111 rs-rs 11011 11101 01110 10011 x		mtspr		955,rs
//	011111 rs-rs 11100 11101 01110 10011 x		mtspr		956,rs
//	011111 rs-rs 11101 11101 01110 10011 x		mtspr		957,rs
//	011111 rs-rs 11110 11101 01110 10011 x		mtspr		958,rs
//	011111 rs-rs 11111 11101 01110 10011 x		mtspr		959,rs
//	011111 rt-rt ra-ra xxxxx 10000 10011 x		clcs		rt,ra
	bt		25,EmulateMFTB
	bt		21,EmulateCLCS
	cmpwi	cr0,UnimpRA,0x10*4					// test for 1xxxx, privileged
	cmpwi	cr1,UnimpRA,0*4
	cmpwi	cr6,UnimpRA,1*4
	bt		23,EmulateMTSPR
EmulateMFSPR:
	bge		EmulatePrivilegedMFSPR				// privileged SPR
//	011111 rt-rt 00000 xxxxx 01010 10011 x		mfmq		rt
//	011111 rt-rt 00001 xxxxx 01010 10011 x		mfxer		rt
//	011111 rt-rt 00010 xxxxx 01010 10011 x		mfspr		rt,0x2
//	011111 rt-rt 00011 xxxxx 01010 10011 x		mfspr		rt,0x3
//	011111 rt-rt 00100 xxxxx 01010 10011 x		mfrtcu		rt
//	011111 rt-rt 00101 xxxxx 01010 10011 x		mfrtcl		rt
//	011111 rt-rt 00110 xxxxx 01010 10011 x		mfdec		rt
//	011111 rt-rt 00111 xxxxx 01010 10011 x		mfspr		rt,0x7
//	011111 rt-rt 01000 xxxxx 01010 10011 x		mflr		rt
//	011111 rt-rt 01001 xxxxx 01010 10011 x		mfctr		rt
//	011111 rt-rt 01010 xxxxx 01010 10011 x		mfspr		rt,0xA
//	011111 rt-rt 01011 xxxxx 01010 10011 x		mfspr		rt,0xB
//	011111 rt-rt 01100 xxxxx 01010 10011 x		mfspr		rt,0xC
//	011111 rt-rt 01101 xxxxx 01010 10011 x		mfspr		rt,0xD
//	011111 rt-rt 01110 xxxxx 01010 10011 x		mfspr		rt,0xE
//	011111 rt-rt 01111 xxxxx 01010 10011 x		mfspr		rt,0xF
//	CR0 is set as follows:
//		0,1,5,6,8,9 - lt,gt,eq cleared, so set based upon xer 
//		2,3,4,7,A,B,C,D,E,F - based upon add. ra,rb
	crclr	lt_b
	beq	cr1,EmulateMFMQ		// SPR=0, MFMQ
	beq	cr6,EmulateMFXER	// SPR=1, MFXER
	cmpwi	cr1,UnimpRA,5*4
	cmpwi	cr6,UnimpRA,6*4
	beq	cr1,EmulateMFRTC	// SPR=5, MFRTCL
	beq	cr6,EmulateMFDEC	// SPR=6, MFDEC
	cmpwi	cr1,UnimpRA,8*4
	cmpwi	cr6,UnimpRA,9*4
	beq	cr1,EmulateMFLR		// SPR=8, MFLR
	beq	cr6,EmulateMFCTR	// SPR=9, MFCTR

//	compute CR0 for RTCU and invalid SPR encodings
	cmpwi	cr6,UnimpRA,4*4
	lwzx	UnimpRA,KernelDataPtr,UnimpRA		// get RA
	lwzx	UnimpRB,KernelDataPtr,UnimpRB		// get RB
	add.	MemDataL,UnimpRA,UnimpRB			// cr0 based upon RA+RB
	beq		cr6,EmulateMFRTC					// SPR=4, MFRTCU
	bf		b_EmulateInvalidSPR,EmulateDisabledInvalidSPR
	b		EmulateUpdateCR0

EmulateMFMQ:
//	011111 rt-rt 00000 xxxxx 01010 10011 x		mfmq		rt
	bf		b_EmulatePowerMQ,EmulateDisabledPowerMQ
	lwz		MemDataL,0(UnimpMQptr)				// get MQ from context block
	b		EmulateUpdateRT_cr0

EmulateMFXER:
//	011111 rt-rt 00001 xxxxx 01010 10011 x		mfxer		rt
	bf		b_EmulateInvalidSPR,EmulateDisabledInvalidSPR
	mtcrf	0x80,SavedCR						// restore CR0
	//.long	(31<<26)+(MemDataL<<21)+(sprXER<<16)+(339<<1)+(1<<0)	// mfxer.	MemDataL
	.long	(31<<26)+(dMemDataL<<21)+(sprXER<<16)+(339<<1)+(1<<0)	// mfxer.	MemDataL
	b		EmulateUpdateRT_cr0

EmulateMFRTC:
//	011111 rt-rt 00100 xxxxx 01010 10011 x		mfrtcu		rt
//	011111 rt-rt 00101 xxxxx 01010 10011 x		mfrtcl		rt
	bf		b_EmulatePowerRTC,EmulateDisabledPowerRTC
RetryMFRTC:
	mftbu	MemDataH							// read the 64 bit Time Base register
	mftb	MemDataL
	mftbu	DataTemp
	cmplw	cr1,DataTemp,MemDataH
	bne-	cr1,RetryMFRTC						// retry if high half changed

	//lwz		DataTemp,TBtoRTCmult(KernelDataPtr)	// TBtoRTCmult
	//lbz		UnimpRA,TBtoRTCshL(KernelDataPtr)	// TBtoRTCshL
	//lbz		UnimpRB,TBtoRTCshR(KernelDataPtr)	// TBtoRTCshR
	mullw	UnimpMQptr,MemDataH,DataTemp		// TBU*TBtoRTCmult (L)
	mulhwu	UnimpMQtmp,MemDataL,DataTemp		// TBL*TBtoRTCmult (H)
	add		UnimpMQptr,UnimpMQptr,UnimpMQtmp	// TBU*TBtoRTCmult (L) + TBL*TBtoRTCmult (H)

	bne		cr6,EmulateMFRTCL

EmulateMFRTCU:
//	011111 rt-rt 00100 xxxxx 01010 10011 x		mfrtcu		rt
	cmplw	cr1,UnimpMQptr,UnimpMQtmp			// test for carry out
	srw		UnimpMQptr,UnimpMQptr,UnimpRB
	mulhwu	MemDataL,MemDataH,DataTemp			// TBU*TBtoRTCmult (upper 32 bits)
	bge+	cr1,$+8
	addi	MemDataL,MemDataL,1					// add in carry out
	slw		MemDataL,MemDataL,UnimpRA			// TBU*TBtoRTCmult (H) left  shifted by TBtoRTCshL
	add		MemDataL,MemDataL,UnimpMQptr		// 32 bit integer seconds
	b		EmulateUpdateRT_cr0

EmulateMFRTCL:
//	011111 rt-rt 00101 xxxxx 01010 10011 x		mfrtcl		rt
	mullw	MemDataL,MemDataL,DataTemp			// TBL*TBtoRTCmult (L)
	srw		MemDataL,MemDataL,UnimpRB			// TBL*TBtoRTCmult (L) right shifted by TBtoRTCshR
	slw		UnimpMQptr,UnimpMQptr,UnimpRA
	add		MemDataL,MemDataL,UnimpMQptr		// 32 bit fractional seconds
	lis		DataTemp,1000000000>>16
	ori		DataTemp,DataTemp,1000000000&0xFFFF
	mulhwu	MemDataL,MemDataL,DataTemp			// convert fraction to nanoseconds
	b		EmulateUpdateRT_cr0

EmulateMFDEC:
//	011111 rt-rt 00110 xxxxx 01010 10011 x		mfdec		rt
	bf		b_EmulatePowerDEC,EmulateDisabledPowerDEC
	mfspr	MemDataL,fromDEC					// get DEC
	b		EmulateUpdateRT_cr0

EmulateMFLR:
//	011111 rt-rt 01000 xxxxx 01010 10011 x		mflr		rt
	bf		b_EmulateInvalidSPR,EmulateDisabledInvalidSPR
	mtcrf	0x80,SavedCR						// restore CR0
	mtlr	SavedLR								// get LR
	//.long	(31<<26)+(MemDataL<<21)+(sprLR<<16)+(339<<1)+(1<<0)	// mflr.	MemDataL
	.long	(31<<26)+(dMemDataL<<21)+(sprLR<<16)+(339<<1)+(1<<0)	// mflr.	MemDataL
	b		EmulateUpdateRT_cr0

EmulateMFCTR:
//	011111 rt-rt 01001 xxxxx 01010 10011 x		mfctr		rt
	bf		b_EmulateInvalidSPR,EmulateDisabledInvalidSPR
	mtcrf	0x80,SavedCR						// restore CR0
	//.long	(31<<26)+(MemDataL<<21)+(sprCTR<<16)+(339<<1)+(1<<0)// mfctr.	MemDataL
	.long	(31<<26)+(dMemDataL<<21)+(sprCTR<<16)+(339<<1)+(1<<0)// mfctr.	MemDataL
	b		EmulateUpdateRT_cr0


	// the following registers are specific to the 604 and allow
	// performance monitoring for the entire system.
	// For now the access to these privileged registers is allowed to
	// let people do their thing. tjm
	//
	// The registers are:
	//		MMCR0:	SPR - 952
	//		PMC1:	SPR - 953
	//		PMC2:	SPR - 954
	//		SIA:	SPR - 955
	//		MMCR1:	SPR - 956
	//		PMC3:	SPR - 957
	//		PMC4:	SPR - 958
	//		SDA:	SPR - 959
	//
	//		PVR:	SPR - 287

EmulatePrivilegedMFSPR:
//	011111 rt-rt 11111 01000 01010 10011 x		mfspr		rt,287
//	011111 rt-rt 11000 11101 01010 10011 x		mfspr		rt,952
//	011111 rt-rt 11001 11101 01010 10011 x		mfspr		rt,953
//	011111 rt-rt 11010 11101 01010 10011 x		mfspr		rt,954
//	011111 rt-rt 11011 11101 01010 10011 x		mfspr		rt,955
//	011111 rt-rt 11100 11101 01010 10011 x		mfspr		rt,956
//	011111 rt-rt 11101 11101 01010 10011 x		mfspr		rt,957
//	011111 rt-rt 11110 11101 01010 10011 x		mfspr		rt,958
//	011111 rt-rt 11111 11101 01010 10011 x		mfspr		rt,959
	mtcrf	0x80,SavedCR						// restore CR0
	//rlwinm	UnimpRB,MemInstr,11+10,0x000003FF	// get SPR field
	rlwinm	UnimpRB,MemInstr,11+10,22,31	// get SPR field
	cmplwi	cr1,UnimpRB,((287&0x1F)<<5)|(287>>5)// test for PVR Register
	beq		cr1,EmulateMFPVR					// SPR=287, MFPVR

	bf		b_EmulatePrivSPRperf0,EmulatePRIVILEGED
								// 604 and Sirocco
	cmplwi	cr1,UnimpRB,((sprMMCR0&0x1F)<<5)|(sprMMCR0>>5)	// test for MMCR0 Register
	beq		cr1,EmulateMFMMCR0					// SPR=952, MFMMCR0
	cmplwi	cr1,UnimpRB,((sprPMC1&0x1F)<<5)|(sprPMC1>>5)	// test for PMC1 Register
	beq		cr1,EmulateMFPMC1					// SPR=953, MFPMC1
	cmplwi	cr1,UnimpRB,((sprPMC2&0x1F)<<5)|(sprPMC2>>5)	// test for PMC2 Register
	beq		cr1,EmulateMFPMC2					// SPR=954, MFPMC2
	cmplwi	cr1,UnimpRB,((sprSIA&0x1F)<<5)|(sprSIA>>5)		// test for SIA Register
	beq		cr1,EmulateMFSIA					// SPR=955, MFSIA

	bf		b_EmulatePrivSPRperf,EmulatePrivilegedMFSPRNoSDA
														// 604 and Sirocco
	cmplwi	cr1,UnimpRB,((sprSDA&0x1F)<<5)|(sprSDA>>5)		// test for SDA Register
	beq		cr1,EmulateMFSDA					// SPR=959, MFSDA

EmulatePrivilegedMFSPRNoSDA:
	bf		b_EmulatePrivSPRperf2,EmulatePRIVILEGED
														// Sirocco only
	cmplwi	cr1,UnimpRB,((sprMMCR1&0x1F)<<5)|(sprMMCR1>>5)	// test for MMCR1 Register
	beq		cr1,EmulateMFMMCR1					// SPR=956, MFMMCR1
	cmplwi	cr1,UnimpRB,((sprPMC3&0x1F)<<5)|(sprPMC3>>5)	// test for PMC3 Register
	beq		cr1,EmulateMFPMC3					// SPR=957, MFPMC3
	cmplwi	cr1,UnimpRB,((sprPMC4&0x1F)<<5)|(sprPMC4>>5)	// test for PMC4 Register
	beq		cr1,EmulateMFPMC4					// SPR=958, MFPMC4

	b		EmulatePRIVILEGED

EmulateMFPVR:
//	011111 rt-rt 11111 01000 01010 10011 x		mfspr		rt,287
	bf		b_EmulatePrivMFPVR,EmulatePRIVILEGED
	//.long	(31<<26)+(MemDataL<<21)+((sprPVR&0x1F)<<16)+((sprPVR>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprPVR&0x1F)<<16)+((sprPVR>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFMMCR0:
//	011111 rt-rt 11000 11101 01010 10011 x		mfspr		rt,952
	//.long	(31<<26)+(MemDataL<<21)+((sprMMCR0&0x1F)<<16)+((sprMMCR0>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprMMCR0&0x1F)<<16)+((sprMMCR0>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFPMC1:
//	011111 rt-rt 11001 11101 01010 10011 x		mfspr		rt,953
	//.long	(31<<26)+(MemDataL<<21)+((sprPMC1&0x1F)<<16)+((sprPMC1>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprPMC1&0x1F)<<16)+((sprPMC1>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFPMC2:
//	011111 rt-rt 11010 11101 01010 10011 x		mfspr		rt,954
	//.long	(31<<26)+(MemDataL<<21)+((sprPMC2&0x1F)<<16)+((sprPMC2>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprPMC2&0x1F)<<16)+((sprPMC2>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFSIA:
//	011111 rt-rt 11011 11101 01010 10011 x		mfspr		rt,955
	//.long	(31<<26)+(MemDataL<<21)+((sprSIA&0x1F)<<16)+((sprSIA>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprSIA&0x1F)<<16)+((sprSIA>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFMMCR1:
//	011111 rt-rt 11000 11101 01010 10011 x		mfspr		rt,956
	//.long	(31<<26)+(MemDataL<<21)+((sprMMCR1&0x1F)<<16)+((sprMMCR1>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprMMCR1&0x1F)<<16)+((sprMMCR1>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFPMC3:
//	011111 rt-rt 11001 11101 01010 10011 x		mfspr		rt,957
	//.long	(31<<26)+(MemDataL<<21)+((sprPMC3&0x1F)<<16)+((sprPMC3>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprPMC3&0x1F)<<16)+((sprPMC3>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFPMC4:
//	011111 rt-rt 11010 11101 01010 10011 x		mfspr		rt,958
	//.long	(31<<26)+(MemDataL<<21)+((sprPMC4&0x1F)<<16)+((sprPMC4>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprPMC4&0x1F)<<16)+((sprPMC4>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0

EmulateMFSDA:
//	011111 rt-rt 11111 11101 01010 10011 x		mfspr		rt,959
	//.long	(31<<26)+(MemDataL<<21)+((sprSDA&0x1F)<<16)+((sprSDA>>5)<<11)+(339<<1)+(1<<0)
	.long	(31<<26)+(dMemDataL<<21)+((sprSDA&0x1F)<<16)+((sprSDA>>5)<<11)+(339<<1)+(1<<0)
	b		EmulateUpdateRT_cr0


EmulateMFTB:
//	011111 rt-rt 01100 01000 01011 10011 x		mftb		rt
//	011111 rt-rt 01101 01000 01011 10011 x		mftbu		rt
	//rlwinm	DataTemp,MemInstr,6+5+10,0x000003FF	// get the TBR field
	rlwinm	DataTemp,MemInstr,6+5+10,22,31	// get the TBR field
	cmplwi	cr1,DataTemp,((268&0x1F)<<5)|(268>>5)	// test for MFTBl
	cmplwi	cr6,DataTemp,((269&0x1F)<<5)|(269>>5)	// test for MFTBu
//	cror	cr0*4+eq,cr1*4+eq,cr6*4+eq			// see if valid TBR field
	cror	rcr0*4+eq_b,rcr1*4+eq_b,rcr6*4+eq_b			// see if valid TBR field
	bne		cr0,EmulateILLEGAL					// invalid TBR field

RetryMFTB:
	mfspr	MemDataH,fromRTCU					// assuming 601, read Power RTC timer
	mfspr	MemDataL,fromRTCL
	mfspr	DataTemp,fromRTCU
	xor.	DataTemp,DataTemp,MemDataH
	bne-	RetryMFTB							// retry if high half changed
	lis		DataTemp,1000000000>>16
	ori		DataTemp,DataTemp,1000000000&0xFFFF
	mfspr	UnimpMQtmp,sprMQ					// preserve MQ from 601 multiply ops

//	011111 rt-rt 01100 01000 01011 10011 x		mftb		rt
	mullw	UnimpRB,MemDataH,DataTemp			// convert Power timer to PowerPC timer
	mtspr	sprMQ,UnimpMQtmp					// restore MQ after 601 multiply ops
	add		MemDataL,MemDataL,UnimpRB			// compute TBL
	beq		cr1,EmulateUpdateRT					// update RT register, when TBL

//	011111 rt-rt 01101 01000 01011 10011 x		mftbu		rt
	cmplw	MemDataL,UnimpRB					// test for carry out
	mulhwu	MemDataL,MemDataH,DataTemp			// compute TBU
	mtspr	sprMQ,UnimpMQtmp					// restore MQ after 601 multiply ops
	bge		EmulateUpdateRT						// update RT register, when TBU, no carry out
	addi	MemDataL,MemDataL,1					// add in carry out
	b		EmulateUpdateRT						// update RT register, when TBU, carry out


EmulateMTSPR:
	lwzx	UnimpRS,KernelDataPtr,UnimpRS		// get RS
	bge		EmulatePrivilegedMTSPR				// privileged SPR
//	011111 rs-rs 00000 xxxxx 01110 10011 x		mtmq		rs
//	011111 rs-rs 00001 xxxxx 01110 10011 x		mtxer		rs
//	011111 rs-rs 00010 xxxxx 01110 10011 x		mtspr		0x2,rs
//	011111 rs-rs 00011 xxxxx 01110 10011 x		mtspr		0x3,rs
//	011111 rs-rs 00100 xxxxx 01110 10011 x		mtspr		0x4,rs
//	011111 rs-rs 00101 xxxxx 01110 10011 x		mtspr		0x5,rs
//	011111 rs-rs 00110 xxxxx 01110 10011 x		mtspr		0x6,rs
//	011111 rs-rs 00111 xxxxx 01110 10011 x		mtspr		0x7,rs
//	011111 rs-rs 01000 xxxxx 01110 10011 x		mtlr		rs
//	011111 rs-rs 01001 xxxxx 01110 10011 x		mtctr		rs
//	011111 rs-rs 01010 xxxxx 01110 10011 x		mtspr		0xA,rs
//	011111 rs-rs 01011 xxxxx 01110 10011 x		mtspr		0xB,rs
//	011111 rs-rs 01100 xxxxx 01110 10011 x		mtspr		0xC,rs
//	011111 rs-rs 01101 xxxxx 01110 10011 x		mtspr		0xD,rs
//	011111 rs-rs 01110 xxxxx 01110 10011 x		mtspr		0xE,rs
//	011111 rs-rs 01111 xxxxx 01110 10011 x		mtspr		0xF,rs
	mr.		UnimpRS,UnimpRS						// cr0 based upon RS
	beq		cr1,EmulateMTMQ						// SPR=0, MTMQ
	bf		b_EmulateInvalidSPR,EmulateDisabledInvalidSPR
	beq		cr6,EmulateMTXER					// SPR=1, MTXER
	cmpwi	cr1,UnimpRA,8*4
	cmpwi	cr6,UnimpRA,9*4
	beq		cr1,EmulateMTLR						// SPR=8, MTLR
	beq		cr6,EmulateMTCTR					// SPR=9, MTCTR
	b		EmulateUpdateCR0

EmulateMTMQ:
//	011111 rs-rs 00000 xxxxx 01110 10011 x		mtmq		rs
	bf		b_EmulatePowerMQ,EmulateDisabledPowerMQ
	stw		UnimpRS,0(UnimpMQptr)				// update MQ in context block
	b		EmulateUpdateCR0

EmulateMTXER:
//	011111 rs-rs 00001 xxxxx 01110 10011 x		mtxer		rs
	mtcrf	0x80,SavedCR						// restore CR0
	//.long	(31<<26)+(UnimpRS<<21)+(0x01<<16)+(467<<1)+(1<<0)	//	mtxer.	UnimpRS
	.long	(31<<26)+(dUnimpRS<<21)+(0x01<<16)+(467<<1)+(1<<0)	//	mtxer.	UnimpRS
	b		EmulateUpdateCR0

EmulateMTLR:
//	011111 rs-rs 01000 xxxxx 01110 10011 x		mtlr		rs
	mtcrf	0x80,SavedCR						// restore CR0
	mr		SavedLR,UnimpRS						// update LR
	//.long	(31<<26)+(UnimpRS<<21)+(0x08<<16)+(467<<1)+(1<<0)	//	mtlr.	UnimpRS
	.long	(31<<26)+(dUnimpRS<<21)+(0x08<<16)+(467<<1)+(1<<0)	//	mtlr.	UnimpRS
	b		EmulateUpdateCR0

EmulateMTCTR:
//	011111 rs-rs 01001 xxxxx 01110 10011 x		mtctr		rs
	mtcrf	0x80,SavedCR						// restore CR0
	//.long	(31<<26)+(UnimpRS<<21)+(0x09<<16)+(467<<1)+(1<<0)	//	mtctr.	UnimpRS
	.long	(31<<26)+(dUnimpRS<<21)+(0x09<<16)+(467<<1)+(1<<0)	//	mtctr.	UnimpRS
	b		EmulateUpdateCR0

EmulatePrivilegedMTSPR:
//	011111 rs-rs 11000 11101 01110 10011 x		mtspr		952,rs
//	011111 rs-rs 11001 11101 01110 10011 x		mtspr		953,rs
//	011111 rs-rs 11010 11101 01110 10011 x		mtspr		954,rs
//	011111 rs-rs 11011 11101 01110 10011 x		mtspr		955,rs
//	011111 rs-rs 11100 11101 01110 10011 x		mtspr		956,rs
//	011111 rs-rs 11101 11101 01110 10011 x		mtspr		957,rs
//	011111 rs-rs 11110 11101 01110 10011 x		mtspr		958,rs
//	011111 rs-rs 11111 11101 01110 10011 x		mtspr		959,rs
	bf		b_EmulatePrivSPRperf0,EmulatePRIVILEGED
	mtcrf	0x80,SavedCR						// restore CR0
	//rlwinm	UnimpRB,MemInstr,11+10,0x000003FF	// get SPR field
	rlwinm	UnimpRB,MemInstr,11+10,22,31	// get SPR field
												// 604 and Sirocco
	cmplwi	cr1,UnimpRB,((sprMMCR0&0x1F)<<5)|(sprMMCR0>>5)	// test for MMCR0 Register
	beq		cr1,EmulateMTMMCR0					// SPR=952, MTMMCR0
	cmplwi	cr1,UnimpRB,((sprPMC1&0x1F)<<5)|(sprPMC1>>5)	// test for PMC1 Register
	beq		cr1,EmulateMTPMC1					// SPR=953, MTPMC1
	cmplwi	cr1,UnimpRB,((sprPMC2&0x1F)<<5)|(sprPMC2>>5)	// test for PMC2 Register
	beq		cr1,EmulateMTPMC2					// SPR=954, MTPMC2
	cmplwi	cr1,UnimpRB,((sprSIA&0x1F)<<5)|(sprSIA>>5)		// test for SIA Register
	beq		cr1,EmulateMTSIA					// SPR=955, MTSIA

	bf		b_EmulatePrivSPRperf,EmulatePrivilegedMTSPRNoSDA
												// Sirocco only
	cmplwi	cr1,UnimpRB,((sprSDA&0x1F)<<5)|(sprSDA>>5)		// test for SDA Register
	beq		cr1,EmulateMTSDA					// SPR=959, MTSDA

EmulatePrivilegedMTSPRNoSDA:
	bf		b_EmulatePrivSPRperf2,EmulatePRIVILEGED
														// Sirocco only
	cmplwi	cr1,UnimpRB,((sprMMCR1&0x1F)<<5)|(sprMMCR1>>5)	// test for MMCR1 Register
	beq		cr1,EmulateMTMMCR1					// SPR=956, MTMMCR1
	cmplwi	cr1,UnimpRB,((sprPMC3&0x1F)<<5)|(sprPMC3>>5)	// test for PMC3 Register
	beq		cr1,EmulateMTPMC3					// SPR=957, MTPMC3
	cmplwi	cr1,UnimpRB,((sprPMC4&0x1F)<<5)|(sprPMC4>>5)	// test for PMC4 Register
	beq		cr1,EmulateMTPMC4					// SPR=958, MTPMC4

	b		EmulatePRIVILEGED

EmulateMTMMCR0:
//	011111 rs-rs 11000 11101 01110 10011 x		mtspr		952,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprMMCR0&0x1F)<<16)+((sprMMCR0>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprMMCR0&0x1F)<<16)+((sprMMCR0>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0

EmulateMTPMC1:
//	011111 rs-rs 11001 11101 01110 10011 x		mtspr		953,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprPMC1&0x1F)<<16)+((sprPMC1>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprPMC1&0x1F)<<16)+((sprPMC1>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0

EmulateMTPMC2:
//	011111 rs-rs 11010 11101 01110 10011 x		mtspr		954,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprPMC2&0x1F)<<16)+((sprPMC2>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprPMC2&0x1F)<<16)+((sprPMC2>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0

EmulateMTSIA:
//	011111 rs-rs 11011 11101 01110 10011 x		mtspr		955,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprSIA&0x1F)<<16)+((sprSIA>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprSIA&0x1F)<<16)+((sprSIA>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0

EmulateMTMMCR1:
//	011111 rs-rs 11000 11101 01110 10011 x		mtspr		956,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprMMCR1&0x1F)<<16)+((sprMMCR1>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprMMCR1&0x1F)<<16)+((sprMMCR1>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0

EmulateMTPMC3:
//	011111 rs-rs 11001 11101 01110 10011 x		mtspr		957,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprPMC3&0x1F)<<16)+((sprPMC3>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprPMC3&0x1F)<<16)+((sprPMC3>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0

EmulateMTPMC4:
//	011111 rs-rs 11010 11101 01110 10011 x		mtspr		958,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprPMC4&0x1F)<<16)+((sprPMC4>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprPMC4&0x1F)<<16)+((sprPMC4>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0

EmulateMTSDA:
//	011111 rs-rs 11111 11101 01110 10011 x		mtspr		959,rs
	//.long	(31<<26)+(UnimpRS<<21)+((sprSDA&0x1F)<<16)+((sprSDA>>5)<<11)+(467<<1)+(1<<0)
	.long	(31<<26)+(dUnimpRS<<21)+((sprSDA&0x1F)<<16)+((sprSDA>>5)<<11)+(467<<1)+(1<<0)
	b		EmulateUpdateCR0


EmulateCLCS:
//	011111 rt-rt ra-ra xxxxx 10000 10011 x		clcs		rt,ra
	bf		b_EmulatePowerCLCS,EmulateDisabledPowerCLCS
	//rlwinm.	UnimpRA,MemInstr,11+5,0x0000000F	// cr0 based upon low 4 bits of RA field
	rlwinm.	UnimpRA,MemInstr,11+5,28,31	// cr0 based upon low 4 bits of RA field
	//rlwinm	MemDataL,MemInstr,11+5,0x0000000E	// setup to test for inst/data cache size
	rlwinm	MemDataL,MemInstr,11+5,28,30	// setup to test for inst/data cache size
	cmpwi	cr1,MemDataL,0xA					// inst/data cache sizes are special cases
//	la		UnimpRA,EmulateCLCS_info-MemProcBase(UnimpRA)
	lbzx	UnimpRA,MemProcPtr,UnimpRA			// get the offset within ProcessorInfo
	//la		MemDataL,PI+ProcessorInfoStart(KernelDataPtr)
	beq		cr1,EmulateCLCS_word				// inst/data cache size is 4 bytes
	lhzx	MemDataL,MemDataL,UnimpRA			// all others are 2 bytes
	b		EmulateUpdateRT_cr0
EmulateCLCS_word:
	lwzx	MemDataL,MemDataL,UnimpRA			// get the 4 byte inst/data cache size
	b		EmulateUpdateRT_cr0

EmulateCLCS_info:
#if	0
	.byte	DataCacheLineSize-ProcessorInfoStart		// x0000 :    64 - undefined
	.byte	DataCacheLineSize-ProcessorInfoStart		// x0001 :    64 - undefined
	.byte	DataCacheLineSize-ProcessorInfoStart		// x0010 :    64 - undefined
	.byte	DataCacheLineSize-ProcessorInfoStart		// x0011 :    64 - undefined

	.byte	DataCacheBlockSizeTouch-ProcessorInfoStart	// x0100 :    32 - touch cache sector size
	.byte	InstCacheBlockSize-ProcessorInfoStart		// x0101 :    32 - inst cache sector size
	.byte	DataCacheBlockSize-ProcessorInfoStart		// x0110 :    32 - data cache sector size
	.byte	CombinedCaches-ProcessorInfoStart			// x0111 :     1 - combined cache

	.byte	InstCacheAssociativity-ProcessorInfoStart	// x1000 :     8 - inst cache set associativity
	.byte	DataCacheAssociativity-ProcessorInfoStart	// x1001 :     8 - data cache set associativity
	.byte	InstCacheTotalSize-ProcessorInfoStart		// x1010 : 32768 - inst cache size
	.byte	DataCacheTotalSize-ProcessorInfoStart		// x1011 : 32768 - data cache size

	.byte	InstCacheLineSize-ProcessorInfoStart		// x1100 :    64 - inst cache line size
	.byte	DataCacheLineSize-ProcessorInfoStart		// x1101 :    64 - data cache line size
	.byte	DataCacheLineSize-ProcessorInfoStart		// x1110 :    64 - min line size
	.byte	DataCacheLineSize-ProcessorInfoStart		// x1111 :    64 - max line size
#endif



EmulateRRIB_SHIFTE:
//	           1 11111 11112 22222 22223 3
//	012345 67890 12345 67890 12345 67890 1
//	------ ----- ----- ----- ----- ----- -
//	011111 rs-rs ra-ra rb-rb 00100 11001 .		sle[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 00110 11001 .		sleq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10000 11001 .		rrib[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10100 11001 .		sre[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11001 .		sreq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 11100 11001 .		srea[.]		ra,rs,rb
	lwzx	UnimpRB,KernelDataPtr,UnimpRB		// get RB
	//rlwinm	UnimpRB,UnimpRB,0,0x1F				// just use low 5 bits of RB
	rlwinm	UnimpRB,UnimpRB,0,27,31		// just use low 5 bits of RB
	bt		23,EmulateSHIFT
EmulateRRIB:
//	011111 rs-rs ra-ra rb-rb 10000 11001 .		rrib[.]		ra,rs,rb
	bf		b_EmulatePowerComplex,EmulateDisabledPowerComplex
	lwzx	UnimpRS,KernelDataPtr,UnimpRS		// get RS
	lis		DataTemp,0xFFFF8000					// mask for bit 0
	lwzx	MemDataL,KernelDataPtr,UnimpRA		// get RA
	srw		DataTemp,DataTemp,UnimpRB			// mask for bit specified by RB
	srw		UnimpRS,UnimpRS,UnimpRB				// position data to be inserted
	b		EmulateUpdateRA_cr0_insert			// insert the bit


EmulateSHIFTQ:
//	           1 11111 11112 22222 22223 3
//	012345 67890 12345 67890 12345 67890 1
//	------ ----- ----- ----- ----- ----- -
//	011111 rs-rs ra-ra rb-rb 00100 11000 .		slq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 00101 11000 .		sliq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 00111 11000 .		slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10100 11000 .		srq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 10101 11000 .		sriq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10110 11000 .		srlq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 10111 11000 .		srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 11100 11000 .		sraq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 11101 11000 .		sraiq[.]	ra,rs,sh
			bt		25,EmulateSHIFT_imm
//	011111 rs-rs ra-ra rb-rb 00100 11000 .		slq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10100 11000 .		srq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11000 .		srlq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 11100 11000 .		sraq[.]		ra,rs,rb
	lwzx	UnimpRB,KernelDataPtr,UnimpRB		// get RB
	//rlwinm	UnimpRB,UnimpRB,0,0x3F				// just use low 6 bits of RB
	rlwinm	UnimpRB,UnimpRB,0,26,31		// just use low 6 bits of RB
	bf		24,EmulateSHIFT
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11000 .		srlq[.]		ra,rs,rb
	cmpwi	cr0,UnimpRB,31
	crnot	23,23								// clear bit 23, don't update MQ
	ble		EmulateSHIFT
	bf		b_EmulatePowerMQ,EmulateDisabledPowerMQ
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb	(rb>31)
//	011111 rs-rs ra-ra rb-rb 10110 11000 .		srlq[.]		ra,rs,rb	(rb>31)
	lwz		MemDataH,0(UnimpMQptr)				// get MQ from context block
	li		DataTemp,-1
	//rlwinm	UnimpRB,UnimpRB,0,0x1F				// just use low 5 bits of RB
	rlwinm	UnimpRB,UnimpRB,0,27,31		// just use low 5 bits of RB
	bt		21,EmulateSHIFT_srlq
EmulateSHIFT_sllq:
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb	(rb>31)
	slw		DataTemp,DataTemp,UnimpRB
	and.	MemDataL,MemDataH,DataTemp
	b		EmulateUpdateRA_cr0

EmulateSHIFT_srlq:
//	011111 rs-rs ra-ra rb-rb 10110 11000 .		srlq[.]		ra,rs,rb	(rb>31)
	srw	DataTemp,DataTemp,UnimpRB
	and.	MemDataL,MemDataH,DataTemp
	b	EmulateUpdateRA_cr0

EmulateSHIFT_imm:
//	011111 rs-rs ra-ra sh-sh 00101 11000 .		sliq[.]		ra,rs,sh
//	011111 rs-rs ra-ra sh-sh 00111 11000 .		slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra sh-sh 10101 11000 .		sriq[.]		ra,rs,sh
//	011111 rs-rs ra-ra sh-sh 10111 11000 .		srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra sh-sh 11101 11000 .		sraiq[.]	ra,rs,sh
	//rlwinm	UnimpRB,MemInstr,11+10,0x0000001F	// get SH
	rlwinm	UnimpRB,MemInstr,11+10,27,31	// get SH
EmulateSHIFT:
//	011111 rs-rs ra-ra rb-rb 00100 11000 .		slq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 00101 11000 .		sliq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb	(rb<32)
//	011111 rs-rs ra-ra sh-sh 00111 11000 .		slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10100 11000 .		srq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 10101 11000 .		sriq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10110 11000 .		srlq[.]		ra,rs,rb	(rb<32)
//	011111 rs-rs ra-ra sh-sh 10111 11000 .		srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 11100 11000 .		sraq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 11101 11000 .		sraiq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00100 11001 .		sle[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 00110 11001 .		sleq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10100 11001 .		sre[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11001 .		sreq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 11100 11001 .		srea[.]		ra,rs,rb
	bf	b_EmulatePowerMQ,EmulateDisabledPowerMQ
	lwzx	UnimpRS,KernelDataPtr,UnimpRS		// get RS
	bt	21,EmulateSHIFT_right

EmulateSHIFT_left_logical:
//	011111 rs-rs ra-ra rb-rb 00100 11000 .		slq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 00101 11000 .		sliq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb	(rb<32)
//	011111 rs-rs ra-ra sh-sh 00111 11000 .		slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00100 11001 .		sle[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 00110 11001 .		sleq[.]		ra,rs,rb
	slw.	MemDataL,UnimpRS,UnimpRB			// RA <- RS shifted left by RB bits
	rotlw	MemDataH,UnimpRS,UnimpRB			// MQ <- RS rotated left by RB bits
	bf		24,EmulateUpdateRA_cr0_mq
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb	(rb<32)
//	011111 rs-rs ra-ra sh-sh 00111 11000 .		slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00110 11001 .		sleq[.]		ra,rs,rb
	li		DataTemp,-1
	slw		DataTemp,DataTemp,UnimpRB
EmulateUpdateRA_cr0_mq_merge:
//	011111 rs-rs ra-ra rb-rb 00110 11000 .		sllq[.]		ra,rs,rb	(rb<32)
//	011111 rs-rs ra-ra sh-sh 00111 11000 .		slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10110 11000 .		srlq[.]		ra,rs,rb	(rb<32)
//	011111 rs-rs ra-ra sh-sh 10111 11000 .		srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00110 11001 .		sleq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11001 .		sreq[.]		ra,rs,rb
	lwz		UnimpRB,0(UnimpMQptr)				// get MQ from context block
	andc	DataTemp,UnimpRB,DataTemp
	or.		MemDataL,MemDataL,DataTemp
	bf		23,EmulateUpdateRA_cr0
EmulateUpdateRA_cr0_mq:
//	011111 rs-rs ra-ra sh-sh 00111 11000 .		slliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra sh-sh 10111 11000 .		srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 00110 11001 .		sleq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11001 .		sreq[.]		ra,rs,rb
	stw		MemDataH,0(UnimpMQptr)				// update MQ in context block
EmulateUpdateRA_cr0:
	stwx	MemDataL,KernelDataPtr,UnimpRA		// update RA
EmulateUpdateCR0:
	bf	31,CompleteMemOpDone
	mfcr	DataTemp							// get CR0
	//rlwimi	SavedCR,DataTemp,0,0xF0000000		// update CR0
	rlwimi	SavedCR,DataTemp,0,0,3		// update CR0
	b	CompleteMemOpDone

EmulateSHIFT_right:
//	011111 rs-rs ra-ra rb-rb 10100 11000 .	srq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 10101 11000 .	sriq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10110 11000 .	srlq[.]		ra,rs,rb(rb<32)
//	011111 rs-rs ra-ra sh-sh 10111 11000 .	srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 11100 11000 .	sraq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 11101 11000 .	sraiq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10100 11001 .	sre[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11001 .	sreq[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 11100 11001 .	srea[.]		ra,rs,rb
	neg		MemDataH,UnimpRB// convert right rotate count to left
	rotlw	MemDataH,UnimpRS,MemDataH// MQ <- RS rotated right by RB bits
	bt		22,EmulateSHIFT_right_arith
EmulateSHIFT_right_logical:
//	011111 rs-rs ra-ra rb-rb 10100 11000 .	srq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 10101 11000 .	sriq[.]		ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10110 11000 .	srlq[.]		ra,rs,rb	(rb<32)
//	011111 rs-rs ra-ra sh-sh 10111 11000 .	srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10100 11001 .	sre[.]		ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10110 11001 .	sreq[.]		ra,rs,rb
	srw.	MemDataL,UnimpRS,UnimpRB// RA <- RS shifted righ by RB bits
	bf		24,EmulateUpdateRA_cr0_mq
//	011111 rs-rs ra-ra rb-rb 10110 11000 .	srlq[.]		ra,rs,rb(rb<32)
//	011111 rs-rs ra-ra sh-sh 10111 11000 .	srliq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 10110 11001 .	sreq[.]		ra,rs,rb
	li		DataTemp,-1
	srw		DataTemp,DataTemp,UnimpRB
	b		EmulateUpdateRA_cr0_mq_merge

EmulateSHIFT_right_arith:
//	011111 rs-rs ra-ra rb-rb 11100 11000 .	sraq[.]		ra,rs,rb
//	011111 rs-rs ra-ra sh-sh 11101 11000 .	sraiq[.]	ra,rs,sh
//	011111 rs-rs ra-ra rb-rb 11100 11001 .	srea[.]		ra,rs,rb
	sraw.	MemDataL,UnimpRS,UnimpRB	// updates live copy of XER
	b		EmulateUpdateRA_cr0_mq


EmulateMASKG_MASKIR:
//	           1 11111 11112 22222 22223 3
//	012345 67890 12345 67890 12345 67890 1
//	------ ----- ----- ----- ----- ----- -
//	011111 rs-rs ra-ra rb-rb 00000 11101 .	maskg[.]	ra,rs,rb
//	011111 rs-rs ra-ra rb-rb 10000 11101 .	maskir[.]	ra,rs,rb
	bf		b_EmulatePowerComplex,EmulateDisabledPowerComplex
	lwzx	UnimpRB,KernelDataPtr,UnimpRB	// get RB
	lwzx	UnimpRS,KernelDataPtr,UnimpRS	// get RS
	bt		21,EmulateMASKIR
EmulateMASKG:
//	011111 rs-rs ra-ra rb-rb 00000 11101 .	maskg[.]	ra,rs,rb
	li		MemDataL,-1		// initial mask
	sub		UnimpRB,UnimpRB,UnimpRS	// end - start
	not		UnimpRB,UnimpRB		// 31 - (end - start) (number of zeros)
	//rlwinm	UnimpRB,UnimpRB,0,0x1F	// mask to 5 bits
	rlwinm	UnimpRB,UnimpRB,0,27,31		// mask to 5 bits
	neg		UnimpRS,UnimpRS		// setup to right rotate field mask by RS
	slw		MemDataL,MemDataL,UnimpRB// left aligned field mask
	rotlw.	MemDataL,MemDataL,UnimpRS	// position the field mask
	b		EmulateUpdateRA_cr0

EmulateMASKIR:
//	011111 rs-rs ra-ra rb-rb 10000 11101 .	maskir[.]	ra,rs,rb
	lwzx	MemDataL,KernelDataPtr,UnimpRA	// get RA
	and		UnimpRS,UnimpRS,UnimpRB	// get new bits to insert
	andc	MemDataL,MemDataL,UnimpRB	// clear old bits to be overwritten
	or.		MemDataL,MemDataL,UnimpRS// insert new bits
	b		EmulateUpdateRA_cr0

EmulateRLMI:
//	010110 rs-rs ra-ra rb-rb mb-mb me-me .	rlmi[.]		ra,rs,rb,mb,me
	bf		b_EmulatePowerComplex,EmulateDisabledPowerComplex
	lwzx	UnimpRS,KernelDataPtr,UnimpRS	// get RS
	//rlwinm	MemDataH,MemInstr,21+5,0x1F	// get MB
	rlwinm	MemDataH,MemInstr,21+5,27,31	// get MB
	lwzx	UnimpRB,KernelDataPtr,UnimpRB	// get RB
	//rlwinm	MemDataL,MemInstr,21+10,0x1F	// get ME
	rlwinm	MemDataL,MemInstr,21+10,27,31	// get ME
	li	DataTemp,-1			// initial mask
	sub	MemDataL,MemDataL,MemDataH	// ME - MB
	not	MemDataL,MemDataL		// 31 - (ME - MB)
	//rlwinm	MemDataL,MemDataL,0,0x1F// mask to 5 bits
	rlwinm	MemDataL,MemDataL,0,27,31	// mask to 5 bits
	neg	MemDataH,MemDataH	// setup to right rotate field mask by MB
	slw	DataTemp,DataTemp,MemDataL	// left aligned field mask
	lwzx	MemDataL,KernelDataPtr,UnimpRA	// get RA
	rotlw	DataTemp,DataTemp,MemDataH	// position the field mask
	rotlw	UnimpRS,UnimpRS,UnimpRB	// position the RS data to be inserted
EmulateUpdateRA_cr0_insert:
	and	UnimpRS,UnimpRS,DataTemp	// get new bits to insert
	andc	MemDataL,MemDataL,DataTemp	// clear old bits to be overwritten
	or.	MemDataL,MemDataL,UnimpRS	// insert new bits
	b	EmulateUpdateRA_cr0


EmulatePowerMemoryInvalidForm:
//	011111 rt-rt ra-ra rb-rb 01000 10101 .	lscbx[.]	rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 10000 10101 x	lswx		rt,ra,rb
//	011111 rt-rt ra-ra nb-nb 10010 10101 x	lswi		rt,ra,nb
//	011111 rt-rt ra-ra rb-rb 00001 10111 x	lwzux		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 00011 10111 x	lbzux		rt,ra,rb
//	011111 rs-rs ra-ra rb-rb 00101 10111 x	stwux		rs,ra,rb
//	011111 rs-rs ra-ra rb-rb 00111 10111 x	stbux		rs,ra,rb
//	011111 rt-rt ra-ra rb-rb 01001 10111 x	lhzux		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01011 10111 x	lhaux		rt,ra,rb
//	011111 rs-rs ra-ra rb-rb 01101 10111 x	sthux		rs,ra,rb
//	011111 ft-ft ra-ra rb-rb 10001 10111 x	lfsux		ft,ra,rb
//	011111 ft-ft ra-ra rb-rb 10011 10111 x	lfdux		ft,ra,rb
//	011111 fs-fs ra-ra rb-rb 10101 10111 x	stfsux		fs,ra,rb
//	011111 fs-fs ra-ra rb-rb 10111 10111 x	stfdux		fs,ra,rb
//	100001 rt-rt ra-ra d---d d---d d---d d	lwzu		rt,d(ra)
//	100011 rt-rt ra-ra d---d d---d d---d d	lbzu		rt,d(ra)
//	100101 rs-rs ra-ra d---d d---d d---d d	stwu		rs,d(ra)
//	100111 rs-rs ra-ra d---d d---d d---d d	stbu		rs,d(ra)
//	101001 rt-rt ra-ra d---d d---d d---d d	lhzu		rt,d(ra)
//	101011 rt-rt ra-ra d---d d---d d---d d	lhau		rt,d(ra)
//	101101 rs-rs ra-ra d---d d---d d---d d	sthu		rs,d(ra)
//	101110 rt-rt ra-ra d---d d---d d---d d	lmw		rt,d(ra)
//	110001 ft-ft ra-ra d---d d---d d---d d	lfsu		ft,d(ra)
//	110011 ft-ft ra-ra d---d d---d d---d d	lfdu		ft,d(ra)
//	110101 fs-fs ra-ra d---d d---d d---d d	stfsu		fs,d(ra)
//	110111 fs-fs ra-ra d---d d---d d---d d	stfdu		fs,d(ra)
	bf	b_EmulatePowerMemory,EmulateDisabledPowerMemory
	b	DSI_Decode		// emulate the memory reference


EmulateUpdateMemOps_STFIWX:
//	011111 rt-rt ra-ra rb-rb 00001 10111 x	lwzux		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 00011 10111 x	lbzux		rt,ra,rb
//	011111 rs-rs ra-ra rb-rb 00101 10111 x	stwux		rs,ra,rb
//	011111 rs-rs ra-ra rb-rb 00111 10111 x	stbux		rs,ra,rb
//	011111 rt-rt ra-ra rb-rb 01001 10111 x	lhzux		rt,ra,rb
//	011111 rt-rt ra-ra rb-rb 01011 10111 x	lhaux		rt,ra,rb
//	011111 rs-rs ra-ra rb-rb 01101 10111 x	sthux		rs,ra,rb
//	011111 ft-ft ra-ra rb-rb 10001 10111 x	lfsux		ft,ra,rb
//	011111 ft-ft ra-ra rb-rb 10011 10111 x	lfdux		ft,ra,rb
//	011111 fs-fs ra-ra rb-rb 10101 10111 x	stfsux		fs,ra,rb
//	011111 fs-fs ra-ra rb-rb 10111 10111 x	stfdux		fs,ra,rb
//	011111 fs-fs ra-ra rb-rb 11110 10111 x	stfiwx		fs,ra,rb
	bt	25,EmulatePowerMemoryInvalidForm	// update forms may be invalid forms
EmulateSTFIWX:
//	011111 fs-fs ra-ra rb-rb 11110 10111 x	stfiwx		fs,ra,rb
	bf	b_EmulateOptional,EmulateDisabledOptional
	b	DSI_Decode		// emulate the memory reference



	.align	8
MemProcBase:

// The Setup routines expect the following register contents:
//
// KernelDataPtr	Points to base of KernelData page, XCP_GPR_0_31 must be first
// SavedSRR0	The address of the instruction that caused the exception
// SavedSRR1	The MSR at the time of the exception (low 16 bits)
// SavedLR	The Link Register at the time of the exception
// SavedCR	The Condition Register at the time of the exception
// SavedVBR	The contents of VectorBaseSPRG at the time of the exception
// MemCmdInfo	6/MemOp,5/RT,5/RA,10/0,5/size,1/RW
// MemUpdateEA	The effective address generated by the memory reference
// MemAddress	Don't care
// MemDataH	Don't care
// MemDataL	High 3 bytes must be zero, low byte is don't care
// MemProcPtr	High 22 bits point to MemProcBase, low 10 bits don't care
// MemSetupInfo	Low byte, completion routine index
// MemCtxFlags	Copy of ActiveCtxFlags
// MSR_Enabled	Data to load into MSR to  enable DR
// MSR_Disabled	Data to load into MSR to disable DR and possibly enable FP
// jMemInstr	The entire instruction that caused the exception (If FlagReadInstr)
// MemByteCount	Don't care
// RegIndex	Don't care
// DataTemp	Don't care
// PTEaddr	unused
// PTEu		unused
// PTEl		unused
// LR		Points to setup routine
// CR		CR3 contains the 4 flag bits
// gpr 0..31	are saved in XCP_GPR_0_31
// gpr 0,2..9	are same as saved copy

// The Completion routines expect the following register contents:
//
// KernelDataPtr Points to base of KernelData page, XCP_GPR_0_31 must be first
// SavedSRR0	The address of the instruction that caused the exception
// SavedSRR1	The MSR at the time of the exception (low 16 bits)
// SavedLR	The Link Register at the time of the exception
// SavedCR	The Condition Register at the time of the exception
// SavedVBR	The contents of VectorBaseSPRG at the time of the exception
// MemCmdInfo	6/MemOp,5/RT,5/RA,10/MemOpSpecific,5/size,1/RW
// MemUpdateEA	The effective address for updating, or other completion purposes
// MemAddress	Address of byte just past end of data
// MemDataH	High 4 bytes of data that was read/written
// MemDataL	Low  4 bytes of data that was read/written
// MemProcPtr	High 22 bits point to MemProcBase, low 10 bits don't care
// MemSetupInfo	Low byte, completion routine index
// MemCtxFlags	Copy of ActiveCtxFlags
// MSR_Enabled	Data to load into MSR to  enable DR
// MSR_Disabled	Data to load into MSR to disable DR and possibly enable FP
// MemInstr	Don't care
// MemByteCount	Don't care
// RegIndex	Don't care
// DataTemp	Don't care
// PTEaddr	unused
// PTEu		unused
// PTEl		unused
// LR		Points to completion routine
// CR		CR3 contains the low 2 flag bits
// gpr 0..31	are saved in XCP_GPR_0_31
//gpr 0,2..9 are same as saved copy, iff b_FlagRegModified=0

SetupLDAR:
Setup0000010:
Setup0000011:
Setup0000100:
Setup0000101:
Setup0000110:
Setup0000111:
Setup0001000:
Setup0001001:
Setup0001010:
Setup0001011:
Setup0001111:
Setup0010000:
Setup0010001:
Setup0010010:
Setup0010011:
Setup0010100:
Setup0010101:
Setup0010110:
Setup0010111:
Setup0011000:
Setup0011001:
Setup0011010:
Setup0011011:
SetupLMD:
SetupSTMD:

Setup0100001:
Setup0100011:
Setup0100110:
Setup0100111:
Setup0101100:
Setup0101101:
Setup0101110:
Setup0101111:
Setup0110001:
Setup0110011:
Setup0110100:
Setup0110110:
Setup0110111:
Setup0111000:
Setup0111001:
Setup0111010:
Setup0111011:
Setup0111100:
Setup0111101:
Setup0111110:
Setup0111111:

Setup1000000:
SetupSTDCX:
Setup1000101:
Setup1000110:
SetupDCBI:
SetupSYNC:
Setup1001011:
SetupEIEIO:
SetupCLF:
Setup1010010:
Setup1010101:
SetupCLI:
Setup1011000:
SetupDCLST:
Setup1011010:
Setup1011011:
Setup1011100:
Setup1011101:
Setup1011110:

SetupLFQX:
Setup1101101:
SetupSTFQX:
SetupLFQUX:
Setup1111101:
SetupSTFQUX:
Setup1111111:
			bl		KernelCrash					// invalid memory exception opcode
#if	0
Complete0001_00:
Complete0001_11:
Complete0010_00:
Complete0010_11:
Complete0011_00:
Complete0011_01:
CompleteLDAR:
CompleteSTDC_:
Complete0110_00:
Complete0110_01:
CompleteLMD:
Complete0111_00:
Complete0111_01:
CompleteSTMD:
Complete1100_01:
Complete1100_10:
Complete1100_11:
Complete1101_00:
Complete1101_01:
Complete1101_10:
Complete1101_11:
Complete1110_00:
Complete1110_01:
Complete1110_10:
Complete1110_11:
Complete1111_00:
Complete1111_01:
CompleteIllegal:
			b		CompleteForceEx


SetupSTFSX:	rlwinm	MemCmdInfo,MemCmdInfo,0,0xFFE0FFFF	// RA <- 0, no update
SetupSTFSUX:crclr	31									// indicate single precision
			b		SetupFloatStore

SetupSTFDX:
SetupSTFIWX:rlwinm	MemCmdInfo,MemCmdInfo,0,0xFFE0FFFF	// RA <- 0, no update
SetupSTFDUX:crset	31									// indicate double precision
SetupFloatStore:
			rlwinm	MemAddress,MemProcPtr,0,0xFFFFFC00	// clear low bits
			rlwimi	MemAddress,MemCmdInfo,6+5+3,0xF8	// get index of FRS register
			la		MemAddress,StoreFPreg-MemProcBase(MemAddress)
			mtlr	MemAddress							// setup to get FRS
			rlwimi	MSR_Disabled,SavedSRR1,0,msr_fp		// setup to enable FP regs
			mtmsr	MSR_Disabled						// enable FP regs, if they were valid
			isync
			blr											// FP reg -> DataConvertBuffer
SetupFloatStoreDone:
			ori		SavedSRR1,SavedSRR1,msr_fp			// FP regs are now valid
			lwz		MemDataH,DataConvertBuffer+0(KernelDataPtr)	// get upper half of data
			lwz		MemDataL,DataConvertBuffer+4(KernelDataPtr)	// get lower half of data
			bt		31,SetupDone						// all done if double prec

			rlwinm	DataTemp,MemDataH,12,0x07FF			// get DP exponent, test for zero
			cmpwi	DataTemp,896						// check for denorm
			rlwimi	MemDataH,MemDataH,3,0x3FFFFFF8		// convert dp to sp
			rlwimi	MemDataH,MemDataL,3,0x00000007		// convert dp to sp
			mr		MemDataL,MemDataH					// setup 32 bits to be stored
			bgt		SetupDone							// all done if normalized single

			cmpwi	DataTemp,874						// check for denorm to zero
			rlwinm	MemDataL,MemDataH,0,0x80000000		// retain sign, exp/frac <- 0
			blt		SetupDone							// all done if denorm to zero

			oris	MemDataH,MemDataH,0x0080			// insert msb
			neg		DataTemp,DataTemp					// shift amount 0
22 (low 6 bits)
			rlwinm	MemDataH,MemDataH,0,0x00FFFFFF		// clear high bits
			srw		MemDataH,MemDataH,DataTemp			// denormalize the data
			rlwimi	MemDataL,MemDataH,31,0x007FFFFF		// insert the shifted fraction
			b		SetupDone							// all done


SetupSTWBRX:
			rlwinm	RegIndex,MemCmdInfo,6+5+2,0x7C	// get index of RS register
			lwbrx	MemDataL,KernelDataPtr,RegIndex	// get register RS, data to store
			b		SetupDoneClearRA

SetupSTHBRX:
			rlwinm	RegIndex,MemCmdInfo,6+5+2,0x7C	// get index of RS register
			la		MemDataL,2(KernelDataPtr)
			lhbrx	MemDataL,MemDataL,RegIndex		// get register RS, data to store
			b		SetupDoneClearRA

SetupSTMW:
SetupSTBUX:
SetupSTHUX:
SetupSTWUX:
SetupSTDUX:
SetupSTDU:
			rlwinm	RegIndex,MemCmdInfo,6+5+2,0x7C	// get index of RS register
			lwzx	MemDataL,KernelDataPtr,RegIndex	// get register RS, data to store
			b		SetupDone

SetupSTBX:
SetupSTHX:
SetupSTWX:
SetupSTWCX_:
SetupSTDX:
SetupSTD:
SetupECOWX:
			rlwinm	RegIndex,MemCmdInfo,6+5+2,0x7C	// get index of RS register
			lwzx	MemDataL,KernelDataPtr,RegIndex	// get register RS, data to store
SetupLBZX:
SetupLHZX:
SetupLHAX:
SetupLWZX:
SetupLWA:
SetupLWAX:
SetupLWAR:
SetupLFSX:
SetupLD:
SetupLDX:
SetupLFDX:
SetupECIWX:
SetupDoneClearRA:
			rlwinm	MemCmdInfo,MemCmdInfo,0,0xFFE0FFFF	// RA <- 0, no update
SetupDCBF:
SetupDCBT:
SetupICBI:
SetupDCBST:
SetupDCBTST:
SetupLBZUX:
SetupLHZUX:
SetupLHAUX:
SetupLHBRX:
SetupLWZUX:
SetupLWAUX:
SetupLWBRX:
SetupLMW:
SetupLFSUX:
SetupLDU:
SetupLDUX:
SetupLFDUX:
SetupDone:
			rlwinm.	MemByteCount,MemCmdInfo,31,0x0F		// get byte count, test for zero
			add		MemAddress,MemUpdateEA,MemByteCount	// point past end of data
RetryStart:
			rlwinm	MemProcPtr,MemProcPtr,0,0xFFFFFC00	// setup to insert alignment info
			rlwimi	MemProcPtr,MemAddress,1,0x0E		// insert low 3 bits of end address
			rlwimi	MemProcPtr,MemCmdInfo,4,0xF0		// insert len, r/w
			lha		MemByteCount,RetryLookup-MemProcBase(MemProcPtr)
			la		DataTemp,VectorTableDSI(KernelDataPtr)
			add		MemByteCount,MemByteCount,MemProcPtr
			mtlr	MemByteCount
			mtsprg	VectorBaseSPRG,DataTemp				// setup to catch nested DSIs
			mtmsr	MSR_Enabled
			isync
			rlwimi	MemProcPtr,MemSetupInfo,2,0x03FC	// insert completion routine index
			bnelr										// read/write the bytes if count <> 0
			b		RetryDone							// nothing to retry if count = 0

W4_22:		srwi	DataTemp,MemDataL,16
			sth		DataTemp,-4(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			sth		MemDataL,-2(MemAddress)
			b		RetryDone

R4_22:		lhz		DataTemp,-4(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			rlwimi	MemDataL,DataTemp,16,0xFFFF0000
R2_2:		lhz		DataTemp,-2(MemAddress)
			rlwimi	MemDataL,DataTemp, 0,0x0000FFFF
RetryDoneNoData:
RetryDone:	bl		WaitBusIdle
			rlwinm.	RegIndex,MemCmdInfo,16+2,0x7C		// get RA register index, test RA=0
			mtlr	MemProcPtr							// point to completion routine
			cror	eq,eq,b_FlagNoUpdate
			mtsprg	VectorBaseSPRG,SavedVBR				// restore previous exception vectors
			beqlr										// run completion proc if no update
			crset	b_FlagRegModified					// indicate that saved registers are modified
			stwx	MemUpdateEA,KernelDataPtr,RegIndex	// update RA register
			blr											// run completion proc

CompleteLHA:
			extsh	MemDataL,MemDataL					// sign extend the data
CompleteLBZ:
CompleteLHZ:
CompleteLWZ:
CompleteLWA:
CompleteLD:
			rlwinm	RegIndex,MemCmdInfo,11+2,0x7C		// get RT register index
			crset	b_FlagRegModified					// indicate that saved registers are modified
			stwx	MemDataL,KernelDataPtr,RegIndex		// update RT register
CompleteSTB:
CompleteSTH:
CompleteSTW:
CompleteSTD:
CompleteMemOpDone:
			andi.	DataTemp,MemCtxFlags,1<<(31-b_CtxFlagTracePending)	// see if trace pending
			la		SavedSRR0,4(SavedSRR0)				// skip over the faulting instr
			mtsrr0	SavedSRR0
			mtsrr1	SavedSRR1
			bne		CompleteTracePending
			mtlr	SavedLR
// when b_FlagRegModified = 0, gpr 0,2
9 are still valid, gpr 0
31 are saved in XCP_GPR_0_31
// when b_FlagRegModified = 1, gpr 0,2
9 may be invalid,  gpr 0
31 are saved in XCP_GPR_0_31
			bf		b_FlagRegModified,CompleteReturnMin
CompleteReturnMax:
			mtcr	SavedCR
			lmw		r2,XCP_GPR_0_31+(2*4)(KernelDataPtr)
			lwz		r0,XCP_GPR_0_31+(0*4)(KernelDataPtr)
			lwz		r1,XCP_GPR_0_31+(1*4)(KernelDataPtr)
			rfi

CompleteReturnMin:
			mtcr	SavedCR
			lmw		r10,XCP_GPR_0_31+(10*4)(KernelDataPtr)
			lwz		r1,XCP_GPR_0_31+( 1*4)(KernelDataPtr)
			rfi

CompleteTracePending:
			mfsprg	SavedVBR,VectorBaseSPRG
			mtsprg	lrSaveSPRG,SavedLR
			rlwinm	MemCtxFlags,MemCtxFlags,0,~(1<<(31-b_CtxFlagTracePending))
			lwz		SavedLR,0x0D00>>6(SavedVBR)			// get the trace handler
			stw		MemCtxFlags,ActiveCtxFlags(KernelDataPtr)
			mtcr	SavedCR
			mtlr	SavedLR
			lmw		r2,XCP_GPR_0_31+(2*4)(KernelDataPtr)
			lwz		r0,XCP_GPR_0_31+(0*4)(KernelDataPtr)
			lwz		r1,XCP_GPR_0_31+(1*4)(KernelDataPtr)
			mtsprg	r1SaveSPRG,r1
			blrl


CompleteLHBR:
			slwi	MemDataL,MemDataL,16				// shift bytes into high half, zero fill
CompleteLWBR:
			rlwinm	RegIndex,MemCmdInfo,11+2,0x7C		// get RT register index
			crset	b_FlagRegModified					// indicate that saved registers are modified
			stwbrx	MemDataL,KernelDataPtr,RegIndex		// update RT register
			b		CompleteMemOpDone

CompleteLFS:rlwinm	MemDataH,MemDataL,0,0x80000000		// copy the sign bit
			xor.	MemDataL,MemDataH,MemDataL			// clear sign, test for +/- zero
			beq		CompleteLFD							// If all zero, all done
			rlwinm.	DataTemp,MemDataL,16,0x00007F80		// get the exponent, test for zero
			addi	DataTemp,DataTemp,0x0080			// setup to extend exp=255
			rlwimi	MemDataH,MemDataL,32-3,0x07FFFFFF	// convert sp to dp
			extsh	DataTemp,DataTemp					// fill with FFFF if exp=255
			rlwimi	MemDataH,MemDataL,0,0x40000000		// update bit 1
			slwi	MemDataL,MemDataL,32-3				// convert sp to dp
			subi	DataTemp,DataTemp,0x4080			// compute bits 2
4
			rlwimi	MemDataH,DataTemp,0,0x38000000		// convert sp to dp
			bne		CompleteLFD							// all done if not denorm
			srwi	MemDataL,MemDataL,20				// reassemble fraction
			rlwimi	MemDataL,MemDataH,12,0xFFFFF000		// reassemble fraction
			cntlzw	DataTemp,MemDataL					// setup to normalize
			slw		MemDataL,MemDataL,DataTemp			// normalize fraction
			neg		DataTemp,DataTemp					// setup to compute exponent
			rlwimi	MemDataH,MemDataL,1+20,0x000FFFFF	// convert sp to dp
			addi	DataTemp,DataTemp,1023-127			// compute dp exponent
			slwi	MemDataL,MemDataL,1+20				// convert sp to dp
			rlwimi	MemDataH,DataTemp,20,0x7FF00000		// insert exponent
CompleteLFD:rlwinm	DataTemp,MemProcPtr,0,0xFFFFFC00	// clear low bits
			rlwimi	DataTemp,MemCmdInfo,6+5+3,0xF8		// get index of FRT register
			la		DataTemp,LoadFPreg-MemProcBase(DataTemp)
			mtlr	DataTemp							// setup to get FRT
			stw		MemDataH,DataConvertBuffer+0(KernelDataPtr)	// save upper half of data
			stw		MemDataL,DataConvertBuffer+4(KernelDataPtr)	// save lower half of data
			rlwimi	MSR_Disabled,SavedSRR1,0,msr_fp		// setup to enable FP regs
			mtmsr	MSR_Disabled						// enable FP regs, if they were valid
			isync
			ori		SavedSRR1,SavedSRR1,msr_fp			// FP regs will become valid
			blr											// FP reg <- DataConvertBuffer

CompleteLMW:rlwinm.	RegIndex,MemCmdInfo,6+5+2,0x7C		// get RT register index, test zero
			rlwinm	DataTemp,MemCmdInfo,6+5+5+2,0x7C	// get RA index, to compare to RT
			cmpw	cr7,RegIndex,DataTemp				// see if RT=RA
			addis	MemCmdInfo,MemCmdInfo,1<<5			// increment RT
			beq		CompleteLMWupdateRT					// always update if RT=0
			beq		cr7,CompleteLMWskipRT				// dont update if RT=RA
CompleteLMWupdateRT:
			stwx	MemDataL,KernelDataPtr,RegIndex		// update RT register
CompleteLMWskipRT:
			cmpwi	RegIndex,31*4						// check RT=31
			li		MemByteCount,(4<<1)+1				// read 4 bytes
			rlwimi	MemCmdInfo,MemByteCount,0,0x3F		// insert byte count, r/w
			la		MemAddress,4(MemAddress)			// point past end of data
			bne		RetryStart							// continue until RT=31
			b		CompleteMemOpDone					// all done when RT=31

CompleteSTMW:
			addis	MemCmdInfo,MemCmdInfo,1<<5			// increment RS
			rlwinm.	RegIndex,MemCmdInfo,6+5+2,0x7C		// get index of RS register, check wrap
			beq		CompleteMemOpDone					// all done when 31 wraps to 0
			lwzx	MemDataL,KernelDataPtr,RegIndex		// get register RS, data to store
			li		MemByteCount,(4<<1)+0				// write 4 bytes
			rlwimi	MemCmdInfo,MemByteCount,0,0x3F		// insert byte count, r/w
			la		MemAddress,4(MemAddress)			// point past end of data
			b		RetryStart							// store the next register


SetupDCBZ:	lhz		MemDataL,PI+DataCacheBlockSize(KernelDataPtr)
			neg		MemDataL,MemDataL					// form mask
			and		MemAddress,MemUpdateEA,MemDataL		// align address to start of cache block
			b		CompleteDCBZnext					// start zeroing

CompleteDCBZ:
			lhz		MemDataL,PI+DataCacheBlockSize(KernelDataPtr)
			subi	MemDataL,MemDataL,8					// DCBZ size - 8
			and.	MemByteCount,MemAddress,MemDataL	// check for wrap into next cache block
			rlwinm	MemAddress,MemAddress,0,-8			// align address to next 8 bytes
			beq		CompleteMemOpDone					// all done when 31 wraps to 0
CompleteDCBZnext:
			li		MemByteCount,(8<<1)+0				// write 8 bytes
			rlwimi.	MemCmdInfo,MemByteCount,0,0x3F		// insert byte count, r/w  force ne for RetryStart
			la		MemAddress,8(MemAddress)			// point past end of data
			li		MemDataH,0							// store 8 bytes of zeros at a time
			li		MemDataL,0							// store 8 bytes of zeros at a time
			b		RetryStart							// store the next register

CompleteLWAR:
			rlwinm	RegIndex,MemCmdInfo,11+2,0x7C		// get RT register index
			crset	b_FlagRegModified					// indicate that saved registers are modified
			stwx	MemDataL,KernelDataPtr,RegIndex		// update RT register
			stwcx.	MemDataL,KernelDataPtr,RegIndex		// clear the reservation
			b		CompleteMemOpDone					// complete the load

CompleteSTWC_:											// perform a rd instead of wr to update HTAB
			stwcx.	r0,0,KernelDataPtr					// XCP_GPR_0_31+(0*4)(KernelDataPtr), clear reservation
			mfcr	DataTemp							// get CR0 (especially updated SO bit)
			rlwinm	DataTemp,DataTemp,0,0xDFFFFFFF		// clear the EQ bit, indicate store not performed
			rlwimi	SavedCR,DataTemp,0,0xF0000000		// update saved CR0
			b		CompleteMemOpDone					// complete the operation

CompleteTouchRd:
			rlwinm	MemCtxFlags,MemCtxFlags,0,~((1<<(31-b_CtxFlagTracePending))|(1<<(31-b_CtxFlagMemInfoValid)))
			la		SavedSRR0,-4(SavedSRR0)				// re-execute the instruction
			stw		MemCtxFlags,ActiveCtxFlags(KernelDataPtr)	// clear pending exceptions
			b		CompleteMemOpDone					// retry the operation

CompleteForceEx:
			li		Tmp1,ecDataInvalidAddress			// there are no legal addresses for this op
			b		GenerateMemException				// Invalid Address, Mem Exception

SetupSTSWI:	subi	MemByteCount,MemInstr,1<<11			// get NB field - 1
			rlwinm	MemByteCount,MemByteCount,21,0x1F	// get count - 1
			b		SetupSTSW							// join common code
SetupSTSWX:	mfxer	MemByteCount						// get byte count
			andi.	MemByteCount,MemByteCount,0x7F		// test for zero, clear unused bits
			subi	MemByteCount,MemByteCount,1			// get count - 1
			beq		CompleteMemOpDone					// all done if count=0
SetupSTSW:	rlwimi	MemCmdInfo,MemByteCount,4,0x07C0	// insert register count
			not		MemByteCount,MemByteCount			// compute ending byte number
			rlwimi	MemCmdInfo,MemByteCount,26,0x0C000000	// modify low 2 bits of memop
			mr		MemAddress,MemUpdateEA				// point to start of data
			b		CompleteSTSWnext					// store the next register

CompleteSTSW_4:
CompleteSTSW_3:
CompleteSTSW_2:
CompleteSTSW_1:
			andi.	MemByteCount,MemCmdInfo,0x07C0		// see if all done
			addis	RegIndex,MemCmdInfo,1<<5			// increment RS, may wrap around
			rlwimi	MemCmdInfo,RegIndex,0,0x03E00000	// insert incremented RS field
			subi	MemCmdInfo,MemCmdInfo,0x0040		// decrement reg count field
			bne		CompleteSTSWnext					// continue if count<>0
			b		CompleteMemOpDone					// all done if count=0


SetupLSWI:	subi	MemByteCount,MemInstr,1<<11			// get NB field - 1
			rlwinm	MemByteCount,MemByteCount,21,0x1F	// get count - 1
			addis	RegIndex,MemInstr,0x1F<<5			// get RT field - 1
			rlwimi	MemCmdInfo,RegIndex,22,0x0000F800	// create RB field = RT-1
			b		SetupLSW							// join common code
SetupLSWX:	mfxer	MemByteCount						// get byte count
			andi.	MemByteCount,MemByteCount,0x7F		// test for zero, clear unused bits
			rlwimi	MemCmdInfo,MemInstr,0,0x0000F800	// insert RB field
			subi	MemByteCount,MemByteCount,1			// get count - 1
			beq		CompleteMemOpDone					// all done if count=0
SetupLSW:	andis.	DataTemp,MemCmdInfo,0x001F			// see if RA=0
			rlwimi	MemCmdInfo,MemByteCount,4,0x07C0	// insert register count
			not		MemByteCount,MemByteCount			// compute ending byte number
			rlwimi	MemCmdInfo,MemByteCount,26,0x0C000000	// modify low 2 bits of memop
			mr		MemAddress,MemUpdateEA				// point to start of data
			bne		CompleteLSWnext						// load the next register (RA<>0)
			rlwimi	MemCmdInfo,MemCmdInfo,5,0x001F0000	// Use RB for RA when RA=0
			b		CompleteLSWnext						// load the next register

CompleteLSW_4:
CompleteLSW_3:
CompleteLSW_2:
CompleteLSW_1:
			andi.	MemByteCount,MemCmdInfo,0x07C0		// see if last register
			rlwinm	RegIndex,MemCmdInfo,6+5+2,0x7C		// get index of RT register
			bne		CompleteLSWnoShift					// don't shift data unless last register
			rlwinm	MemByteCount,MemCmdInfo,9,0x18		// get the shift amount
			slw		MemDataL,MemDataL,MemByteCount		// left align the data
			b		CompleteLSWnoShift					// join common code


CompleteLSCB_4:
CompleteLSCB_3:
CompleteLSCB_2:
CompleteLSCB_1:
CompleteLSCB__4:
CompleteLSCB__3:
CompleteLSCB__2:
CompleteLSCB__1:
			rlwinm.	MemByteCount,MemCmdInfo,32-4,0x07C	// see if last register
			rlwinm	RegIndex,MemCmdInfo,6+5+2,0x7C		// get index of RT register
			bne		CompleteLSCBnoShift					// don't shift data unless last register
			rlwinm	DataTemp,MemCmdInfo,9,0x18			// get the shift amount
			slw		MemDataL,MemDataL,DataTemp			// left align the data
			b		CompleteLSCBnoShift					// join common code

SetupLSCBX:	mfxer	MemByteCount						// get byte count
			andi.	MemByteCount,MemByteCount,0x7F		// test for zero, clear unused bits
			rlwimi	MemCmdInfo,MemInstr,0,0x0000F800	// insert RB field
			rlwimi	MemCmdInfo,MemInstr,28,0x10000000	// insert RC bit
			cmpw	cr7,MemInstr,MemByteCount			// force CR7 to NE condition
			beq		CompleteLSCBend						// all done if count=0
			subi	MemByteCount,MemByteCount,1			// get count - 1
			andis.	DataTemp,MemCmdInfo,0x001F			// see if RA=0
			rlwimi	MemCmdInfo,MemByteCount,4,0x07C0	// insert register count
			not		MemByteCount,MemByteCount			// compute ending byte number
			rlwimi	MemCmdInfo,MemByteCount,26,0x0C000000	// modify low 2 bits of memop
			mr		MemAddress,MemUpdateEA				// point to start of data
			bne		CompleteLSCBnext					// load the next register (RA<>0)
			rlwimi	MemCmdInfo,MemCmdInfo,5,0x001F0000	// Use RB for RA when RA=0
			b		CompleteLSCBnext					// load the next register

//	NOTE: All of the Setup/Complete entry points must reside in the 1024 byte range
//	starting at MemProcBase. To make sure that they all fit, some of the longer routines
//	have been split up, and are continued here, which can be outside of the 1024 byte range.

CompleteSTSWnext:
			andi.	DataTemp,MemCmdInfo,0x07C0			// see if last register
			rlwinm	RegIndex,MemCmdInfo,6+5+2,0x7C		// get index of RS register
			lwzx	MemDataL,KernelDataPtr,RegIndex		// get register RS, data to store
			li		MemByteCount,(4<<1)+0				// write 4 bytes
			rlwimi	MemCmdInfo,MemByteCount,0,0x3F		// insert byte count, r/w
			la		MemAddress,4(MemAddress)			// point past end of data
			bne		RetryStart							// store the next register

			rlwinm	MemByteCount,MemCmdInfo,9,0x18		// get the shift amount
			srw		MemDataL,MemDataL,MemByteCount		// right align the data
			rlwinm	MemByteCount,MemCmdInfo,6,0x03		// get the remaining byte count
			neg		MemByteCount,MemByteCount			// setup for subtraction
			add		MemAddress,MemAddress,MemByteCount	// adjust the ending address
			addi	MemByteCount,MemByteCount,4			// compute number of bytes to write
			rlwimi.	MemCmdInfo,MemByteCount,1,0x3E		// insert byte count, force ne for RetryStart
			b		RetryStart							// store the last register


CompleteLSWnoShift:
			rlwinm	DataTemp,MemCmdInfo,6+5+5+2,0x7C	// get RA index, to compare to RT
			cmpw	cr7,RegIndex,DataTemp				// see if RT=RA
			rlwinm	DataTemp,MemCmdInfo,6+5+5+5+2,0x7C	// get RB index, to compare to RT
			cmpw	cr6,RegIndex,DataTemp				// see if RT=RB
			beq		cr7,CompleteLSWupdateDone			// skip register if RT=RA
			beq		cr6,CompleteLSWupdateDone			// skip register if RT=RB
			stwx	MemDataL,KernelDataPtr,RegIndex		// update register RT
CompleteLSWupdateDone:
			addis	RegIndex,MemCmdInfo,1<<5			// increment RT, may wrap around
			rlwimi	MemCmdInfo,RegIndex,0,0x03E00000	// insert incremented RT field
			subi	MemCmdInfo,MemCmdInfo,0x0040		// decrement reg count field
			beq		CompleteMemOpDone					// all done if count=0
CompleteLSCBnext:
CompleteLSWnext:
			andi.	DataTemp,MemCmdInfo,0x07C0			// see if last register
			li		MemByteCount,(4<<1)+1				// read 4 bytes
			rlwimi	MemCmdInfo,MemByteCount,0,0x3F		// insert byte count, r/w
			la		MemAddress,4(MemAddress)			// point past end of data
			bne		RetryStart							// read the next word

			rlwinm	MemByteCount,MemCmdInfo,6,0x03		// get the remaining byte count
			neg		MemByteCount,MemByteCount			// setup for subtraction
			add		MemAddress,MemAddress,MemByteCount	// adjust the ending address
			addi	MemByteCount,MemByteCount,4			// compute number of bytes to read
			rlwimi.	MemCmdInfo,MemByteCount,1,0x3E		// insert byte count, force ne for RetryStart
			b		RetryStart							// store the last register


CompleteLSCBnoShift:
			rlwinm	DataTemp,MemCmdInfo,6+5+5+2,0x7C	// get RA index, to compare to RT
			cmpw	cr7,RegIndex,DataTemp				// see if RT=RA
			rlwinm	DataTemp,MemCmdInfo,6+5+5+5+2,0x7C	// get RB index, to compare to RT
			cmpw	cr6,RegIndex,DataTemp				// see if RT=RB
			beq		cr7,CompleteLSCBupdateDone			// skip register if RT=RA
			beq		cr6,CompleteLSCBupdateDone			// skip register if RT=RB
			stwx	MemDataL,KernelDataPtr,RegIndex		// update register RT
CompleteLSCBupdateDone:
			addis	RegIndex,MemCmdInfo,1<<5			// increment RT, may wrap around
			rlwimi	MemCmdInfo,RegIndex,0,0x03E00000	// insert incremented RT field
			subi	MemCmdInfo,MemCmdInfo,0x0040		// decrement reg count field
			not		MemByteCount,MemByteCount			// setup to compute negative count
			rlwimi	MemByteCount,MemCmdInfo,6,0x03		// insert low 2 bits
			li		RegIndex,1							// setup to increment neg count
			mfxer	DataTemp							// get byte to compare to
			rlwinm	DataTemp,DataTemp,32-8,0xFF			// right align it

			rlwinm	MemDataH,MemDataL,8,0xFF			// get byte 0
			cmpw	cr7,MemDataH,DataTemp				// check for match
			add.	MemByteCount,MemByteCount,RegIndex	// count the byte, check for end
			beq		cr7,CompleteLSCBmatch				// exit if match found
			beq		CompleteLSCBend						// exit if end of string reached

			rlwinm	MemDataH,MemDataL,16,0xFF			// get byte 1
			cmpw	cr7,MemDataH,DataTemp				// check for match
			add.	MemByteCount,MemByteCount,RegIndex	// count the byte, check for end
			beq		cr7,CompleteLSCBmatch				// exit if match found
			beq		CompleteLSCBend						// exit if end of string reached

			rlwinm	MemDataH,MemDataL,24,0xFF			// get byte 2
			cmpw	cr7,MemDataH,DataTemp				// check for match
			add.	MemByteCount,MemByteCount,RegIndex	// count the byte, check for end
			beq		cr7,CompleteLSCBmatch				// exit if match found
			beq		CompleteLSCBend						// exit if end of string reached

			rlwinm	MemDataH,MemDataL,0,0xFF			// get byte 3
			cmpw	cr7,MemDataH,DataTemp				// check for match
			add.	MemByteCount,MemByteCount,RegIndex	// count the byte, check for end
			beq		cr7,CompleteLSCBmatch				// exit if match found
			bne		CompleteLSCBnext					// continue reading if more bytes
CompleteLSCBend:
CompleteLSCBmatch:
			rlwinm.	RegIndex,MemCmdInfo,0,0x10000000	// test the RC bit
			mfxer	DataTemp							// get string length
			add		MemByteCount,MemByteCount,DataTemp	// compute number of bytes compared
			rlwimi	DataTemp,MemByteCount,0,0x7F		// insert into XER
			mtxer	DataTemp							// restore XER
			beq		CompleteMemOpDone					// all done if RC=0
			mfcr	DataTemp							// get the match/so bits
			rlwinm	DataTemp,DataTemp,0,0x03			// clear the lt/gt bits
			rlwimi	SavedCR,DataTemp,28,0xF0000000		// update the saved CR0 field
			b		CompleteMemOpDone					// all done


R8_1241:	lbz		DataTemp,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,24,0xFF000000
R7_241:		lhz		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp, 8,0x00FFFF00
			b		R5_41
R6_141:		lbz		DataTemp,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp, 8,0x0000FF00
R5_41:		lwz		DataTemp,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp, 8,0x000000FF
			rlwimi	MemDataL,DataTemp, 8,0xFFFFFF00
			b		R1_1
R8_1421:	lbz		DataTemp,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,24,0xFF000000
R7_421:		lwz		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,24,0x00FFFFFF
			rlwimi	MemDataL,DataTemp,24,0xFF000000
			b		R3_21
R6_1221:	lbz		DataTemp,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp, 8,0x0000FF00
R5_221:		lhz		DataTemp,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,24,0x000000FF
			rlwimi	MemDataL,DataTemp,24,0xFF000000
			b		R3_21
R4_121:		lbz		DataTemp,-4(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataL,DataTemp,24,0xFF000000
R3_21:		lhz		DataTemp,-3(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			rlwimi	MemDataL,DataTemp, 8,0x00FFFF00
			b		R1_1
R2_11:		lbz		DataTemp,-2(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataL,DataTemp, 8,0x0000FF00
R1_1:		lbz		DataTemp,-1(MemAddress)
			rlwimi	MemDataL,DataTemp, 0,0x000000FF
			b		RetryDone

R8_242:		lhz		DataTemp,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,16,0xFFFF0000
			b		R6_42
R7_142:		lbz		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,16,0x00FF0000
R6_42:		lwz		DataTemp,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,16,0x0000FFFF
			rlwimi	MemDataL,DataTemp,16,0xFFFF0000
			b		R2_2
R5_122:		lbz		DataTemp,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp, 0,0x000000FF
			b		R4_22
R3_12:		lbz		DataTemp,-3(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataL,DataTemp,16,0x00FF0000
			b		R2_2

R8_44:		lwz		MemDataH,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			lwz		MemDataL,-4(MemAddress)
			b		RetryDone
R7_124:		lbz		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp,16,0x00FF0000
R6_24:		lhz		DataTemp,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp, 0,0x0000FFFF
			lwz		MemDataL,-4(MemAddress)
			b		RetryDone
R5_14:		lbz		DataTemp,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			rlwimi	MemDataH,DataTemp, 0,0x000000FF
R4_4:		lwz		MemDataL,-4(MemAddress)
			b		RetryDone

R8_8:		lwz		MemDataH,-8(MemAddress)
			lwz		MemDataL,-4(MemAddress)
			b		RetryDone


W8_1241:	srwi	DataTemp,MemDataH,24
			stb		DataTemp,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W7_241:		srwi	DataTemp,MemDataH,8
			sth		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			b		W5_41
W6_141:		srwi	DataTemp,MemDataH,8
			stb		DataTemp,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W5_41:		srwi	DataTemp,MemDataL,8
			rlwimi	DataTemp,MemDataH,24,0xFF000000
			stw		DataTemp,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			stb		MemDataL,-1(MemAddress)
			b		RetryDone
W8_1421:	srwi	DataTemp,MemDataH,24
			stb		DataTemp,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W7_421:		srwi	DataTemp,MemDataL,24
			rlwimi	DataTemp,MemDataH, 8,0xFFFFFF00
			stw		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			b		W3_21
W6_1221:	srwi	DataTemp,MemDataH,8
			stb		DataTemp,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W5_221:		srwi	DataTemp,MemDataL,24
			rlwimi	DataTemp,MemDataH, 8,0x0000FF00
			sth		DataTemp,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			b		W3_21
W4_121:		srwi	DataTemp,MemDataL,24
			stb		DataTemp,-4(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W3_21:		srwi	DataTemp,MemDataL,8
			sth		DataTemp,-3(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			stb		MemDataL,-1(MemAddress)
			b		RetryDone
W2_11:		srwi	DataTemp,MemDataL,8
			stb		DataTemp,-2(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W1_1:		stb		MemDataL,-1(MemAddress)
			b		RetryDone

W8_242:		srwi	DataTemp,MemDataH,16
			sth		DataTemp,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			b		W6_42
W7_142:		srwi	DataTemp,MemDataH,16
			stb		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W6_42:		srwi	DataTemp,MemDataL,16
			rlwimi	DataTemp,MemDataH,16,0xFFFF0000
			stw		DataTemp,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			sth		MemDataL,-2(MemAddress)
			b		RetryDone
W5_122:		stb		MemDataH,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
			b		W4_22
W3_12:		srwi	DataTemp,MemDataL,16
			stb		DataTemp,-3(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W2_2:		sth		MemDataL,-2(MemAddress)
			b		RetryDone

W8_44:		stw		MemDataH,-8(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,4*MemCmdSizeDec
			stw		MemDataL,-4(MemAddress)
			b		RetryDone
W7_124:		srwi	DataTemp,MemDataH,16
			stb		DataTemp,-7(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W6_24:		sth		MemDataH,-6(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,2*MemCmdSizeDec
			stw		MemDataL,-4(MemAddress)
			b		RetryDone
W5_14:		stb		MemDataH,-5(MemAddress)
			addi	MemCmdInfo,MemCmdInfo,1*MemCmdSizeDec
W4_4:		stw		MemDataL,-4(MemAddress)
			b		RetryDone

W8_8:		stw		MemDataH,-8(MemAddress)
			stw		MemDataL,-4(MemAddress)
			b		RetryDone

#endif	/* 0 */

/*
** HACK!  Assembler fixups!
*/
CompleteMemOpDone:
DSI_Decode:
GenerateException:
KernelCrash:
	rfi
#endif

/* ---------------------------------------------------------------------------	
	L_MMU(type)

	Adopted from Nukernel technology.

	This is the exception handling routine branched to by a DSI or ISI
	type of system exception. 

	An extension of the data access and instruction access exception handlers
	installed in the hardware vectors.  Satisfies the fault, if possible, from
	the overflow hash table or by generating the PTE from static information.

	NOTE: Must be called in the access exception path before data relocation has been
	re-enabled.  Instruction relocation is OK only if these routines are entirely BAT
	mapped.  Avoiding relocated references is a requirement for being able to move
	*any* PTE out to the overflow table.  Otherwise, it would be possible to "overflow
	fault" inside the overflow fault handler.
	
	This routine acts as a safety valve to handle cases when too many virtual 
	addresses fall in a PTE hash class (aka a PTE group or PTEG). Though the PMAP
	support allows pagable entries to be tossed and reconstituted later, wired
	page frames cannot be handled in this manner. This firmware function extends
	the apparent size of the PTEGs by creating a software overflow list that can
	house extra PTEs. When a DSI or ISI is recognized, this function checks the
	overflow list for the related PTE and upon finding it, places it in its
	appropriate PTEG. This may cause a PTE to be vacated into the overflow list.
	Once the PTE is reinstated, control is passed directly back to the interrupted
	instruction. Upon failing to find the related PTE, control is passed to the
	common exit handler to propagate the DSI or ISI to the upper kernel layer for
	the usual page fault resolution.
	
	Other firmware functions replace some of the PMAP equivalent functions to
	ensure searches and/or deletions include the PTEs in the overflow list.
	These functions are accesed via fast path system calls.
	

   Entry:	via a system exception handler, thus interrupts off, VM off.
 		Entry r1-r3 have been saved in sprg1-3. 
		r2 - byte offset into per_proc_info of this CPU
		r3 - contains the exception number.

   Exit:	If the PTE miss is resolved by finding the related PTE in
 		the overflow list, control passes directly to the exception
 		point. Otherwise, the exception is propagated to the usual
 		common exception handler.
*/

#define	noErr			 0
#define	kernelInUseErr		-1
#define	kernelAlreadyFreeErr	-2

	.section __VECTORS, __interrupts
	.align	ALIGNMENT



/*	Globals used by the MMU support functions to manage
	the overflow hash table.
*/
.gOverflowHashTable:	.long	0
.gFreeOverflowPTEs:	.long	0
.gOverflowPTEGSeed:	.long	0
.gOverflowTossCount:	.long	0
.gOverflowReloadCount:	.long	0

/*	Globals used to track autogen capable areas (a nukernel area
	is similar to a mach region). Areas that are autogen capable
	have physically contiguous pages and are resident (wired).
*/
.gAutogenAreaList:	.long	0
.gAutogenReloadCount:	.long	0
.gAutogenTossCount:	.long	0

		
L_MMU:

	/* Save SRR0 and SRR1 plus cr and r3 into PER_PROC structure.
	*/
	stw	r3,	PP_SAVE_EXCEPTION_TYPE(r2)
	mfsrr0	r1
	mfsrr1	r3
	stw	r1,	PP_SAVE_SRR0(r2)
	stw	r3,	PP_SAVE_SRR1(r2)
	mfdar	r1
	mfdsisr	r3
	stw	r1,	PP_SAVE_DAR(r2)
	stw	r3,	PP_SAVE_DSISR(r2)
	mfcr	r1
	stw	r1,	PP_SAVE_CR(r2)
	
	/* Save registers we'll use into the per-processor buffer.
	*/
	stw	r0,	PP_SAVE_R0(r2)
	stw	r4,	PP_SAVE_R4(r2)
	stw	r5,	PP_SAVE_R5(r2)
	stw	r6,	PP_SAVE_R6(r2)
	stw	r7,	PP_SAVE_R7(r2)
	stw	r8,	PP_SAVE_R8(r2)
	stw	r9,	PP_SAVE_R9(r2)
	stw	r10,	PP_SAVE_R10(r2)
	stw	r11,	PP_SAVE_R11(r2)
	stw	r12,	PP_SAVE_R12(r2)

	mflr	r0
	lwz	r3,	PP_SAVE_EXCEPTION_TYPE(r2)
	stw	r0,	PP_SAVE_LR(r2)

ReloadPTE:

	/* Check the OverflowHashTable and AutogenAreaList, 
	   reloading if possible.
	*/
	cmpwi	r3, EXC_INSTRUCTION_ACCESS
	mfspr	r4, srr0			// fault address is PC
	beq	HaveFaultAddress
	mfdar	r4				// fault address is DAR
HaveFaultAddress:
	li		r3, 0			// pass "current address space"
	bl		LookupPTE
//	EndHashTableSection	r2

	/* Check result and restore the registers. If extended PTE lookup worked
	   (r4 == noErr), rfi to resume faultee's execution.  If it didn't work,
	   continue to process the exception as a page fault.
	*/
	cmpwi	r4, noErr			// check LookupPTE result

	lwz	r3,	PP_SAVE_CR(r2)
	lwz	r5,	PP_SAVE_LR(r2)
	lwz	r0,	PP_SAVE_R0(r2)
	mtlr	r5
	lwz	r4,	PP_SAVE_R4(r2)
	lwz	r5,	PP_SAVE_R5(r2)
	lwz	r6,	PP_SAVE_R6(r2)
	lwz	r7,	PP_SAVE_R7(r2)
	lwz	r8,	PP_SAVE_R8(r2)
	lwz	r9,	PP_SAVE_R9(r2)
	lwz	r10,	PP_SAVE_R10(r2)
	lwz	r11,	PP_SAVE_R11(r2)
	lwz	r12,	PP_SAVE_R12(r2)

	beq-	DoRFI				// LookupPTE worked?
	mtcr	r3
	b	.L_Common


	/* Restore the faultee's r1-r3, then rfi back with the PTE mapped!  Pad (at
	   least) the remainder of the rfi instruction's cache line with benign values
	   to prevent prefetch enigmas as we transition from instruction relocation off
	   back to instruction relocation on. This is a problem on 601s.
	*/
DoRFI:	mtcr	r3		
	mfsprg	r1, 1
	mfsprg	r2, 2
	mfsprg	r3, 3
	rfi
	.long	0				// Safety padding
	.long	0				// Safety padding
	.long	0				// Safety padding
	.long	0				// Safety padding
	.long	0				// Safety padding
	.long	0				// Safety padding
	.long	0				// Safety padding
	.long	0				// Safety padding


#if 0 /* WIP ================================================================== */

/*
-------------------------------------------------------------------------------------
 AllocateOverflowPTE.

 Inputs:	r3 == PTEG address
		r4 == PTE hash information word
		r5 == PTE attributes word
 Outputs:	(none)
 Changes:	r3 - r6

 Description.

 NOTE: This routine is called with interrupts and data relocation disabled.
-------------------------------------------------------------------------------------
*/

AllocateOverflowPTE:

	/* Get an entry from the overflow PTE free list.
	*/
	lwz	r6, .gFreeOverflowPTEs(0)
	stw	r3, OverflowPTE.ptegAddress(r6)
	lwz	r3, OverflowPTE.pNextOverflowPTE(r6)
	stw	r3, .gFreeOverflowPTEs(0)

	/* Copy thePTEG and thePTEValue into overflow PTE with pteValid set to one.
	   r6 == OverflowPTE, r3 == thePTEG, r4 == hash info.
	*/
	oris	r4, r4, PTE_validMaskS
	stw	r4, OverflowPTE.pteValue+PTE.hashInfoWord(r6)
	stw	r5, OverflowPTE.pteValue+PTE.attributesWord(r6)

	/* Place overflow PTE into proper hash table list head.  
	   The hash function is the abbreviated page index (api).
	   r6 == OverflowPTE, r4 == hash information word
	*/
	rlwinm	r4, r4, PTE_apiShiftOut, PTE_apiMaskOut
	slwi	r4, r4, 2
	addi	r3, r4, .gOverflowHashTable
	lwz	r4, 0(r3)
	stw	r4, OverflowPTE.pNextOverflowPTE(r6)
	stw	r6, 0(r3)

	blr

/* -----------------------------------------------------------------------------------
 void		DeallocateOverflowPTE	   (OverflowPTE *	theOverflowPTE)

 Inputs:	r3 == OverflowPTE to deallocate
 Outputs:	(none)
 Changes:	r3 - r5

 Description.
-------------------------------------------------------------------------------------
*/
DeallocateOverflowPTE:

	/* Hash function is the abbreviated page index.
	*/
	lwz	r5, OverflowPTE.pteValue+PTE.hashInfoWord(r3)
	rlwinm	r5, r5, PTE_apiShiftOut, PTE_apiMaskOut
	slwi	r5, r5, 2
	addi	r4, r5, .gOverflowHashTable

	/* Locate the element previous to the one being deallocated
	   r4 == address of current element of which to check link
	   r3 == address of target element.
	*/
DeallocateLoop:
	lwz	r5, OverflowPTE.pNextOverflowPTE(r4)
	cmplw	r5, r3
	beq	DeallocateFound

	mr	r4, r5
	b	DeallocateLoop

	/* Delink target element from previous element
	   r3 == address of target element
	   r4 == address of previous element in list, or the list head itself.
	*/
DeallocateFound:
	lwz	r5, OverflowPTE.pNextOverflowPTE(r3)
	stw	r5, OverflowPTE.pNextOverflowPTE(r4)

	/* For viewing from the debugger: Make the upper nibble of the 
	   ptegAddress be 0xF so we can easily recognize freed elements 
	   with all the information intact (PTEG addresses are physical, 
	   and are most likely very low so the high 0xF does not disturb anything).
	*/
#ifdef  DEBUG
	lwz	r5, OverflowPTE.ptegAddress(r3)
	oris	r5, r5, 0xF000
	stw	r5, OverflowPTE.ptegAddress(r3)
#endif	/* DEBUG */

	/* Link target element into the free list.  Normally, put it at 
	   the head.  For debugger viewing, put it at the end so we have 
	   a little history.
	   r3 == address of target element.
	*/
#ifdef  DEBUG
	lwz	r4, .gFreeOverflowPTEs(0)
FindEnd:
	lwz	r5, OverflowPTE.pNextOverflowPTE(r4)
	cmpwi	r5, 0
	beq	FoundEnd
	mr	r4, r5
	b	FindEnd
FoundEnd:
	stw	r5, OverflowPTE.pNextOverflowPTE(r3)
	stw	r3, OverflowPTE.pNextOverflowPTE(r4)
#else	/* DEBUG */
	lwz	r4, .gFreeOverflowPTEs(0)
	stw	r4, OverflowPTE.pNextOverflowPTE(r3)
	stw	r3, .gFreeOverflowPTEs(0)
#endif	/* DEBUG */
	blr

/* ----------------------------------------------------------------------------------
 AllocateFromPTEG.

 Inputs:	r3 == PTEG address
			r4 == PTE hash information word
			r5 == PTE page attributes word
 Outputs:	r3 == PTE address, nil if there was no room
 Changes:	r3 - r6

 Description.

 NOTE: This routine is called with interrupts and data relocation disabled.
-------------------------------------------------------------------------------------
*/

AllocateFromPTEG:

	/* Loop to find entry with pteValid == 0
	*/
AllocateFromPTEGLoop:
	lwz	r6, PTE.hashInfoWord(r3)
	andis.	r6, r6, PTE_validMaskS		// check PTE valid
	beq	FoundFreePTE			// if clear, go use this PTE
	addi	r3, r3, PTE.sizeof		// else, move to next PTE in PTEG
	andi.	r6, r3, kPTEGSize-1		// off end of PTEG now?
	bne	AllocateFromPTEGLoop		// if not, loop to check PTE

	/* No free entry in this PTEG.
	*/
	li	r3, 0				// return nil
	b	AllocateFromPTEGDone		// bye

	/* Free PTE found.  r3 == PTE address
	*/
FoundFreePTE:
	oris	r4, r4, PTE_validMaskS
	stw	r5, PTE.attributesWord(r3)
	sync
	stw	r4, PTE.hashInfoWord(r3)

	/* Common exit path
	   r3 == PTE address or nil
	*/
AllocateFromPTEGDone:

		blr


/* -----------------------------------------------------------------------------------
 LookupExistingPTEfromVSID.

 Inputs:	r3 == VSID.
			r4 == logical address
 Outputs:	same as LookupExistingPTE

 NOTE: This routine is called with interrupts and data relocation disabled.
-------------------------------------------------------------------------------------
*/

LookupExistingPTEfromVSID:


	/* Use gSDR1 and specified VSID for theSpace.
	   r3 == VSID, r4 == logical address
	*/
	lwz	r8, .gSDR1(rtoc)		// r8 = memory system's SDR1
	lwz	r8, 0(r8)
	b	HaveMMUInformation



/* -----------------------------------------------------------------------------------
 LookupExistingPTE.

 Inputs:	r3 == address space description or nil (meaning current)
			r4 == logical address
 Outputs:	r3 == PTE address or nil
			r4 == Iff r3 is nil: OverflowPTE address, or nil
			r12 == msr value to restore when out of critical section
 Changes:	r3 - r10, r12
			Exits in hash table critical section

 Return the address of the PageTableEntry or OverflowPTE that map the specified
 logical address.  A nil AddressSpace specifies the current MMU mappings.

 NOTE: This routine is called with interrupts and data relocation disabled.
 -------------------------------------------------------------------------------------
*/

LookupExistingPTE:

	/* Check whether caller specified a particular space, or current mappings
	*/
	cmpwi	r3, 0				// theSpace specified?
	bne	UseSpecifiedSpace		// if so, go use it

	/* Use gSDR1 and SegmentRegister from current MMU state.
	*/
	mfsrin	r3, r4				// r3 = SegmentRegister value
	mfsdr1	r8				// r8 = SDR1
	b	HaveMMUInformation

	/* Use gSDR1 and SegmentRegister for theSpace.
	   r3 == AddressSpace pointer, r4 == logical address.
	*/
UseSpecifiedSpace:
	lwz	r3, OpaqueAddressSpacePtr.mmuInfo+MMUSpaceDesc.segmentRegisters(r3)
	rlwinm	r5, r4, EA_segmentNumberShiftOut+2, (EA_segmentNumberMaskOut << 2)	// extract and multiply segment number
	add	r3, r3, r5			// make byte offset into register file
	lwz	r3, 0(r3)			// r3 = SegmentRegister value
	lwz	r8, .gSDR1(rtoc)		// r8 = memory system's SDR1
	lwz	r8, 0(r8)

	/* Calculate value to match with entry's hashInfoWord
	   ((1 << 31) | (segment ID << 7) | (theHashType << 6) | api)
	   r3 == SegmentRegister value, r4 == logical address, r8 == SDR1
	*/
HaveMMUInformation:
	rlwinm	r6, r3, SR_segmentIDShiftOut+7, (SR_segmentIDMaskOut << 7)		// extract, rotate segment ID
	rlwinm	r5, r4, EA_apiShiftOut, EA_apiMaskOut	// extract abbreviated page index
	or	r5, r6, r5				// combine segment ID and api
	oris	r10, r5, PTE_validMaskS			// r10 = hashInfoWord to match

	/* Calculate hash values.
	*/
	rlwinm	r5, r4, EA_virtualPageShiftOut+kLog2PTEGSize, (EA_virtualPageMaskOut << kLog2PTEGSize)	// calculate (virtual page ID * kPTEGSize)
	rlwinm	r6, r3, SR_VSIDShiftOut+kLog2PTEGSize, (SR_VSIDMaskOut << kLog2PTEGSize)		// calculate (19-bit-VSID * kPTEGSize)
	xor	r3, r5, r6			// calculate primary hash value
	not	r5, r3				// calculate secondary hash value

	/* Turn off interrupts and data translation
	*/
	BeginHashTableSection r4, r6, r7

	/* Calculate PTEG address from hash value and SDR1
	   r3 == primary hash value, r5 == secondary hash value,
	   r8 == SDR1, r10 == hashInfoWord to match.
	*/
HaveHashValue:
	rlwinm	r6, r8, SDR1_HTABmaskShiftOut+SDR1_HTABorgShiftIn, (SDR1_HTABmaskMaskOut << SDR1_HTABorgShiftIn)			// extract table mask and ...
	ori	r6, r6, ((-SDR1_HTABorgMask-1) & -kPTEGSize)	// ... ones-fill to make mask
	and	r3, r3, r6					// mask the hash value

	rlwinm	r6, r8, 0, SDR1_HTABorgMask	// isolate physical base of hash table
	or	r3, r6, r3			// sum base and masked hash value to get PTEG

	/* Search the PTEG for matching entry
	   r3 == PTEG address, r10 == hashInfoWord to match.
	*/
SearchPTEGLoop:
	lwz	r6, PTE.hashInfoWord(r3)
	cmplw	r6, r10				// check this PTE
	beq	PTELookupDone			// if matched, go return it
	addi	r3, r3, PTE.sizeof		// move to next PTE in PTEG
	andi.	r6, r3, kPTEGSize-1		// off end of PTEG?
	bne	SearchPTEGLoop			// if not, go check this PTE

	andi.	r6, r10, PTE_hashTypeMask	// tried both PTEGs now?
	bne	LookupOverflowPTE		// if so, go try overflow

	subi	r9, r3, kPTEGSize		// remember primary PTEG address

	/* Calculate secondary hash value
	*/
	ori	r10, r10, PTE_hashTypeMask	// switch match for secondary PTEG
	mr	r3, r5				// get secondary hash value
	b	HaveHashValue			// and retry

	/* PTE not in main hash table, try the overflow table
	   r3 == secondary PTEG address + PTEGSize, 
	   r9 == primary PTEG address,
	   r10 == hashInfoWord to match
	*/
LookupOverflowPTE:
	subi	r8, r3, kPTEGSize		// remember secondary PTEG address
	rlwinm	r10, r10, 0, ~PTE_hashTypeMask	// use hash type for primary PTEG
	rlwinm	r6, r10, PTE_apiShiftOut+2, (PTE_apiMaskOut << 2)	// multiply API by sizeof(OverflowPTE *)
	addi	r5, r6, .gOverflowHashTable	// sum base and offset to get list head
TryAgain:
	lwz	r4, 0(r5)			// get first OverflowPTE in list
SearchOverflowLoop:
	cmpwi	r4, 0				// nil OverflowPTE pointer?
	beq	OverflowLoopNotFound		// if so, nothing found
	lwz	r6, OverflowPTE.ptegAddress(r4)	// get PTEG from OverflowPTE
	cmplw	r6, r9				// matching PTEG address?
	bne	ContinueOverflowLoop		// if not, continue loop
	lwz	r6, OverflowPTE.pteValue+PTE.hashInfoWord(r4)
	cmplw	r6, r10				// matching hash information?
	beq	OverflowLoopDone		// if so, found OverflowPTE!
ContinueOverflowLoop:
	lwz	r4, OverflowPTE.pNextOverflowPTE(r4)
	b	SearchOverflowLoop

OverflowLoopNotFound:
	cmplw	r8, r9				// tried secondary already?
	mr	r9, r8				// switch to secondary PTEG
	ori	r10, r10, PTE_hashTypeMask	// switch match for secondary PTEG
	bne	TryAgain			// go look again

	/* Overflow loop exited one way or the other.
	   r4 == OverflowPTE address, or nil.
	*/
OverflowLoopDone:
#ifdef  DEBUG
	cmpwi	r4, 0
	beq	DoneIncOverflowReloadCount
	lwz	r3, .gOverflowReloadCount(0)
	addi	r3, r3, 1
	stw	r3, .gOverflowReloadCount(0)
DoneIncOverflowReloadCount:
#endif	/* DEBUG */
	li	r3, 0				// make nil PTE address

	/* Common exit.  
	   r3 == PTE address, or nil.  
	   If nil, r4 == OverflowPTE address, or nil.
	*/
PTELookupDone:

	blr

/* -----------------------------------------------------------------------------------
 GeneratePTE.

 Inputs:	r3 == theLogicalAddress
 Outputs:	r3 == primary PTEG address, or nil
			r4 == primary PTE hash information word (with pteValid == 0)
			r5 == PTE page attributes word
			r6 == secondary PTEG address
 Changes:	r3 - r7

 Reconstitute a PageTableEntry for the specified logical address in the current
 address space, based on the AutogenArea list.

 NOTE: This routine is called with interrupts and data relocation disabled.
 -------------------------------------------------------------------------------------
*/

GeneratePTE:

	/* Locate AutogenArea element for the specified logical address
	*/
	lwz	r7, .gAutogenAreaList(0)
GeneratePTELoop:
	cmpwi	r7, 0
	beq	CantGenerate
	lwz	r4, AutogenArea.lowestBackedAddress(r7)
	cmplw	r4, r3				// lowest <= theLogicalAddress?
	bgt	ContinueGeneratePTELoop		// if not, continue
	lwz	r4, AutogenArea.highestBackedAddress(r7)
	cmplw	r3, r4				// theLogicalAddress <= highest?
	ble		FoundAutogenArea	// if so, we can generate the PTE
ContinueGeneratePTELoop:
	lwz	r7, AutogenArea.pNextAutogenArea(r7)
	b	GeneratePTELoop

	/* Found an autogen area for address.  Carry on.
	   r3 == logical address, r7 == address of autogen area
	*/
FoundAutogenArea:

	/* Generate hash information word
	   r3 == logical address
	   ((1 << 31) | (segment ID << 7) | (theHashType << 6) | api)
	*/
	mfsrin	r4, r3						// get segment register value
	rlwinm	r4, r4, PTE_segmentIDShiftIn, PTE_segmentIDMask	// extract, rotate segment ID
	rlwinm	r5, r3, EA_apiShiftOut, EA_apiMaskOut		// extract abbreviated page index
	or	r4, r4, r5					// combine segment ID and api
	oris	r4, r4, PTE_validMaskS				// turn on pteValid

	/* Generate page attributes word
	*/
	lwz	r5, AutogenArea.lowestBackedAddress(r7)
	sub	r5, r3, r5
	rlwinm	r5, r5, 0, kPageAlignMask
	lwz	r6, AutogenArea.physicalBase(r7)
	add	r5, r5, r6
	lwz	r6, AutogenArea.pageProtection(r7)
	rlwimi	r5, r6, PTE_protectionShiftIn, PTE_protectionMask
	lwz	r6, AutogenArea.pageCacheMode(r7)
	rlwimi	r5, r6, PTE_cacheControlShiftIn, PTE_cacheControlMask

	/* Calculate PTEG addresses
	*/
	mfsrin	r7, r3				// get segment register value
	rlwinm	r6, r3, EA_virtualPageShiftOut+kLog2PTEGSize, (EA_virtualPageMaskOut << kLog2PTEGSize)	// calculate (virtual page ID * kPTEGSize)
	rlwinm	r7, r7, SR_VSIDShiftOut+kLog2PTEGSize, (SR_VSIDMaskOut << kLog2PTEGSize)		// calculate (19-bit-VSID * kPTEGSize)
	xor	r3, r6, r7			// calculate primary hash value
	not	r6, r3				// calculate secondary hash value
	mfsdr1	r7				// get hash table register
	rlwinm	r7, r7, SDR1_HTABmaskShiftOut+SDR1_HTABorgShiftIn, (SDR1_HTABmaskMaskOut << SDR1_HTABorgShiftIn)			// extract table mask and ...
	ori	r7, r7, ((-SDR1_HTABorgMask-1) & -kPTEGSize)	// ... ones-fill to make mask
	and	r3, r3, r7					// mask the primary hash value
	and	r6, r6, r7					// mask the secondary hash value

	mfsdr1	r7				// get hash table register
	rlwinm	r7, r7, 0, SDR1_HTABorgMask	// isolate physical base of hash table
	or	r3, r7, r3			// r3 = primary PTEG address
	or	r6, r7, r6			// r6 = secondary PTEG address
#ifdef  DEBUG
	lwz	r7, .gAutogenReloadCount(0)
	addi	r7, r7, 1
	stw	r7, .gAutogenReloadCount(0)
#endif
	b	GeneratePTEDone

CantGenerate:
	li	r3, 0

GeneratePTEDone:
	blr

#endif /* WIP ================================================================== */


/* -----------------------------------------------------------------------------------
 LookupPTE

 Inputs:	r3 == address space description or nil
		r4 == theLogicalAddress
 Outputs:	r3 == thePTE
		r4 == zero iff page was in main hash table
		r12 == value for EndHashTableSection to restore msr
 Changes:	r0, r3 - r12
		msr = hardware interrupts and data translation disabled

 Map, if possible, the specified current logical address into the main hash table by
 either bringing it in from the overflow hash table, or generating the PTE on-the-fly.

 Exits still inside the hash table critical section so caller can access the returned
 PTE address.  While in the critical section, hardware interrupts and data relocation
 are disabled.  Caller must EndHashTableSection when done with the PTE.

 NOTE: When called with r3 == nil, this routine must assume data translation is
 already disabled.
 -------------------------------------------------------------------------------------
*/
LookupPTE:

	/*	Temp stub. Indicate we didn't find a PTE in the overflow
		list to propagate the exception upward.
	*/
	li	r4, kernelInUseErr		// status saying no reload
	blr

#if 0 /* WIP ================================================================== */

	/* save link register so we can make calls.
	*/
	mflr	r0

	/* Check the hash table and overflow table.  Return error if the page is
	   in the main hash table, because the exception must be for some other
	   problem, like access level violation.
	*/
	mr	r11, r4									// r11 = logical address

	bl	LookupExistingPTE		// locate PTE or OverflowPTE
	cmpwi	r3, 0									// found PTE?
	beq	TryOverflow								// no, what about OverflowPTE?

	li	r4, kernelInUseErr		// status saying no reload
	b	LookupPTEDone			// return PTE address and status

	/* No overflow means we can try generating the PTE on-the-fly
	*/
TryOverflow:
	cmpwi	r4, 0									// found OverflowPTE?
	beq	TryGeneratingPTE		// if not, go try making PTE

	/* Retrieve mapping information from, and free, the OverflowPTE
	*/
	lwz	r8, OverflowPTE.ptegAddress(r4)
	lwz	r9, OverflowPTE.pteValue+PTE.hashInfoWord(r4)
	lwz	r10, OverflowPTE.pteValue+PTE.attributesWord(r4)
	mr	r3, r4
	bl	DeallocateOverflowPTE

	mr	r7, r8									// PTEG address for overflow
	mr	r4, r9									// hash information word
	b	ReloadIt

	/* Not in main hash table or in overflow table.  
	   Try to generate a PTE.
	*/
TryGeneratingPTE:
	mr	r3, r11									// pass logical address
	bl	GeneratePTE
	cmpwi	r3, 0									// check result
	bne	GenerateWorked

	li	r3, 0									// nil PTE address
	li	r4, kernelAlreadyFreeErr	// status saying no PTE at all
	b	LookupPTEDone			// bye

	/* PTE was generated.  Try primary PTEG.  Fall back to secondary PTEG.
	   r3 == primary PTEG, r4 == hash information, r5 == attributes, 
	   r6 == secondary PTEG
	*/
GenerateWorked:
	mr	r7, r3									// keep PTEG address for overflow
	mr	r8, r6									// keep secondary PTEG address for retry
	mr	r9, r4									// keep hash information word for retry
	mr	r10, r5									// keep attributes word for retry
	bl	AllocateFromPTEG
	cmpwi	r3, 0
	bne	ReloadWorked

	ori	r4, r9, PTE_hashTypeMask	// hash information word for secondary PTEG
	rlwinm	r9, r9, 0, ~PTE_hashTypeMask	// hash information word for overflow

	/* Search specified PTEG.
	   r8 == PTEG to check, 
	   r4 == PTE hash information word, 
	   r10 == PTE page attribute word
	   r7 == PTEG address for overflow,
	   r9 == PTE hash information word for overflow.
	*/
ReloadIt:
	mr	r3, r8									// pass PTEG address
	mr	r5, r10									// pass attributes word
	bl	AllocateFromPTEG
	cmpwi	r3, 0									// check result
	bne	ReloadWorked			// if it worked, we're done

	/* No room in primary or secondary PTEG.
	   Select an existing PTE to swap out from the primary PTEG.
	*/
	lwz	r4, .gOverflowPTEGSeed(0)	// get current seed value
	addi	r3, r4, 1								// increment it
	cmpwi	r3, kPTEsPerPTEG		// check for wrap
	blt	SetNewSeed								// if none, store value
	li	r3, 0									// else cut back to zero
SetNewSeed:
	stw	r3, .gOverflowPTEGSeed(0)	// store updated seed value
	slwi	r4, r4, kLog2PTESize		// multiply seed by element size
	add	r8, r7, r4								// r8 = PTE address to swap out

#if 0
/*
!!! Don't do this until I/O system agrees with implied restrictions.  !!!

 Use the PTE we located.  Don't waste an OverflowPTE on PTE that can be
 regenerated instead.  The address needs to be with an AutogenArea,
 and the page's cache mode must be the same as the area's.
 NOTE: Because the AutogenArea lookup is based on the physical address,
 the cache mode check might be inaccurate if there are more than one
 AutogenArea containing that physical address.  Therefore, SetProcessorCacheMode
 disallows AutogenArea cache mode changes if there are any aliased pages, and
 we are safe.  The idea is that there should be an area per cache mode for
 AutogenArea-type areas.
 r7 == PTEG address, r8 == PTE address
*/
 
	lwz	r3, PTE.attributesWord(r8)	// get attributes word
	rlwinm	r3, r3, 0, PTE_pageMask		// mask to physical address
	lwz	r5, .gAutogenAreaList(0)
IsAutogenAddressLoop:
	cmpwi	r5, 0
	beq	SaveInOverflowTable
	lwz	r4, AutogenArea.physicalBase(r5)
	cmplw	r4, r3									// physical base <= page address?
	bgt	ContinueIsAutogenAddressLoop	// if not, continue
	lwz	r4, AutogenArea.lowestBackedAddress(r5)
	lwz	r6, AutogenArea.highestBackedAddress(r5)
	sub	r6, r6, r4
	lwz	r4, AutogenArea.physicalBase(r5)
	add	r4, r4, r6
	cmplw	r3, r4									// page address <= highest?
	ble	IsAutogenAddress		// if so, r5 == the AutogenArea
ContinueIsAutogenAddressLoop:
	lwz	r5, AutogenArea.pNextAutogenArea(r5)
	b	IsAutogenAddressLoop

IsAutogenAddress:
	lwz	r4, PTE.hashInfoWord(r8)
	rlwinm	r3, r4, PTE_cacheControlShiftOut, PTE_cacheControlMaskOut
	lwz	r5, AutogenArea.pageCacheMode(r5)	// get area's cache mode
#ifdef  DEBUG
	cmplw	r3, r5
	bne	SaveInOverflowTable
	lwz	r4, .gAutogenTossCount(0)
	addi	r4, r4, 1
	stw	r4, .gAutogenTossCount(0)
#endif /* DEBUG */
	cmplw	r3, r5									// matching cache mode?
	beq	SwapOutPTE								// if so, we can regenerate the PTE

	/* Copy the PTE's contents into an element in the overflow table.
	   r7 == PTEG address, r8 == PTE address.
	*/
SaveInOverflowTable:
#endif /* 0 */

#ifdef  DEBUG
	lwz	r3, .gOverflowTossCount(0)
	addi	r3, r3, 1
	stw	r3, .gOverflowTossCount(0)
#endif /* DEBUG */
	mr	r3, r7									// pass PTEG address
	lwz	r4, PTE.hashInfoWord(r8)	// pass hash information word
	lwz	r5, PTE.attributesWord(r8)	// pass attributes word
	bl	AllocateOverflowPTE

	/* Map the new page in place of the old.  A tlbie is not necessary 
	   because no effective-to-physical mapping is being undone.  Rather, 
	   the PTE is just moving to the overflow list (i.e. the tlb information 
	   still applies).  Changes to the tlb entry happen only as a result of a 
	   page fault (e.g. setting the "modified" bit), so tlb cast out has no 
	   requirement that the PTE be in the main hash table.
	   r8 == PTE, 
	   r9 == hash information word, 
	   r10 == attributes
	*/
SwapOutPTE:
	li	r3, 0									// zero word
	stw	r3, PTE.hashInfoWord(r8)	// invalidate the PTE
	sync										// synchronize
	stw	r10, PTE.attributesWord(r8)	// store new attributes
	sync										// synchronize
	stw	r9, PTE.hashInfoWord(r8)	// store new hash information
	mr	r3, r8									// r3 == PTE address

	/* Page was successfully mapped
	   r3 == PTE address or nil
	*/
ReloadWorked:
	li	r4, noErr								// status saying reload happened

	/* r3 == PTE address or nil, 
	   r4 == noErr iff reload happened
	*/
LookupPTEDone:

	mtlr	r0
	blr

#endif /* WIP ================================================================== */

/* ------------------------------------------------------------------------------
*/





	.globl _ExceptionVectorsEnd
_ExceptionVectorsEnd:	/* Used if relocating the exception vectors */



/*	The entirety of the lowmem area has to fit in the space assigned to it.
	The kernel start point is defined in to places: the makefile.ppc and
	MASTER.ppc files.
*/
#if	(_ExceptionVectorsEnd - _ExceptionVectorsStart) > RELOC
#warning **** lowmem overflows into kernel space ****
#endif