;	Important note: If you want more than r3-r5, get them from ECB!!!

;	Unimplemented MPCalls from MPLibrary:
;		NKSetPrInfoPageSize			109
;		NKSetPrInfoILockSizes		110
;		NKSetPrInfoTransCache		111
;		NKSetPrInfoL1Cache			112
;		NKSetPrInfoL2Cache			113



;	AUTO-GENERATED SYMBOL LIST
;	IMPORTS:
;	  NKAddressSpaces
;	    SpaceL2PIgnoringBATs
;	    SpaceL2PUsingBATs
;	  NKConsoleLog
;	    printb
;	    printd
;	    printh
;	    prints
;	    printw
;	  NKIndex
;	    DeleteID
;	    GetNextIDOfClass
;	    LookupID
;	    MakeID
;	  NKInterrupts
;	    IntReturn
;	    SIGP
;	  NKPaging
;	    PagingL2PWithBATs
;	  NKPoolAllocator
;	    ExtendPool
;	    PoolAllocClear
;	    PoolFree
;	  NKScheduler
;	    CalculateTimeslice
;	    FlagSchEval
;	    FlagSchEvaluationIfTaskRequires
;	    NewCpuEntryPoint
;	    Save_v0_v31
;	    SchEval
;	    SchIdleTask
;	    SchIdleTaskStopper
;	    SchRdyTaskLater
;	    SchRdyTaskNow
;	    SchRestoreStartingAtR14
;	    SchSaveStartingAtR14
;	    SchTaskUnrdy
;	  NKScreenConsole
;	    ScreenConsole_redraw
;	  NKTasks
;	    CreateTask
;	  NKThud
;	    panic
;	  NKTimers
;	    DequeueTimer
;	    EnqueueTimer
;	    GetTime
;	  NKVMCalls
;	    EditPTEInHTAB
;	    GetPARPageInfo
;	    RemovePageFromTLB
;	    VMSecondLastExportedFunc
;	EXPORTS:
;	  BlockMPCall (=> NKInterrupts, NKSync)
;	  CommonMPCallReturnPath (=> NKAddressSpaces, NKSleep, NKTasks)
;	  MPCall_6_0x78 (=> NKTasks)
;	  ReleaseAndMPCallWasBad (=> NKAddressSpaces)
;	  ReleaseAndReturnMPCall (=> NKAddressSpaces, NKSync, NKTasks)
;	  ReleaseAndReturnMPCallBlueBlocking (=> NKSync)
;	  ReleaseAndReturnMPCallInvalidIDErr (=> NKAddressSpaces, NKSync, NKTasks)
;	  ReleaseAndReturnMPCallOOM (=> NKAddressSpaces, NKSync, NKTasks)
;	  ReleaseAndReturnMPCallPrivilegedErr (=> NKAddressSpaces)
;	  ReleaseAndReturnMPCallTaskAborted (=> NKSync)
;	  ReleaseAndReturnParamErrFromMPCall (=> NKAddressSpaces, NKSync)
;	  ReleaseAndReturnZeroFromMPCall (=> NKAddressSpaces, NKSync, NKTasks)
;	  ReleaseAndScrambleMPCall (=> NKAddressSpaces, NKSync, NKTasks)
;	  ReleaseAndTimeoutMPCall (=> NKSync)
;	  ReturnMPCallInvalidIDErr (=> NKAddressSpaces, NKTasks)
;	  ReturnMPCallOOM (=> NKAddressSpaces, NKPrimaryIntHandlers, NKSleep, NKSync, NKTasks)
;	  ReturnParamErrFromMPCall (=> NKAddressSpaces, NKPrimaryIntHandlers, NKSleep)
;	  ReturnZeroFromMPCall (=> NKAddressSpaces, NKSleep, NKSync)
;	  ScrambleMPCall (=> NKSync)
;	  kcMPDispatch (=> NKInit, NKInterrupts)
;	  major_0x0b0cc (=> NKAddressSpaces)



;MPCall_Panic		set		MPCall_Panic



		if		&TYPE('NKDebugShim') != 'UNDEFINED'
MaxMPCallCount		equ		300
		else
MaxMPCallCount		equ		134
		endif



	MACRO
	DeclareMPCall	&n, &code
@h
	org				MPCallTable + 4*&n
	dc.l			&code - NKTop - 4*&n
	org				@h
	ENDM


	;	Creates a blank table without overflowing PPCAsm's default
	;	macro stack size :)

	MACRO
	CreateMPCallTbl	&n

	if			&n >= 1
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 2
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 3
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 4
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 5
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 6
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 7
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 8
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 9
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 10
	dc.l		(MPCallBad - NKTop) - (* - MPCallTable)
	endif

	if			&n >= 11
		CreateMPCallTbl	(&n) - 10
	endif

	ENDM



kcMPDispatch		;	reached by `sc`, or `twi *, *, 8`

	bl		SchSaveStartingAtR14

	lwz		r8, EWA.r6(r8)					;	clobbers our EWA pointer :(
	lwz		r14, KDP.PA_NanoKernelCode(r1)	;	but r14...
	lwz		r15, ContextBlock.r0(r6)		;	...and r15 were saved
	stw		r8, ContextBlock.r6(r6)			;	why move r6 from EWA to ContextBlock?
	b		MPCallTableEnd

MPCallTable
	CreateMPCallTbl	MaxMPCallCount
MPCallTableEnd

;	Not sure where this counter table is?

	lwz		r16, KDP.NanoKernelInfo + NKNanoKernelInfo.MPDispatchCountTblPtr(r1)
	rlwinm	r17, r15,  2, 20, 29
	cmplwi	r16, 0
	beq		@no_count
	lwzx	r18, r16, r17
	addi	r18, r18, 1
	stwx	r18, r16, r17
@no_count

	cmplwi	r15, MaxMPCallCount
	rlwimi	r14, r15, 2, 21, 29
	llabel	r16, MPCallTable
	lwzx	r15, r16, r14
	add		r15, r15, r14
	mtlr	r15
	bltlr



;	Handler for out-of-range or unimplemented (debug)
;	MPCalls.

MPCallBad	;	OUTSIDE REFERER
	li		r3, -4
	b		CommonMPCallReturnPath



ReleaseAndMPCallWasBad	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16
	b		MPCallBad



;	> r1    = kdp

ReleaseAndReturnZeroFromMPCall	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16



ReturnZeroFromMPCall	;	OUTSIDE REFERER
	li		r3,  0x00
	b		CommonMPCallReturnPath



ReleaseAndScrambleMPCall	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16



;	I'd really live a name for this.

ScrambleMPCall	;	OUTSIDE REFERER
	mfspr	r16, pvr
	rlwinm.	r16, r16,  0,  0, 14

	beq		@is_601
	mftb	r4
	b		@not_601
@is_601
	mfspr	r4, rtcl
@not_601

	xori	r16, r4,  0x1007
	xoris	r16, r16,  0x1950

	stw		r16, PSA.ScrambledMPCallTime(r1)
	li		r3, -29294
	b		CommonMPCallReturnPath




;	dead code?
	li		r3, kMPDeletedErr
	b		CommonMPCallReturnPath



ReleaseAndTimeoutMPCall	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16
	li		r3, kMPTimeOutErr
	b		CommonMPCallReturnPath



ReleaseAndReturnMPCallTaskAborted	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16
	li		r3, kMPTaskAbortedErr
	b		CommonMPCallReturnPath



ReleaseAndReturnMPCallOOM	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16



ReturnMPCallOOM	;	OUTSIDE REFERER
	li		r3, kMPInsufficientResourcesErr
	b		CommonMPCallReturnPath



ReleaseAndReturnMPCallBlueBlocking	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock + Lock.Count, scratch=r16



ReturnMPCallBlueBlocking	;	OUTSIDE REFERER
	li		r3, kMPBlueBlockingErr
	b		CommonMPCallReturnPath



ReleaseAndReturnParamErrFromMPCall	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock + Lock.Count, scratch=r16



ReturnParamErrFromMPCall	;	OUTSIDE REFERER
	li		r3, -0x32
	b		CommonMPCallReturnPath



ReleaseAndReturnMPCallPrivilegedErr	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16
	li		r3, kMPPrivilegedErr
	b		CommonMPCallReturnPath



ReleaseAndReturnMPCallInvalidIDErr	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16



ReturnMPCallInvalidIDErr	;	OUTSIDE REFERER
	li		r3, kMPInvalidIDErr
	b		CommonMPCallReturnPath



major_0x0b0cc	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock + Lock.Count, scratch=r16
	li		r3, -0x725a
	b		CommonMPCallReturnPath



ReturnZeroFromMPCall_again	;	OUTSIDE REFERER
	li		r3,  0x00
	b		CommonMPCallReturnPath



BlockMPCall	;	OUTSIDE REFERER
	crclr	10
	b		TrulyCommonMPCallReturnPath

ReleaseAndReturnMPCall	;	OUTSIDE REFERER
	_AssertAndRelease	PSA.SchLock, scratch=r16



CommonMPCallReturnPath	;	OUTSIDE REFERER
	crset	10

TrulyCommonMPCallReturnPath	;	OUTSIDE REFERER
	mfsprg	r8, 0
	lwz		r9,  0x0134(r6)
	stw		r9,  0x0018(r8)

	bc		BO_IF_NOT, 10, @block

;return immediately
	bl		SchRestoreStartingAtR14
	b		IntReturn

@block
	b		SchEval



;	Called after MPLibrary creates a new structure

	DeclareMPCall	0, MPCall_0

MPCall_0	;	OUTSIDE REFERER
	andi.	r16, r3,  0xfff
	mr		r30, r7
	mr		r29, r6
	bne		ReturnMPCallOOM

	;	Fail if this page is outside of the PAR
	rlwinm.	r4, r3, 20, 12, 31
	lwz		r9, KDP.VMLogicalPages(r1)
	beq		ReturnMPCallOOM
	cmplw	r4, r9
	bge		ReturnMPCallOOM

	_Lock			PSA.HTABLock, scratch1=r17, scratch2=r18

	bl		GetPARPageInfo
	bge		cr4, MPCall_0_0xd8
	bgt		cr5, MPCall_0_0xd8
	bns		cr7, MPCall_0_0xd8
	bgt		cr7, MPCall_0_0xd8
	bltl	cr5, RemovePageFromTLB
	bgel	cr5, VMSecondLastExportedFunc
	ori		r16, r16,  0x404
	li		r31,  0x03
	rlwimi	r9, r31,  0, 30, 31
	bl		EditPTEInHTAB
	mr		r7, r30
	mr		r6, r29
	_AssertAndRelease	PSA.HTABLock, scratch=r16

	_Lock			PSA.PoolLock, scratch1=r16, scratch2=r17

	rlwinm	r8, r9,  0,  0, 19
	mr		r9, r3

;	r1 = kdp
;	r8 = anywhere in new page (phys)
;	r9 = page_virt
	bl		ExtendPool
	_AssertAndRelease	PSA.PoolLock, scratch=r16
	b		ReturnZeroFromMPCall

MPCall_0_0xd8
	mr		r7, r30
	mr		r6, r29
	_AssertAndRelease	PSA.HTABLock, scratch=r16
	b		ReturnMPCallOOM



;	                     MPCall_1

	DeclareMPCall	1, MPCall_1

MPCall_1	;	OUTSIDE REFERER
	b		ReturnZeroFromMPCall_again



;	ARG		MPCoherenceGroupID r3, CpuPluginPtr r4, CpuPluginSize r5, CpuPluginDesc r6
;	RET		OSStatus r3

	DeclareMPCall	2, MPRegisterCpuPlugin

MPRegisterCpuPlugin
;	_log	'MPRegisterCpuPlugin '
;	mr		r8, r3
;	bl		Printw
;	mr		r8, r4
;	bl		Printw
;	mr		r8, r5
;	bl		Printw
;	mr		r8, r6
;	bl		Printw
;	_log	'^n'

	mfsprg	r14, 0
	lwz		r15, EWA.PA_CurTask(r14)
	lwz		r16, ContextBlock.r6(r6)

	;	Check that address of CPU plugin is page-aligned,
	;	and that CPU plugin size if page-aligned and nonzero.
	andi.	r8, r4, 0xfff
	bne		ReturnMPCallOOM
	andi.	r8, r5, 0xfff
	cmpwi	cr1, r5, 0
	bne		ReturnMPCallOOM
	beq		cr1, ReturnMPCallOOM

	_Lock			PSA.SchLock, scratch1=r18, scratch2=r19

	;	r14 = coherence group ptr (motherpboard CGRP if not specified in first argument)
	mr.		r8, r3
	bne		@use_specified_cgrp
	mfsprg	r15, 0
	lwz		r14, EWA.CPUBase + CPU.LLL + LLL.Freeform(r15)
	b		@cgrp_done
@use_specified_cgrp
 	bl		LookupID
	cmpwi	r9, CoherenceGroup.kIDClass
	mr		r14, r8
	bne		ReturnMPCallInvalidIDErr
@cgrp_done

	;	Fail if no CPU plugin descriptor is passed in fourth argument
	cmpwi	r16, 0
	bne		@no_table_ptr
	stw		r16, CoherenceGroup.PA_CpuPluginDesc(r14)
	stw		r16, CoherenceGroup.LA_CpuPluginDesc(r14)
	b		ReleaseAndReturnMPCallInvalidIDErr
@no_table_ptr

	;	Fail if CPU plugin descriptor lies within CPU plugin
	add		r17, r4, r5
	cmplw	r16, r4
	cmplw	cr1, r16, r17
	blt		ReleaseAndReturnMPCallOOM
	bge		cr1, ReleaseAndReturnMPCallOOM

	;	What the hell? Wouldn't this always fail?
	lwz		r19, CoherenceGroup.PA_CpuPluginDesc(r14)
	mr.		r19, r19
	bne		ReleaseAndReturnMPCallInvalidIDErr

	;	r18 = physical address of plugin (assumes page-aligned)
	mr		r27, r4
	addi	r29, r1, KDP.BATs + 0xa0
	bl		PagingL2PWithBATs
	beq		ReleaseAndReturnMPCallOOM
	rlwinm	r18, r31, 0, 0, 19

	;	r18 = physical address of descriptor (does not assume page-aligned)
	mr		r27, r16
	mr		r19, r16
	addi	r29, r1, KDP.BATs + 0xa0
	bl		PagingL2PWithBATs
	beq		ReleaseAndReturnMPCallOOM
	rlwimi	r19, r31, 0, 0, 19

	;	Populate the coherence group structure.

		;	Save (page-aligned) address of main plugin (second argument)
		stw		r4, CoherenceGroup.LA_CpuPlugin(r14)
		stw		r18, CoherenceGroup.PA_CpuPlugin(r14)

		;	Save size of main plugin (third argument)
		stw		r5, CoherenceGroup.CpuPluginSize(r14)

		;	Save (non-page-aligned) address of descriptor (fourth argument)
		stw		r16, CoherenceGroup.LA_CpuPluginDesc(r14)
		stw		r19, CoherenceGroup.PA_CpuPluginDesc(r14)

	;	Get physical ptr to table of stack pointers (one per CPU)
	lwz		r27, 0(r19)
	addi	r29, r1, KDP.BATs + 0xa0
	bl		PagingL2PWithBATs
	beq		ReleaseAndReturnMPCallOOM
	rlwimi	r27, r31, 0, 0, 19
	stw		r27, CoherenceGroup.PA_CpuPluginStackPtrs(r14)

	mfsprg	r16, 0

	;	The cpup should operate in the caller's address space
	lwz		r17, EWA.PA_CurAddressSpace(r16)
	stw		r17, CoherenceGroup.CpuPluginSpacePtr(r14)

	;	Get ptr to dispatch table at descriptor + 32
	addi	r16, r19, 0x20
	stw		r16, CoherenceGroup.PA_CpuPluginTOC(r14)
	subi	r16, r16, 4 ; prepare for loop below

	;	Get number of entries in the dispatch table
	lwz		r17, 0x1c(r19)
	cmplwi	r17, 64
	stw		r17, CoherenceGroup.CpuPluginSelectorCount(r14)
	bgt		ReleaseAndReturnMPCallOOM

	;	Convert those entries to physical addresses
@table_convert_loop
	lwzu	r27, 4(r16)
	addi	r29, r1, KDP.BATs + 0xa0
	bl		PagingL2PWithBATs
	beq		ReleaseAndReturnMPCallOOM
	subi	r17, r17, 1
	rlwimi	r27, r31, 0, 0, 19
	cmpwi	r17, 0
	stw		r27, 0(r16)
	bgt		@table_convert_loop



	_log	'CPU plugin registered^n'

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



;	Called by MPProcessors and MPProcessorsScheduled

;	ARG		bool r3 scheduled_only
;	RET		int r3

	DeclareMPCall	3, MPProcessors

MPProcessors

	mfsprg	r15, 0
	lwz		r14, EWA.CPUBase + CPU.LLL + LLL.Freeform(r15)
	mr.		r8, r3

	lwz		r3, CoherenceGroup.CpuCount(r14)
	beq		CommonMPCallReturnPath

	lwz		r3, CoherenceGroup.ScheduledCpuCount(r14)
	b		CommonMPCallReturnPath



;	ARG		AddressSpaceID r3
;	RET		AddressSpaceID r3, ??? r4, ProcessStructID r5

	DeclareMPCall	4, MPCreateProcess

MPCreateProcess

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mr.		r8, r3
	bne		@spac_id_supplied
	lwz		r3, PSA.SystemAddressSpaceID(r1)
	mr		r8, r3
@spac_id_supplied

	bl		LookupID
;	r8 = something not sure what
;	r9 = 0:inval, 1:proc, 2:task, 3:timer, 4:q, 5:sema, 6:cr, 7:cpu, 8:addrspc, 9:evtg, 10:cohg, 11:area, 12:not, 13:log

	cmpwi	r9, AddressSpace.kIDClass
	mr		r30, r8
	bne		ReleaseAndReturnMPCallInvalidIDErr

	li		r8, 0x20 ;Process.Size
	bl		PoolAllocClear

	mr.		r31, r8
	beq		ReleaseAndScrambleMPCall

	li		r9, Process.kIDClass
	bl		MakeID

	cmpwi	r8,  0x00
	bne		@did_not_fail
	mr		r8, r31
	bl		PoolFree
	b		ReleaseAndScrambleMPCall
@did_not_fail

	stw		r8, Process.ID(r31)

	lisori	r16, Process.kSignature
	stw		r16, Process.Signature(r31)

	stw		r3, Process.SystemAddressSpaceID(r31)		;	NOT SYSTEM -- fix struct
	stw		r30, Process.SystemAddressSpacePtr(r31)

	lwz		r17, Process.AddressSpaceCount(r31)
	addi	r17, r17, 1
	stw		r17, Process.AddressSpaceCount(r31)

	mr		r5, r8

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



	DeclareMPCall	5, MPTerminateProcess

MPTerminateProcess

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mr		r8, r3

;	r8 = id
 	bl		LookupID
	cmpwi	r9, Process.kIDClass

	bne		ReleaseAndReturnMPCallInvalidIDErr
	mr		r31, r8
	lwz		r16,  0x0008(r31)
	rlwinm.	r17, r16,  0, 30, 30
	bne		ReleaseAndReturnMPCallOOM
	ori		r16, r16,  0x02
	stw		r16,  0x0008(r31)
	mr		r8, r3

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



;	ARG		ProcessID r3
;	RET		OSStatus r3

	DeclareMPCall	6, MPDeleteProcess

MPDeleteProcess

	_Lock		PSA.SchLock, scratch1=r16, scratch2=r17

	mr		r8, r3
 	bl		LookupID
	cmpwi	r9, Process.kIDClass
	bne		ReleaseAndReturnMPCallInvalidIDErr
	mr		r31, r8

	lwz		r16, Process.Flags(r31)
	lwz		r17, Process.TaskCount(r31)
	rlwinm.	r8, r16, 0, Process.kFlag30, Process.kFlag30
	cmpwi	cr1, r17, 0
	beq		ReleaseAndReturnMPCallOOM
	bne		cr1, ReleaseAndReturnMPCallOOM

	mr		r8, r3
	bl		DeleteID

	_AssertAndRelease	PSA.SchLock + Lock.Count, scratch=r16

	mr		r8, r31
	bl		PoolFree

	b		ReturnZeroFromMPCall



MPCall_6_0x78	;	OUTSIDE REFERER

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mfsprg	r16, 0
	rlwinm.	r8, r7, 0, 10, 10
	lwz		r17, KDP.PA_ECB(r1)
	lwz		r31, EWA.PA_CurTask(r16)

	beq		MPCall_6_0xb4
	lwz		r8, ContextBlock.PriorityShifty(r17)
	rlwinm	r8, r8,  0, 24, 21
	oris	r8, r8,  0x8000
	stw		r8, ContextBlock.PriorityShifty(r17)
MPCall_6_0xb4

	mr		r8, r31

	bl		SchTaskUnrdy

	li		r16, Task.kNominalPriority
	stb		r16, Task.Priority(r31)

	bl		SchRdyTaskNow

	mr		r8, r31
	bl		FlagSchEvaluationIfTaskRequires
	_AssertAndRelease	PSA.SchLock + Lock.Count, scratch=r16
	b		CommonMPCallReturnPath



;	Implements MPYield (null argument)

;	Caller gets its priority set to nominal

;	ARG		TaskID r3 (null for MPYield)
;	RET		OSStatus r3

	DeclareMPCall	13, MPYieldWithHint

MPYieldWithHint

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mfsprg	r16, 0
	rlwinm.	r8, r7,  0, 10, 10
	lwz		r17, KDP.PA_ECB(r1)
	lwz		r31, EWA.PA_CurTask(r16)
	beq		@i_am_important							;	not 68k mode?

	clrlwi.	r8, r3, 31
	lwz		r8, ContextBlock.PriorityShifty(r17)
	rlwinm	r8, r8,  0, 24, 21
	oris	r8, r8,  0x8000
	stw		r8, ContextBlock.PriorityShifty(r17)
	beq		@i_am_important							;	MPYield call

	;	If this task is 
	lbz		r16, Task.Priority(r31)
	cmpwi	r16, Task.kNominalPriority
	bge		@return
	mr		r8, r31
	bl		SchTaskUnrdy
	li		r16, Task.kNominalPriority
	stb		r16, Task.Priority(r31)
	bl		SchRdyTaskLater
	b		@return

@i_am_important
	mr		r8, r31
	bl		SchTaskUnrdy
	li		r16, Task.kNominalPriority
	stb		r16, Task.Priority(r31)
	bl		SchRdyTaskNow

@return
	mr		r8, r31
	bl		FlagSchEvaluationIfTaskRequires
	_AssertAndRelease	PSA.SchLock + Lock.Count, scratch=r16
	b		CommonMPCallReturnPath



;	ARG		AbsoluteTime r3/r4
;	RET		OSStatus r3

	DeclareMPCall	33, MPDelayUntil

MPDelayUntil

	rlwinm.	r8, r7, 0, EWA.kFlagBlue, EWA.kFlagBlue
	bne		ReturnMPCallBlueBlocking

	_Lock	PSA.SchLock, scratch1=r16, scratch2=r17
	b		_MPDelayUntilCommon



;	This version is allowed while the blue task is running

;	ARG		AbsoluteTime r3/r4
;	RET		OSStatus r3

	DeclareMPCall	55, MPDelayUntilSys

MPDelayUntilSys

	rlwinm.	r8, r7, 0, EWA.kFlagBlue, EWA.kFlagBlue
	lwz		r16, KDP.NanoKernelInfo + NKNanoKernelInfo.ExternalIntCount(r1)
	beq		_MPDelayUntilCommon

	;	Why the hell are we counting interrupts?
	lwz		r17, PSA.OtherSystemContextPtr(r1)
	lwz		r18, KDP.PA_ECB(r1)
	cmpw	r16, r17
	stw		r16, PSA.OtherSystemContextPtr(r1)
	bne		ReturnZeroFromMPCall

	lwz		r8, ContextBlock.PriorityShifty(r18)
	rlwinm	r8, r8, 0, 24, 21
	oris	r8, r8, 0x8000
	stw		r8, ContextBlock.PriorityShifty(r18)

	_Lock	PSA.SchLock, scratch1=r16, scratch2=r17

	lwz		r16, PSA.BlueSpinningOn(r1)
	cmpwi	r16, -1
	li		r16, 0
	bne		_MPDelayUntilCommon
	stw		r16, PSA.BlueSpinningOn(r1)
	b		ReleaseAndReturnZeroFromMPCall



_MPDelayUntilCommon

	mfsprg	r16, 0
	li		r17, 1

	lwz		r31, EWA.PA_CurTask(r16)
	addi	r16, r31, Task.Timer

	stb		r17, Timer.Kind(r16)

	;	High bit is possibly suspect? Or a flag?
	clrlwi	r3, r3, 1
	stw		r3, Timer.Time(r16)
	stw		r4, Timer.Time+4(r16)

	stw		r31, Timer.ParentTaskPtr(r16)

	mr		r8, r16
	bl		EnqueueTimer

	mr		r8, r31
	bl		SchTaskUnrdy

	addi	r16, r1, PSA.DelayQueue
	addi	r17, r31, Timer.QueueLLL
	stw		r16, LLL.Freeform(r17)

	InsertAsPrev	r17, r16, scratch=r18

	li		r3, 0
	b		BlockMPCall



	DeclareMPCall	34, MPCall_34

MPCall_34	;	OUTSIDE REFERER
	mr		r8, r3
	mr		r9, r4

;	r1 = kdp
;	r9 = kind
	bl		MakeID
	cmpwi	r8,  0x00
	beq		ScrambleMPCall
	mr		r5, r8
	b		ReturnZeroFromMPCall



	DeclareMPCall	35, MPCall_35

MPCall_35	;	OUTSIDE REFERER
	mr		r8, r3
	bl		DeleteID
	cmpwi	r8,  0x01
	beq		ReturnZeroFromMPCall
	b		ReturnMPCallInvalidIDErr



	DeclareMPCall	36, MPCall_36

MPCall_36	;	OUTSIDE REFERER
	mr		r8, r3

;	r8 = id
 	bl		LookupID
	cmpwi	r9, 0		; invalid

	mr		r4, r9
	mr		r5, r8
	bne		ReturnZeroFromMPCall
	b		ReturnMPCallInvalidIDErr



;	Replace the provided process/coherence/console ID with
;	the "next" one. IDs were opaque but were only longs.
;	Wrapped by MPGetNext*ID, which indirects the opaque ID
;	structure.
;	From MP docs: A coherence group is the set of processors
;	and other bus controllers that have cache-coherent
;	access to memory. Mac OS 9 defines only one coherence
;	group, which is all the processors that can access
;	internal memory (RAM). Other coherence groups are
;	possible; for example, a PCI card with its own memory
;	and processors can comprise a coherence group.

;	> r3    = kind (process=1,coherence=10,console=13)
;	> r4    = prev_id

;	< r3    = MP result code
;	< r4    = next_id

	DeclareMPCall	37, MPGetNextID

MPGetNextID

	mr		r8, r4
	mr		r9, r3
	bl		GetNextIDOfClass
	cmpwi	r8,  0x00
	mr		r4, r8
	bne		ReturnZeroFromMPCall
	b		ReturnMPCallInvalidIDErr



;	Replace the provided address
;	space/task/queue/semaphore/critical
;	region/timer/event/notification ID with the "next" one.
;	IDs were opaque but were only longs. Wrapped by
;	MPGetNext*ID, which indirects the opaque ID structure.
;	Differs from KCGetNextID because it deals in
;	objects owned by a particular process.

;	Useful info about some poorly understood structures

;	ARG		ProcessID r3, IDClass r4, ID r5
;	RET		MPErr r3, IDClass r4, ID r5

	DeclareMPCall	116, MPGetNextIDOwnedByProcess

MPGetNextIDOwnedByProcess

	;	Confirm that owner ID in r3 is a Process

	mr		r8, r3
	bl		LookupID
	cmpwi	r9, Process.kIDClass
	bne		ReturnMPCallInvalidIDErr


	;	Loop over IDs (and resolve them) until one is owned by the Process

@try_another_id
	mr		r8, r5
	mr		r9, r4

;	ARG		ID r8, IDClass r9
	bl		GetNextIDOfClass
;	RET		ID r8

	mr.		r5, r8
	beq		ReturnMPCallInvalidIDErr

;	ARG		ID r8
	bl		LookupID
;	RET		Ptr r8, IDClass r9

	cmpwi	r4, 				Task.kIDClass
	cmpwi	cr1, r4,			Timer.kIDClass
	beq							@task
	beq		cr1,				@timer

	cmpwi	r4,					Queue.kIDClass
	cmpwi	cr1, r4,			Semaphore.kIDClass
	beq							@queue
	beq		cr1,				@semaphore

	cmpwi	r4,					CriticalRegion.kIDClass
	cmpwi	cr1, r4,			AddressSpace.kIDClass
	beq							@critical_region
	beq		cr1,				@address_space

	cmpwi	r4,					EventGroup.kIDClass
	cmpwi	cr1, r4,			Area.kIDClass
	beq							@event_group
	beq		cr1,				@area

	cmpwi	r4,					Notification.kIDClass
	cmpwi	cr1, r4,			ConsoleLog.kIDClass
	beq							@notification
	beq		cr1,				@console_log

	b		ReturnParamErrFromMPCall

@task
	lwz		r17, Task.Flags(r8)
	lwz		r9,  Task.ProcessID(r8)

	rlwinm.	r17, r17,  0, 15, 15
	beq		@not_owned_by_blue_process
	lwz		r9, PSA.blueProcessPtr(r1)
	lwz		r9, Task.ID(r9)
@not_owned_by_blue_process

	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@timer
	lwz		r9, Timer.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@queue
	lwz		r9, Queue.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@semaphore
	lwz		r9, Semaphore.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@critical_region
	lwz		r9, CriticalRegion.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@address_space
	lwz		r9, AddressSpace.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@event_group
	lwz		r9, EventGroup.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@area
	lwz		r9, Area.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@notification
	lwz		r9, Notification.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall

@console_log
	lwz		r9, ConsoleLog.ProcessID(r8)
	cmpw	r9, r3
	bne		@try_another_id
	b		ReturnZeroFromMPCall



	DeclareMPCall	38, MPCall_38

MPCall_38	;	OUTSIDE REFERER
	mr		r8, r3

;	r8 = id
 	bl		LookupID
	cmpwi	r9, Process.kIDClass

	bne		ReturnMPCallInvalidIDErr
	mr		r31, r8

MPCall_38_0x14
	mr		r8, r4
	li		r9,  0x02
	bl		GetNextIDOfClass
	cmpwi	r8,  0x00
	beq		ReturnMPCallInvalidIDErr
	mr		r4, r8

;	r8 = id
	bl		LookupID
;	r8 = something not sure what
;	r9 = 0:inval, 1:proc, 2:task, 3:timer, 4:q, 5:sema, 6:cr, 7:cpu, 8:addrspc, 9:evtg, 10:cohg, 11:area, 12:not, 13:log

	lwz		r17,  0x0064(r8)
	lwz		r16,  0x0060(r8)
	rlwinm.	r17, r17,  0, 15, 15
	beq		MPCall_38_0x48
	lwz		r16, PSA.blueProcessPtr(r1)
	lwz		r16,  0x0000(r16)

MPCall_38_0x48
	cmpw	r16, r3
	beq		ReturnZeroFromMPCall
	b		MPCall_38_0x14



	DeclareMPCall	62, MPGetNextCpuID

MPGetNextCpuID

	mr.		r8, r3
	bne		@r3_nonzero

;r3 zero
	mfsprg	r15, 0
	lwz		r31, EWA.CPUBase + CPU.LLL + LLL.Freeform(r15)
	lwz		r3, 0(r31)
	b		@endif

@r3_nonzero
 	bl		LookupID
	cmpwi	r9, CoherenceGroup.kIDClass
	bne		ReturnMPCallInvalidIDErr
@endif

@loop
	mr		r8, r4
	li		r9, CPU.kIDClass
	bl		GetNextIDOfClass
	cmpwi	r8, 0
	beq		ReturnMPCallInvalidIDErr

	mr		r4, r8

	bl		LookupID
	lwz		r16, CPU.LLL + LLL.Freeform(r8)
	lwz		r17, CPU.ID(r16)
	cmpw	r17, r3
	bne		@loop

	b		ReturnZeroFromMPCall



	DeclareMPCall	42, MPCreateProcessor

MPCreateProcessor

	mr.		r8, r3
	bne		@cgrpProvided

	mfsprg	r15, 0
	lwz		r30, EWA.CPUBase + CPU.LLL + LLL.Freeform(r15)
	b		@gotCgrp

@cgrpProvided
 	bl		LookupID
	cmpwi	r9, CoherenceGroup.kIDClass
	mr		r30, r8
	bne		ReturnMPCallInvalidIDErr

@gotCgrp

	li		r8, CPU.Size
	bl		PoolAllocClear
	mr.		r31, r8
	beq		ScrambleMPCall

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	li		r9, CPU.kIDClass
	bl		MakeID
	cmpwi	r8, 0
	bne+	@success

	mr		r8, r31
	bl		PoolFree
	b		ReleaseAndScrambleMPCall
@success


	stw		r8, CPU.ID(r31)

	lisori	r16, CPU.kSignature

	stw		r8, ContextBlock.r6(r6)		; return ID in r6

	stw		r16, CPU.Signature(r31)

	lwz		r17,  0x0020(r30)
	addi	r17, r17,  0x01
	stw		r17,  0x0020(r30)
	addi	r16, r31,  0x08
	stw		r30,  0x0000(r16)
	InsertAsPrev	r16, r30, scratch=r17

	lisori	r8, 11
	lisori	r8, 6
	stw		r8, CPU.Flags(r31)




	addi	r30, r31, CPU.EWABase


	addi	r8, r1, PSA.Base
	stw		r8, EWA.PA_PSA - EWA.Base(r30)

	stw		r1, EWA.PA_KDP - EWA.Base(r30)

	li		r8, 0
	stw		r8, EWA.PA_CurTask - EWA.Base(r30)


	;	Matches code in Init.s quite closely

	li		r8, -0x01
	sth		r4, 0x020a(r30)
	stb		r8, 0x0209(r30)		; interesting...

	lwz		r8, EWA.PA_IRP(r1)
	stw		r8, EWA.PA_IRP - EWA.Base(r30)

	lisori	r8, 'time'
	stw		r8, EWA.TimeList - EWA.Base + LLL.Signature(r30)

	li		r8, 0x04
	stb		r8, 0x0014(r30)

	li		r8, 0x01
	stb		r8, 0x0016(r30)

	li		r8, 0x00
	stb		r8, 0x0017(r30)

	lisori	r8, 0x7fffffff
	stw		r8, 0x0038(r30)

	oris	r8, r8, 0xffff
	stw		r8, 0x003c(r30)

	b		ReleaseAndReturnZeroFromMPCall



;	Simple MPLibrary wrapper

	DeclareMPCall	43, MPDeleteProcessor

MPDeleteProcessor

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mr		r8, r3
 	bl		LookupID
	cmpwi	r9, CPU.kIDClass
	mr		r31, r8
	bne		ReleaseAndReturnMPCallInvalidIDErr

	lwz		r16, CPU.Flags(r31)
	lisori	r17, 9
	and.	r17, r17, r16
	bne		ReleaseAndReturnMPCallOOM

	mfsprg	r15, 0

	li		r16, kResetProcessor
	stw		r16, EWA.SIGPSelector(r15)

	lhz		r16, CPU.EWA + EWA.CPUIndex(r31)
	stw		r16, EWA.SIGPCallR4(r15)
	li		r8, 2 ; args in EWA
	bl		SIGP
	lwz		r17,  0x0008(r31)
	addi	r16, r31,  0x08
	lwz		r18,  0x0020(r17)
	addi	r18, r18, -0x01
	stw		r18,  0x0020(r17)
	RemoveFromList		r16, scratch1=r17, scratch2=r18
	mr		r8, r31
	bl		PoolFree
	mr		r8, r3
	bl		DeleteID

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



	DeclareMPCall	44, MPStartScheduling

;	ARG		CpuID r3

MPStartScheduling

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mr		r8, r3
	bl		LookupID
	cmpwi	r9, CPU.kIDClass
	bne		ReleaseAndReturnMPCallInvalidIDErr

	mr		r30, r8
	lwz		r16, CPU.Flags(r30)
	rlwinm.	r8, r16,  0, 28, 28
	bne		ReleaseAndReturnZeroFromMPCall

	mfsprg	r15, 0
	li		r16, kResetProcessor
	stw		r16, EWA.SIGPSelector(r15)
	lhz		r16, CPU.EWA + EWA.CPUIndex(r30)
	stw		r16, EWA.SIGPCallR4(r15)


;	Put the boots in?

	_log	'SIGP kResetProcessor^n'
	li		r8, 2 ; args in EWA
	bl		SIGP
	cmpwi	r8, -0x7264
	cmpwi	cr1, r8, 0
	beq		ReleaseAndReturnMPCallOOM
	bne		cr1, ReleaseAndReturnMPCallOOM


;	Every CPU gets an idle task

	_log	'Creating idle task^n'
	mr		r31, r7
	rlwinm	r7, r7, 0, 13, 11
	lwz		r8, PSA.blueProcessPtr(r1)

;	ARG		Flags r7, Process *r8
	bl		CreateTask
;	RET		Task *r8

	mr		r7, r31
	mr.		r31, r8
	beq		ReleaseAndScrambleMPCall

	stw		r31, CPU.IdleTaskPtr(r30)

	lisori	r8, 'idle'
	stw		r8, Task.Name(r31)

	lisori	r8, 0x80040 ; (Z>>Task.kFlag12)| (Z>>Task.kFlag25)
	stw		r8, Task.Flags(r31)

	li		r8, 1
	stw		r8, Task.Weight(r31)

	li		r8, Task.kIdlePriority
	stb		r8, Task.Priority(r31)

	;	whoa -- cpu structs arent this big?
	lhz		r8, CPU.EWA + EWA.CPUIndex(r30)
	sth		r8, Task.CPUIndex(r31)

	lwz		r8, Task.ContextBlock + ContextBlock.Flags(r31)
	_bset	r8, r8, 9
	stw		r8, Task.ContextBlock + ContextBlock.Flags(r31)


	lwz		r8, KDP.PA_NanoKernelCode(r1)
	llabel	r26, SchIdleTask
	add		r8, r8, r26
	stw		r8, Task.ContextBlock + ContextBlock.CodePtr(r31)

	;	better compare this with init code idle task
	lwz		r8, Task.ContextBlock + ContextBlock.MSR(r31)
	andi.	r8, r8,  0xbfcf
	stw		r8, Task.ContextBlock + ContextBlock.MSR(r31)


	_AssertAndRelease	PSA.SchLock, scratch=r16


	mfsprg	r15, 0


	li		r16, kSynchClock
	stw		r16, EWA.SIGPSelector(r15)
	lhz		r16, CPU.EWA + EWA.CPUIndex(r30)
	stw		r16, EWA.SIGPCallR4(r15)

MPCall_44_0x15c
	_log	'SIGP kSynchClock^n'
	li		r8, 2 ; args in EWA
	bl		SIGP
	cmpwi	r8, -0x7264
	cmpwi	cr1, r8,  0x00
	beq		MPCall_44_0x15c


	bne		cr1, MPCall_Panic

	mfsprg	r15, 0

	li		r16, kStartProcessor
	stw		r16, EWA.SIGPSelector(r15)

	lhz		r16, CPU.EWA + EWA.CPUIndex(r30)
	stw		r16, EWA.SIGPCallR4(r15)

	lwz		r16, KDP.PA_NanoKernelCode(r1)
	llabel	r17, NewCpuEntryPoint
	add		r16, r16, r17
	stw		r16, EWA.SIGPCallR5(r15)

	stw		r30, EWA.SIGPCallR6(r15) ; cpu struct

@retry
	_log	'SIGP kStartProcessor^n'
	li		r8, 4 ; args in EWA
	bl		SIGP

	cmpwi	r8, -0x7264
	cmpwi	cr1, r8, 0
	beq		@retry
	bne		cr1, MPCall_Panic

	_log	'Processor scheduled^n'

	b		ReturnZeroFromMPCall



	DeclareMPCall	45, MPStopScheduling

MPStopScheduling

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mr		r8, r3
 	bl		LookupID
	cmpwi	r9, CPU.kIDClass
	bne		ReleaseAndReturnMPCallInvalidIDErr

	mr		r30, r8
	lwz		r16, CPU.Flags(r30)
	rlwinm.	r8, r16, 0, CPU.kFlagScheduled, CPU.kFlagScheduled
	beq		ReleaseAndReturnZeroFromMPCall

	lwz		r31, CPU.IdleTaskPtr(r30)

	rlwinm.	r8, r16, 0, CPU.kFlag31, CPU.kFlag31
	bne		ReleaseAndReturnMPCallOOM

	lbz		r17, Task.Priority(r31)
	cmpwi	r17, Task.kCriticalPriority
	beq		@running_critical_task

	lwz		r17, Task.Flags(r31)
	_bset	r17, r17, Task.kFlag8
	stw		r17, Task.Flags(r31)

	mr		r8, r31
	bl		SchTaskUnrdy

	li		r17, Task.kCriticalPriority
	stb		r17, Task.Priority(r31)

	mr		r8, r31
	bl		SchRdyTaskLater

	bl		CalculateTimeslice

	mr		r8, r31
	bl		FlagSchEval

	lwz		r8, KDP.PA_NanoKernelCode(r1)
	llabel	r9, SchIdleTaskStopper
	add		r8, r8, r9
	stw		r8, Task.ContextBlock + ContextBlock.CodePtr(r31)

@running_critical_task
	_AssertAndRelease	PSA.SchLock + Lock.Count, scratch=r16
	b		MPCall_6_0x78



;	Directly wrapped by MPLibrary

;	ARG		selector r3, arbitrary args r4...
;	RET		arbitrary

	DeclareMPCall	46, MPCpuPlugin

MPCpuPlugin

	;	Arguments in registers
	li		r8, 0

	;	Hopefully returns via kcSchExitInterrupt? Geez...
	bl		SIGP

	mr		r3, r8
	mr		r4, r9	; r4 not accessible calling MPLibrary func from C

	b		CommonMPCallReturnPath



	DeclareMPCall	47, MPCall_47

MPCall_47	;	OUTSIDE REFERER
	rlwinm.	r8, r7, 0, EWA.kFlagVec, EWA.kFlagVec
	lwz		r15,  0x00d8(r6)
	beq		ReturnMPCallOOM
	cmpwi	r15,  0x00
	mr		r16, r2
	beq		ReturnMPCallOOM
	mr		r17, r3
	mr		r18, r4
	mr		r19, r5
	bl		Save_v0_v31
	mr		r2, r16
	mr		r3, r17
	mr		r4, r18
	mr		r5, r19
	b		ReturnZeroFromMPCall



;	                      MPCall_48_Bad


	DeclareMPCall	48, MPCall_48_Bad

MPCall_48_Bad	;	OUTSIDE REFERER
	b		MPCallBad



;	                       MPDebugPutString


	DeclareMPCall	96, MPDebugPutString

MPDebugPutString	;	OUTSIDE REFERER
	rlwinm.	r9, r11,  0, 27, 27
	mr		r8, r3
	beq		MPDebugPutString_0x1c
	li		r9,  0x00
	bl		SpaceL2PUsingBATs ; LogicalPage *r8, MPAddressSpace *r9 // PhysicalPage *r17
	beq		MPDebugPutString_0x24
	rlwimi	r8, r17,  0,  0, 19

MPDebugPutString_0x1c
	bl		PrintS
	b		ReturnZeroFromMPCall

MPDebugPutString_0x24
	_log	'NKxprintf (V->P translation error)^n'
	b		ReturnMPCallOOM



;	ARG		long r3, int r4 size (1:byte, 2:half, else:word)

	DeclareMPCall	97, MPDebugPutHex

MPDebugPutHex

	mr		r8, r3

	cmpwi	r4, 1
	cmpwi	cr1, r4, 2

	beq		@byte
	beq		cr1, @half


	bl		Printw
	b		CommonMPCallReturnPath

@half
	bl		Printh
	b		CommonMPCallReturnPath

@byte
	bl		Printb
	b		CommonMPCallReturnPath



	DeclareMPCall	124, MPDebugPutDec

MPDebugPutDec

	mr		r8, r3
	bl		Printd
	b		CommonMPCallReturnPath



	DeclareMPCall	99, MPSetBlueProcessID

MPSetBlueProcessID

	mfsprg	r16, 0
	rlwinm.	r8, r7, 0, EWA.kFlagBlue, EWA.kFlagBlue
	lwz		r31, EWA.PA_CurTask(r16)
	beq		ReturnMPCallOOM ; not in blue -> fail

	mr		r8, r3
	bl		LookupID
	cmpwi	r9, Process.kIDClass
	bne		ReturnMPCallInvalidIDErr

	stw		r3, Task.ProcessID(r31)
	stw		r4, Task.SomeLabelField(r31)

	b		ReturnZeroFromMPCall



	DeclareMPCall	104, MPRegisterThermalHandler

MPRegisterThermalHandler

	_Lock		PSA.SchLock, scratch1=r16, scratch2=r17

	mr.			r8, r3
	beq			@is_zero
	bl			LookupID
	cmpwi		r9, Notification.kIDClass
	bne			ReleaseAndReturnMPCallInvalidIDErr
@is_zero

	stw			r3, PSA.ThermalHandlerID(r1)

	b			ReleaseAndReturnZeroFromMPCall



	DeclareMPCall	105, MPRegisterPMFHandler

MPRegisterPMFHandler

	_Lock		PSA.SchLock, scratch1=r16, scratch2=r17

	mr.			r8, r3
	beq			@is_zero
	bl			LookupID
	cmpwi		r9, Notification.kIDClass
	bne			ReleaseAndReturnMPCallInvalidIDErr
@is_zero

	stw			r3, PSA.PMFHandlerID(r1)

	b			ReleaseAndReturnZeroFromMPCall



	DeclareMPCall	106, MPMarkPMFTask

MPMarkPMFTask

	_Lock		PSA.SchLock, scratch1=r16, scratch2=r17

	mfsprg		r30, 0
	mr.			r8, r3
	lwz			r31, EWA.PA_CurTask(r30)

	beq			@use_this_task_instead
	bl			LookupID
	cmpwi		r9, Task.kIDClass
	mr			r31, r8
	bne			ReleaseAndReturnMPCallInvalidIDErr
@use_this_task_instead

;	Insert bit 31 of r4 into bit 21 of these flags
	lwz			r17, Task.Flags(r31)
	rlwimi		r17, r4, 10, Task.kFlagPerfMon, Task.kFlagPerfMon
	stw			r17, Task.Flags(r31)


;	Don't know what this does!
	mr			r8, r31
	bl			FlagSchEval

	b			ReleaseAndReturnZeroFromMPCall



;	ARG		int r6:
;				2:	SystemInfo
;				3:	DiagInfo
;				4:	NanoKernelInfo
;				5:	ProcessorInfo
;				6:	HWInfo
;				7:	ProcessorState

;	RET		Ptr r4, short r5 ver, short r6 len

	DeclareMPCall	107, NKLocateInfoRecord

NKLocateInfoRecord

	cmpwi	r3, 5
	cmpwi	cr1, r3, 2
	beq		@ProcessorInfo
	beq		cr1, @SystemInfo

	cmpwi	r3, 3
	cmpwi	cr1, r3, 4
	beq		@DiagInfo
	beq		cr1, @NanoKernelInfo

	cmpwi	r3, 7
	cmpwi	cr1, r3, 6
	beq		@ProcessorState
	bne		cr1, ReturnParamErrFromMPCall

	lwz		r4, KDP.InfoRecord + InfoRecord.NKHWInfoPtr(r1)
	lhz		r16, KDP.InfoRecord + InfoRecord.NKHWInfoLen(r1)
	lhz		r5,  KDP.InfoRecord + InfoRecord.NKHWInfoVer(r1)
	stw		r16, ContextBlock.r6(r6)
	b		ReturnZeroFromMPCall

@ProcessorState
	lwz		r4, KDP.InfoRecord + InfoRecord.NKProcessorStatePtr(r1)
	lhz		r16, KDP.InfoRecord + InfoRecord.NKProcessorStateLen(r1)
	lhz		r5, KDP.InfoRecord + InfoRecord.NKProcessorStateVer(r1)
	stw		r16, ContextBlock.r6(r6)
	b		ReturnZeroFromMPCall

@ProcessorInfo
	lwz		r4, KDP.InfoRecord + InfoRecord.NKProcessorInfoPtr(r1)
	lhz		r16, KDP.InfoRecord + InfoRecord.NKProcessorInfoLen(r1)
	lhz		r5, KDP.InfoRecord + InfoRecord.NKProcessorInfoVer(r1)
	stw		r16, ContextBlock.r6(r6)
	b		ReturnZeroFromMPCall

@NanoKernelInfo
	lwz		r4, KDP.InfoRecord + InfoRecord.NKNanoKernelInfoPtr(r1)
	lhz		r16, KDP.InfoRecord + InfoRecord.NKNanoKernelInfoLen(r1)
	lhz		r5, KDP.InfoRecord + InfoRecord.NKNanoKernelInfoVer(r1)
	stw		r16, ContextBlock.r6(r6)
	b		ReturnZeroFromMPCall

@DiagInfo
	lwz		r4, KDP.InfoRecord + InfoRecord.NKDiagInfoPtr(r1)
	lhz		r16, KDP.InfoRecord + InfoRecord.NKDiagInfoLen(r1)
	lhz		r5, KDP.InfoRecord + InfoRecord.NKDiagInfoVer(r1)
	stw		r16, ContextBlock.r6(r6)
	b		ReturnZeroFromMPCall

@SystemInfo
	lwz		r4, KDP.InfoRecord + InfoRecord.NKSystemInfoPtr(r1)
	lhz		r16, KDP.InfoRecord + InfoRecord.NKSystemInfoLen(r1)
	lhz		r5, KDP.InfoRecord + InfoRecord.NKSystemInfoVer(r1)
	stw		r16, ContextBlock.r6(r6)
	b		ReturnZeroFromMPCall



	;	ARG		int r3 stop
	;			long r4 CpuClockRateHz
	;			long r5 BusClockRateHz
	;			long r6 DecClockRateHz
	;	RET		OSStatus r3

	DeclareMPCall	108, NKSetPrInfoClockRates

NKSetPrInfoClockRates

	cmplwi	r3, 2
	bge		ReturnParamErrFromMPCall

	mulli	r17, r3, 16
	addi	r18, r1, KDP.ProcessorInfo + NKProcessorInfo.ClockRates
	add		r18, r17, r18

	lwz		r16, ContextBlock.r6(r6)
	stw		r4, 0(r18)
	stw		r5, 4(r18)
	stw		r16, 8(r18)

	_log	'Clock rates for step '
	mr		r8, r3
	bl		Printd
	_log	'- Cpu '
	mr		r8, r4
	bl		Printd
	_log	'- Bus '
	mr		r8, r5
	bl		Printd
	_log	'- Dec '
	mr		r8, r16
	bl		Printd
	_log	'Hz^n'

	b		ReturnZeroFromMPCall



;	                     NKSetClockStep

;	Debug string matches MPLibrary!
;	0xf7e(r1) = clock_step (half-word)

;	> r3    = new_clock_step  # (half-word)

	DeclareMPCall	131, NKSetClockStep

NKSetClockStep	;	OUTSIDE REFERER
	mfsprg	r9, 0
	lwz		r8, EWA.CPUBase + CPU.LLL + LLL.Freeform(r9)
	lwz		r9,  0x0024(r8)
	cmpwi	r9,  0x01
	bgt		ReturnMPCallOOM
	lhz		r19,  0x0f7e(r1)
	_log	'NKSetClockStep - current '
	mr		r8, r19
	bl		Printd
	_log	' new '
	mr		r8, r3
	bl		Printd
	_log	'^n'
	cmplwi	r3,  0x02
	cmpw	cr1, r3, r19
	bge		ReturnParamErrFromMPCall
	beq		cr1, ReturnMPCallOOM
	mulli	r17, r3,  0x10
	addi	r18, r1,  0xf80
	sth		r17,  0x0f7e(r1)
	add		r18, r17, r18
	lwz		r16,  0x0000(r18)
	lwz		r17,  0x0004(r18)
	stw		r16,  0x0f24(r1)
	stw		r17,  0x0f28(r1)
	lwz		r16,  0x0f88(r1)
	stw		r16,  0x0f2c(r1)

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	lwz		r16,  0x0008(r18)
	stw		r16, PSA.DecClockRateHzCopy(r1)
	bgt		cr1, NKSetClockStep_0xec
	lwz		r31, PSA.OtherTimerQueuePtr(r1)
	lbz		r18,  0x0017(r31)
	cmpwi	r18,  0x00

;	r1 = kdp
	beq		ReleaseAndReturnZeroFromMPCall
	mr		r8, r31
	bl		DequeueTimer

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall

NKSetClockStep_0xec
	lwz		r31, PSA.OtherTimerQueuePtr(r1)
	lbz		r18,  0x0017(r31)
	cmpwi	r18,  0x01

;	r1 = kdp
	beq		ReleaseAndReturnZeroFromMPCall
	bl		GetTime
	stw		r8,  0x0038(r31)
	stw		r9,  0x003c(r31)
	mr		r8, r31
	bl		EnqueueTimer

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



;	               NKSetClockDriftCorrection

;	There's a one-billion constant in here, for fractional
;	expression.
;	PSA._36c(r1) = tb_drift_numerator
;	PSA._368(r1) = tb_drift_denominator

;	> r3    = to

	DeclareMPCall	132, NKSetClockDriftCorrection

NKSetClockDriftCorrection	;	OUTSIDE REFERER
	lwz		r31, PSA._364(r1)
	mfsprg	r9, 0
	cmpwi	r31,  0x00
	beq		ReturnMPCallOOM
	lwz		r8, EWA.CPUBase + CPU.LLL + LLL.Freeform(r9)
	lwz		r9,  0x0024(r8)
	cmpwi	r9,  0x01
	bgt		ReturnMPCallOOM
	lwz		r19,  0x0fa0(r1)
	cmpwi	r3,  0x00
	cmpw	cr1, r3, r19
	stw		r3,  0x0fa0(r1)
	beq		NKSetClockDriftCorrection_0x12c
	beq		cr1, ReturnZeroFromMPCall
	lis		r16,  0x3b9a
	ori		r16, r16,  0xca00
	lwz		r17,  0x0f88(r1)
	srwi	r17, r17,  7
	divw	r18, r16, r3
	cmpw	r18, r17
	bge		NKSetClockDriftCorrection_0x64
	divw	r16, r16, r17
	mr		r18, r17
	divw	r17, r3, r16
	b		NKSetClockDriftCorrection_0x6c

NKSetClockDriftCorrection_0x64
	rlwinm	r17, r3,  2, 30, 30
	addi	r17, r17,  0x01

NKSetClockDriftCorrection_0x6c
	stw		r17, PSA._36c(r1)
	stw		r18, PSA._368(r1)
	_log	'TB drift adjusted to '
	mr		r8, r3
	bl		Printd
	_log	' ppb ( '
	mr		r8, r17
	bl		Printd
	_log	'/ '
	mr		r8, r18
	bl		Printd
	_log	')^n'

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	lwz		r31, PSA._364(r1)
	lbz		r18,  0x0017(r31)
	cmpwi	r18,  0x01

;	r1 = kdp
	beq		ReleaseAndReturnZeroFromMPCall
	bl		GetTime
	stw		r8,  0x0038(r31)
	stw		r9,  0x003c(r31)
	mr		r8, r31
	bl		EnqueueTimer

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall

NKSetClockDriftCorrection_0x12c

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	li		r17,  0x00
	stw		r17, PSA._36c(r1)
	stw		r17, PSA._368(r1)
	lwz		r31, PSA._364(r1)
	lbz		r18,  0x0017(r31)
	cmpwi	r18,  0x00

;	r1 = kdp
	beq		ReleaseAndReturnZeroFromMPCall
	mr		r8, r31
	bl		DequeueTimer

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



	DeclareMPCall	115, MPCall_115

MPCall_115	;	OUTSIDE REFERER

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mr		r8, r3

;	r8 = id
 	bl		LookupID
	cmpwi	r9, ConsoleLog.kIDClass

	mr		r31, r8
	bne		ReleaseAndReturnMPCallInvalidIDErr
	lwz		r30,  0x000c(r31)
	cmpwi	r30,  0x00
	bne		MPCall_115_0x94

	_Lock			PSA.DbugLock, scratch1=r16, scratch2=r17

	lwz		r30, PSA._404(r1)

MPCall_115_0x54
	addi	r30, r30,  0x01
	andi.	r29, r30,  0xfff
	bne		MPCall_115_0x64
	lwz		r30, -0x1000(r30)

MPCall_115_0x64
	lbz		r16,  0x0000(r30)
	cmpwi	r16,  0x00
	beq		MPCall_115_0x54
	stw		r30,  0x000c(r31)
	_AssertAndRelease	PSA.DbugLock, scratch=r16

MPCall_115_0x94
	cmpwi	r5,  0x00
	ble		ReleaseAndReturnMPCallOOM
	rlwinm.	r9, r11,  0, 27, 27
	mr		r8, r4
	crmove	30, 2
	beq		MPCall_115_0xd0
	li		r9,  0x00
	bl		SpaceL2PIgnoringBATs ; LogicalPage *r8, MPAddressSpace *r9 // PhysicalPage *r17
	beq		ReleaseAndReturnMPCallOOM
	add		r8, r4, r5
	li		r9,  0x00
	addi	r8, r8, -0x01
	mr		r30, r8
	bl		SpaceL2PIgnoringBATs ; LogicalPage *r8, MPAddressSpace *r9 // PhysicalPage *r17
	beq		ReleaseAndReturnMPCallOOM

MPCall_115_0xd0
	lwz		r28, PSA._404(r1)
	lwz		r29,  0x000c(r31)
	li		r5,  0x00
	not		r27, r4

MPCall_115_0xe0
	cmpw	r28, r29
	cmplw	cr1, r4, r30
	beq		MPCall_115_0x144
	bgt		cr1, MPCall_115_0x144
	rlwinm	r16, r4,  0,  0, 19
	mr		r8, r4
	beq		cr7, MPCall_115_0x11c
	cmpw	r16, r27
	mr		r17, r26
	beq		MPCall_115_0x11c
	mr		r27, r16
	li		r9,  0x00
	bl		SpaceL2PIgnoringBATs ; LogicalPage *r8, MPAddressSpace *r9 // PhysicalPage *r17
	beq		ReleaseAndReturnMPCallOOM
	mr		r26, r17

MPCall_115_0x11c
	rlwimi	r17, r4,  0, 20, 31
	lbz		r8,  0x0000(r29)
	addi	r29, r29,  0x01
	andi.	r16, r29,  0xfff
	bne+	MPCall_115_0x134
	lwz		r29, -0x1000(r29)

MPCall_115_0x134
	stb		r8,  0x0000(r17)
	addi	r5, r5,  0x01
	addi	r4, r4,  0x01
	b		MPCall_115_0xe0

MPCall_115_0x144
	stw		r29,  0x000c(r31)

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



;	Point external interrupts (thing PIHes) towards this notification

	DeclareMPCall	121, MPRegisterExternalHandler

MPRegisterExternalHandler

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	mr.				r8, r3
	beq				@zero
	bl				LookupID
	cmpwi			r9, Notification.kIDClass
	bne				ReleaseAndReturnMPCallInvalidIDErr
@zero

	stw				r3, PSA.ExternalHandlerID(r1)

	b				ReleaseAndReturnZeroFromMPCall



	DeclareMPCall	133, MPCall_133

MPCall_133	;	OUTSIDE REFERER

	_Lock			PSA.SchLock, scratch1=r16, scratch2=r17

	cmpw	r3, r0
	lwz		r16,  KDP.NanoKernelInfo + NKNanoKernelInfo.ConfigFlags(r1)
	li		r17,  0x0b
	blt		MPCall_133_0x34
	and		r3, r3, r17
	or		r16, r16, r3
	b		MPCall_133_0x3c

MPCall_133_0x34
	orc		r3, r3, r17
	and		r16, r16, r3

MPCall_133_0x3c
	stw		r16,  0x0edc(r1)
	srawi	r16, r4, 16
	extsh	r17, r4
	cmpwi	r16, -0x01
	cmpwi	cr1, r17, -0x01
	beq		MPCall_133_0x60
	bgt		MPCall_133_0x5c
	li		r16,  0x00

MPCall_133_0x5c
	sth		r16, PSA._360(r1)

MPCall_133_0x60
	beq		cr1, MPCall_133_0x70
	bgt		cr1, MPCall_133_0x6c
	li		r17,  0x00

MPCall_133_0x6c
	sth		r17, PSA._35e(r1)

MPCall_133_0x70
	srawi	r16, r5, 16
	extsh	r17, r5
	cmpwi	r16, -0x01
	cmpwi	cr1, r17, -0x01
	beq		MPCall_133_0x90
	bgt		MPCall_133_0x8c
	li		r16,  0x00

MPCall_133_0x8c
	sth		r16, PSA._35c(r1)

MPCall_133_0x90
	beq		cr1, MPCall_133_0xa0
	bgt		cr1, MPCall_133_0x9c
	li		r17,  0x00

MPCall_133_0x9c
	sth		r17, PSA._35a(r1)

MPCall_133_0xa0
;	r1 = kdp
	bl		ScreenConsole_redraw

;	r1 = kdp
	b		ReleaseAndReturnZeroFromMPCall



MPCall_Panic
	b		panic
