bartop

palmoswerks ARCHIVE
Palm OS programming tips from a (former) CodeWarrior insider

header

Navigation

Search
Home
About
Stories
Stuff I Like
Articles
PilRC
CodeWarrior
palmos.techwood.org
DevTools List
Palm OS Dev FAQ

Developing PACE Native Objects

Ben Combee combee@techwood.org

Version 0.1 2003 January 13

Global Variable Usage

In standard ARM code, the R9 register is used to access global variables. However, in Palm OS 5, R9 is used by the operating system and cannot be changed. In its place, the ARM compilers that target Palm OS use the R10 register as a global pointer.

The way R10 is used is different between CodeWarrior and prc-tools (GCC).

In CodeWarrior, the ARM tools treat the PNO as if it were a standalone program that is linked at address 0, with code coming first, and data following it immediately in memory. The code is compiled as position-independent code where jumps are done using PC-relative instructions. This allows the code to be moved anywhere in memory without having to change any of the instructions.

In this model, R10 points to the start of the PNO, and the code that computes addresses of global variables always adds R10's value to the computed address. This lets you change where the global variables are located by changing the value of the R10 register.

    CODE (storage heap)  <-- R10 points here
    DATA (storage heap)

Basic PNOs, as supported in CW for Palm OS V9.0, have one contiguous code and data region stored in a resource. The startup code at the top of the PNO computes R10 by getting the current PC value and subtracting the offset from the beginning of the function to the current instruction. This allowed read-only data, but since the data region was stored in a resource, you couldn't modify any global variables.

The PNO runtime library in V9.2 added a PNO header and second entry point that supported having the caller supply a value for R10. This allowed you to write a loader that would allocate the data on the storage heap, and give you read/write global variables. This is discussed in more detail in the section on PNOLoader. When you use this model, R10 directly points to a random spot of memory. However, computing R10 plus the size of the PNO's code gives you a pointer to the chunk of heap that holds a copy of the PNO's global variables.

    CODE (storage heap)
    ...
    (codesize bytes before DATA) <-- R10 points here
    DATA (dynamic heap)

Embedded Relocations

The C data model allows the compiler to define data that has embedded pointers. Since PNOs can be located anywhere in memory, you need a mechanism to alter this data to point to the real locations where things are before you run your code.

CW for Palm OS 9.2 added this capability using relocation lists. These are special lists of offsets into the PNO that the linker identifies as having pointers to other parts of the PNO.

The CW linker emits two different lists. The first list is a list of the addresses of data that points to items in the code section, while the second list has addresses of data that points to other data. For example, the code

    int (*gFuncP)() = MyIntFunc;

would allocate a global variable that held the address of the MyIntFunc function. The location of that variable would be in the code relocation list. Similarly,

    int gImportant;
    int *gImportantP = &gImportant;

would add an entry to the data relocation list that matches gImportantP.

The reason to have separate lists is that code and data might be located at different addresses, so the code that applies the relocations needs to use different values.

A relocation is applied by reading the original value, adding the address of the start of the PNO for code, or the R10 value for data, and writing that modified value back to memory. All relocations in CW are for 32-bit values.

Relocation lists are supported by the PnoLoader library and by the Tapwave Native Model loader code.

Function Pointers

CW for Palm OS 9.0 and 9.2 didn't allow you to easily compute the address of a function in code in all circumstances. The compiler would compute the address by taking the offset to the function and adding R10 to it. This worked when you had a PNO where code and data were contiguous. However, if you split the code and data sections, this would compute an address falling in no man's land.

In 9.2, the ARMlet_Runtime library supplied a helper function, __ARMlet_Take_Func_Addr__. This function would take a value, subtract R10 from it, and add in the start of the PNO, fixing the function pointer to the correct value.

In 9.3, the code generation was changed to a true position-independent code model where function addresses are computed by adding an offet to the current program counter value, and the helper function is no longer needed.

C++ and PNOs

to be written

Tapwave Native Applications (a PNO application)

to be written

Floating Point Math

In 68K applications, the compiler would make system calls to perform operations like add, subtract, multiply, divide, and comparison. For ARM code, this isn't practical. CodeWarrior supplies a software floating point library where those operations are done by function calls. The CW for Palm OS V9.2 release shipped with a faulty version of the floating point and extended math library; this was corrected in V9.3.

Limited Stack Space

When a PNO is invoked by PceNativeCall, the R13 register points to a limited amount of stack space, on the order of 4K. This may be sufficient for small routines, but it can quickly run out if you use recursive code or lots of local variables.

The easiest way to get more stack space is to allocate a new chunk of memory and change the stack pointer to point to it. If your PNO has global variable support, the code might look like this:

    const UInt32 stackSize = 12 * 1024;
    MemPtr *gNewStackP = MemPtrNew(stackSize);
    gOrigStack = ReplaceR13((UInt32)gNewStackP + stackSize);
    // ... call your code
    ReplaceR13(gOrigStack);
    MemPtrFree(gNewStack);

Remember that the stack grows downward, which is why we replace R13 with a pointer to the end of the block.

The defintion of ReplaceR13 in CodeWarrior, using ARM code, looks like

static UInt32 asm ReplaceR13(UInt32 newValue)
{
    add r1, r0, #0    // save newValue in R1 scratch register
    add r0, r13, #0   // return original R13 value in R0
    add r13, r1, #0   // move new value into R13
    bx lr             // return to caller
}

Thumb code is similar, but using MOV instructions to copy register values.

You need to be careful when doing this. If your entry function defines any local variables, they won't be available once you've changed R13's value. If you use this technique, it is probably better to move all your real code into a routine called by your ARMlet_Main routine.

Calling 68K Code and Palm OS APIs

When a PNO is started, one of its parameters is a function pointer that lets the PNO call back to the operating system, either to execute more 68K code or to make a system call. This mechanism is described in the document "Palm OS 5 ARM Programming.pdf", in the book "Palm OS Reference.pdf" in the section on PceNativeCall, and in the Palm OS Companion in the "Calling an ARM Function" section.

To call 68K code or the OS, you need to setup a region of memory that will act as the argument stack for the calls. Arguments should be placed in this region the same way that the 68K compiler would push arguments on the stack, and values need to be byte-swapped if they aren't already big-endian. You then jump to the callback function with the 68K emulation state pointer as the first parameter, the address of your 68K routine as the second parameter, and the pointer to the argument stack as the last parameter. Since 68K code can return values in either the D0 or A0 register, you also have to tell the routine in what register to expect a return value.

Calling Palm OS APIs is very similar; rather than taking the address of the API, you encode the trap number for the API call in the address parameter, setting the high bit to tell the OS that this is a system trap.

To call APIs where one trap is used for multiple entry points, you need to modify the 68K emulation state to change the D0 register, and then use the normal trap dispatch mechanism.

Fortunately, when using CodeWarrior, most of this work is encapsulated in the PACEInterface libraries. It has macros that make creating a 68K call fairly easy, including all the byte swapping and calling syntax.

PNOLoader Library

to be written

PACEInterface Class / PACEInterfaceLib Library

to be written

Cache Issues

to be written

Split Resource PNOs

to be written

Linking Your PNO into a 68K Application

to be written

Aligning 68K Data for PNO Access

to be written

Debugging your PNO using Palm OS Debugger

to be written

Setting up CodeWarrior to Launch Palm OS Debugger

In your 68K application target settings, go to the Target/Build Extras preference panel. Click on the "Use External Debugger" checkbox, turning this on. For the application, click "Browse..." and locate the PalmOSDebugger.exe program. In the Arguments box, enter the text "%targetFilePath". You can leave the working directory blank.

When you do this, hitting "Debug" from your 68K app will launch the Palm OS Debugger (POD), with your application as its target. POD cannot read the breakpoints you've set in CodeWarrior, so you'll have to set those manually in POD's file view.

Palm OS 5 Native Hacks

When Palm OS 5 was introduced, PalmSource said that you would need to use notifications to replace the trap patching mechanism used for 68K "hack" programs. This has worked to port some of the popular hacks from OS 4, but others cannot be supported by this mechanism.

Igor Nesterov has developed a native hack manager called "Yet Another Hack Manager" that allows loading ARM code to replace system traps on many OS 5 devices. This is using an unsupported mechanism, but seems to work on everything released throug the end of 2003. Details on the hack manager and ARM hack development are at http://yahm.palmoid.com/yahmv.htm and http://groups.yahoo.com/group/palmnest/. Igor's ARM static libraries of system call stubs works with both prc-tools and CodeWarrior for Palm OS.

Directly Calling Palm OS 5 APIs

to be written

brought to you by weblogger.com


Send feedback to combee@techwood.org
Copyright © 2004 Benjamin L. Combee
Palm OS is a registered trademark of PalmSource, Inc.
Metrowerks and CodeWarrior are registered trademarks of Metrowerks Inc.

The views expressed on this website/weblog are those of mine alone and do not necessarily reflect the views of PalmSource or Metrowerks.

This is a Manila Site

qwertYAK / frobnovich