// Copyright (c) 1995-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\euser\epoc\arm\uc_utl.cia
//
//
#include <e32cia.h>
#include <u32std.h>
#include <e32base.h>
#include <e32rom.h>
#include <e32svr.h>
#include <e32hashtab.h>
#include <u32exec.h>
#include "uc_std.h"
#if defined(__MEM_MACHINE_CODED__)
EXPORT_C __NAKED__ void Mem::Swap(TAny* /*aPtr1*/, TAny* /*aPtr2*/, TInt /*aLength*/)
/**
Swaps a number of bytes of data between two specified locations.
The source and target areas can overlap.
@param aPtr1 A pointer to the first location taking part in the swap.
@param aPtr2 A pointer to second location taking part in the swap.
@param aLength The number of bytes to be swapped between the two locations.
This value must not be negative.
@panic USER 94 In debug builds only, if aLength is negative.
*/
{
asm(" cmp r0,r1");
asm(" cmpne r2,#0");
__JUMP(eq,lr);
//
// Test for same alignment, if more than 16 bytes to swap
//
asm(" and r3,r0,#3");
asm(" and ip,r1,#3");
asm(" cmp r2,#16");
asm(" addlt r3,r3,#4");
asm(" cmp r3,ip");
asm(" beq same_aligned_swap");
asm(" stmfd sp!,{r4,lr}");
asm("swap_loop:");
asm(" ldrb r3,[r0]");
asm(" ldrb r4,[r1]");
asm(" strb r3,[r1],#1");
asm(" strb r4,[r0],#1");
asm(" subs r2,r2,#1");
asm("beq swap_exit1 ");
asm(" ldrb r3,[r0]");
asm(" ldrb r4,[r1]");
asm(" strb r3,[r1],#1");
asm(" strb r4,[r0],#1");
asm(" subs r2,r2,#1");
asm("beq swap_exit1 ");
asm(" ldrb r3,[r0]");
asm(" ldrb r4,[r1]");
asm(" strb r3,[r1],#1");
asm(" strb r4,[r0],#1");
asm(" subs r2,r2,#1");
asm("beq swap_exit1 ");
asm(" ldrb r3,[r0]");
asm(" ldrb r4,[r1]");
asm(" strb r3,[r1],#1");
asm(" strb r4,[r0],#1");
asm(" subs r2,r2,#1");
asm(" bne swap_loop");
asm("swap_exit1: ");
__POPRET("r4,");
asm("same_aligned_swap:");
asm(" stmfd sp!,{r4-r10,lr}");
//
// r3 contains the byte offset from word alignment, 0,1,2 or 3
// subtract 1 to get -1,0,1 or 2, and if -1 make it 3
// that gives us 0,1,2 or 3 if the alignment is 3,2,1 or 0 respectively
// We can use that to jump directly to the appropriate place for
// swapping the relevent number of bytes to achieve word alignment
// r4 is set to 3-r3 to correct the length for the number of bytes
// swapped
//
asm(" subs r3,r3,#1");
asm(" movmi r3,#3");
asm(" rsb r4,r3,#3");
asm(" sub r2,r2,r4");
asm(" add pc,pc,r3,asl #4");
asm(" nop "); // never executed
//
// Jumps here if 3 bytes to swap before word aligned
//
asm(" ldrb r4,[r0]");
asm(" ldrb ip,[r1]");
asm(" strb r4,[r1],#1");
asm(" strb ip,[r0],#1");
//
// Jumps here if 2 bytes to swap before word aligned
//
asm(" ldrb r4,[r0]");
asm(" ldrb ip,[r1]");
asm(" strb r4,[r1],#1");
asm(" strb ip,[r0],#1");
//
// Jumps here if 1 byte to swap before word aligned
//
asm(" ldrb r4,[r0]");
asm(" ldrb ip,[r1]");
asm(" strb r4,[r1],#1");
asm(" strb ip,[r0],#1");
//
// We are now word aligned. Fast swapping, here we come...
//
asm("word_aligned_swap:");
asm(" movs ip,r2,lsr #6"); // Number of 64 blocks to swap
asm(" beq its_smaller_swap");
asm("swap_64_bytes:");
asm(" ldmia r1,{r3-r6}");
asm(" ldmia r0,{r7-r10}");
asm(" stmia r1!,{r7-r10}");
asm(" stmia r0!,{r3-r6}");
asm(" ldmia r1,{r3-r6}");
asm(" ldmia r0,{r7-r10}");
asm(" stmia r1!,{r7-r10}");
asm(" stmia r0!,{r3-r6}");
asm(" ldmia r1,{r3-r6}");
asm(" ldmia r0,{r7-r10}");
asm(" stmia r1!,{r7-r10}");
asm(" stmia r0!,{r3-r6}");
asm(" ldmia r1,{r3-r6}");
asm(" ldmia r0,{r7-r10}");
asm(" stmia r1!,{r7-r10}");
asm(" stmia r0!,{r3-r6}");
asm(" subs ip,ip,#1");
asm(" bne swap_64_bytes");
//
// Less than 64 bytes to go...
//
asm("its_smaller_swap:");
asm(" ands r2,r2,#63");
asm("beq swap_exit2 ");
asm(" cmp r2,#4");
asm(" blt finish_swap");
asm("final_swap_loop:");
asm(" ldr r3,[r1]");
asm(" ldr ip,[r0]");
asm(" str r3,[r0],#4");
asm(" str ip,[r1],#4");
asm(" subs r2,r2,#4");
asm(" cmp r2,#4");
asm(" bge final_swap_loop");
//
// Less than 4 bytes to go...
//
asm("finish_swap:");
asm(" tst r2,#2");
asm(" ldrneb r3,[r0]");
asm(" ldrneb ip,[r1]");
asm(" strneb r3,[r1],#1");
asm(" strneb ip,[r0],#1");
asm(" ldrneb r3,[r0]");
asm(" ldrneb ip,[r1]");
asm(" strneb r3,[r1],#1");
asm(" strneb ip,[r0],#1");
asm(" tst r2,#1");
asm(" ldrneb r3,[r0]");
asm(" ldrneb ip,[r1]");
asm(" strneb r3,[r1],#1");
asm(" strneb ip,[r0],#1");
asm("swap_exit2: ");
__POPRET("r4-r10,");
}
#endif
#ifdef __REGIONS_MACHINE_CODED__
__NAKED__ GLDEF_C void AllocAnotherRect( TRegion * /*aRegion*/ )
{
// Returns with Z flag set to indicate error
asm("ldr r1, [r0, #4] "); // r1=iError
asm("cmp r1, #0 ");
asm("bne return_error ");
asm("ldr r1, [r0, #8] "); // r1=iAllocedRects
asm("ldr r12, [r0] "); // r12=iCount
asm("tst r1, #0x40000000 "); // test ERRegionBuf
asm("beq allocanother1 "); // don't branch if TRegionFix
asm("cmn r1, r12 "); // test if iCount==-iAllocedRects
__JUMP(ne,lr);
asm("b " CSM_ZN7TRegion10ForceErrorEv); // if so, ForceError()
asm("allocanother1: ");
asm("cmp r1, #0 ");
asm("bpl allocanother3 "); // branch if RRegion, continue if RRegionBuf
asm("orr r2, r1, #0x40000000 "); // r2=iAllocedRects|ERRegionBuf
asm("cmn r2, r12 "); // check if iCount==(-(iAllocedRects|ERRegionBuf))
__JUMP(ne,lr);
asm("ldr r2, [r0, #12] "); // r2=iGranularity
asm("add r1, r12, r2 "); // iAllocedRects=iCount+iGranularity - change into RRegion
asm("str r1, [r0, #8] ");
asm("stmfd sp!, {r0,r1,r12,lr} "); // save registers used in function call
asm("mov r0, r1, lsl #4 "); // number of bytes to allocate
asm("bl " CSM_ZN4User5AllocEi); // User::Alloc
asm("movs r2, r0 "); // returned pointer into r2
asm("ldmfd sp!, {r0,r1,r12,lr} "); // restore registers
asm("add r3, r0, #20 "); // r3=address of first rectangle
asm("str r2, [r0, #16] "); // iRectangleList=returned pointer
asm("beq " CSM_ZN7TRegion10ForceErrorEv); // if pointer null, ForceError()
asm("cmp r12, #0 ");
asm("beq return_success ");
asm("stmfd sp!, {r4,r5} ");
asm("allocanother2: ");
asm("ldmia r3!, {r0,r1,r4,r5} "); // copy data to new area
asm("subs r12, r12, #1 ");
asm("stmia r2!, {r0,r1,r4,r5} ");
asm("bne allocanother2 ");
asm("ldmfd sp!, {r4,r5} ");
asm("return_success: ");
asm("movs r0, #1 "); // clear Z flag to indicate success
__JUMP(,lr);
asm("allocanother3: "); // come here if RRegion
asm("cmp r1, r12 "); // check if iCount==iAllocedRects
__JUMP(ne,lr);
asm("ldr r2, [r0, #12] "); // r2 = iGranularity
asm("add r1, r1, r2 "); // iAllocedRects+=iGranularity
asm("str r1, [r0, #8] ");
asm("stmfd sp!, {r0,lr} "); // preserve r0,lr across function call
asm("ldr r0, [r0, #16] "); // r0=address of current cell
asm("mov r1, r1, lsl #4 "); // r1=number of bytes to allocate
asm("mov r2, #0 ");
asm("bl " CSM_ZN4User7ReAllocEPvii); // User::ReAlloc
asm("movs r2, r0 "); // returned pointer into r2
asm("ldmfd sp!, {r0,lr} "); // restore r0,lr
asm("strne r2, [r0, #16] "); // if returned ptr not null, iRectangleList=returned ptr
__JUMP(ne,lr);
asm("b " CSM_ZN7TRegion10ForceErrorEv); // else ForceError()
}
__NAKED__ EXPORT_C void TRegion::ForceError()
{
// Returns with Z flag set to indicate error
asm("stmfd sp!, {r0,lr} ");
asm("bl " CSM_ZN7TRegion5ClearEv); // Clear()
asm("ldmfd sp!, {r0,lr} "); // restore r0,lr
asm("mov r1, #1 ");
asm("str r1, [r0, #4] "); // iError=ETrue
asm("return_error: ");
asm("movs r0, #0 "); // set Z flag to indicate error
__JUMP(,lr);
}
__NAKED__ EXPORT_C TRect TRegion::BoundingRect() const
/**
Gets the minimal rectangle that bounds the entire region.
@return The region's minimal bounding rectangle.
*/
{
asm("ldr r2, [r1] "); // r2=iCount
asm("cmp r2, #0 "); // list empty?
asm("beq boundingrect0 "); // branch if empty
asm("ldr r3, [r1, #8] "); // if not empty, r3 points to first rectangle
asm("stmfd sp!, {r4-r8,lr} ");
asm("cmn r3, r3 ");
asm("ldrcc r3, [r1, #16] "); // if RRegion
asm("addcs r3, r1, #20 "); // RRegionBuf
asm("submi r3, r3, #8 "); // TRegionFix
asm("ldmia r3!, {r4-r7} "); // if not empty bounds = first rectangle
asm("b boundingrect2 "); // if not empty go and check rest of list
asm("boundingrect1: ");
asm("ldmia r3!, {r1,r8,r12,lr} "); // fetch next rectangle
asm("cmp r1, r4 "); // if next.iTl.iX<bounds.iTl.iX
asm("movlt r4, r1 "); // bounds.iTl.iX=next.iTl.iX
asm("cmp r8, r5 "); // if next.iTl.iY<bounds.iTl.iY
asm("movlt r5, r8 "); // bounds.iTl.iY=next.iTl.iY
asm("cmp r12, r6 "); // if next.iBr.iX>bounds.iBr.iX
asm("movgt r6, r12 "); // bounds.iBr.iX=next.iBr.iX
asm("cmp lr, r7 "); // if next.iBr.iY>bounds.iBr.iY
asm("movgt r7, lr "); // bounds.iBr.iY=next.iBr.iY
asm("boundingrect2: ");
asm("subs r2, r2, #1 "); // decrement count
asm("bne boundingrect1 "); // repeat for all rectangles
asm("stmia r0, {r4-r7} "); // store result
__POPRET("r4-r8,");
asm("boundingrect0: ");
asm("mov r1, #0 "); // if list empty, bounds = 0,0,0,0
asm("mov r3, #0 ");
asm("mov r12, #0 ");
asm("stmia r0, {r1,r2,r3,r12} "); // store result
__JUMP(,lr);
}
__NAKED__ EXPORT_C TBool TRegion::IsContainedBy(const TRect & /*aRect*/) const
/**
Tests whether the region is fully enclosed within the specified rectangle.
@param aRect The specified rectangle.
@return True, if the region is fully enclosed within the rectangle (their sides
may touch); false, otherwise.
*/
{
asm("ldr r12, [r0, #8] "); // r12 points to first rectangle
asm("stmfd sp!, {r4-r7,lr} ");
asm("cmn r12, r12 ");
asm("ldrcc r12, [r0, #16] "); // if RRegion
asm("addcs r12, r0, #20 "); // RRegionBuf
asm("submi r12, r12, #8 "); // TRegionFix
asm("ldr r0, [r0] "); // r0=iCount
asm("ldmia r1, {r4-r7} "); // aRect coordinates into r4-r7
asm("subs r0, r0, #1 "); // decrement it
asm("bmi iscontainedby2 "); // if iCount was zero, return TRUE
asm("iscontainedby1: ");
asm("ldmia r12!, {r1,r2,r3,lr} "); // coordinates of next rectangle
asm("cmp r1, r4 "); // compare next.iTl.iX with aRect.iTl.iX
asm("cmpge r2, r5 "); // if >=, compare next.iTl.iY with aRect.iTl.iY
asm("cmpge r6, r3 "); // if >=, compare aRect.Br.iX with next.iBr.iX
asm("cmpge r7, lr "); // if >=, compare aRect.Br.iY with next.iBr.iY
asm("subges r0, r0, #1 "); // if >=, next is contained in aRect, so iterate
asm("bge iscontainedby1 "); // will drop through if r0<0 or if next exceeds aRect
asm("iscontainedby2: ");
asm("mov r0, r0, lsr #31 "); // return 1 if r0<0, 0 if r0>=0
__POPRET("r4-r7,");
}
__NAKED__ EXPORT_C void TRegion::Copy(const TRegion & /*aRegion*/)
/**
Copies another region to this region.
The state of the specified region's error flag is also copied.
@param aRegion The region to be copied.
*/
{
asm("ldr r2, [r1, #4] "); // r2 = aRegion.iError
asm("cmp r2, #0 ");
asm("bne " CSM_ZN7TRegion10ForceErrorEv); // if (aRegion.iError) ForceError();
asm("ldr r2, [r1] "); // r1 = aRegion.iCount
asm("cmp r2, #0 ");
asm("beq " CSM_ZN7TRegion5ClearEv); // region to copy is empty so simply clear our buffer
asm("stmfd sp!, {r0,r1,r4,r5,r6,lr} "); // preserve r0,r1,lr across function calls
asm("mov r4, r1 ");
asm("mov r5, r0 ");
asm("ldr r2, [r0, #4] "); // r2 = iError
asm("cmp r2, #0 ");
asm("blne " CSM_ZN7TRegion5ClearEv); // if (iError) Clear();
asm("mov r0, r5 ");
asm("ldr r1, [r4] "); // r1 = aRegion.iCount, r0 = this
asm("bl " CSM_ZN7TRegion11SetListSizeEi); // SetListSize(aRegion.iCount);
asm("cmp r0, #0 ");
asm("beq copyregion_end ");
asm("ldr r3, [r4] "); // r3 = aRegion.iCount
asm("cmp r3, #0 ");
asm("str r3, [r5] "); // iCount=aRegion.iCount
asm("beq copyregion_end ");
asm("ldr r0, [r5, #8] "); // r0 points to first rectangle
asm("cmn r0, r0 ");
asm("ldrcc r0, [r5, #16] "); // if RRegion
asm("addcs r0, r5, #20 "); // RRegionBuf
asm("submi r0, r0, #8 "); // TRegionFix
asm("ldr r1, [r4, #8] "); // r1 points to first rectangle
asm("cmn r1, r1 ");
asm("ldrcc r1, [r4, #16] "); // if RRegion
asm("addcs r1, r4, #20 "); // RRegionBuf
asm("submi r1, r1, #8 "); // TRegionFix
asm("copyregion1: ");
asm("ldmia r1!, {r2,r4,r5,r12} "); // copy aRegion.iRectangleList to iRectangleList
asm("subs r3, r3, #1 ");
asm("stmia r0!, {r2,r4,r5,r12} ");
asm("bne copyregion1 ");
asm("copyregion_end: ");
__POPRET("r0,r1,r4,r5,r6,");
}
__NAKED__ EXPORT_C void TRegion::Offset(const TPoint & /*anOffset*/)
/**
Moves the region by adding a TPoint offset to the co-ordinates of its corners.
The size of the region is not changed.
@param aOffset The offset by which the region is moved. The region is moved
horizontally by aOffset.iX pixels and vertically by aOffset.iY pixels.
*/
{
asm("ldmia r1, {r1,r2} "); // r1=anOffset.iX, r2=anOffset.iY
// fall through...
}
__NAKED__ EXPORT_C void TRegion::Offset(TInt /*xOffset*/,TInt /*yOffset*/)
/**
Moves the region by adding X and Y offsets to the co-ordinates of its corners.
The size of the region is not changed.
@param aXoffset The number of pixels by which to move the region horizontally.
If negative, the region moves leftwards.
@param aYoffset The number of pixels by which to move the region vertically.
If negative, the region moves upwards.
*/
{
asm("ldr r12, [r0] "); // r12=iCount
asm("cmp r12, #0 ");
__JUMP(eq,lr);
asm("ldr r3, [r0, #8] "); // r0 points to first rectangle
asm("cmn r3, r3 ");
asm("ldrcc r0, [r0, #16] "); // if RRegion
asm("addcs r0, r0, #20 "); // RRegionBuf
asm("submi r0, r0, #8 "); // TRegionFix
asm("stmfd sp!, {r4,r5,lr} ");
asm("offsetregion2: ");
asm("ldmia r0, {r3-r5,lr} "); // r3-r5,lr = next rectangle coordinates
asm("subs r12, r12, #1 ");
asm("add r3, r3, r1 "); // Tl.iX += anOffset.iX
asm("add r4, r4, r2 "); // Tl.iY += anOffset.iY
asm("add r5, r5, r1 "); // Br.iX += anOffset.iX
asm("add lr, lr, r2 "); // Br.iY += anOffset.iY
asm("stmia r0!, {r3-r5,lr} "); // store new coordinates
asm("bne offsetregion2 ");
__POPRET("r4,r5,");
}
__NAKED__ EXPORT_C TBool TRegion::Contains(const TPoint & /*aPoint*/) const
/**
Tests whether a point is located within the region.
If the point is located on the top or left hand side of any rectangle in the
region, it is considered to be within that rectangle and within the region.
If the point is located on the right hand side or bottom of a rectangle, it
is considered to be outside that rectangle, and may be outside the region.
@param aPoint The specified point.
@return True, if the point is within the region; false, otherwise.
*/
{
asm("ldr r12, [r0] "); // r12 = iCount
asm("stmfd sp!, {r4,r5,lr} ");
asm("cmp r12, #0 ");
asm("beq contains0 "); // if iCount=0, return FALSE
asm("ldr r3, [r0, #8] "); // r0 points to first rectangle
asm("cmn r3, r3 ");
asm("ldrcc r0, [r0, #16] "); // if RRegion
asm("addcs r0, r0, #20 "); // RRegionBuf
asm("submi r0, r0, #8 "); // TRegionFix
asm("ldmia r1, {r1, r2} "); // r1=aPoint.iX, r2=aPoint.iY
asm("contains1: ");
asm("ldmia r0!, {r3-r5,lr} "); // coordinates of next rectangle into r3-r5,lr
asm("cmp r3, r1 "); // compare next.iTl.iX with aPoint.iX
asm("cmple r4, r2 "); // if <=, compare next.iTl.iY with aPoint.iY
asm("bgt contains2 "); // if >, aPoint is not contained in rectangle, so iterate
asm("cmp r1, r5 "); // compare aPoint.iX with next.iBr.iX
asm("cmplt r2, lr "); // if <, compare aPoint.iY with next.iBr.iY
asm("contains2: ");
asm("subges r12, r12, #1 "); // if >=, aPoint is not contained in rect, so iterate
asm("bgt contains1 ");
asm("cmp r12, #0 ");
asm("movne r0, #1 "); // if r12 non-zero, return TRUE else FALSE
asm("contains0: ");
asm("moveq r0, #0 ");
__POPRET("r4,r5,");
}
__NAKED__ EXPORT_C TBool TRegion::Intersects(const TRect &/*aRect*/) const
/**
Tests whether where there is any intersection between this region and the specified rectangle.
@param aRect The specified rectangle.
@return True, if there is an intersection; false, otherwise.
*/
{
asm("ldr r12, [r0] "); // r12 = iCount
asm("stmfd sp!, {r4-r7,lr} ");
asm("cmp r12, #0 ");
asm("beq intersects0 "); // if iCount=0, return FALSE
asm("ldr lr, [r0, #8] "); // r0 points to first rectangle
asm("ldmia r1, {r1-r4} "); // (load aRect into r1 - r4)
asm("cmn lr, lr ");
asm("ldrcc r0, [r0, #16] "); // if RRegion
asm("addcs r0, r0, #20 "); // RRegionBuf
asm("submi r0, r0, #8 "); // TRegionFix
asm("cmp r1, r3 "); // check if aRect is empty
asm("cmplt r2, r4 ");
asm("bge intersects0 ");
asm("intersects1: ");
asm("ldmia r0!, {r5-r7,lr} "); // coordinates of next rectangle into r5-r7,lr
asm("cmp r1, r7 "); // check if they intersect
asm("cmplt r2, lr ");
asm("cmplt r5, r3 ");
asm("cmplt r6, r4 ");
asm("subges r12, r12, #1 "); // if not then decrement and loop
asm("bgt intersects1 ");
asm("intersects0: ");
asm("movge r0, #0 ");
asm("movlt r0, #1 ");
__POPRET("r4-r7,");
}
__NAKED__ void TRegion::DeleteRect(TRect * /*aRect*/)
//
// Delete a specific rectangle in the list.
//
{
asm("ldr r12, [r0] "); // r12=iCount
asm("ldr r3, [r0, #8] "); // r0 points to first rectangle
asm("subs r12, r12, #1 "); // decrement it
asm("str r12, [r0] "); // iCount--;
asm("cmn r3, r3 ");
asm("ldrcc r0, [r0, #16] "); // if RRegion
asm("addcs r0, r0, #20 "); // RRegionBuf
asm("submi r0, r0, #8 "); // TRegionFix
asm("sub r2, r1, r0 "); // r2=offset of aRect from iRectangleList
asm("subs r12, r12, r2, lsr #4 "); // r12 now equals number of rectangles requiring moving
__JUMP(eq,lr);
asm("add r0, r1, #16 "); // r0 = aRect+1
asm("stmfd sp!, {r4,lr} ");
asm("deleterect1: ");
asm("ldmia r0!, {r2-r4,lr} "); // move rectangles following aRect back by one place
asm("subs r12, r12, #1 ");
asm("stmia r1!, {r2-r4,lr} ");
asm("bne deleterect1 ");
__POPRET("r4,");
}
__NAKED__ EXPORT_C void TRegion::ClipRect(const TRect & /*aRect*/)
/**
Clips the region to the specified rectangle.
The resulting region is the area of overlap between the region and the rectangle.
If there is no overlap, all rectangles within this region are deleted and
the resulting region is empty.
@param aRect The rectangle to which this region is to be clipped.
*/
// Can not fail.
{
asm("ldr r12, [r0] "); // r12=iCount
asm("cmp r12, #0 ");
__JUMP(eq,lr);
asm("stmfd sp!, {r4-r10,lr} ");
asm("ldmia r1, {r2-r5} "); // get coordinates of aRect into r2-r5
asm("ldr r1, [r0, #8] "); // r1 points to first rectangle
asm("cmn r1, r1 ");
asm("ldrcc r1, [r0, #16] "); // if RRegion
asm("addcs r1, r0, #20 "); // RRegionBuf
asm("submi r1, r1, #8 "); // TRegionFix
asm("cliprect1: ");
asm("ldmia r1!, {r6-r9} "); // next rectangle coordinates into r6-r9
asm("cmp r6, r2 "); // clip the rectangle to aRect
asm("movlt r6, r2 ");
asm("strlt r2, [r1, #-16] ");
asm("cmp r7, r3 ");
asm("movlt r7, r3 ");
asm("strlt r3, [r1, #-12] ");
asm("cmp r8, r4 ");
asm("movgt r8, r4 ");
asm("strgt r4, [r1, #-8] ");
asm("cmp r9, r5 ");
asm("movgt r9, r5 ");
asm("strgt r5, [r1, #-4] ");
asm("cmp r6, r8 "); // check if clipped rect is empty
asm("cmplt r7, r9 "); // empty if r6>=r8 or r7>=r9
asm("bge cliprect_delete "); // if empty, branch to other loop to delete rect
asm("subs r12, r12, #1 "); // decrement loop counter
asm("bne cliprect1 "); // loop if any more rectangles to do
__POPRET("r4-r10,");
asm("cliprect_delete: "); // (enter loop here)
asm("ldr lr, [r0] "); // lr=iCount, updateed if we delete rects
asm("sub r10, r1, #16 "); // r1 -> next rect, r10 -> previous deleted rect
asm("subs r12, r12, #1 "); // decrement loop counter
asm("beq cliprect_move_end ");
asm("cliprect_move: ");
asm("ldmia r1!, {r6-r9} "); // next rectangle coordinates into r6-r9
asm("cmp r6, r2 "); // clip the rectangle to aRect
asm("movlt r6, r2 ");
asm("cmp r7, r3 ");
asm("movlt r7, r3 ");
asm("cmp r8, r4 ");
asm("movgt r8, r4 ");
asm("cmp r9, r5 ");
asm("movgt r9, r5 ");
asm("cmp r6, r8 "); // check if clipped rect is empty
asm("cmplt r7, r9 "); // empty if r6>=r8 or r7>=r9
asm("stmltia r10!, {r6-r9} "); // if non-empty then store the rect
asm("subge lr, lr, #1 "); // else decrement rect count
asm("subs r12, r12, #1 "); // decrement loop counter
asm("bne cliprect_move "); // loop if any more rectangles to do
asm("cliprect_move_end: ");
asm("sub lr, lr, #1 "); // decrement count for first deleted rect
asm("str lr, [r0] "); // store updated iCount
__POPRET("r4-r10,");
}
__NAKED__ EXPORT_C void TRegion::SubRect(const TRect& /*aRect*/,TRegion* /*aSubtractedRegion*/)
/**
Removes a rectangle from this region.
If there is no intersection between the rectangle and this region, then this
region is unaffected.
@param aRect The rectangular area to be removed from this region.
@param aSubtractedRegion A pointer to a region. If this is supplied, the
removed rectangle is added to it. By default this
pointer is NULL.
*/
{
asm("ldr r12, [r0] "); // r12=iCount=limit
asm("cmp r12, #0 ");
__JUMP(eq,lr);
asm("stmfd sp!, {r3-r11,lr} ");
asm("ldmia r1, {r4-r7} "); // r4-r7 = coordinates of aRect
asm("cmp r4, r6 "); // check if aRect is empty i.e. (r4>=r6 || r5>=r7)
asm("cmplt r5, r7 ");
asm("bge subrect_end "); // if aRect is empty nothing to do
asm("mov r3, #0 "); // r3=index
asm("subrect1: ");
asm("ldr lr, [r0, #8] "); // lr points to first rectangle
asm("cmn lr, lr ");
asm("ldrcc lr, [r0, #16] "); // if RRegion
asm("addcs lr, r0, #20 "); // RRegionBuf
asm("submi lr, lr, #8 "); // TRegionFix
asm("add lr, lr, r3, lsl #4 "); // lr=iRectangleList+index
// asm("ldmia r1, {r4-r7} "); // r4-r7 = coordinates of aRect
asm("ldmia lr, {r8-r11} "); // r8-r11 = coordinates of next rectangle in region
asm("cmp r10, r4 "); // compare next.iBr.iX with aRect.iTl.iX
asm("cmpgt r11, r5 "); // if >, compare next.iBr.iY with aRect.iTl.iY
asm("cmpgt r6, r8 "); // if >, compare aRect.iBr.iX with next.iTl.iX
asm("cmpgt r7, r9 "); // if >, compare aRect.iBr.iY with next.iTl.iY
asm("addle r3, r3, #1 "); // if empty intersection, increment index
asm("ble subrect2 "); // if <=, next and aRect have empty intersection, so skip
asm("add r4, lr, #16 "); // r4 = source pointer for copy, lr = dest
asm("ldr r5, [r0] "); // r5 = iCount
asm("sub r5, r5, #1 "); // decrement iCount
asm("str r5, [r0] ");
asm("sub r12, r12, #1 "); // decrement limit
asm("subs r5, r5, r3 "); // loop count for copy = iCount-index
asm("beq subrect4 "); // if loop count zero, skip the copy
asm("stmfd sp!, {r8,r9} "); // preserve r8,r9
asm("subrect3: ");
asm("ldmia r4!, {r6-r9} "); // remove the current rectangle
asm("stmia lr!, {r6-r9} ");
asm("subs r5, r5, #1 ");
asm("bne subrect3 ");
asm("ldmfd sp!, {r8-r9} "); // restore r8,r9
asm("subrect4: ");
asm("ldmia r1, {r4-r7} "); // restore coordinates of aRect into r4-r7
asm("cmp r7, r11 "); // compare aRect.iBr.iY with rect.iBr.iY
asm("movgt r7, r11 "); // r7=inter.iBr.iY
asm("bllt subrectapp1 "); // if <, append 1st subrectangle
asm("cmp r5, r9 "); // compare aRect.iTl.iY with rect.iTl.iY
asm("movlt r5, r9 "); // r5=inter.iTl.iY
asm("blgt subrectapp2 "); // if >, append 2nd subrectangle
asm("cmp r6, r10 "); // compare aRect.iBr.iX with rect.iBr.iX
asm("movgt r6, r10 "); // r6=inter.iBr.iX
asm("bllt subrectapp3 "); // if <, append 3rd subrectangle
asm("cmp r4, r8 "); // compare aRect.iTl.iX with rect.iTl.iX
asm("movlt r4, r8 "); // r4=inter.iTl.iX
asm("blgt subrectapp4 "); // if >, append 4th subrectangle
asm("ldr lr, [r0, #4] "); // lr=iError
asm("cmp lr, #0 "); // check for an error
asm("bne subrect_end ");
asm("cmp r2, #0 "); // check if aSubtractedRegion!=NULL
asm("blne subrectadd "); // if non-null, add inter to aSubtractedRegion
asm("subrect2: ");
asm("cmp r3, r12 "); // compare index to limit
asm("ldmltia r1, {r4-r7} "); // if index<limit, r4-r7 = coordinates of aRect
asm("blt subrect1 "); // if index<limit, loop again
asm("subrect_end: ");
__POPRET("r3-r11,");
// AppendRect(TRect(rect.iTl.iX,inter.iBr.iY,rect.iBr.iX,rect.iBr.iY))
asm("subrectapp1: ");
asm("stmfd sp!, {r0-r3,r12,lr} "); // preserve registers across function call
asm("bl " CSM_Z16AllocAnotherRectP7TRegion);
asm("ldmfd sp!, {r0-r3} ");
asm("beq subrectapp1_end "); // exit if error
asm("ldr r12, [r0] "); // r12=iCount
asm("ldr lr, [r0, #8] "); // lr points to first rectangle
asm("cmn lr, lr ");
asm("ldrcc lr, [r0, #16] "); // if RRegion
asm("addcs lr, r0, #20 "); // RRegionBuf
asm("submi lr, lr, #8 "); // TRegionFix
asm("add lr, lr, r12, lsl #4 "); // lr=&(iRectangleList[iCount])
asm("add r12, r12, #1 "); // increment iCount
asm("str r12, [r0] "); //
asm("stmia lr!, {r8} "); // append rectangle - rect.iTl.iX
asm("stmia lr!, {r7,r10,r11} "); // inter.iBr.iY, rect.iBr.iX, rect.iBr.iY
asm("subrectapp1_end: ");
__POPRET("r12,");
// AppendRect(TRect(rect.iTl.iX,rect.iTl.iY,rect.iBr.iX,inter.iTl.iY))
asm("subrectapp2: ");
asm("stmfd sp!, {r0-r3,r12,lr} "); // preserve registers across function call
asm("bl " CSM_Z16AllocAnotherRectP7TRegion);
asm("ldmfd sp!, {r0-r3} ");
asm("beq subrectapp1_end "); // exit if error
asm("ldr r12, [r0] "); // r12=iCount
asm("ldr lr, [r0, #8] "); // lr points to first rectangle
asm("cmn lr, lr ");
asm("ldrcc lr, [r0, #16] "); // if RRegion
asm("addcs lr, r0, #20 "); // RRegionBuf
asm("submi lr, lr, #8 "); // TRegionFix
asm("add lr, lr, r12, lsl #4 "); // lr=&(iRectangleList[iCount])
asm("add r12, r12, #1 "); // increment iCount
asm("str r12, [r0] "); //
asm("stmia lr!, {r8,r9,r10} "); // append rectangle - rect.iTl.iX,rect.iTl.iY,rect.iBr.iX
asm("stmia lr!, {r5} "); // inter.iTl.iY
__POPRET("r12,");
// AppendRect(TRect(inter.iBr.iX,inter.iTl.iY,rect.iBr.iX,inter.iBr.iY))
asm("subrectapp3: ");
asm("stmfd sp!, {r0-r3,r12,lr} "); // preserve registers across function call
asm("bl " CSM_Z16AllocAnotherRectP7TRegion);
asm("ldmfd sp!, {r0-r3} ");
asm("beq subrectapp1_end "); // exit if error
asm("ldr r12, [r0] "); // r12=iCount
asm("ldr lr, [r0, #8] "); // lr points to first rectangle
asm("cmn lr, lr ");
asm("ldrcc lr, [r0, #16] "); // if RRegion
asm("addcs lr, r0, #20 "); // RRegionBuf
asm("submi lr, lr, #8 "); // TRegionFix
asm("add lr, lr, r12, lsl #4 "); // lr=&(iRectangleList[iCount])
asm("add r12, r12, #1 "); // increment iCount
asm("str r12, [r0] "); //
asm("stmia lr!, {r6} "); // append rectangle - inter.iBr.iX
asm("stmia lr!, {r5,r10} "); // inter.iTl.iY, rect.iBr.iX
asm("stmia lr!, {r7} "); // inter.iBr.iY
__POPRET("r12,");
// AppendRect(TRect(rect.iTl.iX,inter.iTl.iY,inter.iTl.iX,inter.iBr.iY))
asm("subrectapp4: ");
asm("stmfd sp!, {r0-r3,r12,lr} "); // preserve registers across function call
asm("bl " CSM_Z16AllocAnotherRectP7TRegion);
asm("ldmfd sp!, {r0-r3} ");
asm("beq subrectapp1_end "); // exit if error
asm("ldr r12, [r0] "); // r12=iCount
asm("ldr lr, [r0, #8] "); // lr points to first rectangle
asm("cmn lr, lr ");
asm("ldrcc lr, [r0, #16] "); // if RRegion
asm("addcs lr, r0, #20 "); // RRegionBuf
asm("submi lr, lr, #8 "); // TRegionFix
asm("add lr, lr, r12, lsl #4 "); // lr=&(iRectangleList[iCount])
asm("add r12, r12, #1 "); // increment iCount
asm("str r12, [r0] "); //
asm("stmia lr!, {r8} "); // append rectangle - rect.iTl.iX
asm("stmia lr!, {r5} "); // inter.iTl.iY
asm("stmia lr!, {r4,r7} "); // inter.iTl.iX, inter.iBr.iY
__POPRET("r12,");
// aSubtractedRegion->AddRect(inter)
asm("subrectadd: ");
asm("stmfd sp!, {r0-r7,r12,lr} "); // preserve registers and put inter onto stack
asm("mov r0, r2 "); // this = aSubtractedRegion
asm("add r1, sp, #16 "); // inter is 16 bytes above sp
asm("bl " CSM_ZN7TRegion7AddRectERK5TRect); // call TRegion::AddRect
__POPRET("r0-r7,r12,");
}
__NAKED__ EXPORT_C void TRegion::Intersection(const TRegion& /*aRegion1*/,const TRegion& /*aRegion2*/)
/**
Replaces this region with the area of intersection between two specified regions.
Notes:
1. If the error flag of either of the two specified regions is set, then this
region is cleared and its error flag is set. This frees up allocated memory.
2. If this region's error flag is already set, then the function has no effect.
@param aRegion1 The first region.
@param aRegion2 The second region.
*/
{
// r0=this, r1=&aRegion1, r2=&aRegion2
asm("ldr r3, [r1, #4] "); // r3=aRegion1.iError
asm("ldr r12, [r2, #4] "); // r12=aRegion2.iError
asm("orrs r3, r3, r12 ");
asm("bne " CSM_ZN7TRegion10ForceErrorEv); // if either set, ForceError()
asm("str r3, [r0] "); // iCount=0
asm("ldr r3, [r1] "); // r3=aRegion1.iCount
asm("ldr r12, [r2] "); // r12=aRegion2.iCount
asm("cmp r3, #0 ");
asm("cmpne r12, #0 ");
__JUMP(eq,lr);
asm("stmfd sp!, {r3-r11,lr} ");
asm("ldr lr, [r1, #8] "); // r1 points to first rectangle of aRegion1 = pRect1
asm("cmn lr, lr ");
asm("ldrcc r1, [r1, #16] "); // if RRegion
asm("addcs r1, r1, #20 "); // RRegionBuf
asm("submi r1, r1, #8 "); // TRegionFix
asm("intersection1: ");
asm("ldr lr, [r2, #8] "); // lr points to first rectangle of aRegion2
asm("cmn lr, lr ");
asm("ldrcc lr, [r2, #16] "); // if RRegion
asm("addcs lr, r2, #20 "); // RRegionBuf
asm("submi lr, lr, #8 "); // TRegionFix
asm("ldr r12, [r2] "); // r12=aRegion2.iCount
asm("intersection2: ");
asm("ldmia r1, {r4-r7} "); // r4-r7 = *pRect1
asm("ldmia lr!, {r8-r11} "); // r8-r11 = *pRect2++
asm("cmp r6, r8 "); // compare pRect1->iBr.iX with pRect2->iTl.iX
asm("cmpgt r7, r9 "); // if >, compare pRect1->iBr.iY with pRect2->iTl.iY
asm("cmpgt r10, r4 "); // if >, compare pRect2->iBr.iX with pRect1->iTl.iX
asm("cmpgt r11, r5 "); // if >, compare pRect2->iBr.iY with pRect1->iTl.iY
asm("ble intersection3 "); // if <=, rectangles have empty intersection, so iterate
asm("cmp r4, r8 "); // compute intersection and place in r8-r11
asm("movgt r8, r4 ");
asm("cmp r5, r9 ");
asm("movgt r9, r5 ");
asm("cmp r6, r10 ");
asm("movlt r10, r6 ");
asm("cmp r7, r11 ");
asm("movlt r11, r7 ");
asm("stmfd sp!, {r0-r3,r12,lr} "); // preserve registers across function call
asm("bl " CSM_Z16AllocAnotherRectP7TRegion);
asm("ldmfd sp!, {r0-r3} ");
asm("ldmeqfd sp!, {r12,lr} "); // exit if error
asm("beq intersection_end ");
asm("ldr r12, [r0] "); // r12=iCount
asm("ldr lr, [r0, #8] "); // lr points to first rectangle
asm("cmn lr, lr ");
asm("ldrcc lr, [r0, #16] "); // if RRegion
asm("addcs lr, r0, #20 "); // RRegionBuf
asm("submi lr, lr, #8 "); // TRegionFix
asm("add lr, lr, r12, lsl #4 "); // lr=&(iRectangleList[iCount])
asm("add r12, r12, #1 "); // increment iCount
asm("str r12, [r0] "); //
asm("stmia lr!, {r8-r11} "); // append intersection of rectangles
asm("ldmfd sp!, {r12,lr} "); // restore registers
asm("intersection3: ");
asm("subs r12, r12, #1 ");
asm("bne intersection2 "); // loop for all values of pRect2
asm("add r1, r1, #16 "); // increment pRect1
asm("subs r3, r3, #1 ");
asm("bne intersection1 "); // loop for all values of pRect1
asm("intersection_end: ");
__POPRET("r3-r11,");
}
#endif
#ifdef __COBJECT_MACHINE_CODED__
__NAKED__ EXPORT_C CObject *CObjectIx::At(TInt /*aHandle*/,TInt /*aUniqueID*/)
/**
Gets a pointer to the reference counting object with the specified handle
number and matching unique ID.
@param aHandle The handle number of the reference counting object.
@param aUniqueID The unique ID.
@return A pointer to the reference counting object. If there is no matching
object, then this is NULL.
*/
{
// r0=this, r1=aHandle, r2=aUniqueID
asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(CObjectIx,iHighWaterMark)); // r3=iHighWaterMark
asm("ldr r0, [r0, #%a0]" : : "i" _FOFF(CObjectIx,iObjects)); // r0=iObjects
asm("mov r12, r1, lsl #17 "); // r12=r1<<17 = index(aHandle)<<17
asm("cmp r3, r12, lsr #17 "); // compare iHighWaterMark with index(aHandle)
asm("movle r0, #0 "); // if hwm<=index, return NULL
__JUMP(le,lr);
asm("add r0, r0, r12, lsr #14 "); // r0=iObjects+index(Handle)=pS
asm("ldr r3, [r0] "); // r3=pS->uniqueID:pS->instance
asm("mov r1, r1, lsl #2 "); // r1=instance(Handle)<<18
asm("mov r1, r1, lsr #18 "); // r1=instance(Handle)
asm("orr r1, r1, r2, lsl #16 "); // r1=aUniqueID:instance(Handle)
asm("cmp r1, r3 "); // check uniqueID and instance
asm("movne r0, #0 "); // if wrong, return 0
asm("ldreq r0, [r0, #4] "); // else return pointer to CObject
__JUMP(,lr);
}
__NAKED__ EXPORT_C CObject *CObjectIx::At(TInt aHandle)
/**
Gets a pointer to the reference counting object with the specified
handle number.
@param aHandle The handle number of the reference counting object.
@return A pointer to the reference counting object. If there is no matching
object, then this is NULL.
*/
{
// r0=this, r1=aHandle
asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(CObjectIx,iHighWaterMark)); // r3=iHighWaterMark
asm("ldr r0, [r0, #%a0]" : : "i" _FOFF(CObjectIx,iObjects)); // r0=iObjects
asm("mov r12, r1, lsl #17 "); // r12=r1<<17 = index(aHandle)<<17
asm("cmp r3, r12, lsr #17 "); // compare iHighWaterMark with index(aHandle)
asm("movle r0, #0 "); // if hwm<=index, return NULL
__JUMP(le,lr);
asm("add r0, r0, r12, lsr #14 "); // r0=iObjects+index(Handle)=pS
asm("ldr r3, [r0] "); // r3=pS->uniqueID:pS->instance
asm("ldr r2, __instanceMask ");
asm("and r1, r1, r2 "); // r1=instance(Handle)<<16
asm("cmp r1, r3, lsl #16 "); // check instance
asm("movne r0, #0 "); // if wrong, return 0
asm("ldreq r0, [r0, #4] "); // else return pointer to CObject
__JUMP(,lr);
asm("__instanceMask: ");
asm(".word 0x3FFF0000 ");
}
GLREF_C void PanicCObjectIxIndexOutOfRange(void);
__NAKED__ EXPORT_C CObject* CObjectIx::operator[](TInt /*anIndex*/)
/**
Gets a pointer to a reference counting object located at the specified offset
within the object index.
@param anIndex The offset of the reference counting object within the object
index. Offset is relative to zero.
@return A pointer to the reference counting object.
@panic E32USER-CBase 21 if the value of anIndex is negative or is greater than
or equal to the total number of objects held by
the index.
*/
{
// r0=this, r1=anIndex
asm("cmp r1, #0 "); // check anIndex>=0
asm("ldrge r3, [r0, #%a0]" : : "i" _FOFF(CObjectIx,iHighWaterMark)); // if so, r3=iHighWaterMark
asm("cmpge r3, r1 "); // and compare iHighWaterMark to anIndex
asm("ldrgt r0, [r0, #%a0]" : : "i" _FOFF(CObjectIx,iObjects)); // if OK, r0=iObjects
asm("addgt r0, r0, r1, lsl #3 "); // r0=iObjects+anIndex
asm("ldrgt r0, [r0, #4] "); // r0=pointer to CObject
#ifdef __CPU_ARMV6
asm("ble 1f ");
__JUMP(,lr);
#else
__JUMP(gt,lr);
#endif
asm("1: ");
asm("b " CSM_Z29PanicCObjectIxIndexOutOfRangev); // if anIndex<0 or iCount<=anIndex, panic
}
GLREF_C void PanicCObjectConIndexOutOfRange(void);
GLREF_C void PanicCObjectConFindBadHandle(void);
GLREF_C void PanicCObjectConFindIndexOutOfRange(void);
__NAKED__ EXPORT_C CObject *CObjectCon::operator[](TInt /*anIndex*/)
/**
Gets a pointer to the reference counting object located at the specified offset
within the object container.
@param anIndex The offset of the reference counting object within the object
container. Offset is relative to zero.
@return A pointer to the owning reference counting object.
@panic E32USER-CBase 21 if anIndex is negative or is greater than or equal to
the total number of objects held by the container.
*/
{
// r0=this, r1=anIndex
asm("cmp r1, #0 ");
asm("ldrge r2, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iCount));
asm("cmpge r2, r1 ");
asm("ldrgt r0, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iObjects));
asm("ldrgt r0, [r0, r1, lsl #2] ");
#ifdef __CPU_ARMV6
asm("ble 1f ");
__JUMP(,lr);
#else
__JUMP(gt,lr);
#endif
asm("1: ");
asm("b " CSM_Z30PanicCObjectConIndexOutOfRangev);
}
__NAKED__ EXPORT_C CObject *CObjectCon::At(TInt /*aFindHandle*/) const
/**
Gets a pointer to the reference counting object with the specified find-handle
number.
A find-handle number is an integer which uniquely identifies a reference
counting object with respect to its object container.
@param aFindHandle The find-handle number of the reference counting object.
The unique Id part of this number must be the same as the
unique Id of this container.
The index part of the find-handle number must be
a valid index.
@return A pointer to the reference counting object.
@panic E32User-CBase 38 if the unique Id part of aFindHandle is not the same as
the unique Id of this container.
@panic E32User-CBase 39 if the index part of aFindHandle is negative or greater
than or equal to the total number of reference counting
objects held by this object container.
*/
{
// r0=this, r1=aFindHandle
asm("ldr r2, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iUniqueID));
asm("cmp r2, r1, lsr #16 ");
asm("ldreq r2, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iCount));
asm("bne " CSM_Z28PanicCObjectConFindBadHandlev);
asm("mov r1, r1, lsl #17 ");
asm("cmp r2, r1, lsr #17 ");
asm("ldrgt r0, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iObjects));
asm("ble " CSM_Z34PanicCObjectConFindIndexOutOfRangev);
asm("ldr r0, [r0, r1, lsr #15] ");
__JUMP(,lr);
}
__NAKED__ EXPORT_C CObject *CObjectCon::AtL(TInt /*aFindHandle*/) const
/**
Gets a pointer to the reference counting object with the specified find-handle
number, and leaves on error.
A find-handle number is an integer which uniquely identifies a reference
counting object with respect to its object container.
@param aFindHandle The find-handle number of the reference counting object.
The unique Id part of this number must be the same as
the unique Id of this container.
The index part of the find-handle number must be
a valid index.
@return A pointer to the reference counting object.
@leave KErrBadHandle if the unique Id part of aFindHandle is not the same as
the unique Id of this container.
@leave KErrArgument if the index part of aFindHandle is negative or greater
than or equal to the total number of reference counting
objects held by this object container.
*/
{
// r0=this, r1=aFindHandle
asm("ldr r2, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iUniqueID));
asm("cmp r2, r1, lsr #16 ");
asm("ldreq r2, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iCount));
asm("bne 1f ");
asm("mov r1, r1, lsl #17 ");
asm("cmp r2, r1, lsr #17 ");
asm("ldrgt r0, [r0, #%a0]" : : "i" _FOFF(CObjectCon,iObjects));
asm("ble 2f ");
asm("ldr r0, [r0, r1, lsr #15] ");
__JUMP(,lr);
// User::Leave tail called, so no annotations required since
// current frame is reused by User::Leave
asm("1: ");
asm("mvn r0, #7 "); // KErrBadHandle
asm("b " CSM_ZN4User5LeaveEi);
asm("2: ");
asm("mvn r0, #5 "); // KErrArgument
asm("b " CSM_ZN4User5LeaveEi);
}
#endif
#ifdef __CACTIVESCHEDULER_MACHINE_CODED__
extern "C" void PanicStrayEvent();
/**
@internalComponent
The inner active scheduler loop. This repeatedly waits for a signal and then
dispatches the highest priority ready active object. The loop terminates either
if one of the RunL()s stops the current active scheduler level or leaves.
Stop when aLoop becomes 'Inactive'
*/
__NAKED__ void CActiveScheduler::DoRunL(TLoopOwner* const volatile& aLoop, CActive* volatile & aCurrentObj, TCleanupBundle* aCleanupBundlePtr)
{
asm("stmfd sp!, {r4-r8,lr} ");
__EH_FRAME_PUSH2(r4-r8,lr)
#ifdef _DEBUG
// need to copy aCleanupBundlePtr to somewhere else before it's clobbered by the next line.
asm("mov r8, r3 "); // r8 = &aCleanupBundlePtr
#endif
asm("ldr r3, [r0, #%a0]" : : "i" (_CBASE_VPTR_OFFSET_)); // r3 = vptr
asm("add r4, r0, #%a0" : : "i" _FOFF(CActiveScheduler,iActiveQ)); // r4 = &iActiveQ
asm("mov r5, r1 "); // r5 = &aLoop
asm("mov r7, r2 "); // r7 = &aCurrentObj
asm("ldr r6, [r3, #%a0]" : : "i" (_CACTIVESCHEDULER_WAIT_OFFSET_)); // r6 = &WaitForAnyRequest()
asm("active_scheduler_loop: ");
asm("ldr r1, [r5] "); // r1 = aLoop
asm("adr lr, 1f "); // return address
asm("sub r0, r4, #%a0 " : : "i" _FOFF(CActiveScheduler,iActiveQ)); // this
asm("cmp r1, #0 ");
__JUMP(ne, r6); // call WaitForAnyRequest() if still active
__POPRET("r4-r8,"); // else return
// get here when WaitForAnyRequest() returns
asm("1: ");
asm("ldr r14, [r4, #0] "); // r14->first active object
asm("2: ");
asm("cmp r14, r4 "); // end of queue?
asm("sub r0, r14, #%a0" : : "i" _FOFF(CActive,iLink)); // r0->CActive
asm("ldmneda r14, {r2, r12, r14} "); // r2=iStatus, r12=iStatus.iFlags (old iActive), r14 = next object
asm("beq PanicStrayEvent "); // if end of queue, panic
#ifdef _DEBUG
asm("ands r3, r12, #%a0" : : "i" ((TInt)(TRequestStatus::EActive|TRequestStatus::ERequestPending))); // only active bit and request-pending bit
asm("cmpne r3, #%a0" : : "i" ((TInt)(TRequestStatus::EActive|TRequestStatus::ERequestPending))); // active bit == request pending bit
asm("bne PanicStrayEvent "); // if active bit != request pending bit, panic
#endif
asm("cmp r2, #%a0" : : "i" ((TInt)KRequestPending)); // test if iStatus!=KRequestPending
asm("andnes r3, r12, #%a0" : : "i" ((TInt)TRequestStatus::EActive)); // if so, test iActive
asm("beq 2b "); // if not active or still pending, do next CActive
// have an active object to run
#ifdef __SMP__
__DATA_MEMORY_BARRIER_Z__(r3); // acquire semantics
#endif
asm("ldr r3, [r0, #%a0]" : : "i" (_CBASE_VPTR_OFFSET_)); // r3=CActive->vptr
asm("bic r12, r12, #%a0" : : "i" ((TInt)(TRequestStatus::EActive|TRequestStatus::ERequestPending)));
asm("ldr r3, [r3, #%a0]" : : "i" (_CACTIVE_RUNL_OFFSET_)); // r3 = &CActive::RunL()
asm("str r12, [r0, #%a0]" : : "i" (_FOFF(CActive,iStatus)+_FOFF(TRequestStatus,iFlags))); // iActive=EFalse
asm("str r0, [r7] "); // save active object in aCurrentObj in case RunL leaves
#ifdef _DEBUG
__JUMPL(3); // call RunL() (and continue)
#else
asm("adr lr, active_scheduler_loop "); // set lr (return address) to label, active_scheduler_loop
__JUMP(,r3); // call RunL() (and loop)
#endif
#ifdef _DEBUG
//check whether there's a cleanup stack installed:
asm("cmp r8, #0"); // check CleanupBundle* == NULL
asm("beq active_scheduler_loop "); // If r8 == NULL, branch to label, active_scheduler_loop
asm("ldr r0, [r8, #%a0]" : : "i" _FOFF(TCleanupBundle,iCleanupPtr)); // r0 = CCleanup* (load the CCleanup*)
//there is a cleanupstack installed:
asm("add r1, r8, #%a0" : : "i" _FOFF(TCleanupBundle,iDummyInt)); // r1 = iDummyInt* (load the TInt*)
asm("adr lr, active_scheduler_loop "); // set lr (return address) to label, active_scheduler_loop
asm("b " CSM_ZN8CCleanup5CheckEPv); // call CCleanup::Check(iDummyInt*)
#endif
}
#endif
#ifdef __CSERVER_MACHINE_CODED__
__NAKED__ EXPORT_C void CServer2::RunL()
{
asm("ldr r2, [r0, #%a0]" : : "i" _FOFF(CServer2,iMessage.iFunction)); // r2=Message().Function()
asm("stmfd sp!, {r4, lr}"); // save regs
__EH_FRAME_PUSH2(r4,lr)
asm("cmp r2, #0"); // check for Connect/Disconnect message
asm("bmi server2_con_dis");
// Service the message
asm("mov r4, r0 "); // r4=this
asm("ldr r0, [r4, #%a0]" : : "i" _FOFF(CServer2,iMessage.iSessionPtr)); // r0=session
asm("add r1, r4, #%a0" : : "i" (_FOFF(CServer2,iMessage))); // r1=&iServer.Message()
asm("cmp r0, #0"); // Check for NULL session
asm("ldrne r12, [r0, #%a0]" : : "i" (_CBASE_VPTR_OFFSET_)); // r12=CSession2::vptr
#ifdef __SUPPORT_THUMB_INTERWORKING
asm("ldrne r3, [r12, #%a0]" : : "i" (_CSESSION2_SERVICEL_OFFSET_)); // call CSession2::ServiceL(iMessage)
asm("adr lr, server2_run_postamble ");
asm("bxne r3 ");
#else
asm("adr lr, server2_run_postamble ");
asm("ldrne pc, [r12, #%a0]" : : "i" (_CSESSION2_SERVICEL_OFFSET_)); // call CSession2::ServiceL(iMessage)
#endif
asm("mov r0, r1");
asm("b " CSM_ZN8CServer212NotConnectedERK9RMessage2); // NULL session ptr means not connected
// Do this after processing any message
asm("server2_run_postamble: ");
asm("ldr r2, [r4, #%a0]" : : "i" (_FOFF(CServer2,iStatus)+_FOFF(TRequestStatus,iFlags))); // r2=iStatus.iFlags (old iActive)
asm("mov r0, #0x80000001 "); // r0=KRequestPending
asm("ands r1, r2, #%a0" : : "i" ((TInt)TRequestStatus::EActive));
asm("bne server2_run_end "); // if already active, finished
asm("orr r2, r2, #%a0" : : "i" ((TInt)(TRequestStatus::EActive|TRequestStatus::ERequestPending)));
asm("add r1, r4, #%a0" : : "i" _FOFF(CActive,iStatus)); // r1->iStatus
asm("stmia r1, {r0,r2} "); // set iStatus=KRequestPending, set active bit, set request pending bit
asm("add r2, r4, #%a0" : : "i" _FOFF(CServer2,iMessage)); // r2->iServer.Message()
asm("ldr r0, [r4, #%a0]" : : "i" _FOFF(CServer2,iServer)); // r0=iServer.iHandle
asm("bl " CSM_ZN4Exec13ServerReceiveEiR14TRequestStatusPv);// call Exec::ServerReceive
asm("server2_run_end: ");
__POPRET("r4,");
// Deal with Connect and Disconnect messages
asm("server2_con_dis:");
asm("mov r4, r0 "); // r4=this
asm("add r1, r0, #%a0" : : "i" _FOFF(CServer2,iMessage)); // r1=&iServer.Message()
asm("adr lr, server2_run_postamble "); // return address for after any processing
asm("cmp r2, #%a0" : : "i" (RMessage2::EConnect));
asm("beq " CSM_ZN8CServer27ConnectERK9RMessage2); // Do Connect()
asm("cmp r2, #%a0" : : "i" (RMessage2::EDisConnect));
asm("beq " CSM_ZN8CServer210DisconnectERK9RMessage2); // Do Disconnect()
asm("mov r0, r1");
asm("b " CSM_ZN8CServer210BadMessageERK9RMessage2); // Bad message
}
#endif
EXPORT_C __NAKED__ void RFastLock::Wait()
{
asm("1: ");
asm("add r0, r0, #4 "); // point to iCount
#ifdef __CPU_ARM_HAS_LDREX_STREX
asm("2: ");
LDREX( 2, 0); // read
asm("subs r1, r2, #1 "); // decrement
STREX( 3, 1, 0); // write
asm("teq r3, #0 "); // success?
asm("bne 2b "); // no!
asm("sub r0, r0, #4 "); // r0 = this
asm("bcs " CSM_ZN10RSemaphore4WaitEv); // if no borrow from decrement wait on semaphore
#ifdef __SMP__
__DATA_MEMORY_BARRIER__(r3); // no need to wait, but still need acquire barrier
#endif
__JUMP(, lr );
#else
asm("mov r1, #1 "); // 'looking' value
asm("swp r1, r1, [r0] "); // write looking value, read original
asm("subs r1, r1, #1 "); // decrement count
asm("strlt r1, [r0] "); // if it becomes negative, no-one was looking
__JUMP(cc, lr); // if borrow, was originally zero so we are finished
asm("sub r0, r0, #4 "); // r0=this
asm("blt " CSM_ZN10RSemaphore4WaitEv); // lock held so wait on semaphore
asm("stmfd sp!, {r0,lr} "); // otherwise save registers
asm("mov r0, #1000 "); // someone was looking, so wait 1ms and try again
asm("bl " CSM_ZN4User12AfterHighResE27TTimeIntervalMicroSeconds32);
asm("ldmfd sp!, {r0,lr} ");
asm("b 1b ");
#endif
}
EXPORT_C __NAKED__ void RFastLock::Signal()
{
asm("1: ");
asm("add r0, r0, #4 "); // point to iCount
#ifdef __CPU_ARM_HAS_LDREX_STREX
#ifdef __SMP__
__DATA_MEMORY_BARRIER_Z__(r3); // need release barrier
#endif
asm("2: ");
LDREX( 2, 0); // read
asm("adds r1, r2, #1 "); // increment
STREX( 3, 1, 0); // write
asm("teq r3, #0 "); // success?
asm("bne 2b "); // no!
asm("sub r0, r0, #4 "); // r0 = this
asm("bcc " CSM_ZN10RSemaphore6SignalEv); // if no carry from increment, signal semaphore
__JUMP(, lr );
#else
asm("mov r1, #1 "); // 'looking' value
asm("swp r1, r1, [r0] "); // write looking value, read original
asm("adds r1, r1, #1 "); // increment count
asm("strle r1, [r0] "); // if still <=0, no-one was looking
__JUMP(eq, lr); // if it's now zero, no-one is waiting so we are finished
asm("sub r0, r0, #4 "); // r0=this
asm("blt " CSM_ZN10RSemaphore6SignalEv); // someone is waiting so signal semaphore
asm("stmfd sp!, {r0,lr} "); // otherwise save registers
asm("mov r0, #1000 "); // someone was looking, so wait 1ms and try again
asm("bl " CSM_ZN4User12AfterHighResE27TTimeIntervalMicroSeconds32);
asm("ldmfd sp!, {r0,lr} ");
asm("b 1b ");
#endif
}
// Entry point stub to allow EKA1 binaries to be executed under EKA2
// Only called when process is first loaded
extern "C" TLinAddr GetEka1ExeEntryPoint();
__NAKED__ TInt E32Loader::V7ExeEntryStub()
{
// Process entry point
// R4 = entry reason
// SP points to information block
asm("cmp r4, #%a0" : : "i" ((TInt)KModuleEntryReasonProcessInit) );
asm("bne " CSM_ZN4User9InvariantEv ); // invalid entry reason
asm("bl GetEka1ExeEntryPoint "); // load the entry stub and return its address
__JUMP(,r0); // jump to the entry stub with R4, SP unchanged
}
__NAKED__ TInt E32Loader::V7DllEntryStub(TInt)
{
__JUMP(,lr);
}
// Hash an 8 bit string at aPtr, length aLen bytes.
__NAKED__ TUint32 DefaultStringHash(const TUint8* /*aPtr*/, TInt /*aLen*/)
{
asm("ldr r3, one_over_phi ");
asm("subs r1, r1, #4 ");
asm("mov r2, r0 ");
asm("mov r0, #0 ");
asm("blo 1f ");
asm("ands r12, r2, #3 ");
asm("bne hash_unal ");
asm("2: ");
asm("ldr r12, [r2], #4 ");
asm("subs r1, r1, #4 ");
asm("eor r0, r0, r12 ");
asm("umull r0, r12, r3, r0 ");
asm("bcs 2b ");
asm("1: ");
asm("adds r1, r1, #4 ");
__JUMP(eq,lr);
asm("4: ");
asm("ldrb r12, [r2], #1 ");
asm("cmp r1, #2 ");
asm("eor r0, r0, r12 ");
asm("ldrcsb r12, [r2], #1 ");
asm("eorcs r0, r0, r12, lsl #8 ");
asm("ldrhib r12, [r2], #1 ");
asm("eorhi r0, r0, r12, lsl #16 ");
asm("umull r0, r12, r3, r0 ");
__JUMP(,lr);
asm("hash_unal: ");
asm("bic r2, r2, #3 ");
asm("stmfd sp!, {r4,r5,lr} ");
asm("mov r12, r12, lsl #3 ");
asm("rsb r14, r12, #32 ");
asm("ldr r4, [r2], #4 ");
asm("3: ");
asm("eor r0, r0, r4, lsr r12 ");
asm("ldr r4, [r2], #4 ");
asm("subs r1, r1, #4 ");
asm("eor r0, r0, r4, lsl r14 ");
asm("umull r0, r5, r3, r0 ");
asm("bcs 3b ");
asm("adds r1, r1, #4 ");
asm("ldmfd sp!, {r4,r5,lr} ");
asm("subne r2, r2, #4 ");
asm("addne r2, r2, r12, lsr #3 ");
asm("bne 4b ");
__JUMP(,lr);
}
// Hash a 16 bit string at aPtr, length aLen bytes.
__NAKED__ TUint32 DefaultWStringHash(const TUint16* /*aPtr*/, TInt /*aLen*/)
{
asm("str lr, [sp, #-4]! ");
asm("ldr r3, one_over_phi ");
asm("subs r1, r1, #8 ");
asm("mov r2, r0 ");
asm("mov r0, #0 ");
asm("blo 1f ");
asm("ands r12, r2, #3 ");
asm("bne whash_unal ");
asm("2: ");
asm("ldmia r2!, {r12,r14} ");
asm("subs r1, r1, #8 ");
asm("eor r0, r0, r12 ");
asm("eor r0, r0, r14, ror #24 ");
asm("umull r0, r12, r3, r0 ");
asm("bcs 2b ");
asm("1: ");
asm("adds r1, r1, #8 ");
asm("beq 8f ");
asm("4: ");
asm("ldrh r12, [r2], #2 ");
asm("cmp r1, #4 ");
asm("eor r0, r0, r12 ");
asm("ldrcsh r12, [r2], #2 ");
asm("eorcs r0, r0, r12, lsl #16 ");
asm("ldrhih r12, [r2], #2 ");
asm("eorhi r0, r0, r12, ror #24 ");
asm("umull r0, r12, r3, r0 ");
asm("8: ");
__POPRET("");
asm("whash_unal: ");
asm("add r2, r2, #2 "); // r2 must be 2 mod 4
asm("ldr r14, [r2, #-4] ");
asm("3: ");
asm("eor r0, r0, r14, lsr #16 "); // char 0 goes into bytes 0,1
asm("ldmia r2!, {r12,r14} ");
asm("subs r1, r1, #8 ");
asm("eor r0, r0, r12, lsl #16 "); // char 1 goes into bytes 2,3
asm("mov r12, r12, lsr #16 ");
asm("orr r12, r12, r14, lsl #16 "); // r12 = char3:char2
asm("eor r0, r0, r12, ror #24 "); // char 2 into bytes 1,2 ; char 3 into bytes 3,0
asm("umull r0, r12, r3, r0 ");
asm("bcs 3b ");
asm("adds r1, r1, #8 ");
asm("subne r2, r2, #2 ");
asm("bne 4b ");
__POPRET("");
}
/**
@publishedAll
@released
Calculate a 32 bit hash from a 32 bit integer.
@param aInt The integer to be hashed.
@return The calculated 32 bit hash value.
*/
EXPORT_C __NAKED__ TUint32 DefaultHash::Integer(const TInt& /*aInt*/)
{
asm("ldr r0, [r0] ");
asm("ldr r1, one_over_phi ");
asm("umull r0, r2, r1, r0 ");
__JUMP(,lr);
asm("one_over_phi: ");
asm(".word 0x9e3779b9 ");
}
#ifdef __USERSIDE_THREAD_DATA__
/**
@internalComponent
Get a pointer to the thread local user data stored in the thread ID register.
*/
__NAKED__ TLocalThreadData* LocalThreadData()
{
GET_RWRW_TID(,r0);
__JUMP(,lr);
}
#endif