// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32\nkernsmp\arm\ncthrd.cia
// 
//

#define __INCLUDE_NTHREADBASE_DEFINES__

#include <arm.h>
#include <e32cia.h>

#undef	iDfcState
#define	iDfcState		i8816.iHState16

extern "C" void send_accumulated_resched_ipis();

/******************************************************************************
 * Thread
 ******************************************************************************/
extern "C" __NAKED__ void __StartThread()
	{
	// On entry:
	//		R0->TSubScheduler, R1=0, R2=1, R3->current thread
	//		R12=resched IPIs
	// Interrupts disabled

	// need to send any outstanding reschedule IPIs
	asm("cmp	r12, #0 ");
	asm("blne " CSM_CFUNC(send_accumulated_resched_ipis));
#ifdef __USER_MEMORY_GUARDS_ENABLED__
	asm("ldr	r0, [sp, #%a0]" : : "i" _FOFF(SThreadExcStack, iCPSR));
	asm("tst	r0, #0x0f ");
	asm("bne	2f ");
	USER_MEMORY_GUARD_OFF(,r0,r0);
	asm("2:		");
#endif
	asm("ldmia	sp, {r0-r14}^ ");			// load initial values for R0-R12, R13_usr, R14_usr
	asm("nop	");							// don't access banked register immediately after
	asm("add	sp, sp, #64 ");				// point to saved PC, CPSR (skip iExcCode)
	asm("adr	lr, 1f ");					// set lr_svc in case thread returns
	RFEIAW(13);								// restore PC and CPSR - jump to thread entry point

	asm("1:		");
	asm("b "	CSM_ZN5NKern4ExitEv);		// if control returns, call NKern::Exit()
	}


extern "C" __NAKED__ TInt get_kernel_context_type(TLinAddr /*aReschedReturn*/)
	{
	asm("adr	r1, 9f ");
	asm("mov	r3, r0 ");
	asm("mvn	r0, #0 ");
	asm("1:		");
	asm("ldr	r2, [r1], #4 ");
	asm("add	r0, r0, #1 ");
	asm("cmp	r2, r3 ");
	asm("beq	2f ");
	asm("cmp	r2, #0 ");
	asm("bne	1b ");
	asm("mvn	r0, #0 ");
	asm("2:		");
	__JUMP(,	lr);

	asm("9:		");
	asm(".word " CSM_CFUNC(__StartThread));
	asm(".word	nkern_unlock_resched_return ");
	asm(".word	nkern_preemption_point_resched_return ");
	asm(".word	nkern_wfar_resched_return ");
	asm(".word	irq_resched_return ");
	asm(".word	exec_wfar_resched_return ");
	asm(".word	0 ");
	}


/**	Mark the beginning of an event handler tied to a thread or thread group

	Return the number of the CPU on which the event handler should run
*/
__NAKED__ TInt NSchedulable::BeginTiedEvent()
	{
	asm("add r1, r0, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("1: ");
	LDREX(0,1);						// r0 = original value of iEventState
	asm("add r2, r0, #%a0" : : "i" ((TInt)EEventCountInc));
	STREX(3,2,1);
	asm("cmp r3, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r3);
	asm("tst r0, #%a0" : : "i" ((TInt)EEventParent));
	asm("ldrne r2, [r1, #%a0]" : : "i" (_FOFF(NSchedulable,iParent) - _FOFF(NSchedulable,iEventState)));
	asm("beq bte0 ");				// EEventParent not set so don't look at group
	asm("cmp r2, #0 ");
	asm("addne r2, r2, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("beq bte_bad ");
	asm("cmp r2, r1 ");
	asm("beq bte2 ");				// parent not yet updated, use iNewParent
	asm("bte1: ");
	LDREX(0,2);						// r0 = original value of iEventState
	asm("add r3, r0, #%a0" : : "i" ((TInt)EEventCountInc));
	STREX(12,3,2);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
	asm("bte0: ");
	asm("and r0, r0, #%a0" : : "i" ((TInt)EEventCpuMask));
	__JUMP(,lr);					// return event CPU

	asm("bte2: ");
	__DATA_MEMORY_BARRIER__(r3);	// make sure iNewParent is read after iParent
	asm("ldr r2, [r1, #%a0]" : : "i" (_FOFF(NThreadBase,iNewParent) - _FOFF(NSchedulable,iEventState)));
	asm("cmp r2, #0 ");
	asm("addne r2, r2, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("bne bte1 ");				// iNewParent set so OK
	__DATA_MEMORY_BARRIER__(r3);	// make sure iParent is read after iNewParent
	asm("ldr r2, [r1, #%a0]" : : "i" (_FOFF(NSchedulable,iParent) - _FOFF(NSchedulable,iEventState)));
	asm("cmp r2, #0 ");
	asm("addne r2, r2, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("cmp r2, r1 ");
	asm("bne bte1 ");				// iParent now set so OK, otherwise something is wrong

	asm("bte_bad: ");
	__ASM_CRASH();
	}


/**	Mark the end of an event handler tied to a thread or thread group

*/
__NAKED__ void NSchedulable::EndTiedEvent()
	{
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("ldr r1, [r0, #%a0]!" : : "i" _FOFF(NSchedulable, iEventState));
	asm("tst r1, #%a0" : : "i" ((TInt)EEventParent));
	asm("bne etep0 ");				// branch out if EEventParent set

	// r0->NSchedulable::iEventState
	asm("ete1: ");
	LDREX(1,0);
	asm("sub r1, r1, #%a0" : : "i" ((TInt)EEventCountInc));	// decrement event count
	asm("cmp r1, #%a0" : : "i" ((TInt)EEventCountInc));		// check if now zero
	asm("biccc r1, r1, #0xFF ");	// if so, mask event CPU ...
	asm("andcc r2, r1, #0x1F00 ");	// ... and r2 = thread CPU << 8 ...
	asm("orrcc r1, r1, r2, lsr #8 ");	// ... and event CPU = thread CPU
	STREX(12,1,0);
	asm("teq r12, #0 ");			// test for success, leave carry alone
	asm("bne ete1 ");				// retry if STREX failed
	asm("bcs ete2 ");				// if not last tied event, finish
	asm("tst r1, #%a0" : : "i" ((TInt)EDeferredReady));
	asm("addne r0, r0, #%a0" : : "i" (_FOFF(NSchedulable,i_IDfcMem) - _FOFF(NSchedulable,iEventState)));
	asm("bne " CSM_ZN4TDfc3AddEv );	// if deferred ready, add IDFC to action it
	asm("ete2: ");					// ready not deferred so finish
	__JUMP(,lr);

	asm("etep0: ");
	__DATA_MEMORY_BARRIER__(r12);	// make sure iParent is read after seeing parent flag set
	asm("ldr r3, [r0, #%a0]" : : "i" (_FOFF(NSchedulable,iParent) - _FOFF(NSchedulable,iEventState)));
	asm("cmp r3, #0 ");
	asm("addne r3, r3, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("beq ete_bad ");			// no parent - shouldn't happen
	asm("cmp r3, r0 ");				// parent == this ?
	asm("beq etep1 ");				// if so, parent not yet updated so use iNewParent

	asm("etep2: ");
	asm("stmfd sp!, {r0,lr} ");		// save this and return address
	asm("mov r0, r3 ");				// operate on parent
	asm("bl ete1 ");				// update parent state
	asm("ldmfd sp!, {r0,lr} ");
	asm("1: ");
	LDREX(1,0);
	asm("sub r1, r1, #%a0" : : "i" ((TInt)EEventCountInc));	// decrement event count
	STREX(12,1,0);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__JUMP(,lr);

	asm("etep1: ");
	__DATA_MEMORY_BARRIER__(r12);	// make sure iNewParent is read after iParent
	asm("ldr r3, [r0, #%a0]" : : "i" (_FOFF(NThreadBase,iNewParent) - _FOFF(NSchedulable,iEventState)));
	asm("cmp r3, #0 ");
	asm("addne r3, r3, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("bne etep2 ");				// iNewParent set so OK
	__DATA_MEMORY_BARRIER__(r12);	// make sure iParent is read after iNewParent
	asm("ldr r3, [r0, #%a0]" : : "i" (_FOFF(NSchedulable,iParent) - _FOFF(NSchedulable,iEventState)));
	asm("cmp r3, #0 ");
	asm("addne r3, r3, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("cmp r3, r0 ");
	asm("bne etep2 ");				// iParent now set so OK, otherwise something is wrong

	asm("ete_bad: ");
	__ASM_CRASH();
	}


/**	Check for concurrent tied events when a thread/group becomes ready

	This is only ever called on a lone thread or a group, not on a thread
	which is part of a group.

	Update the thread CPU field in iEventState
	If thread CPU != event CPU and event count nonzero, atomically
	set the ready deferred flag and return TRUE, else return FALSE.
	If event count zero, set event CPU = thread CPU atomically.

	@param aCpu the CPU on which the thread/group is to become ready
	@return	TRUE if the ready must be deferred.
*/
__NAKED__ TBool NSchedulable::TiedEventReadyInterlock(TInt /*aCpu*/)
	{
	asm("add r0, r0, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("1: ");
	LDREX(2,0);						// r2 = original iEventState
	asm("bic r3, r2, #0x1F00 ");	// r3 = original iEventState with thread CPU zeroed out
	asm("orr r3, r3, r1, lsl #8 ");	// set thread CPU field = aCpu
	asm("cmp r3, #%a0" : : "i" ((TInt)EEventCountInc));
	asm("bhs 2f ");					// branch if event count nonzero
	asm("bic r3, r3, #0xFF ");		// else mask event CPU ...
	asm("orr r3, r3, r1 ");			// ... and set event CPU = thread CPU = aCpu
	asm("3: ");
	STREX(12,3,0);
	asm("teq r12, #0 ");
	asm("bne 1b ");
	asm("eor r0, r2, r3 ");			// r0 = old event state ^ new event state
	asm("and r0, r0, #%a0" : : "i" ((TInt)EDeferredReady));
	__JUMP(,lr);					// return TRUE if we just set EDeferredReady

	// event count is nonzero
	asm("2: ");
	asm("eor r12, r3, r3, lsr #8 ");	// r12 bottom 5 bits = thread CPU ^ event CPU
	asm("tst r12, #0x1F ");				// thread CPU == event CPU?
	asm("orrne r3, r3, #%a0" : : "i" ((TInt)EDeferredReady));	// if not, set EDeferredReady
	asm("b 3b ");
	}


/**	Check for concurrent tied events when a thread leaves a group

	If event count zero, atomically	set the event and thread CPUs to the
	current CPU, clear the parent flag and return TRUE, else return FALSE.

	@return	TRUE if the parent flag has been cleared
	@pre	Preemption disabled
*/
__NAKED__ TBool NThreadBase::TiedEventLeaveInterlock()
	{
	GET_RWNO_TID(, r1);					// R1->SubScheduler
	asm("ldr r1, [r1, #%a0]" : : "i" _FOFF(TSubScheduler, iCpuNum));
	asm("add r0, r0, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("orr r1, r1, r1, lsl #8 ");		// event CPU = thread CPU = this CPU, EDeferredReady, EEventParent clear
	asm("1: ");
	LDREX(2,0);
	asm("cmp r2, #%a0" : : "i" ((TInt)EEventCountInc));		// check if event count zero
	asm("bhs 0f ");						// if not, finish and return FALSE
	STREX(3,1,0);						// else update CPUs and clear parent flag
								// NOTE: Deferred ready flag must have been clear since thread is running
	asm("cmp r3, #0 ");
	asm("bne 1b ");
	__JUMP(,lr);				// return TRUE (assumes this!=0)
	asm("0:");
	asm("mov r0, #0 ");
	__JUMP(,lr);				// return FALSE
	}


/**	Check for concurrent tied events when a thread joins a group

	If event count zero, atomically	set the parent flag and return TRUE,
	else return FALSE.

	@return	TRUE if the parent flag has been set
	@pre	Preemption disabled
*/
__NAKED__ TBool NThreadBase::TiedEventJoinInterlock()
	{
	asm("add r0, r0, #%a0" : : "i" _FOFF(NSchedulable,iEventState));
	asm("1: ");
	LDREX(1,0);
	asm("cmp r1, #%a0" : : "i" ((TInt)EEventCountInc));		// check if event count zero
	asm("bhs 0f ");						// if not, finish and return FALSE
	asm("orr r2, r1, #%a0" : : "i" ((TInt)EEventParent));	// else set parent flag
	STREX(3,2,0);
	asm("cmp r3, #0 ");
	asm("bne 1b ");
	__JUMP(,lr);				// return TRUE (assumes this!=0)
	asm("0:");
	asm("mov r0, #0 ");
	__JUMP(,lr);				// return FALSE
	}


#ifdef __FAST_SEM_MACHINE_CODED__
/******************************************************************************
 * Fast semaphore
 ******************************************************************************/

/** Waits on a fast semaphore.

    Decrements the signal count for the semaphore and
	removes the calling thread from the ready-list if the semaphore becomes
	unsignalled. Only the thread that owns a fast semaphore can wait on it.
	
	Note that this function does not block, it merely updates the NThread state,
	rescheduling will only occur when the kernel is unlocked. Generally threads
	would use NKern::FSWait() which manipulates the kernel lock for you.

	@pre The calling thread must own the semaphore.
	@pre No fast mutex can be held.
	@pre Kernel must be locked.
	
	@post Kernel is locked.
	
	@see NFastSemaphore::Signal()
	@see NKern::FSWait()
	@see NKern::Unlock()
 */
EXPORT_C __NAKED__ void NFastSemaphore::Wait()
	{
	ASM_DEBUG1(FSWait,r0);

	GET_RWNO_TID(,r1);
	asm("stmfd	sp!, {r4-r7} ");
	asm("ldr	r6, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	asm("mov	r3, r0 ");
	asm("mov	r2, #%a0" : : "i" ((TInt)NThreadBase::EWaitFastSemaphore << 8));
	asm("add	r7, r6, #%a0" : : "i" _FOFF(NThreadBase, iWaitState));
	asm("orr	r2, r2, #%a0" : : "i" ((TInt)NThreadWaitState::EWtStWaitPending));
	asm("1:		");
	LDREXD(		4,7);
	STREXD(		12,2,7);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("str	r12, [r7, #%a0]" : : "i" _FOFF(NThreadWaitState, iTimer.iTriggerTime));
	asm("cmp	r4, #0 ");
	asm("bne	0f ");

	__DATA_MEMORY_BARRIER__(r12);
	asm("1:		");
	LDREX(		2,0);					// count
	asm("mov	r5, r6, lsr #2 ");		// thread>>2
	asm("orr	r5, r5, #0x80000000 ");
	asm("subs	r4, r2, #1 ");
	asm("movlt	r4, r5 ");				// if --count<0, r4=(thread>>2)|0x80000000
	STREX(		12,4,0);
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	__DATA_MEMORY_BARRIER__(r12);

	asm("cmp	r2, #0 ");				// original count zero ?
	asm("bne	2f ");					// if yes, don't need to wait
	asm("mov	r2, #1 ");
	asm("strb	r2, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iRescheduleNeededFlag));	// else we need to reschedule
	asm("ldmfd	sp!, {r4-r7} ");
	__JUMP(,	lr);

	asm("2:		");
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("1:		");
	LDREXD(		4,7);
	STREXD(		12,2,7);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("tst	r4, #%a0" : : "i" ((TInt)(NThreadWaitState::EWtStDead|NThreadWaitState::EWtStWaitActive)));
	asm("bne	0f ");
	asm("ldmfd	sp!, {r4-r7} ");
	__JUMP(,	lr);

	asm("0:		");
	__ASM_CRASH();
	}


/** Waits on a fast semaphore.

    Decrements the signal count for the semaphore
	and waits for a signal if the semaphore becomes unsignalled. Only the
	thread that owns a fast	semaphore can wait on it.

	@param aSem The semaphore to wait on.
	
	@pre The calling thread must own the semaphore.
	@pre No fast mutex can be held.
	
	@see NFastSemaphore::Wait()
*/
EXPORT_C __NAKED__ void NKern::FSWait(NFastSemaphore* /*aSem*/)
	{
	ASM_DEBUG1(NKFSWait,r0);

	__ASM_CLI();							// all interrupts off
	GET_RWNO_TID(,r1);
	asm("stmfd	sp!, {r4,r5,r11,lr} ");
	asm("ldr	r11, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	asm("mov	r2, #%a0" : : "i" ((TInt)NThreadBase::EWaitFastSemaphore << 8));
	asm("orr	r2, r2, #%a0" : : "i" ((TInt)NThreadWaitState::EWtStWaitPending));
	asm("mov	r3, r0 ");
	asm("add	r0, r11, #%a0" : : "i" _FOFF(NThreadBase, iWaitState));
	asm("b		nkfswait1 ");
	}


/** Waits for a signal on the current thread's I/O semaphore.

	@pre No fast mutex can be held.
	@pre Call in a thread context.
	@pre Kernel must be unlocked
	@pre interrupts enabled
 */
EXPORT_C __NAKED__ void NKern::WaitForAnyRequest()
	{
	ASM_DEBUG0(WFAR);

	__ASM_CLI();							// all interrupts off
	GET_RWNO_TID(,r1);
	asm("stmfd	sp!, {r4,r5,r11,lr} ");
	asm("ldr	r11, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	asm("mov	r2, #%a0" : : "i" ((TInt)NThreadBase::EWaitFastSemaphore << 8));
	asm("orr	r2, r2, #%a0" : : "i" ((TInt)NThreadWaitState::EWtStWaitPending));
	asm("add	r0, r11, #%a0" : : "i" _FOFF(NThreadBase, iWaitState));
	asm("add	r3, r11, #%a0" : : "i" _FOFF(NThreadBase, iRequestSemaphore));

	asm("nkfswait1: ");
	asm("1:		");
	LDREXD(		4,0);
	STREXD(		12,2,0);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(NThreadWaitState, iTimer.iTriggerTime));
	asm("cmp	r4, #0 ");
	asm("bne	0f ");

	__DATA_MEMORY_BARRIER__(r12);
	asm("1:		");
	LDREX(		2,3);					// count
	asm("mov	r5, r11, lsr #2 ");		// thread>>2
	asm("orr	r5, r5, #0x80000000 ");
	asm("subs	r4, r2, #1 ");
	asm("movlt	r4, r5 ");				// if --count<0, r4=(thread>>2)|0x80000000
	STREX(		12,4,3);
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	__DATA_MEMORY_BARRIER__(r12);

	asm("cmp	r2, #0 ");				// original count zero ?
	asm("beq	2f ");					// if so we must wait
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("1:		");
	LDREXD(		4,0);
	STREXD(		12,2,0);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("tst	r4, #%a0" : : "i" ((TInt)(NThreadWaitState::EWtStDead|NThreadWaitState::EWtStWaitActive)));
	asm("bne	0f ");
	__ASM_STI();
	__POPRET("r4,r5,r11,");

	asm("0:		");
	__ASM_CRASH();

	asm("2:		");
	asm("ldmfd	sp!, {r4-r5} ");
	asm("mov	r2, #1 ");
	asm("str	r2, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iKernLockCount));			// else lock the kernel
	__ASM_STI();
	asm("strb	r2, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iRescheduleNeededFlag));	// and set the reschedule flag
	asm("stmfd	sp!, {r0,r4-r10} ");
	asm("bl "	CSM_ZN10TScheduler10RescheduleEv );	// reschedule
	asm(".global nkern_wfar_resched_return ");
	asm("nkern_wfar_resched_return: ");

	// need to send any outstanding reschedule IPIs
	asm("cmp	r12, #0 ");
	asm("blne " CSM_CFUNC(send_accumulated_resched_ipis));
	__ASM_STI();
	__POPRET("r0,r4-r11,");

	asm(".global wait_for_any_request ");
	asm("wait_for_any_request: ");
	asm("add	r3, r9, #%a0" : : "i" _FOFF(NThreadBase,iRequestSemaphore));
	asm("mov	r2, #%a0" : : "i" ((TInt)NThreadBase::EWaitFastSemaphore << 8));
	asm("add	r7, r9, #%a0" : : "i" _FOFF(NThreadBase, iWaitState));
	asm("orr	r2, r2, #%a0" : : "i" ((TInt)NThreadWaitState::EWtStWaitPending));
	asm("1:		");
	LDREXD(		4,7);
	STREXD(		12,2,7);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("str	r12, [r7, #%a0]" : : "i" _FOFF(NThreadWaitState, iTimer.iTriggerTime));
	asm("cmp	r4, #0 ");
	asm("bne	0b ");

	__DATA_MEMORY_BARRIER__(r12);
	asm("1:		");
	LDREX(		0,3);					// count
	asm("mov	r5, r9, lsr #2 ");		// thread>>2
	asm("orr	r5, r5, #0x80000000 ");
	asm("subs	r4, r0, #1 ");
	asm("movlt	r4, r5 ");				// if --count<0, r4=(thread>>2)|0x80000000
	STREX(		12,4,3);
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	__DATA_MEMORY_BARRIER__(r12);
#ifdef __RECORD_STATE__
	asm("str	r0, [r9, #%a0]" : : "i" _FOFF(NThreadBase,iNThreadBaseSpare6));
#endif

	asm("cmp	r0, #0 ");				// original count zero ?
	asm("beq	exec_wfar_wait ");		// yes - must wait
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("1:		");
	LDREXD(		4,7);
	STREXD(		12,2,7);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("tst	r4, #%a0" : : "i" ((TInt)(NThreadWaitState::EWtStDead|NThreadWaitState::EWtStWaitActive)));
	asm("ldreq	r4, [r9, #%a0]" : : "i" _FOFF(NThreadBase,iUserModeCallbacks));	// check for callbacks
	asm("beq	exec_wfar_finish ");
	asm("b		0b ");
	}


/** Signals a fast semaphore.

    Increments the signal count of a fast semaphore by
	one and releases any waiting thread if the semphore becomes signalled.
	
	Note that a reschedule will not occur before this function returns, this will
	only take place when the kernel is unlocked. Generally threads
	would use NKern::FSSignal() which manipulates the kernel lock for you.
	
	@pre Kernel must be locked.
	@pre Call either in a thread or an IDFC context.
	
	@post Kernel is locked.
	
	@see NFastSemaphore::Wait()
	@see NKern::FSSignal()
	@see NKern::Unlock()
 */
EXPORT_C __NAKED__ void NFastSemaphore::Signal()
	{
	ASM_DEBUG1(FSSignal,r0);

	asm("mov	r1, #1 ");
	asm("fssignal1: ");
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("1:		");
	LDREX(		2,0);				// count
	asm("cmp	r2, #0 ");
	asm("sublt	r3, r1, #1 ");		// if count<0, replace with aCount-1
	asm("addges	r3, r2, r1 ");		// if count>=0, add aCount
	asm("bvs	0f ");				// if overflow, leave alone
	STREX(		12,3,0);
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	asm("cmp	r2, #0 ");
	asm("movlt	r1, r2, lsl #2 ");	// if original count<0 r1 = original count<<2 = thread
	asm("blt	fs_signal_wake ");
	asm("0:		");
	__JUMP(,	lr);				// else finished

	asm("fs_signal_wake: ");
	asm("stmfd	sp!, {r4-r6,lr} ");
	asm("mov	r4, r0 ");
	asm("mov	r5, r1 ");
	asm("mov	r0, r1 ");
	asm("bl		AcqSLock__12NSchedulable ");
	asm("add	r0, r5, #%a0" : : "i" _FOFF(NThreadBase, iWaitState));
	asm("mov	r1, #%a0" : : "i" ((TInt)NThreadBase::EWaitFastSemaphore));
	asm("mov	r2, r4 ");
	asm("mov	r3, #0 ");
	asm("bl		UnBlockT__16NThreadWaitStateUiPvi ");
	asm("mov	r0, r5 ");
	asm("ldmfd	sp!, {r4-r6,lr} ");
	asm("b		RelSLock__12NSchedulable ");
	}


/** Signals a fast semaphore multiple times.

	@pre Kernel must be locked.
	@pre Call either in a thread or an IDFC context.
	
	@post Kernel is locked.

	@internalComponent	
 */
EXPORT_C __NAKED__ void NFastSemaphore::SignalN(TInt /*aCount*/)
	{
	ASM_DEBUG2(FSSignalN,r0,r1);

	asm("cmp	r1, #0 ");
	asm("bgt	fssignal1 ");
	__JUMP(,	lr);
	}


/** Signals the request semaphore of a nanothread several times.

	This function is intended to be used by the EPOC layer and personality
	layers.  Device drivers should use Kern::RequestComplete instead.

	@param aThread Nanothread to signal.  If NULL, the current thread is signaled.
	@param aCount Number of times the request semaphore must be signaled.
	
	@pre aCount >= 0

	@see Kern::RequestComplete()
 */
EXPORT_C __NAKED__ void NKern::ThreadRequestSignal(NThread* /*aThread*/, TInt /*aCount*/)
	{
	ASM_DEBUG2(NKThreadRequestSignalN,r0,r1);

	asm("cmp	r1, #0 ");
	asm("ble	0f ");
	asm("cmp	r0, #0 ");
	asm("addne	r0, r0, #%a0" : : "i" _FOFF(NThreadBase,iRequestSemaphore));
	asm("bne	nkfssignal1 ");
	__ASM_CLI();
	GET_RWNO_TID(,r0);
	asm("ldr	r0, [r0, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	asm("add	r0, r0, #%a0" : : "i" _FOFF(NThreadBase,iRequestSemaphore));
	asm("b		nkfssignal2 ");

	asm("0:		");
	__JUMP(eq,	lr);
	__ASM_CRASH();
	}


/** Signals the request semaphore of a nanothread.

	This function is intended to be used by the EPOC layer and personality
	layers.  Device drivers should use Kern::RequestComplete instead.

	@param aThread Nanothread to signal. Must be non NULL.

	@see Kern::RequestComplete()

	@pre Interrupts must be enabled.
	@pre Do not call from an ISR
 */
EXPORT_C __NAKED__ void NKern::ThreadRequestSignal(NThread* /*aThread*/)
	{
	ASM_DEBUG1(NKThreadRequestSignal,r0);
	asm("add	r0, r0, #%a0" : : "i" _FOFF(NThreadBase,iRequestSemaphore));

	/* fall through to FSSignal() ... */
	}


/** Signals a fast semaphore.

    Increments the signal count of a fast semaphore
	by one and releases any	waiting thread if the semaphore becomes signalled.
	
	@param aSem The semaphore to signal.

	@see NKern::FSWait()

	@pre Interrupts must be enabled.
	@pre Do not call from an ISR
 */
EXPORT_C __NAKED__ void NKern::FSSignal(NFastSemaphore* /*aSem*/)
	{
	ASM_DEBUG1(NKFSSignal,r0);

	asm("mov	r1, #1 ");
	asm("nkfssignal1: ");
	__ASM_CLI();
	asm("nkfssignal2: ");
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("1:		");
	LDREX(		2,0);				// count
	asm("cmp	r2, #0 ");
	asm("sublt	r3, r1, #1 ");		// if count<0, replace with aCount-1
	asm("addges	r3, r2, r1 ");		// if count>=0, add aCount
	asm("bvs	0f ");				// if overflow, leave alone
	STREX(		12,3,0);
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	asm("cmp	r2, #0 ");
	asm("blt	2f ");
	asm("0:		");
	__ASM_STI();
	__JUMP(,	lr);				// else finished

	asm("2:		");
	GET_RWNO_TID(,r3);
	asm("mov	r1, r2, lsl #2 ");	// if original count<0 r1 = original count<<2 = thread
	asm("ldr	r12, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iKernLockCount));
	asm("stmfd	sp!, {r4,lr} ");
	asm("add	r12, r12, #1 ");			// lock the kernel
	asm("str	r12, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iKernLockCount));
	__ASM_STI();
	asm("bl		fs_signal_wake ");			// wake up the thread
	asm("ldmfd	sp!, {r4,lr} ");
	asm("b		Unlock__5NKern ");
	}


/** Signals a fast semaphore multiple times.

    Increments the signal count of a
	fast semaphore by aCount and releases any waiting thread if the semphore
	becomes signalled.
	
	@param aSem The semaphore to signal.
	@param aCount The number of times to signal the semaphore.

	@see NKern::FSWait()

	@pre Interrupts must be enabled.
	@pre Do not call from an ISR
 */
EXPORT_C __NAKED__ void NKern::FSSignalN(NFastSemaphore* /*aSem*/, TInt /*aCount*/)
	{
	ASM_DEBUG2(NKFSSignalN,r0,r1);

	asm("cmp	r1, #0 ");
	asm("bgt	nkfssignal1 ");
	__JUMP(,	lr);
	}


/** Cancels a wait on a fast semaphore.

	@pre Kernel must be locked.
	@pre Call either in a thread or an IDFC context.
	
	@post Kernel is locked.

	@internalComponent	
 */
__NAKED__ void NFastSemaphore::WaitCancel()
	{
	asm("mov	r1, #1 ");
	/* Fall through ... */
	}

/* Fall through ... */
#endif
/* Fall through ... */

/**	Increment a fast semaphore count

	Do memory barrier
	If iCount >= 0, increment by aCount and return 0
	If iCount < 0, set count equal to aCount-1 and return (original count << 2)

	Release semantics
*/
__NAKED__ NThreadBase* NFastSemaphore::Inc(TInt /*aCount*/)
	{
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("1: ");
	LDREX(2,0);					// count
	asm("cmp r2, #0 ");
	asm("sublt r3, r1, #1 ");	// if count<0, replace with aCount-1
	asm("addges r3, r2, r1 ");	// if count>=0, add aCount
	asm("bvs 0f ");				// if overflow leave alone
	STREX(12,3,0);
	asm("teq r12, #0 ");
	asm("bne 1b ");
	asm("0: ");
	asm("cmp r2, #0 ");
	asm("movlt r0, r2, lsl #2 ");	// if original count<0, return count<<2
	asm("movge r0, #0 ");			// else return 0
	__JUMP(,lr);
	}


/**	Decrement a fast semaphore count

	If count > 0, decrement
	If count = 0, set equal to (thread>>2)|0x80000000
	Return original count
	Full barrier semantics
*/
__NAKED__ TInt NFastSemaphore::Dec(NThreadBase*)
	{
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("1: ");
	LDREX(2,0);					// count
	asm("subs r3, r2, #1 ");
	asm("movlt r3, #0x80000000 ");
	asm("orrlt r3, r3, r1, lsr #2 ");	// if --count<0, r3=(thread>>2)|0x80000000
	STREX(12,3,0);
	asm("teq r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
	asm("mov r0, r2 ");			// return original count
	__JUMP(,lr);
	}

/**	Reset a fast semaphore count

	Do memory barrier
	If iCount >= 0, set iCount=0 and return 0
	If iCount < 0, set iCount=0 and return (original count << 2)

	Release semantics
*/
__NAKED__ NThreadBase* NFastSemaphore::DoReset()
	{
	__DATA_MEMORY_BARRIER_Z__(r3);
	asm("1: ");
	LDREX(2,0);					// count
	STREX(12,3,0);				// zero count
	asm("teq r12, #0 ");
	asm("bne 1b ");
	asm("cmp r2, #0 ");
	asm("movlt r0, r2, lsl #2 ");	// if original count<0, return count<<2
	asm("movge r0, #0 ");			// else return 0
	__JUMP(,lr);
	}


#ifdef __NTHREAD_WAITSTATE_MACHINE_CODED__
/******************************************************************************
 * Thread wait state
 ******************************************************************************/

__NAKED__ void NThreadWaitState::SetUpWait(TUint /*aType*/, TUint /*aFlags*/, TAny* /*aWaitObj*/)
	{
	asm("stmfd	sp!, {r4-r5} ");
	asm("and	r2, r2, #%a0" : : "i" ((TInt)EWtStObstructed));
	asm("and	r1, r1, #0xff ");
	asm("orr	r2, r2, #%a0" : : "i" ((TInt)EWtStWaitPending));
	asm("orr	r2, r2, r1, lsl #8 ");
	asm("1:		");
	LDREXD(		4,0);
	STREXD(		12,2,0);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("cmp	r4, #0 ");
	asm("bne	0f ");
	asm("ldmfd	sp!, {r4-r5} ");
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(NThreadWaitState, iTimer.iTriggerTime));
	__JUMP(,	lr);

	asm("0:		");
	__ASM_CRASH();
	}

__NAKED__ void NThreadWaitState::SetUpWait(TUint /*aType*/, TUint /*aFlags*/, TAny* /*aWaitObj*/, TUint32 /*aTimeout*/)
	{
	asm("stmfd	sp!, {r4-r5} ");
	asm("and	r2, r2, #%a0" : : "i" ((TInt)EWtStObstructed));
	asm("and	r1, r1, #0xff ");
	asm("orr	r2, r2, #%a0" : : "i" ((TInt)EWtStWaitPending));
	asm("orr	r2, r2, r1, lsl #8 ");
	asm("1:		");
	LDREXD(		4,0);
	STREXD(		12,2,0);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("ldr	r12, [sp, #8] ");
	asm("cmp	r4, #0 ");
	asm("bne	0f ");
	asm("ldmfd	sp!, {r4-r5} ");
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(NThreadWaitState, iTimer.iTriggerTime));
	__JUMP(,	lr);

	asm("0:		");
	__ASM_CRASH();
	}

__NAKED__ void NThreadWaitState::CancelWait()
	{
	asm("mov	r12, r0 ");
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("1:		");
	LDREXD(		0,12);
	STREXD(		1,2,12);
	asm("cmp	r1, #0 ");
	asm("bne	1b ");
	asm("tst	r0, #%a0" : : "i" ((TInt)(EWtStDead|EWtStWaitActive)));
	asm("bne	0f ");
	__JUMP(,	lr);

	asm("0:		");
	__ASM_CRASH();
	}

__NAKED__ TInt NThreadWaitState::DoWait()
	{
	asm("ldr	r1, [r0, #%a0]" : : "i" _FOFF(NThreadWaitState,iTimer.iTriggerTime));
	asm("1:		");
	LDREXD(		2,0);
	asm("cmp	r1, #0 ");
	asm("orrne	r2, r2, #%a0" : : "i" ((TInt)EWtStTimeout));
	asm("tst	r2, #%a0" : : "i" ((TInt)EWtStDead));
	asm("bne	0f ");
	asm("tst	r2, #%a0" : : "i" ((TInt)EWtStWaitPending));
	asm("beq	9f ");
	asm("bic	r2, r2, #%a0" : : "i" ((TInt)EWtStWaitPending));
	asm("orr	r2, r2, #%a0" : : "i" ((TInt)EWtStWaitActive));
	STREXD(		12,2,0);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	asm("cmp	r1, #0 ");
	asm("bne	2f ");
	asm("mov	r0, r2, lsr #8 ");
	__JUMP(,	lr);

	asm("2:		");
	asm("stmfd	sp!, {r2-r4,lr} ");
	asm("mov	r4, r0 ");
	asm("add	r0, r0, #%a0" : : "i" _FOFF(NThreadWaitState,iTimer));
	asm("mov	r2, #1 ");
	asm("bl	"	CSM_ZN6NTimer7OneShotEii );
	asm("ldr	r1, [r4, #%a0]" : : "i" _FOFF(NThreadWaitState,iTimer.iNTimerSpare1));
	asm("cmp	r0, #0 ");
	asm("bne	8f ");
	asm("add	r1, r1, #1 ");
	asm("str	r1, [r4, #%a0]" : : "i" _FOFF(NThreadWaitState,iTimer.iNTimerSpare1));
	asm("ldmfd	sp!, {r2-r4,lr} ");
	asm("mov	r0, r2, lsr #8 ");
	__JUMP(,	lr);

	asm("0:		");
	asm("mvn	r0, #%a0" : : "i" (~KErrDied));
	__JUMP(,	lr);
	asm("9:		");
	asm("mvn	r0, #%a0" : : "i" (~KErrGeneral));
	__JUMP(,	lr);
	asm("8:		");
	__ASM_CRASH();
	}

__NAKED__ TInt NThreadWaitState::UnBlockT(TUint /*aType*/, TAny* /*aWaitObj*/, TInt /*aReturnValue*/)
	{
	asm("stmfd	sp!, {r4-r6,lr} ");
	asm("mov	r6, r2 ");					// r6 = aWaitObj
	asm("mov	r2, #0 ");
	__DATA_MEMORY_BARRIER__(r2);
	asm("1:		");
	LDREXD(		4,0);						// r5:r4 = oldws64
	asm("cmp	r5, r6 ");					// does iWaitObj match?
	asm("bne	2f ");						// no
	asm("eor	r12, r4, r1, lsl #8 ");		// does wait type match?
	asm("cmp	r12, #%a0" : : "i" ((TInt)EWtStDead));
	asm("bhs	2f ");						// no
	STREXD(		12,2,0);					// yes - wait matches - try to write return value
	asm("cmp	r12, #0 ");					// success?
	asm("bne	1b ");						// no - retry
	asm("mov	r6, r0 ");
	asm("tst	r4, #%a0" : : "i" ((TInt)EWtStTimeout));
	asm("blne	CancelTimerT__16NThreadWaitState ");
	asm("tst	r4, #%a0" : : "i" ((TInt)EWtStWaitActive));
	asm("beq	0f ");
	asm("ldr	r1, [r6, #%a0]" : : "i" (_FOFF(NThreadBase,iPauseCount)-_FOFF(NThreadBase,iWaitState)));
	asm("sub	r0, r6, #%a0" : : "i" _FOFF(NThreadBase,iWaitState));	// r0 = Thread()
	asm("movs	r1, r1, lsl #16 ");				// check if iPauseCount=iSuspendCount=0
	asm("bleq	ReadyT__12NSchedulableUi ");	// if so, make thread ready
	asm("0:		");
	asm("mov	r0, #0 ");
	__POPRET("	r4-r6,");					// return KErrNone

	asm("2:		");
	STREXD(		12,4,0);					// no matching wait - write back to check atomicity
	asm("cmp	r12, #0 ");					// success?
	asm("bne	1b ");						// no - retry
	asm("mvn	r0, #%a0" : : "i" (~KErrGeneral));
	__POPRET("	r4-r6,");					// no matching wait - return KErrGeneral
	}

__NAKED__ TUint32 NThreadWaitState::ReleaseT(TAny*& /*aWaitObj*/, TInt /*aReturnValue*/)
	{
	asm("stmfd	sp!, {r4-r5} ");
	asm("mov	r3, r2 ");
	asm("mov	r2, #0 ");
	__DATA_MEMORY_BARRIER__(r2);
	asm("1:		");
	LDREXD(		4,0);
	asm("and	r2, r4, #%a0" : : "i" ((TInt)EWtStDead));
	STREXD(		12,2,0);
	asm("cmp	r12, #0 ");
	asm("bne	1b ");
	__DATA_MEMORY_BARRIER__(r12);
	asm("str	r5, [r1] ");
	asm("tst	r4, #%a0" : : "i" ((TInt)EWtStTimeout));
	asm("bne	2f ");
	asm("mov	r0, r4 ");
	asm("ldmfd	sp!, {r4-r5} ");
	__JUMP(,	lr);

	asm("2:		");
	asm("mov	r5, lr ");
	asm("bl		CancelTimerT__16NThreadWaitState ");
	asm("mov	r0, r4 ");
	asm("mov	lr, r5 ");
	asm("ldmfd	sp!, {r4-r5} ");
	__JUMP(,	lr);
	}
#endif


#ifdef __FAST_MUTEX_MACHINE_CODED__
/******************************************************************************
 * Fast mutex
 ******************************************************************************/

/** Releases a previously acquired fast mutex.
	
	Generally, threads would use NKern::FMSignal() which manipulates the kernel lock
	for you.
	
	@pre The calling thread holds the mutex.
	@pre Kernel must be locked.
	
	@post Kernel is locked.
	
	@see NFastMutex::Wait()
	@see NKern::FMSignal()
*/
EXPORT_C __NAKED__ void NFastMutex::Signal()
	{
	ASM_DEBUG1(FMSignal,r0);
#ifdef BTRACE_FAST_MUTEX
//	BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexSignal, this);
	asm("stmfd	sp!, {r0,lr} ");
	asm("mov	r1, r0 ");
	asm("ldr	r0, btrace_hdr_fmsignal ");
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("bl		OutX__6BTraceUlUlUlUl ");
	asm("ldmfd	sp!, {r0,lr} ");
#endif
	GET_RWNO_TID(,r3);
	asm("mov	r12, #0 ");
	__DATA_MEMORY_BARRIER__(r12);
	asm("ldr	r1, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	__ASM_CLI();
	asm("1:		");
	LDREX(		2,0);				// r2=aMutex->iHoldingThread
	asm("cmp	r2, r1 ");			// anyone else waiting?
	asm("mov	r2, #0 ");
	asm("bne	2f ");				// branch out if someone else waiting
	STREX(		12,2,0);			// else try to clear the holding thread
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	asm("str	r12, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	__ASM_STI();
	__JUMP(,lr);					// mutex released without contention

#ifdef BTRACE_FAST_MUTEX
	asm("btrace_hdr_fmsignal: ");
	asm(".word %a0" : : "i" (BTRACE_HEADER_C(8,BTrace::EFastMutex,BTrace::EFastMutexSignal)));
#endif

	// there is contention
	asm("2:		");
	asm("orr	r12, r0, #1 ");
	asm("str	r12, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	__ASM_STI();
	asm("b		DoSignalL__10NFastMutex ");
	}


/** Acquires the System Lock.

    This will block until the mutex is available, and causes
	the thread to enter an implicit critical section until the mutex is released.

	@post System lock is held.

	@see NKern::UnlockSystem()
	@see NKern::FMWait()

	@pre No fast mutex can be held.
	@pre Call in a thread context.
	@pre Kernel must be unlocked
	@pre interrupts enabled

*/
EXPORT_C __NAKED__ void NKern::LockSystem()
	{
	asm("ldr	r0, __SystemLock ");

	/* fall through to FMWait() ... */
	}

/** Acquires a fast mutex.

    This will block until the mutex is available, and causes
	the thread to enter an implicit critical section until the mutex is released.

	@param aMutex The fast mutex to acquire.
	
	@post The calling thread holds the mutex.
	
	@see NFastMutex::Wait()
	@see NKern::FMSignal()

	@pre No fast mutex can be held.
	@pre Call in a thread context.
	@pre Kernel must be unlocked
	@pre interrupts enabled

*/
EXPORT_C __NAKED__ void NKern::FMWait(NFastMutex* /*aMutex*/)
	{
	ASM_DEBUG1(NKFMWait,r0);

	__ASM_CLI();
	GET_RWNO_TID(,r3);
	asm("ldr	r1, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	asm("1:		");
	LDREX(		2,0);				// r2=aMutex->iHoldingThread
	asm("cmp	r2, #0 ");			//
	asm("bne	2f ");				// branch out if mutex held
	STREX(		12,1,0);			// else try to set us as holding thread
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	asm("str	r0, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	__DATA_MEMORY_BARRIER__(r12);
	__ASM_STI();
#ifdef BTRACE_FAST_MUTEX
//	BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexWait, aMutex);
	asm("mov	r1, r0 ");
	asm("ldr	r0, btrace_hdr_fmwait ");
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("b		OutX__6BTraceUlUlUlUl ");
#endif
	__JUMP(,lr);					// mutex acquired without contention

	// there is contention
	asm("2:		");
	asm("mov	r2, #1 ");
	asm("str	r0, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	asm("str	r2, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iKernLockCount));
	__ASM_STI();
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("stmfd	sp!, {r4,lr} ");
	asm("bl		DoWaitL__10NFastMutex ");
	asm("ldmfd	sp!, {r4,lr} ");
	asm("b		Unlock__5NKern ");

	asm("__SystemLock: ");
	asm(".word	%a0" : : "i" ((TInt)&TheScheduler.iLock));
#ifdef BTRACE_FAST_MUTEX
	asm("btrace_hdr_fmwait: ");
	asm(".word %a0" : : "i" (BTRACE_HEADER_C(8,BTrace::EFastMutex,BTrace::EFastMutexWait)));
#endif
	}


/** Releases the System Lock.

	@pre System lock must be held.

	@see NKern::LockSystem()
	@see NKern::FMSignal()
*/
EXPORT_C __NAKED__ void NKern::UnlockSystem()
	{
	asm("ldr	r0, __SystemLock ");

	/* fall through to FMSignal() ... */
	}

/** Releases a previously acquired fast mutex.
	
	@param aMutex The fast mutex to release.
	
	@pre The calling thread holds the mutex.
	
	@see NFastMutex::Signal()
	@see NKern::FMWait()
*/
EXPORT_C __NAKED__ void NKern::FMSignal(NFastMutex* /*aMutex*/)
	{
	ASM_DEBUG1(NKFMSignal,r0);
#ifdef BTRACE_FAST_MUTEX
//	BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexSignal, this);
	asm("stmfd	sp!, {r0,lr} ");
	asm("mov	r1, r0 ");
	asm("ldr	r0, btrace_hdr_fmsignal ");
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("bl		OutX__6BTraceUlUlUlUl ");
	asm("ldmfd	sp!, {r0,lr} ");
#endif
	__ASM_CLI();
	GET_RWNO_TID(,r3);
	asm("mov	r12, #0 ");
	__DATA_MEMORY_BARRIER__(r12);
	asm("ldr	r1, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	asm("1:		");
	LDREX(		12,0);				// r12=aMutex->iHoldingThread
	asm("mov	r2, #0 ");
	asm("cmp	r12, r1 ");			// anyone else waiting?
	asm("bne	2f ");				// branch out if someone else waiting
	STREX(		12,2,0);			// else try to clear the holding thread
	asm("teq	r12, #0 ");
	asm("bne	1b ");
	asm("str	r12, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	__ASM_STI();
	__JUMP(,lr);					// mutex released without contention

	// there is contention
	asm("2:		");
	asm("stmfd	sp!, {r4,lr} ");
	asm("mov	r12, #1 ");
	asm("orr	r4, r0, #1 ");
	asm("str	r12, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iKernLockCount));
	asm("str	r4, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	__ASM_STI();
	asm("bl		DoSignalL__10NFastMutex ");
	asm("ldmfd	sp!, {r4,lr} ");
	asm("b		Unlock__5NKern ");
	}


/** Temporarily releases the System Lock if there is contention.

    If there
	is another thread attempting to acquire the System lock, the calling
	thread releases the mutex and then acquires it again.
	
	This is more efficient than the equivalent code:
	
	@code
	NKern::UnlockSystem();
	NKern::LockSystem();
	@endcode

	Note that this can only allow higher priority threads to use the System
	lock as lower priority cannot cause contention on a fast mutex.

	@return	TRUE if the system lock was relinquished, FALSE if not.

	@pre	System lock must be held.

	@post	System lock is held.

	@see NKern::LockSystem()
	@see NKern::UnlockSystem()
*/
EXPORT_C __NAKED__ TBool NKern::FlashSystem()
	{
//	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"NKern::FlashSystem");
	asm("ldr	r0, __SystemLock ");

	/* fall through to FMFlash() ... */
	}

/** Temporarily releases a fast mutex if there is contention.

    If there is another thread attempting to acquire the mutex, the calling
	thread releases the mutex and then acquires it again.
	
	This is more efficient than the equivalent code:
	
	@code
	NKern::FMSignal();
	NKern::FMWait();
	@endcode

	@return	TRUE if the mutex was relinquished, FALSE if not.

	@pre	The mutex must be held.

	@post	The mutex is held.
*/
EXPORT_C __NAKED__ TBool NKern::FMFlash(NFastMutex* /*aM*/)
	{
	ASM_DEBUG1(NKFMFlash,r0);
	__ASM_CLI();
	GET_RWNO_TID(,r3);
	asm("ldr	r1, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
	asm("ldrb	r2, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iMutexPri));
	asm("ldrb	r12, [r1, #%a0]" : : "i" _FOFF(NThreadBase,iBasePri));
	asm("cmp	r2, r12 ");
	asm("bhs	1f ");							// a thread of greater or equal priority is waiting
	__ASM_STI();
#ifdef BTRACE_FAST_MUTEX
//	BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexFlash, aM);
	asm("mov	r1, r0 ");
	asm("ldr	r0, btrace_hdr_fmsignal ");
	asm("stmfd	sp!, {r4,lr} ");
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("bl		OutX__6BTraceUlUlUlUl ");
	asm("ldmfd	sp!, {r4,lr} ");
#endif
	asm("mov	r0, #0 ");
	__JUMP(,lr);								// return FALSE

#ifdef BTRACE_FAST_MUTEX
	asm("btrace_hdr_fmflash: ");
	asm(".word %a0" : : "i" (BTRACE_HEADER_C(8,BTrace::EFastMutex,BTrace::EFastMutexFlash)));
#endif

	asm("1:		");
	asm("mov	r12, #1 ");
	asm("str	r12, [r3, #%a0]" : : "i" _FOFF(TSubScheduler,iKernLockCount));
	__ASM_STI();
	asm("stmfd	sp!, {r4,lr} ");
	asm("mov	r4, r0 ");
	asm("bl		Signal__10NFastMutex ");
	asm("bl		PreemptionPoint__5NKern ");
	asm("mov	r0, r4 ");
	asm("bl		Wait__10NFastMutex ");
	asm("bl		Unlock__5NKern ");
	asm("ldmfd	sp!, {r4,lr} ");
	asm("mov	r0, #1 ");
	__JUMP(,lr);								// return TRUE
	}
#endif



/** Check whether a thread holds a fast mutex.
	If so set the mutex contention flag and return TRUE, else return FALSE.

	Called with kernel lock held

	@internalComponent
 */
__NAKED__ TBool NThreadBase::CheckFastMutexDefer()
	{
	asm("ldr r1, [r0, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	asm("bics r2, r1, #3 ");		// r2 = pointer to mutex if any, r1 bit 0 = flag
	asm("bne 1f ");
	asm("mov r0, #0 ");				// no mutex - return FALSE
	__JUMP(,lr);

	// iHeldFastMutex points to a mutex
	asm("1: ");
	asm("tst r1, #1 ");				// test flag
	asm("beq 2f ");					// branch if not being released

	// mutex being released
	asm("3: ");
	LDREX(3,2);						// r3 = m->iHoldingThread
	asm("sub r3, r3, r0 ");			// m->iHoldingThread - this
	asm("cmp r3, #1 ");
	asm("bhi 4f ");					// if m->iHoldingThread != this or this+1, skip
	asm("orr r3, r0, #1 ");			// if m->iHoldingThread = this or this+1, set m->iHoldingThread = this+1
	STREX(12,3,2);
	asm("teq r12, #0 ");
	asm("bne 3b ");
	asm("mov r0, #1 ");				// return TRUE
	__JUMP(,lr);

	asm("4: ");
	asm("mov r3, #0 ");				// already released, so set iHeldFastMutex=0
	asm("str r3, [r0, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	asm("0: ");
	asm("mov r0, #0 ");				// no mutex - return FALSE
	__JUMP(,lr);

	// mutex being acquired or has been acquired
	// if it has been acquired set the contention flag and return TRUE, else return FALSE
	asm("2: ");
	LDREX(3,2);						// r3 = m->iHoldingThread
	asm("sub r3, r3, r0 ");			// m->iHoldingThread - this
	asm("cmp r3, #1 ");
	asm("bhi 0b ");					// if m->iHoldingThread != this or this+1, finish and return FALSE
	asm("orr r3, r0, #1 ");			// if m->iHoldingThread = this or this+1, set m->iHoldingThread = this+1
	STREX(12,3,2);
	asm("teq r12, #0 ");
	asm("bne 2b ");
	asm("mov r0, #1 ");				// return TRUE
	__JUMP(,lr);

	asm("4: ");
	asm("mov r3, #0 ");				// already released, so set iHeldFastMutex=0
	asm("str r3, [r0, #%a0]" : : "i" _FOFF(NThreadBase,iHeldFastMutex));
	asm("mov r0, #0 ");				// no mutex - return FALSE
	__JUMP(,lr);
	}


/******************************************************************************
 * IDFC/DFC
 ******************************************************************************/

/**	Transition the state of an IDFC or DFC when Add() is called

	0000->008n, 00Cn->00En, all other states unchanged
	Return original state.

	Enter and return with interrupts disabled.
*/
__NAKED__ TUint32 TDfc::AddStateChange()
	{
	GET_RWNO_TID(, r1);				// r1->SubScheduler
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	asm("ldr r1, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iCpuNum));	// r1 = current CPU number
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("1: ");
	LDREXH(0,3);
	asm("cmp r0, #0 ");				// original state 0000 ?
	asm("orreq r2, r1, #0x0080 ");	// yes -> 008n
	asm("movne r2, r0 ");			// no -> R2=original state ...
	asm("eorne r12, r0, #0x00C0 ");	// ... and R12=original state^00C0 ...
	asm("cmpne r12, #0x0020 ");		// ... and check if result < 0020 (i.e. original==00C0..00DF)
	asm("addlo r2, r2, #0x0020 ");	// 00Cn->00En otherwise leave R2 alone
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
	__JUMP(,lr);
	}

/**	Transition the state of an IDFC just before running it.

	002g->00Cn, 008n->00Cn, 00An->00Cn, XXYY->XX00, XX00->0000
	other initial states invalid
	Return original state

	Enter and return with interrupts disabled.
*/
__NAKED__ TUint32 TDfc::RunIDFCStateChange()
	{
	GET_RWNO_TID(, r1);				// r1->SubScheduler
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	asm("ldr r1, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iCpuNum));	// r1 = current CPU number
	__DATA_MEMORY_BARRIER_Z__(r12);
#ifdef _DEBUG
	asm("str r4, [sp, #-4]! ");
	asm("ldr r4, __IdleGeneration ");
	asm("ldrb r4, [r4] ");			// r4 = TheScheduler.iIdleGeneration
	asm("eor r4, r4, #0x0021 ");	// r4 = expected state of idle IDFCs
#endif
	asm("1: ");
	LDREXH(0,3);
	asm("eor r2, r0, #0x0080 ");
	asm("cmp r2, #0x0040 ");
	asm("bhs 2f ");					// branch out unless 008n or 00An
#ifdef _DEBUG
	asm("and r2, r0, #0x001F ");
	asm("cmp r2, r1 ");
	asm("bne 0f ");					// if n!=current CPU number, die
#endif
	asm("orr r2, r1, #0x00C0 ");	// 008n->00Cn, 00An->00Cn
	asm("3: ");
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
#ifdef _DEBUG
	asm("ldr r4, [sp], #4 ");
#endif
	__JUMP(,lr);

	asm("2: ");
	asm("bic r2, r0, #1 ");
	asm("cmp r2, #0x0020 ");
	asm("orreq r2, r1, #0x00C0 ");	// 002g->00Cn
#ifdef _DEBUG
	asm("bne 4f ");
	asm("cmp r0, r4 ");
	asm("bne 0f ");					// wrong idle state
	asm("4: ");
#endif
	asm("beq 3b ");
	asm("cmp r0, #0x0100 ");		// C=1 if XXYY or XX00, C=0 if bad state
	asm("bic r2, r0, #0x00FF ");	// XXYY->XX00, C unchanged
	asm("tst r0, #0x00FF ");		// C unchanged
	asm("moveq r2, #0 ");			// XX00->0000, C unchanged
	asm("bcs 3b ");					// branch to STREX if valid state

	asm("0: ");
	__ASM_CRASH();					// bad state

	asm("__IdleGeneration: ");
	asm(".word %a0 " : : "i" ((TInt)&TheScheduler.iIdleGeneration));
	}

/**	Transition the state of an IDFC just after running it.

	First swap aS->iCurrentIDFC with 0
	If original value != this, return 0xFFFFFFFF and don't touch *this
	Else 00Cn->0000, 00En->008n, 006n->006n, XXCn->XX00, XXEn->XX00, XX6n->XX00, XX00->0000
	other initial states invalid
	Return original state

	Enter and return with interrupts disabled.
*/
__NAKED__ TUint32 TDfc::EndIDFCStateChange(TSubScheduler* /*aS*/)
	{
	asm("add r1, r1, #%a0" : : "i" _FOFF(TSubScheduler,iCurrentIDFC));
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("1: ");
	LDREX(2,1);
	asm("subs r2, r2, r0 ");		// aS->iCurrentIDFC == this?
	asm("bne 9f ");					// no - bail out immediately
	STREX(12,2,1);					// yes - set aS->iCurrentIDFC=0
	asm("cmp r12, #0 ");
	asm("bne 1b ");

	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	__DATA_MEMORY_BARRIER__(r12);
#ifdef _DEBUG
	asm("str r4, [sp, #-4]! ");
	GET_RWNO_TID(, r4);				// r4->SubScheduler
	asm("ldr r4, [r4, #%a0]" : : "i" _FOFF(TSubScheduler,iCpuNum));	// r4 = current CPU number
#endif
	asm("2: ");
	LDREXH(0,3);					// r0 = original DFC state
	asm("mov r2, #0 ");				// r2 = 0 to begin with
#ifdef _DEBUG
	asm("tst r0, #0x00FF ");
	asm("beq 5f ");
	asm("eor r12, r0, r4 ");		// original state ^ CPU number, should be xxC0, xxE0 or xx60
	asm("and r12, r12, #0x00E0 ");
	asm("cmp r12, #0x00E0 ");
	asm("cmpne r12, #0x00C0 ");
	asm("cmpne r12, #0x0060 ");
	asm("beq 5f ");
	__ASM_CRASH();					// bad state
	asm("5: ");
#endif
	asm("bic r12, r0, #0x001F ");
	asm("cmp r12, #0x00E0 ");
	asm("bhi 4f ");					// branch out if XXYY or XX00
	asm("subeq r2, r0, #0x0060 ");	// 00En->008n
	asm("cmp r12, #0x0060 ");
	asm("moveq r2, r0 ");			// 006n->006n, else R2=0
	asm("3: ");
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 2b ");
	__DATA_MEMORY_BARRIER__(r12);
#ifdef _DEBUG
	asm("ldr r4, [sp], #4 ");
#endif
	__JUMP(,lr);

	asm("4: ");
	asm("tst r0, #0x00FF ");
	asm("bicne r2, r0, #0x00FF ");	// XXYY->XX00, XX00->0000
	asm("b 3b ");

	asm("9: ");
	asm("mvn r0, #0 ");				// return 0xFFFFFFFF
	__JUMP(,lr);
	}

/**	Transition the state of an IDFC just after running it.

	006n->002g where g = TheScheduler.iIdleGeneration
	XX6n->XX00
	other initial states invalid
	Return original state

	Enter and return with interrupts disabled.
*/
__NAKED__ TUint32 TDfc::EndIDFCStateChange2()
	{
	asm("ldr r12, __IdleGeneration ");
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
#ifdef _DEBUG
	asm("str r4, [sp, #-4]! ");
	GET_RWNO_TID(, r4);				// r4->SubScheduler
	asm("ldr r4, [r4, #%a0]" : : "i" _FOFF(TSubScheduler,iCpuNum));	// r4 = current CPU number
#endif
	asm("ldrb r1, [r12] ");			// r1 = TheScheduler.iIdleGeneration
	asm("1: ");
	LDREXH(0,3);
#ifdef _DEBUG
	asm("eor r12, r0, r4 ");
	asm("and r12, r12, #0x00FF ");
	asm("cmp r12, #0x0060 ");		// should be 006n or XX6n
	asm("beq 2f ");
	__ASM_CRASH();					// if not, die
	asm("2: ");
#endif
	asm("tst r0, #0xFF00 ");		// XX6n or 006n ?
	asm("orreq r2, r1, #0x0020 ");	// 006n->002g
	asm("bicne r2, r0, #0x00FF ");	// XX6n->XX00
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
#ifdef _DEBUG
	asm("ldr r4, [sp], #4 ");
#endif
	__JUMP(,lr);
	}

/**	Transition the state of a DFC just before moving it from the IDFC queue to
	its final queue.

	002g->0001, 008n->0001, XX2g->XX00, XX8n->XX00, XX00->0000
	other initial states invalid
	Return original state
*/
__NAKED__ TUint32 TDfc::MoveToFinalQStateChange()
	{
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	__DATA_MEMORY_BARRIER_Z__(r12);
#ifdef _DEBUG
	asm("str r4, [sp, #-4]! ");
	asm("ldr r4, __IdleGeneration ");
	GET_RWNO_TID(, r1);				// r1->SubScheduler
	asm("ldrb r4, [r4] ");			// r4 = TheScheduler.iIdleGeneration
	asm("ldr r1, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iCpuNum));	// r1 = current CPU number
	asm("eor r4, r4, #0x0021 ");	// r4 = expected state of idle IDFCs
	asm("orr r1, r1, #0x0080 ");
#endif
	asm("1: ");
	LDREXH(0,3);
#ifdef _DEBUG
	asm("cmp r0, #0 ");
	asm("beq 0f ");					// 0000 -> die
	asm("ands r2, r0, #0x00FF ");
	asm("beq 3f ");					// XX00 -> OK
	asm("cmp r2, r4 ");				// 002g ?
	asm("beq 3f ");					// yes -> OK
	asm("cmp r2, r1 ");				// 008n ?
	asm("beq 3f ");					// yes -> OK
	asm("0: ");
	__ASM_CRASH();					// otherwise die
	asm("3: ");
#endif
	asm("bics r2, r0, #0x00FF ");	// XXYY->XX00
	asm("moveq r2, #0x0001 ");		// 002g,008n->0001
	asm("beq 2f ");
	asm("tst r0, #0x00FF ");
	asm("moveq r2, #0 ");			// XX00->0000
	asm("2: ");
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
#ifdef _DEBUG
	asm("ldr r4, [sp], #4 ");
#endif
	__JUMP(,lr);
	}

/**	Transition the state of an IDFC when transferring it to another CPU

	002g->00Am, 008n->00Am, XXYY->XX00, XX00->0000
	other initial states invalid
	Return original state

	Enter and return with interrupts disabled and target CPU's ExIDfcLock held.
*/
__NAKED__ TUint32 TDfc::TransferIDFCStateChange(TInt /*aCpu*/)
	{
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	__DATA_MEMORY_BARRIER_Z__(r12);
#ifdef _DEBUG
	asm("stmfd sp!, {r4-r5} ");
	asm("ldr r4, __IdleGeneration ");
	GET_RWNO_TID(, r5);				// r5->SubScheduler
	asm("ldrb r4, [r4] ");			// r4 = TheScheduler.iIdleGeneration
	asm("ldr r5, [r5, #%a0]" : : "i" _FOFF(TSubScheduler,iCpuNum));	// r5 = current CPU number
	asm("eor r4, r4, #0x0021 ");	// r4 = expected state of idle IDFCs
	asm("orr r5, r5, #0x0080 ");
#endif
	asm("1: ");
	LDREXH(0,3);
#ifdef _DEBUG
	asm("cmp r0, #0 ");
	asm("beq 0f ");					// 0000 -> die
	asm("ands r2, r0, #0x00FF ");
	asm("beq 3f ");					// XX00 -> OK
	asm("cmp r2, r4 ");				// 002g ?
	asm("beq 3f ");					// yes -> OK
	asm("cmp r2, r5 ");				// 008n ?
	asm("beq 3f ");					// yes -> OK
	asm("0: ");
	__ASM_CRASH();					// otherwise die
	asm("3: ");
#endif
	asm("bics r2, r0, #0x00FF ");	// XXYY->XX00
	asm("orreq r2, r1, #0x00A0 ");	// 002g,008n->00Am
	asm("beq 2f ");
	asm("tst r0, #0x00FF ");
	asm("moveq r2, #0 ");			// XX00->0000
	asm("2: ");
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
#ifdef _DEBUG
	asm("ldmfd sp!, {r4-r5} ");
#endif
	__JUMP(,lr);
	}

/**	Transition the state of an IDFC/DFC just before cancelling it.

	0000->0000, XX00->ZZ00, xxYY->zzYY
	Return original state

	Enter and return with interrupts disabled.
*/
__NAKED__ TUint32 TDfc::CancelInitialStateChange()
	{
	GET_RWNO_TID(,r1);
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("ldr r1, [r1, #%a0]" : : "i" _FOFF(TSubScheduler,iCpuMask));	// r1 = mask of current CPU number

	asm("1: ");
	LDREXH(0,3);
	asm("cmp r0, #0 ");
	asm("beq 2f ");				// if original state 0000 leave alone
	asm("orr r2, r0, r1, lsl #8 ");	// else set bit 8-15 corresponding to CPU number
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	asm("2: ");
	__DATA_MEMORY_BARRIER__(r12);
	__JUMP(,lr);
	}

/**	Transition the state of an IDFC/DFC at the end of a cancel operation

	XXYY->XX00, XX00->0000
	Return original state

	Enter and return with interrupts disabled.
*/
__NAKED__ TUint32 TDfc::CancelFinalStateChange()
	{
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	__DATA_MEMORY_BARRIER_Z__(r12);

	asm("1: ");
	LDREXH(0,3);
	asm("tst r0, #0x00FF ");
	asm("bicne r2, r0, #0x00FF ");	// XXYY->XX00
	asm("moveq r2, #0 ");			// xx00->0000
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
	__JUMP(,lr);
	}

/**	Transition the state of an IDFC or DFC when QueueOnIdle() is called

	0000->002g where g = TheScheduler.iIdleGeneration,
	00Cn->006n, all other states unchanged
	Return original state.

	Enter and return with interrupts disabled and IdleSpinLock held.
*/
__NAKED__ TUint32 TDfc::QueueOnIdleStateChange()
	{
	asm("ldr r12, __IdleGeneration ");
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	asm("ldrb r1, [r12] ");			// r1 = TheScheduler.iIdleGeneration
	__DATA_MEMORY_BARRIER_Z__(r12);
	asm("1: ");
	LDREXH(0,3);
	asm("cmp r0, #0 ");				// original state 0000 ?
	asm("orreq r2, r1, #0x0020 ");	// yes -> 002g
	asm("movne r2, r0 ");			// no -> R2=original state ...
	asm("eorne r12, r0, #0x00C0 ");	// ... and R12=original state^00C0 ...
	asm("cmpne r12, #0x0020 ");		// ... and check if result < 0020 (i.e. original==00C0..00DF)
	asm("sublo r2, r2, #0x0060 ");	// 00Cn->006n otherwise leave R2 alone
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
	__DATA_MEMORY_BARRIER__(r12);
	__JUMP(,lr);
	}


__NAKED__ void TDfc::ResetState()
	{
	asm("add r3, r0, #%a0" : : "i" _FOFF(TDfc,iDfcState));
	__DATA_MEMORY_BARRIER_Z__(r2);
#ifdef _DEBUG
	asm("1: ");
	LDREXH(0,3);
	asm("cmp r0, #0 ");
	asm("beq 0f ");				// if state already zero, die
	STREXH(12,2,3);
	asm("cmp r12, #0 ");
	asm("bne 1b ");
#else
	asm("strh r2, [r3] ");		// __e32_atomic_store_rel16(&iDfcState, 0)
#endif
	__JUMP(,lr);
#ifdef _DEBUG
	asm("0: ");
	__ASM_CRASH();
#endif
	}



