Stanford Digital Library PalmPilot Infra Structure

This memo includes a description of the technical facilities we have built so far, as well as the conventions for collaborative code development. Corresponding code is under the InfoBus dldev source tree, as is this README (dldev/src/PilotsDL)

Table of Contents:

1. Error and Event Logging Infrastructure
   1.1 Overview
   1.2 Usage Details for Error Handling
   1.2.1 Throwing Exceptions
   1.2.2 Catching Exceptions
   1.2.3 Subclassing (for a more specific type of exception)
2. Memory Management Infrastructure 
   2.1 Overview
   2.2 Selected Methods of the Buffer Class
   2.3 Selected Methods of the LockableBuffer Class
   2.4 Selected Methods of the StringBuffer Class
   2.5 Selected Methods of the XMLStringBuffer Class
   2.6 Selected Methods of the Packet Class
   2.7 Help In Dealing With Handles
3. Communication Infrastructure
   3.1 Selected Methods for Connections
   3.2 The InfoBus Name Resolution Facility
   3.2.1 Selected Methods of the Name Resolution Facility
4. Project Software Management
   4.1 CVS Installation and Operation
   4.2 Current CVS Problems and Work-Arounds
   4.2.1 New Directories Not Coming in During 'Update' Operation
   4.2.2 Permissions Screwed Up in Repository
5. PalmPilot and Telecommunication
6. CodeWarrior Windows SDK Support and Pilot Emulator Installation
7. CodeWarrior Tricks and Gotchas

1. Error and Event Logging Infrastructure

1.1 Overview

The Error and Event Logging Infrastructure (EELI) provides a uniform way for our Pilot applications to (i) report errors or other messages to users on the screen and, (ii) communicate error conditions among caller and callee within an application. User messages are displayed on automatically generated popup windows with OK buttons. This message display facility has the following features:

Caller/callee error communication uses the C++ throw/catch mechanism. However, EELI enhances that mechanism by adding the following facilities:

EELI consists of the
EventManager and PilotException classes. EventManager is responsible for the display of messages, and for keeping event histories. EventManager's PilotException subclass is the base class for all exceptions. Developers may subclass PilotException.

At the heart of the EventManager are its family of static ShowMsg() methods. They take either one or two strings, and optionally one error code. The messages and error number will be reported to the user as shown in the example above. In addition, the errno of the last call to a ShowMsg() method are always stored and is available for recall. When there are two string message parameters, the first is taken to be the location of the error, and the second is taken to be the message. For example:

                     "Will convert to black/white",

would display an 'official' PalmPilot error popup with the example above. In addition, the error number would be available via the static EventManager::GetErrno().

Visual error display to the user can be turned on and off by using the static SetVisibleErrorReporting() method on EventManager.

The AddEventHistory(CharPtr/StrBufPtr) on EventManager instances is used to add messages to the event history. Note that the event history methods are not static. The DisplayEventHistory() method displays all of the accumulated messages in an automatically generated popup window, and the MostRecentEvent() message returns a StringBuffer with the most recent message that was added to the event history. Similarly, GetEventHistory() returns the whole history in a StringBuffer.

A testing facility for EventLogger is available at dldev/src/pilotsDL/TestProjects.

1.2 Usage Details for Error Handling

1.2.1 Throwing Exceptions

The constructor for PilotException takes the same parameters as the ShowMsg() method explained above: a code origin, the message, and an errno. Various constructors allow you to leave out some of these.

   if (<something goes wrong>) {
     // Optionally show a msg. Don't do that if you expect
     // someone up the call stack to catch the exception and
     // put up its own message:
     EventManager::ShowMsg("myFunc", "I couldn't do it.");
     throw PilotException("myFunc", 
                          "The dog ate the pointer", 

1.2.2 Catching Exceptions

Here is how you call something, and catch errors. Note: the compiler/runtime take care of deallocating all exception objects. Don't try to delete them.

   try {
     <call code that tries to make a socket connection>
   catch (ConnectionException& ce) {
     // Handle all connection-oriented exceptions here. 
     // ConnectionException is a subclass of PilotException.
     // ce is deallocated automatically.
   // All non-connection related exceptions are caught here:
   catch (PilotException& pe) {
     // optionally show the exception's event history:
     throw;    // throw same exception

Or, we could add to the event history of the exception like this:

     try {
     catch (ConnectionException& e) {
     // Compose some nice message to add to the 
     // existing event history stack:
     StringBuffer errMsg;
     errMsg.append("Connection to ");
     errMsg.append(" failed.");
     // re-throw error:

Alternatively, if we want to throw another, specialized exception:

   catch (PilotException& pe) {
     // Make a new exception of class BufferException. Make it's event
     // history be the same as what's now in pe. Add a new message to 
     // the history, and throw the new exception. All this is done
     // by passing the old exception to BufferException's constructor:
     throw BufferException("Layer below me messed it all up", pe);

Note: you can also leave out the '&', like this:

            catch (PilotException pe)

as opposed to:

            catch (PilotException& pe)

This works fine, but it takes a bit more storage during the throw process because an extra temporary instance of PilotException is created.

1.2.3 Subclassing (for a more specific type of exception)

You may want to subclass PilotException to provide a more specific indication of your modules set of exceptions. Here is how you do that:

   class ConnectionException : public PilotException {
     // Define as many constructors as you need (see
     // PilotException constructors for available choices):
     ConnectionException(CharPtr errloc, CharPtr msg) 
       : PilotException(errloc, msg) {};
     ConnectionException(CharPtr errloc, 
	   	       CharPtr msg, 
		         short errno) 
       : PilotException(errloc, msg, errno) {};
     ConnectionException(CharPtr errloc, 
		         CharPtr msg, 
		         EventManager &excInst) 
       : PilotException(errloc, msg, excInst) {};
     ~ConnectionException() {};

 For more information, refer to
EventManager.h and PilotException.h

Memory Management Infrastructure

2.1 Overview

The MMI provides a base for dealing with the tricky issue of memory usage on the Pilot: On the one hand, it is advantageous to use the memory Handle facility, rather than heap pointers, because the use of Handles gives the OS the opportunity to move/consolidate memory. On the other hand, using Handles is tricky, because locking/unlocking/freeing is critical to avoiding obscure bugs.

The MMI is an extensible facility that uses Handles, but provides a friendlier abstraction. It was created by consolidating contributions by MICO, and the project members. Here's the current class hierarchy:

             |             |                |
             v             v                v
         Packet     StringBuffer  PersistentLockableBuffer
                           |                |

Buffer provides a sequence of bytes with a read and a write pointer. So there can be a writer putting information into a buffer, with a reader trailing behind to access the info. Read and write pointers can be moved with seek operations, much like Unix file pointers. 'Put' and 'Get' methods add and read information into and out of a buffer. All memory management is taken care of. In addition, buffers have the ability to maintain multi-byte alignment, inserting padding where necessary. This is required to build packets for some low-level internet protocols. For example, there is a 'Put' method that inserts its paramater into the buffer in such a way that the following information begins on a 4-byte boundary.

LockableBuffer is derived from Buffer and adds the ability to obtain a pointer into a buffer for reading or writing. This is not a terribly desirable facility because it can lead to the very memory bounds violations the MMI is trying to avoid. The facility should be used very sparingly, and primarily by the methods of subclasses. There are two reasons for including the lock feature: (i) when using low-level functions, such as socket reading function in the network library, a pointer to memory must be passed as a parameter. The called function then writes directly to that memory. Without the lock feature, MMI clients would need to have the network utility write to a separate space, and then copy the information to a buffer using 'Put'. Similarly, (ii), some utilities such as StrStr() for substring searching expect pointers into memory. So subclasses like StringBuffer need the lock facility.

The Packet class is a convenient way of receiving and preparing packets for network transport. It is mainly there for backward compatibility with an earlier Packet class.

StringBuffer, finally, allows MMI clients to have a very efficient string management facility, without having to worry about storage management. String buffers have methods like StrCat, StrCopy, StrCompare, StrNCaselessCompare, etc.

Two classes PersistentLockableBuffer and PersistentStringBuffer are added which are equivalent to LockableBuffer and StringBuffer. They use the Database manager for storing the buffer content instead of the memory handle. These classes are added to make use of relatively unlimited non-volatile memory of the Palm Pilot.

PersistentLockableBuffer implements the functionality of both Buffer and LockableBuffer classes. PersistentStringBuffer implements the functionality of StringBuffer. They create a database with the name "PilotsDL" if it is not already existing. Whenever a new Persistent buffer is created, it is created as a new record in the database. The records are retained across different sessions. To clear the database from the Palm device, one can go to the applications screen, and delete the database using the menu option.

PersistentDB class is provided to create a database on the Palm given a database name and append flag. When the append flag is false, the old contents of the database are deleted.

When ByteLockAppend and CharLockAppend are used, the database buffer is expanded by the required number of bytes and a memory buffer of given size is allocated, locked and the pointer is given to the user. When the buffer is unlocked, the contents of the memory buffer are transferred to the database buffer and the memory buffer is freed. AppendItem class is used to maintain the correspondence between memory and database buffers. BufferTests are modified to test memory or database buffers and the option can be selected at runtime.

The GIOP codec layer can use either memory buffers or database buffers. Currently the codec layer uses persistent or database buffers. One can use the memory buffers version, by not defining USE_PERSISTENT_BUFFER flag in utils/prefix.h.

When USE_PERSISTENT_BUFFER flag is defined, the ReceivePacket method in Connection class makes use of NetLibDmReceive() to receive the data into the database buffer directly.

2.2 Selected Methods of the Buffer Class

The Buffer class is not always as comfortable to use as its derived classes. It is expected that most applications will use the derived classes, though that's not a must. Here are selected methods that are most useful. The authoritative source is, of course buffer.h and

All the constructors take an optional EventLogger instance. In addition, they all take three optional arguments which control memory allocation. These are primarily intended for use by derived classes. Minimum size is used as the minimum whenever memory is allocated for the buffer. ResizeIncrement is the size we add at least if we have to enlarge storage space for the buffer. You can check the resize() method to see the details. You shouldn't have to concern yourself with these, unless you are building a derived class that expects particularly large or small buffers to be frequent.

Another constructor uses a handle, but should be used with care. Given a handle, that handle will become the storage for the buffer. I.e. you are putting Buffer functionality around a handle. But note two things: (1) When you destroy a buffer, the handle is deallocated. And, (2) you don't want somebody else to deallocate the handle. This can happen, for example, if you use a UI field's handle for making a Buffer. If you ever use FldSetTextHandle() to put a different handle into that field, the old handle (that is, the handle at the core of your buffer) will be deallocated!!!

The following methods are available for manipulating the buffer contents:

For more information, refer buffer.h

2.3 Selected Methods of the LockableBuffer Class

Methods CharLockAppend(size) and ByteLockAppend(size) are for appending to LockableBuffers. They return ptrs to the current end of the buffer (i.e the current write pointer). The buffer will enlarge itself if necessary, and will advance the write pointer to just beyond the area you are locking before it returns to you.

CharLockOverwrite(size) and ByteLockOverwrite(size) are the same, but you'll be overwriting from the start of the buffer. The read ptr will be 0.  The write pointer will pt to just beyond size.

CharLockRead() and ByteLockRead() instead return ptrs to the current read position.  Do not use them for writing.

All these methods require calling Unlock when you are done.

 For more information, refer to

2.4 Selected Methods of the StringBuffer Class

A test suite for StringBuffer is available under dldev/src/PilotsDL/testing/BufferTests. It runs through a sizeable number of tests on string buffers.

StringBuffer class has constructors for accepting a string, a handle and an optional EventLogger parameter.

The following methods are available for manipulating a StringBuffer contents:

These methods are overloaded to accept a CharPtr, Handle and StringBuffer pointer.

 For more information, refer

2.5 Selected Methods of the XMLStringBuffer Class

XML string buffers are a subclass of StringBuffer. They allow simple parsing of XML text. You can pull out particular XML elements, you can count how many elements with a particular tag are present, or you can pull out like elements one at a time. The latter acts like a Java enumeration. You get to set the enumeration tag, and then you just call nextElement() over and over.

There are two groups of constructors. One takes a CharPtr, and another takes a StringBuffer as the XML string. Both groups have two variations: the first just makes an XML string buffer with the given string. The second also sets an enumeration tag.

In addition to the methods of StringBuffer, LockableBuffer, and Buffer, the following methods are available for manipulating XMLStringBuffer contents:

 For more information, refer to XMLStringBuffer.h

2.6 Selected Methods of the Packet Class

Again, the Packet class is supported for backward compatibility. Most of these methods are simply front-ends to methods of the superclasses. Remember that through its inheritance, you can use Put, Get, cursor seeking, etc. Currently Packet class is not used.

The following methods are available for manipulating a Packet contents:

 For more information, refer Packet.h

2.7 Help In Dealing With Handles

If you cannot use Buffers because you have to interact with the OS, there is a set of utilities in a module of the same name. Below are the major methods. These are all static, so you don't ever make an instance of class utility. You just use the facility with calls like:

    utilities::MemHandleCopy(srcHandle, dstHandle);

Users of this module should assign a pt to an EventLogger object to the static variable evLog.  This gives the module an effective way to do error reporting.  If you don't, the module will use its own EventLogger which will properly record all errors in its errno, etc. But there will, of course, be no visual error displays. In that case, use utitlites::evLog->GetErrno() after calling routines that leave errors codes there. This module is used a lot by the Buffer class and its derivatives. Chances are that they have made the assignment.

The following methods are available in this module:

 For more information, refer to utilities.h

Communication Infrastructure

Basic communications are covered by the Connection class which lets you open and close socket connections. Above this layer is the CODEC facility, which lets you build packets easily: It understands how to marshall and unmarshall basic types, like integers, floats, etc. If you build packets with the CODEC facility and send them to IIOP-compliant CORBA servers, they'll think they have a full-fledged CORBA client to deal with.

Also above the Connection facility is a CORBA name resolution facility that takes the name of an InfoBus service and finds its IOR (a.k.a object identifier) by connecting to the InfoBus naming service.

This section only describes the Connection class and the naming service, not the CODEC.

Internet access from the Pilot is built up and broken down in two phases: During phase 1 a PPP connection or equivalent is built up. In phase 2, a socket is connected. You can close sockets, but keep the PPP connection open. This is very useful if your application is planning to open a new socket connection soon, because building up PPP connections, for instance through the wireless modem, is slow.

Upon the first connection opening command, Connection instances load a dynamic system library that handles network access. When you destroy the last Connection instance, that library is unloaded, freeing significant memory. Note again, though, that if you are going to do multiple interactions across the wire, you just want to open and close sockets, not shut connections down explicitly, or implicitly by deleting them.

In testing connections, the PortListener facility is very handy. It listens on a port and either just shows on the screen what it sees, or in addition echoes received information back to the sender. This facility is under the dldev/src/PilotsDL/testing directory. A Pilot-side connection testing project is there as well. It lets you open/close connections with the PortListener or any other socket-aware application. It can also send out and receive packets.

3.1 Selected Methods for Connections

Most of the methods on the Connection class return an error code. Often, evLog->GetErrno() will give more detail. In all cases, the methods will call evLog->HandleError() if anything goes wrong.

  There is a global timeout default for network operations. Sending and receiving use that default timeout. One of the two methods for opening a connection lets you provide a timeout just for opening the socket. After that, the timeout reverts to the default. Note that OpenConnection() accomplishes both phases of gaining Internet access: It cause the Pilot to build up a connection, via PPP, for example, and it opens a socket. Which method is used for phase one depends on how you set the Network settings in the Prefs application before you started your application. In any case, you'll most likely see some Pilot UI activity during phase 1 which tells you what's happening (dialing, running a login script, etc.).

The following methods are available for manipulating a Connection:

  Note that, sometimes ReceivePacket receives partial receive buffer when the network connection is slow and also when the buffer to be received is too large. Make sure to call it repeatedly if necessary. ReceivePacket appends to the existing packet so one can call it repeatedly to receive a big packet.

  Earlier Connection class used Packet instance, now it uses LockableBuffer. This change is made to make it work with Persistent buffers also.

  There are three ways of closing a connection: the following two methods, and deleting a Connection instance. CloseConnection() will only close a socket; it will not break down the Internet connection. So, once you've done an initial OpenConnection(), subsequent CloseConnection()/OpenConnection() cycles are fast. The ShutDown() method closes both the socket, and the Internet connection, unless you explicitly pass in C_DELAYED_SHUTDOWN. The default for this method is C_IMMEDIATE_SHUTDOWN. So, if you call ShutDown() with no parameter, the socket and the Internet connection are taken down. A subsequent OpenConnection() will go through both build-up phases.

  The destructor will invoke ShutDown() with a settable default close mode. By default, this is C_IMMEDIATE_SHUTDOWN. But a call to SetCloseMode() prior to destruction lets you destroy a Connection instance without breaking down the Internet connection. Usually, you'll not mess with that, because you want a way to shut the Internet connection down.

 For more information, refer

3.2 The InfoBus Name Resolution Facility

The naming resolution is simple: you make yourself a Naming instance, and call a single method on it. You pass in the 'naming context' and the name of the service, and you get back an IOR, which is IIOP's name for an OID. CORBA names are really name spaces, so that you can have the same service name for multiple objects, as long as these names are in different name spaces. Think of it as a Unix directory kind of hierarchy. We currently have two main name spaces in use for the InfoBus: dl/Services/Search, and dl/Services/Processing. Within the search service context we have Infoseek, Dialog, etc. In the processing name space, we have services like InterBib and the document summarizer.

3.2.1 Selected Methods of the Name Resolution Facility

  The two constructors differ in which Connection object is used to contact the name server. The first constructor will cause the Naming instance to create a Connection object, use it, and delete it again. The second constructor lets you pass in a Connection object which should not have an open connection. This object is used, but not destroyed. Most of the time you'll want to use that second constructor. It ensures that you don't go through building up an Internet connection (phase 2 above) more than once in the typical sequence of getting an IOR, connecting to the service, and communicating with it. The GetObject() method resolves a naming context. It is overloaded to accept either CharPtr or Handle.

  Publishing and withdrawing is for services that are running on the Pilot, i.e. for the case where the Pilot is a server, and others connect to it. This is not yet implemented.

 For more information, refer to

Project Software Management

All the official software is under CVS source code control with the other InfoBus software. The directory structure at the time of this writing is like this:

             common/incs  ;; include files for the major facilities
             common/src   ;; source files for the major facilities
             common/lib   ;; various libraries
             TestProjects ;; CodeWarrior projects for testing modules facilities
             <Project1>   ;; Fun DL-related project
             <Project2>   ;; Another exciting DL-related project

Note that not all the files and directories created when you make a CodeWarrior project should go into the repository. We only check in the minimal amount that lets others re-create the project. The necessary files are:

ProjDir/foo.mcp        ;; cvs add -kb foo.mcp  (the Project file)
ProjDir/foo.prc        ;; cvs add -kb foo.prc  (the Pilot binary. This is so
                                                that not everyone needs a copy
                                                of CodeWarrior to use the code)
ProjDir/src/foo.rsrc   ;; cvs add -kb foo.rsrc (the UI resource file created
                                                by UI constructor)
ProjDir/src/ ;; simple cvs add       (any .cc or .cpp files)
ProjDir/src/foo_res.h  ;; simple cvs add       (created by UI constructor)
ProjDir/src/fooRsc.h   ;; simple cvs add. This is not always there.
ProjDir/src/RESOURCE.FRK/foo.rsrc  ;; cvs add -kb foo.rsrc (created by the
                                               (UI constructor. You need this
                                                one and the one in ProjDir/src.)

Note that four of these files are binary. So they must be added to cvs with the -kb flag!

CVS has worked well while we were on Unix. On the PC it has been a bit trying. But we now have all wrinkles out, but two. The basic idea of CVS is that there is a central code repository into which code is checked in and out. Multiple programmers can maintain (multiple) copies of the source tree, or of source tree portions. After you make changes on your copy, you check it in ('commit' it, in CVS terms). If you want to update your source tree snapshot to reflect the changes people have checked into the repository, you perform an 'update' operation.

The detail section below describe the installation and operation of CVS on PCs as they should work. The subsequent section (Current CVS Problems and Work-Arounds) explains what we haven't worked out yet. The UNIX machines have man pages for CVS. Andreas also has a printed version.

4.1 CVS Installation and Operation

CVS is distributed and maintained for GNU FSF by They make their money by selling support for it. The download URL with choices for OS is:

The real URL for Windows (version 1.9.28) is:
It only has a single cvs.exe file which is the final thing (i.e not a self-extracting binary). After placing that file somewhere convenient, Put something like the following into your autoexec.bat and reboot:

rem *** For CVS:
set PATH=C:\users\paepcke\software\cvs;%PATH%
set CVSROOT=:local:t:\CVSROOT
set CVSUMASK=002
set CVSEDITOR=C:\Emacs\bin\runemacs.exe
set HOMEPATH=C:\users\paepcke


Put file .cvsrc into $HOMEPATH. The file should contain a single line: update -d -P
It ensures that when you do a 'cvs update', any new directories are also created in your local tree.

Now all you have to remember is that before using any cvs commands, you need to mount \\bigeye\testbed on drive T: of your PC (or whatever drive you chose). If you don't, all cvs commands will tell you that they cannot find T:\CVSROOT\..., and will abort. No harm is done. Just mount the directory and repeat the cvs operation.

To get started, go to wherever you want the dldev tree to be on your PC, and checkout some portion of the source tree. For example:

   cvs checkout dldev/src/PilotsDL
   cvs checkout dldev/src/wallets

Once you've done a checkout, all you need to do later is updates. Like:

   cvs update dldev/src/wallets

to get any changes committed to the repository. You can also add new files or directories. Assume you are in dldev/src/wallets and have added a new subdirectory on your PC, called 'instruments', and you added some files in that new subdirectory:

   cvs add instruments            // add the new dir to the repository
   cd instruments                 
   cvs add *                      // add all new files to the dir NOTE BELOW!!!
   cvs commit -m 'Added new instruments dir and files'

NOTE: if you add a binary file, such as a CodeWarrior project file (.mcp), CodeWarrior constructor file (.rsrc), or Java byte code (.class), add the file as like this:

     cvs add -kb WalletTests.mcp

This will tell cvs not to stick its header files anywhere in the file, or do line ending conversion, etc.

The commit is necessary, because adding files and directory is only 'scheduled', not executed, till a commit. The 'cvs add' will tell you so, no need to remember.

4.2 Current CVS Problems and Work-Arounds

We have experience a few problems which should be solvable by an enterprising soul. Here they are:

4.2.1 New Directories Not Coming in During 'Update' Operation

Even though the .cvsrc file described above is supposed to tell cvs that when you do an update operation, you want any new directories that were added to the repository since your last update to be brought over into your source snapshot, they don't. How's that for a sentence? The symptom is that you'll do a 'cvs update', expecting that great new subtree to come over, but it doesn't. Solution: use 'cvs update -d'.

4.2.2 Permissions Screwed Up in Repository

This problem only occurs when you add a *new* file or directory to the repository, using 'cvs add'. It does not happen when you commit changes to an already existing file. What you need to do after adding files is to log into Shrimp as user 'testbed'. Cd to '~testbed/CVSROOT/dli2/src/...' until you find the file(s)/directory you added. Then make sure that each file's owner is 'testbed', and that each file has at least read permission for everyone.

PalmPilot and Telecommunication

- Modem setup for dialout through Emulator:
   * Prefs->Modem:
      - Standard
      - 14,400bps
      - Speaker: Low     // anything, of course
      - FlowCtl: off     // !
      - String: AT&FX4   // Default

   * Prefs->Network
      - Service: Unix
      - Username: paepcke
      - Password: <enter your PPP dialin pwd for 1-650-325-1010>
      - Phone: 1-650-325-1010  // Check dialout prefix: '9,' if at Stanford
        - Connection type: PPP
        - Idle timeout: Power OFF
        - Primary DNS:
        - Secondary DNS:
        - IP Address automatic
          - WaitFor:name:
          - SendUserID:
          - SendCR:
          - WaitFor:word:
          - SendPassword:
          - SendCR:
          - WaitFor:>
          - Send: ppp
          - SendCR:
          - GetIP:

- Modem settings for dialout on Pilot device:
  For an actual Pilot device: Prefs->Modem: Use Pilot Modem US.
  Speed 19,200, and Flow Control: Automatic. Else same.

- Ricochet (wireless) modem setup for Palm pilot device:
   * Prefs->Modem:
      - Custom
      - 57,600bps
      - Speaker: Low     // anything, of course
      - FlowCtl: Automatic
      - String: ATX4&C1&K1

   * Prefs->Network
      - Service: Ricochet (new service: make it via menu->New)
      - Username: (not required)
      - Password: Prompt
      - Phone: 777
        - Connection type: PPP
        - Idle timeout: Power OFF
        - Primary DNS:
        - Secondary DNS:
        - IP Address automatic
          - (empty script, only End)

- Modem settings for dialout on emulator via Rocochet:
  Same as on device, but with Flow control OFF.

- Minstrel (wireless) modem setup for Palm pilot device:
   * Prefs->Modem:
      - Standard
      - 57,600bps
      - Speaker: Low
      - FlowCtl: Automatic
      - String:(blank, no string required)

   * Prefs->Network
      - Service: Minstrel (new service: make it via menu->New)
      - Username: (leave blank)
      - Password: Prompt
      - Phone: 00(zero zero)
        - Connection type: PPP
        - Idle timeout: Power OFF
        - Primary DNS:
        - Secondary DNS:
        - IP Address automatic
          - Delay:1
          - Send:at\\appp
          - SendCR:
          - WaitFor:1

6. CodeWarrior Windows SDK Support and Pilot Emulator Installation

CodeWarrior support for the Pilot under Windows has been updated. Part of this update includes new networking facilities. Another part includes the Pilot Emulator. Here are instructions on how to put the latest Windows SDK on top of CodeWarrior Rel. 4. The instructions for the Emulator are also in this section.

My installation of these components is in



   c:\users\paepcke\software\PilotSoftware\PalmOSSDK30 (for the SDK zip only)

- Install Code Warrior via CD-ROM
- Download 3COM's Emulator (formerly Co-Pilot) from the Web:
- Download CodeWarrior emulator plugin to make code warrior start the emulator:  (i.e. same as emulator)
- Download corresponding debug ROMs 3.0 and 2.0. Official entry point is:
  Save time of filling in form and accepting license by going directly to:
- Download 3.0 PalmOS SDK from: 
  This adds stdio and many other UNIX-like libraries and things.

- Unzip emulator, plugin, and ROM. I only unzipped and used the 3.0 ROM. Copy file 'Palm 3.0 Debug.ROM' to the emulator directory. Now you
    can start the emulator by itself.

- NOTE: The emulator has registry entries which tell it where to look for the ROM file! If you don't put the ROM file there once the Emulator ran
    and initialized the registry, you'll get a TRAP$0 error on startup of the Emulator. You can change the registry entry to get it to work again.

- The PalmOS Emulator supports an API for external debuggers. At this time, only Metrowerks' MWDebug debugger (from CodeWarrior) supports
  this API in the form of an updated PilotPlugin.dll component. Download this component and use it to replace the same-named component in your
  CodeWarrior installation. This is at:
    C:\Program Files\Metrowerks\CodeWarrior\bin\Plugins\Debugger
  NOTE: the old file will sometimes still be recognized by the debugger, even if you rename it!!!! The symptom then is that the debugger will keep
  asking you to put the real Pilot in its cradle. Renaming worked for me on W95, and for someone else. But on Topsmelt it failed. We had to move
  the old one out.

  You can then start the debugger as normal; it should behave just as if it was connected to a real Palm Computing Platform device, even though it's
  actually working in conjunction with the emulator. By using the debugger's preferences panels, you can configure the debugger to target debugger to
  target either the PalmOS Emulator or an actual hardware device when debugging.

- Unzip 3.0 PalmOS SDK into
    C:\Program Files\Metrowerks\CodeWarrior\PalmPilotWinSDK
  This will replace some of the files (tutorials, etc.)

7. CodeWarrior Tricks and Gotchas

There are a few things you should know about CodeWarrior and the Pilot. Please, everybody, add to this list as you learn nasty things.


     Char foo[] = "Blue skies are great for skiing"

  will give you a char array of the right length, but the string will be cut off after 12 chars.

  For R4, use the following project preferences:

  In the Code generation 68K processor panel:
       Peephole Optimizer on,
       CSE optimizer on,
       PC-Relative is on, and
       8-byte doubles on.
       Everything else off.

  In the Global optimizations pane:
       everything off.