Embedded Systems


Probing the Palm OS

Trevor Harmon

You can develop Palm applications in most any host language. Here’s how to do so in C.


By now, you probably own or know someone who owns a digital organizer, sometimes called a PDA (personal digital assistant). The success of these battery-powered, pocket-sized devices lies in their ability to wed the muscles of a computer with the ease of a pen-and-paper organizer.

While products like these have been around for years, the difference in the newest generation of organizers is that they also appeal to hackers like me. Much more than souped-up calculators, these gadgets sport 32-bit processors, several megabytes of RAM (serving double duty as permanent storage), a touch-sensitive rasterized display, and a well-documented API: all the ingredients for a programmer’s playground. As a result, developers have been hacking away at PDAs and producing thousands of software add-ons for them.

Some of the software that has come out of this movement is a little off-the-wall. There are programs that can simulate a Star Trek tricorder or turn your PDA into a television remote by trapping its infrared data port. Yet most are genuinely useful and often are just as powerful as their PC-based counterparts. For example, I once tracked my income and expenses with desktop financial software; I now use PDA software with identical features to keep tabs on my budget wherever I go.

The PDA has clearly become a new platform for developers to target, much the same way that developers would target Linux or Windows. The PDA platform has unique advantages over these larger, more complex systems (i.e., low price and high portability), but it does not replace them. A handheld device with a 16 MHz processor and a low-res black-and-white screen can’t match the performance of today’s laptops with gigabytes of storage and accelerated graphics.

Even so, PDAs can be a better solution to some problems, so programmers should be aware of the issues involved in developing products for them. And besides, studying PDA architectures won’t just expand your skills; it can also be fun. Developing for these limited machines is like taking a step backward to a time when computers didn’t have so many whiz-bang features. Smaller memory and slower speed might seem like constraints, but this can actually free you to focus less on bells and whistles and more on the program’s task. Plus, the tools you wield are up-to-date — you might be targeting an underpowered processor of the past, but you’re compiling on today’s fast PCs with some pretty fantastic visual development tools and source-level debuggers.

In this article, I’ll explain how to get started with these tools and how to create programs for the Palm OS, the most common PDA OS (see the sidebar “The Palm OS”). I’ll also highlight some of the pitfalls that come from restrictions of the Palm OS. Finally, I’ll present a complete program that ties all of the concepts together. In the end, you’ll be able to create software you can carry around in your pocket.

Targeting the Palm OS

Porting desktop apps to a handheld environment is not simple, and it’s not just because of the hardware’s limited resources. Even when writing code from scratch, the process of building Palm OS software requires a change in design, a change in the way you structure your programs. Consider the normal life cycle of a word processing program: start program, open file, edit file, save file, close program. That’s a lot of steps, or states, that the program goes through. Palm OS programs, on the other hand, should be “stateless.” You will almost never see a Save or Exit command on a Palm OS menu. Instead, all editing is done in place, and programs appear to be running forever without exiting (from the user’s perspective, at least). After all, you can’t load data from disk because there is no disk!

Some pseudocode might explain this edit-in-place approach a bit better. Here’s how you would modify data in permanent storage (i.e., on a hard disk) in a traditional desktop OS:

void* data = malloc(DATA_SIZE);
FILE file = fopen("data.dat");
fread(data, DATA_SIZE, file);
ModifyData(data);
fseek(0);
fwrite(data, DATA_SIZE, file);
fclose(file);
free(data);

And here’s how you would perform the same task on the Palm OS:

database = OpenDatabase("data");
void* data = MemLock(database);
ModifyData(data);
MemUnlock(data);
CloseDatabase(database);

Notice that the Palm OS example doesn’t actually load any data into memory. The code merely locks a handle to a memory chunk (called a “database” in Palm lingo) and modifies it as needed. The OS applies the changes directly to RAM, which a battery constantly refreshes even when the device is turned off. Thus, the concept of temporary and permanent storage doesn’t quite exist as it does on the desktop.

There is, however, a dynamic heap for those times when you need a memory buffer to work with but don’t want the overhead of creating a database. Be warned that this heap is extremely small — as low as 12 KB on older devices, or up to 184 KB on newer models. These numbers are not the same as the total RAM size of the device, so even if the OS says there’s 4 MB free, you can’t malloc all of that space. It’s reserved for databases.

“Database” is, unfortunately, an overloaded term on the Palm OS. Unlike traditional databases with tables, records, and queries, a database on the Palm OS is essentially just a portion of RAM. Each portion has a program-defined name, plus standard tags that identify the data type, creator, and special attributes such as “read-only.” Put together, these databases make up something equivalent to a file system. Programs can store information in a database, and it will stick around even after the program terminates, just like a file on a disk. The analogy only breaks down when you peek inside: files are a contiguous stream, while Palm databases are broken into smaller logical pieces called records, accessed by index number. This OS feature allows programs to store separate pieces of data in one location.

Although databases allow a Palm application to restore its state, users are not supposed to know when a program terminates or comes back to life. Part of this philosophy is rooted in the limitations of the OS, which is unable to run multiple programs simultaneously despite a kernel that supports pre-emptive multitasking. Naturally, users accustomed to switching among desktop apps freely would grow annoyed having to restart Palm programs as they switch between them. For this reason, developers should forgo Exit commands and restore a program’s entire state on startup, right down to the last pixel, creating an illusion that the program never stopped running.

The motive behind this design approach is not just due to OS restrictions. It also comes from an understanding of the way users run Palm OS software. In the desktop environment, users normally don’t mind waiting a few moments for an application to load because they will likely run it for an extended period. But Palm applications are different. A user would not want to wait five seconds for a program to initialize when he only wants to do a one-second address lookup and then put the thing back in his pocket. Fast turnaround is the norm, and you should design your applications accordingly. Remember: unlike Windows, Macintosh, and other desktop platforms, the Palm has no “wait” cursor.

Fortunately, performance is seldom an issue. Most programs designed for the Palm OS are simple enough that they never need the gigahertz clock speeds of desktop processors. Plus, the absence of a hard drive — a major bottleneck on the desktop — greatly improves the speed of Palm applications. Some users, in fact, run utilities such as Afterburner that slow the processor in order to save battery life. Still, you should keep in mind that the fastest Palm OS device tops out at 33 MHz, and your users, accustomed to prompt Palm apps, can quickly become impatient if yours requires lengthy processing.

Dirty Secrets of the Palm OS

So far, I’ve focused on the benefits of targeting the Palm OS. But, as with any OS, there are certain snags that make Palm development anything but a cakewalk. They’re typically the kinds of pitfalls you don’t discover until after you’ve made the decision to target the Palm OS. To help keep you from pulling your hair out during the product cycle, I want to highlight these drawbacks now so you can be ready for them before they pop up.

Perhaps the biggest overall problem is the steep learning curve. While teaching myself Palm programming, I spent much of the first few weeks sifting through the 1,225-page Palm OS Programmer’s API Reference. This massive manual describes over 950 functions, 288 for the user interface alone.

You need not learn the full API, of course, and many of the functions are intuitive. Many functions are for special features, such as low-level access to Graffiti, the handwriting recognizer. And if you’re already fluent with the core API of any other graphical OS, you will find many of the functions very familiar.

C purists have something else not to like in the Palm OS API: the ANSI C libraries are unavailable. Instead, Palm OS provides its own versions of memory, string, and other functions in the standard libraries. For example, MemPtrNew takes the place of malloc; StrCopy replaces strcpy. Though not really a problem when writing from scratch, it can become a major hassle when you want to port code to — or from — the Palm OS. Calling standard library functions in Palm programs is legal, but the code will be linked statically into your executable, greatly increasing its size.

Another limitation owes to a quirk of history. The first Palm device had just 128 KB of RAM, so the memory manager opted for efficiency over capacity. Consequently, dynamic memory allocations maxed out at a little less than 64 KB per block. While newer devices with much more RAM have appeared, the 64 KB barrier remains. Database records also face the 64 KB limit, though this can be overcome through “File Streaming,” a proprietary Palm OS version of the stdio library.

Probably the most annoying restriction of all is a 32 KB limit on executable code (.PRC files). This limit comes from the “near” memory model of the Palm OS, limiting relative jumps (i.e., calls to functions) to 32 KB or less. The best workaround is a tedious process of multi-segmenting your code. Check out articles 1418 and 1469 of the Developer Knowledge Base on <http://palmos.com>.

Foundations of Palm Programs

If you still want to target the Palm OS after learning both the advantages and shortcomings, then it’s time to learn the basic structure of Palm programs. Your first step is to download and install the free SDK and compiler from <http://palmos.com>. (Commercial environments, such as CodeWarrior, are also available if you need more advanced features.) Before you fire up that editor and start coding, first get your “Creator ID,” a 32-bit value made from four human-readable ASCII characters packed together. This ID, which you must register free of charge in a public database at <http://dev.palmos.com/creatorid>, identifies you as the author of your executable and prevents conflicts when the Palm OS synchronizes its data with the desktop. It also links your code to the databases that it creates, allowing the OS to wipe out the appropriate files when a user wants to uninstall your application.

Once you have registered a Creator ID, you can build the foundation of your code. The Palm OS is heavily event-oriented, so the core of any application merely watches for events generated by key presses, pen taps, menu selections, and so on. Listing 1, a “Hello, World!” program, shows that even the simplest possible Palm app requires a while loop that repeatedly waits for an event, handles it or dispatches it, and then waits for the next event.

As you’ll notice in Listing 1, this loop lies entirely in PilotMain, a startup function that derives its name from the Pilot 1000 (the original Palm device). Required in all Palm programs, this entry point delivers launch codes to the application in its cmd parameter, such as sysAppLaunchCmdNormalLaunch (the default startup state). Other predefined codes may tell your app to search its databases for a given string or to open and display a particular record, but you only need to handle the default launch code.

Of course, you need not dump the entire program’s logic into a single function. Many developers choose to break up PilotMain, so you will often see modular Palm source code with private functions such as StartApplication, EventLoop, and StopApplication, each handling a separate stage of the program’s life. Though not required, these function names have become a standard convention, probably because the examples in the Palm OS SDK use them consistently.

The “Hello, World!” example is trivial, so you won’t find any of these modular functions inside it (not even a user interface). For more powerful applications, you’ll have to design some resources. Much like its desktop counterparts, the Palm OS defines “resource” as any of a variety of UI elements such as bitmaps, icons, localized strings, or dialog boxes. You’re not obligated to create resources, however. You could specify your own bitmap file format or build a custom windowing toolkit. But why bother? The standard Palm OS toolkit is tucked inside the ROM of every Palm OS device, and it’s stable and complete. Without it, you’d have to bulk up your code with custom routines just to display a simple message box. A better alternative is to define a message box resource in a script file:

ALERT ID 1000 ERROR
BEGIN
  TITLE "Error"
  MESSAGE "An error occurred."
  BUTTONS "OK"
END

Using the SDK tools, you can compile this script (with pilrc) and link it to your executable code (with build-prc). Displaying the message box then requires three API calls:

FormPtr frm = FrmInitForm(1000);
FrmDoDialog(frm);
FrmDeleteForm(frm);

Notice that you don’t have to handle user input in this simple case. The Palm OS watches the pen and closes the box automatically when the user taps “OK.” You can always add more sophisticated handling, such as input validation, but obviously that requires additional API calls, as well. And if you want your dialog box to contain list boxes, labels, text fields, or any other standard UI widget normally found in desktop toolkits, those are available, too. You only need to put an extra line in your resource script to define one. The following statement, for example, would put a checkbox labeled “Check me” in the center of the dialog box with a default width and height:

CHECKBOX "Check me" ID 100
    AT CENTER CENTER AUTO AUTO

You could then track changes to this checkbox by handling ctlSelectEvent in your event loop and testing for an ID of 100.

The full syntax for resource scripts is well documented, so I won’t go into further detail. But I will warn you about the potential tedium of building dialog box resources — known as “forms” in the Palm OS SDK. Although it’s fairly straightforward, the process of defining, compiling, and testing your forms can turn into a seemingly endless cycle because you can easily place the elements of the form off-target by a pixel or two, merely because you can’t actually see the form you are designing. To get around this problem, you can purchase a commercial Palm development tool, most of which offer a visual forms designer instead of a script compiler. You might also try a little-known visual resource editor available from Handspring. Still in beta, this free tool can generate a visual preview of a resource script and allow you to modify it on the fly. Look for “RscEdit” in the Springboard Development pages at <http://handspring.com>.

A Better Example

As you add resources and code to your app, you may soon run into confusion. There may be API functions that you find cryptic, or there may be features from other programs that you would love to implement, but don’t know how. For these situations, the SDK examples offer plenty of pointers. Palm ships the complete source code to all of its built-in apps with the SDK (To Do List, Memo Pad, etc.). Not only do they demonstrate how to use the API functions, but they also show the right way to call them. For instance, the documentation on DmNewRecord, which adds a new record to a database, is vague about what occurs if the record already exists. The Address List source code, which happens to call this function, provides the answer: it fails, and you have to call DmResizeRecord instead.

The SDK examples are unfortunately very complex, and they overwhelmed me a bit when I was first learning Palm programming. To help you avoid similar feelings, I’ve put together a simple example called Etch, which combines code for handling forms, menus, databases, and user input into a single tiny program. Though it may seem like just an Etch-a-Sketch simulator, I find it useful for drawing notes to myself, as shown in Figure 1.

The source code to Etch is shown in Listings 2, 3, and 4, containing the main program, the header file, and the resource script, respectively (also available for download at <www.cuj.com>). The header file is unimportant; it only defines a few constants to make the main program easier to read. By defining EtchMenu as 1000, for instance, the program can refer to its menu resource by a name instead of an ID.

The resource script is also fairly self-explanatory. The AboutForm displays program information (just like the ubiquitous Help|About dialog box), and EtchMenu defines a tree of menu commands that the Palm OS dispatches automatically. For example, if the user selects Edit|Clear from the menu, then Etch’s event loop will receive ClearCmd as a parameter of the menuEvent message. This process, incidentally, is no different from any other graphical operating system that takes over the maintenance of menus. With no keyboard, though, the user must stroke the pen from the lower-left to the upper-right as a substitute for the traditional Ctrl key shortcut. The only confusing aspect of this resource script is the empty EtchForm. This form has no UI elements because it exists only to provide a home for the main menu.

Listing 2 (Etch.c) is the real meat and potatoes of the program. It contains PilotMain, the event loop, and all of the private helper functions. This particular PilotMain is pretty basic; it checks for the default launch code and then delegates the rest of the work to other functions. If this were a commercial-grade application, PilotMain would have extra code to verify that the Palm OS is at least version 3.5, since Etch makes API calls available only in versions 3.5 and higher. (You can check the OS version by calling FtrGet and passing the sysFtrNumROMVersion code.)

StartApplication, as usual, performs application-specific initializing. In this case, it determines the number of pixels in the display and loads the user’s previous drawing, if it exists. Next comes EventLoop, which makes a few API calls in case the OS wants to handle the message. If not, Etch calls ApplicationHandleEvent to handle the message on its own. For such a simple application, the only relevant message is frmLoadEvent, which tells Etch that it’s time to load and activate the main form.

At this point, Etch also assigns MainFormHandleEvent as a callback. This function can then catch all subsequent messages sent to the screen, such as pen taps and menu selections. By handling penMoveEvent, for example, Etch knows when the user is dragging the pen across the form and can draw a line accordingly.

As dictated by Palm user-interface guidelines, Etch has no command for quitting. It doesn’t stop until it receives the appStopEvent message, sent by the OS when the user decides to run a different application. Etch cleans up by saving to a database whatever drawing the user has made.

POSEd for Debugging

After compiling Etch or any other Palm program, you have two options for testing the code: you can install the resulting .PRC file onto an actual Palm device, just as you would with any other Palm program, or you can run it inside POSE (Palm OS Emulator), which is bundled free with the Palm OS SDK.

POSE allows programs to run on the desktop by emulating the Palm hardware — the Dragonball processor, the serial port, the memory cards — but it’s a nifty debugging tool, too. It can log and profile your code, and it performs run-time checking of memory access and API calls. If, for example, your code tries to access a resource directly instead of through an API function, the emulator will halt and explain what happened, offering options to halt, continue, or jump to an external debugger. Other OS debuggers can do this as well; POSE is different because it occasionally tries to suggest tips on how to fix the offending code, complete with references to API functions that might help. POSE can also run “Gremlins,” a fool-proofing feature that sends random user input to your app in an attempt to crash it.

POSE isn’t perfect, and no Gremlin can substitute for a user in the flesh. Naturally, you should always test your application on an actual handheld, preferably several devices from various vendors with different OS versions. When you find the inevitable bug, the SDK and the commercial Palm development tools provide a source-level debugger (Windows and Macintosh only), or you can debug with the open-source, command-line gdb on any platform. As for me, when I need to dump variables or find out when a function is called, I just call ErrDisplay, the poor programmer’s equivalent of printf.

Where to Go from Here

Despite the diminutive hardware on which it runs, the Palm OS is truly an OS, not just an API, and that requires a lot of rethinking and relearning. You can expect to spend about the same time mastering Palm programming as, say, Windows programming. Numerous publishers have discovered this and have released over a dozen books on Palm OS development (see Table 1).

The Internet also holds tons of resources such as tutorials, FAQs, and discussion groups. Table 2 provides a list of the most relevant websites for Palm programmers.

But before you check out any of these sites, be sure to read the Palm OS Programmer’s Companion, an electronic book available for free from <http://palmos.com>. It’s included as part of the SDK documentation and offers advice on Palm-specific program design, as well as tips on user-interface guidelines and how to avoid performance jams.

Sidebar: "Unconventional Code"

Trevor Harmon began using Palm OS software in Ghana, West Africa, where he served as a volunteer physics teacher for the Peace Corps. Despite the dust and the heat, his Handspring Visor always remembered his class schedule and kept track of his students’ grades. He still uses a PDA for his teaching duties as an English teacher in Japan. Trevor can be reached at trevor@vocaro.com.