C The Wimp

The RISC OS WIMP system is addressed using SWI calls (SWI stands for SoftWare Interrupt). These SWI commands cover just about every single part of the RISC OS system from screen to filer to memory and then some more!

SWI commands normally require arguments (or parameters) to be passed to them. The arguments are passed in something known as a register. In the normal course of proceedings, there are only 13 registers (r0 to r12) ever needed. There are another three (r13 - r15) which are never normally used, except in some very special circumstances.

In Basic, a SWI would be called using something along the lines of

SYS "OS_File",5,filename$ TO ,,,,r4%

This would tell the machine to call OS_File with r0=5, r1 = filename to look at and on exit from the routine, the length of the file is returned into r4%.

There are two ways to call this in C

_kernel_swi_regs regs;
regs.r[0]=5;
regs.r[1]=filename;
_kernel_swi (OS_File,&regs,&regs);
r4=regs.r[4];

or

_swi(OS_File,_IN(0)|_IN(1)|_OUT(4),5,filename,&file_size);

It's not hard to see which will be the one in use here!

At this point, unless you already have them, I would suggest getting hold of a copy of the PRMs (programmers reference manuals) or the StrongHelp OSfile. When programming the WIMP or even when not programming the WIMP, these are essential if you're serious about programming.

Both of these _swi calls require the non-ANSI header file swis.h which exists in both GCC (inside !Unixlib) and in Acorn C/C++. This header file creates the SWIs used by the machine.

The problem here though is that the swis.h will go out of date as more SWIs are added. This is where you can either create your own header file, or use a ready made one such as Desklibor !OSLib. These keep up with the new SWIs added to the OS and are also a lot more flexible than the standard Acorn or GCC headers (this is only my opinion!).

I suppose that, from here on in, quite a lot of the material will be RISC OS only - the SWI commands are RISC OS specific and therefore no good for Mac or PC machines. This means that for porting to other platforms, the files should be as well documented as possible - that said, I've yet to see a well documented source code from either a Mac or PC which requires a WIMP system!

A word of thanks

This series wouldn't have been possible without having first read and gone through (sometimes quite a few times) the brilliant book "Wimp Programming for All on Acorn RISC Computers" by Calcraft and Wrigley - and thanks to Roger Darlington at University of Salford for the loan of this book. I am also indebted to Joseph Heenan & Rob Kendrick for proof reading the entire series for me. Thanks.

Unlike the C from the Top series, where there are a plethora of texts available, WIMP programming books for RISC OS machines are somewhat thin on the ground - the Wimp Programming for All dates back to 1993. I don't think it is still available as RISC Developments are no more, but you may strike lucky at a library.

Though it is written for Basic, as you will have already seen, C and Basic V share a great many of the same structures, so the conversion from C to Basic can be quite simple. I've attempted not to do a direct conversion though.

The WIMP - what is it?

WIMP stands for Windows - Icon - Menus - Pointer. On the RISC OS platform, this refers to the Window Manager module (the current version, under RISC OS 4, is 4.15 - available from the RISCOS Ltd website). The RISC OS wimp system is a great example of a GUI (pronounced gooey). There are plenty of GUIs around (such as Windows and MacOS), but none, in my opinion, touch that on the RISC OS platform. You click on an icon and the program responds, quickly and efficiently. Even under OS2, the Acorn WIMP system is better than WinNT4 under most circumstances!

What sets the RISC OS wimp apart from the likes of Windows is the ability to truly multitask (though MicroSoft frequently boasts that Windows can multitask - the truth is that Windows can't but the DOS side can).

The RISC OS wimp system works as follows. The program is written, compiled and run. A window comes up and waits for something to happen. This something is called an event. If no event is forthcoming, it is known as a NULL event.

The multitasking ability works like this. Say you have five programs running at any one time - all are on screen. The processor looks at application 1 and sees that only NULL events are being returned. This means the processor doesn't have to do anything with application 1. The processor looks at application 2; again NULL events are returned so it's on to application 3 and so on. If any of the applications return an event code, the processor acts, but acts so quickly (and even on the ARM 3 this happens quickly) that you don't notice - the system multitasks.

One thing worth noting here is the different types of applications possible.

First we have the single-task application. This is a non-WIMP application and typically runs in either a task window or from the * prompt when you press <f12>.

Next, we have the "type 2" application. These use the WIMP but when processing information, they act as single-task applications, locking the WIMP and stopping it from multitasking. Programs created using WimpBasic are type 2 applications.

The third is the fully WIMP and multitasking application (such as Impression and TechWriter) which allows other similar applications to work in parallel.

Using SWIs

In one of the first C from the Top tutorials, I talked about RISC OS machines using a buffered input system and that there was a non-standard PC library called Conio.h which did things like wait for a keypress.

We have the same under RISC OS, OS_ReadC. A simple example of using an OS call such as ReadC is given below - note, it is still a CLA program, but you can now see what I mean that in some instances, a simple single-tasking program helps with the understanding of how something works!

#include <stdio.h>
#include "swis.h"

int main(void)
{
 int return_code;
 printf("Press any key\n");
 _swi(OS_ReadC,_IN(0)|_OUT(0),0,&return_code);
 if (return_code>32)
 printf("Thank you. You have just pressed the %c key\n", return_code);
 else
 {
  if (return_code==32)
  printf("Thank you. You have pressed the space bar\n");
  else
  printf("Thank you for pressing the return key\n");
 }
 return 0;
}

What is this _IN(0) lark?

The method for calling a SWI should be

SYS "SWI_NAME",r0,r1,r2,r3...r12 TO r0,r1,r2,r3...r12

As for most SWI calls, not all 12 registers are used for data in or data out; the format is truncated to just using the registers required. The TO indicated that values are returned from the module you called. Again, if you're only interested in (say) r4, the SWI passed the TO can be truncated to

the absence of the registers not required are just indicated by the correct number of commas (for clarity, I've included what should be there as well).

This is quite messy and it is also very easy to make mistakes (one comma short and you read back r3, one too many and you get r5 - neither of which you want). This is where the _IN and _OUT are so useful.
You say which SWI is to be called and then the values to be passed and read. Using the same example, if r0 to r3 had to be sent to the module and r4 and r7 to be read back, the call would look like

_swi(some call,_IN(0)|_IN(1)|_IN(2) |_IN(3)|_OUT(4)|_OUT(7) ,0,1,2,3,&r4,&r7);

the possibility of the errors by using the comma separators has gone. The only error that can now be made is the wrong data being passed to the module in the first place.

What else can OS calls do?

Everything.

That was simple. OK, they can't feed the dog or make a coffee, but for your machine, they can do anything.

Why use C and not Basic?

It's swings and roundabouts. While Basic V is the most powerful Basic available, it has its drawbacks such as being (relatively) slow, although it has the large attraction of its string handling capabilities.

That said, Basic doesn't lend itself very well to being used as a modular programming language (i.e. using libraries of commonly used functions). While there are Basic libraries such as Justin Fletcher's JFShared, they are few and far between. C is the opposite.

Without having the likes of !OSLib or DeskLib (these are just two of the many available PD libraries of common RISC OS C functions and will be covered later), a simple header file (called openwin.h) can be constructed and included in any part of your program by just #includeing it. The .h file would look like this:

typedef struct win_open {
                    int vis_x_min;
                    int vis_y_min;
                    int vis_x_max;
                    int vis_y_max;
                    int x_scroll;
                    int y_scroll;
                    int open_behind;
                    int win_flags;
                        } win_open;

and in the main code

win_open *open_win;
open_win=malloc(sizeof(win_open));

followed by an appropriate check to see if this much memory can be assigned.

This means that, as soon as you've got your library together, application building can be very rapid.

That about wraps things up for this introduction. Next time, we'll get down to some hard stuff, such as starting a program.
Until then, using the StrongHelp OS manual, try constructing some simple CLI/single-task programs for yourself. You will find it very useful.


Source: Archive Magazine 14.4
Publication: Archive Magazine
Contributor: Paul Johnson