/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (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.
 * 
 * This 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@
 */
/*
 * @OSF_COPYRIGHT_INTERNAL_USE_ONLY@
 */

/* 																							
 	MP_2p.s 

	MP low-level signaling, configuration, et all.  This is for a and Apple/Daystar 2p board

	Lovingly crafted by Bill Angell using traditional methods

*/

#include <ppc/asm.h>
#include <ppc/proc_reg.h>
#include <ppc/POWERMAC/mp/MPPlugIn.h>
#include <assym.s>
#include <mach/machine/vm_param.h>



			.set	MPPlugInVersion,0						/* Current version code */

/* */
/*			Interfaces to hardware */
/* */

			.set	PCI1ARdisp,		0x00800000				/* Displacement from Bandit to PCI1 address configuiration register */
			.set	GrandCdisp,		0x01000000				/* Displacement from Bandit to Grand Central */
			.set	EventsReg,		0x20					/* Interruption events register (latched) */
			.set	LevelsReg,		0x2C					/* Interruption levels register (unlatched) */
			.set	MaskReg,		0x24					/* Interruption mask register */
			.set	ClearReg,		0x28					/* Interruption clear register */
			.set	TicksPerMic,	11						/* We'll use 11 ticks per µS - 120MHz is really 10, 180MHz is 11.24 */
			.set	EtherNRdisp,	0x01019000				/* Displacement into bandit of EtherNet ROM */

#ifdef	__ELF__
			.section ".data"
#else
			.data
#endif

			.align	5									/* Get us out to the end */

			.globl MPPIwork
#ifdef __ELF__
			.type  MPPIwork,@function
#endif

MPPIwork:
MPPIstatus:	.byte	0									/* Global MP board status */
			.set	MPPIinit,	0x80					/* Global initialization complete */
			.set	MPPI2Pv2,	0x40					/* Second rev of 2P board (no watchdog and different state machine) */
			.byte	0									/* Reserved */
MPPIinst:	.byte	0									/* Mask of CPUs installed */
MPPIonline:	.byte	0									/* Mask of CPUs online (i.e., initialized) */
MPPIlogCPU:	.long	0									/* Used to configure CPU addresses */
MPPITBsync:	.long	0									/* Used to sync time bases */
			.long	0
MPPIHammer:	.long	0									/* Address of HammerHead */
MPPIGrandC:	.long	0									/* Address of GrandCentral */
MPPIPCI1Adr: .long	0									/* Address of PCI1's config reg addr */
MPPIEther:	.long	0									/* Address of EtherNet ROM */
			
			.align	5
MPPISncFght: .fill	4,4,0								/* Space for 9 passes of a TB sync fight + 1 guard pass */
			.fill	4,4,0
			.fill	4,4,0
			.fill	4,4,0
			.fill	4,4,0
			.fill	4,4,0
			.fill	4,4,0
			.fill	4,4,0
			.fill	4,4,0
			.fill	4,4,0
 			.align	7									/* Point to the start of the CPU status */

			.globl EXT(MPPICPUs)
#ifdef __ELF__
			.type  EXT(MPPICPUs),@function
#endif
EXT(MPPICPUs):												/* Start of Processor specific areas */
/*			There are 8 of these indexed by processor number */


MPPICPU0:	.fill	8,4,0									/* First processor */
MPPICPU1:	.fill	8,4,0									/* Second processor */
MPPICPU2:	.fill	8,4,0									/* Third processor */
MPPICPU3:	.fill	8,4,0									/* Fourth processor */
			.set	MPPIMaxCPU, (.-EXT(MPPICPUs)-32)/32			/* Get the maximum CPU address */


			.text

/******************************************************************************************************** */
/******************************************************************************************************** */
/* */
/*			Here starteth ye stuff */
/* */
/******************************************************************************************************** */
/******************************************************************************************************** */

/******************************************************************************************************** */
/* */
/*			Validate that the hardware matches with our code.  At this point, we cannot check */
/*			for anything other than the possibility of this working.  There's no version code */
/*			or nothin'.  So, if we have a second processor and are a 604 or 604e, we'll say */
/*			we're capable.  Also we'll check version codes for our code. */
/* */
/*			When we get here, DDAT and IDAT are both on, 'rupts are disabled. */
/*  */
/*			We're called like this:  */
/*				OSStatus MP_probe(MPPlugInSpecPtr spec, UInt32 HammerheadAddr); */
/* */
/******************************************************************************************************** */

ENTRY(MPprobe, TAG_NO_FRAME_USED)

			
MPPIbase:	mfpvr	r7									/* Get the processor version */
			rlwinm	r7,r7,16,16,31						/* Isolate the processor type */

			lbz		r5,ArbConfig(r4)					/* See if there is another processor */
			
			andi.	r5,r5,TwoCPU						/* Are we a real live two processor? */
			beq		OneWay								/* Nope, we be gone... */
			
			cmplwi	cr0,r7,4							/* Are we a 604? */
			beq		SeemsOK								/* Yeah, we're cool... */
			cmplwi	cr0,r7,9							/* Are we a 604E? */
			beq		SeemsOK								/* Yeah, go finish up... */
			
OneWay:		li		r3,0								/* Say we can't find the proper CPU */
			blr											/* Leave... */
			
SeemsOK:	mr		r10,r3								/* Save the parameter list */
			
			lwz		r4,MPSversionID(r10)				/* Get the version ID */
			cmplwi	cr0,r4,kMPPlugInVersionID			/* Correct version? */
			beq		IsOK								/* Yeah, we think we're ok... */
			
			li		r3,0								/* Set bad version' */
			blr											/* Leave... */
			
IsOK:		mflr	r11									/* Save the LR */
			lis		r9,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			bl		SetBase1							/* Jump to the next instruction */
SetBase1:	mflr	r12									/* Get the base register */
			ori		r9,r9,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			addi	r12,r12,LOW_ADDR(MPPIbase-SetBase1)	/* Adjust to the start of all our code */
			
			stw		r12,MPSbaseAddr(r10)				/* Save off the common base for all functions */
			
			la		r5,LOW_ADDR(MPPIFunctions-MPPIbase)(r12)	/* Point to the base of all functions */
			stw		r5,MPSareaAddr(r10)					/* Pass back the code address */
			
			la		r5,LOW_ADDR(MPPIFuncOffs-MPPIbase)(r12)	/* Point to the function offset table */
			stw		r5,MPSoffsetTableAddr(r10)			/* Pass back the pointer to the offset table */
			
			li		r5,LOW_ADDR(MPPISize-MPPIFunctions)	/* Get our size without data area */
			stw		r5,MPSareaSize(r10)					/* Save it */
			
			stw		r9,MPSdataArea(r10)					/* Save it */
			
			la		r5,LOW_ADDR(EXT(MPPICPUs)-MPPIwork)(r9)	/* Point to the CPU area base */
			stw		r5,MPSCPUArea(r10)					/* Save it */
			
			mtlr	r11									/* Restore that return address */
			li		r3,1								/* Set no error */
			blr											/* Leave, we're all done... */

/******************************************************************************************************** */
/******************************************************************************************************** */
/* */
/*			Here starteth ye code that starteth up ye second prothether. */
/*			Yea, though ye prothether executeth asynchronously, it appears unto men */
/*			in ye shape of a synchronous process.  By ye instruction of He who gave it */
/*			form and being, it stopeth to worship and praise its Lord, to joyously  */
/*			receive His blessings and teachings, to guide its way along the path to */
/*			righteous execution. */
/* */
/******************************************************************************************************** */
/******************************************************************************************************** */


/******************************************************************************************************** */
/* */
/*			Initialize the MP hardware.  This will bring the other processor online. */
/* */
/*			First we will tick the board to its 5th state the "TBEN off" state. */
/* */
/*			Just for giggles, here's the states: */
/* */
/*			1) 1st ROM			- This state exists after motherboard reset */
/*			2) Open Firmware	- Transitions here when the SecInt line is first asserted */
/*								  Open Firmware attempts to execute some code on the secondary */
/*								  processor to obtain the PVR register.  It's got some problems */
/*								  and hangs the secondary disabled. */
/*			3) Reset (my name)	- Entered when the SecInt line is deasserted. A timer starts and */
/*								  468µS later the reset line is pulled.  I may have this wrong here, */
/*								  it may be that the reset line is held for 468µS.  Either way, */
/*								  this state is invisible to us. */
/*			4) 2nd ROM			- This state exists when the secondary processor begins executing */
/*								  after the reset. */
/*			5) TBEN off			- We transition here when SecInt is asserted in the 2nd ROM state. */
/*								  In this state, the TBEN pin is set to disable the timebase from  */
/*								  running on all processors, thus freezing time. (Performace analysis */
/*								  note: here would be the best time to run stats, all tests would */
/*								  run in 0 time giving us infinite speed.) Also the "primary arbitration" */
/*								  mode is set.  This mode causes the CPU board to arbitrate both processors */
/*								  using a single bus master.  This gets us around the L2 cache dumbness. */
/*								  We should also note that because of this, there is now no way to  */
/*								  tell if we are on the secondary processor, the WhoAmI register will */
/*								  always indicate the primary processor.  We need to have sewn */
/*								  name tags into our underwear before now. */
/*								  Finally, this state is the only way we can tell if we are executing */
/*								  on the older version of the 2-way board.  When it is in this state */
/*								  "primary arbitration" has not been enabled yet.  The WhoAmI register */
/*								  will indicate if we are on the secondary processor on not.  We should */
/*								  check this because we need to do signals differently. */
/*			6) TBEN on			- The next assertion of SecInt brings us to our final destination.  For */
/*								  those of you who will be deplaning, please remember that timebases */
/*								  are running and primary arbitration is enabled.  Always remember: */
/*								  buckle up for safety and if you're tired pull over for a rest. */
/* */
/******************************************************************************************************** */

ENTRY(MPinstall, TAG_NO_FRAME_USED)

/*			int MP_install(unsigned int *physAddr, unsigned int band1, unsigned int hammerh, unsigned int grandc,
 *			unsigned int pci1ar, unsigned int enetr);
 */
	
			lis		r11,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			mflr	r0									/* Save the LR */
			ori		r11,r11,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			
			stw		r5,MPPIHammer-MPPIwork(r11)			/* Save the HammerHead address for later */
			stw		r6,MPPIGrandC-MPPIwork(r11)			/* Save address of Grand Central */
			stw		r7,MPPIPCI1Adr-MPPIwork(r11)		/* Save the PCI1 address register address */
			stw		r8,MPPIEther-MPPIwork(r11)			/* Save Ethernet ROM address */

			li		r4,LOW_ADDR(0xC080)					/* Set CPU 0&1 installed, CPU 0 online */
			lis		r10,(MPPICOnline+MPPICReady)>>16	/* Set CPU 0 online and ready */
			
			mfspr	r6,pir								/* Get the PIR contents */
			
			sth		r4,MPPIinst-MPPIwork(r11)			/* Set 'em for later */
			rlwinm	r6,r6,0,0,27						/* Clear to use processor 0 */
			stw		r10,EXT(MPPICPUs)-MPPIwork(r11)		/* Preset CPU 0 online and ready */

			mtspr	pir,r6								/* Set our PIR */

/* */
/*			Ok, ok, enough of this.  Let's really start 'em up. */
/* */

			lis		r9,HIGH_ADDR(CPUInit)				/* Top of init code */
			li		r6,1								/* Get the other guy's CPU address		 */
			ori		r9,r9,LOW_ADDR(CPUInit)				/* Get physical address of init code */
		
			mfmsr	r8									/* Get the MSR */

			stw		r6,MPPIlogCPU-MPPIwork(r11)			/* Set the logical CPU address to assign */
						
			rlwinm	r6,r8,0,17,15						/* Turn off interruptions */
			sync										/* Make sure the work area is updated */
			mtmsr	r6									/* Flip the EE bit off */
			isync										/* Chill a bit */
			
			stw		r9,0(r7)							/* Pass the initialization code address to our friend */
			sync										/* Fence off the pig */
			
			li		r6,0								/* Clear this out */
			stb		r6,IntReg(r5)						/* Kick the other processor */
			eieio										/* Pig in the sty */

/*			At this point we should be in the "TBEN off" state.  The second processor should be starting */
/*			to come up. */

/*			Note that we are assuming that the secondary processor will reset the interrupt request. */
/*			If we are on one of the old boards, we will die in about 256µS if it is not reset, 'cause */
/*			of that silly watchchihuahua timer.  We can't use the TB or decrimenter here to set a  */
/*			timeout because when we are in "TBEN off" state these guys don't run. */

			lis		r4,HIGH_ADDR(SpinTimeOut)			/* Get about 1 second at 200MHz */
														/* At 120 MHz this is 1.66 seconds, at 400MHz it is .5 */
														/* All these are more than enough time for this handshake */
			ori		r4,r4,LOW_ADDR(SpinTimeOut)			/* Get the bottom part */

WaitReady:	lwz		r9,0(r7)							/* Get this back */
			mr.		r9,r9								/* The other processor will set to 0 */
														/*    when it is ready for the work area address */
			beq		CodeUp								/* The code is up on the other side */
			subi	r4,r4,1								/* Count the try */
			mr.		r4,r4								/* Did we timeout? */
			bne+	WaitReady							/* Nope... */
			
			li		r3,kMPPInitTO1						/* Set that we timed out with initial code bringup */
			mtmsr	r8									/* Restore the interrupt state */
			mtlr	r0									/* Restore the return addess */
			blr											/* Return a failure... */

CodeUp:		isync										/* Make sure we don't prefetch past here */
			
/*			Timebase is stopped here, no need for the funky "get time base right" loop */

			mftbu	r4									/* Get upper timebase half */
			mftb	r9									/* Get bottom  */
			stw		r4,MPPITBsync-MPPIwork(r11)			/* Save the top */
			stw		r9,MPPITBsync+4-MPPIwork(r11)		/* Save the second half */
			sync										/* Be very sure it's there */
			
			stw		r11,0(r7)							/* Set the PCI1 adr reg non-zero - this releases the spin */
														/*	loop and allows the timebase to be set. */
			eieio

			lis		r9,HIGH_ADDR(SpinTimeOut)			/* Get the spin time */
			ori		r9,r9,LOW_ADDR(SpinTimeOut)			/* Get the bottom part */
			
WaitTBset:	lwz		r4,0(r7)							/* Get this back */
			mr.		r4,r4								/* When zero, the other guy's TB is set up */
			beq-	TBSetUp								/* She's'a all done... */
			subi	r9,r9,1								/* Count the try */
			mr.		r9,r9								/* Did we timeout? */
			bne+	WaitTBset							/* Nope... */
			
			li		r3,kMPPInitTO3						/* Set that we timed out setting clock */
			mtmsr	r8									/* Restore the interrupt state */
			isync
			mtlr	r0									/* Restore the return addess */
			blr											/* Return a failure... */

TBSetUp:	stb		r6,IntReg(r5)						/* Kick the other processor again */
														/* This will tick us to the next state */
			eieio
						
SpinDelay:	addi	r6,r6,1								/* Bump spin count (we finally are trashing R6) */
			cmplwi	cr0,r6,4096							/* Spun enough? */
			ble+	SpinDelay							/* Nope... */
			
			li		r6,SecInt							/* Set the interrupt bit */
			stb		r6,IntReg(r5)						/* Deassert the external signal */
/* */
/*			Ok, the other processor should be online in a spin waiting for a start signal from */
/*			us.  It should be in the reset state with no external interruptions pending.  There may */
/*			be a decrimenter pop waiting in the wings though. */
/* */
			
			lwz		r7,MPPIGrandC-MPPIwork(r11)			/* Point to GrandCentral */
			lwz		r4,MaskReg(r7)						/* Get the grand central mask register (note that this */
														/* is a little-endian area, but I'm too lazy to access it that way */
														/* so I'll document what it really should be, but, probably, it would */
														/* have been much, much easier just to code up the lwbrx and be done */
														/* with it rather than producing this monograph describing my alternate */
														/* access method that I really don't explain anyway. */
			ori		r4,r4,0x0040						/* Flip on bit 30 (hah, figure that one out).  This enables the */
														/* Ext10 interrupt which is connected to the MACE ethernet chip's */
														/* chip-select pin. */
			stw		r4,MaskReg(r7)						/* Stick it on back */
			eieio
			
			mtlr	r0									/* Get back the original LR */
			sync										/* Make sure all storage ops are done */
			mtmsr	r8									/* Restore the MSR */
			isync
			li		r3,kSIGPnoErr						/* Set that we worked jest fine and dandy */
			blr											/* Bye now... */

			.align	5
/******************************************************************************************************** */
/******************************************************************************************************** */
/* */
/*			This is where the individual SIGP function calls reside.  */
/*			Also, it is where we cram the second processor's initialization code wo'w we */
/*			can use physical addressing. */
/* */
/******************************************************************************************************** */
/******************************************************************************************************** */

MPPIFunctions:											/* Start of all externally called functions and interrupt handling code */


/******************************************************************************************************** */
/* */
/*			Count the number of processors.  This hardwires to 2 (or 1 if no secondary) */
/* */
/******************************************************************************************************** */

CountProcessors:
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			mfmsr	r9									/* Get the MSR */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			
			ori		r10,r9,0x0010						/* Turn on DDAT */
			
			lwz		r8,MPPIHammer-MPPIwork(r12)			/* Point to the HammerHead controller */
			
			mtmsr	r10									/* Turn on DDAT */
			isync										/* Kill speculation */

			li		r3,2								/* Assume we have them all */
			lbz		r5,ArbConfig(r8)					/* Check if we've seen a second processor */
			andi.	r5,r5,TwoCPU						/* Are we a real live two processor? */
			mtmsr	r9									/* Put back the DDAT */
			isync
			
			bnelr+										/* Yeah... */
			li		r3,1								/* Nope, set a count of 1 */
			blr											/* Leave, we're inadequate... */

/******************************************************************************************************** */
/* */
/*			Start up the selected processor (R3=processor; R4=physical start address; R5=pass-thru parm) */
/* */
/******************************************************************************************************** */
			
StartProcessor:

			mr		r7,r5								/* Copy pass-thru parameter */
			mfspr	r10,pir								/* Get our processor number */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			cmplw	cr0,r3,r10							/* Trying to start ourselves? */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			li		r3,kMPPHairyPalms					/* Set trying to do it to ourselves */
			beqlr-										/* Self abuse... */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			mflr	r11									/* Save the return address */
			add		r9,r9,r12							/* Point right at the entry */
			
SPretry:	lwarx	r5,0,r9								/* Pick up the status flags (MPPICStat) and reserve it */
			li		r3,kSIGPInterfaceBusyErr			/* Fill dead space and get busy return code */
			rlwinm.	r0,r5,0,0,0							/* Are we marked as busy? */
			lis		r6,MPPICOnline>>16					/* Get the online flag */
			bne-	ErrorReturn							/* Yeah, go leave, don't bother me now... */
			and.	r0,r5,r6							/* Are we online */
			li		r3,kMPPOffline						/* Set offline */
			beq-	ErrorReturn							/* Ain't online, ain't ready, buzz off... */
			li		r3,kMPPBadState						/* Set bad state */
			oris	r5,r5,(MPPICBusy>>16)&0x0000FFFF	/* Turn on the busy bit */
			
			stwcx.	r5,0,r9								/* Try to set busy */
			bne-	SPretry								
			
			ori		r6,r10,MPPICfStrt<<8				/* Put the Start function in front of the processor ID */
			rlwimi	r5,r6,0,16,31						/* Put these behind the status flags */
			stw		r4,MPPICParm0(r9)					/* Set the starting physical address parameter */
			stw		r7,MPPICParm2(r9)					/* Set pass-thru parameter */
			
			sync										/* Make sure it's all out there */
			b		KickAndGo							/* We're done now... */

/******************************************************************************************************** */
/* */
/*			Reset the selected processor (R3=processor).  You can't reset yourself or the primary. */
/*			We're gonna try, try real hard...  This is not for the faint-of-heart. */
/*			If there's ever any way to yank a reset line, we'll do it here. */
/* */
/******************************************************************************************************** */
			
ResetProcessor:
			mfspr	r10,pir								/* Get our processor number */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			rlwinm	r10,r10,0,28,31						/* Clean up the PIR */
			cmplw	cr0,r3,r10							/* Trying to start ourselves? */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			li		r3,kMPPHairyPalms					/* Set trying to do it to ourselves */
			beqlr-										/* Self abuse... */
			mr.		r9,r9								/* Trying to reset the primary?!?  Dude, that's insubordination!!!! */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			li		r3,kMPPInvalCPU						/* Say that that's a major offense */
			beqlr-										/* Bye now... */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)		/* Point into the proccessor control area		 */
			mflr	r11									/* Save the return address */
			add		r9,r9,r12							/* Point right at the entry */
			
			li		r4,16								/* Try for 16 times to get the busy lock */

RSlockS:	mftb	r6									/* Time stamp start */

RSlock:		lwarx	r5,0,r9								/* Pick up the status flags (MPPICStat) and reserve it */
			rlwinm.	r0,r5,0,2,2							/* Are we online */
			li		r3,kMPPOffline						/* Set offline */
			cmplwi	cr1,r5,0							/* Check for busy */
			beq-	ErrorReturn							/* Ain't online, ain't ready, buzz off... */
			bge+	cr1,RSnotBusy						/* Not busy, make it so... */
			
			mftb	r7									/* Stamp the time */
			sub		r7,r7,r6							/* Get elapsed time */
			rlwinm.	r7,r7,16,16,31						/* Divide ticks by microseconds (this is pretty darn "kinda-in-the-ballpark") */
			cmplwi	cr0,r7,TicksPerMic					/* See if we hit 65536µS yet */
			blt+	RSlock								/* Not yet... */
			
RSatmtCnt:	subi	r4,r4,1								/* Count the retries */
			mr.		r4,r4								/* Are we done yet? */
			bgt+	RSlockS								/* Start the lock attempt again... */
			
			li		r3,kMPPCantLock						/* Say we can't get the lock */
			b		ErrorReturn							/* Bye, dude... */
			
RSnotBusy:	rlwinm	r5,r5,0,0,15						/* Clear out the function and requestor */
			oris	r5,r5,(MPPICBusy>>16)&0x0000FFFF	/* Set busy */
			or		r5,r10,r5							/* Add in our processor */
			ori		r5,r5,MPPICfReset<<8				/* Set the reset function */
			stwcx.	r5,0,r9								/* Cram it back */
			bne-	RSatmtCnt							/* We lost the reservation... */
			b		KickAndGo							/* Try to send it across... */
			

/******************************************************************************************************** */
/* */
/*			Here we will try to resume execution of a stopped processor (R3=processor). */
/* */
/******************************************************************************************************** */
			
ResumeProcessor:
			mfspr	r10,pir								/* Get our processor number */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			cmplw	cr0,r3,r10							/* Trying to resume ourselves? */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			li		r3,kMPPHairyPalms					/* Set trying to do it to ourselves */
			beqlr-										/* Self abuse... */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			mflr	r11									/* Save the link register */
			add		r9,r9,r12							/* Point right at the entry */
			
RPretry:	lwarx	r5,0,r9								/* Pick up the status flags (MPPICStat) and reserve it */
			li		r3,kSIGPInterfaceBusyErr			/* Fill dead space and get busy return code */
			rlwinm.	r0,r5,0,0,0							/* Are we marked as busy? */
			lis		r6,MPPICOnline>>16					/* Get the online flag */
			bne-	ErrorReturn							/* Yeah, go leave, don't bother me now... */
			and.	r0,r5,r6							/* Are we online */
			li		r3,kMPPOffline						/* Set offline */
			lis		r6,MPPICReady>>16					/* Get the ready bit */
			beq-	ErrorReturn							/* Ain't online, ain't ready, buzz off... */
			and.	r0,r5,r6							/* Are we ready? */
			li		r3,kMPPNotReady						/* Set not ready */
			lis		r6,MPPICStop>>16					/* Get the stopped bit */
			beq-	ErrorReturn							/* Ain't ready, buzz off... */
			and.	r0,r5,r6							/* Are we stopped? */
			li		r3,kMPPNotStopped					/* Set not stopped */
			oris	r5,r5,(MPPICBusy>>16)&0x0000FFFF	/* Turn on the busy bit */
			beq-	ErrorReturn							/* Nope, not stopped, so how do we resume? */
			
			stwcx.	r5,0,r9								/* Try to set busy */
			bne-	RPretry
			
			ori		r6,r10,MPPICfResm<<8				/* Put the resume function in front of the processor ID */
			rlwimi	r5,r6,0,16,31						/* Put these behind the status flags */
			b		KickAndGo							/* We're done now... */



/******************************************************************************************************** */
/* */
/*			Here we will try to stop execution of a running processor (R3=processor). */
/* */
/******************************************************************************************************** */
			
StopProcessor:
			mfspr	r10,pir								/* Get our processor number */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			cmplw	cr0,r3,r10							/* Are we doing ourselves? */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			li		r3,kMPPHairyPalms					/* Set trying to do it to ourselves */
			beqlr-										/* Self abuse... */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			mflr	r11									/* Save the link register */
			add		r9,r9,r12							/* Point right at the entry */
			
PPretry:	lwarx	r5,0,r9								/* Pick up the status flags (MPPICStat) and reserve it */
			li		r3,kSIGPInterfaceBusyErr			/* Fill dead space and get busy return code */
			rlwinm.	r0,r5,0,0,0							/* Are we marked as busy? */
			lis		r6,MPPICOnline>>16					/* Get the online flag */
			bne-	ErrorReturn							/* Yeah, go leave, don't bother me now... */
			and.	r0,r5,r6							/* Are we online */
			li		r3,kMPPOffline						/* Set offline */
			lis		r6,MPPICReady>>16					/* Get the ready bit */
			beq-	ErrorReturn							/* Ain't online, ain't ready, buzz off... */
			and.	r0,r5,r6							/* Are we ready? */
			li		r3,kMPPNotReady						/* Set not ready */
			lis		r6,MPPICStop>>16					/* Get the stopped bit */
			beq-	ErrorReturn							/* Ain't ready, buzz off... */
			and.	r0,r5,r6							/* Are we stopped? */
			li		r3,kMPPNotRunning					/* Set not running */
			oris	r5,r5,(MPPICBusy>>16)&0x0000FFFF	/* Turn on the busy bit */
			bne-	ErrorReturn							/* Nope, already stopped, so how do we stop? */
			
			stwcx.	r5,0,r9								/* Try to set busy */
			ori		r10,r10,MPPICfStop<<8				/* Put the stop function in front of the processor ID */
			bne-	PPretry
			
			rlwimi	r5,r10,0,16,31						/* Put these behind the status flags */
			b		KickAndGo							/* We're done now... */


/******************************************************************************************************** */
/* */
/*			Here we will try to signal a running processor (R3=processor). */
/*			Note that this should have good performace.  Well, actually, seeing as how slow we really are, it */
/*			probably is moot anyhow. */
/*			Another note: this function (and all most others as well) will return a timeout when the  */
/*			second processor tries to do itself on the old version of the board.  This happens because */
/*			In order to keep the watchchihuahua from popping (just imagine the scene: that little runt-dog just so */
/*			excited that its veins and eyes bulge and then explode) signaling to the secondary  */
/*			is done syncronously and disabled.  If the secondary signals the secondary, it will never enable so */
/*			it will never see the 'rupt, so it will never clear it, so it will time out, so there... */
/* */
/******************************************************************************************************** */
			
SignalProcessor:
			mfspr	r10,pir								/* Get our processor number */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			mflr	r11									/* Save the link register */
			add		r9,r9,r12							/* Point right at the entry */
			
SiPretry:	lwarx	r5,0,r9								/* Pick up the status flags (MPPICStat) and reserve it */
			li		r3,kSIGPInterfaceBusyErr			/* Fill dead space and get busy return code */
			rlwinm.	r0,r5,0,0,0							/* Are we marked as busy? */
			lis		r6,MPPICOnline>>16					/* Get the online flag */
			bne-	ErrorReturn							/* Yeah, go leave, don't bother me now... */
			and.	r0,r5,r6							/* Are we online */
			li		r3,kMPPOffline						/* Set offline */
			lis		r6,MPPICReady>>16					/* Get the ready bit */
			beq-	ErrorReturn							/* Ain't online, ain't ready, buzz off... */
			and.	r0,r5,r6							/* Are we ready? */
			li		r3,kMPPNotReady						/* Set not ready */
			oris	r5,r5,(MPPICBusy>>16)&0x0000FFFF	/* Turn on the busy bit */
			beq-	ErrorReturn							/* Ain't ready, buzz off... */
			
			stwcx.	r5,0,r9								/* Try to set busy */
			ori		r10,r10,MPPICfSigp<<8				/* Put the SIGP function in front of the processor ID */
			bne-	SiPretry
			
			stw		r4,MPPICParm0(r9)					/* Pass along the SIGP parameter */
			
			rlwimi	r5,r10,0,16,31						/* Put these behind the status flags */
			b		KickAndGo							/* We're done now... */


/******************************************************************************************************** */
/* */
/*			Here we will store the state of a processor (R3=processor; R4=status area). */
/*			Self abuse will store the state as is, is not asynchronous, and grows hair on your palms. */
/* */
/******************************************************************************************************** */
			
StoreProcessorStatus:
			mfspr	r10,pir								/* Get our processor number */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			cmplw	cr0,r3,r10							/* Saving our own state???  Abusing oneself??? */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			mflr	r11									/* Save the link register */
			beq		Flagellant							/* Oh baby, oh baby... */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			add		r9,r9,r12							/* Point right at the entry */
			
SSretry:	lwarx	r5,0,r9								/* Pick up the status flags (MPPICStat) and reserve it */
			li		r3,kSIGPInterfaceBusyErr			/* Fill dead space and get busy return code */
			rlwinm.	r0,r5,0,0,0							/* Are we marked as busy? */
			lis		r6,MPPICOnline>>16					/* Get the online flag */
			bne-	ErrorReturn							/* Yeah, go leave, don't bother me now... */
			and.	r0,r5,r6							/* Are we online */
			li		r3,kMPPOffline						/* Set offline */
			beq-	ErrorReturn							/* Ain't online, buzz off... */
			oris	r5,r5,(MPPICBusy>>16)&0x0000FFFF	/* Turn on the busy bit */
			
			stwcx.	r5,0,r9								/* Try to set busy */
			ori		r10,r10,MPPICfStat<<8				/* Put the store status function in front of the processor ID */
			bne-	SSretry								/* Lost reservation, return busy... */
			
			li		r0,0								/* Get false */
			stb		r0,CSAregsAreValid(r4)				/* Set that the registers ain't valid */
			stw		r4,MPPICParm0(r9)					/* Set the status area physical address parameter */
			
			rlwimi	r5,r10,0,16,31						/* Put these behind the status flags */
			b		KickAndGo							/* We're done now... */
			
/*			Spill one's seed upon the soil */

Flagellant:	bl		StoreStatus							/* Go store off all the registers 'n' stuff */
			mtlr	r11									/* Restore the return address */
			li		r3,kSIGPnoErr						/* Return no error */
			blr											/* Leave... */


/******************************************************************************************************** */
/* */
/*			Here we will attempt to syncronize clocks (R3=processor). */
/*			Self abuse will just return with an all-ok code. */
/* */
/******************************************************************************************************** */
			
SynchClock:
			mfspr	r10,pir								/* Get our processor number */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			cmplw	cr0,r3,r10							/* Cleaning our own clock?? */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			li		r3,kSIGPnoErr						/* Assume self-cleaning clock */
			beqlr										/* Oh baby, oh baby... */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			mflr	r11									/* Save the link register */
			add		r9,r9,r12							/* Point right at the entry */
			
SyCretry:	lwarx	r5,0,r9								/* Pick up the status flags (MPPICStat) and reserve it */
			li		r3,kSIGPInterfaceBusyErr			/* Fill dead space and get busy return code */
			rlwinm.	r0,r5,0,0,0							/* Are we marked as busy? */
			lis		r6,MPPICOnline>>16					/* Get the online flag */
			bne-	ErrorReturn							/* Yeah, go leave, don't bother me now... */
			and.	r0,r5,r6							/* Are we online */
			li		r3,kMPPOffline						/* Set offline */
			beq-	ErrorReturn							/* Ain't online, ain't ready, buzz off... */
			oris	r5,r5,(MPPICBusy>>16)&0x0000FFFF	/* Turn on the busy bit */
			li		r0,0								/* Clear this */
			
			stwcx.	r5,0,r9								/* Try to set busy */
			ori		r10,r10,MPPICfTBsy<<8				/* Put the timebase sync function in front of the processor ID */
			bne-	SyCretry							/* Lost reservation, return busy... */

			stw		r0,MPPITBsync+4-MPPIwork(r12)		/* Make sure the parm area is 0 */
			mr		r0,r11								/* Save the LR */
			bl		SyCbase								/* Get a base register */
SyCbase:	rlwimi	r5,r10,0,16,31						/* Put these behind the status flags */
			mflr	r11									/* Get the base */
			la		r11,(4*4)(r11)						/* DON'T MESS WITH THESE INSTRUCTIONS Make up the return point */
			b		KickAndGo							/* Go signal the other side */

SyCKrtrn:	mr		r11,r0								/* Restore the return */

/* */
/*			Start sync'ing 'er up */
/* */
		
			mftb	r4									/* Take a timeout stamp (don't need top half, we have at least 13 hours) */

SyCInP0:	lwz		r5,0(r9)							/* Get the CPU status word */
			rlwinm	r5,r5,24,24,31						/* Isolate the command byte */
			cmplwi	cr0,r5,MPPICfTBsy1					/* Have we reached time base sync phase 1 yet? */
			beq		SyCInP1								/* Yeah, we're in phase 1... */
			mftb	r5									/* Get the bottom half of the timer again */
			sub		r5,r5,r4							/* How long we been messin' around? */
			cmplwi	cr0,r5,1000*TicksPerMic				/* Don't try more'n' a 1000µS */
			blt+	SyCInP0								/* We haven't, so wait some more... */
			li		r3,kMPPTimeOut						/* Signal timeout */
			b		ErrorReturn							/* By dude... */
			
/* */
/*			Here we make sure there is enough time to sync the clocks before the lower part of the TB ticks */
/*			up into the high part.  This eliminates the need for any funky  */
/*			"get-the-top-then-get-the-bottom-then-get-the-top-again-to-see-if-it-changed" stuff.  That would */
/*			only make the sync harder to do. */
/*			 */
/*			Also, because we use the lower TB value for the signal, we also need to make sure we do not have */
/*			a value of 0, we would be ever-so-sorry if it was. */
/* */

SyCInP1:	li		r4,lo16(0xC000)						/* Get the minimum time left on clock before tick ('bout 1 1/4 ms) */
			li		r8,0								/* Get a 0 constant */
			
SyCdelay:	mftb	r5									/* Get the time left */
			cmplw	cr0,r5,r4							/* See if there is sufficient time before carry into high clock */
			bgt-	SyCdelay							/* Nope, hang until it is... */
			mr.		r5,r5								/* Did we just tick, however? */
			beq-	SyCdelay							/* Yeah, wait until it is at least 1... */
			
			mftbu	r4									/* Get the upper */
			stw		r4,MPPITBsync-MPPIwork(r12)			/* Make sure the top half is set */
			sync										/* Wait until it is done */
			
			mftb	r5									/* Get the lower timebase now */
			stw		r5,MPPITBsync+4-MPPIwork(r12)		/* Shove it out for the other processor */

			la		r6,MPPISncFght-MPPIwork(r12)		/* Point to the courtroom area */
			li		r5,0								/* Point to the first line */

SyCclear:	dcbz	r5,r6								/* Clear the court */
			addi	r5,r5,32							/* Point to the next line */
			cmplwi	cr0,r5,10*2*32						/* Enough for 9 iterations, 2 chunks at a time */
			blt+	SyCclear							/* Clear the whole smear... */
			sync										/* Make sure everyone's out */

			mftb	r5									/* Get the lower timebase now */
		
SyCWait:	lwz		r7,MPPITBsync+4-MPPIwork(r12)		/* Get it back */
			mftb	r6									/* Get the bottom half again */
			mr.		r7,r7								/* Have they set their clock yet? */
			sub		r0,r6,r5							/* See if we're hung up */
			beq-	SyCdonesync							/* Clock is set */
			cmplwi	cr0,r0,1000*TicksPerMic				/* Timeout if we spend more than 1000µS doing this */
			blt+	SyCWait								/* No timeout, wait some more... */
			li		r3,kMPPTimeOut						/* Set timeout */
			b		ErrorReturn							/* Leave... */
			
/* */
/*			Ok, so now we have set a preliminary TB value on the second processor.  It's close, but only */
/*			within handgranade range.   */
/* */
/*			What we will do now is to let the processors (starting with the other guy) argue about the time for */
/*			a while (10 passes-we use the middle 8).  We'll look at the results and try to adjust the other processor's */
/*			time such that the timing windows are overlapping evenly. This should put the TBs close enough together */
/*			(0-2 ticks) that the difference is undetectable. */
/* */


			
SyCdonesync:
			li		r4,0								/* Clear this */
			la		r5,MPPISncFght-MPPIwork(r12)		/* Point to the squared circle */

SyCWtArg:	
			dcbf	0,r5								/* Make sure of it */
			sync										/* Doubly shure */
			lwz		r6,0(r5)							/* Listen for the defence argument */

			mr.		r6,r6								/* See if they are done */
			beq+	SyCWtArg							/* Nope, still going... */

			mftb	r7									/* They're done, time for rebuttal */
			stw		r7,32(r5)							/* Make rebuttle */
	
			addi	r4,r4,1								/* Count rounds */

			cmplwi	cr0,r4,10							/* See if we've gone 8 rounds plus an extra one */
			addi	r5,r5,64							/* Point to the next round areas */

			blt+	SyCWtArg							/* Not yet, come out of your corners fighting... */
			
			mftb	r5									/* Stamp the wait */
			
SyCWadj:	lwz		r7,MPPITBsync+4-MPPIwork(r12)		/* Get adjustment flag */
			mftb	r6									/* Get timebase again */
			
			mr.		r7,r7								/* Have they set their timebase with adjusted time yet? */
			sub		r6,r6,r5							/* Get elapsed time */
			bne+	SyCdone								/* They say it, sync done... */
			cmplwi	cr0,r6,1000*TicksPerMic				/* Timeout if we spend more than 1000µS doing this */
			blt+	SyCWadj								/* Still time, wait until adjustment is done... */
			
			li		r3,kMPPTimeOut						/* Set timeout */
			b		ErrorReturn							/* Pass it back... */
			
SyCdone:	li		r3,kSIGPnoErr						/* No errors */
			mtlr	r11									/* Restore LR */
			blr											/* Leave... */


/******************************************************************************************************** */
/* */
/*			Here we will get the physical address of the interrupt handler. */
/* */
/******************************************************************************************************** */
			
GetExtHandlerAddress:
			mflr	r11									/* Save our return */
			bl		GEXbase								/* Make a base address */
GEXbase:	mflr	r3									/* Get address into our base */
			addi	r3,r3,LOW_ADDR(GotSignal-GEXbase)	/* Get the logical address of the 'rupt handler */

			mtlr	r11									/* Restore LR */
			blr


/******************************************************************************************************** */
/* */
/*			Here we will get a snapshot of the processor's current signaling state (R3=processor). */
/* */
/******************************************************************************************************** */
			
ProcessorState:
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			cmplwi	cr1,r3,MPPIMaxCPU					/* See if we are bigger than max */
			li		r3,kSIGPTargetAddrErr				/* CPU number is too big */
			bgtlr-	cr1									/* Sure are... (Get our address also) */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			add		r9,r9,r12							/* Point right at the entry */
			lwz		r4,MPPICStat(r9)					/* Get the status word */
			li		r3,kSIGPnoErr						/* Set no errors */
			rlwinm.	r4,r4,0,0,0							/* Test for busy status */
			beqlr										/* Return kSIGPnoErr if not busy */
			li		r3,kSIGPInterfaceBusyErr			/* Otherwise, return busy */
			blr											/* Return it */

/******************************************************************************************************** */
/* */
/*			Here we will try to handle any pending messages (just as if an interruption occurred). */
/*			The purpose of this function is to assure the message passing system runs even */
/*			though external interrupts are disabled. Lacking a separate physical signalling */
/*			class, we have to share the external interrupt signal. Unfortunately, there are */
/*			times when disabled loops occur (in spin locks, in the debugger, etc.), and when they */
/*			happen, a low level message sent to a processor will not get processed, hence this */
/*			function exists to be called from those disabled loops. Since the calls are often */
/*			from disabled code, all that can be done is to process any pending *message*. Any */
/*			pending notification interruption (referred to throughtout this code as a SIGP */
/*			interruption) must remain pending. */
/* */
/******************************************************************************************************** */
			
RunSIGPRun:
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			mfspr	r3,pir								/* Get our CPU address */
			rlwinm	r9,r3,5,23,26						/* Get index into CPU array */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			mflr	r11									/* Save the link register */
			add		r9,r9,r12							/* Point right at our entry */
			lwz		r3,MPPICPriv(r9)					/* Get our privates */
			cmplw	cr1,r11,r11							/* Make sure IdleWait doesn't try to clear 'rupt request */
			oris	r3,r3,MPPICXRun>>16					/* Diddle with them and show we entered here */
			stw		r3,MPPICPriv(r9)					/* Put away our privates */
			b		IdleWait							/* Go pretend there was an interrupt... */

/******************************************************************************************************** */
/* */
/*			Error return.  We only need this when we leave with a reservation.  We really SHOULD clear it... */
/* */
/******************************************************************************************************** */

ErrorReturn:
			mtlr	r11									/* Restore LR */
			blr											

/******************************************************************************************************** */
/* */
/*			Kick the target processor.  Note that we won't set the passing bit until we are ready to exit. */
/*			The reason for this is that we have the silly, old watchchihuahua board to deal with.  Because  */
/*			we can't just set the interrupt and leave, we gotta wait for it to be seen on the other side. */
/*			This means that there could be a timeout and if so, we need to back off the function request else */
/*			we'd see busy when they tried to redrive it.  We'll have to deal with a tad of spin on the secondary side. */
/*			note that this just applies to a primary to secondary function on the old board. */
/* */
/******************************************************************************************************** */
	
KickAndGo:
			la		r8,MPPICPU0-MPPIwork(r12)			/* Get the primary work area address */
			mtlr	r11									/* Restore the link register */
			cmplw	cr0,r8,r9							/* Which is target?  primary or secondary? */
			mfmsr	r11									/* Save off the MSR */
			oris	r5,r5,MPPICPass>>16					/* Set the passing bit on */
			stw		r5,MPPICStat(r9)					/* Store the pass and let the other processor go on */
			
			beq		KickPrimary							/* The target is the primary... */
			
			ori		r3,r11,0x0010						/* Turn on DDAT bit */
			lbz		r4,MPPIstatus-MPPIwork(r12)			/* Load up the global status byte */
			lwz		r8,MPPIHammer-MPPIwork(r12)			/* Point to the Hammerhead area */
	
			mtmsr	r3									/* Turn on DDAT */
			isync
			
			andi.	r4,r4,MPPI2Pv2						/* Are we on the new or old board? */
			li		r3,0								/* Set the bit for an interrupt request */
			beq		KickOld								/* Ok, it's the old board... */
			
			sync										/* Make sure this is out there */
			stb		r3,IntReg(r8)						/* Set the interruption signal */
			eieio
	
			mtmsr	r11									/* Set DDAT back to what it was */
			isync
			li		r3,kSIGPnoErr						/* Set no errors */
			blr											/* Leave... */
			
KickOld:	li		r4,8								/* Set the number of tries */

KickAgain:	mftb	r6									/* Stamp the bottom half of time base */
			stb		r3,IntReg(r8)						/* Stick the interrupt */
			eieio										/* Fence me in */
			
CheckKick:	lbz		r10,IntReg(r8)						/* Get the interrupt request back again */
			mr.		r10,r10								/* Yes? Got it? */
			bne		FinalDelay							/* Yeah, do the final delay and then go away... */
			
			mftb	r7									/* Get the time again */
			sub		r7,r7,r6							/* Get time-so-far */
			cmplwi	cr0,r7,75*TicksPerMic				/* Hold it for 75µS (average disable is supposed to be 100µS or so) */
			blt+	CheckKick							/* Keep waiting the whole time... */
			
			li		r10,SecInt							/* Set the deassert bit */
			mftb	r6									/* Stamp start of deassert time */
			stb		r10,IntReg(r8)						/* Deassert the interrupt request */
			eieio
			
DeassertWT:	mftb	r7									/* Stamp out the time */
			sub		r7,r7,r6							/* Get elapsed */
			cmplwi	cr0,r7,16*TicksPerMic				/* Hold off 16µS (minimum is 12µS) */
			blt+	DeassertWT							/* Keep spinning... */
			
			subi	r4,r4,1								/* See if we have another retry we can do */
			mr.		r4,r4								/* Are we there yet? */
			blt+	KickAgain							/* Retry one more time... */
			
			rlwinm	r5,r5,0,2,31						/* Clear busy and passing bits */
			rlwinm	r5,r5,0,24,15						/* Clear the function request to idle */
			
			mtmsr	r11									/* Restore DDAT stuff */
			isync

			stw		r5,MPPICStat(r9)					/* Rescind the request */
			li		r3,kMPPTimeOut						/* Set timeout */
			blr											/* Leave... */
			
FinalDelay:	mftb	r6									/* Stamp the start of the final delay */

FinalDelayW:
			mftb	r7									/* Stamp out the time */
			sub		r7,r7,r6							/* Get elapsed */
			cmplwi	cr0,r7,16*TicksPerMic				/* Hold off 16µS (minimum is 12µS) */
			blt+	FinalDelayW							/* Keep spinning... */

			mtmsr	r11									/* Restore DDAT stuff */
			isync
			li		r3,kSIGPnoErr						/* Set no errors */
			blr											/* Leave... */
			
KickPrimary:
			ori		r3,r11,0x0010						/* Turn on the DDAT bit */
			lwz		r8,MPPIEther-MPPIwork(r12)			/* Get the address of the ethernet ROM */
			
			mtmsr	r3									/* Turn on DDAT */
			isync

			li		r4,4								/* Get flip count */
			
			sync										/* Make sure the status word is out there */

FlipOff:	lbz		r3,0(r8)							/* Reference ethernet ROM to get chip select twiddled */
			eieio										/* Make sure of this (Hmm, this is chip select, not memory-mapped */
														/* storage.  Do we even need the eieio?) */
														
			addic.	r4,r4,-1							/* Have we flipped them off enough? */
			bgt+	FlipOff								/* Not yet, they deserve more... */
			
			mtmsr	r11									/* Restore DDAT stuff */
			isync
			li		r3,kSIGPnoErr						/* Set no errors */
			blr											/* Return... */

/******************************************************************************************************** */
/* */
/*			This is the code for the secondary processor */
/* */
/******************************************************************************************************** */

/*			Note that none of this code needs locks because there's kind of a synchronization */
/*			shuffle going on. */

/* */
/*			First, we need to do a bit of initialization of the processor. */
/* */


CPUInit:	
			li		r27,0x3040							/* Set floating point and machine checks on, IP to 0xFFF0xxxx */
			mtmsr	r27									/* Load 'em on in */
			isync
			
			lis		r28,-32768							/* Turn on machine checks */
														/* should be 0x8000 */
			ori		r28,r28,0xCC84						/* Enable caches, clear them,  */
														/* disable serial execution and turn BHT on */
			sync
			mtspr	HID0,r28							/* Start the cache clear */
			sync

/* */
/*			Clear out the TLB.  They be garbage after hard reset. */
/* */

			li		r0,512								/* Get number of TLB entries (FIX THIS) */
			li		r3,0								/* Start at 0 */
			mtctr	r0									/* Set the CTR */
			
purgeTLB:	tlbie	r3									/* Purge this entry */
			addi	r3,r3,4096							/* Next page */
			bdnz	purgeTLB							/* Do 'em all... */
					
			sync										/* Make sure all TLB purges are done */
			tlbsync										/* Make sure on other processors also */
			sync										/* Make sure the TLBSYNC is done */

/* */
/*			Clear out the BATs.  They are garbage after hard reset. */
/* */

			li		r3,0								/* Clear a register */
			
			mtspr	DBAT0L,r3							/* Clear BAT */
			mtspr	DBAT0U,r3							/* Clear BAT */
			mtspr	DBAT1L,r3							/* Clear BAT */
			mtspr	DBAT1U,r3							/* Clear BAT */
			mtspr	DBAT2L,r3							/* Clear BAT */
			mtspr	DBAT2U,r3							/* Clear BAT */
			mtspr	DBAT3L,r3							/* Clear BAT */
			mtspr	DBAT3U,r3							/* Clear BAT */
			
			mtspr	IBAT0L,r3							/* Clear BAT */
			mtspr	IBAT0U,r3							/* Clear BAT */
			mtspr	IBAT1L,r3							/* Clear BAT */
			mtspr	IBAT1U,r3							/* Clear BAT */
			mtspr	IBAT2L,r3							/* Clear BAT */
			mtspr	IBAT2U,r3							/* Clear BAT */
			mtspr	IBAT3L,r3							/* Clear BAT */
			mtspr	IBAT3U,r3							/* Clear BAT */

/* */
/*			Map 0xF0000000 to 0xFFFFFFFF for I/O; make it R/W non-cacheable */
/*			Map	0x00000000 to 0x0FFFFFFF for mainstore; make it R/W cachable */
/* */

			lis		r6,0xF000							/* Set RPN to last segment */
			ori		r6,r6,0x1FFF						/* Set up upper BAT for 256M, access both */
			
			lis		r7,0xF000							/* Set RPN to last segment */
			ori		r7,r7,0x0032						/* Set up lower BAT for 256M, access both, non-cachable */
		
			mtspr	DBAT0L,r7							/* Setup ROM and I/O mapped areas */
			mtspr	DBAT0U,r6							/* Now do the upper DBAT */
			sync
	
			li		r6,0x1FFF							/* Set up upper BAT for 256M, access both */
			li		r7,0x0012							/* Set up lower BAT for r/w access */
			
			mtspr	DBAT1L,r7							/* Set up an initial view of mainstore */
			mtspr	DBAT1U,r6							/* Now do the upper DBAT */
			sync

/* */
/*			Clean up SDR and segment registers */
/* */

			li		r3,0								/* Clear a register */
			mtspr	SDR1,r3								/* Clear SDR1 */
			
			li		r4,0								/* Clear index for segment registers */
			lis		r5,0x1000							/* Set the segment indexer */
			
clearSR:	mtsrin	r3,r4								/* Zero out the SR */
			add.	r4,r4,r5							/* Point to the next segment */
			bne-	clearSR								/* Keep going until we wrap back to 0 */
			
			lis		r5,HIGH_ADDR(EXT(FloatInit))		/* Get top of floating point init value */
			ori		r5,r5,LOW_ADDR(EXT(FloatInit))		/* Slam bottom */
			lfd		f0,0(r5)							/* Initialize FP0 */
			fmr		f1,f0								/* Ours in not */					
			fmr		f2,f0								/* to wonder why, */
			fmr		f3,f0								/* ours is but to */
			fmr		f4,f0								/* do or die! */
			fmr		f5,f0						
			fmr		f6,f0						
			fmr		f7,f0						
			fmr		f8,f0						
			fmr		f9,f0						
			fmr		f10,f0						
			fmr		f11,f0						
			fmr		f12,f0						
			fmr		f13,f0						
			fmr		f14,f0						
			fmr		f15,f0						
			fmr		f16,f0						
			fmr		f17,f0						
			fmr		f18,f0						
			fmr		f19,f0						
			fmr		f20,f0						
			fmr		f21,f0						
			fmr		f22,f0						
			fmr		f23,f0						
			fmr		f24,f0						
			fmr		f25,f0						
			fmr		f26,f0						
			fmr		f27,f0						
			fmr		f28,f0						
			fmr		f29,f0						
			fmr		f30,f0						
			fmr		f31,f0						
			
/* */
/*			Whew, that was like, work, man!  What a cleaning job, I should be neater */
/*			when I reset. */
/* */
/*			Finally we can get some data DAT turned on and we can reset the interrupt */
/*			(which may have been done before we get here) and get into the bring up */
/*			handshakes. */
/*		 */
/*			Note that here we need to use the actual V=R addresses for HammerHead */
/*			and PCI1 adr.  There are no virtual mappings set up on this processor. */
/*			We need to switch once the firmware is initialized.  Also, we don't know */
/*			where our control block is yet. */
/* */
		
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */

			mfmsr	r3									/* Get the MSR */
			ori		r3,r3,0x0010						/* Turn data DAT on */
			mtmsr	r3									/* DAT is on (well, almost) */
			isync										/* Now it is for sure */
		
			lis		r8,HammerHead>>16					/* Point to the HammerHead controller */
			li		r7,SecInt							/* Get value to reset */
			stb		r7,IntReg(r8)						/* Reset the interrupt */
			eieio										/* Fence it off */
		
/* */
/*			Now we can plant and harvest some bits. */
/* */
		
			lwz		r6,MPPIlogCPU-MPPIwork(r12)			/* Get the logical CPU address to assign */
			mfspr	r7,pir								/* Get the old PIR */
			rlwimi	r7,r6,0,27,31						/* Copy all of the reserved parts */
			mtspr	pir,r7								/* Set it */
			
/* */
/*			This little piece of code here determines if we are on the first or second version */
/*			of the two processor board.  The old one shouldn't ever be shipped (well, maybe by  */
/*			DayStar) but there are some around here. */
/* */
/*			The newer version of the 2P board has a different state machine than the older one. */
/*			When we are in the board state we're in now, primary arbitration is turned on while */
/*			it is not until the next state in the old board.  By checking the our bus address */
/*			(WhoAmI) we can tell. */
/* */

			lbz		r7,WhoAmI(r8)						/* Get the current bus master ID */
			andi.	r7,r7,PriCPU						/* Do we think we're the primary? */
			beq		On2Pv1								/* No, that means we're on the old 2P board */
			
			lbz		r7,MPPIstatus-MPPIwork(r12)			/* Get the status byte */
			ori		r7,r7,MPPI2Pv2						/* Show we're on the new board */
			stb		r7,MPPIstatus-MPPIwork(r12)			/* Set the board version */
			
On2Pv1:		rlwinm	r9,r6,5,23,26						/* Get index into the CPU specific area */
				
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Index to processor */
			add		r9,r9,r12							/* Get a base for our CPU specific area */
			
			oris	r6,r6,((MPPICBusy+MPPICOnline+MPPICStop)>>16)&0x0000FFFF	/* Set CPU busy, online, stopped,  */
																				/*   and busy set by himself */
			stw		r6,MPPICStat(r9)					/* Save the whole status word */
			
			li		r4,0x80								/* Get beginnings of a CPU address mask */
			lhz		r11,MPPIinst-MPPIwork(r12)			/* Get the installed and online status flags */
			srw		r4,r4,r6							/* Make a mask */
			rlwimi	r4,r4,8,16,23						/* Double up the mask for both flags */
			or		r11,r11,r4							/* Set that we are installed and online */
			sync										/* Make sure the main processor sees the rest of the stuff */
			
			sth		r11,MPPIinst-MPPIwork(r12)			/* We're almost done, just need to set the TB */
			
			lis		r5,PCI1AdrReg>>16					/* Point to the PCI1 address register		 */
			li		r4,0								/* Clear this out */
			stw		r4,0(r5)							/* Set PCI register to 0 to show we're ready for TB sync */
			eieio										/* Fence it off */

Wait4TB:	lwz		r7,0(r5)							/* Get the PCI1 reg to see if time to set time */
			mr.		r7,r7								/* Is it ready yet? */
			beq		Wait4TB								/* Nope, wait for it... */
			isync										/* No peeking... */
			
			lwz		r3,MPPITBsync-MPPIwork(r12)			/* Get the high word of TB */
			lwz		r4,MPPITBsync+4-MPPIwork(r12)		/* Get the low word */
			
/*			Note that we need no TB magic here 'cause they ain't running */

			mttbu	r3									/* Set the high part */
			mttbl	r4									/* Set the low part */
			
			rlwinm	r6,r6,0,2,31						/* Clear the busy bit and passed */
			stw		r6,MPPICStat(r9)					/* Store the status word */
			
			sync										/* Make sure all is right with the world */
			
			li		r3,0								/* Set the init done signal */
			stw		r3,0(r5)							/* Feed the dog and let him out */
			sync										/* Make sure this is pushed on out */

			li		r27,0x3040							/* Make MSR the way we likes it */
			mtmsr	r27									/* Load 'em on in */
			isync

/* */
/*			Jump on to the idle wait loop.  We're online and ready, but we're */
/*			still in the reset state.  We need to wait until we see a start signal. */
/* */
/*			Note that the idle loop expects R9 to be our CPU-specific work area; */
/*			R12 is the base of the code and global work area  */
/* */

			cmplw	cr1,r11,r12							/* Make sure IdleWait knows to clear 'rupt request */
			b		IdleWait


/******************************************************************************************************** */
/******************************************************************************************************** */
/* */
/*			Here is the interruption handler. */
/* */
/*			What we'll do here is to get our registers into a standard state and figure out which */
/*			which processor we are on.  The processors have pretty much the same code.  The primary */
/*			will reset the the secondary to primary interruption bit and the secondary will reset the SecInt */
/*			flags. */
/* */
/*			The primary to secondary interrupt is an exception interruption contolled by a bit in the  */
/*			Hammerhead IntReg.  The only bit in here is SecInt which is active low.  Writing a 0 into the */
/*			bit (bit 0) yanks on the external pin on the secondary.  Note that it is the only external */
/*			connected on the secondary.  SecInt must be set to 1 to clear the interruption.  On the old */
/*			2P board, asserting the external interrupt causes a watchdog timer to start which expires unless */
/*			the interrupt request is withdrawn. On a 180Mhz system the time to expire is about 256µS,  */
/*			not very long.  So, what we need to do is to time the assertion and if it has not been reset */
/*			reset, do it ourself.  Unfortunatelty we need to keep it deasserted for at least 12µS or the  */
/*			watchdog will not stop.  This leads to another problem: even if the secondary processor sees */
/*			the interrupt and deasserts the request itself,  we cannot reassert before the 12µS limit, */
/*			else havoc will be wrought.  We just gotta make sure. */
/* */
/*			So, the secondary to primary interrupt is megafunky.  The mother board is wired with the  */
/*			MACE ethernet chip's chip-select pin wired to Grand Centeral's external interrrupt #10 pin. */
/*			This causes a transient interrupt whenever MACE is diddled. GC latches the interrupt into the */
/*			events register where we can see it and clear it. */
/* */
/******************************************************************************************************** */
/******************************************************************************************************** */

GotSignal:	mfspr	r9,pir								/* Get our processor ID */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top half of the data area */
			rlwinm	r9,r9,5,23,26						/* Clean this up */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get the bottom half of the data area */
			la		r9,EXT(MPPICPUs)-MPPIwork(r9)			/* Point into the proccessor control area */
			mflr	r11									/* Save our return */
			add		r9,r9,r12							/* Point right at the entry */

/*			We'll come in here if we're stopped and found the 'rupt via polling */
/*			or we were kicked off by the PollSIGP call.  We  need */
/*			to wipe out the interrupt request no matter how we got here. */

SimRupt:	mfmsr	r4									/* Get the MSR */

			la		r8,MPPICPU0-MPPIwork(r12)			/* Get address of main processor's work area */
			ori		r5,r4,0x0010						/* Turn on the DDAT bit */
			cmplw	cr0,r8,r9							/* Are we on the main? */
			cmplw	cr1,r4,r4							/* Set CR1 to indicate we've cleared any 'rupts */
			bne		SecondarySig						/* Go if we are not on main processor... */

/* */
/*			Handle the secondary to primary signal */
/* */

PrimarySig:

			lwz		r8,MPPIGrandC-MPPIwork(r12)			/* Get the address of the Grand Central area base */
			mtmsr	r5									/* Turn on DDAT */
			isync										/* Now don't be usin' dem speculative executions */
			li		r7,EventsReg						/* Get address of the interrupt events register */
			lwbrx	r6,r7,r8							/* Grab the interruption events */

			lis		r5,0x4000							/* Get the mask for the Ext10 pin */
			and.	r0,r6,r5							/* See if our bit is on */
			li		r7,ClearReg							/* Point to the interruption clear register */
		
			beq+	SkpClr								/* Skip the clear 'cause it's supposed to be soooo slow... */
			
			stwbrx	r5,r7,r8							/* Reset the interrupt latch */
			eieio										/* Fence off the last 'rupt */

SkpClr:		mtmsr	r4									/* Set MSR to entry state */
			isync										/* Make sure we ain't gunked up no future storage references */
		
			bne+	IdleWait							/* Go join up and decode the function... */
			
			mtlr	r11									/* Restore return address */
			andc.	r0,r6,r5							/* Any other bits on? */
			li		r3,kMPVainInterrupt					/* Assume we got nothing */
			beqlr										/* We got nothing, tell 'em to eat 'rupt... */
			li		r3,kMPIOInterruptPending			/* Tell them to process an I/O 'rupt */
			blr											/* Ignore the interrupt... */

/* */
/*			Handle the primary to secondary signal */
/* */

SecondarySig:
			lwz		r3,MPPICStat(r9)					/* Pick up our status word */
			lis		r8,HammerHead>>16					/* Get the address of the hammerhead (used during INIT on non-main processor) */
			rlwinm.	r3,r3,0,3,3							/* Check if we are already "in-the-know" (all started up) */
			beq-	UseAltAddr							/* Nope, use hardcoded Hammerhead address */
			lwz		r8,MPPIHammer-MPPIwork(r12)			/* Get the kernel's HammerHead area */

UseAltAddr:	mtmsr	r5									/* Turn on DDAT */
			isync										/* Now don't be usin' dem speculative executions */
			li		r0,SecInt							/* Get the Secondary interrupt bit */
			stb		r0,IntReg(r8)						/* Reset the interrupt request */
			mtmsr	r4									/* Set MSR to entry state */
			eieio										/* Fence me in */
			isync										/* Make sure we ain't gunked up no future storage references */
			
			b		IdleWait							/* Go decode this request... */
			
/******************************************************************************************************** */
/******************************************************************************************************** */
/* */
/*			This is the idle wait.   */
/* */
/*			We're stuck in here so long as we are stopped or reset. */
/*			All functions except for "start" pass back through here.  Start is weird because */
/*			it is an initial thing, i.e., we can't have gotten here via any kind of exception, */
/*			so there is no state to restore.  The "started" code is expected to require no know */
/*			state and will take care of all initialization/fixup required. */
/* */
/******************************************************************************************************** */
/******************************************************************************************************** */

BadRuptState:											/* We don't do anything special yet for a bad state, just eat request */
KillBusy:	rlwinm	r3, r3, 0, 2, 31					/* Remove the message pending flags. */
			rlwinm	r3, r3, 0, 24, 16					/* Set the function to idle. */
			stw		r3,MPPICStat(r9)					/* Update/unlock the status word. */

ReenterWait: cmplwi	cr1,r9,0							/* Turn off the 'rupt cleared flag */

IdleWait:	lis		r4,MPPICBusy>>16					/* Get busy status */

SpinIdle:				
			lwz		r3,MPPICStat(r9)					/* Pick up our status word */
		
			and.	r5,r3,r4							/* Isolate the busy bit */
			lis		r6,MPPICPass>>16					/* Get the passed busy flag */
			bne		TooBusy								/* Work, work, work, that's all we do is work... */
			
			rlwinm.	r5,r3,0,4,4							/* See if we are stopped */
			lwz		r8,MPPICPriv(r9)					/* Pick up our private flags */
			bne-	SpinIdle							/* Yeah, keep spinning... */
			
			
/* */
/*			Restore the state and get outta here.  Now, we shouldn't be in a reset state and not be stopped, */
/*			so we can go ahead and safely return up a level because it exists.  If we are reset, no state exists */
/*			and we should always be stopped. */
/* */
			
			rlwinm	r4, r8, 1, 0, 0 					/* Get the explicit run bit, shifted left one. */
			rlwinm.	r5, r8, 0, 0, 0						/* See if there is a SIGP signal pending */
			and		r4, r8, r4							/* Turn off the SIGP pending bit if this was not an explicit run */
														/* Also the explicit run bit is cleared */
			mtlr	r11									/* Restore the return point */
			li		r3,kMPVainInterrupt					/* Tell the interrupt handler to ignore the interrupt */
			stw		r4,MPPICPriv(r9)					/* Set that flag back for later */
			beqlr										/* Time to leave if we ate the 'rupt... */

			li		r3,kMPSignalPending					/* Set that there is a SIGP interruption pending */
			
			blr											/* Go away, let our caller handle this thing... QED!!!!!!!!! */

/* */
/*           QQQQQ       EEEEEEEEEE    DDDDDDDDD */
/*         QQQQQQQQQ     EEEEEEEEEE    DDDDDDDDDDD */
/*        QQQQ   QQQQ    EEEE          DDD     DDD */
/*       QQQQ     QQQQ   EEEEEEEEEE    DDD     DDD */
/*       QQQQ   Q QQQQ   EEEEEEEEEE    DDD     DDD */
/*        QQQQ  QQQQQ    EEEE          DDD     DDD */
/*         QQQQQQQQQQQ   EEEEEEEEEE    DDDDDDDDDDD */
/*           QQQQQ QQQ   EEEEEEEEEE    DDDDDDDDD */
/* */
/*			(I finished here) */
/* */

			
/* */
/*			This is where we decode the function and do what's right. */
/*			First we need to check if it's really time to do something. */
/* */

TooBusy:	and.	r5,r3,r6							/* See if the passed flag is on */
			beq		SpinIdle							/* No, not yet, try the whole smear again... */
			
			beq+	cr1,KeepRupt						/* Don't clear 'rupt if we already did (or entered via RunSIGRun) */

			lwz		r5,MPPICPriv(r9)					/* Get the private flags */
			rlwinm.	r5, r5, 0, 1, 1						/* Did we enter via RunSIGPRun? */
			beq		SimRupt								/* Nope, 's'ok, go clear physical 'rupt... */
			
KeepRupt:
			bl		GetOurBase							/* Get our address */
GetOurBase:	rlwinm	r4,r3,26,22,29						/* Get the opcode index * 4 */
			mflr	r12									/* Get the base address */
			la		r7,LOW_ADDR(IFuncTable-GetOurBase)(r12)		/* Point to the function table */
			
			cmplwi	cr0,r4,7*4							/* See if they sent us some bogus junk */
														/* Change 7 if we add more functions */
			add		r7,r7,r4							/* Point right at the entry */
			bgt-	KillBusy							/* Bad request code, reset busy and eat it... */
			
			mtlr	r7									/* Set up the LR */
			
			blr											/* Go execute the function... */
	
IFuncTable:
			b		KillBusy							/* This handles the signal in vain... */
			b		IStart								/* This handles the start function */
			b		IResume								/* This handles the resume function */
			b		IStop								/* This handles the stop function */
			b		ISIGP								/* This handles the SIGP function */
			b		IStatus								/* This handles the store status function */
			b		ITBsync								/* This handles the synchronize timer base function */
			b		IReset								/* This handles the reset function */

/******************************************************************************************************** */
/******************************************************************************************************** */
/* */
/*			Here are the functions handled at interrupt time */
/* */
/******************************************************************************************************** */
/******************************************************************************************************** */

/******************************************************************************************************** */
/* */
/*			The Start function.  This guy requires that the processor be in the reset and online state. */
/* */
/******************************************************************************************************** */

IStart:		lis		r4,MPPICOnline>>16					/* Get bits required to be on */
			isync										/* Make sure we haven't gone past here */
			and		r6,r3,r4							/* See if they are on */
			cmplw	cr1,r6,r4							/* Are they all on? */
			lwz		r4,MPPICParm0(r9)					/* Get the physical address of the code to go to */
			bne-	cr1,BadRuptState					/* Some required state bits are off */
			rlwinm	r3,r3,0,2,31						/* Kill the busy bits */
			rlwinm	r3,r3,0,24,15						/* Set the function to idle */
			oris	r3,r3,MPPICReady>>16				/* Set ready state */
			rlwinm	r3,r3,0,5,3							/* Clear out the stop bit */
			mtlr	r4									/* Set the LR */
			stw		r3,MPPICStat(r9)					/* Clear out the status flags */
			lwz		r3,MPPICParm2(r9)					/* Get pass-thru parameter */
			blrl										/* Start up the code... */
/* */
/*			The rules for coming back here via BLR are just opposite the normal way:  you can trash R0-R3 and */
/*			R13-R31, all the CRs; don't touch SPRG1 or SPRG3, the MSR, the SRs or BATs 0 and 1. */
/*			Follow these simple rules and you allowed back; don't follow them and die. */
/*			We only come back here if there is some kind of startup failure so's we can try again later */
/* */

			lwz		r3,MPPICStat(r9)					/* Get back the status word */
			cmplw	cr1,r4,r4							/* Show that we have already taken care of the 'rupt */
			rlwinm	r3,r3,0,4,2							/* Reset the ready bit */
			b		KillBusy							/* Back into the fold... */

/******************************************************************************************************** */
/* */
/*			The Resume function.  This guy requires that the processor be online and ready. */
/* */
/******************************************************************************************************** */

IResume:	lis		r4,(MPPICOnline+MPPICReady)>>16		/* Get states required to be set */
			and		r6,r3,r4							/* See if they are on */
			cmplw	cr0,r6,r4							/* Are they all on? */
			bne-	BadRuptState						/* Some required off state bits are on */
			rlwinm	r3,r3,0,5,3							/* Clear out the stop bit */
			b		KillBusy							/* Get going... */

/******************************************************************************************************** */
/* */
/*			The Stop function.  All we care about here is that the guy is online. */
/* */
/******************************************************************************************************** */

IStop:		lis		r4,MPPICOnline>>16					/* All we care about is if we are online or not */
			and.	r6,r3,r4							/* See if we are online */
			beq-	BadRuptState						/* Some required off state bits are on */
			oris	r3,r3,MPPICStop>>16					/* Set the stop bit */
			b		KillBusy							/* Get stopped... */


/******************************************************************************************************** */
/* */
/*			The SIGP function.  All we care about here is that the guy is online. */
/* */
/******************************************************************************************************** */

ISIGP:		lis		r4,(MPPICOnline+MPPICReady)>>16		/* Get states required to be set */
			and		r6,r3,r4							/* See if they are on */
			lwz		r7,MPPICPriv(r9)					/* Get the private flags */
			cmplw	cr0,r6,r4							/* Are they all on? */
			oris	r6,r7,(MPPICSigp>>16)&0x0000FFFF	/* Set the SIGP pending bit */
			bne-	BadRuptState						/* Some required off state bits are on */
			lwz		r4,MPPICParm0(r9)					/* Get the SIGP parameter */
			stw		r6,MPPICPriv(r9)					/* Stick the pending bit back */
			stw		r4,MPPICParm0BU(r9)					/* Back up parm 0 so it is safe once we unlock */
			b		KillBusy							/* Get stopped... */

/******************************************************************************************************** */
/* */
/*			The store status function.  This guy requires that the processor be in the stopped state. */
/* */
/******************************************************************************************************** */

IStatus:	lis		r4,MPPICOnline>>16					/* All we care about is if we are online or not */
			and.	r6,r3,r4							/* See if we are online */
			isync										/* Make sure we havn't gone past here */
			beq-	BadRuptState						/* Some required off state bits are on */
			lwz		r4,MPPICParm0(r9)					/* Get the status area physical address */
			rlwinm.	r6,r3,0,3,3							/* Test processor ready */
			
			beq		INotReady							/* Not ready, don't assume valid exception save area */
			bl		StoreStatus							/* Go store off all the registers 'n' stuff */
			b		KillBusy							/* All done... */
			
INotReady:
			lis		r7,0xDEAD							/* Get 0xDEAD + 1 */
			ori		r7,r7,0xF1D0						/* Get 0xDEADF1D0 */
			stw		r7,CSAgpr+(0*4)(r4)					/* Store invalid R0 */
			stw		r7,CSAgpr+(1*4)(r4)					/* Store invalid R1 */
			stw		r7,CSAgpr+(2*4)(r4)					/* Store invalid R2 */
			stw		r7,CSAgpr+(3*4)(r4)					/* Store invalid R3 */
			stw		r7,CSAgpr+(4*4)(r4)					/* Store invalid R4 */
			stw		r7,CSAgpr+(5*4)(r4)					/* Store invalid R5 */
			stw		r7,CSAgpr+(6*4)(r4)					/* Store invalid R6 */
			stw		r7,CSAgpr+(7*4)(r4)					/* Store invalid R7 */
			stw		r7,CSAgpr+(8*4)(r4)					/* Store invalid R8 */
			stw		r7,CSAgpr+(9*4)(r4)					/* Store invalid R9 */
			stw		r7,CSAgpr+(10*4)(r4)				/* Store invalid R10 */
			stw		r7,CSAgpr+(11*4)(r4)				/* Store invalid R11 */
			stw		r7,CSAgpr+(12*4)(r4)				/* Store invalid R12 */
			stw		r13,CSAgpr+(13*4)(r4)				/* Save general registers */
			stw		r14,CSAgpr+(14*4)(r4)				/* Save general registers */
			stw		r15,CSAgpr+(15*4)(r4)				/* Save general registers */
			stw		r16,CSAgpr+(16*4)(r4)				/* Save general registers */
			stw		r17,CSAgpr+(17*4)(r4)				/* Save general registers */
			stw		r18,CSAgpr+(18*4)(r4)				/* Save general registers */
			stw		r19,CSAgpr+(19*4)(r4)				/* Save general registers */
			stw		r20,CSAgpr+(20*4)(r4)				/* Save general registers */
			stw		r21,CSAgpr+(21*4)(r4)				/* Save general registers */
			stw		r22,CSAgpr+(22*4)(r4)				/* Save general registers */
			stw		r23,CSAgpr+(23*4)(r4)				/* Save general registers */
			stw		r24,CSAgpr+(24*4)(r4)				/* Save general registers */
			stw		r25,CSAgpr+(25*4)(r4)				/* Save general registers */
			stw		r26,CSAgpr+(26*4)(r4)				/* Save general registers */
			stw		r27,CSAgpr+(27*4)(r4)				/* Save general registers */
			stw		r28,CSAgpr+(28*4)(r4)				/* Save general registers */
			stw		r29,CSAgpr+(29*4)(r4)				/* Save general registers */
			stw		r30,CSAgpr+(30*4)(r4)				/* Save general registers */
			stw		r31,CSAgpr+(31*4)(r4)				/* Save general registers */
			bl		StoreLiveStatus
			b		KillBusy

/* */
/*			Save the whole status.  Lot's of busy work. */
/*			Anything marked unclean is of the devil and should be shunned.  Actually, it depends upon  */
/*			knowledge of firmware control areas and is no good for a plug in.  But, we've sacrificed the */
/*			white ram and are standing within a circle made of his skin, so we can dance with the devil */
/*			safely. */
/* */

StoreStatus:
			mfspr	r10,sprg0							/* Get the pointer to the exception save area (unclean) */
			
			lwz		r5,saver0(r13)						/* Get R0 (unclean) */
			lwz		r6,saver1(r13)						/* Get R1 (unclean) */
			lwz		r7,saver2(r13)						/* Get R2 (unclean) */
			stw		r5,CSAgpr+(0*4)(r4)					/* Save R0 */
			stw		r6,CSAgpr+(1*4)(r4)					/* Save R1 */
			stw		r7,CSAgpr+(2*4)(r4)					/* Save R2 */
			lwz		r5,saver3(r13)						/* Get R3 (unclean) */
			lwz		r6,saver4(r13)						/* Get R4 (unclean) */
			lwz		r7,saver5(r13)						/* Get R5 (unclean) */
			stw		r5,CSAgpr+(3*4)(r4)					/* Save R3 */
			stw		r6,CSAgpr+(4*4)(r4)					/* Save R4 */
			stw		r7,CSAgpr+(5*4)(r4)					/* Save R5 */
			lwz		r5,saver6(r13)						/* Get R6 (unclean) */
			lwz		r6,saver7(r13)						/* Get R7 (unclean) */
			lwz		r7,saver8(r13)						/* Get R8 (unclean) */
			stw		r5,CSAgpr+(6*4)(r4)					/* Save R6 */
			stw		r6,CSAgpr+(7*4)(r4)					/* Save R7 */
			stw		r7,CSAgpr+(8*4)(r4)					/* Save R8 */
			lwz		r5,saver9(r13)						/* Get R9 (unclean) */
			lwz		r6,saver10(r13)						/* Get R10 (unclean) */
			lwz		r7,saver11(r13)						/* Get R11 (unclean) */
			stw		r5,CSAgpr+(9*4)(r4)					/* Save R9 */
			stw		r6,CSAgpr+(10*4)(r4)				/* Save R10 */
			lwz		r5,saver12(r13)						/* Get R12 (unclean) */
			stw		r7,CSAgpr+(11*4)(r4)				/* Save R11 */
			stw		r5,CSAgpr+(12*4)(r4)				/* Save R12 */
			
			lwz		r5,saver13(r13)						/* Get R13 (unclean) */
			lwz		r6,saver14(r13)						/* Get R14 (unclean) */
			lwz		r7,saver15(r13)						/* Get R15 (unclean) */
			stw		r5,CSAgpr+(13*4)(r4)				/* Save R13 */
			stw		r6,CSAgpr+(14*4)(r4)				/* Save R14 */
			stw		r7,CSAgpr+(15*4)(r4)				/* Save R15 */
			lwz		r5,saver16(r13)						/* Get R16 (unclean) */
			lwz		r6,saver17(r13)						/* Get R17 (unclean) */
			lwz		r7,saver18(r13)						/* Get R18 (unclean) */
			stw		r5,CSAgpr+(16*4)(r4)				/* Save R16 */
			stw		r6,CSAgpr+(17*4)(r4)				/* Save R17 */
			stw		r7,CSAgpr+(18*4)(r4)				/* Save R18 */
			lwz		r5,saver19(r13)						/* Get R19 (unclean) */
			lwz		r6,saver20(r13)						/* Get R20 (unclean) */
			lwz		r7,saver21(r13)						/* Get R21 (unclean) */
			stw		r5,CSAgpr+(19*4)(r4)				/* Save R19 */
			stw		r6,CSAgpr+(20*4)(r4)				/* Save R20 */
			stw		r7,CSAgpr+(21*4)(r4)				/* Save R21 */
			lwz		r5,saver22(r13)						/* Get R22 (unclean) */
			lwz		r6,saver23(r13)						/* Get R23 (unclean) */
			lwz		r7,saver24(r13)						/* Get R24 (unclean) */
			stw		r5,CSAgpr+(22*4)(r4)				/* Save R22 */
			stw		r6,CSAgpr+(23*4)(r4)				/* Save R23*/
			stw		r7,CSAgpr+(24*4)(r4)				/* Save R24 */
			lwz		r5,saver25(r13)						/* Get R25 (unclean) */
			lwz		r6,saver26(r13)						/* Get R26 (unclean) */
			lwz		r7,saver27(r13)						/* Get R27 (unclean) */
			stw		r5,CSAgpr+(25*4)(r4)				/* Save R25 */
			stw		r6,CSAgpr+(26*4)(r4)				/* Save R26 */
			stw		r7,CSAgpr+(27*4)(r4)				/* Save R27 */

			lwz		r5,saver28(r13)						/* Get R28 (unclean) */
			lwz		r6,saver29(r13)						/* Get R29 (unclean) */
			lwz		r7,saver30(r13)						/* Get R30 (unclean) */
			stw		r5,CSAgpr+(28*4)(r4)				/* Save R28 */
			lwz		r5,saver31(r13)						/* Get R31(unclean) */
			stw		r6,CSAgpr+(29*4)(r4)				/* Save R29 */
			stw		r7,CSAgpr+(30*4)(r4)				/* Save R30 */
			stw		r5,CSAgpr+(31*4)(r4)				/* Save R31 */

StoreLiveStatus:
			mfmsr	r5									/* Get the current MSR */
			ori		r6,r5,0x2000						/* Turn on floating point instructions */
			mtmsr	r6									/* Turn them on */
			isync										/* Make sure they're on */
			
			stfd 	f0,CSAfpr+(0*8)(r4)					/* Save floating point registers */
			stfd	f1,CSAfpr+(1*8)(r4)					/* Save floating point registers */
			stfd	f2,CSAfpr+(2*8)(r4)					/* Save floating point registers */
			stfd	f3,CSAfpr+(3*8)(r4)					/* Save floating point registers */
			stfd	f4,CSAfpr+(4*8)(r4)					/* Save floating point registers */
			stfd	f5,CSAfpr+(5*8)(r4)					/* Save floating point registers */
			stfd	f6,CSAfpr+(6*8)(r4)					/* Save floating point registers */
			stfd	f7,CSAfpr+(7*8)(r4)					/* Save floating point registers */
			stfd	f8,CSAfpr+(8*8)(r4)					/* Save floating point registers */
			stfd	f9,CSAfpr+(9*8)(r4)					/* Save floating point registers */
			stfd	f10,CSAfpr+(10*8)(r4)				/* Save floating point registers */
			stfd	f11,CSAfpr+(11*8)(r4)				/* Save floating point registers */
			stfd	f12,CSAfpr+(12*8)(r4)				/* Save floating point registers */
			stfd	f13,CSAfpr+(13*8)(r4)				/* Save floating point registers */
			stfd	f14,CSAfpr+(14*8)(r4)				/* Save floating point registers */
			stfd	f15,CSAfpr+(15*8)(r4)				/* Save floating point registers */
			stfd	f16,CSAfpr+(16*8)(r4)				/* Save floating point registers */
			stfd	f17,CSAfpr+(17*8)(r4)				/* Save floating point registers */
			stfd	f18,CSAfpr+(18*8)(r4)				/* Save floating point registers */
			stfd	f19,CSAfpr+(19*8)(r4)				/* Save floating point registers */
			stfd	f20,CSAfpr+(20*8)(r4)				/* Save floating point registers */
			stfd	f21,CSAfpr+(21*8)(r4)				/* Save floating point registers */
			stfd	f22,CSAfpr+(22*8)(r4)				/* Save floating point registers */
			stfd	f23,CSAfpr+(23*8)(r4)				/* Save floating point registers */
			stfd	f24,CSAfpr+(24*8)(r4)				/* Save floating point registers */
			stfd	f25,CSAfpr+(25*8)(r4)				/* Save floating point registers */
			stfd	f26,CSAfpr+(26*8)(r4)				/* Save floating point registers */
			stfd	f27,CSAfpr+(27*8)(r4)				/* Save floating point registers */
			stfd	f28,CSAfpr+(28*8)(r4)				/* Save floating point registers */
			stfd	f29,CSAfpr+(29*8)(r4)				/* Save floating point registers */
			stfd	f30,CSAfpr+(30*8)(r4)				/* Save floating point registers */
			stfd	f31,CSAfpr+(31*8)(r4)				/* Save floating point registers */
			
			mffs	f1									/* Get the FPSCR */
			stfd	f1,CSAfpscr-4(r4)					/* Save the whole thing (we'll overlay the first half with CR later) */
			
			lfd		f1,CSAfpr+(1*4)(r4)					/* Restore F1 */
			
			mtmsr	r5									/* Put the floating point back to what it was before */
			isync										/* Wait for it */
			
			lwz		r6,savecr(r13)						/* Get the old CR (unclean) */
			stw		r6,CSAcr(r4)						/* Save the CR */
			
			mfxer	r6									/* Get the XER */
			stw		r6,CSAxer(r4)						/* Save the XER */
			
			lwz		r6,savelr(r13)						/* Get the old LR (unclean) */
			stw		r6,CSAlr(r4)						/* Save the LR */
			
			mfctr	r6									/* Get the CTR */
			stw		r6,CSActr(r4)						/* Save the CTR */
			
STtbase:	mftbu	r5									/* Get the upper timebase */
			mftb	r6									/* Get the lower */
			mftbu	r7									/* Get the top again */
			cmplw	cr0,r5,r7							/* Did it tick? */
			bne-	STtbase								/* Yeah, do it again... */
			
			mfdec	r7									/* Get the decrimenter (make it at about the same time as the TB) */
			stw		r7,CSAdec(r4)						/* Save the decrimenter */
			
			
			stw		r5,CSAtbu(r4)						/* Stash the top part */
			stw		r6,CSAtbl(r4)						/* Stash the lower part */
			
			lwz		r5,savesrr1(r13)					/* SRR1 at exception is as close as we get to the MSR (unclean) */
			lwz		r6,savesrr0(r13)					/* Get SRR0 also */
			stw		r5,CSAmsr(r4)						/* Save the MSR */
			stw		r6,CSApc(r4)						/* Save the PC */
			stw		r5,CSAsrr1(r4)						/* Set SRR1 also */
			stw		r6,CSAsrr0(r4)						/* Save SRR0 */
			
			mfpvr	r5									/* Get the PVR */
			stw		r5,CSApvr(r4)						/* Save the PVR */
			
			mfspr	r5,pir								/* Get the PIR */
			stw		r5,CSApir(r4)						/* Save the PIR */
			
			mfspr	r5,ibat0u							/* Get the upper IBAT0 */
			mfspr	r6,ibat0l							/* Get the lower IBAT0 */
			stw		r5,CSAibat+(0*8+0)(r4)				/* Save the upper IBAT0 */
			stw		r6,CSAibat+(0*8+4)(r4)				/* Save the upper IBAT0 */

			mfspr	r5,ibat1u							/* Get the upper IBAT1 */
			mfspr	r6,ibat1l							/* Get the lower IBAT1 */
			stw		r5,CSAibat+(1*8+0)(r4)				/* Save the upper IBAT1 */
			stw		r6,CSAibat+(1*8+4)(r4)				/* Save the upper IBAT1 */

			mfspr	r5,ibat2u							/* Get the upper IBAT2 */
			mfspr	r6,ibat2l							/* Get the lower IBAT2 */
			stw		r5,CSAibat+(2*8+0)(r4)				/* Save the upper IBAT2 */
			stw		r6,CSAibat+(2*8+4)(r4)				/* Save the upper IBAT2 */

			mfspr	r5,ibat3u							/* Get the upper IBAT3 */
			mfspr	r6,ibat3l							/* Get the lower IBAT3 */
			stw		r5,CSAibat+(3*8+0)(r4)				/* Save the upper IBAT3 */
			stw		r6,CSAibat+(3*8+4)(r4)				/* Save the upper IBAT3 */

			mfspr	r5,dbat0u							/* Get the upper DBAT0 */
			mfspr	r6,dbat0l							/* Get the lower DBAT0 */
			stw		r5,CSAdbat+(0*8+0)(r4)				/* Save the upper DBAT0 */
			stw		r6,CSAdbat+(0*8+4)(r4)				/* Save the upper DBAT0 */

			mfspr	r5,dbat1u							/* Get the upper DBAT1 */
			mfspr	r6,dbat1l							/* Get the lower DBAT1 */
			stw		r5,CSAdbat+(1*8+0)(r4)				/* Save the upper DBAT1 */
			stw		r6,CSAdbat+(1*8+4)(r4)				/* Save the upper DBAT1 */

			mfspr	r5,dbat2u							/* Get the upper DBAT2 */
			mfspr	r6,dbat2l							/* Get the lower DBAT2 */
			stw		r5,CSAdbat+(2*8+0)(r4)				/* Save the upper DBAT2 */
			stw		r6,CSAdbat+(2*8+4)(r4)				/* Save the upper DBAT2 */

			mfspr	r5,dbat3u							/* Get the upper DBAT3 */
			mfspr	r6,dbat3l							/* Get the lower DBAT3 */
			stw		r5,CSAdbat+(3*8+0)(r4)				/* Save the upper DBAT3 */
			stw		r6,CSAdbat+(3*8+4)(r4)				/* Save the upper DBAT3 */
			
			mfsdr1	r5									/* Get the SDR1 */
			stw		r5,CSAsdr1(r4)						/* Save the SDR1 */
			
			mfsr	r5,sr0								/* Get SR 0 */
			mfsr	r6,sr1								/* Get SR 1 */
			mfsr	r7,sr2								/* Get SR 2 */
			stw		r5,CSAsr+(0*4)(r4)					/* Save SR 0 */
			stw		r6,CSAsr+(1*4)(r4)					/* Save SR 1 */
			mfsr	r5,sr3								/* Get SR 3 */
			mfsr	r6,sr4								/* Get SR 4 */
			stw		r7,CSAsr+(2*4)(r4)					/* Save SR 2 */
			mfsr	r7,sr5								/* Get SR 5 */
			stw		r5,CSAsr+(3*4)(r4)					/* Save SR 3 */
			stw		r6,CSAsr+(4*4)(r4)					/* Save SR 4 */
			mfsr	r5,sr6								/* Get SR 6 */
			mfsr	r6,sr7								/* Get SR 7 */
			stw		r7,CSAsr+(5*4)(r4)					/* Save SR 5 */
			mfsr	r7,sr8								/* Get SR 8 */
			stw		r5,CSAsr+(6*4)(r4)					/* Save SR 6 */
			stw		r6,CSAsr+(7*4)(r4)					/* Save SR 7 */
			mfsr	r5,sr9								/* Get SR 9 */
			mfsr	r6,sr10								/* Get SR 11 */
			stw		r7,CSAsr+(8*4)(r4)					/* Save SR 8 */
			mfsr	r7,sr11								/* Get SR 11 */
			stw		r5,CSAsr+(9*4)(r4)					/* Save SR 9 */
			stw		r6,CSAsr+(10*4)(r4)					/* Save SR 10 */
			mfsr	r5,sr12								/* Get SR 12 */
			mfsr	r6,sr13								/* Get SR 13 */
			stw		r7,CSAsr+(11*4)(r4)					/* Save SR 11 */
			mfsr	r7,sr14								/* Get SR 14 */
			stw		r5,CSAsr+(12*4)(r4)					/* Save SR 12 */
			stw		r6,CSAsr+(13*4)(r4)					/* Save SR 13 */
			mfsr	r5,sr15								/* Get SR 15 */
			stw		r7,CSAsr+(14*4)(r4)					/* Save SR 14 */
			stw		r5,CSAsr+(15*4)(r4)					/* Save SR 15 */
			
			mfdar	r6									/* Get the DAR */
			stw		r6,CSAdar(r4)						/* Save it */
			
			mfdsisr	r5									/* Get the DSISR */
			stw		r5,CSAdsisr(r4)						/* Save it */
			
			stw		r10,CSAsprg+(1*4)(r4)				/* Save SPRG1 */
			mfspr	r7,sprg0							/* Get SPRG0 */
			mfspr	r6,sprg2							/* Get SPRG2 */
			stw		r7,CSAsprg+(0*4)(r4)				/* Save SPRG0 */
			mfspr	r5,sprg3							/* Get SPRG3 */
			stw		r6,CSAsprg+(2*4)(r4)				/* Save SPRG2 */
			stw		r5,CSAsprg+(3*4)(r4)				/* Save SPRG4 */
			
			mfspr	r6,1013								/* Get the DABR */
			mfspr	r7,1010								/* Get the IABR */
			stw		r6,CSAdabr(r4)						/* Save the DABR */
			stw		r7,CSAiabr(r4)						/* Save the IABR */
			
			mfspr	r5,282								/* Get the EAR */
			stw		r5,CSAear(r4)						/* Save the EAR */
			
			lis		r7,0xDEAD							/* Get 0xDEAD */
			ori		r7,r7,0xF1D0						/* Get 0xDEADF1D0 */
			
			mfpvr	r5									/* Get the processor type */
			rlwinm	r5,r5,16,16,31						/* Isolate the processor */
			cmplwi	cr1,r5,4							/* Set CR1_EQ if this is a plain 604, something else if it's a 604E */
			
			mfspr	r6,hid0								/* Get HID0 */
			mr		r5,r7								/* Assume 604 */
			beq		cr1,NoHID1							/* It is... */
			mfspr	r5,hid1								/* Get the HID1 */

NoHID1:		stw		r6,CSAhid+(0*4)(r4)					/* Save HID0 */
			stw		r5,CSAhid+(1*4)(r4)					/* Save HID1 */
			stw		r7,CSAhid+(2*4)(r4)					/* Save HID2 */
			stw		r7,CSAhid+(3*4)(r4)					/* Save HID3 */
			stw		r7,CSAhid+(4*4)(r4)					/* Save HID4 */
			stw		r7,CSAhid+(5*4)(r4)					/* Save HID5 */
			stw		r7,CSAhid+(6*4)(r4)					/* Save HID6 */
			stw		r7,CSAhid+(7*4)(r4)					/* Save HID7 */
			stw		r7,CSAhid+(8*4)(r4)					/* Save HID8 */
			stw		r7,CSAhid+(9*4)(r4)					/* Save HID9 */
			stw		r7,CSAhid+(10*4)(r4)				/* Save HID10 */
			stw		r7,CSAhid+(11*4)(r4)				/* Save HID11 */
			stw		r7,CSAhid+(12*4)(r4)				/* Save HID12 */
			stw		r7,CSAhid+(13*4)(r4)				/* Save HID13 */
			stw		r7,CSAhid+(14*4)(r4)				/* Save HID14 */
			stw		r7,CSAhid+(15*4)(r4)				/* Save HID15 */
			
			mfspr	r6,952								/* Get MMCR0 */
			mr		r5,r7								/* Assume 604 */
			beq		NoMMCR1								/* It is... */
			mfspr	r5,956								/* Get the MMCR1 */

NoMMCR1:	stw		r6,CSAmmcr+(0*4)(r4)				/* Save MMCR0 */
			stw		r5,CSAmmcr+(1*4)(r4)				/* Save MMCR1 */
			
			mfspr	r6,953								/* Get PMC1 */
			mfspr	r5,954								/* Get PMC2 */
			stw		r6,CSApmc+(0*4)(r4)					/* Save PMC1 */
			stw		r5,CSApmc+(1*4)(r4)					/* Save PMC2 */
			
			mr		r6,r7								/* Assume 604 */
			mr		r5,r7								/* Assume 604 */
			beq		NoPMC3								/* Yeah... */
			mfspr	r6,957								/* Get the PMC3 for a 604E */
			mfspr	r5,958								/* Get the PMC4 for a 604E */
			
NoPMC3:		stw		r6,CSApmc+(2*4)(r4)					/* Save PMC3 */
			stw		r5,CSApmc+(3*4)(r4)					/* Save PMC4 */

			mfspr	r6,955								/* Get SIA */
			mfspr	r5,959								/* Get SDA */
			stw		r6,CSAsia(r4)						/* Save the SIA */
			stw		r5,CSAsda(r4)						/* Save the SDA */
			
			stw		r7,CSAmq(r4)						/* There is no MQ on either the 604 or 604E */
			
			
			lwz		r6,MPPICStat(r9)					/* Get the status of this processor */
			lis		r10,MPPICReady>>16					/* Get the flag for reset or not */
			li		r5,kSIGPResetState					/* Assume we're operating */
			and.	r0,r6,r10							/* See if the ready bit is set */
			lis		r10,MPPICStop>>16					/* Get the flag for stopped or not */
			beq		SetStateInf							/* Go set that we are reset... */
			and.	r0,r6,r10							/* Are we stopped? */
			li		r5,kSIGPStoppedState				/* Assume we area */
			bne		SetStateInf							/* We are, go set it... */
			li		r5,kSIGPOperatingState				/* Not stopped, so we're going */
			
SetStateInf: stb		r5,CSAstate(r4)					/* Set the state byte */
			
			li		r0,1								/* Set the truth */
			sync										/* Make sure it's stored */
			
			stb		r0,CSAregsAreValid(r4)				/* Set that the status is valid */

			blr											/* We're done here... */
			

/******************************************************************************************************** */
/* */
/*			The synchronize time base function.  No state requirements for this one. */
/* */
/******************************************************************************************************** */

ITBsync:												/* This handles the synchronize time base function */
			lis		r12,HIGH_ADDR(MPPIwork)				/* Get the top of work area */
			li		r0,MPPICfTBsy1						/* Get the flag for TB sync state 1 */
			li		r7,0								/* Get a 0 */
			ori		r12,r12,LOW_ADDR(MPPIwork)			/* Get low part of work area */
			mttbl	r7									/* Clear the bottom of the TB so's there's noupper ticks */
			mttbu	r7									/* Clear the top part, just 'cause I wanna */
			
			sync										/* Make sure all is saved */
			stb		r0,MPPICStat+2(r9)					/* Tell the main dude to tell us the time */
			isync										/* Make sure we don't go nowhere's */

/* */
/*			Remember that the sync'ing processor insures that the TB won't tick the high part for at least */
/*			16k ticks.  That should be way longer than we need for the whole process here */
/* */

WaitTBLower: lwz	r5,MPPITBsync+4-MPPIwork(r12)		/* Get the lower part of the TB */
			mttbl	r5									/* Put it in just in case it's set now */
			mr.		r5,r5								/* Was it actually? */
			beq+	WaitTBLower							/* Nope, go check again... */
			lwz		r4,MPPITBsync-MPPIwork(r12)			/* Get the high order part */
			mttbu	r4									/* Set the top half also */

			stw		r7,MPPITBsync+4-MPPIwork(r12)		/* Tell 'em we've got it */
			
			sync
			
			li		r4,0								/* Clear this */
			la		r5,MPPISncFght-32-MPPIwork(r12)		/* Point to the squared circle (our corner) */

			b		TB1stPnch							/* Go take the first punch... */

TBSargue:	
			dcbf	0,r5								/* *** Fix cache coherency (data integrity) HW bug *** */
			sync										/* *** Fix cache coherency (data integrity) HW bug *** */
			lwz		r6,0(r5)							/* Listen for the procecution's argument */
			mr.		r6,r6								/* See if they are done */
			beq+	TBSargue							/* Nope, still going... */
			
TB1stPnch:	mftb	r7									/* They're done, time for rebuttal */
			stw		r7,32(r5)							/* Make rebuttle */
	
			addi	r4,r4,1								/* Count rounds */

			cmplwi	cr0,r4,10							/* See if we've gone 9 more rounds */
			addi	r5,r5,64							/* Point to the next round areas */
		
			blt+	TBSargue							/* Not yet, come out of your corners fighting... */
			
/* */
/*			We'll set the latest-up-to-datest from the other processor now */
/* */
TBSetTB:		
			dcbf	0,r5								/* *** Fix cache coherency (data integrity) HW bug *** */
			sync										/* *** Fix cache coherency (data integrity) HW bug *** */
			lwz		r6,0(r5)							/* Listen for the procecution's argument */
			mttbl	r6									/* Set it just in case it's ok */
			mr.		r6,r6								/* See if they are done */
			beq+	TBSetTB								/* Nope, still going... */

/* */
/*			Get average duration for each processor.  We skip the first pass on the asumption */
/*			that the caches were not warmed up and it would take longer.  In proctice this */
/*			is what was seen. */
/* */

			mr		r0,r11								/* Move return address to a safe register */

			li		r4,0								/* Clear a counter */
			li		r3,0								/* Clear accumulator for duration */
			li		r10,0								/* Clear start time accumulator top half */
			li		r11,0								/* Clear start time accumulator bottom half */
			li		r1,0								/* Clear start time accumulator top half */
			li		r2,0								/* Clear start time accumulator bottom half */
			li		r10,0								/* Clear accumulator for durations */
			la		r5,MPPISncFght+64-MPPIwork(r12)		/* Get second round start time address */
		
TBSaccumU:	lwz		r6,0(r5)							/* Get start time */
			lwz		r11,32(r5)							/* Get the other processor's start time */
			lwz		r7,64(r5)							/* Get end time */
			lwz		r8,96(r5)							/* Other proc's end time */
			sub		r7,r7,r6							/* Get duration */
			sub		r8,r8,r11							/* Get other side's duration */
			addi	r4,r4,1								/* Count arguments */
			add		r3,r3,r7							/* Accumulate durations */
			add		r2,r2,r7							/* Accumulate other side's durations */
			cmplwi	cr0,r4,8							/* Have we gotten them all yet? */
			addi	r5,r5,64							/* Step to the next argument */
			blt+	TBSaccumU							/* We're not done yet... */

			add		r7,r2,r3							/* Sum the two differences */
			addi	r7,r7,0x10							/* Round up */
			rlwinm	r7,r7,27,5,31						/* Get the average difference divided in half */

			mftb	r8									/* Get the time now */
			add		r8,r8,r7							/* Slide the window */
			mttbl	r8									/* Set the time */
			
			stw		r12,MPPITBsync+4-MPPIwork(r12)		/* Show that we are done */
			
			lwz		r3,MPPICStat(r9)					/* Get back our status */
			mr		r11,r0								/* Restore the return register */
			b		KillBusy							/* We're all done now, done for it, c'est la vie... */
			

/******************************************************************************************************** */
/* */
/*			The reset function.  No state requirements for this one. */
/*			This suicides the processor. Our caller is never returned to (good english).  The only way out of  */
/*			this is a start function subsequently.  So, we give a flying f**k about the registers 'n' sutff. */
/* */
/******************************************************************************************************** */

IReset:		lis		r28,0x8000							/* Turn on machine checks */
			
			ori		r28,r28,0xCC84						/* Enable caches, clear them,  */
														/* disable serial execution and turn BHT on */
			sync
			mtspr	HID0,r28							/* Start the cache clear */
			sync

/* */
/*			Clear out the TLB.  They be garbage after hard reset. */
/* */

			li		r0,512								/* Get number of TLB entries (FIX THIS) */
			li		r3,0								/* Start at 0 */
			mtctr	r0									/* Set the CTR */
			
IRpurgeTLB:	tlbie	r3									/* Purge this entry */
			addi	r3,r3,4096							/* Next page */
			bdnz	IRpurgeTLB							/* Do 'em all... */
			
			sync										/* Make sure all TLB purges are done */
			tlbsync										/* Make sure on other processors also */
			sync										/* Make sure the TLBSYNC is done */

/* */
/*			Clear out the BATs. */
/* */

			li		r3,0								/* Clear a register */
			
			mtspr	DBAT0L,r3							/* Clear BAT */
			mtspr	DBAT0U,r3							/* Clear BAT */
			mtspr	DBAT1L,r3							/* Clear BAT */
			mtspr	DBAT1U,r3							/* Clear BAT */
			mtspr	DBAT2L,r3							/* Clear BAT */
			mtspr	DBAT2U,r3							/* Clear BAT */
			mtspr	DBAT3L,r3							/* Clear BAT */
			mtspr	DBAT3U,r3							/* Clear BAT */
			
			mtspr	IBAT0L,r3							/* Clear BAT */
			mtspr	IBAT0U,r3							/* Clear BAT */
			mtspr	IBAT1L,r3							/* Clear BAT */
			mtspr	IBAT1U,r3							/* Clear BAT */
			mtspr	IBAT2L,r3							/* Clear BAT */
			mtspr	IBAT2U,r3							/* Clear BAT */
			mtspr	IBAT3L,r3							/* Clear BAT */
			mtspr	IBAT3U,r3							/* Clear BAT */

/* */
/*			Map 0xF0000000 to 0xFFFFFFFF for I/O; make it R/W non-cacheable */
/*			Map	0x00000000 to 0x0FFFFFFF for mainstore; make it R/W cachable */
/* */

			lis		r6,0xF000							/* Set RPN to last segment */
			ori		r6,r6,0x1FFF						/* Set up upper BAT for 256M, access both */
			
			lis		r7,0xF000							/* Set RPN to last segment */
			ori		r7,r7,0x0032						/* Set up lower BAT for 256M, access both, non-cachable */
		
			mtspr	DBAT0L,r7							/* Setup ROM and I/O mapped areas */
			mtspr	DBAT0U,r6							/* Now do the upper DBAT */
			sync
	
			li		r6,0x1FFF							/* Set up upper BAT for 256M, access both */
			li		r7,0x0012							/* Set up lower BAT for r/w access */
			
			mtspr	DBAT1L,r7							/* Set up an initial view of mainstore */
			mtspr	DBAT1U,r6							/* Now do the upper DBAT */
			sync

/* */
/*			Clean up SDR and segment registers */
/* */

			li		r3,0								/* Clear a register */
			mtspr	SDR1,r3								/* Clear SDR1 */
			
			li		r4,0								/* Clear index for segment registers */
			lis		r5,0x1000							/* Set the segment indexer */
			
IRclearSR:	mtsrin	r3,r4								/* Zero out the SR */
			add.	r4,r4,r5							/* Point to the next segment */
			bne-	IRclearSR							/* Keep going until we wrap back to 0 */
			
			lis		r3,(MPPICOnline+MPPICStop)>>16		/* Set the reset/online state flags */
			b		KillBusy							/* Go wipe out the busy flags... */

/*	(TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG)  */
/* */
/*			Here lies the Phoney Firmware used to test SIGPs.  Take this out later. */
/* */
/*	(TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG)  */

mp_PhoneyFirmware:

			li		r27,0x3040							/* Set floating point and machine checks on, IP to 0xFFF0xxxx */
			mtmsr	r27									/* Load 'em on in */
			isync
			
			bl		PhoneyBase							/* Make a base register */
PhoneyBase:	mflr	r26									/* Get it */
			addi	r26,r26,LOW_ADDR(MPPIbase-PhoneyBase)	/* Adjust it back */

			la		r20,LOW_ADDR(rupttab-MPPIbase)(r26)		/* Get the address of the interrupt table */
			la		r21,LOW_ADDR(rupttabend-MPPIbase)(r26)	/* Get the end of the table */
			
relocate:	lwz		r22,0(r20)							/* Get the displacement to routine */
			add		r22,r22,r12							/* Relocate to the physical address */
			stw		r22,0(r20)							/* Stick it back */
			addi	r20,r20,4							/* Point to the next one */
			cmplw	cr0,r20,r21							/* Still in table? */
			ble+	cr0,relocate						/* Yeah... */
			
			la		r20,LOW_ADDR(rupttab-MPPIbase)(r26)	/* Get the interrupt table back again */
			mtsprg	3,r20								/* Activate the phoney Rupt table */
			
			lis		r24,hi16(HammerHead)				/* Get the actual hammerhead address */
			ori		r24,r24,0x0032						/* Make R/W non-cachable */
			lwz		r23,MPPIHammer-MPPIwork(r12)		/* Get the address mapped on the main processor */
			ori		r23,r23,0x0003						/* Set both super and user valid for 128KB */
			
			mtspr	DBAT0L,r24							/* Setup hammerhead's real address */
			mtspr	DBAT0U,r23							/* Map hammerhead to the same virtual address as on the main processor */
			sync										/* Make sure it is done */

			la		r25,MPPICPU2-MPPIwork(r12)			/* Point to a phoney register save area */
			mtsprg	1,r25								/* Phoney up initialized processor state */

			lis		r24,0xFEED							/* Get 0xFEED */
			ori		r24,r24,0xF1D0						/* Get 0xFEEDF1D0 */
			
			stw		r24,CSAgpr+(0*4)(r25)				/* Store invalid R0 */
			stw		r24,CSAgpr+(1*4)(r25)				/* Store invalid R1 */
			stw		r24,CSAgpr+(2*4)(r25)				/* Store invalid R2 */
			stw		r24,CSAgpr+(3*4)(r25)				/* Store invalid R3 */
			stw		r24,CSAgpr+(4*4)(r25)				/* Store invalid r4 */
			stw		r24,CSAgpr+(5*4)(r25)				/* Store invalid R5 */
			stw		r24,CSAgpr+(6*4)(r25)				/* Store invalid R6 */
			stw		r24,CSAgpr+(7*4)(r25)				/* Store invalid r7 */
			stw		r24,CSAgpr+(8*4)(r25)				/* Store invalid R8 */
			stw		r24,CSAgpr+(9*4)(r25)				/* Store invalid R9 */
			stw		r24,CSAgpr+(10*4)(r25)				/* Store invalid R10 */
			stw		r24,CSAgpr+(11*4)(r25)				/* Store invalid R11 */
			stw		r24,CSAgpr+(12*4)(r25)				/* Store invalid R12 */
			
waititout:	lwz		r25,0x30(br0)						/* Get wait count */
			mfmsr	r24									/* Get the MSR */
			addi	r25,r25,1							/* Bounce it up */
			ori		r24,r24,0x8000						/* Turn on external interruptions */
			stw		r25,0x30(br0)						/* Save back the count */
			mtmsr	r24									/* Set it */
			isync										/* Stop until we're here */
			b		waititout							/* Loop forever... */
			
/* */
/*			Phoney interrupt handlers */
/* */

pexternal:	mflr	r29									/* Get the LR value */
			lwz		r29,0(r29)							/* Get the rupt code */
			stw		r29,0x0B0(br0)						/* Save the code */
			bl		GotSignal							/* Call the signal handler */
			oris	r3,r3,0x8000						/* Turn on high bit so we see a code 0 */
			stw		r3,0xA8(br0)						/* Save return code in debug area */
			
ignorerupt:	mflr	r29									/* Get the LR value */
			lwz		r29,0(r29)							/* Get the rupt code */
			stw		r29,0x0B0(br0)						/* Save the code */
			rfi											/* Bail to from whence we commest... */
			.long	0			
			.long	0			
			.long	0			
			.long	0			
			.long	0			
			.long	0			
			.long	0			

rupttab:	.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	pexternal							/* Phoney external handler */
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
			.long	ignorerupt
rupttabend:	.long	ignorerupt

/*	(TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG)  */
/* */
/*			Here lies the end of the Phoney Firmware used to test SIGPs.  Take this out later. */
/* */
/*	(TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG) (TEST/DEBUG)  */


/* */
/*			Table of function offsets */
/* */

MPPIFuncOffs:

			.long	CountProcessors-MPPIFunctions					/* Offset to routine */
			.long	StartProcessor-MPPIFunctions					/* Offset to routine */
			.long	ResumeProcessor-MPPIFunctions					/* Offset to routine */
			.long	StopProcessor-MPPIFunctions						/* Offset to routine */
			.long	ResetProcessor-MPPIFunctions					/* Offset to routine */
			.long	SignalProcessor-MPPIFunctions					/* Offset to routine */
			.long	StoreProcessorStatus-MPPIFunctions				/* Offset to routine */
			.long	SynchClock-MPPIFunctions						/* Offset to routine */
			.long	GetExtHandlerAddress-MPPIFunctions				/* Offset to routine */
			.long	GotSignal-MPPIFunctions							/* Offset to routine */
			.long	ProcessorState-MPPIFunctions					/* Offset to routine */
			.long	RunSIGPRun-MPPIFunctions						/* Offset to routine */
			.long	mp_PhoneyFirmware-MPPIFunctions					/* (TEST/DEBUG) */

MPPISize:
	
