GCC programming tricks for the PalmPilot

All code snippets presented here are public domain, feel free to use them in your own Pilot programs!

Having more than 32k code

Interfacing to IEEE floating point system traps

Printing and scanning IEEE floating point

Having more than 32k code

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()
<32kyour code
\hooks
Now the trick is to put start() in the middle of your objects and provide an entry function myStart() at adress 0, which simply calls start().

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()
<32kyour code
\
/
start()
<32kmore of your code
\hooks
That's the theory, to make this work you have to do the following:
  1. Increase the coderes size in the file pilot.ld. Here's mine:
    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 );
    }
    
  2. Compile the following file (start.c):
    extern unsigned long start();
    unsigned long myStart()
    {
      return start();
    }
    
  3. Link with a command like this:
    $(CC) $(LDFLAGS) -nostartfiles start.o some objects $(CRT0) more objects $(LIBS) -oyourprog

    The option -nostartfiles tells the linker not to automatically use crt0.o as the first object.

Interfacing to IEEE floating point system traps

GCC doesn't handle NewFloatMgr.h correctly (in version 0.5.0). On the other hand, using arithmetic operators like + in a C program use the emulated operations from the C runtime library, not the Pilot system traps, which creates bloated PRC files.

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!

Printing and scanning IEEE floating point

Though you could use SysTrapFlpFToA or SysTrapFlpAToF, I don't recommend so, as the ROM routines Unfortunately, not even the otherwise excellent MathLib supports floating point IO, so I had to write my own for LispMe.

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!