The multi-application environment
One of the most important things to bear in mind when writing applications for the Z88 is that each application must share the system with other activities in the machine. It is vital that applications do not interfere with each other and only access resources in a legal way; if this is not done some applications may have the ground cut from under their feet. Providing legal interfaces are used there are no problems with interference. Back door techniques are dangerous on a system like the Z88 because things which happen behind the systems back may have dire effects on other applications in the machine, possibly meaning resources are lost to the system, that some applications may not run, or in most cases that the system simply crashes.
Whenever an application is waiting for a keypress it may be suspended. This may take the form of the machine being switched off and back on again, or in a more complex case the user may enter other applications, use them for a while and return to the original application - this case is called pre-emption. The application is largely unaffected by what takes place during suspension and can carry on as normal, although occasionally routines, such as the system get line routine, have to be set up again when suspension occurs. In some circumstances the application's screen will have become disrupted and in these cases the system informs the application that it needs to regenerate its screen. Where this is not possible the system provides a way of saving an entire screen image, although this is costly in time and memory and cannot always be guaranteed. There is one case, however, where what has occurred during suspension does, however, have a profound effect on the application and this case is that of a kill request from the Index. This is a request for the application to free all claimed resources, ie. close files, filters, streams, and de-allocate memory, before gracefully shutting itself down. Applications are morally obliged to respond to this request.
Perhaps what makes the Z88 environment most different from other Z80 based environments is that applications can readily access as much memory as is available to the machine, possibly up to 3M. Of course the Z80 can only address 64K so the physical memory is divided into 256 16K banks, four of which can be paged into the four 16K segments which make up the Z80 address space. In fact, things are a little more complex, but this is discussed more fully in 'Memory Management'. When applications require memory they first open a memory pool, and get a memory handle. This handle is then used to make requests for memory which can be between 2 and 256 bytes in size. The advantage of the memory pool is that all allocations made within one pool can be de-allocated by simply closing the pool. The memory which is allocated has an address within its 16K bank and a bank number. To access the memory the appropriate bank must be bound to a segment. To make things easier a particular memory allocation can be associated with a particular segment, thus the address returned by the allocation routine can be used to access the memory directly once the appropriate bank has been bound to the appropriate segment.
Some readers may find it quite alarming that the maximum continuous memory allocation is restricted to 256 bytes. In practice this should not be a problem, although it requires a change of style in writing code from more conventional Z80 systems. It means that chunks of memory have to be linked together - the system provides a set of linked list routines which can be used if required - rather than using adjacency as an integral part of a data structure. All but one of the built in applications are written using the memory management system, and because of their adherence to the rule they are called 'good'. Some applications do have a genuine need for a large continuous block of memory such as the BASIC supplied with the machine. Because it is a version of BBC BASIC(Z80) which has been ported across to the Z88, it assumes that continuous memory is available. To meet the needs of this kind of application the operating system provides a facility to give applications between 8K and 40K of continuous memory. Since much of this memory is unlikely to be used for much of the time, when the application is suspended memory can be given back to the system. In order to create the continuous memory environment a great deal of swapping of memory chunks has to take place, and the system becomes generally clogged up which is why applications which use this environment are said to be 'bad' and should be avoided if possible. All this having been said there are cases where a bad application is a good idea. If your application is likely to be the only one running on the Z88 at any one time, ie. it dominates rather than supplements the machine, then a bad application is a perfectly justifiable route. Note that if a bad application is started as the first application, and does not give memory back to the system, then ironically memory swapping is reduced and the application cohabits better with existing facilities.
Note that when the memory resources of the machine begin to be used up, it may not be possible to enter an application because there is not enough memory to run it. This is especially true of bad applications which tend to demand more memory than they actually need. The application writer does not need worry about this because when memory is short the application will simply not be entered. However it is necessary for the application to cope with a situation where requests for memory fail. In such cases the user should be informed. The system provides a set of error messages which the application can use when resources are not available.
Some components of an application
- All applications available to the user are listed in the index on the left hand window. Associated with each application is a key letter which can be used to select it in conjunction with the square key. If two applications have the same key letter then the application with the higher slot number will be preceded by a Z, or even two Z's, if there are three applications. The suspended applications window automatically gives the name of all suspended applications along with the time and date of suspension. If the application is running in an external card then the slot number will be placed in the card column to the right of the window. The 'Your Ref' column on the left of the window is a 15 character wide slot which the application can set. It is useful for distinguishing between different instantiations of the same application. Finally the user can kill off applications by placing the highlight bar over the appropriate application in the suspended applications window and entering <>KILL. Note that on the Index menu the kill command is a 'safe' one and so must be entered using its diamond sequence, and not via the menu.
- The Filer is a built in popdown for handling the RAM filing system. Each application is associated with a default device and directory. These are set when the application is started by looking at the default settings in the panel. When the filer is called from an application it will inherit these settings. If the settings are changed from within the filer, using 'Select Directory' and 'Select Device', then the application settings will be altered correspondingly. Further, it is possible for these settings to be changed by the application itself. The filer delivers mail to applications, which consists of the name of the last marked file. Mailboxing is the way in which Z88 applications can communicate with each other. Any application can put a piece of mail in the mail box and each piece of mail has a type associated with it. Applications expecting mail then look for specific mail types and if they are present can read the message. The internal applications PipeDream and the Diary can collect the filename the filer leaves in the mailbox, th us providing a very friendly way of finding documents and diaries. The Z88 currently only has two types of mail which are the name type, for filenames, and the date type, used by the Diary, Calendar and Alarm.
- The screen is accessed by the application by means of the screen driver. All the screen functions, apart from high resolution graphics, can be accessed by sending a series of codes to the screen device, which will normally be standard output. The screen driver sequences are constructed from an ASCII SOH character ($01) followed by a count character, indicating how many parameters follow, a mnemonic function code (eg. 'B' for bold text) and any other parameters. From the programmer's point of view the screen driver allows very complex screen displays (including simple windows) to be built up mainly by sending string constants to the display. High resolution graphics are accessed by using system calls. The basic display is up to 256 pixels (96 in an unexpanded machine) across and 64 pixels deep.
- The menu, topic, help system provides the application with a friendly user interface for practically no programming cost at all. All the menu commands and help pages are defined by a static data structure. The special key sequences which correspond to the commands on the menus, and operating the menus by hand (using the highlight bar) both have the effect of generating the same unique key code which is returned to the application. The programmer decides which commands should return which codes, inserts this in the data structure, and the operating system will then automatically generate all the menus and help pages, process the diamond and square sequences and any special keys (eg. TAB) and simply pass a key code back to the application. Also note that menu commands can be hidden, ie. not displayed, and safe which means they are displayed, but are not accessible through the menu, only through the key sequence. Hidden commands are used to build aliases, where two or more key combinations perform the same function.
- Each application must take care to look after any errors which occur. One particular error code which every application must deal with is the RC_QUIT code which is produced when the user uses <>KILL in the Index. In response to a KILL request the application must close all open channels and de-allocate all allocated memory before using a system call to close itself down.
Guidelines for application writing
- when making requests for resources, such as memory, make sure you get back what you asked for. You must not assume that resources will always be available, but always check for error returns, and be able to cope if they occur.
- be careful to clean up after yourself. Whatever has been opened or allocated should be closed or de-allocated. If this is not done before an application closes down resources, eg. handles and memory, may be lost permanently to the system. Open files will be marked 'In Use' and will not be writeable or deleteable. These resources can only be regained after a soft reset, and since this should be a relatively rare event for the Z88 (unlike other systems because it is never off, it never needs to reboot), so this kind of loss of resources is very serious for the machine. Keep your system tidy.
- direct access to hardware can be useful in dealing with the screen and serial port. However, it is important that any access does not interfere with other applications. It may, for example, be necessary to prevent pre-emption so that the operating system does not have the wrong picture about the true state of the machine.
- note that not every Z88 system is the same. If 128K or more RAM exists in slot 1 (or slot 0 with V4 ROM) then the machine is said to be expanded and this effects bad applications, graphics and user defined characters. Not all Z88's in circulation have the same ROMs. In the UK most ROMs will be v2.2 or v3.0 (which are identical), but continental machines ROMs differ in many significant respects. However all versions of the ROM above 2.2 support all the features and calls documented in this book, so sticking with these means that there will be no compatibility problems. The machine version, the amount of memory and the expanded or not status can all be read by examining the file status of a dummy channel with a handle of FFFFh (65535d).
- make sure you keep track of your own stack. The system stack is 2K in size, and the application inherits this stack. However, some of it may already be used by the operating system. Furthermore the static memory allowed for the application is taken from the same bit of memory, and the mailboxing system which allows for communication between applications, leaves its messages in this memory. What is left after all these other uses is the stack for the application. In practice it is usually around 1.5K. There is no checking for over running stacks so the application is responsible for keeping the stack within bounds.
- avoid splitting strings and other resources across bank boundaries. This applies especially for bad applications that has large continuous RAM memory. Even though RAM is all yours, the system cannot cope with information that crosses bank boundaries, or crossing a segment (eg. start address of a string begins at $7F00 and end at $8050). Large BBC BASIC programs sometimes behave strangely because of this.
- try to avoid cutting the user off. The user should be able to switch out of an application at most times. When heavy processing is going on it is a good idea for the user to be able to interrupt this, and at the very least be informed that it is taking place eg. by a flashing on screen message such as PipeDream uses when doing file transfer or calculating a spreadsheet.
- if interrupts are to be disabled the calls provided must be used. This is due to a bug in the Z80 processor which means that the old interrupt status can sometimes be lost. The machine must not be allowed to enter coma while interrupts are disabled since it relies on the keyboard interrupt to wake it up again. Generally while interrupts are disabled you should ensure that the other applications never get to see this and that you use system calls with great care. If interrupts are disabled remember that non-maskable interrupts still occur. These are caused by the machine timing out and power failure.
- software timing loops are not recommended because they are both unreliable, generally and due to interrupts, and because they mean the machine is in the awake state but doing nothing, and thus wasting power. The system provides a delay routine which sends the machine into snooze and then returns to the program after the delay period.