Res type | Res id | description |
---|---|---|
code | 0001 | 68000 application code |
Talt | 1000 | an alert displayed when LispMe is not installed |
tAIB | 1000 | big icon for launcher |
tAIB | 1001 | small icon for launcher (Palm III and later) |
tAIN | 1000 | application name for launcher. This name also identifies the LispMe program database. |
Other resources (e.g. rloc) are not necessary for such a tiny application.
The launcher code is compiled and linked just like any other C application with the GNU C compiler but no .prc file is built from it, instead it is put into the LispMe .prc but using the id number 9001. So this code fragment is part of LispMe, but will never be called directly by PalmOS. But when LispMe creates a launcher icon, it copies this resource to the newly created resource database, this time using the special id 1, making it the code executed when tapping the icon.
As no C runtime library (crt0.o) is needed and to reduce the code size further, the object is linked with the option -nostartfiles and handles the necessary system calls itself.
One problem is that the LispMe program database and the launcher app should have the same name, but they can't, as every database name must be unique. You can't use a hashing scheme to create an internal name for the launcher app, the mapping must be reversible. So I simply inverted the highest bit of the session name (= LispMe program database name) to create the launcher app's name.
The following code snippet shows how to access the system traps with GCC.
#define _DONT_USE_FP_TRAPS_ 1 #include <Common.h> #include <System/SysAll.h> #include <SysTraps.h> #include <System/NewFloatMgr.h> void SysTrapFlpLToF(FlpDouble*, Long) SYS_TRAP(sysTrapFlpEmDispatch); /* convert a long to double */ double longToDouble(long l) { FlpCompDouble fcd; asm("moveq.l %0,%%d2" : : "i" (sysFloatEm_d_itod) : "d2"); SysTrapFlpLToF(&fcd.fd, l); return fcd.d; } Long SysTrapFlpFToL(FlpDouble) SYS_TRAP(sysTrapFlpEmDispatch); /* convert a double to long */ long doubleToLong(double d) { FlpCompDouble fcd; fcd.d = d; asm("moveq.l %0,%%d2" : : "i" (sysFloatEm_d_dtoi) : "d2"); return SysTrapFlpFToL(fcd.fd); } void SysTrapBinOp(FlpDouble*, FlpDouble, FlpDouble) SYS_TRAP(sysTrapFlpEmDispatch); /* the same interface is used for all basic arithmetic operations */ double genericDoubleOp(double a, double b, long opcode) { FlpCompDouble fcda, fcdb, fcds; fcda.d = a; fcdb.d = b; asm("move.l %0,%%d2" : : "g" (opcode) : "d2"); SysTrapBinOp(&fcds.fd, fcda.fd, fcdb.fd); return fcds.d; } /* basic arithmetic operations */ #define addDouble(a,b) genericDoubleOp(a,b,sysFloatEm_d_add) #define subDouble(a,b) genericDoubleOp(a,b,sysFloatEm_d_sub) #define mulDouble(a,b) genericDoubleOp(a,b,sysFloatEm_d_mul) #define divDouble(a,b) genericDoubleOp(a,b,sysFloatEm_d_div) SDWord SysTrapCompare(FlpDouble, FlpDouble) SYS_TRAP(sysTrapFlpEmDispatch); /* compare 2 doubles for equality */ Boolean eqDouble(double a, double b) { FlpCompDouble fcda, fcdb; fcda.d = a; fcdb.d = b; asm("moveq.l %0,%%d2" : : "i" (sysFloatEm_d_feq) : "d2"); return SysTrapCompare(fcda.fd, fcdb.fd); } /* compare 2 doubles for less or equal */ Boolean leqDouble(double a, double b) { FlpCompDouble fcda, fcdb; fcda.d = a; fcdb.d = b; asm("moveq.l %0,%%d2" : : "i" (sysFloatEm_d_fle) : "d2"); return SysTrapCompare(fcda.fd, fcdb.fd); }You should get the idea how to extend this for other operations. By the way, you can use floating point constants in your code, they are correctly converted into their IEEE bit pattern by GCC!
Here's my printing function for doubles:
/**********************************************************************/ /* Formatting parameters */ /**********************************************************************/ #define NUM_DIGITS 15 #define MIN_FLOAT 4 #define ROUND_FACTOR 1.0000000000000005 /* NUM_DIGITS zeros */ /**********************************************************************/ /* FP conversion constants */ /**********************************************************************/ static double pow1[] = { 1e256, 1e128, 1e064, 1e032, 1e016, 1e008, 1e004, 1e002, 1e001 }; static double pow2[] = { 1e-256, 1e-128, 1e-064, 1e-032, 1e-016, 1e-008, 1e-004, 1e-002, 1e-001 }; void printDouble(double x, Char* s) { FlpCompDouble fcd; short e,e1,i; double *pd, *pd1; char sign = '\0'; short dec = 0; /*------------------------------------------------------------------*/ /* Round to desired precision */ /* (this doesn't always provide a correct last digit!) */ /*------------------------------------------------------------------*/ x = mulDouble(x, ROUND_FACTOR); /*------------------------------------------------------------------*/ /* check for NAN, +INF, -INF, 0 */ /*------------------------------------------------------------------*/ fcd.d = x; if ((fcd.ul[0] & 0x7ff00000) == 0x7ff00000) if (fcd.fdb.manH == 0 && fcd.fdb.manL == 0) if (fcd.fdb.sign) StrCopy(s, "[-inf]"); else StrCopy(s, "[inf]"); else StrCopy(s, "[nan]"); else if (FlpIsZero(fcd)) StrCopy(s, "0"); else { /*----------------------------------------------------------------*/ /* Make positive and store sign */ /*----------------------------------------------------------------*/ if (FlpGetSign(fcd)) { *s++ = '-'; FlpSetPositive(fcd); } if ((unsigned)fcd.fdb.exp < 0x3ff) /* meaning x < 1.0 */ { /*--------------------------------------------------------------*/ /* Build negative exponent */ /*--------------------------------------------------------------*/ for (e=1,e1=256,pd=pow1,pd1=pow2; e1; e1>>=1, ++pd, ++pd1) if (!leqDouble(*pd1, fcd.d)) { e += e1; fcd.d = mulDouble(fcd.d, *pd); } fcd.d = mulDouble(fcd.d, 10.0); /*--------------------------------------------------------------*/ /* Only print big exponents */ /*--------------------------------------------------------------*/ if (e <= MIN_FLOAT) { *s++ = '0'; *s++ = '.'; dec = -1; while (--e) *s++ = '0'; } else sign = '-'; } else { /*--------------------------------------------------------------*/ /* Build positive exponent */ /*--------------------------------------------------------------*/ for (e=0,e1=256,pd=pow1,pd1=pow2; e1; e1>>=1, ++pd, ++pd1) if (leqDouble(*pd, fcd.d)) { e += e1; fcd.d = mulDouble(fcd.d, *pd1); } if (e < NUM_DIGITS) dec = e; else sign = '+'; } /*----------------------------------------------------------------*/ /* Extract decimal digits of mantissa */ /*----------------------------------------------------------------*/ for (i=0;i<NUM_DIGITS;++i,--dec) { Long d = doubleToLong(fcd.d); *s++ = d + '0'; if (!dec) *s++ = '.'; fcd.d = subDouble(fcd.d, longToDouble(d)); fcd.d = mulDouble(fcd.d, 10.0); } /*----------------------------------------------------------------*/ /* Remove trailing zeros and decimal point */ /*----------------------------------------------------------------*/ while (s[-1] == '0') *--s = '\0'; if (s[-1] == '.') *--s = '\0'; /*----------------------------------------------------------------*/ /* Append exponent */ /*----------------------------------------------------------------*/ if (sign) { *s++ = 'e'; *s++ = sign; StrIToA(s, e); } else *s = '\0'; } }The scanning function is too deeply interwoven with the rest of LispMe's scanner, I'll extract it when I've got more time!
It has often been said, that this is not possible, but this is wrong. In fact, there're two ways to overcome the 32k limit. The first one is using shared libraries, which has some problems (e.g. accessing global data) and the second one is described here.
Your PilotMain() is not the first function called when your app starts. The (normal) entry point is a function start(), which is buried in the object module crt0.o. (This module is automatically put at the beginning of the code section by the linker) start() relocates the data segment and calls some "hooks" (I don't know, what these hooks exactly do, but it seems that you need them).
The PalmOS starts an app by simply jumping to adress 0, where the start() function is put by the linker.
Now here comes the problem: start() is at the very beginning of the code segment, then your code follows and the hooks are at the very end, so to call the hooks from start(), they must be within 32k (the range of the relative branch instructions of the 68000), so normally your code mustbe <32k.
/ | start() |
<32k | your code |
\ | hooks |
When start() is within 32k of myStart() and the hooks are within 32k of start(), you have TWO 32k ranges to put your code. Of course you must ensure (by shuffling your functions and objects) that calls between your functions don't exceed the 32k branch limit.
/ | myStart() |
<32k | your code |
\ / | start() |
<32k | more of your code |
\ | hooks |
MEMORY { coderes : ORIGIN = 0x10000, LENGTH = 65535 datares : ORIGIN = 0x0, LENGTH = 32767 } SECTIONS { .text : { *(.text) . = ALIGN(4); bhook_start = .; *(bhook) bhook_end = .; . = ALIGN(4); ehook_start = .; *(ehook) ehook_end = .; } > coderes .data : { data_start = .; *(.data) } > datares .bss : { bss_start = .; *(.bss) *(COMMON) } > datares end = ALIGN( 4 ); edata = ALIGN( 4 ); }
extern unsigned long start(); unsigned long myStart() { return start(); }
The option -nostartfiles tells the linker not to automatically use crt0.o as the first object.