Arm Development
ARM Programming Under OS 5
First, it's worth reiterating that most applications will gain no signifigant speed improvement from using ARM code. Profile your application and know where the bottlenecks are before you plunge in. Furthermore, programs that use the PalmSource documented API are likely to work under ALP (ACCESS Linux Platform), the (non-Cobalt) successor to OS 5. Using unofficial techniques is liable to break under ALP, or even later versions of OS 5. (Relatively few programs which followed the PalmSource programming guidelines broke when OS5 was introduced.)
Code Environments
There are actually three new environments under which code can run in OS 5, each with different restrictions than the traditional 68k environment:- 68k sound callback
- PNOlet (ARMlet)
- ARM sound callback
68k Sound Callbacks
This is the function passed to SndStreamCreate(..., false) or SndStreamCreateExtended(..., false) and the functions called from it. You do not have access to global variables, with all that implies. Instead, you allocate a block of data, which can contain pointers to other blocks of data, and pass its pointer to SndStreamCreate(), which passes it to your callback as userDataP. These blocks can contain data being passed from the foreground code to your callback, or be persistent storage for your callback.An excellent place to start with sound callbacks is the PalmSoruce "soundstream" example, Answer ID 40 in the PalmSource Knowledge Base.
The callback runs in a separate thread from the foreground 68k code, so it may be called while your foreground code is in the middle of an operation. If you have never written multi-threaded code before, beware! You have all the (potential) problems of thread synchronization, with no OS support for semaphores or other standard tools. See the Thread Synchronization section below.
The code in a sound callback does not actually have anything to do with sound. You can do any processing you want done at regular invervals, provided you handle the threading issues discussed above. (But first consider standard mechanisms like nilEvents and alarms.) Create an output stream, have your callback fill the system buffer with zeros, then go do other things.
The sound callback thread runs at a high priority -- if your callback takes too long, the user interface will become unresponsive, and users will shun your program.
Some system calls are safe to make from a sound callback, some are not.
Safe System Calls
- VFSFileRead
- SysNotifyBroadcastDeferred
Interrupt Safe System Calls -- Presumably Safe
- EvtEnqueueKey
- SysDisableInts
- SysRestoreStatus
Unsafe System Calls
Tim Norman has found the following:- DmDeleteDatabase - calling this from the audio callback on some devices causes resource-management functions (e.g. DmGetResource) to later fail in catastrophic ways.
- PrefGetPreference - calling this from the audio callback at precisely the wrong time (i.e. when another thread is accessing the system preferences DB) causes a Fatal alert because the system preferences DB can't be opened.
- FileClose - Calling this from the audio callback corrupts something and leads to hard-to-track-down crashes sometime later (possibly after leaving your app and coming back, which is kind of scary!)
68k sound callbacks do not need to be standalone code segments, but they can be if your main code segment(s) are getting too large.
PNOlet (ARMlet)
These are fairly well documented in the Palm OS Programmer's Companion, vol. I, and elsewhere.You do not have access to global variables, with all that implies.
System calls can be made by calling back through PACE. Calling the system calls directly is officially unsupported, but can be done if you set up the ARM registers correctly.
See the section on Passing Data from 68k Code to ARM Code, and Vice Versa, below.
See the section on Standalone Code Segments below.
ARM Sound Callbacks
This is the function passed to SndStreamCreate(..., true) or SndStreamCreateExtended(..., true) and the functions called from it. You do not have access to global variables, with all that implies.The callback runs in a separate thread from the foreground 68k code, so it may be called while your foreground code is in the middle of an operation. See the Thread Synchronization section below.
The four arguments passed to your callback (userDataP, stream, bufferP, and frameCount) have the correct endianess, but data in the block pointed to by userDataP do not. Use the ByteSwap16 and ByteSwap32 macros as needed. Note that values used ONLY by your callback code do NOT need to be byte-swapped. See the section on Passing Data from 68k Code to ARM Code, and Vice Versa, below.
Officially, there is no support for system calls, but, in fact, the ARM registers are properly set up. Calling depends on certain OS data structures being at certain addresses in memory, but at present, no Palm OS licencee has changed this. This is likely to break under ALP.
Safe System Calls
- SysNotifyBroadcastDeferred: Note that the notifyType field of SysNotifyParamType is not byte-swapped -- you can pass a multibyte character constant such as 'ABCD' just like in 68k code.
- MemMove
Interrupt Safe System Calls -- Presumably Safe
- EvtEnqueueKey
- SysDisableInts
- SysRestoreStatus
Unsafe System Calls
Anything unsafe in a 68k callback is likely unsafe in an ARM callback.See the section on Standalone Code Segments below.
See http://www.mobile-stream.com/devzone.html for The Unofficial PalmOS 5 SDK for native applications/libraries and advanced PNOlets.
Converting a 68k Sound Callback to ARM
Prc-Tools
In this example, the callback code resource is arbitrarily given the ID 5.- Move the callback and the functions it calls to their own file (and debug this)
- Add
#include <Standalone.h>to the includes
- Add
- Rename callback to "start"
- Modify your code to byte-swap any values in the userDataP block before using them.
- If your project uses a definition file, you do NOT need to modify it.
- Change the makefile to compile the callback file with arm-palm-os-gcc without any start files, ala:
callbacks: callbacks.c Resources.h
arm-palmos-gcc -nostartfiles $(CFLAGSARM) $< -o callbacks
- Add the compiled ARM file to the list of arguments to build-prc
$(PRC): Resources.h $(APP) callbacks;
$(BUILDPRC) -t appl -o $(PRC) -n "$(NAME)" -c $(CREATOR) $(APP) *.bin callbacks
- In the main program, change the direct reference to the callback function to code which finds and locks the ARM code resource, and coerces the pointer to the right kind of function pointer, and change the last arg of SndStreamCreate to true:
callbackH = DmGetResource ('armc', 5);
if (callbackH == NULL) {
ErrDisplay("Can't find armc resource 5");
return;
}
callbackP = MemHandleLock(callbackH);
if (callbackP == NULL) {
ErrDisplay("Can't lock armc resource 5");
return;
}
result = SndStreamCreate(&(callbackDataP->streamRef),
sndOutput,
sampleRate,
sampleType,
sampleWidth,
callbackP,
callbackDataP,
REQUESTED_BUFFER_SIZE,
true);
Thread Synchronization
There are several approaches to this problem:- no interaction between callback and foreground, after setup. You can still call SndStreamPause(), SndStreamStop(), etc. from your foreground code.
- Notifications -- SysNotifyBroadcastDeferred() can be called from your callback, and pass an unlimited amount of data in a block pointed to by notify->notifyDetailsP. SysNotifyBroadcastDeferred() will even copy your data and deallocate the space when finished.
- interaction using only an atomic value -- if your callback changes the value of an integer in the callback arg area, the foreground code will always read it as either the old value or the new value. Strings, structures , etc. are not atomic, but multiple pieces of information can be packed into a UInt32.
- Roll-your-own semaphores, etc.
Passing Data from 68k Code to ARM Code, and Vice Versa
68k processors are big-endian, while ARM processors are little-endian. Furthermore, ARM processors require 32-bit values to be 4-byte aligned, while 68k processors only require them to be 2-byte aligned.
Endianess
Int16, UInt16, Int32, and UInt32 have endianess, while Boolean and char do not. Structs and arrays do not have endianess, but their elements may. Structs and arrays also have alignment and padding issues.Enums depend on their implementation. If you're using prc-tools, you can set the -fshort-enums flag to use the smallest size necessary -- for example, an enum with only three values will be stored in a byte, and thus not have endianess.
The OS will pass arguments to your function with correct endianess, but data pointed to by the arguments is your responsibility.
The file Endianutils.h in PODS contains the useful macros ByteSwap16 and ByteSwap32 -- these will convert big-endian to little-endian, or vice versa. If you're using prc-tools, you can copy it from an installation of PODS.
Alignment
Data allocated by ARM code (for example, function (automatic) variables) will always be 4-byte aligned. Data allocated by 68k code is may not be.One approach is to compile all your 68k code with 4-byte alignment. This will cause your code to be slightly larger (perhaps 0.5%) and very slightly faster. The compiler flag for prc-tools is -malign-int. The analogous option for CodeWarrior is
#pragma options align=mac68k4byte
or
Target Settings -> 68K Processor -> Struct Alignment -> 68K 4-byte
Another approach is to ensure all buffers passed to ARM code are 4-byte aligned. Memory allocated with MemHandleNew or MemPtrNew will always be 4-byte aligned. M68k-palmos-gcc appears to 4-byte align the first static variable in each compilation unit, but as the compiler docs don't guarantee this, it is risky to depend on. The alignment of variables allocated on the stack (that is, variables with automatic scope) will vary from call to call, so they can't be passed.
Alignment Within Structs
You also need to be concerned about alignment within structs. ARM code and 68k code that's not 4-byte aligned will produce incompatible results for the following structure:struct bad
The following structure will always compile compatibly:
struct good
So, the safest pattern to follow is to put 32-bit items first in structures, followed by 16-bit and 8-bit items.
While it isn't an ARM/68k issue, putting 16-bit items before 8-bit items is reccomended to ensure all compilers pad the struct identically.
Standalone Code Segments
Prc-Tools
See http://prc-tools.sourceforge.net/doc/prc-tools_3.html#SEC22-- DougReeder - 24 Feb 2006