This article is reprinted from the September/October (5.5) issue of Handheld Systems, a bimonthly technical magazine devoted to handheld computers.
Copyright © 1997 by Creative Digital Publishing Inc. All rights reserved. Reprinted with permission.

For more information about Handheld Systems, to request a complementary magazine copy, or to find out more about Creative Digital's other products relating to handheld computers, such as The Handheld Computer Almanac, visit their web site at http://www.cdpubs.com.

EPOC32 C++ Development – In the Deep End

By Cade Roux

cade@acm.org

 

This series of articles explores developing software in C++ for Psion’s powerful new EPOC32 operating system. EPOC32 is the successor to the purpose-built EPOC16 mobile OS first used in 1989 in the Mobile Computer. This OS was successfully continued through the Series 3 range, HC, Workabout, and Siena. EPOC was designed to take advantage of Psion’s SIBO (SIxteen-Bit Organiser) hardware architecture. EPOC achieved pre-emptive multi-tasking using an 8086-class processor. EPOC32 utilizes a 32-bit micro-kernel architecture, which features pre-emptive multi-tasking, and an object-oriented design that is scalable. The only hardware platform currently available running EPOC32 is the Series 5, based on the ARM 7100 RISC processor, with the StrongARM a clear possibility for a future machine. EPOC32 is available for license, and a UNICODE build is available.

 

As usual, Psion offers several development options. Currently available for EPOC32 is OPL32, a close relative to the original OPL, and the completely new C++ SDK. OVAL32, the Object-based Visual Application Language similar to Visual Basic, which Psion debuted with the 3c and Workabout, is also expected to be available. Psion has licensed the complete Java package from Sun Microsystems, and a Java development kit is expected in 1998. In this series, we will deal mainly with the new C++ environment, touching on other areas as necessary. Psion have consistently embraced object-oriented design and programming, and this is evident in their comprehensive use of C++ features. This makes a solid C++ understanding a prerequisite for using the C++ SDK well. Because C++ is such a feature-rich language, it always helps to have a good C++ book handy, so I have included a bibliography of books — I highly recommend them.

 

This article was largely written with the pre-release beta version of the SDK. The first release of the C++ and OPL32 SDKs has just become available, so there may be some corrections later in this series. We will not do much coding this time around, because there is plenty of new stuff to learn.

A Whole New Kettle of Fish

The C development system for EPOC16 is built on the DOS-based TopSpeed C with extensive proprietary libraries to take advantage of the features of the OS. Although TopSpeed C++ is usable with later versions of the SDK, Psion’s EPOC16 object model is not directly compatible with traditional C++. In particular, exceptions are not implemented in TopSpeed C++. Psion’s standard library PLIB already supports a mature system for handling exceptions in C using a variation of the setjmp/longjmp library functions – p_enter() and p_leave(). Since the object models are not comparable, there is little incentive to use C++ wrapper classes around Psion’s built-in object-oriented classes. Psion’s OO extension to C uses a model very close to that used in most early C++ compilers. A class file is processed to produce a header file that lays out the member functions (or method) in a table, and each method is assigned a number. To call a member function, the object in question is sent this number with a suitable assortment of arguments using the p_send() function. The arguments can not be checked by type and are restricted to 16-bit values (usually necessitating pointers). This system loses all type-safety, and eliminating bugs is a matter of discipline and a keen eye.

 

The new C++ development system for EPOC32 is based on Microsoft’s Developer Studio, in particular the Visual C++ component. It requires Windows 95 or Windows NT, and features an integrated platform for debugging and testing applications. The new system really uses C++ capabilities well, defining class hierarchies in C++ that give full type-safety. Although this means learning a great deal of terminology and Psion’s peculiar naming conventions, it is no different from any other application framework.

Captain, the engines just can’t take it any more!

EPOC16, while a successful OS in its own right, is limited by several factors. To implement pre-emptive multi-tasking and memory protection on the segmented Intel 8086 architecture, processes are limited to 64K code and 64K data. In addition, the OS cannot have programs executing beyond the first 512K of memory. This presents a challenge for developers in the 90s, when most skills in developing lean software have been lost in the general developer population. When you add to this the pressure for open and feature-rich software, the technical limits usually require severe tradeoffs. This triage is part of the EPOC16 development cycle.

 

Aided by the efficient OPL interpreter, which loads and unloads code modules, and the efficient TopSpeed C compiler, developers manage to continually push the envelope. The original Series 3a and 3c range still manages to compete well with other 32-bit PDAs, including WindowsCE. While the OS supports client-server architectures very well, sharing of resources is not easy, particularly with the built-in applications, which do not offer an interface to allow third party developers to extend them. This invariably means shutting down the application, freeing up its file, reading it using code written from scratch or using library routines, and then restarting it once access is no longer necessary. Under EPOC16, sharing other physical resources, such as the serial port, is not possible.

 

EPOC32 effectively removes most of these limits. The ARM processor does not have the segmented memory architecture that so limits EPOC16. The first EPOC32-based units now shipping, Psion’s Series 5, have a whopping 4MB and 8MB. The application framework, which supports separating the user interface and engine at the OS level, also removes some barriers for sharing data between applications, since the application’s engine can be driven directly to provide any necessary data. The addition of a powerful SQL database also opens new possibilities for high-powered applications. Sharing of communications devices is much improved under EPOC32, including protocol stacks for TCP/IP and IrDA.

In the New Engine Room:

The EPOC32 architecture is made up of components with well-defined roles and relationships.

 

[** Figure 1 – EPOC32 Architecture **]

 

Psion refers to the main part of EPOC32 as the Base. The Base comprises several distinct components. E32 is the microkernel, which handles threads and processes. F32 is the file server, which handles conventional files. Store handles stream stores of two types: direct file stores, usually re-written upon saving (cf. an MS-Word document), or permanent file stores, with capacity for growth and a more complex internal structure (cf. an MS-Access database). The DBMS subsystem supports relational databases with multiple tables and SQL queries. The GDI handles all low-level graphics, including fonts, and uses a 24-bit color model, automatically mapping the colors as appropriate for the device. It also virtualizes the graphical capabilities and sets a standard for graphics APIs to be implemented at a higher level. At the top of the Base, lie components to handle higher-level functions, such as object embedding, rich text content and other application support. The kernel supports multiple processes and threads. Resources are allocated at the thread level, which allows for the Win32 Single-process emulator or WINS to run most EPOC32 applications.

 

On top of the Base is a comprehensive Graphics subsystem whose components give a solid platform on which to place actual applications. In particular, CONE (control environment) sets the standard for controls, including embedded controls, and how the user interacts with them. CONE does not impose a particular UI, but provides a common layer which any UI is expected to require.

 

Above the Graphics subsystem is EIKON. EIKON has components that handle the concrete part of the UI. EIKON dictates a particular style and attitude to applications. EIKON can just as easily be bypassed or even removed from the ROM for future machines from licensees. That would be comparable to have the Mac UI (menus, dialogs, windows, and controls) on top of Win32 subsystems. EIKON handles toolbars, dialogs, menus and scrollbars.

 

This architecture is pretty close to EPOC16, which is similarly modular. EPOC32 adds several new concepts. Firstly, Psion have embraced compound documents, in the form of stream stores and embedded objects. Anyone who has done any reading around the Microsoft OLE/COM architecture will be familiar with the notion. Within a single store (which may be in a disk file), there may be many different streams which can represent the different components of the document, including streams which will be managed by other application engines. This concept winds its way up through the architecture from the Base, which provides stream stores and application services, through Graphics, where CONE provides a mechanism for nested application controls, to EIKON, which supports embedded applications.

 

The embedding system allows iconic and glass doors. Doors are gateways into the embedded application/object/data. An iconic door displays only a simple pictogram representing the embedded object, while a glass door displays a view of the embedded object.

 

[** Figure 2 – Application Architecture **]

 

Psion envisions most application developers developing their applications in at least two parts. They have embraced the model-view-controller architecture but they have given it a new name (engine-view-application), and supported it at the OS level. The basic architecture divides an application into an engine and a GUI. The engine will be a DLL, providing all of the core application functionality and can be driven through calls to its methods or member functions that are exported in a link library.

 

Psion further advocates splitting the GUI into view and application. The views form the basis for glass doors and printing. In Psion’s three-tier structure, views handle the entire interaction with the engine. Thus a well-designed engine can be driven through OPL, a plain text console interface or from a view component embedded in a container in a full-fledged EIKON GUI application.

 

WINC is Psion’s bold strategy for unifying application access to data on both the PC and palmtop. This aspect of their system is currently limited to Win32. The WINC system is an EPOC32 subsystem that runs on Win32 and actually executes the same application engine (just compiled for Intel) running on the palmtop. If your application is designed with UI separate from the engine, then you can use the same engine code to provide services that can be accessed on both Windows and EPOC32. This can give file-sharing capability between desktop and palmtop without requiring conversion or feature translation at the file level. The strong correlation does mean that developers will be better able to bring out application modules for Win32 and EPOC32 at the same time. The engine components can be re-used either for just when dealing with palmtop files or even more completely if the application has fewer desktop needs. Whether we see practical use of this remains a question, as market uptake in the Windows developer sector will be necessary, and programming for WINC will require as much learning curve as programming for EPOC32 palmtops.

New Tackle

 

The new C++ SDK comes on a single CD-ROM, with no accompanying printed material. Installing the C++ SDK from CD-ROM is painless, and Psion’s portion takes a modest amount of storage — around 100MB. Psion actually denotes it the ASDK, for Application Software Development Kit, to distinguish it from the development kit available for OEMs looking to port EPOC32 to their hardware platform. The SDK also installs Perl (the version of Perl changed between the beta release and the shipping release, but I re-installed with no problems) and GCC, the GNU C++ compiler. The development system is based on Microsoft Visual C++ (4.2 or 5.0, including the VC++ learning edition — no MFC is required) and runs on Win95 or NT4. No changes are required to an existing Microsoft Developer Studio installation, although the addition of Psion’s resource compiler to the tools menu is recommended.

 

Psion recommends using Windows NT instead of Windows 95 for development as they claim it is more stable. I used Windows NT 4 and had no problems, and none running the emulator under Windows 95, either. I also recommend using it on NT, because it has just been reported that the release SDK will not run under Windows 95 due to some last minute unverified changes.

 

The first thing you will notice moving to the EPOC32 SDK is the large scope of the material. The documentation is supplied in HTML form, mainly comprised of plain HTML with some diagrams and a few frames, and is well organized. The SDK requires a solid knowledge of C++, as Psion have built their entire class framework in C++, using its features comprehensively. The documentation is incomplete and it can be patchy in some parts. Areas that are not yet written have no hyperlinks, but the missing material is acknowledged in that way. The EPOC World subscription will include updates to the CD-ROM and access to a web site for developers. The new SDK has a great deal of potential as it fills out; the object hierarchy is impressively complete. This is not surprising, given Psion’s long history of OO design and development. The OO system rises from the depths of the operating system and winds its way up through every part of the ROM.

 

Where the earlier SIBO C SDK for EPOC16 permitted programming in straight Standard ANSI C — even supporting plain console applications compiled with ANSI C standard library calls — the EPOC32 C++ SDK does not offer such simplicity. The SDK includes a glossary and index, which are useful in practice. I used Internet Explorer 3 under Win95 and NT4, and unsurprisingly, I did not have any problems with the SDK documentation.

 

I saw no indication of support for the Standard Template Library or the (draft) Standard C++ Library. As with EPOC16, most of that traditional functionality is provided within the ROM and is accessible through the existing EPOC32 classes. Browsing the list of types and classes is daunting, with a huge variety of template types for buffers and strings. While the HTML format of the documentation makes browsing extremely easy, it is easy to overlook some file or link with useful information. This is a typical problem with documentation in electronic form; it is difficult to judge the amount of data available. With a printed manual, it is easier to judge the amount of information and the presence of unread material. Psion have used HTML well in presenting the SDK, however, and the navigational tools are good. Coupling a full-text retrieval engine would improve things somewhat, but multiple file searches are something most decent text editors can do.

 

EPOC32 is currently available in two environments for software developers: the WINS Win32 "emulator" under Win95/NT and the MARM platform in the form of a Series 5. WINS stands for Win32 Single-process platform, while MARM stands for Machine ARM processor platform. There are other platforms for hardware development and testing for OS licensees. Since there are usually at least two target platforms for each project, Psion provides a single .mmp file to describe a project. A makmake tool processes these, generating .mak files for Visual C++ and .arm files for GCC.

 

A sample MMP file:

 

TARGET bosseng.dll

TARGETTYPE dll

UID 0x1000008d 0x10000251

PROJECT boss

SUBPROJECT eng1

SOURCE bosseng.cpp

USERINCLUDE .

SYSTEMINCLUDE \epoc32\include

LIBRARY euser.lib estor.lib

 

Psion uses a system of unique IDs (as set in the UID line) to identify applications. This is similar concept to OLE/COM’s class identifier. IDs are allocated by Psion upon request, but there are reserved ranges for testing applications. The UID system actually calls for three UIDs in every file: type, subtype, and subsubtype. For instance, applications executables in the form of DLLs have type KUidExe, set at compilation time, and a fixed subtype (0x1000008d) which indicates that they are applications. The subsubtype (0x10000251) uniquely identifies the application as WizBang 3.0, say. A stream store file which WizBang 3.0 has created will have type denoting the type of store, subtype denoting that it is an EPOC32 application DLL document, and subsubtype identical to the subsubtype of WizBang 3.0. When the operating system attempts to launch a document of type 0x10000251, it looks for an application of the same type to be able to handle the document.

 

To build for WINS, compilation and debugging occur within the Developer Studio environment using WINS. The WINS platform is a single-process version of EPOC32, which uses Intel binaries with support DLLs for Win32. Thus, it is not an emulator in the traditional sense, but it runs Intel code and utilizes DLLs to provide the EPOC32 functionality translated to the Windows API. The development platform requires Win95 or NT and Visual C++ to develop for WINS. Compilation for WINS can also be carried out on the command line, and Psion have provided example batch files that carry out much of the work.

 

To build for MARM, the provided tools create the project makefiles for use with the Cygnus GNU GCC compiler generating ARM code. Compilation for MARM is completely carried out on the command line. It is unclear whether an existing GCC compiler on, for example, a UNIX machine can compile for the MARM platform. I installed VC++ 4.2 and the beta SDK under NT4 and had just a few problems due to changes in the arguments expected by one of the batch files: EIKRS.BAT. I eventually found this documented in the release notes and it is stated correctly in the "Getting Started" tutorial.

 

[** Figure 3 – WINS/Emulator **]

 

Psion have created, as usual, a unique dialect for the development platform. One reason is cross-platform portability, but another is to promote safer programming. Psion have used an interesting form of prefix notation to identify class properties. Intrinsic C++ types, and other types that behave like them, have a type name beginning with ‘T’. So int and bool are replaced by TInt and TBool. The ‘T’ denotes that the object behaves like an intrinsic type. These are usually created on the stack, do not usually require a constructor or destructor, and do not own any external resources so they can safely be orphaned on the stack. Such objects do not usually require a copy constructor or assignment operator either, since the normal compiler generated member-wise shallow copy is sufficient. Some buffer and array types, or parameterized template types such as those Psion provides to implement strings and buffers have such member functions.

 

Normal classes in the class hierarchy are denoted with a prefix ‘C’. This denotes that the class is allocated on the heap. Most ‘C’ classes are derived from CBase. Psion commonly uses a two-phase construction — the first phase is carried out by the standard C++ constructor, while the second is a member function, called manually once C++ construction has completed. The second constructor, called ConstructL, can possibly call the Leave function on an error condition. The trailing L denotes that the member function can call Leave. The documentation states that C++ exceptions are not available, partially due to the lack of support for them in GCC. Psion have, instead used their Leave system. Functions which raise error conditions call the function User::Leave, which causes control to pass up to the last previously declared TRAP macro. The TRAP macro actually instantiates a TTrap object and calls a member function to handle setting up the trap harness. The User class is a collection of static functions grouped into a class for convenience and clarity. The leave system and two-stage construction is analogous to Psion’s system in EPOC16, which constructed objects using p_new() and then sent them an initialization message by method number.

 

Psion also have classes prefixed with R, denoting proxies for resources. Each of these is unique in its needs for construction and cleanup. An example of a ‘R’ class is RDevComm, the lowest level interface to the EPOC32 serial device. Psion denotes class member data with an ‘i’ prefix, denoting "instance" data. This is a term probably derived from Smalltalk. Psion have borrowed liberally from all areas of OO heritage.

 

The purpose for most of these mnemonics is to catch problems, through programmer force of habit, which the compiler cannot detect. In addition, Psion have used the C++ type system and liberal use of const to enforce as much correct behavior as possible.

 

The SDK installs several directories:

 

\boss\ The Boss Puzzle example application. This is used by the Practical Guide to introduce programming for EPOC32 from text-UI to GUI.

\cone\ The source to the CONE component, the low-level control environment

\eikon\ The source to EIKON, the higher-level UI manager

\epoc32\ The EPOC32 WINS Platform, Docs, Includes, Libs, and GCC Compiler

\epoc32ex\ EPOC32 example programs ranging from a simple DBMS demo to an EIKON skeleton application.

\paint\ The source to the Paint application

 

In addition to compiling full applications and engines with the SDK, the ability to compile OPX modules for use from OPL is also useful. OPL is a BASIC-like language with a shallow learning curve. When augmented by OPXs, OPL can rival C++ applications for scope and power. OPL can also be compiled on the Series 5 itself. OPXes are special DLLs callable from OPL as efficiently as normal OPL functions. The vendor of the OPX provides an OXH header file allowing the OPL programmer to access published interfaces into the OPX. The OPX must derive a class from COpxBase that implements the interface. Combining the power of C++ for small fast reusable component development and OPL for interface development can make a powerful duo.

New Lines & Sinkers

 

[** Figure 4 – Series 5 **]

 

The first things that most users will notice about a new Series 5 system are the large keys. Shortly thereafter, they will notice the touch screen and stylus. While the Series 5 will function perfectly well without the pen, the stylus brings new possibilities for a traditional keyboard-bound PDA.

 

The two Psion Series 5 machines have an impressive spec:

 

Notably missing from the SDK materials is the style guide (the hypertext link is not active). EPOC32 follows a full-screen application model with the addition of a few toolbars:

 

 

Forthcoming software and accessories include an Internet client and Quicken, and, in announcements directed at corporate users, Oracle mobile agents and Citrix ICA, the Windows NT-based thin-client.

Thar She Blows!

Psion provides a complete tutorial and several examples in the SDK. The "Getting Started" tutorial takes the developer through building a basic engine and text user interface, gradually adding functionality. The application is the Boss puzzle or sliding 15 puzzle. Although the code is not easily readable, Psion makes the component model design clear, and the documentation leads you through most aspects of the source. Since Psion’s object model is based on proper C++ without any intermediate layer, you actually get a usable component framework that is properly supported by the language. There are no message maps or translation tables. Psion recommends that engines be designed to be passive; they carries out particular actions as requested but do not interact with the user interface. The UI requests actions and then queries the engine for information.

 

[** Figure 5 – Boss Puzzle Text UI Version **]

 

The engine is compiled as a DLL, with an exported link library. The engine relies only on components found in Base. The text user interface is a standalone exe using the engine DLL. It instantiates the class TBossPuzzle and drives the engine directly. The display is a simple console interface which terminal users would be familiar. This interface is developed sufficiently well to test the game logic and even to save and restore the game state to a stream store.

 

[** Figure 6 – Boss Puzzle EIKON Version **]

 

To move on to a proper graphical interface, a view component is added. The view component is a DLL implementing a graphical view and is a subclass of a standard control class. This immediately gives the ability of the puzzle to be embedded in other documents. The view relies only on components found in the Graphics component, it does not utilize any higher-level EIKON facilities.

 

To use the view from an application, a graphical user interface based on EIKON is constructed. This DLL accesses the view by instantiating the view class in a container. The app never accesses the engine directly, but only through calls to the view. The EIKON app itself exports specific functionality to identify itself to the OS as an application.

Descriptors: Call Me Ishmael

In the next installment of this series, we will attack the SDK, building an application from scratch, tackling C++ issues, as well as debugging using the Developer Studio.

 

In the meantime, here is Psion’s take on a String class. Psion have created a mini-hierarchy of classes to handle strings and most general binary data. Often, strings in a program are not modified and remain constant for the lifetime of a program, they will be loaded as resources from a repository such as a resource file or a store.

 

The class TDesC is a constant descriptor class. It represents data that is not changeable. In C++, the class is represented (with many member functions omitted):

 

class TDesC8 {

// many member functions omitted

public:

TInt Length() const;

TInt Size() const;

const TUint8 *Ptr() const;

private:

TUint iLength;

};

 

typedef TDesC8 TDesC ;

 

TDesC is either defined to be TDesC8 or TDesC16 depending on whether the build is for an ASCII or UNICODE version. The Length() and Size() member functions are declared to be a const. A const member function may be called for both const and non-const objects of the class, whereas non-const member functions may only be called on non-const objects. This member function has been declared to not modify the object in any way. For ASCII builds, the Length() and Size() functions return identical values, whereas on a UNICODE build, the Size() function will return twice the Length(). The actual length is stored in the instance data iLength. You should have noticed that TDesC does not contain any of the actual data, or even a pointer to some data. TDesC is designed to be a base class. It does define a function Ptr() which returns a const TUint8* — a pointer to 8-bit unsigned values which are not allowed to be modified through that pointer.

 

The class TDes (defined to be TDes8 in ASCII) is a non-constant version of TDesC:

 

class TDes8 : public TDesC8 {

// many member functions omitted

public:

TInt MaxLength() const;

TInt MaxSize() const;

protected:

TInt iMaxLength;

};

 

TDes also has a bevy of member functions allowing modification of the data held therein. All other descriptors inherit from one of these two classes.

 

[** Figure 7 – TPtr and TPtrC **]

 

The TPtr (TPtr8 in ASCII) and TPtrC (TPtrC8 in ASCII) classes are derived from TDes and TDesC respectively. These are the simplest concrete implementations of descriptors. The TPtr classes store just a single pointer to data stored elsewhere. Whether that data is modifiable or not depends on the class: TPtr or TPtrC.

 

class TPtrC8 : public TDesC8 {

public:

// constructors omitted

private:

// operators omitted

protected:

const TUint8 *iPtr;

};

 

The instance data iPtr points to the location of existing data. It is not modifiable even by the instance of TPtrC.

 

class TPtr8 : public TDes8 {

public:

// constructors omitted

protected:

TUint8 *iPtr;

};

 

In TPtr, the instance data iPtr is non-const, allowing modification of the data.

 

Psion defines several macros to handle string literals. Since string literals will change size in ASCII and UNICODE builds, it is important to define literals using the _L() macro. The _S() macro creates a plain C-style string of either ASCII or UNICODE characters.

 

#define _L8(a) (TPtrC8((const TText8 *)(a)))

#define _S8(a) ((const TText8 *)a)

#define _L16(a) (TPtrC16((const TText16 *)L ## a))

#define _S16(a) ((const TText16 *)L ## a)

 

#if defined(_UNICODE)

typedef TText16 TText;

#define _L(a) (TPtrC((const TText *)L ## a))

#define _S(a) ((const TText *)L ## a)

#else

typedef TText8 TText;

#define _L(a) (TPtrC((const TText *)(a)))

#define _S(a) ((const TText *)a)

#endif

 

This construction lets _L() and _S() track the build correctly. By declaring:

 

iConsole->Printf(_L("Hello, World\n"));

 

a descriptor of the correct character width is created and passed to the Printf method.

 

Get your ARM-powered Psion and your Stroustrup-powered ARM and prepare to set sail for uncharted waters.

Davey Jones Locker (Bibliography)

Thinking in C++, Bruce Eckel: This book is excellent if you are moving from C to C++, or have a background in a functional language like Pascal or even Visual Basic, where OO styles are possible but not supported completely

The Annotated C++ Reference Manual, Ellis & Stroustrup: The definitive reference to the language, the ANSI base document; this will serve as a guide until the ANSI standard is finalized

The Design and Evolution of C++, Stroustrup: Gives great insight into the motivation behind most features in C++ and also has informed opinion on the ANSI standardization process

Effective C++, Scott Meyers: Easily digestible hints and techniques for using C++ like a native speaker

More Effective C++, Scott Meyers: What more can I say?

Design Patterns, Gamma et al (now available on CD-ROM): Many great ideas for designing systems

Psion homepage, http://www.psion.com

My homepage, http://ourworld.compuserve.com/homepages/cade/

 


Back to My home page
This article © 1997 Creative Digital Publishing, Inc.