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


Changes from Version 1.0 Beta 3 fluff 7 to Version 2.0

All this required two things:

  1. Pila had to become a 3-path assembler
  2. with Pila you now need a set of include files containing all the structures, unions, types and trap-definitions found in the Palm OS SDK (see Appendix B for a discussion of the Pila SDK).


 

 

 

 

Table Of Contents

1   Pila
  1.1 Introduction
  1.2 The Pila Package
  1.3 PilRC
  1.4 Emulator & Debugger
  1.5 Creating A Minimal Pilot Application With Pila
 
2   Pila Syntax
  2.1 Pila Command-Line Syntax
  2.2 Statement Format
  2.3 Symbols
  2.4 Constants
  2.5 Operators
  2.6 Built-in Functions
 
Appendix
  A The Making Of Pila
  B Pila SDK
  C PalmOS API Calling Conventions
  D 68000 Instruction Set Quick Reference
3   Pila Directives
  3.1 align
  3.2 appl
  3.3 beginproc
  3.4 call
  3.5 code
  3.6 data
  3.7 dc
  3.8 dcb
  3.9 ds
  3.10 else
  3.11 end
  3.12 endenum
  3.13 endif
  3.14 endproc
  3.15 endproxy
  3.16 endstruct
  3.17 endunion
  3.18 enum
  3.19 equ
  3.20 error
  3.21 extern
 
     
  3.22 global
  3.23 if
  3.24 ifndef
  3.25 ifdef
  3.26 incbin
  3.27 include
  3.28 list
  3.29 local
  3.30 proc
  3.31 procdef
  3.32 proxy
  3.33 reg
  3.34 res
  3.35 set
  3.36 struct
  3.37 systrap (depricated)
  3.38 syslibtrap (depricated)
  3.39 trapdef
  3.40 typedef
  3.41 union

Chapter 1: Pila

1.1 Introduction

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

1.2 The Pila Package

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.

1.3 PilRC

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.

1.4 Emulator & Debugger

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 Pila
This 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     1
These equates define symbolic names for the various resources in the Hello apps. Symbolic names are easier to change later, if necessary.
        code
After 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).UInt16
The 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.EventType
This 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.
        beginproc
This 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 out
As 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 stack
This 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).UInt16
For which in turn the definition of sysTrapFrmAlert is found in CoreTraps.inc:
sysTrapFrmAlert  equ  0xA192
See 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
        endproc
endproc 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 text
The 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!", 0
You 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", 0
All 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 size
The '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.


Chapter 2: Pila Syntax

2.1 Pila Command-Line Syntax
Pila [options] sourcefile
Options 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.

2.2 Statement Format

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	endproc
the 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.

2.3 Symbols

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.

2.4 Constants

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.

2.5 Operators

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

2.6 Built-in Functions

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.

Chapter 3: Directives

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!

3.1 align

Syntax:
align size
Description:
The align directive forces emitted code and data to be aligned on the next multiple of size. The beginproc directive automatically emits an align 2 directive because all code must be aligned on two-byte boundaries.
3.2 appl
Syntax:
appl "applicationname",'apid'
Description:
The appl directive defines the application's name and unique four character identifier. The application name must be 31 characters or less and shows up under its icon in the Pilot application launcher if it is not overridden by a 'tAIN' resource. The application's identifier is supposed to be registered with USRobotics to guarantee that no other application uses the same identifier. If this identifier collides with that of another application on the same Pilot then all manner of nasty problems will ensue (e.g., hotsyncing of those applications and their data records won't work properly).


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'.

3.3 beginproc
Syntax:
beginproc
Description:
The beginproc directive marks the beginning of a procedure. It instructs Pila to emit the proper link a6,#nnn instruction where nnn is the negative sum of the sizes of all locals. It is not possible to specify a label in front of the directive beginproc.
[FIXME: make beginproc take an optional register list to be saved on the stack and thus have the necessary MOVEM.L instructions generated automatically by beginproc and endproc (yet another way to eliminate a potential source of programming bugs).]
3.4 call
Syntax:
[label] call procname([argument][,argument]...)
Description:
The call directive emits code to push the arguments on the stack, invoke the procedure/proxy/trap and then clean up the stack. call can be used to invoke procedures just as well as traps, lib-traps and proxies. Since Pila now keeps track of symbol types the necessary way of calling the different entry points is automatically selected based on the entry points type (proc, trap or proxy).
When passing pointers to locals or globals, prefix the argument with the & character. Constants must be prefixed with the # character.
Argument sizes do not have to be specified anymore since Pila will use the sizes from the proc, proxy, procdef or trapdef directives defining the call-target. The only exception from that rule are parameters of calls to a trap or procedure with a variable parameter list. In those cases - since Pila does not know about the types of those extra parameters - you will have to specify the type of each parameter by enclosing it with parenthesis and the right parenthesis must be followed by '.' (dot) and the type name.That will tell Pila how to push those parameters on the stack and how much to clean up afterwards again.
3.5 code
Syntax:
code
Description:
The code directive places the assembler in code generation mode. Pila is always considered to be in one of three modes: code generation, data generation, or resource generation. The mode dictates where any assembler output will reside. For example, a dc.b directive while in code generation mode places the constant data in the code section, not the data section. The directives code, data, and res set the compiler in the respective generation mode. The default mode is code generation.


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.

3.6 data
Syntax:
data
Description:
The data directive places the assembler in data generation mode. Pila is always considered to be in one of three modes: code generation, data generation, or resource generation. The mode dictates where any assembler output will reside. For example, a dc.b directive while in code generation mode places the constant data in the code section, not the data section. The directives code, data, and res set the compiler in the respective generation mode. The default mode is code generation.


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!).

3.7 dc
Syntax:
[label] dc.b initializer [, initializer]...
[label] dc.w initializer [, initializer]...
[label] dc.l initializer [, initializer]...
Description:
The dc (data constant) directive defines a list of constant data bytes, words, or longs. Initializers may be a numerical value, an expression, or a string. Strings are surrounded by double quotes and are not automatically null-terminated. The dc.w and dc.l directives force their data to begin on a word boundary.
3.8 dcb
Syntax:
[label] dcb.b blocksize, initializer
[label] dcb.w blocksize, initializer
[label] dcb.l blocksize, initializer
Description:
The dcb (data constant block) directive defines a constant block of bytes, words, or longs. The size (in bytes) of the constant block is the blocksize times the size of the block type (byte, word, long). The initializer is repeated blocksize times. Initializers may be a numerical value or an expression. The dcb.w and dcb.l directives force their data to begin on a word boundary.
3.9 ds
Syntax:
[label ds.b blocksize
[label ds.w blocksize
[label ds.l blocksize
Description:
The ds (data storage) directive defines a theoretically uninitialized (initialized to zero at run time) block of bytes, words, or longs. The size (in bytes) of the memory block is the blocksize times the size of the block type (byte, word, long). The advantage of ds over dcb should be that the space for the data is only allocated when the application loads. Unfortunately Palm OS does not support uninitialized global data. Therefore ds.w 4 is equivalent to dcb.w 4,0.
The ds.w and ds.l directives issue a warning and force their data to begin on a word boundary if the current output location is not already on a word boundary.
3.10 else
Syntax:
else
Description:
The else directive can be put in between an if, ifndef, ifdef and the corresponding endif directive to define code that is to be processed only if the condition of the if directive was not met.
3.11 end
Syntax:
end
Description:
The end directive forces Pila to stop assembly immediately. The end directive is optional.
3.12 endenum
Syntax:
endenum
Description:
The endenum directive marks the end of an enumeration definition that was started with the directive enum.
3.13 endif
Syntax:
endif
Description:
An endif directive is required for each if, ifndef, ifdef directive to specify the end of the conditionally processed source code.
3.14 endproc
Syntax:
[label] endproc
Description:
The endproc directive marks the end of a procedure. It instructs Pila to emit an unlk and rts instruction. If the '-s' (symbols) switch is specified on the command line then endproc also emits a PalmDebugger-compatible symbol at the end of the procedure following the MacsBug standard.
[FIXME: see beginproc]
3.15 endproxy
Syntax:
[label] endproxy
Description:
The endproxy directive is the equivalent for the proxy directive what the endproc is for the beginproc directive. For a more detailed description of what endproxy does see the documentation of the proxy directive.
3.16 endstruct
Syntax:
endstruct
Description:
The endstruct directive marks the end of a structure definition that was started with an according struct directive.
3.17 endunion
Syntax:
endunion
Description:
The endunion directive marks the end of a union definition that was started with an union directive.
3.18 enum
Syntax:
enumname enum
          [membername [= initializer]]...
         endenum

or alternatively it can be specified this way:

         enum enumname
           [membername [= initializer]]...
         endenum
Description:
The enum directive starts the definition of an enumeration. The name under which this enumeration can later be referenced as a type identifier must be specified as a label in front of enum. It is not possible to define an enumeration without a name.

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.

3.19 equ
Syntax:
name equ expression
Description:
The equ directive evaluates expression and assigns its value to name. After definition, all uses of name are replaced by its value. Unlike set, equ does not allow equates to be redefined.
3.20 error
Syntax:
error message
Description:
The error directive allows the programmer to issue compile time error messages similiar to the #pragma error directive of C compilers.
3.21 extern
Syntax:
name equ expression
Description:
The extern directive allows to define external symbols. But since Pila does not yet support linking the value of the symbol remains undefined and any references to it will produce an error message. The extern directive was included for future use and the possiblity to absorb some declarations made in the C header files of the Palm OS SDK without having to drop the declarations alltogether.
3.22 global
Syntax:
globalname global type

or alternatively it can be specified this way:

           global globalname.type
Description:
The global directive declares global variables. The global directive must occur after a data directive. For a description of valid global names see 2.3 Symbols and the possible specifications for the type are described with the typedef directive.
3.23 if
Syntax:
if expression
Description:
The if directive allows for source code to be processed conditionally. The code following the directive up to the corresponding else or endif directive (if no corresponding else exists) is included in the processing if the expression evaluates to anything other than 0.
If the expression evaluates to 0 the source code up to the corresponding else or endif (if no corresponding else exists) is skipped and not processed in any way.
3.24 ifndef
Syntax:
ifndef symbolname
Description:
The ifndef directive works just like the if directive except that instead of an expression a symbol name must be specified and the following source code is processed only if the specified symbol name is not defined.
3.25 ifdef
Syntax:
ifdef symbolname
Description:
The ifdef directive works just like the ifndef directive except that instead of processing the following source code if the symbol name is not defined it processes it if the symbol name is defined.
3.26 incbin
Syntax:
[label] incbin "includefile"
Description:
The incbin directive inserts the binary file includefile into the prc file. It is equivalent to doing a "dc.b" for each byte in the file, except that it's a lot faster.
3.27 include
Syntax:
include "includefile"
Description:
The include directive inserts source code from includefile into the current source file during assembly. If the path to includefile is not fully specified Pila looks for the include file in the following places, in order:
  1. the same directory as the source file
  2. the current directory
  3. each of the directories specified by the environment variable PILAINC (e.g., for Windows: set PILAINC=c:\common\include;c:\Pila\inc and for Linux: export PILAINC=~/common/include:~/Pila/inc)
  4. the directory pila20 is located in
  5. the 'inc' directory that is a sibling of the directory pila20 is located in. For example, if pila20 is in /ASDK/bin its sibling inc directory would be /ASDK/inc.
3.28 list
Syntax:
list expression
Description:
The list directive disables and enables output to the listing file. It only takes effect if the '-l' switch was specified on the Pila command line. If expression evaluates to 0 listing output is disabled, a result of 1 enables listing output.
3.29 local
Syntax:
localname local type

or alternatively it can be specified this way:

          local localname.type
Description:
The local directive declares procedure local variables. The local directive must occur after a proc directive and before a beginproc directive. For a description of valid local names see 2.3 Symbols and the possible specifications for the type are described with the typedef directive.
3.30 proc
Syntax:
procname proc ([argumentname.type][,argumentname.type]...[,...])[.returntype]

or alternatively it can be specfied this way:

         proc procname ([argumentname.type][,argumentname.type]...[,...])[.returntype]
Description:
The proc directive declares a procedure. Procedures may have zero or more arguments. For each argument a name and a type must be specified. See 2.3 Symbols for valid argument names and the typedef directive for valid type specifications (argument type and return type). The argument list can be ended with the special argument specification of '...' (three dots). In this case Pila will allow additional arguments in the call directive without issueing an error message.
The proc directive will define the symbol procname with the proper address and all the symbols for the arguments specified with it. Those symbols are local to the procedure and not accessible from outside the code wrapped by the following beginproc and endproc directives.
The returntype in the proc directive is optional and actually not used at all by Pila. But since the return value will either be returned in a0 (for addresses) or d0 and possibly d1 (for other values) it is generally a good idea to specify the return type for documentation purposes.
[FIXME: make sure Pila produces an error message if one tries to access the symbols by prefixing them with the procedure name (will be fixed in the next version that will have a much reworked symbol handling and a generalized concept of symbol context - see symbol.c for more details on this).]
3.31 procdef
Syntax:
procname procdef ([argumentname.type][,argumentname.type]...[,...])[.returntype]

or alternatively it can be specfied this way:

         procdef procname ([argumentname.type][,argumentname.type]...[,...])[.returntype]
Description:
The procdef directive's syntax is identical with the proc directive's. The only difference between the two is, that procdef can not be followed by any local or beginproc directives and that it is only used to define a procedure's name and arguments and not it's implementation.
3.32 proxy
Syntax:
proxyname proxy ([argumentname.type][,argumentname.type]...)[.returntype]

or alternatively it can be specfied this way:

          proxy proxyname ([argumentname.type][,argumentname.type]...)[.returntype]
Description:
The proxy directive can be used to create wrappers for procedures or trap calls with minimal overhead. It's syntax follows the one of the proc directive except that a proxy can not be defined with a variable parameter list ('...' as the last argument specification).
When Pila processes a proxy directive like this:
        WrapWinDrawChar proxy (theChar.WChar,x.Coord,y.Coord).void
it 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 caller
The 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 caller
As 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.
[FIXME: create a possiblity for proxies to use local variables and add parameters 'on the other side' of the parameter list (shouldn't be all THAT difficult).]
3.33 reg
Syntax:
name reg registerlist
Description:
The reg directive defines a named list of registers as accepted by the movem instruction. The Motorola convention for register lists is used. Registers are separated by '/' and register ranges are denoted by '-'. For example, d0/d1/d4-d7/a0-a3/a6 is a register list that includes the registers d0, d1, d4, d5, d6, d7, a0, a1, a2, a3, and a6. It is invalid to declare a range across data and address registers (e.g., d5-a3).
3.34 res
Syntax:
res 'type', id
res 'type', id, "datafile"
Description:
The res directive places the assembler in resource generation mode. Pila is always considered to be in one of three modes: code generation, data generation, or resource generation.. The mode dictates where any assembler output will reside. For example, a dc.b directive while in code generation mode places the constant data in the code section, not the data section. The directives code, data, and res set the compiler in the respective generation mode. The default mode is code generation.


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).

3.35 set
Syntax:
name set expression
Description:
The set directive evaluates expression and assigns its value to name. After definition, all uses of name are replaced by its value. Unlike equ, set equates can be redefined any number of times.
3.36 struct
Syntax:
structname struct
            [membername.type]...
           endstruct

alternatively it can also be specified this way:

           struct structname
             [membername.type]...
           endstruct
Description:
The struct directive declares a structure named structname. The members of the structure are specified next. See 2.3 Symbols for valid member names and directive typedef for valid type specifications.
Currently it is not possible to nest struct and union directives. To define nested structures and/or unions define the lower level ones first and then use their names as type specifications for the members of the higher level structure/union.
In a structure member definition the type specification can also be followed by ':' (colon) and an integer number. This defines a bit field where the type specifies the range of bytes it is part of and the integer number behind the colon defines how many bits the field is wide. See Appendix B.3 Bitfields for a more detailed discussion of bit fields.
[FIXME: make it possible to nest union and struct directives.]
3.37 systrap (depricated)
Syntax:
[label] systrap systrapname([argument][,argument]...)
Description:
The systrap directive is depricated. It is a functional subset of the call directive. Therefore use call instead of systrap!
3.38 syslibtrap (depricated)
Syntax:
[label] syslibtrap libtrap([argument][,argument]...)
Description:
The syslibtrap directive is depricated. It is a functional subset of the call directive. Therefore use call instead of syslibtrap!
3.39 trapdef
Syntax:
trapname trapdef '['trapnumber[:selector[.w]]']'([argname.type][,argname.type]...[,...])

or alternatively it can be specified this way:

         trapdef trapname '['trapnumber[:selector[.w]]']'([argname.type][,argname.type]...[,...])
Description:
The trapdef directive is equivalent to the procdef directive. But while a procdef defines the name and parameters of an otherwise still undefined procedure (no entry address is given with the procdef directive) the trapdef directive specifies all necessary information (trap number and possibly an 8 or 16 bit selector) to actually be able to call the defined trap routine. The trapdef directive must be preceeded by a label (the name under which the call directive will be able to emit the necessary code to execute the trap) and followed by a left bracket and an expression evaluating to the trap number of the trap to be defined. This expression can optionally be followed by a ':' (colon) and an expression evaluating to the selector. If the selector is a 16 bit selector the expression must be followed by '.w' (dot-w) or '.W' (dot-W). After that a right bracket is required followed by the list of arguments just like in the procdef directive.
3.40 typedef
Syntax:
typename typedef type

or alternatively it can be specified this way:

         typedef typename.type
Description:
The typedef directive can be used to define an alias name for any type or modified type. The typename needs to be a valid symbol name (see 2.3 Symbols) and the type specification can be any pre-defined or user-defined type with or without modifiers or an entry point specification.
Pre-defined types in Pila are:
Type Name   Length in Bytes
void 0
int 2
float 4
double 8
char 1
b 1
w 2
l 4
d 8

User-defined types are any structure-, union-, enum- or typedef-definitions.
Possible type modifiers are:
  pointer to a type
defined by appending '*' to the type name i.e. int*
  array of a type
defined by appending '[]' to the type name i.e. int[].
Inside the brackets you can specifiy the size of the array and for multi-dimensional arrays you can specify multiple sizes either within one pair of brackets separated by ',' (comma) or multiple bracket-pairs with size specifications. I.e. int[3][2][4] is the same as int[3,2][4] or int[3][2,4] or int[3,2,4].
The size specifications can be numeric expressions.

An entry point specification is a list of arguments enclosed by parenthesis and followed by '.' (dot) and the return type. I.e.
typedef FormEventHandlerType.(eventP.EventType*).Boolean
Note 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.
That is also the reason why you can't use an entry point specification as the type definition anywhere else than in typedef!
3.41 union
Syntax:
unionname union
           [membername.type]...
          endunion

or alternatively it can also be specified this way:

          union unionname
            [membername.type]...
          endunion
Description:
The union directive declares a union named unionname. The members of the union are specified next. See 2.3 Symbols for valid member names and directive typedef for valid type specifications.
Currently it is not possible to nest struct and union directives. To define nested structures and/or unions define the lower level ones first and then use their names as type specifications for the members of the higher level structure/union.
[FIXME: make it possible to nest union and struct directives.]

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>


Appendix B: Pila SDK

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

#define sysDispatchTrapNum 15

and the words would have the numbers 20 (for define), 21 (for sysDispatchTrapNum) and 22 (for 15) and the corresponding line in the .inc file would be
sysDispatchTrapNum equ 15

then the resulting line in the .sdk file would look like this
@21@ equ @22@

thus the resulting .sdk files don't contain any Palm OS specific information anymore and can be freely distributed with the Pila package. Well... at least something *wink*

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:

  1. Make sure you've got the infozip package installed on your computer (exists for Windows and Unix). This will give you the unzip program necessary to execute the following steps.
  2. Unpack the Pila-2.0.zip package with infozip's unzip program to a directory of your liking (i.e. ~/PalmDev): unzip Pila-2.0.zip -d ~/PalmDev
    That will produce a subdirectory Pila-2.0 in that directory (i.e. ~/PalmDev/Pila-2.0) with quite a few files and further subdirectories.
  3. Now you have to download a few files from the Internet into the pila-sdk subdirectory of your Pila-2.0 directory just created (~/PalmDev/Pila-2.0/pila-sdk):
  4. Once you have downloaded the above files change into the directory you downloaded those files into (i.e. ~/PalmDev/Pila-2.0/pila-sdk) and run the script decode-pila-sdk that is already present in that directory: ./decode-pila-sdk
  5. You'll get some messages about unpacking, transforming and cleaning up things. And hopefully no other messages than a few "No header file found to transform Sony/xxx (skipped)" if you didn't download the Sony SDK files.
  6. The result of running the decode-pila-sdk script is a subdirectory inc in Pila-2.0/pila-sdk containing the new and shiny Pila SDK. Move that directory down one level into Pila-2.0 and set your PILAINC environment variable to include at least the directories Pila-2.0/lib and Pila-2.0/inc for Pila to know where to find the include files and the library files distributed with this Pila package.
  7. You are all set and can start reading the rest of this manual to get a feel for how to write programs for/with Pila. Have fun!

B.1 Structures

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.

B.2 Unions

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>.

B.3 Bitfields

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.FieldAttrTag
For 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.
The symbol FieldAttrTag.justification.shift has the value 4 since it takes a right-shift of 4 to move the bit field to the lowest bit of the UInt16 it is part of.
The symbol FieldAttrTag.justification.mask has the value of 0x0003 since that would mask out the shifted bit field.
The symbol FieldAttrTag.justification.count has the value of 2 since the bit field spans 2 bits.

In your program you can use
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*.

B.4 Enumerations

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.

B.5 APIs

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.

Motorola 68000 Instruction Set. ------------------------------------------------------------------------------- Condition Codes --------------- Assembler Data Instruction Description Syntax Size X N Z V C ----------------------- --------- ---- --------- ABCD Add BCD with extend Dx,Dy B-- * U * U * -(Ax),-(Ay) ADD ADD binary Dn,<ea> BWL * * * * * <ea>,Dn ADDA ADD binary to An <ea>,An -WL - - - - - ADDI ADD Immediate #x,<ea> BWL * * * * * ADDQ ADD 3-bit immediate #<1-8>,<ea> BWL * * * * * ADDX ADD eXtended Dy,Dx BWL * * * * * -(Ay),-(Ax) AND Bit-wise AND <ea>,Dn BWL - * * 0 0 Dn,<ea> ANDI Bit-wise AND with Immediate #<data>,<ea> BWL - * * 0 0 ASL Arithmetic Shift Left #<1-8>,Dy BWL * * * * * Dx,Dy <ea> ASR Arithmetic Shift Right ... BWL * * * * * Bcc Conditional Branch Bcc.S <label> BW- - - - - - Bcc.W <label> BCHG Test a Bit and CHanGe Dn,<ea> B-L - - * - - #<data>,<ea> BCLR Test a Bit and CLeaR ... B-L - - * - - BSET Test a Bit and SET ... B-L - - * - - BSR Branch to SubRoutine BSR.S <label> BW- - - - - - BSR.W <label> BTST Bit TeST Dn,<ea> B-L - - * - - #<data>,<ea> CHK CHecK Dn Against Bounds <ea>,Dn -W- - * U U U CLR CLeaR <ea> BWL - 0 1 0 0 CMP CoMPare <ea>,Dn BWL - * * * * CMPA CoMPare Address <ea>,An -WL - * * * * CMPI CoMPare Immediate #<data>,<ea> BWL - * * * * CMPM CoMPare Memory (Ay)+,(Ax)+ BWL - * * * * DBcc Looping Instruction DBcc Dn,<label> -W- - - - - - DIVS DIVide Signed <ea>,Dn -W- - * * * 0 DIVU DIVide Unsigned <ea>,Dn -W- - * * * 0 EOR Exclusive OR Dn,<ea> BWL - * * 0 0 EORI Exclusive OR Immediate #<data>,<ea> BWL - * * 0 0 EXG Exchange any two registers Rx,Ry --L - - - - - EXT Sign EXTend Dn -WL - * * 0 0 ILLEGAL ILLEGAL-Instruction Exception ILLEGAL - - - - - JMP JuMP to Affective Address <ea> - - - - - JSR Jump to SubRoutine <ea> - - - - - LEA Load Effective Address <ea>,An --L - - - - - LINK Allocate Stack Frame An,#<displacement> - - - - - LSL Logical Shift Left Dx,Dy BWL * * * 0 * #<1-8>,Dy <ea> LSR Logical Shift Right ... BWL * * * 0 * MOVE Between Effective Addresses <ea>,<ea> BWL - * * 0 0 MOVE To CCR <ea>,CCR -W- I I I I I MOVE To SR <ea>,SR -W- I I I I I MOVE From SR SR,<ea> -W- - - - - - MOVE USP to/from Address Register USP,An --L - - - - - An,USP MOVEA MOVE Address <ea>,An -WL - - - - - MOVEM MOVE Multiple <register list>,<ea> -WL - - - - - <ea>,<register list> MOVEP MOVE Peripheral Dn,x(An) -WL - - - - - x(An),Dn MOVEQ MOVE 8-bit immediate #<-128.+127>,Dn --L - * * 0 0 MULS MULtiply Signed <ea>,Dn -W- - * * 0 0 MULU MULtiply Unsigned <ea>,Dn -W- - * * 0 0 NBCD Negate BCD <ea> B-- * U * U * NEG NEGate <ea> BWL * * * * * NEGX NEGate with eXtend <ea> BWL * * * * * NOP No OPeration NOP - - - - - NOT Form one's complement <ea> BWL - * * 0 0 OR Bit-wise OR <ea>,Dn BWL - * * 0 0 Dn,<ea> ORI Bit-wise OR with Immediate #<data>,<ea> BWL - * * 0 0 PEA Push Effective Address <ea> --L - - - - - RESET RESET all external devices RESET - - - - - ROL ROtate Left #<1-8>,Dy BWL - * * 0 * Dx,Dy <ea> ROR ROtate Right ... BWL - * * 0 * ROXL ROtate Left with eXtend ... BWL * * * 0 * ROXR ROtate Right with eXtend ... BWL * * * 0 * RTE ReTurn from Exception RTE I I I I I RTR ReTurn and Restore RTR I I I I I RTS ReTurn from Subroutine RTS - - - - - SBCD Subtract BCD with eXtend Dx,Dy B-- * U * U * -(Ax),-(Ay) Scc Set to -1 if True, 0 if False <ea> B-- - - - - - STOP Enable & wait for interrupts #<data> I I I I I SUB SUBtract binary Dn,<ea> BWL * * * * * <ea>,Dn SUBA SUBtract binary from An <ea>,An -WL - - - - - SUBI SUBtract Immediate #x,<ea> BWL * * * * * SUBQ SUBtract 3-bit immediate #<data>,<ea> BWL * * * * * SUBX SUBtract eXtended Dy,Dx BWL * * * * * -(Ay),-(Ax) SWAP SWAP words of Dn Dn -W- - * * 0 0 TAS Test & Set MSB & Set N/Z-bits <ea> B-- - * * 0 0 TRAP Execute TRAP Exception #<vector> - - - - - TRAPV TRAPV Exception if V-bit Set TRAPV - - - - - TST TeST for negative or zero <ea> BWL - * * 0 0 UNLK Deallocate Stack Frame An - - - - - ------------------------------------------------------------------------------- Symbol Meaning ------ ------- * Set according to result of operation - Not affected 0 Cleared 1 Set U Outcome (state after operation) undefined I Set by immediate data <ea> Effective Address Operand <data> Immediate data <label> Assembler label <vector> TRAP instruction Exception vector (0-15) <rg.lst> MOVEM instruction register specification list <displ.> LINK instruction negative displacement ... Same as previous instruction ------------------------------------------------------------------------------- Addressing Modes Syntax ---------------- ------ Data Register Direct Dn Address Register Direct An Address Register Indirect (An) Address Register Indirect with Post-Increment (An)+ Address Register Indirect with Pre-Decrement -(An) Address Register Indirect with Displacement w(An) Address Register Indirect with Index b(An,Rx) Absolute Short w Absolute Long l Program Counter with Displacement w(PC) Program Counter with Index b(PC,Rx) Immediate #x Status Register SR Condition Code Register CCR Legend ------ Dn Data Register (n is 0-7) An Address Register (n is 0-7) b 08-bit constant w 16-bit constant l 32-bit constant x 8-, 16-, 32-bit constant Rx Index Register Specification, one of: Dn.W Low 16 bits of Data Register Dn.L All 32 bits of Data Register An.W Low 16 bits of Address Register An.L All 32 bits of Address Register ------------------------------------------------------------------------------- Condition Codes for Bcc, DBcc and Scc Instructions. --------------------------------------------------- Condition Codes set after CMP D0,D1 Instruction. Relationship Unsigned Signed ------------ -------- ------ D1 < D0 CS - Carry Bit Set LT - Less Than D1 <= D0 LS - Lower or Same LE - Less than or Equal D1 = D0 EQ - Equal (Z-bit Set) EQ - Equal (Z-bit Set) D1 != D0 NE - Not Equal (Z-bit Clear) NE - Not Equal (Z-bit Clear) D1 > D0 HI - HIgher than GT - Greater Than D1 >= D0 CC - Carry Bit Clear GE - Greater than or Equal PL - PLus (N-bit Clear) MI - Minus (N-bit Set) VC - V-bit Clear (No Overflow) VS - V-bit Set (Overflow) RA - BRanch Always DBcc Only - F - Never Terminate (DBRA is an alternate to DBF) T - Always Terminate Scc Only - SF - Never Set ST - Always Set ------------------------------------------------------------------------------- Parts from "Programming the 68000" by Steve Williams. (c) 1985 Sybex Inc. Parts from BYTE Magazine article. Compiled by Diego Barros. e-mail : alien@zikzak.apana.org.au Revision 2.1 22 May, 1994