Back in late 2002, I did a couple of articles for the now-defunct Handheld Computing Developer newsletter under the title "The Secret World of Palm OS". Since that site is gone, I'm reprinting those articles here for your reference. This article was originally published in October 2002, about a month before Palm OS 5 was made available to the general public. —ben
Your Palm OS device does a lot of work from the time you click on the icon to when your code takes control. This article examines the origins of Palm OS, how that affects the nature of Palm applications, and what the operating system does under the covers to provide you with a runtime environment.
History lesson
In the first of what will hopefully be a continuing series of articles, I'll be looking at how applications start after being launched by the OS. But first, let's start with the origins of the Palm device platform. By understanding the origins of Palm OS, you can understand why there are limitations on what applications can do.
All current Palm OS handhelds use one of the Motorola Dragonball CPUs. These chips are based on the MC68000, a CPU introduced back in 1979. This CPU has been used in the Apple Macintosh, the Commodore Amiga, and the Sega Genesis video game system, and it has proved to be quite capable. In the mid-1990's, Motorola took the 68K core and put it into an embedded chip, along with a collection of peripherals, to make a complete handheld computer solution, combining the CPU with serial ports, LCD controllers, timers, and other useful modules. This system-on-a-chip, the original Dragonball, became the core of the original Pilot 1000 and Pilot 5000, the 128K and 512K devices that launched Palm into the hardware business.
When Palm designed the Pilot, the company wasn't thinking about setting up a developer economy; it just wanted to make a compact organizer for managing personal information. The original devices had limited RAM space, since RAM chips were expensive and used lots of battery power. On the original devices, RAM was divided into two parts: the dynamic heap (32K) and the storage heaps (everything else). While both areas could be read directly, writing to the storage heaps required using special OS routines that verified their arguments.
This dichotomy remains with us today, although the dynamic heap has grown in size to 256K on modern 8MB devices. On Palm OS 1.0 and 2.0 devices, the RAM used for storage was divided into a series of heaps, each at most 64K in size. With Palm OS 3.0, the storage heap became unified, avoiding fragmentation problems that plagued the early devices. On a PalmPilot Professional, it was possible to have 100K free, but not be able to install a 50K program because the free space was scattered among the different storage heaps.
Having a resourceful memory
Both the storage and dynamic heaps have a basic chunk structure. We normally use these chunks either through pointers or handles. On the dynamic heap, there is no higher-level organization to this; one isn't needed because most of the heap will be cleared out before the next program. In the storage heap, these chunks are organized into Palm OS databases.
A database is a list of chunks on the storage heap. Databases can be identified uniquely by a database name, at most 32 characters long. Databases also have two four-byte identifiers: a creator and a type. For example, the database used by Memo Pad to keep track of memos is called "MemoDB", it has a creator code of "memo", and a type of "DATA". The "MemoDB" shares a creator code with the actual Memo Pad program, and the data stores for the other built in applications also use the type "DATA". Only the name must be unique.
Databases can be used for either data storage or resource storage. When a database is used for data storage, it holds chunks of memory in a list, each one called a record and referenced by its position in the list. Each record also has a unique ID given to it when it was added to the database.
Resource databases also keep track of a list of chunks, but here the chunks are called resources. Each resource has a unique pairing of a four-character resource type and a 16-bit resource ID. Resource databases are usually used to hold Palm OS programs, since the parts of the program can be easily identified.
A basic Palm OS program has just a few key attributes: it has a resource database, it has type 'appl', and it has a "code" resource with ID 1. Usually, a program also has a "code" resource with ID 0, a "data" resource with ID 0, and a collection of miscellaneous resources that hold things like forms, bitmaps, icons, and strings.
When the operating system starts a program, it doesn't have to do very much. When you tap an icon in the Launcher application, Launcher maps the location of the press to its own internal database, uses this to find a reference to the application's resource database, then passed this information on to the system API SysUIAppSwitch. This routine saves this information and then asks the Launcher to exit. When Launcher exits, the OS takes over, pulls out this saved information, and goes to work.
Palm OS now does as little work as possible to start up the new program. It opens the resource database, checks for the "code 1" resource, locks it into place, sets up a new program stack and dynamic heap, then jumps to the start of the code 1 resource. That's it--the OS has finished its work.
The runtime takes over
Now, starting a program is a bit more complicated than that. The next job is done by the runtime code for the program. This is code that's included by your compiler to handle everything else. Not having the OS do this is a good thing, as it gives maximum flexibility to the development tools.
The original runtime code is still used for some programs; it is included as part of StartupCode.lib in the Palm OS SDK. This is a basic runtime library that Palm created for the CodeWarrior toolset; it consists of a single source file, StartupCode.c. The runtimes provided by your compiler vendors, like Metrowerks or PRC-Tools (GCC), are a bit more complicated than this to handle the extensions of each toolset. However, Palm's original code is a good starting point for understanding the concepts.
StartupCode.lib defines a function called __Startup__. The name is
significant, since the CodeWarrior linker looks for a symbol with that name
as its default entry point. The entry point is important because the linker
will make sure that the entry function will be at the beginning of the code
resource, so it will be the first thing called when the application starts.
Palm's startup code immediate calls a system function called SysAppStartup.
This function is not documented in the Palm OS SDK, but it has to be called
by every program. This system API has several major responsibilities:
Determine if you program has global variables, and if it does, allocate a chunk of memory for them.
Setup the application information block that is used by the operating system to store important information about the program.
If there were global variables, initialize them with their default values.
Adjust any global variables that are pointing to other variables or to code to match the locations where the OS put the code and the data.
Reset the Palm device's user interface so it will be ready for the new program.
Usually on Palm OS, global variables are access by the program by taking the value of the A5 register and adding or subtracting an offset. This allows the variables to be placed anywhere in memory without changing the program code. In a standard application, the size of the global space is determined by the first two 32-bit values in the "code 0" resource - the two values indicate how much space to allocate above and below the A5 pointer. By adding the above and below values, you get the total size of the global variable space.
Using A5 as a global pointer limits the size of the global variable space to 64K, and it limits the size of any one variable to just under 32K. This for a few reasons; first, the largest heap chunk that Palm OS lets you allocate is 64K. Second, you can't access more than 32K above or below the A5 pointer in a single 68000 instruction; this is a limitation of the processor. Third, Palm OS stores a pointer to the application information block at offset zero. A variable larger than 32K would overlap this pointer, something not allowed.
The data that initialized the new global space is stored in the "data 1" resource. This data is stored in a compressed form, so the startup code in the OS decompresses this data, filling up the global space. After initializing all the data, the OS then goes through a table of relocations. These are records that indicate that you have to add the start of the code or data segments to pointers in the data.
After going through all this work to setup global variables, the runtime code then starts up the user program. For C and C++ programs, it calls the PilotMain routine, passing in command parameters and flags that it gets from the application information block. At this time, the user's program has control until it exits, usually in response to an appStopEvent.
After your PilotMain returns to StartupCode.c, it performs its final act by
calling SysAppExit. This OS call reverses all of the actions done by
SysAppStartup. On debug versions of Palm OS, this is the code that warns
you about memory leaks. After this call returns, everything is cleaned up
and the system is in a state where the next program can be started.
Just the beginning
This is just the bare minimum set of steps needed to start a Palm OS program. If you use a more complicated runtime, like the ones included with CodeWarrior or PRC-Tools that support multiple-segment programs and C++, you will find the runtime library doing a lot more before your code takes over, including setting up additional code segments, setting up jump tables between the pieces of code, constructing C++ objects, and setting up compiler data structures like exception handling tables.
There is one other twist here: in many cases, applications are started in a special mode that bypasses most of this runtime code. This is called a no-global launch and is used frequently by the operating system to send notifications to applications. When an application is started in this way, its actions are very limited because it cannot safely access its global variables, since nothing has been setup for them. The OS uses this mode for two reasons: first, another program is usually running, so there wouldn't be space on the heap for the new application's global variables, and second, avoiding setting up globals saves time, as these notifications or queries should be handled as quickly as possible. Usually, this has been a good tradeoff, helping to reduce the memory requirements of the device.
Ben Combee is the technical lead for the Metrowerks CodeWarrior for Palm OS Platform development tools. He lives in Austin, TX, where he spends his free time as a writer and technical advisor for the public access TV program "The Show With No Name".
