kernel/eka/kernel/arm/cexec.cia
author William Roberts <williamr@symbian.org>
Mon, 28 Jun 2010 11:25:30 +0100
branchGCC_SURGE
changeset 184 0e2270015475
parent 31 56f325a607ea
permissions -rw-r--r--
Identify (and sometimes fix) remaining uses of old-style GCC name mangling in cia files - Bug 3115 Instances which don't have defined "CSM_xxx" macros are marked "// CSM needed"

// Copyright (c) 1996-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\kernel\arm\cexec.cia
// 
//

#include <e32cia.h>
#include <arm.h>
#include <kernel/cache.h>

#include "nk_priv.h"

GLREF_C TInt CalcKernelHeapUsed();
GLREF_C void InvalidFastExec();

void GetLatencyValues(TInt aMode, TInt& aCount, TInt* aDest);
void KernMsgTest();
void InvalidExecHandler();
void PreprocessHandler();

#define __GEN_KERNEL_EXEC_CODE__

#include "execs.h"

/***********************************************************************************
 * User-side executive handlers
 ***********************************************************************************/

#ifdef __ATOMIC64_USE_FAST_EXEC__
__NAKED__ void ExecHandler::FastAtomicAxo64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #8 ");		// r12 = a
	asm("stmfd sp!, {r4-r7} ");
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = u
	asm("ldrt r5, [r0], #4 ");
	asm("ldrt r2, [r12], #4 ");		// r3:r2 = *a = oldv
	asm("ldrt r3, [r12], #-4 ");
	asm("ldrt r6, [r0], #4 ");		// r7:r6 = v
	asm("ldrt r7, [r0], #-20 ");
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #4 ");
	asm("and r2, r2, r4 ");
	asm("and r3, r3, r5 ");			// r3:r2 = oldv & u
	asm("eor r2, r2, r6 ");
	asm("eor r3, r3, r7 ");			// r3:r2 = (oldv & u) ^ v
	asm("strt r2, [r12], #4 ");		// write back to *a
	asm("strt r3, [r12], #-4 ");
	asm("ldmfd sp!, {r4-r7} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ TBool ExecHandler::FastAtomicCas64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r1, [r0], #4 ");		// r1 = q
	asm("stmfd sp!, {r4-r5} ");
	asm("ldrt r2, [r12], #4 ");		// r3:r2 = *a
	asm("ldrt r3, [r12], #-4 ");
	asm("ldrt r4, [r1], #4 ");		// r5:r4 = *q
	asm("ldrt r5, [r1], #-4 ");
	asm("cmp r2, r4 ");
	asm("cmpeq r3, r5 ");
	asm("ldreqt r2, [r0], #4 ");	// if equal r3:r2 = v
	asm("ldreqt r3, [r0], #4 ");
	asm("strnet r2, [r1], #4 ");	// if not equal *q = *a
	asm("strnet r3, [r1], #-4 ");
	asm("streqt r2, [r12], #4 ");	// if equal *a = v
	asm("streqt r3, [r12], #-4 ");
	asm("ldmfd sp!, {r4-r5} ");
	asm("movne r0, #0 ");
	asm("moveq r0, #1 ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::FastAtomicAdd64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #8 ");		// r12 = a
	asm("stmfd sp!, {r4-r5} ");
	asm("ldrt r2, [r12], #4 ");		// r3:r2 = *a = oldv
	asm("ldrt r3, [r12], #-4 ");
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = v
	asm("ldrt r5, [r0], #-12 ");
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #-4 ");
	asm("adds r2, r2, r4 ");		// r3:r2 = oldv + v
	asm("adcs r3, r3, r5 ");
	asm("strt r2, [r12], #4 ");		// write back to *a
	asm("strt r3, [r12], #-4 ");
	asm("ldmfd sp!, {r4-r5} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::FastAtomicTau64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0] ");			// r12 = a
	asm("stmfd sp!, {r4-r5} ");
	asm("ldrt r2, [r12], #4 ");		// r3:r2 = *a = oldv
	asm("ldrt r3, [r12], #-4 ");
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #4 ");
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = t
	asm("ldrt r5, [r0], #4 ");
	asm("cmp r2, r4 ");				// oldv - t
	asm("sbcs r1, r3, r5 ");
	asm("addcc r0, r0, #8 ");		// if oldv<t r0->v else r0->u
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = u or v
	asm("ldrt r5, [r0], #4 ");
	asm("adds r2, r2, r4 ");		// r3:r2 = oldv + u or v
	asm("adcs r3, r3, r5 ");
	asm("strt r2, [r12], #4 ");		// write back to *a
	asm("strt r3, [r12], #-4 ");
	asm("ldmfd sp!, {r4-r5} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::FastAtomicTas64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0] ");			// r12 = a
	asm("stmfd sp!, {r4-r5} ");
	asm("ldrt r2, [r12], #4 ");		// r3:r2 = *a = oldv
	asm("ldrt r3, [r12], #-4 ");
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #4 ");
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = t
	asm("ldrt r5, [r0], #4 ");
	asm("cmp r2, r4 ");				// oldv - t
	asm("sbcs r1, r3, r5 ");
	asm("addlt r0, r0, #8 ");		// if oldv<t r0->v else r0->u
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = u or v
	asm("ldrt r5, [r0], #4 ");
	asm("adds r2, r2, r4 ");		// r3:r2 = oldv + u or v
	asm("adcs r3, r3, r5 ");
	asm("strt r2, [r12], #4 ");		// write back to *a
	asm("strt r3, [r12], #-4 ");
	asm("ldmfd sp!, {r4-r5} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}
#endif

#ifdef __ATOMIC64_USE_SLOW_EXEC__
#ifndef __CPU_ARM_HAS_CPS
#error Only need slow exec atomics on ARMv6
#endif


// Attempt to load and store the lsb of data.  If interrupts are re-enabled 
// during the ldr or str then a page fault occurred so retry.
// The exception handler prevents the strt instruction writing old data to 
// memory by skipping that instruction if a page fault occurs.
// Note only need to ldr and str one word as the values will be 8 byte 
// aligned and therefore won't span page boundaries.
#define ENSURE_PAGED_IN_64(rAddr, rLsbs, rMsbs, rCpsr, tag) 					\
	asm("retry_atomic64_"#tag": "); 											\
	CPSIDAIF; 																	\
	asm(".global magic_atomic64_ldrt_"#tag" "); 								\
	asm("magic_atomic64_ldrt_"#tag": "); 										\
	asm("ldrt r"#rLsbs", [r"#rAddr"]"); 										\
	asm("mrs r"#rCpsr", cpsr "); 												\
	asm("and r"#rCpsr", r"#rCpsr", #%a0 " : : "i" ((TInt)KAllInterruptsMask)); 	\
	asm("cmp r"#rCpsr", #%a0 " : : "i" ((TInt)KAllInterruptsMask)); 			\
	asm("bne retry_atomic64_"#tag" "); 											\
	asm(".global magic_atomic64_strt_"#tag" "); 								\
	asm("magic_atomic64_strt_"#tag": "); 										\
	asm("strt r"#rLsbs", [r"#rAddr"], #4 "); 									\
	asm("mrs r"#rCpsr", cpsr "); 												\
	asm("and r"#rCpsr", r"#rCpsr", #%a0 " : : "i" ((TInt)KAllInterruptsMask)); 	\
	asm("cmp r"#rCpsr", #%a0 " : : "i" ((TInt)KAllInterruptsMask)); 			\
	asm("bne retry_atomic64_"#tag" "); 											\
	asm("ldrt r"#rMsbs", [r"#rAddr"], #-4")


__NAKED__ void ExecHandler::SlowAtomicAxo64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("stmfd sp!, {r4-r8} ");
	asm("ldrt r12, [r0], #8 ");		// r12 = a
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = u
	asm("ldrt r5, [r0], #4 ");
	asm("ldrt r6, [r0], #4 ");		// r7:r6 = v
	asm("ldrt r7, [r0], #-20 ");

	// Disable interrupts and ensure the 64-bit data is paged in with write permissions.
	ENSURE_PAGED_IN_64(12,2,3,8,axo);
	// Data paged in so perform the operation.
	asm("and r4, r2, r4 ");
	asm("and r5, r3, r5 ");			// r5:r4 = oldv & u
	asm("eor r4, r4, r6 ");
	asm("eor r5, r5, r7 ");			// r5:r4 = (oldv & u) ^ v
	asm("strt r4, [r12], #4 ");		// write back to *a
	asm("strt r5, [r12], #-4 ");
	CPSIEIF;
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #-4 ");
	asm("ldmfd sp!, {r4-r8} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ TBool ExecHandler::SlowAtomicCas64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("stmfd sp!, {r4-r8} ");
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r1, [r0], #4 ");		// r1 = q
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = v
	asm("ldrt r5, [r0], #-12 ");
	asm("ldrt r6, [r1], #4 ");		// r7:r6 = *q
	asm("ldrt r7, [r1], #-4 ");

	// Disable interrupts and ensure the 64-bit data is paged in with write permissions.
	ENSURE_PAGED_IN_64(12,2,3,8,cas);
	// Data paged in so perform the operation.
	asm("cmp r2, r6 ");
	asm("cmpeq r3, r7 ");
	asm("streqt r4, [r12], #4 ");	// if oldv==*q, *a=v
	asm("streqt r5, [r12], #-4 ");
	CPSIEIF;
	asm("strnet r2, [r1], #4 ");	// if oldv!=*q, *q=oldv
	asm("strnet r3, [r1], #-4 ");
	asm("ldmfd sp!, {r4-r8} ");
	asm("movne r0, #0 ");			// return 0 if oldv!=*q
	asm("moveq r0, #1 ");			// return 1 if oldv==*q
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::SlowAtomicAdd64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("stmfd sp!, {r4-r6} ");
	asm("ldrt r12, [r0], #8 ");		// r12 = a
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = v
	asm("ldrt r5, [r0], #-12 ");

	// Disable interrupts and ensure the 64-bit data is paged in with write permissions.
	ENSURE_PAGED_IN_64(12,2,3,6,add);
	// Data paged in so perform the operation.
	asm("adds r4, r2, r4 ");
	asm("adcs r5, r3, r5 ");		// r5:r4 = oldv + v
	asm("strt r4, [r12], #4 ");		// write back to *a
	asm("strt r5, [r12], #-4 ");
	CPSIEIF;
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #-4 ");
	asm("ldmfd sp!, {r4-r6} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::SlowAtomicTau64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("stmfd sp!, {r4-r10} ");
	asm("ldrt r12, [r0], #8 ");		// r12 = a
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = t
	asm("ldrt r5, [r0], #4 ");
	asm("ldrt r6, [r0], #4 ");		// r7:r6 = u
	asm("ldrt r7, [r0], #4 ");
	asm("ldrt r8, [r0], #4 ");		// r9:r8 = v
	asm("ldrt r9, [r0], #-28 ");

	// Disable interrupts and ensure the 64-bit data is paged in with write permissions.
	ENSURE_PAGED_IN_64(12,2,3,10,tau);
	// Data paged in so perform the operation.
	asm("cmp r2, r4 ");
	asm("sbcs r1, r3, r5 ");		// oldv - t
	asm("movcc r6, r8 ");			// if oldv<t r7:r6=v
	asm("movcc r7, r9 ");
	asm("adds r4, r2, r6 ");		// r5:r4 = oldv + u or v
	asm("adcs r5, r3, r7 ");
	asm("strt r4, [r12], #4 ");		// write back to *a
	asm("strt r5, [r12], #-4 ");
	CPSIEIF;
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #-4 ");
	asm("ldmfd sp!, {r4-r10} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::SlowAtomicTas64(SAtomicOpInfo64*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("stmfd sp!, {r4-r10} ");
	asm("ldrt r12, [r0], #8 ");		// r12 = a
	asm("ldrt r4, [r0], #4 ");		// r5:r4 = t
	asm("ldrt r5, [r0], #4 ");
	asm("ldrt r6, [r0], #4 ");		// r7:r6 = u
	asm("ldrt r7, [r0], #4 ");
	asm("ldrt r8, [r0], #4 ");		// r9:r8 = v
	asm("ldrt r9, [r0], #-28 ");

	// Disable interrupts and ensure the 64-bit data is paged in with write permissions.
	ENSURE_PAGED_IN_64(12,2,3,10,tas);
	// Data paged in so perform the operation.
	asm("cmp r2, r4 ");
	asm("sbcs r1, r3, r5 ");		// oldv - t
	asm("movlt r6, r8 ");			// if oldv<t r7:r6=v
	asm("movlt r7, r9 ");
	asm("adds r4, r2, r6 ");		// r5:r4 = oldv + u or v
	asm("adcs r5, r3, r7 ");
	asm("strt r4, [r12], #4 ");		// write back to *a
	asm("strt r5, [r12], #-4 ");
	CPSIEIF;
	asm("strt r2, [r0], #4 ");		// return oldv
	asm("strt r3, [r0], #-4 ");
	asm("ldmfd sp!, {r4-r10} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);
	}
#endif

#ifdef __ATOMIC_USE_FAST_EXEC__
__NAKED__ TUint32 ExecHandler::FastAtomicAxo32(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r2, [r0], #4 ");		// r2 = u
	asm("ldrt r3, [r0], #4 ");		// r3 = v
	asm("ldrt r0, [r12] ");			// r0 = *a = oldv
	asm("and r1, r0, r2 ");
	asm("eor r1, r1, r3 ");
	asm("strt r1, [r12] ");			// *a = (oldv & u) ^ v
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TBool ExecHandler::FastAtomicCas32(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r1, [r0], #4 ");		// r1 = q
	asm("ldrt r2, [r12] ");			// r2 = *a = oldv
	asm("ldrt r3, [r1] ");			// r3 = *q
	asm("cmp r2, r3 ");
	asm("ldreqt r2, [r0], #4 ");	// if (oldv==*q) *a=v
	asm("strnet r2, [r1] ");		// if (oldv!=*q) *q=oldv
	asm("streqt r2, [r12] ");
	asm("movne r0, #0 ");
	asm("moveq r0, #1 ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv==*q
	}

__NAKED__ TUint32 ExecHandler::FastAtomicAdd32(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r2, [r0], #4 ");		// r2 = v
	asm("ldrt r0, [r12] ");			// r0 = *a = oldv
	asm("add r1, r0, r2 ");
	asm("strt r1, [r12] ");			// *a = oldv + v
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TUint32 ExecHandler::FastAtomicTau32(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("mov r3, r0 ");
	asm("ldrt r12, [r3], #4 ");		// r12 = a
	asm("ldrt r2, [r3], #4 ");		// r2 = t
	asm("ldrt r0, [r12] ");			// r0 = *a = oldv
	asm("cmp r0, r2 ");				// oldv - t
	asm("addcc r3, r3, #4 ");		// if oldv<t r3->v else r3->u
	asm("ldrt r1, [r3] ");			// r1 = u or v
	asm("add r1, r0, r1 ");
	asm("strt r1, [r12] ");			// *a = oldv + u or v
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TInt32 ExecHandler::FastAtomicTas32(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("mov r3, r0 ");
	asm("ldrt r12, [r3], #4 ");		// r12 = a
	asm("ldrt r2, [r3], #4 ");		// r2 = t
	asm("ldrt r0, [r12] ");			// r0 = *a = oldv
	asm("cmp r0, r2 ");				// oldv - t
	asm("addlt r3, r3, #4 ");		// if oldv<t r3->v else r3->u
	asm("ldrt r1, [r3] ");			// r1 = u or v
	asm("add r1, r0, r1 ");
	asm("strt r1, [r12] ");			// *a = oldv + u or v
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TUint16 ExecHandler::FastAtomicAxo16(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r2, [r0], #4 ");		// r2 = u
	asm("ldrt r3, [r0], #4 ");		// r3 = v
	asm("ldrbt r0, [r12], #1 ");	// r0 = *a = oldv
	asm("ldrbt r1, [r12], #-1 ");
	asm("orr r0, r0, r1, lsl #8 ");
	asm("and r1, r0, r2 ");
	asm("eor r1, r1, r3 ");
	asm("strbt r1, [r12], #1 ");	// *a = (oldv & u) ^ v
	asm("mov r1, r1, lsr #8 ");
	asm("strbt r1, [r12] ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TBool ExecHandler::FastAtomicCas16(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r1, [r0], #4 ");		// r1 = q
	asm("stmfd sp!, {r4-r5} ");
	asm("ldrbt r2, [r12], #1 ");	// r3:r2 = *a = oldv
	asm("ldrbt r3, [r12], #-1 ");	//
	asm("ldrbt r4, [r1], #1 ");		// r5:r4 = *q
	asm("ldrbt r5, [r1], #-1 ");
	asm("cmp r2, r4 ");
	asm("cmpeq r3, r5 ");
	asm("ldreqbt r2, [r0], #1 ");	// if (oldv==*q) *a=v
	asm("ldreqbt r3, [r0], #-1 ");	// if (oldv==*q) *a=v
	asm("strnebt r2, [r1], #1 ");	// if (oldv!=*q) *q=oldv
	asm("strnebt r3, [r1], #-1 ");
	asm("streqbt r2, [r12], #1 ");
	asm("streqbt r3, [r12], #-1 ");
	asm("movne r0, #0 ");
	asm("moveq r0, #1 ");
	asm("ldmfd sp!, {r4-r5} ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv==*q
	}

__NAKED__ TUint16 ExecHandler::FastAtomicAdd16(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r2, [r0], #4 ");		// r2 = v
	asm("ldrbt r0, [r12], #1 ");	// r0 = *a = oldv
	asm("ldrbt r1, [r12], #-1 ");
	asm("orr r0, r0, r1, lsl #8 ");
	asm("add r1, r0, r2 ");
	asm("strbt r1, [r12], #1 ");	// *a = oldv + v
	asm("mov r1, r1, lsr #8 ");
	asm("strbt r1, [r12], #-1 ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TUint16 ExecHandler::FastAtomicTau16(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("mov r3, r0 ");
	asm("ldrt r12, [r3], #4 ");		// r12 = a
	asm("ldrt r2, [r3], #4 ");		// r2 = t
	asm("ldrbt r0, [r12], #1 ");	// r0 = *a = oldv
	asm("ldrbt r1, [r12], #-1 ");
	asm("orr r0, r0, r1, lsl #8 ");
	asm("cmp r0, r2 ");				// oldv - t
	asm("addcc r3, r3, #4 ");		// if oldv<t r3->v else r3->u
	asm("ldrt r1, [r3] ");			// r1 = u or v
	asm("add r1, r0, r1 ");
	asm("strbt r1, [r12], #1 ");	// *a = oldv + u or v
	asm("mov r1, r1, lsr #8 ");
	asm("strbt r1, [r12], #-1 ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TInt16 ExecHandler::FastAtomicTas16(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("mov r3, r0 ");
	asm("ldrt r12, [r3], #4 ");		// r12 = a
	asm("ldrt r2, [r3], #4 ");		// r2 = t
	asm("ldrbt r0, [r12], #1 ");	// r0 = *a = oldv
	asm("ldrbt r1, [r12], #-1 ");
	asm("orr r0, r0, r1, lsl #8 ");
	asm("mov r0, r0, lsl #16 ");
	asm("cmp r0, r2, lsl #16 ");	// oldv - t
	asm("addlt r3, r3, #4 ");		// if oldv<t r3->v else r3->u
	asm("ldrt r1, [r3] ");			// r1 = u or v
	asm("add r1, r1, r0, asr #16 ");
	asm("strbt r1, [r12], #1 ");	// *a = oldv + u or v
	asm("mov r1, r1, lsr #8 ");
	asm("strbt r1, [r12], #-1 ");
	asm("mov r0, r0, asr #16 ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TUint8 ExecHandler::FastAtomicAxo8(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r2, [r0], #4 ");		// r2 = u
	asm("ldrt r3, [r0], #4 ");		// r3 = v
	asm("ldrbt r0, [r12] ");		// r0 = *a = oldv
	asm("and r1, r0, r2 ");
	asm("eor r1, r1, r3 ");
	asm("strbt r1, [r12] ");		// *a = (oldv & u) ^ v
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TBool ExecHandler::FastAtomicCas8(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r1, [r0], #4 ");		// r1 = q
	asm("ldrbt r2, [r12] ");		// r2 = *a = oldv
	asm("ldrbt r3, [r1] ");			// r3 = *q
	asm("cmp r2, r3 ");
	asm("ldreqt r2, [r0], #4 ");	// if (oldv==*q) *a=v
	asm("strnebt r2, [r1] ");		// if (oldv!=*q) *q=oldv
	asm("streqbt r2, [r12] ");
	asm("movne r0, #0 ");
	asm("moveq r0, #1 ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv==*q
	}

__NAKED__ TUint8 ExecHandler::FastAtomicAdd8(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("ldrt r12, [r0], #4 ");		// r12 = a
	asm("ldrt r2, [r0], #4 ");		// r2 = v
	asm("ldrbt r0, [r12] ");		// r0 = *a = oldv
	asm("add r1, r0, r2 ");
	asm("strbt r1, [r12] ");		// *a = oldv + v
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TUint8 ExecHandler::FastAtomicTau8(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("mov r3, r0 ");
	asm("ldrt r12, [r3], #4 ");		// r12 = a
	asm("ldrt r2, [r3], #4 ");		// r2 = t
	asm("ldrbt r0, [r12] ");		// r0 = *a = oldv
	asm("cmp r0, r2 ");				// oldv - t
	asm("addcc r3, r3, #4 ");		// if oldv<t r3->v else r3->u
	asm("ldrt r1, [r3] ");			// r1 = u or v
	asm("add r1, r0, r1 ");
	asm("strbt r1, [r12] ");		// *a = oldv + u or v
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}

__NAKED__ TInt8 ExecHandler::FastAtomicTas8(SAtomicOpInfo32*)
	{
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("mov r3, r0 ");
	asm("ldrt r12, [r3], #4 ");		// r12 = a
	asm("ldrt r2, [r3], #4 ");		// r2 = t
	asm("ldrbt r0, [r12] ");		// r0 = *a = oldv
	asm("mov r0, r0, lsl #24 ");
	asm("cmp r0, r2, lsl #24 ");	// oldv - t
	asm("addlt r3, r3, #4 ");		// if oldv<t r3->v else r3->u
	asm("ldrt r1, [r3] ");			// r1 = u or v
	asm("add r1, r1, r0, asr #24 ");
	asm("strbt r1, [r12] ");		// *a = oldv + u or v
	asm("mov r0, r0, asr #24 ");
	USER_MEMORY_GUARD_ON(,r12,r12);
	__JUMP(,lr);					// return oldv
	}
#endif

#ifdef __FASTEXEC_MACHINE_CODED__
__NAKED__ RAllocator* ExecHandler::Heap()
	{
	asm("ldr r0, [r1, #%a0]" : : "i" (_FOFF(DThread,iAllocator)-_FOFF(DThread,iNThread)));
	__JUMP(,lr);
	}

__NAKED__ TTrapHandler* ExecHandler::PushTrapFrame(TTrap* /*aFrame*/ /* r1=TheCurrentThread */)
//
// Push a new trap frame.
// For user code only, kernel code should not use TRAP/Leave.
//
	{
#ifdef __LEAVE_EQUALS_THROW__
	asm("b  " CSM_Z15InvalidFastExecv);
#else
	asm("ldr r2, [r1, #%a0]" : : "i" (_FOFF(DThread,iFrame)-_FOFF(DThread,iNThread)));		// r2=TheCurrentThread->iFrame
	asm("ldr r3, [r1, #%a0]" : : "i" (_FOFF(DThread,iTrapHandler)-_FOFF(DThread,iNThread)));// r3=TheCurrentThread->iTrapHandler
	USER_MEMORY_GUARD_OFF(,r12,r12);
	asm("add r12, r0, #%a0" : : "i" _FOFF(TTrap,iNext));									// r12->aFrame.iNext (in user space)
	asm("strt r2, [r12] ");																	// aFrame.iNext=TheCurrentThread->iFrame
	asm("add r12, r0, #%a0" : : "i" _FOFF(TTrap,iHandler));									// r12->aFrame.iHandler (in user space)
	asm("strt r3, [r12] ");																	// aFrame.iHandler=TheCurrentThread->iTrapHandler
	USER_MEMORY_GUARD_ON(,r12,r12);
	asm("str r0, [r1, #%a0]" : : "i" (_FOFF(DThread,iFrame)-_FOFF(DThread,iNThread)));		// TheCurrentThread->iFrame=aFrame
	asm("mov r0, r3 ");																		// return TheCurrentThread->iTrapHandler
	__JUMP(,lr);
#endif
	}

__NAKED__ TTrap* ExecHandler::PopTrapFrame()
//
// Pop the current frame.
// For user code only, kernel code should not use TRAP/Leave.
//
	{
#ifdef __LEAVE_EQUALS_THROW__
	asm("b  " CSM_Z15InvalidFastExecv);
#else
	asm("ldr r0, [r1, #%a0]" : : "i" (_FOFF(DThread,iFrame)-_FOFF(DThread,iNThread)));		// r0=TheCurrentThread->iFrame
	asm("cmp r0, #0 ");																		// ignore rest of code if NULL
	USER_MEMORY_GUARD_OFF(ne,r12,r12);
	asm("addne r12, r0, #%a0" : : "i" _FOFF(TTrap,iNext));									// r12->iFrame.iNext (in user space)
	asm("ldrnet r2, [r12] ");																// r2=iFrame->iNext
	USER_MEMORY_GUARD_ON(ne,r12,r12);
	asm("strne r2, [r1, #%a0]" : : "i" (_FOFF(DThread,iFrame)-_FOFF(DThread,iNThread)));	// iFrame=iFrame->iNext
	__JUMP(,lr);																			// returning old iFrame
#endif
	}

__NAKED__ CActiveScheduler* ExecHandler::ActiveScheduler()
//
// Return the address of the current active scheduler
//
	{
	asm("ldr r0, [r1, #%a0]" : : "i" (_FOFF(DThread,iScheduler)-_FOFF(DThread,iNThread)));
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::SetActiveScheduler(CActiveScheduler* /*aScheduler*/)
//
// Set the address of the current active scheduler
//
	{
	asm("str r0, [r1, #%a0]" : : "i" (_FOFF(DThread,iScheduler)-_FOFF(DThread,iNThread)));
	__JUMP(,lr);
	}

__NAKED__ TTrapHandler* ExecHandler::TrapHandler()
//
// Return the current trap handler.
//
	{
	asm("ldr r0, [r1, #%a0]" : : "i" (_FOFF(DThread,iTrapHandler)-_FOFF(DThread,iNThread)));
	__JUMP(,lr);
	}

__NAKED__ TTrapHandler* ExecHandler::SetTrapHandler(TTrapHandler* /*aHandler*/)
//
// Set the current trap handler.
//
	{
	asm("ldr r2, [r1, #%a0]" : : "i" (_FOFF(DThread,iTrapHandler)-_FOFF(DThread,iNThread)));
	asm("str r0, [r1, #%a0]" : : "i" (_FOFF(DThread,iTrapHandler)-_FOFF(DThread,iNThread)));
	asm("mov r0, r2 ");
	__JUMP(,lr);
	}

__NAKED__ void ExecHandler::SetReentryPoint(TLinAddr)
	{
	asm("ldr r2, [r1, #%a0]" : : "i" (_FOFF(DThread,iOwningProcess)-_FOFF(DThread,iNThread)));
	asm("str r0, [r2, #%a0]" : : "i" _FOFF(DProcess,iReentryPoint));
	__JUMP(,lr);
	}
#endif

/***********************************************************************************
 * Exec dispatch code
 ***********************************************************************************/

__NAKED__ void InvalidFastExec()
	{
	asm("mov r0, #0x13 ");
	asm("msr cpsr, r0 ");
	asm("b  " CSM_Z18InvalidExecHandlerv);
	}


/***************************************************************************
 * Look up a handle in r0 in the current thread or process handles array
 * On entry r5=0xY000000X, where bits 0-5 indicate the type of object referenced.
 * Also r10 points to K::TheScheduler, r9 to the current NThread
 * Can use registers r0,r4,r7,r8,ip. Preserve r1,r2,r3,r5,r6,r9-r11
 * Return r0=address of object referenced, or NULL if handle invalid
 * Enter and leave with system locked
 ***************************************************************************/
__NAKED__ void PreprocessHandler()
	{
	asm("tst r5, #0x20 ");				// message lookup?
	asm("bne lookup_message ");
	asm("ands r7, r5, #0x1f ");			// r7 = object container number+1 = DObjectCon uniqueID
	asm("mvneq r7, #0 ");				// r7=-1 if any type ok
	asm("adds ip, r0, r0 ");			// check for special handle (bit 31 set => special)
	asm("bcs lookup_special ");			// if not, N flag indicates local handle
	asm("ldrpl ip, [r9, #%a0]" : : "i" (_FOFF(DThread,iOwningProcess)-_FOFF(DThread,iNThread)));	// if not local, ip=aThread->iOwningProcess
	asm("addmi ip, r9, #%a0" : : "i" (_FOFF(DThread,iHandles)-_FOFF(DThread,iNThread)));	// if local, ip=&aThread->iHandles
	asm("addpl ip, ip, #%a0" : : "i" _FOFF(DProcess,iHandles));		// if not, ip=&aThread->iOwningProcess->iHandles

#ifdef __HANDLES_USE_RW_SPIN_LOCK__
	asm("stmfd sp!, {r0,ip,lr} ");		// save r0, ip and lr
	asm("mov r0, ip ");						// move RObjectIx this(ip) to r0
	asm("BL _ZN9RObjectIx15AcquireReadLockEv") // call RObjectIx::AquireReadLock
	asm("ldmfd sp!, {r0,ip,lr} ");		// restore r0, ip and lr
#else
	// assumes system lock is held
#endif

	asm("mov r4, r0, lsl #17 ");		// r4=r0<<17 = index(aHandle)<<17
	asm("mov r0, r0, lsl #2 ");			// r0=instance(Handle)<<18
	asm("ldr r8, [ip, #%a0]" : : "i" _FOFF(RObjectIx,iCount));		// r8=iCount
	asm("ldr ip, [ip, #%a0]" : : "i" _FOFF(RObjectIx,iSlots));		// ip=iSlots
	asm("mov r0, r0, lsr #18 ");		// r0=instance(Handle)
	asm("cmp r8, r4, lsr #17 ");		// compare iCount with index(aHandle)
	asm("ldrgt r8, [ip, r4, lsr #14]! ");	// if count>index, r8=pS->uniqueID(bits14-19):pS->instance(bits0-13), ip=iObjects+index(Handle)=pS
	asm("ble lookup_handle_bad ");		// if count<=index, bad handle
	asm("cmn r7, #1 ");					// check if any type of object is ok
	asm("orreq r8, r8, r7, lsl #14 ");	// if it is, (therefore r7=-1) set top 18 bits of r8
	asm("orr r0, r0, r7, lsl #14 ");	// r0=aUniqueID(bits14-19):instance(Handle)(bits0-13)
	asm("mov r0, r0, lsl #12 ");		// only interested in comparing lower 20 bits...
	asm("mov r8, r8, lsl #12 ");
	asm("cmp r0, r8 ");					// check instance, and unique ID if necessary
	asm("ldreq r0, [ip, #4] ");			// if OK return pointer to CObject
	asm("movne r0, #0");				// else r0 = 0
	asm("andeq r0, r0, #%a0" : : "i" (RObjectIx::EObjRObjMask));	// r0 = (pointer & EObjRObjMask);

#ifdef __HANDLES_USE_RW_SPIN_LOCK__
	asm("stmfd sp!, {r0,ip,lr} ");		// save r0, ip and lr
	asm("mov r0, ip ");					// move RObjectIx this(ip) to r0
	asm("bl _ZN9RObjectIx15ReleaseReadLockEv") // call RObjectIx::ReleaseReadLock
	asm("ldmfd sp!, {r0,ip,lr} ");		// restore r0, ip and lr
#else
	// system lock is held, nothing to do
#endif

	asm("cmp r0, #0 ");
	__JUMP(ne,lr);
	asm("b lookup_handle_bad ");		// if NULL, bad handle
	
	asm("lookup_special: ");			// r12=handle<<1 (valid values are fffx0000 and fffx0002, where x=e or f)
	asm("bic ip, ip, #0x10000 ");		// clear 'no close' flag
#ifdef	__OBSOLETE_V1_IPC_SUPPORT__
	asm("cmp ip, #0x10000000 ");
	asm("blo lookup_thread_pseudo ");	// if handle<0x88000000, it's an IPC client thread pseudo handle
#endif
	asm("add ip, ip, #0x20000 ");		// valid values now 0 and 2
	asm("cmp r7, #2 ");					// r7=container number+1 or -1 if any object OK (can only be -1,1-31)
	asm("bgt lookup_handle_bad ");
	asm("beq 1f ");
	asm("cmp ip, #2 ");
	asm("subeq r0, r9, #%a0" : : "i" _FOFF(DThread,iNThread));
	__JUMP(eq,lr);
	asm("cmn r7, #1 ");					// r7=-1 means any type of object will do
	asm("bne lookup_handle_bad ");
	asm("1: ");
	asm("cmp ip, #0 ");
	asm("ldreq r0, [r9, #%a0]" : : "i" (_FOFF(DThread,iOwningProcess)-_FOFF(DThread,iNThread)));
	__JUMP(eq,lr);
	asm("b lookup_handle_bad ");

#ifdef	__OBSOLETE_V1_IPC_SUPPORT__
	asm("lookup_thread_pseudo: ");
	asm("ldr r4, __KernMsgInfo ");									// r4->msg chunk info
	asm("cmp r7, #1 ");
	asm("bgt lookup_handle_bad ");									// object type must be thread or unspecified
	asm("mov r0, ip, lsl #16 ");									// demangle handle; r0 = low half of handle
	asm("add r7, ip, r0, lsr #16 ");								// demangle handle; r7 = purported offset of RMessageK in msg chunk
	asm("ldr r0, [r4, #%a0]" : : "i" _FOFF(K::SMsgInfo,iBase));		// r0 = base address of kernel msg chunk
	asm("add r12, r7, #%a0" : : "i" ((TInt)sizeof(RMessageK)));		// r12 = offset + sizeof(RMessageK)
	asm("ldr r4, [r4, #%a0]" : : "i" _FOFF(K::SMsgInfo,iMaxSize));	// r4 = max size of kernel msg chunk
	asm("add r0, r0, r7 ");											// demangle handle; r0 = pointer to RMessageK (if good handle)
	asm("cmp r4, r12 ");											// is this off the end of the msg chunk?
	asm("blo lookup_handle_bad ");									// if so, bad handle
	asm("b lookup_message_client ");								// if not, get client
#endif

	asm("lookup_message: ");										// r0 points to RMessageK, validate it
	asm("ldr r4, __KernMsgInfo ");									// r4->msg chunk info
	asm("ldr r7, [r4, #%a0]" : : "i" _FOFF(K::SMsgInfo,iBase));		// r7 = base address of kernel msg chunk
	asm("ldr r4, [r4, #%a0]" : : "i" _FOFF(K::SMsgInfo,iMaxSize));	// r4 = max size of kernel msg chunk
	asm("subs r7, r0, r7 ");										// r7 = aMsg - base of kernel msg chunk
	asm("addhs r12, r7, #%a0" : : "i" ((TInt)sizeof(RMessageK)));	// if >=, r12 = offset+sizeof(RMessageK)
	asm("cmphs r4, r12 ");											// compare max size to this value
	asm("blo bad_message_handle ");									// if offset < 0 or max size < offset+sizeof(RMessageK), panic

#ifdef	__OBSOLETE_V1_IPC_SUPPORT__
	asm("lookup_message_client: ");
#endif
	// r0 is pointer to message, r7 is offset within chunk
	asm("tst r7, #%a0" : : "i" (RMessageK::KMessageSize-1));		// check alignment
	asm("ldr r8, [r9, #%a0]" : : "i" (_FOFF(DThread,iOwningProcess)-_FOFF(DThread,iNThread)));	// r8->current process
	asm("bne bad_message_handle ");									// reject if misaligned
	asm("adds ip, r0, #%a0" : : "i" _FOFF(RMessageK, iServerLink));
	// NOTE: Z flag clear here after 'adds' above
	asm(".global __magic_address_msg_lookup_1 ");
	asm("__magic_address_msg_lookup_1: ");							// this instruction is magically immune from exceptions
	// Warning: HARDCODED OFFSET(RMessageK) - assumes next 3 words are from class RMessageKBase
	asm("ldmia ip, {r4,r7,ip} ");									// get message iNext, iPrev, iFunction, set Z if bad
																	// should have r4=~r0, r7=~r8 if OK
	asm("beq bad_message_handle ");									// if exception, panic
	asm("eor r4, r4, r0 ");											// should be 0xffffffff
	asm("eor r7, r7, r8 ");											// should be 0xffffffff
	asm("and r4, r4, r7 ");											// should be 0xffffffff
	asm("cmn r4, #1 ");
	asm("bne bad_message_handle ");									// if no good, panic
	asm("and r7, r5, #0x3f ");
	asm("cmp r7, #%a0" : : "i" ((TInt)EIpcMessageD));				// EIpcMessageD requested?
	__JUMP(eq, lr);													// if so, finished
	asm("cmp ip, #%a0" : : "i" ((TInt)RMessage2::EDisConnect));		// else check iFunction != RMessage2::EDisConnect
	asm("beq bad_message_handle ");									// if it is, no good
	asm("cmp r7, #%a0" : : "i" ((TInt)EIpcMessage));				// EIpcMessage requested?
	asm("ldrne r0, [r0, #%a0]" : : "i" _FOFF(RMessageK, iClient));	// if not, r0=message->iClient
	__JUMP(,lr);

	asm("lookup_handle_bad: ");
	asm("mov r0, #%a0" : : "i" (EBadHandle));
	asm("b  " CSM_ZN1K18PanicCurrentThreadEi);

	asm("bad_message_handle: ");
	asm("mov r0, #%a0" : : "i" (EBadMessageHandle));
	asm("b  " CSM_ZN1K18PanicCurrentThreadEi);

	asm("__KernMsgInfo: ");
	asm(".word  " CSM_ZN1K7MsgInfoE);
	}


/** Execute a HAL function

 Executes a HAL function in the group specified.  
 Kern::HalFunction is the way to call the HAL functions from kernel code.  

@param aGroup The HAL group this function belonmgs to.  Defined in THalFunctionGroup.
@param aFunction The function within the specified group
@param a1 HAL Function parameter
@param a2 HAL Function parameter
@param aDeviceNumber The device number (eg. screen number)
@return KErrNone, if successful, KErrNotSupported if aGroup or aFunction is out of range, 
		or if there is no function registered to handle the group or any of the other system errors.
@see Kern::AddHalEntry
@see THalFunctionGroup
@see KMaxHalGroups

@pre	No fast mutex can be held.
@pre	Kernel must be unlocked.
@pre	Call in a thread context.
@pre	Interrupts must be enabled.
*/
EXPORT_C __NAKED__ TInt Kern::HalFunction(TInt /*aGroup*/, TInt /*aFunction*/, TAny* /*a1*/, TAny* /*a2*/,TInt /*aDeviceNumber*/)
	{
	ASM_CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC|MASK_NO_FAST_MUTEX);

	asm("ldr ip, [sp, #0] ");
	asm("orr r0, r0, ip, lsl #16 ");
	}

/** Execute a HAL function

 Executes a HAL function in the group specified.  
 Kern::HalFunction is the way to call the HAL functions from kernel code.  

@param aGroup The HAL group this function belonmgs to.  Defined in THalFunctionGroup.
@param aFunction The function within the specified group
@param a1 HAL Function parameter
@param a2 HAL Function parameter
@return KErrNone, if successful, KErrNotSupported if aGroup or aFunction is out of range, 
		or if there is no function registered to handle the group or any of the other system errors.
@see Kern::AddHalEntry
@see THalFunctionGroup
@see KMaxHalGroups

@pre	No fast mutex can be held.
@pre	Kernel must be unlocked.
@pre	Call in a thread context.
@pre	Interrupts must be enabled.
*/
EXPORT_C __NAKED__ TInt Kern::HalFunction(TInt /*aGroup*/, TInt /*aFunction*/, TAny* /*a1*/, TAny* /*a2*/)
// This must be done as a SWI to get the correct permissions when calling from supervisor mode.
	{
	ASM_CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC|MASK_NO_FAST_MUTEX);

	asm("mrs	ip, spsr ");
	asm("stmfd	sp!, {ip, lr} ");		// swi will trash lr and spsr when called in svc mode
	asm("swi	%a0" : : "i" (EExecHalFunction|EXECUTIVE_SLOW));
	asm("ldmfd	sp!, {ip, lr} ");
	asm("msr	spsr, ip");
#if defined(__CPU_CORTEX_A9__) && !defined(__CPU_ARM_A9_ERRATUM_571622_FIXED)
	asm("nop ");							// ARM Cortex-A9 MPCore erratum 571622 workaround
	asm("nop ");							// Insert nops so branch doesn't occur in 2nd or 3rd position after a msr spsr
	asm("nop ");
#endif
	__JUMP(,	lr);

	// Assembler version of ExecHandler::ThreadRequestSignal
	// Enter with r0->DThread
	// r9->current NThread
	// Can trash r0-r4, r6-r8, r12
	asm("_asm_exec_ThreadRequestSignal: ");
	asm("ldr r6, [r0, #%a0]" : : "i" _FOFF(DThread,iOwningProcess));	// r6->target process
	asm("ldr r12, [r9, #%a0]" : : "i" (_FOFF(DThread,iOwningProcess)-_FOFF(DThread,iNThread)));	// r12->current process
	asm("add r0, r0, #%a0" : : "i" _FOFF(DThread,iNThread));	// r0->target NThread
	asm("mov r1, #0 ");											// Mutex arg for NKern::ThreadRequestSignal
	asm("cmp r6, r12 ");										// target process = current process?
	asm("beq " CSM_ZN5NKern19ThreadRequestSignalEP7NThreadP10NFastMutex );	// NKern::ThreadRequestSignal
	asm("b  " CSM_ZN1K27LockedPlatformSecurityPanicEv);
	}