Chapter 2: Moving to C

In Chapter 1 we constructed a very simple Wimp application in BASIC. It didn’t do very much – simply registering itself with the Wimp and then waiting quietly in the background until it was asked to quit – but it contained all the core elements required to make software multitask under RISC OS.

We’re now going to convert that simple application into C, to allow the two languages to be compared. For now we’ll copy the structure of the BASIC version directly – this isn’t the best way to go about things, but it will make it easier to compare the two without introducing too many new concepts in one go.

The core parts of this simple application will still be useful when we come to look at restructuring the code around event handlers – a much better way to write applications in C – in the next chapter.

Accessing RISC OS

The first thing, which might not be obvious to anyone who is familiar with C but has never written software for RISC OS, is how to access the SWI calls which are central to the system. In BASIC we’ve already seen that it’s possible to write something like

SYS "Wimp_Initialise", 310, &4B534154, "Example App", 0

and the interpreter will do the rest. Natively C doesn’t have any equivalent to the BASIC SYS call, and so with the DDE we need to turn to the kernel.h and swis.h headers to find a way to achieve the same effect.

The kernel.h header provides a _kernel_swi() function which allows SWIs to be called in a somewhat cumbersome way using the SWI names defined in swis.h. The call to Wimp_Initialise could be coded as follows:

#include "swis.h"
#include "kernel.h"

_kernel_oserror *error;
_kernel_swi_regs regs;
regs.r[0] = (int) 310;
regs.r[1] = (int) 0x4b534154;
regs.r[2] = (int) "Example App";
regs.r[3] = (int) 0;
error = _kernel_swi(Wimp_Initialise, &regs, &regs);

This is clearly quite complex for a single SWI call, and includes a fair bit of casting to get the various types of parameter into integers, so the libraries supplied with the DDE offer an alternative in the form of the _swi() and _swix() functions in swis.h. Using these, we could re-write the call to Wimp_Initialise like this:

#include "swis.h"

_kernel_oserror *error;
error = _swix(Wimp_Initialise, _INR(0,3), (int) 310, (int) 0x4b534154,
                (int) "Example App", (int) 0);

This is better, but is still a little ungainly.

The DDE does offer some other options. The WimpLib, which comes as part of TBoxLib, supplies veneers for all of the Wimp SWIs – this would allow us to write

#include "wimplib.h"

_kernel_oserror *error;
error = wimp_initialise(310, "Example App", 0, 0, 0);

This is starting to look a lot better, but the supplied header files concentrate on sections of the OS such as the Wimp and the Toolbox – plenty of areas still need the likes of _swi() to access them. Fortunately, there’s a better option still.

Introducing OSLib

Originally developed by Jonathan Coxhead while he worked at Acorn, OSLib is a library that covers the whole of the RISC OS API with dedicated veneer functions for each individual SWI call (and even functions for individual reason codes when SWIs like OS_SpriteOp provide multiple actions).

Unlike the _kernel_swi(), _swi() and _swix() interfaces provided by the DDE, OSLib makes use of defined types so that SWI parameters require little casting and can be type-checked by the compiler. In addition, C structures are provided to represent all of the data structures used by supported SWIs, making it much easier to extract data from them.

Without going into any detail about how it works for now, we could re-write the Wimp_Initialise example using OSLib as follows

#include "oslib/wimp.h"

os_error *error;
error = xwimp_initialise(wimp_VERSION_RO3, "Example App", NULL, NULL, NULL);

Notice that in all the C examples so far, we’ve used a variable error, which we’ve assigned from the function. This is loosely equivalent to the following code in BASIC.

SYS "XWimp_Initialise", 310, &4B534154, "Example App", 0 TO error% ;flags%
IF (flags% AND 1) = 0 THEN error% = 0

That is, the X form of the SWI is called: this means that in the event of an error the processor’s V flag is set and R0 contains a pointer to an error block. If the V flag is set, SYS sets bit 0 of flags%, so the second line of code results in error% being set to zero on return if no error occurred; if something went wrong, error% points to the error block. The C convention is similar: SWI functions which return an error block pointer return NULL if no error occurred.

In most of the C examples above, we’ve explicitly called the X form of the SWI – in the case of _kernal_swi() and WimpLib’s wimp_initialise(), the X form was called for us. That’s usually a good idea when working in C: without BASIC’s error handler to look after us, it’s often better to test for errors and handle them immediately. However, OSLib gives us the choice and the following would also work:

#include "oslib/wimp.h"

wimp_initialise(wimp_VERSION_RO3, "Example App", NULL, NULL);

Which to use depends on the circumstances, but generally if there’s a risk of an error occurring the X form of the SWI is better: it allows us to trap the error, tidy up, and exit gracefully.

OSLib’s StrongHelp manual is more-or-less essential reading for developers, and we can see in Figure 2.1 that it contains full details of how to use wimp_initialise() amongst its many pages.

Figure 2.1: OSLib’s manual showing the use of wimp_initialise()

Although we’ll start by documenting the various parts of OSLib that we use in the opening examples, being able to navigate the supplied information will soon become essential.

Different types of variable

Now that we’ve been introduced to OSLib, let’s see how we can use it to re-write Listing 1.1 in C.

As this simple example should all fit inside main(), the first thing to do is declare the variables that we’ll be using. The BASIC version used three variables: quit%, b% and reason%, and the C version will be the same.

osbool          quit = FALSE;
wimp_block      block;
wimp_event_no   reason;

Just as in BASIC, we’re going to use the quit variable as a boolean flag to track when the application must exit. We could just define it as an int, but that isn’t a good idea when C lets us be more descriptive. The standard stdbool.h header gives us the bool type, but OSLib offers us osbool and we’ve opted to us that here.

Similarly, the BASIC code used reason% to store the reason code returned by Wimp_Poll. Although it’s “just” an int, we’ve already said that OSLib defines precise types to represent different SWI parameters more exactly. The oslib/wimp.h header contains the definition

typedef int wimp_event_no;

to provide a special type of wimp_event_no to hold the reason code – this is exactly what we need to define the reason variable.

Finally, the BASIC contained the line

DIM b% 255

to allocate 256 bytes of memory for the Wimp_Poll information block. OSLib defines a union wimp_block and a wimp_block type as follows

union wimp_block {
        wimp_draw       redraw;
        wimp_open       open;
        wimp_close      close;
        wimp_leaving    leaving;
        wimp_entering   entering;
        wimp_pointer    pointer;
        wimp_dragged    dragged;
        wimp_key        key;
        wimp_selection  selection;
        wimp_scroll     scroll;
        wimp_caret      caret;
        wimp_pollword   pollword;
        wimp_message    message;
        byte            reserved[256];
};

typedef union wimp_block wimp_block;

Each of the wimp_ types contained in the union are defined by OSLib to match the data structures returned by the Wimp for the different reason codes – we’ll look into these when we meet them. The final byte reserved[256] is simply a block of 256 bytes to reserve the space (similar to BASIC’s DIM b% 255), which means that the act of declaring a variable of type wimp_block (as we do with wimp_block block above) will make the compiler reserve 256 bytes of space for it on the stack.

Initialising the Wimp

As with the BASIC version of our program, the next thing to do is to call Wimp_Initialise to register as a multitasking application. In BASIC, doing that looked like this:

SYS "Wimp_Initialise", 310, &4B534154, "Example App", 0

OSLib provides function veneers for all the OS SWIs, and as we saw above, one of those is wimp_initialise():

wimp_initialise(wimp_VERSION_RO3, "Example App", NULL, NULL);

OSLib’s implementations of SWIs take their parameters in the same order as BASIC’s SWI instruction, but unnecessary or invariant parameters are often omitted. The function prototype for wimp_initialise() is as follows:

extern wimp_t wimp_initialise(
        wimp_version_no         version,
        char const              *name,
        wimp_message_list const *messages,
        wimp_version_no         *version_out
);

In Wimp_Initialise, we saw before that R0 takes the minimum version of the Wimp that the application can accept: OSLib gives its version parameter a type of wimp_version_no, which is defined as typedef int wimp_version_no. It turns out that there are a small number of ‘significant’ values that the Wimp recognises for the version number, and OSLib defines constants for these:

#define wimp_VERSION_RO2        ((wimp_version_no) 0xC8u)       /* 200 */
#define wimp_VERSION_RO30       ((wimp_version_no) 0x12Cu)      /* 300 */
#define wimp_VERSION_RO3        ((wimp_version_no) 0x136u)      /* 310 */
#define wimp_VERSION_RO35       ((wimp_version_no) 0x15Eu)      /* 350 */
#define wimp_VERSION_RO36       ((wimp_version_no) 0x168u)      /* 360 */
#define wimp_VERSION_RO37       ((wimp_version_no) 0x172u)      /* 370 */
#define wimp_VERSION_RO38       ((wimp_version_no) 0x17Cu)      /* 380 */
#define wimp_VERSION_RO40       ((wimp_version_no) 0x190u)      /* 400 */

Although the constants are defined in terms of hexadecimal (which can be confusing, despite their values not really being meant to be human-readable), we can see that wimp_VERSION_RO3 is set to be (wimp_version_no) 0x136u – which is 310 in decimal with an appropriate cast from uint to wimp_version_number included.

R1 takes the “magic” constant &4B534154 – or “TASK” – to switch off Arthur compatibility. Although we have to pass this manually in BASIC, it never changes in modern software and so OSLib’s wimp_initialise() doesn’t even offer a parameter to give us control. If you really need to write for Arthur, you’ll need to use OSLib’s alternative wimp_init() function which allows the value of R1 to be specified.

The next parameter to Wimp_Initialise, in R2, was a pointer to a string containing the name of the application. OSLib’s wimp_initialise() is no exception, and the next parameter is *name – a pointer to a string.

In the BASIC version, we said that we could safely pass zero in to R3, and that’s not changed now. The only difference is that as the Wimp treated that zero as a pointer, we pass NULL here to make that more obvious. We’ll come back and look at what R3 is doing later on.

One thing that we didn’t consider in BASIC was the fact that Wimp_Initialise also returns a couple of values in R0 and R1. R0 contains the version of the Wimp that is currently active (to compare to the “ideal” one that we supplied in R0 on entry), while R1 contains a task handle: a number that uniquely identifies our application from all of the others running on the system. We don’t actually need either of these yet – but while it was OK to just ignore them in BASIC, we now need to at least acknowledge their existence.

OSLib’s convention is that for non-X SWIs the most useful return value is returned by the function, while the others are returned via additional parameters passed as pointers. In the case of wimp_initialise(), the task handle from R1 is returned and we can supply a pointer to a variable of type wimp_version_no if we want the actual version number of the Wimp from R0. Since we don’t care about either at present, we just pass NULL for the final parameter instead of a pointer to a suitable variable – OSLib won’t then return the value.

As an aside, it’s worth mentioning that for the X form of SWIs, the function returns a pointer to an os_error block (or NULL if no error occurred). In this case, all return values are passed back via parameters. This means that xwimp_initialise() has a slightly different prototype from wimp_initialise():

extern os_error *xwimp_initialise(
        wimp_version_no         version,
        char const              *name,
        wimp_message_list const *messages,
        wimp_version_no         *version_out,
        wimp_t                  *task_out
);

We’ll be seeing more of both forms of the OSLib API as we progress.

Polling the Wimp

It’s still necessary to call Wimp_Poll repeatedly to make our small application multitask, and that’s done in a very similar way in C:

while (!quit) {
        reason = wimp_poll(wimp_MASK_NULL | wimp_MASK_ENTERING |
                        wimp_MASK_LEAVING | wimp_MASK_GAIN |
                        wimp_MASK_LOSE | wimp_MASK_POLLWORD,
                        &block, NULL);

        switch (reason) {
        case wimp_USER_MESSAGE:
        case wimp_USER_MESSAGE_RECORDED:
                if (block.message.action == message_QUIT)
                        quit = TRUE;
                break;
        }
}

There’s a similarity between the while () loop here and the REPEAT UNTIL loop in the BASIC version: both just call Wimp_Poll until instructed to stop.

The call to Wimp_Poll is similar, although again it shows some of the differences in approach between BASIC and C. Its prototype in OSLib looks like this:

extern wimp_event_no wimp_poll(
        wimp_poll_flags mask,
        wimp_block      *block,
        int             *pollword
);

As with the native SWI, the call takes both an event mask (passed in R0) and a pointer to a block of memory to return details of any event that has occurred (passed in R1). As noted in the previous chapter, the mask is a 32-bit integer in which bits are set to inform that Wimp that we don’t wish to receive certain types of event. In BASIC, we passed in the “magic number” of &3831; as might be expected, OSLib makes things a bit more self-documenting by defining some constants:

#define wimp_MASK_NULL          ((wimp_poll_flags) 0x1u)
#define wimp_QUEUE_REDRAW       ((wimp_poll_flags) 0x2u)
#define wimp_MASK_LEAVING       ((wimp_poll_flags) 0x10u)
#define wimp_MASK_ENTERING      ((wimp_poll_flags) 0x20u)
#define wimp_QUEUE_MOUSE        ((wimp_poll_flags) 0x40u)
#define wimp_QUEUE_KEY          ((wimp_poll_flags) 0x100u)
#define wimp_MASK_LOSE          ((wimp_poll_flags) 0x800u)
#define wimp_MASK_GAIN          ((wimp_poll_flags) 0x1000u)
#define wimp_MASK_POLLWORD      ((wimp_poll_flags) 0x2000u)
#define wimp_MASK_ICON_LEAVING  ((wimp_poll_flags) 0x4000u)
#define wimp_MASK_ICON_ENTERING ((wimp_poll_flags) 0x8000u)
#define wimp_MASK_MESSAGE       ((wimp_poll_flags) 0x20000u)
#define wimp_MASK_RECORDED      ((wimp_poll_flags) 0x40000u)
#define wimp_MASK_ACKNOWLEDGE   ((wimp_poll_flags) 0x80000u)
#define wimp_GIVEN_POLLWORD     ((wimp_poll_flags) 0x400000u)
#define wimp_POLL_HIGH_PRIORITY ((wimp_poll_flags) 0x800000u)
#define wimp_SAVE_FP            ((wimp_poll_flags) 0x1000000u)
#define wimp_ISSUE_POLL14_AND15 ((wimp_poll_flags) 0x2000000u)

Adding together the individual bits defined by the constants wimp_MASK_NULL, wimp_MASK_ENTERING, wimp_MASK_LEAVING, wimp_MASK_GAIN, wimp_MASK_LOSE and wimp_MASK_POLLWORD should give the result 0x3831 – it is, however, clearer what’s going on and where the value came from. Or it would be, if we knew what the flags all mean: we’ll introduce them properly in the following chapters.

The address of the poll block that we initialised at the top of the function is passed in with the help of the ‘address of’ operator; as we’re not interested in the pollword for now, NULL is passed in for the final parameter to prevent a value being returned. The reason code is returned by the function when it returns.

Just as in BASIC, the returned reason code (in reason) is tested against a list of known codes. Previously we looked for the values 17 and 18; this time – as we’re coming to expect – OSLib provides some more meaningful constants to use instead of “magic numbers”:

#define wimp_NULL_REASON_CODE           ((wimp_event_no) 0x0u)  /* 0    */
#define wimp_REDRAW_WINDOW_REQUEST      ((wimp_event_no) 0x1u)  /* 1    */
#define wimp_OPEN_WINDOW_REQUEST        ((wimp_event_no) 0x2u)  /* 2    */
#define wimp_CLOSE_WINDOW_REQUEST       ((wimp_event_no) 0x3u)  /* 3    */
#define wimp_POINTER_LEAVING_WINDOW     ((wimp_event_no) 0x4u)  /* 4    */
#define wimp_POINTER_ENTERING_WINDOW    ((wimp_event_no) 0x5u)  /* 5    */
#define wimp_MOUSE_CLICK                ((wimp_event_no) 0x6u)  /* 6    */
#define wimp_USER_DRAG_BOX              ((wimp_event_no) 0x7u)  /* 7    */
#define wimp_KEY_PRESSED                ((wimp_event_no) 0x8u)  /* 8    */
#define wimp_MENU_SELECTION             ((wimp_event_no) 0x9u)  /* 9    */
#define wimp_SCROLL_REQUEST             ((wimp_event_no) 0xAu)  /* 10   */
#define wimp_LOSE_CARET                 ((wimp_event_no) 0xBu)  /* 11   */
#define wimp_GAIN_CARET                 ((wimp_event_no) 0xCu)  /* 12   */
#define wimp_POLLWORD_NON_ZERO          ((wimp_event_no) 0xDu)  /* 13   */
#define wimp_USER_MESSAGE               ((wimp_event_no) 0x11u) /* 17   */
#define wimp_USER_MESSAGE_RECORDED      ((wimp_event_no) 0x12u) /* 18   */
#define wimp_USER_MESSAGE_ACKNOWLEDGE   ((wimp_event_no) 0x13u) /* 19   */

It turns out that 17 and 18 are in fact the numbers that the Wimp gives to the User_Message and User_Message_Recorded events – we’ll return to what these actually are later on, but as we saw in the BASIC version, we’re looking for the word at offset 16 in the poll block returned by the Wimp to be zero for either of these reason codes.

The block is a variable of type wimp_block, which as we saw above is a union of several different types – one of which is wimp_message. Although not quite true, OSLib ‘almost’ defines it as follows:

struct wimp_message {
        int     size;           /* Offset 0     */
        wimp_t  sender;         /* Offset 4     */
        int     my_ref;         /* Offset 8     */
        int     your_ref;       /* Offset 12    */
        bits    action;         /* Offset 16    */
        byte    reserved[236];  /* Offset 20    */
};

That is, there are five word-sized variables at the start (int on RISC OS is 32-bits long, and both wimp_t and bits are also 32-bits in length) taking up 20 bytes, followed by 236 bytes of reserved space starting at the byte offset of 20. Altogether that gives 256 bytes, which luckily matches the byte reserved[256]; that was also included in the wimp_block union.

The reason for the ‘almost’ above is that there’s some more jiggery-pokery in the OSLib definition to give access to the additional data which arrives with most messages. Message_Quit doesn’t have any more data (the application is expected to quit without asking any questions), so for now we can safely ignore the extra complexity – it will make more sense when we meet user messages properly in a later section.

At offset 16 in the structure, we find bits action, which means that

if (block.message.action == message_QUIT)
        quit = TRUE;

is testing the word at offset 16 to see if it is equal to message_QUIT. That’s also defined by OSLib, to be

#define message_QUIT    0x0u

which makes the whole construct have the same effect as the BASIC

IF b%!16 = 0 THEN quit% = TRUE

It should be fair to say that the version in OSLib is clearer: there’s no need to remember offsets into blocks of data, or the “magic numbers” which correspond to events and messages (clearly one could define constant variables in BASIC, but there’s soon a speed penalty).

As soon as Message_Quit is detected, the quit is set to TRUE – which will cause the while loop to terminate.

A clean exit

As with the BASIC, once Message_Quit has been received, it’s necessary to call Wimp_CloseDown before the program exits. With OSLib, that’s done using wimp_close_down() which has the following prototype:

extern void wimp_close_down(
        wimp_t  t
);

There’s one parameter, which is the handle of the task that’s exiting. For most ‘normal’ applications this can safely be left as zero since the Wimp knows which application is quitting from context.

wimp_close_down(0);

There’s no harm in passing in the task’s handle if we know it, however – it’s returned by Wimp_Initialise, and often an application will need to know it for other reasons.

Putting it all together

Assembling all of the snippets of code above will result in Listing 2.1. Although its structure doesn’t follow the way in which a Wimp application would normally be written in C, it has the same structure as its BASIC sibling from Listing 1.1 – allowing the two to be compared easily.

/**
 * Example 2.1
 *
 * (c) Stephen Fryatt, 2015
 *
 * File: main.c
 */

#include "oslib/wimp.h"

int main(int argc, char *argv[])
{
        osbool          quit = FALSE;
        wimp_block      block;
        wimp_event_no   reason;

        wimp_initialise(wimp_VERSION_RO3, "Example App", NULL, NULL);

        while (!quit) {
                reason = wimp_poll(wimp_MASK_NULL | wimp_MASK_ENTERING |
                        wimp_MASK_LEAVING | wimp_MASK_GAIN |
                        wimp_MASK_LOSE | wimp_MASK_POLLWORD,
                        &block, NULL);

                switch (reason) {
                case wimp_USER_MESSAGE:
                case wimp_USER_MESSAGE_RECORDED:
                        if (block.message.action == message_QUIT)
                                quit = TRUE;
                        break;
                }
        }

        wimp_close_down(0);

        return 0;
}

Listing 2.1 (c.main): The application converted to C

This time, however, we will need to compile the code before we can see it in action. In the next chapter, we’ll look at how to do that.