C for Yourself - part 21

Steve Mumford creates a minimalist multi-tasking application

In the last couple of months we've been perusing some of the basic SWI commands we would need in order to create a basic multi-tasking program. When I say 'basic', that's precisely what I mean; as yet, we don't know how to include icons on the icon bar, open windows, or indeed do anything particularly useful. Still, that will change with time - for now, though, I'll lay out the minimum code required to start your program multitasking.

Back in December, I introduced two header files, kernel.h and swis.h. These contained the code necessary for calling the SWI functions from within C, and because these header files were provided by Acorn's C development kit as well as Beebug's EasyC, a program written using these calls compiles happily on both versions. Although this is quite desirable, there's a price to pay. If you've tried to make use of the functions, you might have discovered they're a mite tricky to control.

The problem arises when we have to convert the variables we're using in C into the format the SWI is expecting - the function _kernel_swi() makes use of structures to pass the register data back and forth, and each structure consists of an array of ten integers. However, when you examine the arguments that some of the SWI calls are looking for, it turns out that integers aren't the only thing they're after, and at first sight it can be unclear as to how you're actually supposed to pass the parameters to the call.

However, you don't have to worry unduly; just set up the data beforehand and use the casting operator in order to convert it to the correct type. For instance, to send the Wimp_Initialise call, the code might look something like this - taken from the example application on the cover disc:

_kernel_swi_regs regs_in, regs_out;
char appname[] = "Example";
long int msglist[1] = {0};

regs_in.r[0] = 310;
regs_in.r[1] = *(int *) "TASK";
regs_in.r[2] = (int) appname;
regs_in.r[3] = (int) msglist;

_kernel_swi(Wimp_Initialise, &regs_in, &regs_out);

The first three lines declare the variables we will be using; the task name is stored in an array of characters and an array of integers is set up to hold a list of the message numbers that our task is interested in, terminated by 0. However, at this stage we're interested in all of them, so giving 0 as the list's only value informs the WIMP that we should be notified whenever a message is passed.

Note that we can't just pass zero to the SWI - instead, we must pass a pointer to a list whose first member is zero. As an aside, it's impossible to mask out Message_Quit since it would be rather impractical if programs ignored any requests for them to quit.

Once our task has been initialised, we can then start the main poll loop - in this example application, it just listens out for messages by calling Wimp_Poll with a suitably-sized data block, although other conditions can be added by expanding the switch() construction. I've used an array of characters for the data block; it's then possible to access any byte individually and reconstitute whole words by a simple procedure of adding and bit-shifting.

In the example, this is carried out by the function btow(). If a message is received, the function wimpmsg() is called which determines the message type, held in the word beginning at the address pollblock+16.

Again, the only message catered for is Message_Quit, but this is enough to allow the program to multitask - if you run the code and open up the Task Manager, you'll see the name of our application listed with all the others at the top. Clicking Menu over its name and selecting Quit will fire off the appropriate message and your program will terminate.

If you're using Acorn's C development kit, there's another way of calling SWI commands fiven in swis.h. Instead of passing the register contents in one amorphous block, you can make use of the _swi() function - its syntax is somewhat different to _kernel_swi(), but it's worth mentioning since it doesn't require you to set up any banks of variables beforehand. I've supplied an example program in this format so you can take a look for yourself. That's all for now; see you next time.

Source: Acorn User - 165 - February 1996
Publication: Acorn User
Contributor: Steve Mumford