Pilot Hack Tutorial

By Darrin Massena (darrin@massena.com)
28 Jun 96

This tutorial is meant to show you how to find your way around the Pilot's memory space with the tool Pilot Hack. Once you've downloaded Pilot Hack (pilhack.prc) to your Pilot ("instapp pilhack.prc") and launched it you can begin exploring the contents of that fascinating little black (until now) box.

Pilot Hack displays memory as hex bytes, ASCII or strings and then lets you page up and down to view more. Although it can be fun exploring a 4 gigabyte address space at random, unless you have a lot of spare time on your hands it helps to at least have some kind of idea what is where. I happened to have a lot of time on my hands and here's some of what I found out (all addresses, etc. in this tutorial are in hex unless otherwise specified):

Crude Pilot 1000 Memory Map (not verified on a Pilot 5000 yet but likely to be the same)

00000000-000003ff 68k vectors (including traps starting at $80, #15 at $bc)
000000bc          Trap 15 vector (points to $10c03656)
00000000-00007fff RAM ("Dynamic")
00008000-0fffffff faults on access attempt
10000000-10007fff mirror of the 00000000-00007fff range above (RO w/o permission)
10008000-1001ffff more RAM ("Storage") on 128k machine (RO w/o permission)
10020000-1003ffff out of bounds but doesn't cause a fault
10040000-10bfffff faults on access attempt
10c00000-10c7ffff 512K ROM!
10c80000-???????? repeated images of the ROM
fffff000-fffffb12 DragonBall registers

Very interesting! The first 32K of the Pilot's address space is RAM that is duplicated (simultaneously mapped to) the range $1000000-$10007fff. The memory from $0000000-$00007fff can be read from, written to, and executed from but the memory starting at $10000000 can only be read or executed -- no writing without gaining permission from an API (I later discovered) called MemSemaphoreReserve. The Pilot appears to support at least a primitive form of hardware memory protection which is used to separate "Dynamic" (Palm's term) from "Storage" RAM and keep rogue programs from trashing persistent system data structures, user data, and other programs accidentally. This is not real data security, just a very sensible precaution that should protect from the majority of random software happenings.

68328 CPU Registers

Using Pilot Hack we can view the 68328 processor's current register state. Just enter "r" into the Graffiti area to get a register dump. Most of these register's contents are random or just a side-effect of Pilot Hack's execution but a few of them are very informative. First, you'll notice that the program counter (PC) is at an address in the Storage range (e.g., $1000dc48). This tells us that program code executes directly from Storage. I won't go into how I uncovered this but another point worth knowing is that the executing code is exactly the code downloaded to the Pilot, not a decompressed or fixed up copy or anything like that.

The second register of note is A7, a.k.a. SP the Stack Pointer, which points to an address in the lower 32K (e.g., $5ea4). Since a program's stack needs to be writeable at all times it must be located in the Dynamic area. In case you're wondering, the standard stack size for a program is 4096 (decimal) bytes, despite the claims in Palm's SDK documentation that the stack is 2K. It may still be wise, however, to assume that only 2K of that stack is 'yours' to use if you plan to be calling any PalmOS APIs since they may consume up to 2K stack themselves.

A third register you should know about is A5, the application data pointer (there's probably a precise official term but I don't know it so this is what I call it). When a Pilot application is launched by the OS, its startup code (the first routine in the code resource) calls the PalmOS API SysAppStartup which allocates space in Dynamic RAM for the application's data, decompresses the application's initialized data into this space, then sets A5 to point to it before returning. From then on, all references to application data are relative to A5 (the MetroWerks compiler makes sure this is the case). Address-relative references can only be +/- 32K which tells us that a Pilot application can (conveniently) have only 64K of application data. Of course, this doesn't include dynamically allocated data, code-relative data (e.g., static strings), resources, or other storage so this shouldn't turn out to be much of a pinch.

Hacking Pilot Hack

You want to hack Pilot Hack? Enter "h <address> <return>" where <address> is the value stored in the PC register and a <return> is the slashing Graffiti stroke from upper-right to lower left. Don't be alarmed when you see the addresses on the left don't include the first two digits of your address -- I needed the screen space so something had to go. You'll see a hex/ASCII dump of Pilot Hack's code (pretty boring, no easter eggs, sorry) followed by (if you scroll down far enough) resources (its icon, etc. -- hard to recognize unless you know what you're looking for) and its initialized data (some strings like "registers", "Write a '?' for some help", etc). This is basically what a Pilot executable PRC file is: a header and a collection of resources, one if which is code, one of which is initialized data (compressed), and an arbitrary number of other resources for icons, menus, forms, strings, bitmaps, whatever.

Ok, so much for what the CPU registers are pointing at. What else is to be found in Pilot memory? From here I'll break memory contents into two categories: hardware stuff and software stuff. Hardware stuff is things that are there because the "DragonBall" 68328 puts them there or requires them to be there, and software stuff is data created by the folks at Palm, primarily stored in the 512K of ROM.

Hardware Stuff

First let's get into the hardware stuff. Many wonderful things can be discovered by consulting the MC68328 DragonBall ™ User's Manual. Read this cover to digital cover and you will gain endless insights on what your Pilot is capable of. Many of these capabilities are not exposed by the PalmOS so if you want 'em you'll have to be prepared to go direct. WARNING: one of the primary purposes of the PalmOS (and most operating systems) is to isolate you from the specifics of the hardware. A Pilot application that writes exclusively to the PalmOS APIs has a good chance of being fully portable to other platforms running the PalmOS (e.g., the Emulator running on the Mac, new hardware devices). Writing directly to the hardware is like playing with fire -- you might get burned as USRobotics and their licensees release new hardware, operating system upgrades, etc. Plus, since the multitasking PalmOS manages and uses much of the hardware you may be interested in playing with there's a good chance conflicts will arise with unexpected consequences. On the other hand, fire is pretty cool and so are some of the nifty DragonBall capabilities the PalmOS doesn't provide access to (yet).

Starting at address $fffff000 you can find the 68328 registers. These are not the CPU registers but a block of registers (~130 total) that control hardware functions like interrupts, parallel I/O (hey, I don't see a parallel port on my Pilot!), audio, timers, serial I/O, the LCD display, and the Real Time Clock. What these registers are do are beyond the scope of this document (and in many cases beyond the scope of my knowledge!) but I'll call out a couple here to give you an idea of what can be found.

Screen Starting Address Register

Early in my hacking of the Pilot I didn't know what any of the APIs were or how to call them but I needed to get some kind of feedback from my test programs as they executed. Text output, serial output, even beeping are all done through APIs I had no clue about. Scrounging through the 68328 User's Manual I found a register called "LSSA", "Screen Starting Address Register". Hmm... very interesting! Its address is (base) $fffff000 + $a00. You can use Pilot Hack to examine this register ("h fffffa00"). I find the value $000063b0 on my Pilot but it may be different on yours. The Pilot's LCD gets its display data directly from this address. Write a byte at $63b0 and it shows up at the upper-left corner of your display. Use Pilot Hack to view screen memory ("h 63b0" or whatever is right for your Pilot) and you'll see (in hex) the values that make up the display, 20 (decimal) bytes per scan line. Since the first scan line of Pilot Hack's display is blank, the first 20 bytes of the dump are zero. Then $80 (the top-left pixel of the 'h'), $46 (part of the '6' and '3'), and so on.

I wrote debugging output directly to the screen until I figured out enough to be able to call the WinDrawChars API ("function Foo returns 17" sure beats "... .. .... ..."). Pilot Hack still reads and writes video memory directly to scroll the screen when in event trace mode. To check this out, enter 'e' to turn event tracing on and drag the pen around on the screen. Enter 'e' again to toggle tracing off. At the time I didn't know about the API WinScrollRectangle but if I were writing Pilot Hack today I'd use WinScrollRectangle for scrolling because it would be safer, more portable, keep my program smaller, and possibly even be faster (if written in assembler or Palm's compiler optimizes better than mine).

I haven't tried this but presumably one could scroll the screen through memory simply by changing the screen starting address. Combined with the other LCD registers for panning this could be a great speedup for games that scroll the screen a lot. Someone should write a GameBoy Emulator for Pilot.

Real Time Clock

This one is kind of fun because it is always changing. Enter "h fffffb00" (be sure to turn off event tracing first, if you haven't already) and the first four hex bytes are the time your Pilot thinks it is in hours (first byte), minutes (second byte), and seconds (third and fourth byte). If you enter "h <return>" you'll force a refresh at the current address and you can see the second and possibly the minute value has incremented. Nice to know that low-level hardware is taking care of this rather than software that can easily get screwed up, say by an interrupt, and lose track of time.

Many of the other 68328 registers look very interesting. Go figure them out and YOU can write the next hacking tutorial.

Software Stuff

On the border between hardware and software are the 68328's Exception Vectors. When an exception of any kind of occurs (e.g., divide by zero, misaligned memory access, reset, interrupt, TRAP) the CPU looks up the appropriate vector in the table starting at memory address $0 and jumps to that vector to handle the exception. When the PalmOS initializes it sets these vectors to point at its handlers for the various exceptions. Sometimes it can be useful to know where these handlers are -- just look them up in the vector table.

TRAPs

The vectors that interested me the most were the TRAP vectors. By disassembling the game Puzzle, I could tell that it accessed operating services by executing a TRAP #15 instruction followed by (what I call) an API code that indicates which API to dispatch to. The Macintosh uses a similar technique. I'm not really a fan of this approach because it introduces a fair amount of overhead on each API call while the trap dispatcher grabs the API code, decodes it, and dispatches to it. The Amiga uses a lower overhead approach which involves an array of API vectors at a known address and indirecting through the vectors to call APIs. Oh well.

I found it difficult to get much of an idea what Puzzle (and other programs I was disassembling) was doing because I didn't know which APIs were being called as a result of their TRAPs. The first step in cracking this nut was to unravel the algorithm the trap dispatcher uses to map from an API code to the API's address. Question is, where is the trap dispatch routine located? Answer: one of the Exception Vectors is used by the CPU to dispatch to the TRAP #15 handler. My 68000 reference manual tells me this vector is at address $bc. Pilot Hack shows that the vector at $bc points to $10c03656 -- the trap 15 dispatcher!

Enter "h 10c03656" into Pilot Hack to dump the code for the trap 15 dispatcher. If you read the ASCII dump down the right-hand side you'll notice, toward the bottom, the string "TrapDispatcher" (well, the 'a' and 'p' are truncated at the screen edge but you get the idea). More later on what this helpful string is doing there. I typed the bytes (instructions) I found at this address into my PC and used a 68000 disassembler to reveal what the code is. The trap dispatcher is a simple routine that uses part of the API code as an index into an API dispatch table full of vectors pointing to the actual APIs.

I added a command to Pilot Hack to perform this API code-to-address translation upon request. Enter "t a000" (or just the "t 0" shorthand) to go to the address of the first API (MemInit).

Function Names

Disassembly of anything, especially something large and complex like the Pilot 512K ROM, can be quite challenging and time consuming without at least some sort of guide posts to give you a clue what the code you're disassembling is doing. The gods granted a great gift to hackers when they created the Pilot and it comes in the form of function names. To my amazement when I first started poking around in PRC files I discovered that every function in a Pilot app is followed by its (length preceeded) name in ASCII! Just run a program like strings.exe on Giraffe.prc or Puzzle.prc and you will see strings like "__Startup__", "InitGameBoard", "DrawPiece", etc. Further, many of the programs have compiled in Assert-type strings like "Error launching application", "didn't find empty square position", "pos out of bounds".

I have two reactions to this. My first one is "yay! this makes disassembly a piece of cake!" but my second one is "bummer! all these symbols and asserts are wasting my precious Pilot RAM!". Normally we only see this kind of stuff in DEBUG versions of programs. Are all the released Pilot apps DEBUG versions? Are all software developers for Pilot insane? I don't really have the answer to this but I'm hoping Pilot developers will wake up to the kind of code they're generating and start building more efficient programs for their final releases. You may note that Pilot Hack does NOT have any bogus symbols, asserts, or anything of the kind. Sorry if this makes hacking harder but I'm finding my 128K Pilot squeezed enough as it is.

Do the ROMs also have these function names? Take a look. First toggle Pilot Hack into string dump mode ("s <return>"). Now enter "t a1f6". What do you see a few lines down the screen? "WinCreateWindow"!!! So, the ROMs have these symbols too. I don't care as much about wasted ROM space as I do RAM space but this still seems strange (albeit handy for hackers). Perhaps there is a Pilot debugging tool or something that can make use of these function names. Let me know if you have the answer.

Browsing For Strings

With Pilot Hack in string dump mode you can quickly scan through memory for points of interest. To start scanning at the base of the 512K ROM enter "s 10000000". Then just page down until you hit the end or get bored out of your skull, whichever happens first. In addition to the names of all the PalmOS APIs you can find many internal, private routines that don't have TRAP entrypoints. Knowledge of these can be valuable. Some other strings I found amusing:

10c2207c "Hackers Rule!!!!" -- I'm not making this up! Totally excellent!
10c4a9e6 "AMX 68000 Kernel ... Copyright (c) 1994-1995 KADAK Products Ltd."
10c601db "EggOfInfiniteWisdom" -- what is this?
10c612c8 The credit strings -- "Brought to you by:", etc.

Looks like USRobotics licensed their operating system kernel from another company "KADAK Products Ltd.". You can find them on the Web along with more information about their kernel (sorry, no link handy).

Events

To write Pilot Hack I needed to be able to handle various events like Graffiti handwriting events, button presses, etc. I added an event trace facility to Pilot Hack to view these events and their arguments. Enter "e" (no <return> needed) to turn on event tracing. As you press buttons or move the pen around on the display Pilot Hack will show you a dump of the event your action triggers. Each value in the dump is actually a word although the format of the dump might confuse you into thinking they're bytes. The first word of the dump is the event type ("eType") and you can see some obvious ones (1 = pen down, 2 = pen up, 3 = pen move). The high byte of the second word is a Boolean that indicates whether the pen is down. The third and fourth words are the pen's x and y coordinates. The rest of the values are event specific.

ROMDump

Using a tool like Pilot Hack to view the contents of the Pilot's memory works well for certain jobs (e.g., decoding a trap) but unless you have Pilot versions of all the tools you want to use (yeah, right) eventually you're going to wish you had the Pilot ROMs where the rest of your tools are -- on the PC (or Mac). It didn't take me long to reach this point because my disassembler runs on the PC. So I want the data from the Pilot ROMs on my PC. How do I get it there? One way would be to write a program to send it over the serial line and capture it with a terminal program on the PC side but I didn't yet have any information on the serial APIs. I fumbled around for a bit until I noticed "Giraffe_High_Score.PRC" in the Backup directory of my Pilot Desktop installation. Giraffe has a way to create a file that is automatically transferred to the PC (backed up) when you hotsync. If I could figure out how they did it I could have the Pilot ROMs 'backed up' onto my PC.

Upon examination, Giraffe revealed that it creates a new resource database, sets a special flag on it that indicates that the database should be backed up on hotsync, then creates a new resource in the database into which it writes the high scores. So, all I had to do is add the same code to Pilot Hack but copy ROM data rather than high scores. NOT! High scores are ~100 bytes long, Pilot ROMs are 512K long, my Pilot has 128K of RAM to store the new database in. So, just write the ROMs out in several pieces and stitch them back together on the other side. Given the amount of free memory on my Pilot I imagined each piece would be ~64K in length, so 8 pieces for the whole thing. Not too bad. But when I wrote the code to do this the Hotsync Manager crashed as it was trying to backup my file! Looks like some sort of size limitation. I tried 48K, crash, 32K, crash, then 16K -- it works! 32 hotsyncs and another set of batteries later I have all the data on my PC. Each piece has the PRC header on it so I wrote a little program to strip it off and concatenate all the data into one file.

To do this yourself, use the (undocumented) 'x' command in Pilot Hack. Start by setting the current address to $10c00000 (the base address of the ROMs) "h 10c00000". Writing an "x" causes Pilot Hack to write 16384 (decimal) bytes starting at the current address into a new resource database, then advance the current address by 16384 bytes. At that point you press hotsync to backup the database. It will show up in your Backup directory with the name "ROMDump.PRC". Rename it to "romdump.0". Go back to your Pilot, write "x" again, hotsync again, rename the new file "romdump.1" and do it again 30 more times. This is the true test of how committed you are to hacking the Pilot. Actually, the truly committed would find a better way to do this and share it with the rest of us. With modern-day knowledge of the serial API a better solution wouldn't be difficult to write.

Here's the program I used to stitch all those files together: 

// MakeROM.cpp
// This program reads 32 16384-byte files with the name "romdump" and
// the extension ".n" where n varies from 0 to 31.  The PRC header is
// stripped and the result is written as a single 512K file "Pilot.ROM"

#include <stdio.h>

unsigned char gab[16384];

void main()
{
	FILE *pfilCombo = fopen("Pilot.ROM", "wb");

	for (int i = 0; i < 32; i++) {
		char szFilename[255];
		sprintf(szFilename, "romdump.%d", i);

		FILE *pfil = fopen(szFilename, "rb");
		fseek(pfil, 0x5a, SEEK_SET);
		fread(gab, 16384, 1, pfil);
		fwrite(gab, 16384, 1, pfilCombo);
		fclose(pfil);
	}

	fclose(pfilCombo);
}

Conclusion

So now you know your way around the Pilot, have seen a few of the sights, and can start exploring on your own. What will you find? What will you do with what you learn? I'd love to hear about it.


Back to Pilot Software Development