Pila Version 2.0 User' Manual |
Based on the work of Darrin Massena
(darrin@massena.com)
27 Oct 96
and enhancements by Wes Cherry
(wesc@ricochet.net)
and Mikael
Klasson (fluff@geocities.com)
18 Feb 98
with major additions for version 2.0
by Frank Schaeckermann
(frmipg602@sneakemail.com)
(I know it's a horrible address but necessary
to be able to keep the spam in check)
23 Jul 2003
All this required two things:
|
|
|
Pila is a tool for developing applications written in 68000 Assembly Language for the Palm Pilot handheld computer. Pila combines the operations of assembling, resource compiling, and linking into a single program that takes a 68000 assembler source file with embedded or external resources as input and produces a Pilot executable (PRC) file as output. Pila is a console application and runs under Linux as well as Windows XP/NT/95.
Note: I haven't tested Pila 2.0 under Windows at all since all my development has first been done using CYGWIN and later directly under Linux. I haven't changed any of the #ifndef Unix parts of the code therefore it should still work under Windows. If anybody is willing to try and make possibly necessary adjustments, just send them to me for me to incorporate them into the source!
"An assembler? Who cares about assembly language any more?" While assembly language has fallen out of favor for creating the kind of big, slow, bloated, er, I mean feature laden, applications we run on our desktop computers today it is exactly what is needed for maximizing performance on a constrained device like the Pilot. Smaller programs leave more space for our data and other programs. Faster programs are not only more responsive but by spending more time idle they increase the Pilot's battery life. Have you looked at the quality of the code MetroWerks' compiler for Pilot produces? Let's just say there's room for improvement and leave it at that.
This document details Pila's features and syntax and provides a walkthrough demonstration of using Pila to create a minimal Pilot application. To create your own applications you'll need an understanding of 68000 assembly language programming as well as documentation on the PalmOS and its APIs. You might also be interested in the architecture and details of Motorola's 68328 microprocessor, the 68000 variant inside your Pilot.
The classic and most essential 68000 programming book in my library is Motorola's M68000 16/32-bit Microprocessor Programmer's Reference Manual (a.k.a. M68000UM). An online (PDF) version that also includes information on the rest of the 680x0 family of processors can be found at the Motorola site http://www.motorola.com/. This book is a great reference but will not teach you assembly language. To learn more you'll definitely want to check out Robert Boys' M68K FAQ (http://www.ee.ualberta.ca/archive/m68kfaq.html). I'm also told that the tutorial/reference that can be found at http://www.cs.cornell.edu/Info/Courses/Spring-95/CS314/toc.html is useful (this link does not work anymore - anybody has a replacement?). Keep in mind that the assembler and environment used for that course is somewhat different from Pila and the Pilot.
The Palm Source site (http://www.palmsource.com) has all the documentation they ship with their PalmOS SDK. The documentation covers most of what you'll want to know about the PalmOS and its APIs. Do not attempt any Pilot programming without first acquiring and reading this documentation! You'll need Adobe Acrobat Reader to read it.
Motorola makes the MC68328 " DragonBall" integrated processor that is the heart of the Pilot and they have some great documentation online in PDF form. I highly recommend downloading and digesting the MC68328 (DragonBall) Integrated Processor User's Manual. You'll need Adobe Acrobat Reader to read it.
Lastly, you should definitely check out the great Pilot Software Development site (http://www.massena.com/darrin/pilot/index.html) hosted by Darrin. Even if it is a little out-dated, it still contains Pilot programming tools, tips and tidbits, etc.
Pila used to have its own page at http://www.massena.com/darrin/pilot/pila.htm but moved to Fluff's Den (to be found at http://mklasson.cjb.net/) when Mikael Klasson took the post as Pila guardian. Now, since Pila Version 2.0, I assumed the responsibilities as the Pila guardian from Mikael and moved it's home to http://www.schaeckermann.net/pila.html. Thanks Mikael to keep this up for sooooo long!
See Appendix A if you would like to know more about the making of Pila. Appendix B describes what you need to do to get the Pila SDK *.inc include files onto your machine to use Pila (since the Palm OS SDK can not be redistributed by me there is a bit of work for you to do to convert the Palm OS SDK C header files into the Pila SDK *.inc include files!
For the curious, Appendix C describes the PalmOS API calling convention. And Appendix D provides a quick reference card for the 68000 instruction set.
<FRANK>
"Pila Version 2.0" has - by declaration - become the
current official release of Pila. Since there hasn't been any development on
Pila for years version 1.0 Beta 3 Fluff 7 was picked up as a starting point to
create this package. There doesn't seem to be an ASDK package anymore which this
version of Pila could be made a part of. There are however lots of other tools
around PalmOS development and they all have evolved along their own path the
last years. Therefore for Pila Version 2.0 and until further notice I will
maintain Pila as it's own, independent package.
</FRANK>
The only "must have" to do development with Pila is the PilRC compiler to create PalmOS resources from *.rcp files. See PilRC below for more information
These are the files of the current Pila Version 2.0 package. By my own
convention any file actually producing output in the data and/or code sections
has an extension of .asm whereas 'definition-only' files have an extension of
.inc.
File | Description |
---|---|
makefile | Makefile to compile and link Pila with the GNU compiler gcc. |
pila20 | Pila proper. pila20 acts as assembler, resource integrator, and linker |
pila-user-manual.html | This documentation |
README | Last minute notes you'll definitely want to read first |
lib/* | Bits and pieces of helpful code I have put together that can be put
into your code by using the include
directive. See the various files for more information on their content and
usage. [FIXME: make sure that information is actually available in those files.] |
lib/startup.asm | Standard startup code that every application must include FIRST |
licences/* | Licence statements of all developers who worked on Pila, making their contributions public domain. |
package | Batch file to create the Pila-2.0.zip package file from my install directory |
pila-env | Batch file to set the environment variable PILAINC which tells Pila where to look for the files referenced by the include directive. You will have to modify the export statement according to your installation and move it i.e. to your ~/.bashrc file. |
pila-sdk/* | This directory contains the necessary files to generate the Pila SDK from the original C header files. See Appendix B for more details. |
sample/sample.asm | A sample application converting decimal to hex and vice-versa |
sample/sample.rcp | Sample resources, processed by PilRC to produce Pilot resource binaries |
sample/sample.bmp | The sample application's icon. Pila converts this to a 'tAIB' resource |
sample/sample.prc | The sample executable as produced by Pila |
sample/sampleRcp.inc | Include file containing the resource ID definitions. Used in sample.asm and sample.rcp. |
sample/*.bin | Binary resource files as they are produced by PilRC from the definitions in sample.rcp. |
source/*.h | Various include files to create the Pila executable. See files for further details. |
source/*.c | The C source files of Pila. See files for further details. |
source/libiberty/* | Source files I copied from GNU gcc and modified to fit with the rest of the Pila sources. This is the beginning of my effort to incorporate a C pre-processor into Pila to make the Pila SDK obsolete. Not all of the files in this directory are currently used by this version of Pila. |
PalmOS user interface elements like menus, forms, and icons are instantiated by means of PalmOS 'resources'. Wes Cherry has written a resource compiler, PilRC, to make the task of creating resources easier. Aaron Ardiri picked up that development and you can get the latest version of PilRC from his site at http://www.ardiri.com/index.php?redir=palm&cat=pilrc. PilRC takes as input a textual description of an application's user interface elements and compiles them down to PalmOS binary resources. In turn, Pila can incorporate these binary resources into an application by means of the res directive.
The included sample program, Sample.asm, illustrates how this is done. Another nice tool to deal with the resource definition files (*.rcp) is PilRCEdit which is available through SourceForge.net. It basically is a WYSIWYG editor (what you see is what you get) for *.rcp files, which cuts the form editing cycle from edit, assemble, download, launch, preview, repeat to the much quicker edit, save, assemble, download and use.
After you create your program there's a good chance you'll be wanting to debug it. There are some emulators as well as debuggers for the PalmOS platform available. I personally downloaded my tools from http://www.palmsource.com/dev/tools. Sony - as another example - offers a modified version of the PalmOS Emulator for their Cliés together with the necessary ROM images at http://www.cliedeveloper.com/.
The Debugger I got from Palm Source supports symbols and with Pila's
command-line option -s you can include the procedure symbols in the generated
PRC file which makes debugging a little easier.
<FRANK>I hope I soon
find the time to add the possiblity to generate a symbol map file that can be
loaded into the debugger making ALL the symbols available and even allow for
source-code debugging (something that isn't all THAT important in assembler
programming but it really helps finding the bugs faster...<>FRANK>
1.5 Creating A Minimal Pilot Application With Pila
The purpose of this walkthrough is to demonstrate the Pila-specific aspects of writing a Pilot program, not to illustrate Pilot programming in general. I've kept the example as simple as possible and have even eliminated features that almost every Pilot application will contain (e.g., a main form, an event handling loop, an icon). The Pila Package includes a more complete application, Sample.asm, that illustrates form creation, event handling, and the like.
Start things off by opening your text editor with the new file "Hello.asm". You guessed it, we're going to recreate the classic "Hello world" application for the Pilot. The actual code will be kept very simple (just enough to invoke a dialog) because it's everything around the code that's important for you to know in order to use Pila. Enter or cut/paste the following lines of code as you see them and when you're done you'll have a ready-to-assemble application.
; Hello.asm -- a minimal Pilot application to be assembled by PilaThis is just a comment line. I've heard that all good programs should have at least one comment.
appl "Hello", 'hllo'The appl directive sets the application's name and id. The name will show up under its icon and the type must be unique amongst all Pilot applications. Palm Inc. maintains a registry to ensure app ids are unique. At http://www.palmos.com/ you can register your own unique IDs online.
include "PalmOS.inc"The *.inc include files needed with Pila are contained in this package.
These include files contain all the definitions for the structures and constants as well as the PalmOS APIs. Hello uses several of these symbols (e.g., FrmAlert, sysAppLaunchFlagNewStack). See Appendix B for further details on the include files. The include directive allows us to incorporate other files in our assembly.
include "startup.asm"A special startup sequence is needed to fully initialize your Pilot application after loading. This sequence is common to all applications so it is precreated for your convenience. IMPORTANT: the startup function must be the very first function in your program. Always include Startup.asm before any of your own code OR data. Also note the *.asm extension! By convention I use *.inc for files containing definitions only and not producing anything actually stored in the resulting PRC - contrary to *.asm files that MAY produce actual output.
kidrHelloAlert equ 1000 kidrHelpString equ 1001 kidrPREF equ 1 kidrTVER equ 1These equates define symbolic names for the various resources in the Hello apps. Symbolic names are easier to change later, if necessary.
codeAfter a code directive all code and data bytes are placed in the code section of the Pilot executable. Similarly, the data directive (not used in our Hello app) causes subsequent code and data to be placed in the data section of the application. The res directive causes subsequent code and data to be placed in the specified resource.
PilotMain proc (cmd.UInt16, cmdPBP.void*, launchFlags.UInt16).UInt16The startup code in Startup.asm calls the function PilotMain after it is done initializing the app. Three arguments are passed to PilotMain (cmd, cmdPBP, and launchFlags). Pila takes these arguments and generates suitable offsets from the base pointer (a6) for them. Note that the type of the argument must be specified after the period.
local err.w ; neither of these locals are used in this example local evt.EventTypeThis defines the local variables for PilotMain. Locals are also accessed via offsets from a6. The type of the local variable is specified after the period following the variable name.
beginprocThis marks the beginning of the procedure. Pila will automatically insert a link a6,#nnnn instruction where nnnn is the total size of all locals. This saves the old base pointer and sets up a new one for this function. Buy a book on 68000 assembly language programming if this doesn't mean anything to you.
tst.w cmd(a6) ;sysAppLaunchCmdNormalLaunch is 0 bne Return ;not a normal launch, bag outAs covered in USRobotics' documentation, PilotMain is called under many circumstances -- including at HotSync time! This test ensures "Hello world!" only pops up when we want it to (e.g., not during hotsync, not during 'find' operations).
call FrmAlert(#kidrHelloAlert)call is a Pila directive which emits the code to push the arguments on the stack, call the trap and then clean up the stack. It will emit code equivalent to:
move.w #kidrHelloAlert,-(a7) ;push alert id on stack trap #15 ;PalmOS API call dc.w $A192 ;invoke the alert dialog (sysTrapFrmAlert==$A192) addq.l #2,a7 ;pop alert id off stackThis is the assembly language equivalent to 'FrmAlert(kidrHelloAlert);' in 'C' and illustrates how to call PalmOS APIs. The size of the parameter (#kidrHelloAlert) and the trap number ($A192) are both taken from the definition of the FrmAlert API in the Form.inc in the Pila SDK where you can find the following line:
FrmAlert trapdef [sysTrapFrmAlert](alertId.UInt16).UInt16For which in turn the definition of sysTrapFrmAlert is found in CoreTraps.inc:
sysTrapFrmAlert equ 0xA192See Appendix C for more information on PalmOS API calling conventions and the trapdef directive for information on the syntax of this line. In the end, all the PalmOS APIs are called through the 'trap' mechanism, similar to the Macintosh mechanism for calling Toolbox routines.
Return endprocendproc emits the unlk and rts instructions for you. It is required and must follow a previous beginproc.
This completes the PilotMain function. That's the entire program code for Hello.
Next we move on to the resource section that most applications will have. Typically PilRC would be used to create these resources but they can also be defined inline as demonstrated here:
res 'Talt', kidrHelloAlert ;Alert resource dc.w informationAlert ;alertType dc.w kidrHelpString ;helpRscID dc.w 1 ;numButtons dc.w 0 ;defaultButton dc.b "Red Alert", 0 ;title text dc.b "Hello world!", 0 ;message text dc.b "Most excellent!", 0 ;button textThe res directive is used here to define an Alert ('Talt') resource that is read by the FrmAlert API and used to build the dialog box you'll see. This is the complete data definition of the resource.
res 'tSTR', kidrHelpString dc.b "I am a Pilot programming stud!", 0You may have noticed that the Alert resource references a help resource. This shows up when the user clicks on the little info symbol in the upper-right corner of the dialog box. We use the res directive again to define the help string (not very helpful in this case).
res 'tver', kidrTVER dc.b "1.0", 0All applications should have a version resource. I'm not sure this is enforced anywhere but it's a good idea anyway.
res 'pref', kidrPREF dc.w sysAppLaunchFlagNewStack|sysAppLaunchFlagNewGlobals|sysAppLaunchFlagUIApp|sysAppLaunchFlagSubCall dc.l $1000 ; stack size dc.l $1000 ; heap sizeThe 'pref' resource defines the application launch flags, stack size, and heap size.
Well, that's all there is to it. Save Hello.asm and assemble it with the command "pila20 Hello.asm". If you got it right, out pops Hello.prc. Use instapp.exe ("instapp Hello.prc") and HotSync to download Hello to your Pilot. Press the applications button on your Pilot and you'll see Hello there, without an icon. It is valid but not very interesting to have an icon-less program. Hello was done this way to keep this example short. Follow the proper example in Sample.asm to define icons for your own apps.
Select Hello from the applications dialog and, baring data entry errors, you'll see "Hello world!". Try the info/tip icon. Sooner or later your curiosity or boredom will lead you to press the "Most excellent!" button. As you can see from the code, when FrmAlert returns PilotMain simply exits. When you try this you'll find that the program doesn't disappear. Why not? Pilot applications are executed by the 'Shell' and the rule is that one of them must be running at all times. If it terminates and returns to the Shell, the Shell just launches it again! Leave Hello by pressing one of the other application buttons.
Note that this fine piece of software engineering is only ~400 bytes long.
The Pilot Memory application only reports application sizes rounded up to the
nearest 'K' so most users won't fully appreciate your studliness at producing
such a micronic application. In the face of this ignorance I recommend the time
proven software development practice known as "bragging". The important thing is
that the memory savings are real.
Pila [options] sourcefileOptions specify options that modify assembler actions. Precede each option with a '-'. Separate options with spaces.
Option | Description |
l | Generate a listing file. The listing file has same name as the source file suffixed with '.lis'. |
c | Show full constant expansions for DC directives. |
d | Generate a bunch of debug output. This option is of little use to most people. |
s | Generate MacBug-compatible symbols the PalmDebugger can understand. |
r | Do not generate 'code' and 'data' resources. |
t TYPE | Set the output PRC database's type to the specified four characters |
Pila assembles the sourcefile, integrates any resources, and outputs a Pilot resource database (PRC) file with the same name as the source file suffixed with '.prc'. The application's name as it appears on the Pilot is defined by the appl directive or a 'tAIN' resource, not its DOS/Windows file name.
Listing files show the expansion of any directives that generate code or additional symbols (beginproc, endproc, call etc.). Listing output can be enabled and disabled from within a program by using the list directive. An easy way to suppress the listing of all the standard include files! Just put list 0 before your include "PalmOS.inc". And add a list 1 behind it to get a listing of your code.
Pila generated code symbols are produced inline in the code section directly following each procedure. The symbol format follows that of MacsBug and is compatible with Copilot's debugger. As of this version, only code symbols for procedure entries are supported, no data symbols.
With the '-r' option Pila becomes more of a PRC builder than an assembler. It collects all the resources specified with res directives and bundles them into a valid Resource Database (PRC) that can be downloaded to the Pilot. In this mode Pila has two main uses. First, an experimenter could deconstruct an existing PRC file with prc2bin, modify any of its resources, and reconstruct the PRC file (unaltered aside from the intentional changes) with Pila. Second, Pila can be used as a backend for any language or tool that can generate the appropriate 'code' and 'data' (or other) resources. Used this way, the language/tool would generate binary files containing code and/or preinitialized data and a small assembly stub, basically just a list of res directives, would be used to include those resources along with any other resources, say, forms and menus created with PilRC, that were desired for the final PRC.
With the '-t' option one can override the default database type of 'appl'. Generally this isn't something you'd want to do.
Pila follows the Motorola syntax for the 68000 microprocessor. The general format of a statement is:
[Name] [Operation] [Operands] [;Comment]Name defines a label that can be accessed from elsewhere in the program. If the statement has a data directive (e.g., dc, ds), this field is a variable name. If the statement has an instruction, this field is a code label. Names must start with a letter or one of the characters '_', '?', '@' (no '$'!) and may contain letters, numbers and the characters '_', '?', '$', '@'.
Furthermore there are special temporary labels that can only be used in the code section. They start with a '.' (dot) followed by one digit (1..9). These temporary labels are referenced by i.e. '.1b' or '.2f' which are a backward-reference to the temporary label .1 and a forward reference to the temporary label .2 respectively. In a code fragment like this:
doSomething proc (parm1.b,parm2.UInt16).UInt32 local localvar.w beginproc ... chkloop moveq #1,d0 cmpi #10,d1 bcc .1f addq #2,d0 .1 addq #1,d0 ... cmpi #5,d0 bcc .1f ... bra .1b .1 endprocthe first reference .1f and the reference .1b are therefore actually referencing the same - first - temporary label .1 whereas the second reference .1f is referencing the second temporary label .1. For clarity it would be wise to replace the second temporary label .1 with the temporary label .2 and avoid the confusion of which .1 is actually meant *wink* or even better use 'return' instead of the second .1 since all code labels defined within beginproc and endproc are LOCAL to the procedure they are defined in! That means you can have a label 'return' in every procedure. To reference a label in another procedure or from outside any procedure you have to prefix the label with the procedure's name it is defined in. In our above example chkloop could thus be referenced as 'doSomething.chkloop' from anywhere outside the procedure doSomething (and from inside as well if you wish to do so *grin*).
[Note: if you have a global code label 'return' outside any procedure and another one inside some procedure you won't be able to reference the global 'return' from inside that procedure since there is no prefix for global labels and just using 'return' will always reference the procedure's local label 'return'.]
Names are case-sensitive and significant to fifty-two characters. They must either start in column 1 of the line or - if indented with some whitespace characters - must be followed by a ':' (colon) which in the other case is optional.
Operation states the action of the statement. This field is either a 68000 instruction or a directive and is case-insensitive. The Motorola syntax has instructions suffixed by the size of data they operate on (e.g., clr.b, add.w, move.l). When the data size is unspecified, Pila assumes it's a word (same as a '.w' suffix). Pila does not support the MIT syntax which allows the period to be omitted from the data length suffix. A program that converts from MIT to Motorola format can be found at ftp://nyquist.ee.ualberta.ca/pub/motorola/portable/mit2mot.tar.gz.
Operands list the item(s) to be operated on. Operands are separated by commas.
Comment provides a comment for the user. This field is for documentation purposes only and is ignored by the assembler. Comments may begin with an asterisk if it is the first non-whitespace character in the line or a ';' (semicolon) anywhere in the line and outside of single- or double-quotes.
Symbols can be defined as labels (see Statement Format - Name above) or through the set or equ directives making it possible to give gibberish values like 0x03E3188F an meaningful name.
Symbols are also used to name types, structures, unions or enumerations and their respective members. The directives struct, union, enum and typedef are responsible for achieving that.
In general a symbol name is case-sensitive, must start with a letter or one of the characters '_', '@', '?' and can contain letters and digits and '_', '@', '?', '$'. It's significant length is 52 meaning if you have two symbols that are identical in the first 52 characters, Pila will not distinguish between them and you will get some sort of re-definition error.
All global symbols are thrown into the same pot. That means you can't have a symbol 'Event' representing a structure and at the same time defining a procedure of the same name. This is not the case for parameter-names, local variables or labels defined inside a procedure since all those are local to the procedure and have to be unique only within the procedure's context. The global representation of those symbols is prefixed with the procedure's name and thus becomes unique again in the global context.
Constants can be single-quoted strings of up to 4 characters that are padded with \0 on the left to full 32 bits or numeric constants.
To indicate the radix of a numeric constant Pila allows the following
prefixes:
Specifier | Radix | Sample |
% | Binary | %10011010 |
0 | Octal | 0773 |
none | Decimal | 123 |
$ | Hexadecimal | $1Fa |
0x | Hexadecimal | 0xAB8 |
0X | Hexadecimal | 0Xcf1 |
Floating-point constants are not supported.
For improved legibility dots
('.') can be inserted as separators into the numeric constant anywhere after the
first digit. They will just be ignored.
Hexadecimal digits can be upper or
lower case.
A suffix of u, l or ul (either upper or lower case) can be
specified but has not effect and is simply ignored. This was done for
compatability reasons with the generated include files which contain constants
like this from the original C include files.
All constants are assumed to be
32 bits. No automatic sign extension is done, therefore $FF00 is different from
$FFFFFF00 but -$100 and $FFFFFF00 are the same.
Sorted by order of precedence from highest to lowest:
Operator | Syntax | Description |
-------------------------------------------------------------------------------------------- | ||
. | structure.member | member reference (also for union and a procedure's local labels) |
-------------------------------------------------------------------------------------------- | ||
- | - expression | Unary minus |
~ | ~ expression | One's complement |
-------------------------------------------------------------------------------------------- | ||
(, ) | ( expression ) | Parenthesized expression |
-------------------------------------------------------------------------------------------- | ||
<< | expression << count | Shift left |
>> | expression >> count | Shift right |
-------------------------------------------------------------------------------------------- | ||
| | expression | expression | Logical OR |
& | expression & expression | Logical AND |
-------------------------------------------------------------------------------------------- | ||
* | expression * expression | Multiply |
/ | expression / expression | Divide |
\ | expression \ expression | Modulo |
-------------------------------------------------------------------------------------------- | ||
+ | expression + expression | Add |
- | expression - expression | Subtract |
The only currently implemented built-in function is
sizeof(Symbol)which returns the size of the specified symbol. In the case of the symbol being the name of a type, the type's size is returned (i.e. sizeof(char)==1). In the case of the symbol being something else than a type, the size of the symbol's type is returned. If the symbol doesn't have a type associated with it, 0 is returned.
This chapter describes all the different directives Pila understands. When specifying a directive in your source code make sure it does not start in column 1 of the line because in that case Pila will interpret it as a label and not the directive you want it to be!
If the appl
directive is omitted Pila will default the application name to its file name
(e.g., "Sample.asm" becomes "Sample") and the four character id will be
'temp'.
You change generation scope as many times as you want, interspersing
code and data.
In general, read-only variables (e.g., constant strings) should be defined within the code scope to save runtime memory. Code is accessed in-place in Storage memory while data is duplicated in Dynamic memory at application load time. Note, if you embed data within your code, use the align directive before any subsequent code to ensure it is placed on a two-byte boundary. If you don't and the alignment is not on a two-byte boundary, Pila will do the alignment implicitly and issue a warning.
As this implies, code can be assembled into the data section.
Doing so is ill-advised because code in the data section is duplicated in
Dynamic memory at load time, whereas code in the code section is executed
in-place in Storage memory. But if you want to do some sort of wicked
self-modifying hack you can.
Be sure to include a data directive before defining any writable global variables. Failing to do so will result in a memory error when your code attempts to write to the variable (stored in read-only Storage memory!).
The line with the enum directive is followed by 0 or more lines with member definitions of the form name [= initializer] where name is a valid symbol name (see 2.3 Symbols) and the optional initializer is a numeric expression.
Finally the directive endenum is finishing the definition of the enumeration. See also Appendix B for a discussion of the enumeration support in Pila.
WrapWinDrawChar proxy (theChar.WChar,x.Coord,y.Coord).voidit issues the following code:
WrapWinDrawChar move.l (a7)+,6(a7)Thus effectively removing the return address from the stack and storing it in the space the call directive reserved for it automatically. Now the proxy code can add additional parameters or modify the ones already on the stack before making a call to the intended routine. Here is the WrapWinDrawChar proxy code from my Sony HiRes-Wrapper utility code as an example for the use of the proxy directive:
WrapWinDrawChar proxy (theChar.WChar,x.Coord,y.Coord).void tst.b hiresMode(a5) ; are we running on a SonyClie with HiRes capabilities beq.s lowres ; nope - just call the normal WinDrawChar move.w sonyHRLibRefNum(a5),-(a7) ; yes - push the library reference number on the stack trap #sysDispatchTrapNum ; and call the HRWinDrawChar routine by Sony dc.w HRTrapWinDrawChar ; with the parameters (sonyHRLibRefNum,theChar,x,y) addq.l #2,a7 ; clean up the additional parameter bra.s return ; and get the hell out of here lowres trap #sysDispatchTrapNum ; for the low res case use the PalmOS routine WinDrawChar dc.w sysTrapWinDrawChar return endproxy ; restore the return address and return to callerThe directive endproxy emits code to restore the return address from the location it was saved before and a rts instruction. Therefore the actually assembled code for this proxy looks like this:
WrapWinDrawChar move.l (a7)+,6(a7) ; save return address in reserved space tst.b hiresMode(a5) ; are we running on a SonyClie with HiRes capabilities beq.s lowres ; nope - just call the normal WinDrawChar move.w sonyHRLibRefNum(a5),-(a7) ; yes - push the library reference number on the stack trap #sysDispatchTrapNum ; and call the HRWinDrawChar routine by Sony dc.w HRTrapWinDrawChar ; with the parameters (sonyHRLibRefNum,theChar,x,y) addq.l #2,a7 ; clean up the additional parameter bra.s return ; and get the hell out of here lowres trap #sysDispatchTrapNum ; for the low res case use the PalmOS routine WinDrawChar dc.w sysTrapWinDrawChar return move.l 6(a7),-(a7) ; restore the return address rts ; and return to callerAs you may have recognized, this proxy can only be called after the two global variables hiresMode and sonyHRLibRefNum are properly set *wink* This job is done in a normal procedure WrapHiresInit of my Sony HiRes-Wrapper utility code that is called at the very beginning of any of my programs using this wrapper.
The second form of the res
directive allows the resource data to be read from a binary data file rather
than defined inline. Use the second form to include resources generated by Wes
Cherry's Pilot Resource Compiler (PilRC).
trapdef trapname '['trapnumber[:selector[.w]]']'([argname.type][,argname.type]...[,...])
Type Name | Length in Bytes |
---|---|
void | 0 |
int | 2 |
float | 4 |
double | 8 |
char | 1 |
b | 1 |
w | 2 |
l | 4 |
d | 8 |
typedef FormEventHandlerType.(eventP.EventType*).BooleanNote that it is not possible to use modifiers in this case since those would just apply to the return type of the entry point specification. To define a pointer to an entry point use typedef to create an alias (like the FormEventHandlerType above) and then use
typedef FormEventHandlerPtrType.FormEventHandlerType*to define a pointer to the entry point. Use the same technique for arrays.
Appendix A: The Making Of Pila
The first time I saw a Pilot I was struck by all kinds of software ideas -- programs I could create to make this device more useful and entertaining for me and for others. If only I had the tools to do so. First, I needed a Pilot. Pay out the cash, no problem. Then I needed software development documentation. After a while, USRobotics released SDK documentation on their FTP site . Great! Now all I needed was a set of PC-based cross-development tools for creating Pilot applications. USRobotics' SDK? Mac only. Hmm...
I tired of hoping for a PC-based SDK and started building my own tools. My first set of tools leveraged the IDE and 68000 C Compiler of Microsoft's Visual C++ Cross-Development Edition for Macintosh . This is swell but has two drawbacks. First, Visual C++ Mac edition can be quite expensive (I don't want to create all the Pilot apps myself!) and second, it doesn't come with a 68000 assembler. Perhaps I could do something about this.
To my surprise (before I realized that anything can be found on the Net if one looks long enough) there are several public domain assemblers for the 68000. A few are available in source form and one of them, an assembler written by Paul McKee at North Carolina State University in 1986, looked like a good match for the job. I decided to build my Pilot assembler on this base. You can find Paul McKee's original assembler and a partially working 68000 emulator ( 68asmsim.zip ) where I did on a backup site for Motorola's BBS ( ftp://nyquist.ee.ualberta.ca/pub/motorola ). There's lots of other great stuff there.
With many thanks to Paul McKee I started hacking his assembler to meet my needs. I wanted the assembler to produce fully formed Pilot executables without the need for an external linker or resource compiler. The lack of a linker may make creating extremely large applications more difficult (good!) but it saves time now and I can write one later if needed. A resource compiler would certainly be nice but I'd rather spend my time creating high level GUI tools for resource creation and editing. In the meantime, a tweak or two to the assembler would let it do the job of integrating resources into the final executable.
So I modified the assembler to build a list of resources as it assembled. With the help from some new directives, code becomes one resource, data becomes another, and additional Pilot-specific resources can be defined in-line or included. I borrowed some PRC-building code from exe2prc , a tool I created for converting Win32 EXEs into Pilot PRCs, and bolted it to the end of the assembler to convert the final collection of resources into something the Pilot could recognize.
Wes Cherry had some great ideas for some new directives and syntax for Pila to make many common operations (procedure parameter definition, procedure calling, structure definition, API calling, local and global variable definition) as easy as they are in high level languages like 'C'. Wes took a copy of the Pila sources, implemented his (now indispensable) extensions, fixed a couple Pila bugs, and even updated the documentation!
<Mikael Klasson signing on> Unfortunately, I have to write this myself so you'll have to judge if my ideas are great or not... ;) Anyway, I too have made some extensions to Pila: local labels, the incbin directive and compression of the datasection. <Back to Darrin>
Other tools have arrived on the scene to help complete the set of Pilot development tools. Wes Cherry's PilRC is a resource compiler that saves hours of time and makes things possible that wouldn't be practical without it. Greg Hewgill's Copilot Pilot Emulator includes a Pila-compatible symbolic debugger. Bill Hunt's PilDis is a disassembler that can provide a peek into the innards of existing Pilot applications.
<Frank Schäckermann signing on>
That was about five years ago. In
the meanwhile I bought a Sony Clie and to my surprise learned that it contains a
Motorola CPU (actually I bought the thingy for my wife *wink*). Now... a good 12
years ago I built my own computer (soldering and everything) which was based on
a M68020. On that computer I programmed pretty much everything myself and - to
whom's surprise - fell in love with the assembler programming of that CPU. Later
on my job required me to change to Intel based computers and the times of 68K
assembler where over. Until I bought the Sony Clie. Once I found out there is a
68K CPU ticking in it finding a possibility to program it in assembler became a
MUST. Some searching on the Internet turned up Pila. A Pilot assembler - exactly
what I needed. But the thing was hopelessly out of date (Pilot.inc was at Palm
OS 2.0 level and the Clie has Palm OS 4.1 on it) and my first project turned up
quite a few bugs in the symbol handling. Hence I downloaded the source and set
to work. The results are documented in this updated user manual.
<Frank
Schäckermann signing off>
The Pila SDK was generated by a fairly complex REXX script that I (Frank Schäckermann) wrote. It is based on the - for the process slightly modified - C header files of SDK 5 from Palm Source Inc. and the SDK 5 Version 1.2 from Sony for i.e. the high resolution (320x320) the Sony Clie is capable of. I generated the *.inc files in a way that preserved all the original comments in the SDK and hopefully made it just as easy to use as the original SDK.
Having done all that - and knowing perfectly well, that Pila without this SDK is pretty useless - I realized, that I can't distribute my version of the SDK with Pila since the copyright agreement I 'signed' when I downloaded the Palm OS SDK from PalmSource Inc. doesn't allow me to. I finally figured out a way how to get around this problem (see below). Nevertheless I am working on a Pila version that can actually read the original C-header files directly. But that project is probably a few month away from completion since I basically have to incorporate a C preprocessor into Pila to achieve that...
Until then I guess everybody will have to go through a certain process to generate the Pila SDK from the original header files using the program and templates supplied with the Pila package. Here is what I did and what you have to do to get the necessary Pila SDK installed on your machine.
What I did to create the *.sdk files distributed with this package
After I had written the REXX script, modified the C header files in a few places to take SOME complexity out of the things the REXX script had to do and used the script to transform the C header files into .inc files for Pila (each .h file has a corresponding .inc file with the exception of PalmCompatibility.h which I deemed unnecessary since the Pila SDK hasn't been in existence before) I realized that this process would be just too horrible for anybody to go through just to get the Pila SDK - apart from the fact that you'd have to install REXX for the script to run and gcc for the C pre-processor the script uses (well... you'll need gcc anyway if you want to compile Pila yourself *grin*).
It took me an alarming long time to come up with an idea to solve the dilemma and this is what I came up with:
Since all the identifiers in the corresponding .inc and .h files are the same I wrote a program that enumerates all the different words (consequetive alphanumeric and '_' (underscore) characters) found in the .h file and replaces these words in the .inc file with @<number>@ where number is the word's number in the .h file and every single '@' (at) in the .inc file is replaced with @@. The result is stored in a .sdk file which is distributed with this package.
To illustrate what's happening look at the following example:
let's
assume a line in the .h file reads
All of the above is accomplished by the encode-pila-sdk script found in the pila-sdk subdirectory of the Pila-2.0 directory. Even though the encode-pila-sdk is distributed with this package it is not really of any use to YOU except if you want to create your own .sdk files after you modified the .inc files. The real transformation work is done by the program transform-sdk. This program receives as a parameter the name of a file containing a list of files it is supposed to work on. Depending on the suffix of those files it will transform sdk files into inc files (if the suffix is sdk) or vice versa (if the suffix is inc). Thus the same program can be used to encode and to decode the Pila SDK.
What you have to do to recreate the .inc files
Now on YOUR computer - if you use the very same .h files as I used on mine for the encoding - you can easily reverse the process by replacing any @<number>@ with the corresponding word from the corresponding .h file and replacing every @@ with @ and this way decode the .sdk files back into .inc files. Voila! Cool, isn't it?
The only problem is, that you will have to make sure you use EXACTLY the same .h files as I did because otherwise the number of some words in your .h file might be different for the decoding than it was for the encoding and the resulting .inc file will be garbage.
To give you some kind of security I did two things: first I wrote a bash script (decode-pila-sdk) that will look for the palmos-sdk-5.0-1.tar.gz file and the Sony SDK files cliesdk30e.zip and cliesdk50e.zip (which you will have to download - see below) and check their sizes to make sure they are the same as the ones I used. The cliesdk*.zip files are optional since not everyone will need the Sony SDK available to Pila. Once that is verified the files are unpacked, shuffled around a little, the .sdk files are unpacked from the distributed file pila-sdk-5.zip and the program transform-sdk is called with a list of all .sdk files just unpacked and will transform every .sdk file that has a corresponding .h file into an .inc file. The resulting mess is cleaned up and the .inc files kept in the inc directory in the pila-sdk subdirectory of your Pila installation. Hurra! (anybody still with me?)
The second thing I did to make sure things don't get messed up is to calculate the CRC32 checksum of the .inc file as it exists on my computer (leaving out the \r bytes to avoid problems with the difference between Windows- and Unix-based text files) and appending the resulting 16 hex characters together with a prefix of *@*' at the end of the .sdk file. The transform-sdk program on your computer will then create the CRC32 checksum of the .inc file it creates on your computer and compares it with the value found in the .sdk file. If the two match we can safely assume that you've got the same .inc file on your computer now as I had on mine before the whole mess started.
Okay... now you know how the process goes principally. And here are the step by step instructions to actually get it done:
A 'C' structure typedef like this:
typedef struct _FormLabelType { Word id; PointType pos; FormObjAttrType attr; FontID fontID; Char *text; struct _Nested { more1 Word; more2 char; } nested; } FormLabelType, *FormLabelPtr;Becomes these struct and typedef definitions:
struct _Nested more1.Word more2.char endstruct struct _FormLabelType id.Word pos.PointType attr.FormObjAttrType fontID.FontID text.Char* nested._Nested endstruct typedef FormLabelType._FormLabelType typedef FormLabelPtr._FormLabelType*Members nested inside of structures containing structures can be reached by using the . (dot) operator. For pointers to structures the type of the pointer is declared by the structure name. For example, after loading a pointer to a FormLabelType structure into A0 one can use FormLabelType.pos.x(A0) to perform the equivalent to the 'C' operation formLabel->pos.x. For a local declared as local formLabel.FormLabelType one would use formLabel.pos.x(a6). A similar global would be formLabel.pos.x(a5). There will be a warning issued if you reference a global variable without using the address register a5 or a local variable or parameter without using the address register a6.
Unions are now fully supported by Pila. Just use the the period to reference any of the union members the same way you do with structures as shown above.
A 'C' union typedef like this:
typedef struct HelperNotifyEventTypeTag { UInt16 version; HelperNotifyActionCodeType actionCode; union { struct HelperNotifyEnumerateListTypeTag* enumerateP; struct HelperNotifyValidateTypeTag* validateP; struct HelperNotifyExecuteTypeTag* executeP; } data; } HelperNotifyEventType;Becomes these struct, union and typedef definitions:
struct HelperNotifyExecuteTypeTag endstruct struct HelperNotifyValidateTypeTag endstruct struct HelperNotifyEnumerateListTypeTag endstruct union _union178 enumerateP.HelperNotifyEnumerateListTypeTag* validateP.HelperNotifyValidateTypeTag* executeP.HelperNotifyExecuteTypeTag* endunion struct HelperNotifyEventTypeTag version.UInt16 actionCode.HelperNotifyActionCodeType data._union178 endstruct typedef HelperNotifyEventType.HelperNotifyEventTypeTag
Note the name _union178 for the union that was used 'inline' in the C code. Since Pila does not understand nested struct or union definitions (yet) I had to break them apart into multiple definitions of which a few didn't have any name. Thus my script generated unique names of the format _union<counter>.
A bitfield is an interesting construct combining two properties: a count of bits and a bit offset within a number. An operation often performed with bitfields, especially single-bit fields, is a masking operation. I wanted to make both properties of bitfields available to assembly language programmers and to make masking operations convenient as well. So a set of bitfields like this:
typedef struct FieldAttrTag { UInt16 usable:1; // Set if part of ui UInt16 visible:1; // Set if drawn, used internally UInt16 editable:1; // Set if editable UInt16 singleLine:1; // Set if only a single line is displayed UInt16 hasFocus:1; // Set if the field has the focus UInt16 dynamicSize:1; // Set if height expands as text is entered UInt16 insPtVisible:1; // Set if the ins pt is scolled into view UInt16 dirty:1; // Set if user modified UInt16 underlined:2; // text underlined mode UInt16 justification:2; // text alignment UInt16 autoShift:1; // Set if auto case shift UInt16 hasScrollBar:1; // Set if the field has a scroll bar UInt16 numeric:1; // Set if numeric, digits and secimal separator only UInt16 reserved:1; // Reserved for future use } FieldAttrType;Becomes these struct and typedef definitions in Pila:
struct FieldAttrTag usable.UInt16:1 ; Set if part of ui visible.UInt16:1 ; Set if drawn, used internally editable.UInt16:1 ; Set if editable singleLine.UInt16:1 ; Set if only a single line is displayed hasFocus.UInt16:1 ; Set if the field has the focus dynamicSize.UInt16:1 ; Set if height expands as text is entered insPtVisible.UInt16:1 ; Set if the ins pt is scolled into view dirty.UInt16:1 ; Set if user modified underlined.UInt16:2 ; text underlined mode justification.UInt16:2 ; text alignment autoShift.UInt16:1 ; Set if auto case shift hasScrollBar.UInt16:1 ; Set if the field has a scroll bar numeric.UInt16:1 ; Set if numeric, digits and secimal separator only reserved.UInt16:1 ; Reserved for future use endstruct typedef FieldAttrType.FieldAttrTagFor each bit-field-member there are automatically three sub-members defined. I.e. the symbol FieldAttrTag.justification has the value 0 since it references the first UInt16 value in the structure which has an offset of 0.
ANDI.W #!(FieldAttrTag.justification.mask<<FieldAttrTag.justification.shift),FieldAttrTag.justification(a0)to clear the justification bits in a FieldAttrTag structure pointed to by a0. Now once Pila gets macro capabilities (yes, I am working on it) there will be a way to do this with a little less typing *wink*.
Along with the structure and union support came the support for enumerations and it is pretty straight forward. This is the enumeration of Bitmap compression algorithms as it is found in Bitmap.h:
typedef enum { BitmapCompressionTypeScanLine = 0, BitmapCompressionTypeRLE, BitmapCompressionTypePackBits, BitmapCompressionTypeEnd, // must follow last compression algorithm BitmapCompressionTypeBest = 0x64, BitmapCompressionTypeNone = 0xFF } BitmapCompressionType;The Pila digestible definition of this enumeration is the following:
; Compression Types for BitmapVersionTwo enum _enum12 BitmapCompressionTypeScanLine = 0 BitmapCompressionTypeRLE BitmapCompressionTypePackBits BitmapCompressionTypeEnd ; must follow last compression algorithm BitmapCompressionTypeBest = $64 BitmapCompressionTypeNone = $FF endenum typedef BitmapCompressionType._enum12
Here again a temporary unique identifier was generated by the automatic translation and the real name of the enumeration is established with a typedef directive defining the proper name for it. The elements of the enumeration become globally available constants and don't have to be prefixed with the enumeration name. Just like it is in 'C' as well.
APIs are called by executing a trap #15 followed by a two-byte API index. The PalmOS trap handler uses the API index to look up the API's address and calls it. API indexes are named the same as the APIs preceeded by 'sysTrap'. So, 'EvtGetEvent' becomes 'sysTrapEvtGetEvent'. The systrap directive makes it easy to call APIs. When using systrap specify the trap name without the sysTrap prefix, i.e., systrap EvtGetEvent(&evt(a6), #evtWaitForever)
<Frank>
Please note that call
EvtGetEvent(&evt(a6),#evtWaitForever) will accomplish exactly the same
without requiring the knowledge, that EvtGetEvent is actually an API to be
called via TRAP and not a procedure. This is why the use of the directives systrap
and syslibtrap
is depricated and should be replace with the directive call.
</Frank>
Appendix C: PalmOS API Calling Conventions
All I know about the PalmOS API calling conventions I've discovered by examining MetroWerks-compiled PRC files, the Pilot ROM, and quite a bit of trial and error. I think this information is correct but stay alert as you write your code.
The PalmOS uses the 'C' (aka cdecl) calling convention. The caller pushes the arguments on the stack last to first (right to left) and is responsible for popping them off after the API returns. APIs preserve all registers except D0, D1, D2, A0, and A1. APIs that return pointers return them in the A0 register. All other APIs return their values in the D0 register.
All arguments are word-aligned on the stack as per Motorola conventions. That is, a byte pushed on to the stack actually consumes a word with the byte value in the upper half of the word.
Several registers have special purposes. As mentioned above, D0 and A0 are
used to return values from APIs. A7 is the stack pointer. A6 is used as a local
frame base pointer. A5 points to a location within the application's data
section. All global variable accesses are performed relative to A5.
Appendix D: 68000 Instruction Set Quick Reference
I found this at http://www.freeflight.com/fms/comp/CPUs/68000.txt and will save you some time by reprinting it here.