kernel/eka/nkern/arm/ncutils.cia
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 8 538db54a451d
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1994-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\nkern\arm\ncutils.cia
// 
//

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

//#define __DBG_MON_FAULT__
//#define __RAM_LOADED_CODE__
//#define __EARLY_DEBUG__

#ifdef _DEBUG
#define ASM_KILL_LINK(rp,rs)	asm("mov "#rs", #0xdf ");\
								asm("orr "#rs", "#rs", "#rs", lsl #8 ");\
								asm("orr "#rs", "#rs", "#rs", lsl #16 ");\
								asm("str "#rs", ["#rp"] ");\
								asm("str "#rs", ["#rp", #4] ");
#else
#define ASM_KILL_LINK(rp,rs)
#endif


#ifdef __PRI_LIST_MACHINE_CODED__
/** Return the priority of the highest priority item present on a priority list.

	@return	The highest priority present or -1 if the list is empty.
 */
EXPORT_C __NAKED__ TInt TPriListBase::HighestPriority()
	{
#ifdef __CPU_ARM_HAS_CLZ
	asm("ldr r2, [r0, #4] ");				// r2=iPresent MSW
	asm("ldr r1, [r0, #0] ");				// r1=iPresent LSW
	CLZ(0,2);								// r0=31-MSB(r2)
	asm("subs r0, r0, #32 ");				// r0=-1-MSB(r2), 0 if r2=0
	CLZcc(CC_EQ,0,1);						// if r2=0, r0=31-MSB(r1)
	asm("rsb r0, r0, #31 ");				// r0=highest priority
#else
	asm("ldmia r0, {r1,r2} ");				// r2:r1=iPresent
	asm("mov r0, #31 ");					// start at 31
	asm("cmp r2, #0 ");						// high word non-zero?
	asm("movne r0, #63 ");					// if so, start at 63
	asm("movne r1, r2 ");					// and set r1=high word
	asm("cmp r1, #0 ");
	asm("beq highest_pri_0 ");
	asm("cmp r1, #0x00010000 ");
	asm("movcc r1, r1, lsl #16 ");
	asm("subcc r0, r0, #16 ");
	asm("cmp r1, #0x01000000 ");
	asm("movcc r1, r1, lsl #8 ");
	asm("subcc r0, r0, #8 ");
	asm("cmp r1, #0x10000000 ");
	asm("movcc r1, r1, lsl #4 ");
	asm("subcc r0, r0, #4 ");
	asm("cmp r1, #0x40000000 ");
	asm("movcc r1, r1, lsl #2 ");
	asm("subcc r0, r0, #2 ");
	asm("cmp r1, #0x80000000 ");
	asm("subcc r0, r0, #1 ");
	__JUMP(,lr);
	asm("highest_pri_0: ");
	asm("mvn r0, #0 ");						// if list empty, return -1
#endif
	__JUMP(,lr);
	}

/** Find the highest priority item present on a priority list.
	If multiple items at the same priority are present, return the first to be
	added in chronological order.

	@return	a pointer to the item or NULL if the list is empty.
 */
EXPORT_C __NAKED__ TPriListLink* TPriListBase::First()
	{
#ifdef __CPU_ARM_HAS_CLZ
	asm("ldr r2, [r0, #4] ");				// r2=iPresent MSW
	asm("ldr r1, [r0], #8 ");				// r1=iPresent LSW, r0=&iQueue[0]
	CLZ(3,2);								// r3=31-MSB(r2)
	asm("subs r3, r3, #32 ");				// r3=-1-MSB(r2), 0 if r2=0
	CLZcc(CC_EQ,3,1);						// if r2=0, r3=31-MSB(r1)
	asm("rsbs r3, r3, #31 ");				// r3=highest priority
	asm("ldrpl r0, [r0, r3, lsl #2] ");		// if r3>=0 list is nonempty, r0->first entry
	asm("movmi r0, #0 ");					// if r3<0 list empty, return NULL
#else
	asm("ldmia r0!, {r1,r2} ");				// r2:r1=iPresent, r0=&iQueue[0]
	asm("cmp r2, #0 ");						// high word non-zero?
	asm("addne r0, r0, #128 ");				// if so, r0=&iQueue[32]
	asm("movne r1, r2 ");					// and set r1=high word
	asm("cmp r1, #0x00010000 ");
	asm("movcc r1, r1, lsl #16 ");
	asm("addcs r0, r0, #0x40 ");			// if iPresent>=0x00010000, step r0 on by 16 words
	asm("cmp r1, #0x01000000 ");
	asm("movcc r1, r1, lsl #8 ");
	asm("addcs r0, r0, #0x20 ");			// if iPresent>=0x01000000, step r0 on by 8 words
	asm("cmp r1, #0x10000000 ");
	asm("movcc r1, r1, lsl #4 ");
	asm("addcs r0, r0, #0x10 ");			// if iPresent>=0x10000000, step r0 on by 4 words
	asm("cmp r1, #0x40000000 ");
	asm("movcc r1, r1, lsl #2 ");
	asm("addcs r0, r0, #0x08 ");			// if iPresent>=0x40000000, step r0 on by 2 words
	asm("cmp r1, #0 ");
	asm("addmi r0, r0, #4 ");				// if iPresent>=0x80000000, step r0 on by 1 word
	asm("ldrne r0, [r0] ");					// if iPresent was not zero, r0 points to first entry
	asm("moveq r0, #0 ");					// else r0=NULL
#endif
	__JUMP(,lr);
	}

/** Add an item to a priority list.

	@param aLink = a pointer to the item - must not be NULL
 */
EXPORT_C __NAKED__ void TPriListBase::Add(TPriListLink* /*aLink*/)
	{
	asm("ldrb r2, [r1, #8]" );				// r2=priority of aLink
	asm("add ip, r0, #8 ");					// ip=&iQueue[0]
	asm("ldr r3, [ip, r2, lsl #2]! ");		// r3->first entry at this priority
	asm("cmp r3, #0 ");						// is this first entry at this priority?
	asm("bne pri_list_add_1 ");				// branch if not
	asm("str r1, [ip] ");					// if queue originally empty, iQueue[pri]=aThread
	asm("ldrb ip, [r0, r2, lsr #3]! ");		// ip=relevant byte of present mask, r0->same
	asm("and r2, r2, #7 ");
	asm("mov r3, #1 ");
	asm("str r1, [r1, #0] ");				// aThread->next=aThread
	asm("orr ip, ip, r3, lsl r2 ");			// ip |= 1<<(pri&7)
	asm("str r1, [r1, #4] ");				// aThread->iPrev=aThread
	asm("strb ip, [r0] ");					// update relevant byte of present mask
	__JUMP(,lr);
	asm("pri_list_add_1: ");
	asm("ldr ip, [r3, #4] ");				// if nonempty, ip=last
	asm("str r1, [r3, #4] ");				// first->prev=aThread
	asm("stmia r1, {r3,ip} ");				// aThread->next=r3=first, aThread->prev=ip=last
	asm("str r1, [ip, #0] ");				// last->next=aThread
	__JUMP(,lr);
	}

/** Change the priority of an item on a priority list

	@param	aLink = pointer to the item to act on - must not be NULL
	@param	aNewPriority = new priority for the item
 */
EXPORT_C __NAKED__ void TPriListBase::ChangePriority(TPriListLink* /*aLink*/, TInt /*aNewPriority*/)
	{
	asm("ldrb r3, [r1, #8] ");				// r3=old priority
	asm("stmfd sp!, {r4-r6,lr} ");
	asm("cmp r3, r2 ");
	asm("ldmeqfd sp!, {r4-r6,pc} ");		// if old priority=new, finished
	asm("ldmia r1, {r4,r12} ");				// r4=next, r12=prev
	asm("ldmia r0!, {r6,lr} ");				// lr:r6=present mask, r0=&iQueue[0]
	asm("subs r5, r4, r1 ");				// check if aLink is only one at that priority, r5=0 if it is
	asm("beq change_pri_1 ");				// branch if it is
	asm("ldr r5, [r0, r3, lsl #2] ");		// r5=iQueue[old priority]
	asm("str r4, [r12, #0] ");				// prev->next=next
	asm("str r12, [r4, #4] ");				// next->prev=prev
	asm("cmp r5, r1 ");						// was aLink first?
	asm("streq r4, [r0, r3, lsl #2] ");		// if it was, iQueue[old priority]=aLink->next
	asm("b change_pri_2 ");
	asm("change_pri_1: ");
	asm("str r5, [r0, r3, lsl #2] ");		// if empty, set iQueue[old priority]=NULL
	asm("mov r12, #0x80000000 ");
	asm("rsbs r3, r3, #31 ");				// r3=31-priority
	asm("bicmi lr, lr, r12, ror r3 ");		// if pri>31, clear bit is MS word
	asm("bicpl r6, r6, r12, ror r3 ");		// if pri<=31, clear bit in LS word
	asm("change_pri_2: ");
	asm("ldr r4, [r0, r2, lsl #2] ");		// r4=iQueue[new priority]
	asm("strb r2, [r1, #8] ");				// store new priority
	asm("cmp r4, #0 ");						// new priority queue empty?
	asm("bne change_pri_3 ");				// branch if not
	asm("str r1, [r0, r2, lsl #2] ");		// if new priority queue was empty, iQueue[new p]=aLink
	asm("mov r12, #0x80000000 ");
	asm("str r1, [r1, #0] ");				// aLink->next=aLink
	asm("rsbs r2, r2, #31 ");				// r2=31-priority
	asm("str r1, [r1, #4] ");				// aLink->prev=aLink
	asm("orrmi lr, lr, r12, ror r2 ");		// if pri>31, set bit is MS word
	asm("orrpl r6, r6, r12, ror r2 ");		// if pri<=31, set bit in LS word
	asm("stmdb r0!, {r6,lr} ");				// store present mask and restore r0
	asm("ldmfd sp!, {r4-r6,pc} ");
	asm("change_pri_3: ");
	asm("ldr r12, [r4, #4] ");				// r12->last link at this priority
	asm("str r1, [r4, #4] ");				// first->prev=aLink
	asm("str r1, [r12, #0] ");				// old last->next=aLink
	asm("stmia r1, {r4,r12} ");				// aLink->next=r3=first, aLink->prev=r12=old last
	asm("stmdb r0!, {r6,lr} ");				// store present mask and restore r0
	asm("ldmfd sp!, {r4-r6,pc} ");
	}
#endif

__NAKED__ void initialiseState()
	{
	// entry in mode_svc with irqs and fiqs off	
	asm("mrs r0, cpsr ");
	asm("bic r1, r0, #0x1f ");
	asm("orr r1, r1, #0xd3 ");				// mode_svc
	asm("msr cpsr, r1 ");
	__JUMP(,lr);
	}

// Called by a thread when it first runs
__NAKED__ void __StartThread()
	{
	// On entry r4->current thread, r5->entry point, r6->parameter block
	asm("mov r0, r6 ");
	USER_MEMORY_GUARD_OFF_IF_MODE_USR(r6);
	ERRATUM_353494_MODE_CHANGE(,r6);
	asm("mov lr, pc ");
	asm("movs pc, r5 ");
	asm("b  " CSM_ZN5NKern4ExitEv);
	}

// Called by a thread which has been forced to exit
// Interrupts off here, kernel unlocked
__NAKED__ void __DoForcedExit()
	{
	asm("mov r0, #0x13 ");
	asm("msr cpsr, r0 ");		// interrupts back on
	asm("bic sp, sp, #4 ");		// align stack since it may be misaligned on return from scheduler
	asm("bl  " CSM_ZN5NKern4LockEv);	// lock the kernel (must do this before setting iCsCount=0)
	asm("ldr r0, __TheScheduler ");			// r0 points to scheduler data
	asm("mov r1, #0 ");
	asm("ldr r0, [r0, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread));	// r0=iCurrentThread
	asm("str r1, [r0, #%a0]" : : "i" _FOFF(NThreadBase,iCsCount));	// set iCsCount=0
	asm("b  " CSM_ZN11NThreadBase4ExitEv);	// exit

	asm("__TheScheduler: ");
	asm(".word TheScheduler ");
	asm("__BTraceData: ");
	asm(".word BTraceData ");
	asm("__DBTraceFilter2_iCleanupHead:");
#ifdef __EABI__
	asm(".word _ZN14DBTraceFilter212iCleanupHeadE");
#else
	asm(".word _14DBTraceFilter2.iCleanupHead");
#endif
	}


/** @internalTechnology

	Called to indicate that the system has crashed and all CPUs should be
	halted and should dump their registers.

*/
__NAKED__ void NKern::NotifyCrash(const TAny* /*a0*/, TInt /*a1*/)
	{
	asm("stmfd	sp!, {r0-r1} ");			// save parameters
	asm("ldr	r0, __CrashState ");
	asm("mov	r1, #1 ");
	asm("str	r1, [r0] ");				// CrashState = ETrue
	asm("ldr	r0, __TheScheduler ");
	asm("ldr	r0, [r0, #%a0]" : : "i" _FOFF(TScheduler,i_Regs));
	asm("ldr	r1, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet, iExcCode));
	asm("cmp	r1, #0 ");					// context already saved?
	asm("bge	state_already_saved ");		// skip if so
	asm("mov	r1, lr ");
	asm("bl "	CSM_ZN3Arm9SaveStateER14SFullArmRegSet );
	asm("str	r1, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet, iN.iR15));
	asm("ldmia	sp!, {r2-r3} ");			// original R0,R1
	asm("stmia	r0, {r2-r3} ");				// save original R0,R1
	asm("add	r1, r0, #%a0" : : "i" _FOFF(SFullArmRegSet, iExcCode));
	asm("stmib	r1, {r2-r3} ");				// save a0, a1 in iCrashArgs
	asm("mov	r1, #13 ");					// r1 = regnum
	asm("mrs	r2, cpsr ");				// r2 = mode
	asm("bl "	CSM_ZN3Arm3RegER14SFullArmRegSetim );	// r0 = pointer to exception mode R13
	asm("str	sp, [r0] ");				// save correct original value for exception mode R13
	asm("b		state_save_complete ");

	asm("state_already_saved: ");
	asm("ldmia	sp!, {r2-r3} ");			// original R0,R1
	asm("add	r1, r0, #%a0" : : "i" _FOFF(SFullArmRegSet, iExcCode));
	asm("ldr	r4, [r1, #4]! ");
	asm("cmp	r4, #0 ");
	asm("stmeqia	r1, {r2-r3} ");			// save a0, a1 in iCrashArgs, provided iCrashArgs not already set
	asm("state_save_complete: ");

	asm("mov	r2, #0xd1 ");
	asm("msr	cpsr, r2 ");				// mode_fiq, interrupts off
	asm("mov	r4, r0 ");
	asm("bic	sp, sp, #4 ");				// align stack to multiple of 8

	asm("mov	r0, #0 ");
	asm("mov	r1, #0 ");
	asm("mov	r2, #0 ");
	asm("bl		NKCrashHandler ");

	asm("mov	r0, #1 ");
	asm("ldr	r1, [r4, #%a0] " : : "i" _FOFF(SFullArmRegSet,iN.iR0));	// original R0 = a0 parameter
	asm("ldr	r2, [r4, #%a0] " : : "i" _FOFF(SFullArmRegSet,iN.iR1));	// original R1 = a1 parameter
	asm("bl		NKCrashHandler ");

	// shouldn't get back here
	__ASM_CRASH();

	asm("__CrashState: ");
	asm(".word %a0" : : "i" ((TInt)&CrashState));
	}



__NAKED__ EXPORT_C TBool BTrace::Out(TUint32 a0, TUint32 a1, TUint32 a2, TUint32 a3)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r2,r3,r4,lr}");
	asm("and	r2, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r2, [r12, r2, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("mov	r3, r1");			// r3 = a1 (ready for call to handler)
	asm("adr	lr, 9f");
	asm("cmp	r2, #0");
	asm("moveq	r0, #0");
	asm("ldrne	pc, [r12, #%a0]" : : "i" _FOFF(SBTraceData,iHandler));
	asm("9:");
	__POPRET("r2,r3,r4,");
	}

__NAKED__ EXPORT_C TBool BTrace::OutN(TUint32 a0, TUint32 a1, TUint32 a2, const TAny* aData, TInt aDataSize)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r2,r3,r4,lr}");
	asm("and	r2, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r2, [r12, r2, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("ldr	r4, [sp, #16]");	// r2 = aDataSize
	asm("cmp	r2, #0");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r2,r3,r4,");

	asm("cmp	r4, #%a0" : : "i" ((TInt)KMaxBTraceDataArray));
	asm("movhi	r4, #%a0" : : "i" ((TInt)KMaxBTraceDataArray));
 	asm("orrhi	r0, r0, #%a0" : : "i" ((TInt)(BTrace::ERecordTruncated<<(BTrace::EFlagsIndex*8))));
	asm("add	r0, r0, r4");
	asm("subs	r4, r4, #1");
	asm("ldrhs	r2, [r3]");			// get first word of aData is aDataSize!=0
	asm("mov	r3, r1");			// r3 = a1 (ready for call to handler)
	asm("cmp	r4, #4");
	asm("strlo	r2, [sp, #4]");		// replace aData with first word if aDataSize is 1-4

	asm("mov	lr, pc");
	asm("ldr	pc, [r12, #%a0]" : : "i" _FOFF(SBTraceData,iHandler));
	__POPRET("r2,r3,r4,");
	}

__NAKED__ EXPORT_C TBool BTrace::OutX(TUint32 a0, TUint32 a1, TUint32 a2, TUint32 a3)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r2,r3,r4,lr}");
	asm("and	r2, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r2, [r12, r2, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("mov	r3, r1");			// r3 = a1 (ready for call to handler)
	asm("ldr	lr, __TheScheduler");
	asm("cmp	r2, #0");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r2,r3,r4,");

	// set r2 = context id
	asm("ldrb	r4, [lr, #%a0]" : : "i" _FOFF(TScheduler,iInIDFC));
	asm("mrs	r2, cpsr");
	asm("and	r2, r2, #0x0f");
	asm("cmp	r2, #3");
	asm("movhi	r2, #2");		// r2 = context ID => 1 for FIQ, 2 for IRQ/ABT/UND/SYS
	asm("cmpeq	r4, #0");
	asm("ldreq	r2, [lr, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread));

	asm("mov	lr, pc");
	asm("ldr	pc, [r12, #%a0]" : : "i" _FOFF(SBTraceData,iHandler));
	__POPRET("r2,r3,r4,");
	}

__NAKED__ EXPORT_C TBool BTrace::OutNX(TUint32 a0, TUint32 a1, TUint32 a2, const TAny* aData, TInt aDataSize)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r2,r3,r4,lr}");
	asm("and	r2, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r2, [r12, r2, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("ldr	r4, [sp, #16]");	// r2 = aDataSize
	asm("ldr	lr, __TheScheduler");
	asm("cmp	r2, #0");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r2,r3,r4,");

	asm("cmp	r4, #%a0" : : "i" ((TInt)KMaxBTraceDataArray));
	asm("movhi	r4, #%a0" : : "i" ((TInt)KMaxBTraceDataArray));
 	asm("orrhi	r0, r0, #%a0" : : "i" ((TInt)(BTrace::ERecordTruncated<<(BTrace::EFlagsIndex*8))));
	asm("add	r0, r0, r4");
	asm("subs	r4, r4, #1");
	asm("ldrhs	r2, [r3]");			// get first word of aData is aDataSize!=0
	asm("mov	r3, r1");			// r3 = a1 (ready for call to handler)
	asm("cmp	r4, #4");
	asm("strlo	r2, [sp, #4]");		// replace aData with first word if aDataSize is 1-4

	// set r2 = context id
	asm("ldrb	r4, [lr, #%a0]" : : "i" _FOFF(TScheduler,iInIDFC));
	asm("mrs	r2, cpsr");
	asm("and	r2, r2, #0x0f");
	asm("cmp	r2, #3");
	asm("movhi	r2, #2");		// r2 = context ID => 1 for FIQ, 2 for IRQ/ABT/UND/SYS
	asm("cmpeq	r4, #0");
	asm("ldreq	r2, [lr, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread));

	asm("mov	lr, pc");
	asm("ldr	pc, [r12, #%a0]" : : "i" _FOFF(SBTraceData,iHandler));
	__POPRET("r2,r3,r4,");
	}

__NAKED__ EXPORT_C TBool BTrace::OutBig(TUint32 a0, TUint32 a1, const TAny* aData, TInt aDataSize)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r4,lr}");
	asm("and	r4, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r4, [r12, r4, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("cmp	r4, #0");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r4,");

	asm("ldr	r12, __TheScheduler");
	asm("stmdb	sp!, {lr}");
	asm("ldrb	lr, [r12, #%a0]" : : "i" _FOFF(TScheduler,iInIDFC));
	asm("mrs	r4, cpsr");
	asm("and	r4, r4, #0x0f");
	asm("cmp	r4, #3");
	asm("movhi	r4, #2");		// r4 = context ID => 1 for FIQ, 2 for IRQ/ABT/UND/SYS
	asm("cmpeq	lr, #0");
	asm("ldreq	r4, [r12, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread));
	asm("stmdb	sp!, {r4}");
	asm("bl " CSM_ZN6BTrace8DoOutBigEmmPKvimm);
	asm("add	sp, sp, #8");
	__POPRET("r4,");
	}


__NAKED__ TBool DBTraceFilter2::Check(TUint32 aUid)
	{
	asm("stmdb	sp!, {lr}");
	asm("ldr	r3, [r0,#%a0]" : : "i" _FOFF(DBTraceFilter2,iNumUids));
	asm("add	r0, r0, #%a0" : : "i" _FOFF(DBTraceFilter2,iUids));
	asm("mov	r2, #0");
	asm("0:");
	asm("cmp	r3, r2");
	asm("bls	9f");
	asm("add	r12, r2, r3");
	asm("mov	r12, r12, asr #1");
	asm("ldr	lr, [r0, r12, lsl #2]");
	asm("cmp	r1, lr");
	asm("addhi	r2, r12, #1");
	asm("movlo	r3, r12");
	asm("bne	0b");
	asm("movs	r0, #1");
	__POPRET("");
	asm("9:");
	asm("movs	r0, #0");
	__POPRET("");
	}


__NAKED__ TBool SBTraceData::CheckFilter2(TUint32 aUid)
	{
	asm("btrace_check_filter2:");
	// returns r0 = 0 or 1 indicating if trace passed the filter check
	// returns r2 = trace context id

	asm("ldr	r12, __TheScheduler");
	asm("stmdb	sp!, {r4-r6,lr}");
	asm("mrs	r2, cpsr");
	// r2 = cpsr
	asm("ldrb	lr, [r12, #%a0]" : : "i" _FOFF(TScheduler,iInIDFC));
	asm("and	r4, r2, #0x0f");
	asm("cmp	r4, #3");
	asm("movhi	r4, #2");		// r4 = context ID => 1 for FIQ, 2 for IRQ/ABT/UND/SYS
	asm("cmpeq	lr, #0");
	asm("ldreq	lr, [r12, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked));
	asm("ldreq	r4, [r12, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread));
	asm("cmpeq	lr, #0");
	// r4 = context value for trace
	// zero flag set if we need to enter a critical section

	// NKern::ThreadEnterCS()
	asm("ldreq	r5, [r4, #%a0]" : : "i" _FOFF(NThreadBase,iCsCount));
	asm("movne	r5, #0");
	asm("addeq	r5, r5, #1");
	asm("streq	r5, [r4, #%a0]" : : "i" _FOFF(NThreadBase,iCsCount));
	// r5 = true if we entered a critical section

	// DBTraceFilter2::Open()
	INTS_OFF(r12, r2, INTS_ALL_OFF);
	asm("ldr	r0, [r0, #%a0]" : : "i" (_FOFF(SBTraceData,iFilter2)));
	asm("cmp	r0, #1");
	asm("ldrhi	r12, [r0, #%a0]" : : "i" _FOFF(DBTraceFilter2,iAccessCount));
	asm("addhi	r12, r12, #1");
	asm("strhi	r12, [r0, #%a0]" : : "i" _FOFF(DBTraceFilter2,iAccessCount));
	asm("msr	cpsr_c, r2");
	asm("bls	8f");


	asm("mov	r6, r0");
	asm("bl		Check__14DBTraceFilter2Ul");
	// r0 = result


	// DBTraceFilter2::Close()
	asm("mrs	r2, cpsr");
	INTS_OFF(r12, r2, INTS_ALL_OFF);
	asm("ldr	r12, [r6, #%a0]" : : "i" _FOFF(DBTraceFilter2,iAccessCount));
	asm("ldr	r1, __DBTraceFilter2_iCleanupHead");
	asm("subs	r12, r12, #1");
	asm("str	r12, [r6, #%a0]" : : "i" _FOFF(DBTraceFilter2,iAccessCount));
	asm("ldreq	r12, [r1]");
	asm("streq	r6, [r1]");
	asm("streq	r12, [r6, #%a0]" : : "i" _FOFF(DBTraceFilter2,iCleanupLink));
	asm("msr	cpsr_c, r2");

	// NKern::ThreadLeaveCS()	
	asm("8:");
	asm("cmp	r5, #0");
	asm("beq	9f");
	asm("mov	r5, r0");
	asm("bl " CSM_ZN5NKern13ThreadLeaveCSEv);
	asm("mov	r0, r5");
	asm("9:");
	asm("mov	r2, r4"); // r2 = context id
	__POPRET("r4-r6,");
	}

__NAKED__ EXPORT_C TBool BTrace::OutFiltered(TUint32 a0, TUint32 a1, TUint32 a2, TUint32 a3)
	{
	// fall through to OutFilteredX...
	}

__NAKED__ EXPORT_C TBool BTrace::OutFilteredX(TUint32 a0, TUint32 a1, TUint32 a2, TUint32 a3)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r2,r3,r4,lr}");
	asm("and	r2, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r2, [r12, r2, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("mov	r3, r1");			// r3 = a1 (ready for call to handler)
	asm("cmp	r2, #0");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r2,r3,r4,");

	asm("stmdb	sp!, {r0,r3,r12}");
	asm("mov	r0, r12");
	asm("bl		btrace_check_filter2");
	asm("cmp	r0, #0");
	asm("ldmia	sp!, {r0,r3,r12}");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r2,r3,r4,");

	asm("adr	lr, 9f");
	asm("ldr	pc, [r12, #%a0]" : : "i" _FOFF(SBTraceData,iHandler));
	asm("9:");
	__POPRET("r2,r3,r4,");
	}

__NAKED__ EXPORT_C TBool BTrace::OutFilteredN(TUint32 a0, TUint32 a1, TUint32 a2, const TAny* aData, TInt aDataSize)
	{
	// fall through to OutFilteredNX...
	}

__NAKED__ EXPORT_C TBool BTrace::OutFilteredNX(TUint32 a0, TUint32 a1, TUint32 a2, const TAny* aData, TInt aDataSize)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r2,r3,r4,lr}");
	asm("and	r2, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r2, [r12, r2, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("cmp	r2, #0");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r2,r3,r4,");

	asm("stmdb	sp!, {r0,r1,r3,r12}");
	asm("mov	r0, r12");
	asm("bl		btrace_check_filter2");
	asm("cmp	r0, #0");
	asm("ldmia	sp!, {r0,r1,r3,r12}");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r2,r3,r4,");

	asm("ldr	r4, [sp, #16]");	// r4 = aDataSize
	asm("cmp	r4, #%a0" : : "i" ((TInt)KMaxBTraceDataArray));
	asm("movhi	r4, #%a0" : : "i" ((TInt)KMaxBTraceDataArray));
 	asm("orrhi	r0, r0, #%a0" : : "i" ((TInt)(BTrace::ERecordTruncated<<(BTrace::EFlagsIndex*8))));
	asm("add	r0, r0, r4");
	asm("subs	r4, r4, #1");
	asm("ldrhs	lr, [r3]");			// get first word of aData is aDataSize!=0
	asm("mov	r3, r1");			// r3 = a1 (ready for call to handler)
	asm("cmp	r4, #4");
	asm("strlo	lr, [sp, #4]");		// replace aData with first word if aDataSize is 1-4

	asm("mov	lr, pc");
	asm("ldr	pc, [r12, #%a0]" : : "i" _FOFF(SBTraceData,iHandler));
	__POPRET("r2,r3,r4,");
	}

__NAKED__ EXPORT_C TBool BTrace::OutFilteredBig(TUint32 a0, TUint32 a1, const TAny* aData, TInt aDataSize)
	{
	asm("ldr	r12, __BTraceData");
	asm("stmdb	sp!, {r4,lr}");
	asm("and	r4, r0, #%a0" : : "i" ((TInt)(0xff<<(BTrace::ECategoryIndex*8))));
	asm("ldrb	r4, [r12, r4, lsr #%a0]" : : "i" ((TInt)(BTrace::ECategoryIndex*8)));
	asm("cmp	r4, #0");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r4,");

	asm("stmdb	sp!, {r0-r3,r4,lr}");
	asm("mov	r0, r12");
	asm("bl		btrace_check_filter2");
	asm("cmp	r0, #0");
	asm("mov	r12, r2");
	asm("ldmia	sp!, {r0-r3,r4,lr}");
	asm("moveq	r0, #0");
	__CPOPRET(eq,"r4,");

	asm("stmdb	sp!, {r12,lr}");
	asm("bl " CSM_ZN6BTrace8DoOutBigEmmPKvimm);
	asm("add	sp, sp, #8");
	__POPRET("r4,");
	}

	
__NAKED__ EXPORT_C TBool BTrace::OutFilteredPcFormatBig(TUint32 a0, TUint32 aModuleUid, TUint32 aPc, TUint16 aFormatId, const TAny* aData, TInt aDataSize)
	{
	asm("mov	r0, #0"); //kernel side not implemented yet
	}

/******************************************************************************/

/** Save all the ARM registers

@internalTechnology
*/
__NAKED__ void Arm::SaveState(SFullArmRegSet&)
	{
	asm("stmia	r0, {r0-r14}^ ");	// save R0-R7, R8_usr-R14_usr
	asm("str	lr, [r0, #60]! ");	// save R15
	asm("mrs	r1, cpsr ");
	asm("str	r1, [r0, #4]! ");	// save CPSR
	asm("bic	r2, r1, #0x1f ");
	asm("orr	r2, r2, #0xd3 ");	// mode_svc, all interrupts off
	asm("msr	cpsr, r2 ");
	asm("stmib	r0!, {r13,r14} ");	// save R13_svc, R14_svc
	asm("mrs	r3, spsr ");
	asm("str	r3, [r0, #4]! ");	// save SPSR_svc
	asm("bic	r2, r1, #0x1f ");
	asm("orr	r2, r2, #0xd7 ");	// mode_abt, all interrupts off
	asm("msr	cpsr, r2 ");
	asm("stmib	r0!, {r13,r14} ");	// save R13_abt, R14_abt
	asm("mrs	r3, spsr ");
	asm("str	r3, [r0, #4]! ");	// save SPSR_abt
	asm("bic	r2, r1, #0x1f ");
	asm("orr	r2, r2, #0xdb ");	// mode_und, all interrupts off
	asm("msr	cpsr, r2 ");
	asm("stmib	r0!, {r13,r14} ");	// save R13_und, R14_und
	asm("mrs	r3, spsr ");
	asm("str	r3, [r0, #4]! ");	// save SPSR_und
	asm("bic	r2, r1, #0x1f ");
	asm("orr	r2, r2, #0xd2 ");	// mode_irq, all interrupts off
	asm("msr	cpsr, r2 ");
	asm("stmib	r0!, {r13,r14} ");	// save R13_irq, R14_irq
	asm("mrs	r3, spsr ");
	asm("str	r3, [r0, #4]! ");	// save SPSR_irq
	asm("bic	r2, r1, #0x1f ");
	asm("orr	r2, r2, #0xd1 ");	// mode_fiq, all interrupts off
	asm("msr	cpsr, r2 ");
	asm("stmib	r0!, {r8-r14} ");	// save R8_fiq ... R14_fiq
	asm("mrs	r3, spsr ");
	asm("str	r3, [r0, #4]! ");	// save SPSR_fiq
	asm("bic	r2, r1, #0x1f ");
	asm("orr	r2, r2, #0xd3 ");	// mode_svc, all interrupts off
	asm("msr	cpsr, r2 ");

	asm("mov	r4, #0 ");
	asm("mov	r5, #0 ");
	asm("mov	r6, #0 ");
	asm("mov	r7, #0 ");
	asm("mov	r8, #0 ");
	asm("mov	r9, #0 ");
	asm("mov	r10, #0 ");
	asm("mov	r11, #0 ");

	// monitor mode - skip for now
	asm("mov	r3, #0 ");
	asm("stmib	r0!, {r4-r6} ");	// R13_mon, R14_mon, SPSR_mon

	// zero spare words
	asm("mov	r3, #0 ");
	asm("stmib	r0!, {r4-r11} ");
	asm("add	r0, r0, #4 ");		// r0 = &a.iA

#ifdef __CPU_ARMV7
	asm("mrc	p14, 6, r3, c1, c0, 0 ");
#else
	asm("mov	r3, #0 ");
#endif
	asm("str	r3, [r0], #4 ");	// TEEHBR
#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG
	GET_CAR(,r3);
#else
	asm("mov	r3, #0 ");
#endif
	asm("str	r3, [r0], #4 ");	// CPACR

	// skip SCR, SDER, NSACR, PMCR, MVBAR for now
	asm("mov	r3, #0 ");
	asm("stmia	r0!, {r4-r8} ");	// SCR, SDER, NSACR, PMCR, MVBAR

	// zero spare words
	asm("mov	r3, #0 ");
	asm("stmia	r0!, {r3-r11} ");	// r0 = &a.iB[0]

	// just fill in iB[0]
#ifdef __CPU_HAS_MMU
	asm("mrc	p15, 0, r3, c1, c0, 0 ");
	asm("str	r3, [r0], #4 ");	// SCTLR
#ifdef __CPU_HAS_ACTLR
	asm("mrc	p15, 0, r3, c1, c0, 1 ");
#else
	asm("mov	r3, #0 ");
#endif
	asm("str	r3, [r0], #4 ");	// ACTLR
	asm("mrc	p15, 0, r3, c2, c0, 0 ");
	asm("str	r3, [r0], #4 ");	// TTBR0
#ifdef __CPU_HAS_TTBR1
	asm("mrc	p15, 0, r2, c2, c0, 1 ");
	asm("mrc	p15, 0, r3, c2, c0, 2 ");
#else
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
#endif
	asm("stmia	r0!, {r2,r3} ");	// TTBR1, TTBCR
	asm("mrc	p15, 0, r3, c3, c0, 0 ");
	asm("str	r3, [r0], #4 ");	// DACR
#ifdef __CPU_MEMORY_TYPE_REMAPPING
	asm("mrc	p15, 0, r2, c10, c2, 0 ");
	asm("mrc	p15, 0, r3, c10, c2, 1 ");
#else
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
#endif
	asm("stmia	r0!, {r2,r3} ");	// PRRR, NMRR
#ifdef __CPU_ARMV7
	asm("mrc	p15, 0, r3, c12, c0, 0 ");
#else
	asm("mov	r3, #0 ");
#endif
	asm("str	r3, [r0], #4 ");	// VBAR
#if defined(__CPU_SA1) || defined(__CPU_ARM920T) || defined(__CPU_ARM925T) || defined(__CPU_ARMV5T) || defined(__CPU_ARMV6) || defined(__CPU_ARMV7)
	asm("mrc	p15, 0, r3, c13, c0, 0 ");
#else
	asm("mov	r3, #0 ");
#endif
	asm("str	r3, [r0], #4 ");	// FCSEIDR
#if defined(__CPU_ARMV6) || defined(__CPU_ARMV7)
	asm("mrc	p15, 0, r3, c13, c0, 1 ");
#else
	asm("mov	r3, #0 ");
#endif
	asm("str	r3, [r0], #4 ");	// CONTEXTIDR
#ifdef __CPU_HAS_CP15_THREAD_ID_REG
	GET_RWRW_TID(,r2);
	GET_RWRO_TID(,r3);
	GET_RWNO_TID(,r12);
#else
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
	asm("mov	r12, #0 ");
#endif
	asm("stmia	r0!, {r2,r3,r12} ");	// RWRWTID, RWROTID, RWNOTID
	asm("mrc	p15, 0, r2, c5, c0, 0 ");	// DFSR
#ifdef __CPU_ARM_HAS_SPLIT_FSR
	asm("mrc	p15, 0, r3, c5, c0, 1 ");	// IFSR
#else
	asm("mov	r3, #0 ");
#endif
	asm("stmia	r0!, {r2,r3} ");	// DFSR, IFSR
#ifdef __CPU_ARMV7
	asm("mrc	p15, 0, r2, c5, c1, 0 ");	// ADFSR
	asm("mrc	p15, 0, r3, c5, c1, 1 ");	// AIFSR
#else
	asm("mov	r2, #0 ");
	asm("mov	r3, #0 ");
#endif
	asm("stmia	r0!, {r2,r3} ");	// ADFSR, AIFSR
	asm("mrc	p15, 0, r2, c6, c0, 0 ");	// DFAR
#ifdef __CPU_ARM_HAS_CP15_IFAR
	asm("mrc	p15, 0, r3, c6, c0, 2 ");	// IFAR
#else
	asm("mov	r3, #0 ");
#endif
	asm("stmia	r0!, {r2,r3} ");	// DFAR, IFAR

	// zero spare words
	asm("stmia	r0!, {r4-r7} ");
	asm("stmia	r0!, {r4-r11} ");
#else	// __CPU_HAS_MMU
	asm("stmia	r0!, {r4-r11} ");	// no MMU so zero fill
	asm("stmia	r0!, {r4-r11} ");	// no MMU so zero fill
	asm("stmia	r0!, {r4-r11} ");	// no MMU so zero fill
	asm("stmia	r0!, {r4-r11} ");	// no MMU so zero fill
#endif	// __CPU_HAS_MMU

	// zero iB[1]
	asm("stmia	r0!, {r4-r11} ");
	asm("stmia	r0!, {r4-r11} ");
	asm("stmia	r0!, {r4-r11} ");
	asm("stmia	r0!, {r4-r11} ");	// r0 = &a.iMore[0]
	asm("add	r1, r0, #62*8 ");	// r1 = &a.iExcCode

	// Save VFP state
	// Save order:
	//				FPEXC	FPSCR
	// VFPv2 ONLY:	FPINST	FPINST2
	//				D0-D3	D4-D7	D8-D11	D12-D15 
	// VFPv3 ONLY:	D16-D19	D20-D23	D24-D27	D28-D31
#ifdef __CPU_HAS_VFP
	GET_CAR(,r2);
	asm("bic	r2, r2, #0x00f00000 ");
#ifdef __VFP_V3
	asm("bic	r2, r2, #0xc0000000 ");	// mask off ASEDIS, D32DIS
#endif
	asm("orr	r2, r2, #0x00500000 ");	// enable privileged access to CP10, CP11
	SET_CAR(,r2);
	VFP_FMRX(,2,VFP_XREG_FPEXC);		// r2=FPEXC
	asm("orr	r3, r2, #%a0" : : "i" ((TInt)VFP_FPEXC_EN));
	VFP_FMXR(,VFP_XREG_FPEXC,3);		// enable VFP
	__DATA_SYNC_BARRIER__(r4);
	__INST_SYNC_BARRIER__(r4);
	VFP_FMRX(,3,VFP_XREG_FPSCR);		// r3=FPSCR
	asm("stmia	r0!, {r2,r3} ");		//
#ifdef __VFP_V3
	VFP_FSTMIADW(CC_AL,0,0,16);			// save D0 - D15
	VFP_FMRX(,3,VFP_XREG_MVFR0);
	asm("tst r3, #%a0" : : "i" ((TInt)VFP_MVFR0_ASIMD32)); // Check to see if all 32 Advanced SIMD registers are present
	VFP_FSTMIADW(CC_NE,0,16,16);		// if so then save D16 - D31 (don't need to check CPACR.D32DIS as it is cleared above)
#else
	VFP_FMRX(,2,VFP_XREG_FPINST);
	VFP_FMRX(,3,VFP_XREG_FPINST2);
	asm("stmia	r0!, {r2,r3} ");		// FPINST, FPINST2
	VFP_FSTMIADW(CC_AL,0,0,16);			// save D0 - D15
#endif
#endif	// __CPU_HAS_VFP
	asm("1:		");
	asm("cmp	r0, r1 ");
	asm("strlo	r4, [r0], #4 ");		// clear up to end of iMore[61]
	asm("blo	1b ");
	asm("mov	r1, #%a0" : : "i" ((TInt)KMaxTInt));
	asm("stmia	r0!, {r1,r5-r7} ");		// iExcCode=KMaxTInt, iCrashArgs[0...2]=0
	asm("sub	r0, r0, #1024 ");		// r0 = &a
#ifdef __CPU_HAS_VFP
	asm("ldr	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iMore[0]));
	VFP_FMXR(,VFP_XREG_FPEXC,2);		// restore FPEXC
	__DATA_SYNC_BARRIER__(r4);
	__INST_SYNC_BARRIER__(r4);
	asm("ldr	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iA.iCPACR));
	SET_CAR(,r2);						// restore CPACR
#endif
	asm("ldr	r1, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iFlags));
	asm("orr	r1, r1, #0xC0 ");		// interrupts off
	asm("msr	cpsr, r1 ");			// restore CPSR with interrupts off
	asm("ldmia	r0, {r0-r11} ");		// restore R4-R11
	__JUMP(,lr);
	}


/** Update the saved ARM registers with information from an exception

@internalTechnology
*/
__NAKED__ void Arm::UpdateState(SFullArmRegSet&, TArmExcInfo&)
	{
	asm("ldmia	r1!, {r2,r3,r12} ");
	asm("str	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iFlags));
	asm("str	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iExcCode));
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR13Svc));
	asm("ldmia	r1!, {r2,r3,r12} ");
	asm("str	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR4));
	asm("str	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR5));
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR6));
	asm("ldmia	r1!, {r2,r3,r12} ");
	asm("str	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR7));
	asm("str	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR8));
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR9));
	asm("ldmia	r1!, {r2,r3,r12} ");
	asm("str	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR10));
	asm("str	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR11));
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR14Svc));
	asm("ldr	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iExcCode));
	asm("ldmia	r1!, {r2,r3} ");	// r2=iFaultAddress, r3=iFaultStatus
	asm("cmp	r12, #%a0 " : : "i" ((TInt)EArmExceptionPrefetchAbort));
	asm("streq	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iB[0].iIFAR));
	asm("strne	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iB[0].iDFAR));
	asm("streq	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iB[0].iIFSR));
	asm("strne	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iB[0].iDFSR));
	asm("ldmia	r1!, {r2,r3,r12} ");
	asm("str	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iSpsrSvc));
	asm("str	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR13));
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR14));
	asm("ldmia	r1!, {r2,r3,r12} ");
	asm("str	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR0));
	asm("str	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR1));
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR2));
	asm("ldmia	r1!, {r2,r3,r12} ");
	asm("str	r2, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR3));
	asm("str	r3, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR12));
	asm("str	r12, [r0, #%a0]" : : "i" _FOFF(SFullArmRegSet,iN.iR15));
	__JUMP(,lr);
	}


/** Get a pointer to a stored integer register, accounting for registers which
	are banked across modes.

@param	a		Pointer to saved register block
@param	aRegNum	Number of register required, 0-15 or -1 (indicates SPSR)
@param	aMode	Bottom 5 bits indicate which processor mode
				Other bits of aMode are ignored
@return			Pointer to the required saved register value

@internalTechnology
*/
__NAKED__ TArmReg* Arm::Reg(SFullArmRegSet& /*a*/, TInt /*aRegNum*/, TArmReg /*aMode*/)
	{
	asm("cmp	r1, #8 ");				// register number < 8 ?
	asm("addlo	r0, r0, r1, lsl #2 ");	// register R0-R7 are not banked
	asm("blo	0f ");
	asm("cmp	r1, #15 ");				// register number = 15 ?
	asm("addeq	r0, r0, r1, lsl #2 ");	// register R15 not banked
	asm("movgt	r0, #0 ");				// no registers > 15
	asm("bge	0f ");
	asm("cmn	r1, #1 ");
	asm("movlt	r0, #0 ");				// no registers < -1
	asm("blt	0f ");
	asm("and	r12, r2, #0x1F ");
	asm("cmp	r12, #0x11 ");			// mode_fiq?
	asm("beq	1f ");					// skip if it is
	asm("cmp	r1, #13 ");
	asm("addlo	r0, r0, r1, lsl #2 ");	// register R8-R12 are only banked in mode_fiq
	asm("blo	0f ");
	asm("cmp	r12, #0x10 ");			// mode_usr ?
	asm("cmpne	r12, #0x1F ");			// if not, mode_sys ?
	asm("bne	2f ");					// skip if neither
	asm("cmp	r1, #16 ");
	asm("addlo	r0, r0, r1, lsl #2 ");	// handle R13_usr, R14_usr
	asm("movhs	r0, #0 ");				// no SPSR in mode_usr or mode_sys
	asm("blo	0f ");
	asm("1: ");							// mode_fiq, regnum = 8-12
	asm("2: ");							// exception mode, regnum not 0-12 or 15
	asm("cmn	r1, #1 ");				// regnum = -1 ?
	asm("moveq	r1, #15 ");				// if so, change to 15
	asm("sub	r1, r1, #13 ");
	asm("add	r0, r0, r1, lsl #2 ");	// add 0 for R13, 4 for R14, 8 for SPSR
	asm("cmp	r12, #0x16 ");
	asm("addeq	r0, r0, #12 ");			// if mon, add offset from R13Fiq to R13Mon
	asm("cmpne	r12, #0x11 ");
	asm("addeq	r0, r0, #32 ");			// if valid but not svc/abt/und/irq, add offset from R13Irq to R13Fiq
	asm("cmpne	r12, #0x12 ");
	asm("addeq	r0, r0, #12 ");			// if valid but not svc/abt/und, add offset from R13Und to R13Irq
	asm("cmpne	r12, #0x1b ");
	asm("addeq	r0, r0, #12 ");			// if valid but not svc/abt, add offset from R13Abt to R13Und
	asm("cmpne	r12, #0x17 ");
	asm("addeq	r0, r0, #12 ");			// if valid but not svc, add offset from R13Svc to R13Abt
	asm("cmpne	r12, #0x13 ");
	asm("addeq	r0, r0, #%a0" : : "i" _FOFF(SFullArmRegSet, iN.iR13Svc));	// if valid mode add offset to R13Svc
	asm("movne	r0, #0 ");
	asm("0: ");
	__JUMP(,lr);
	}


/** Restore all the ARM registers

@internalTechnology
*/
__NAKED__ void Arm::RestoreState(SFullArmRegSet&)
	{
	}