Z88 Developers' Notes

(C) Copyright Cambridge Computer Ltd. 1988

Authors:     John Harrison
             Matthew Elton

Consultants: Matthew Soar
             Trinity Concepts Ltd
             Jim Westwood
             Graham French

Disclaimers


(c) Cambridge Computer Ltd 1988

Proof Edition, September 1988

All rights reserved

No part of this book may be reproduced by any means without the prior
consent of the copyright holder. The only exceptions are as provided by
the Copyright (photocopying) Act for the purpose of review or in order
for the software herein to be entered into a computer for the sole use
of the owner of this book.

Disclaimer

Cambridge Computer Ltd will not in any event be liable for any loss,
including consequential loss, caused by any error, defect or inaccuracy
in this information, including but not limited to loss of profit or loss
of contracts.

Release Information

This is the first release of these "Developers' Notes" and and as such
it has not been extensively proof read nor has all the information been
fully tested. If you find any inaccuracies or inconsistencies, or if you
have any comments on the style and presentation of the document then
please pass these on in writing to:

Third Party Office
Cambridge Computer Ltd.
1 Crompton Way
North Newmoor Industrial Estate
Irvine
Ayrshire
SCOTLAND
KA11 4HU

Telephone  0294 222 100
FAX        0294 222 109

Email      cambridge@cix.compulink.co.uk


Other Useful Sources of Information

Information on the Z80 version of BBC BASIC may be obtained in the form
of a manual called 'The BBC BASIC(Z80) Reference Manual for the Z88'
ISBN 1 871895 00 6
from:

M-TEC Computer Services
4 Church Hill
Reepham
Norfolk
ENGLAND
NR10 4JL

For a general description of the Z88, refer to the current edition of
the Z88 User Guide. This is supplied with the Z88 or can be obtained via
Cambridge Computer Third Party Office.

Trinity Concepts also supply a Z88 user guide. For more information
contact:

Trinity Concepts Limited
264 Newmarket Road
Cambridge
ENGLAND

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

  W A R N I N G

 If you write an application which contains bugs or tries to

 circumnavigate the operating system, then it is likely that all the

 other applications in the Z88 will be affected. The effect may not be

 immediate, some indiscretions take weeks or even months to become

 apparent, but will usually be in the form of a system crash. You must

 always remember that the resources of the Z88 are not devoted

 exclusively to your application and therefore only use legal interfaces.

 Please note that all the internal applications follow all the rules and

 use no 'back door' techniques.

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Section 0 - Introduction


This document sets out to describe how potential Z88 software
developers, or indeed anyone else, may get the best out of the machine.
It explains how to use the system calls correctly, and illustrates the
methods for setting up a machine code program as an application. This
means that a developer will be able to set up a program so that it
behaves just like the inbuilt ones (Pipedream, BASIC, calculator etc.).
Thus, it can be called from the Index, contain help text and menus, and
make use of diamond combinations to select commands. Perhaps most
importantly, it can be suspended and re-entered in the same state.

The explanations assume a reasonable understanding of Z80 machine code
and simple use of BBC BASIC, but treat most Z88-specific topics from the
beginning and in some detail. Note that the examples have been tried and
tested on an expanded Z88, ie. one with 128K of RAM in slot 1. They may
not work on a machine with less memory in slot 1.

Section 1: Organization of memory, explains how up to 4Mbytes of
physical memory is used from a Z80, which can only directly access a
64Kbyte logical address space. Understanding of this material, or at
least the terminology introduced, is fundamental to making full use of
the machine and is referred to repeatedly in subsequent sections.

Section 2: Simple use of system calls, sets out to explain many of
the more useful calls, and how they may be used in assembler programs
within the BBC BASIC application using the inline assembler provided.
This should enable the reader to write useful machine code programs.

Section 3: How to write an application, deals with setting up a
machine code program as an application. It explains the three types of
application ('good', 'bad', and 'ugly') and their advantages and
disadvantages. It also covers the detailed structure of an application
and how to use the menu and help systems to best advantage. As a
concrete example it gives a simple BBC BASIC / Z80 assembler program
which will create an application EPROM with a user code fragment as its
basis.

Section 4: System calls reference section, is a complete guide to
all the main system calls. Each call is explained and a fairly precise
interface specification is given. Those calls which are used by the
system, but would not be of use to application developers, are usually
not detailed in full.

Finally, a glossary is provided along with a table showing the ASCII
character set and Z80 instruction set.

Section 1 - Organization of memory


The Z88 is based on a Z80 central processor running at 3.2876 MHz. The
Z80 has a 16-bit address bus and can thus only directly access 64Kbytes
of memory. Reconciling oneself to this total amount of memory would
be very restrictive for a machine like the Z88 and in fact the Z88 can
use up to 4Mbytes of memory. The decoding is handled by the 'BLINK'
gate array, which sits between the CPU and memory. The CPU communicates
via i/o ports, which address various BLINK registers.
The 4Mbyte physical address space is split up into 256 'banks',
each of 16Kbytes. At any time, the Z80 can address 4 such banks. The 64K
logical address space is notionally divided into four 16K
'segments', which are by convention numbered as follows:

 Segment 0: Logical addresses &0000 - &3FFF
 Segment 1: Logical addresses &4000 - &7FFF
 Segment 2: Logical addresses &8000 - &BFFF
 Segment 3: Logical addresses &C000 - &FFFF

Facilities are provided for a program to 'bind' each segment to any
of the 256 banks. This means that a logical address falling within each
segment will be translated into a physical address within the bank bound
to that segment, the lower 14 bits of the logical address providing an
offset within the bank. The exact method by which a program may bind
banks is explained in the next section, along with other useful system
calls.
It is prudent, however, to be cautious with rebinding of banks. Firstly,
it is quite possible to page out the bank in which the code is running,
or in which the machine stack resides. The consequences of this would
almost certainly be a sytem crash.
Secondly, segment 0 has rather more complex organization than is
indicated above. The bottom 8K (&0000 - &1FFF) is normally bound to the
RAM used by many vital machine operating system (MOS) components. It is
impossible for the CPU to rebind this section to an arbitrary bank, nor
would one normally want to as it would lead to the MOS having the ground
cut from under its feet.
This lower 8K may be bound either to bank 0 (ROM - this is the
arrangement when the machine is hard-reset) or bank &20 (RAM -
containing the restart routines and other MOS components, which is the
normal state of affairs). This duality is intended only to ensure that
the machine boots up properly, and need not normally concern the user.
Thus, paging a bank into segment 0 actually involves paging an 8K half-
bank into the logical address space &2000 - &3FFF. In this case, only
the top 7 bits of the bank specification are significant in the bank
number and the bottom bit specifies which half of the bank is bound to
the segment (0 being the lower part). So if the bank specifier were:

a b c d e f g h

where each character represents a bit, then 'h' specifies which half of
the bank is bound, and the number of the bank bound would be, in binary:

a b c d e f g 0

and is thus always even, ie. only even-numbered banks may be bound to
segment 0.
In view of this complexity, then, it is probably advisable to avoid
rebinding segment 0 unless it is really felt to be necessary. The
'recommended' use of memory is as follows:

        Segment 0: RAM
        Segment 1: RAM
        Segment 2: RAM or code
        Segment 3: code.

Finally, a few words may be in order concerning the way in which the
memory is physically set up with respect to the internal chips and the
three card slots at the front of the machine. The answer is quite
simple:

        Banks   &00 - &3F  are internal (lower half ROM, upper half RAM)
        Banks   &40 - &7F  are wired to Slot 1
        Banks   &80 - &BF  are wired to Slot 2
        Banks   &C0 - &FF  are wired to Slot 3 (usually EPROM).

Note, however, that cards with an addressing range of less than 1M
(which at time of writing means all of them!) only decode the lower
address lines, so for example a 32K RAM card in slot 1 will appear
identically in banks &40 and &41, &42 and &43 .... &7E
and &7F, ie.
whatever the state of the top 5 address lines. A 128K EPROM card (in
slot 3) would appear identically in banks &C0 to &C7, &C8 to &CF, &D0 to
&D8 ... &F8 to &FF. This makes it easy to address the top and bottom of
its address space without prior knowlege of its size ie. by addressing
the top and bottom bank of the slot. The system expects this behaviour
so the convention should be followed by any hardware developers
intending to create their own cards. Note that it is possible to mix ROM
and RAM in a single card, but care must taken in decoding the address
lines. A simple scheme, which should work, is to have the lower half of
a card containing RAM (the RAM can occur several times in first half
megabyte) and the upper half ROM (again the ROM can repeat). If a card
is arranged like this the system will be able to use the RAM and run
software from the ROM. Other schemes exist which can make on-card RAM
private. If you intend to develop hybrid ROM and RAM cards, you are
strongly advised to consult Cambridge Computer.

Section 2 - Simple use of system calls


It is quite easy to set up machine code programs of reasonable size
within the BBC BASIC application, using the inline Z80 assembler
provided. Though not without its limitations, this environment provides
a straightforward way of familiarizing oneself with the system calls,
and will be used throughout this section and the whole book.


2.1 The BBC BASIC assembler

The assembler is not covered explicitly in the Z88 User Guide, so a short explanation is provided here. The definitive source of information about the assembler, and BBC BASIC in general, is the BBC BASIC (Z80) manual by Richard Russell, which is available from M-TEC (see address at the front of this book). Before moving on to the assembler proper, we cover a few unusual features of BBC BASIC. Some useful BASIC commands are: LOAD "filename" loads a BASIC program SAVE "filename" saves a BASIC program NEW clears out BASIC workspace for a new program OLD attempts to recover a program lost through NEW RUN execute a BASIC program (this will assemble inline assembly code but will not execute it). LIST list out a BASIC program to the screen RENUMBER [a,[b]] renumbers the lines of a BASIC program (starting from line a (in increments of b)) DELETE a,b delete the lines between a and b CALL x start a machine code routine at address x In its Z88 incarnation, BASIC occupies a memory map with the following form: &0000-&1FFF Operating system use &2000-&3FFF BASIC program/workspace &4000-&BFFF (additional 32K of program/workspace if running a 40K BASIC) &C000-&FFFF BBC BASIC interpreter BASIC's program/workspace is arranged in the following manner: |------------------------| &FFFF | BASIC interpreter | |------------------------| . . . . . . HIMEM |------------------------| &C000 or &4000 | Stack | |------------------------| Current limit of the stack . . (Stack expands downwards) . Unused memory . . . |------------------------| Current limit of the heap | Heap | (Heap expands upwards) LOMEM |------------------------| . . . . . . TOP |------------------------| | Program | | | PAGE |------------------------| &2300 | Workspace for | | interpreter | |------------------------| &2000 HIMEM, LOMEM and PAGE are pseudo-variables whose values can be read and also set. TOP is a read-only pseudo-variable. If you intend to change the values of these pseudo-variables then you must bear in mind that the 'Unused Memory' area in BASIC's memory map will not necessarily be constant. If task switching occurs, this memory may be used for other purposes and when you return to BASIC its contents may have changed. Therefore any memory which you want to stay constant through task switching must either be in or below the heap. This memory is readily allocated within the heap using the DIM statement, explained below. LOMEM, the start of the heap, defaults to the value of TOP, the end of the program, although it is possible to increase LOMEM, thus providing some safe memory between the end of the program and the start of the variables. The value of PAGE is always set on a page (256 byte) boundary, ie. the less significant byte is ignored. By changing the value of PAGE, it is possible to have more than one program resident at once, within the same instantiation of BASIC. The difficulty with this technique on the Z88 is the inconstancy of the unused memory. It is posible to set LOMEM to some high value and put all the programs between the start of BASIC (&2300) and that value, but extreme care is required, because whenever a program is RUN, LOMEM is reset to TOP. TOP, however, is not reset when PAGE is changed unless part of the program is altered or OLD is used. If you attempt this technique, keep in mind that whenever task switching could occur, all the memory used for programs should be allocated to BASIC and therefore safe. BBC BASIC provides the '&' symbol to prefix a hexadecimal number, as distinct from the more usual '$', which is used in BBC BASIC for string indirection. For printing an expression in hex form, prefix it with '~', eg: PRINT &58 gives: 88 while: PRINT ~100 gives: 64 It is perhaps also worth mentioning the indirection operators provided by BBC BASIC; these are used to find the contents of a cell or cells given an address. The ? ('query') operator represents byte indirection, so: ?pointer represents the byte addressed by 'pointer'. This can be used either on the right or left of an expression; the statement: ?address=contents sets the contents of the byte-sized cell addressed by 'address' to the value 'contents'. In most BASICs this would be written POKE address,contents whereas the statement: contents=?address is equivalent to contents=PEEK(address) The ! ('pling') operator represents word (32-bit and not 16-bit) indirection, so the expression: !pointer represents the value of the 32-bit word, the address of whose first byte is 'pointer'. Thus, !a (on the righthand side of an expression) is equivalent to: a?0 + 256*a?1 + 256*256*a?2 + 256*256*256*a?3 Notice that the least significant byte comes first; this is generally true for the Z88 system, an extension to four bytes of the standard Z80 order. The query and pling operators may also be used in a dyadic context, which is often more natural (cf. array indexing, which actually works similarly) base?offset is equivalent to ?(base+offset) base!offset is equivalent to !(base+offset) In this case 'base' must be a simple variable, but 'offset' may be any expression. Thus "2!x" will not work. Remember also that the operators in a dyadic context are symmetric, so a!1 addresses a word starting at a+1 and NOT a+4, as some people might expect. Finally, note that ! and ? have the highest level of priority in an expression, equal to unary plus, minus and 'NOT', above binary arithmetical, relational and logical operators, so for example a?1^2 will be interpreted as (a?1)^2. The operator $ ('dollar') implements string indirection: $a refers to a string which begins at address 'a' and is terminated by carriage return. Thus: $a="hello" sets up successive bytes, starting at address 'a', with these five letters and a 13. An unfortunate anachronism in ASCII is the fact that newline has not been allocated a single character, but requires two characters, Carriage Return (CR, ASCII 13) and Line Feed (LF, ASCII 10) - as if driving a mechanical printer. In the internal representation of files only CR is used, to avoid wasting space. Note that the maximum length of a string in BBC BASIC is 255 characters. This is not because the string storage uses a length byte - it does not - but simply for convenience of internal manipulation on an 8-bit machine. Space for a machine code routine, strings, and memory to be used with indirection operators may conveniently be reserved using a special form of the DIM statement, without any brackets. The BASIC statement: DIM code 255 reserves a block of 256 contiguous bytes of memory and sets the variable 'code' to point to the start of this block. The elements of the block therefore start at address 'code' and finish at code+255. This is quite distinct from the statement: DIM code(255) which reserves space for 256 floating point variables - which will be considerably more than 256 bytes. We can now move onto the assembler itself. Remember that, in common with the rest of BBC BASIC, the assembler is case-sensitive, and opcodes, pseudo-ops and registers should be in upper case (EX DE,HL not Ex de,hL). Some oddities worth mentioning are that the brackets around the port number for the 'IN' and 'OUT' instructions are optional (ie. OUT 5,A and OUT (5),A are equivalent). The instruction IN F,(C) is not accepted, but the source code IN (HL),(C) produces the equivalent object code. It is conventional to use lower case for labels and manifests, as this avoids lexical pitfalls and improves readability. It is also important to put spaces between the instruction mnemonic and its operands. Assembler code may simply be placed within the BASIC program, surrounded by square brackets. The assembler uses the BASIC variable P% as a 'program counter', which advances as the assembler moves through the source code (note that P%, as with any BASIC variable with a '%' suffix, is a 4-byte signed (2's-complement) integer rather than floating point variable). The user must, therefore, set P% to the desired start point for the machine code output before invoking the assembler. The program might look like this: 10 REM Trivial example of how to use Z80 assembler 20 DIM code 100 30 P%=code 40 [ 50 LD BC,50 60 RET 70 ] When this BASIC program is RUN it assembles the two-line assembler program into the first four bytes of the reserved memory, but does not execute the code itself. As the BASIC is RUN, an assembly listing is provided. This may be supressed by using option flags, set by using the assembler directive 'OPT n' at the start of the code; n=0 will supress a listing, ie. change line 40 to: 40 [ OPT 0 A number given by any combination of the following bit settings may follow OPT: Bit 0 (decimal 1) 1= give a listing Bit 1 (decimal 2) 1= report errors Bit 2 (decimal 4) 1= place assembled code starting at O% rather than P%. The last option means that the code is actually placed starting at O%, but labels have values as if the code started at P% (see below for details of labels declarations). This allows one to assemble code into one space which is designed to fit somewhere else. For instance, in the following code fragment: . 50 P%=&C000 60 O%=code 70 [ OPT 6 80 .codestart 90 DEC A 100 CP (HL) 110 RET Z 120 JP codestart . . 200 ] . . then although the code will actually go into the 'code' array, the label 'codestart' has the value &C000, and so the word in the 'JP' statement will appear as such. This facility will be useful later in assembling code ultimately destined for an application EPROM (see section 3) - we cannot simply assemble the code into EPROM since bytes need to be written to EPROM using a special method. Comments may be inserted in the assembler source by preceding them with either semicolon or backslash, viz: 42 ; This is a comment 55 \ And so is this. Note, however, that a comment ends at the end of a BASIC statement. This will normally be the end of the line, but a colon will have the same effect. Hence any characters after a colon will be regarded as a new assembler statement: 54 ; The following will be regarded as an assembler statement: RST 0 This practice is, of course, very confusing and is not recommended. Labels may be used in the assembler code; simply precede each label with a full stop. (The full stop is NOT part of the label.) A label may or may not be followed by an assembler statement on the same line, but if it is, then at least one space must be left between them, eg: 51 LD C,15 52.loop1 LD B,30 53.loop2 54 CALL misc 55 DJNZ loop2 56 CALL wrnl 57 DEC C 58 JR NZ,loop1 When the assembler encounters a label, it sets a BASIC variable of that name to the current value of P%. Assembler labels and BASIC variables are thus interchangeable; so the assembler code could use: JP code to jump back to the very start of the program. Also, this allows BASIC variables to be used to define manifest constants for use in the assembler listing: 5 maxsize=62 . . 40 [ . . 56 CP maxsize . 150 ] The assembler simply passes once through the source from start to finish, and so will not know the values of labels before they are defined. It would be inconvenient to have to define every label before it is used, so the way round the problem is to make two passes through the code. The first will, in general, encounter errors, so set OPT 0 to supress their reporting. This pass will set up all the labels correctly, so that a second pass (with OPT 2, or OPT 3 if a listing is desired, to make sure there are no 'genuine' errors) will complete the assembly. For example: 10 DIM code 100 20 FOR pass%=0 TO 2 STEP 2 30 P%=code 40 [ OPT pass% 50 LD BC,13 60 JR label 70 LD BC,26 80.label 90 RET 100 ] 110 NEXT pass% Two rough edges in the assembler regarding labels are: 1) If a label is redeclared, no warning is given 2) If a label which is undeclared is the operand of a JR (jump relative) instruction, then the error issued is 'out of range' rather than 'label not found'. Inline data definition is possible in the assembler source using the directives DEFB (define byte), DEFW (define word) and DEFM (define message). DEFB &12 ; sets up a byte of storage and initialises it to &12 ; (ie. hex 12). ; Remember that &, not $, indicates hexadecimal in ; BBC BASIC. DEFW 123 ; sets up a 16-bit word of storage and initialises it to ; decimal 123, less significant byte first (usual Z80 order). DEFM "Hello there" ; sets up space initialised with this string ; one character per byte The DEFM directive does not introduce any 'magic characters', so if you want a string to be null or carriage return terminated, you must explicitly append the terminator byte(s), eg: .pointer DEFM "This string is null-terminated" DEFB 0 Contrast the BASIC string indirection which when used thus: $pointer="This string is CR-terminated" will automatically carriage-return (13) terminate the string. Unfortunately, there is no define-storage directive. A second DIM statement may be used, or for small spaces, one could use DEFM with a dummy string. This may conveniently be done as follows: DEFM STRING$(100,"A"). This is a useful consequence of the close intertwining of the assembler with BASIC: the arguments to assembler ops and pseudo-ops may include many forms of BASIC expression (though brackets may lead to ambiguity as they often indicate an extra level of indirection in Z80 assembler). Two other handy incidences of this are: 1) The use of ASC"x" as a character constant, viz: LD A,ASC"Q". 2) The use of user-defined functions to provide macro and conditional assembly facilities: suppose we are using the (non-local) variable 'pass' to represent the current assembler option. Then: OPT pass will have no effect. Taking this a stage further, if the user-defined function 'macro' always evaluates to 'pass', then: OPT FNmacro(arguments...) will have no effect, except that it will execute the body of the function, if any. For instance, suppose we define: DEF FNsave_regs(savearea) [ OPT pass LD (savearea),HL LD (savearea+2),DE LD (savearea+4),BC ] =pass then including: OPT FNsave_regs(space) in the main code would reproduce the above three lines of code with 'savearea' set to 'space'. This feature is used in, Section 3.2 and Section 4.3, as a way of calling system routines with symbolic names. Note that assembler mode is local to the function so: 1) OPT must reappear in the function. 2) The closing square bracket in the function body does not exit assembler mode in the main program Finally, to call the machine code program once it has been assembled, do: CALL code or a = USR(code) which returns the contents of the HL main and alternate set at termination (H most significant; L' least). There is a mechanism for initialising the contents of registers from the CALL or USR statements: the registers A, F, B, C, D, E, H, L on entry are set to the values of the BASIC variables A%, F%, B%, C%, D%, E%, H% and L% respectively. The CALL statement also allows the user to set up a parameter block on entry by appending the required parameters to the CALL statement, ie: CALL code, parm1, parm2, parm3 ........ parmx On entry IX will point to a parameter block with the following format: 1 byte Number of parameters. 1 byte Parameter 1type n bytes Parameter 1 1 byte Parameter 2 type n bytes Parameter 2 . . . . 1 byte Last parameter type n bytes Last parameter. The parameter type byte may be any of: 0 byte, eg. ?x 4 32-bit word, eg. !x or x% 5 Floating point (40-bit), eg. x 128 String, eg. "Hello" 129 Two byte pointer to string (this allows the string to be stored elsewhere and thus vary in size dynamically). eg. x$

2.2 System calls - general points

Most of the main system calls are entered using a RST &20 instruction (the floating point routines being the exception), followed by one, or possibly two, bytes which define the required function. A full list is given in section 4. For example, RST &20 DEFB &27 sends the character whose code is in A to the screen. This format of operating system call has the merit of economy of space; one byte for the RST instruction, one byte for the appended function code. The system call can easily find the appended byte using the return address pushed by the RST instruction, and adjust the return address to point after it. A scheme in which the function code was loaded into a register before executing RST &20 would waste an extra byte every time the function is called. The mnemonic names for the calls are usually prefixed by either 'gn', 'os' or 'dc'. This represents a rather arbitrary division into: 'gn' (general) - General utilities like arithmetic routines and data type conversion. 'os' (operating system) - Operating system calls like process management and error handling. 'dc' (Director/CLI) - Very low level calls, for which an alternative 'os' or 'gn' call usually provides a more convenient interface. Director is a synonym for Index. CLI stands for Command Line Interpreter. When one wishes to find the system call to perform a particular action, turn to the 'system calls finder', section 4.1 This lists various actions the user may want to perform, and refers to the appropriate system call(s) whose specification may be found in section 4.2 We can use the simple method of function call which was explained above in example programs. However there are various important things to bear in mind. The problem of paging out the stack was mentioned earlier. The main restart code itself needs to page memory in and out; for instance one of the first things it does is bind bank 0 (which contains the vector table for the calls) to segment 3; but it expects the stack to perform properly when it has done so. So in general, a program should not place its stack where it is liable to be paged out. To be extra-safe it should be in the bottom 8K of the logical address space, which, as mentioned earlier, is never paged out. The BBC BASIC application stack has to be fairly large as it is used for parameter-passing during BASIC execution, and so cannot be placed in the bottom 8K by default. It is in a very vulnerable position, typically at the top of segment 2, and so it is advisable to select a safer stack if any system calls are to be used. This may reliably be done by loading the stack pointer from the location &1FFE. For example, if the main user code starts at 'main' then the program as a whole might look like: EXX ; Use alternate registers to preserve HL LD HL,0 ADD HL,SP ; Get stack pointer in HL LD SP,(&1FFE) ; Load new stack pointer PUSH HL ; Store old stack pointer on new stack EXX ; Back to main registers CALL main ; Call main program EXX ; Select alternate registers again POP HL ; Retrieve old stack pointer LD SP,HL ; And restore it to SP EXX ; Back to main registers RET ; Return to BASIC .main . . . RET The use of the alternate register set avoids corrupting the main set, thus allowing parameter passing with HL (by using the BASIC variables H% and L%), but this may be dispensed with if the contents of HL are not important. The old stack pointer is pushed onto the new stack so it can be recalled at the end. The old stack pointer could also be saved in a static memory location and this technique is used in some of the other examples. We now introduce the notation that will be adopted in presenting the interface specifications of the various calls. Uppercase register letters (A,HL etc.) represent the main register set; lower case letters (a,hl etc.) represent the alternate set (as distinct from the more usual notation A',H'L' etc). This is probably best illustrated by an example; if the effect of a call on registers is indicated thus: A..BCDE...../...IY same ..F.........HL/IX... different afbcdehl different then A,BC,DE and IY are preserved over the call; the others, including the alternate set may change (by which it is meant that their constancy should not be relied upon, not that they definitely will change). The stack pointer, as one might expect, will never change; this is not indicated explicitly. The above provides a succinct and clear notation which will be used throughout the subsequent sections, particularly Section 4. One other notational point worth mentioning is that Fz represents the zero flag, Fc the carry flag, etc.

2.3 Error handling and related issues

Before we can explore any useful system calls it is necessary to look at the way in which the Z88 deals with errors. Most system calls will return with the carry set (Fc=1) and a return code in A when an error occurs. Most of the time this mechanism alone will be sufficient to handle errors, but some errors are better coped with in an error handler. The system has its own error handler, but a call exists to insert the user's own. The current error handler, be it the system's or the user's, is called in the following circumstances: 1) If an error occurs in a system call, then after the call has finished (insofar as it is able), the error handler is called with Fc=1 , an error code in A, and possibly Fz=1 to indicate a fatal (ie. non- recoverable as far as the application is concerned) error. The error handler may take whatever action it wishes, then return control to the error handler supervisor, with Fc=0, by a RET statement. This will then pass control back to the program just after the call. 2) Certain system calls may be 'pre-empted' by the operating system. That is, at the start of one of these calls, a check is made for: a) An escape condition, ie. the ESCAPE key having been pressed while escape detection is enabled (see later). b) The INDEX or SQUARE keys having been pressed, ie. a request for process switching. Some calls may also time out. After the operating system has finished whatever it is doing (for instance, running other applications), the BASIC will eventually be re-entered, and the error handler called with a return code in A which is one of: 1) RC.SUSP (&69) - either: a) The process (ie. BASIC as a whole) has been suspended and re-entered, or b) the machine has been revived (ie. switched off and on again). 2) RC.DRAW (&66) - as for RC.SUSP except that the application's screen has been corrupted and the application should attempt to redraw it. This should not normally happen in BASIC as the operating system always endeavours to save the screen on suspension and restore it on reactivation. This is a special feature of BASIC (see section 3), and if the user writes an application, then it SHOULD try to process this error, as it could probably do it without wasting as much as 2K (which is how much memory it takes to save a screen). 3) RC.QUIT (&67) - the process has been suspended and a KILL request made for it from the index. The reader may have noticed from the screen that a KILL request causes the relevant process to be re-entered momentarily - processes are always expected to kill themselves off in response to this error; they are not killed BY the index. 4) RC.ESC (&01) - an escape condition has been detected. This means ESC has been pressed while escape detection is enabled. 5) RC.TIME (&02) - the call has timed out. Among such pre-emptable calls are file or keyboard read and time delay routines. Thus if the user routine uses no pre-emptable calls (for instance, no calls at all), then none of the conditions mentioned above will be detected and the state of the entire machine is at the mercy of the user program. Hence it is wise to include such calls in any non- trivial program, and set up the error handler to deal with the four conditions mentioned above. This section explains how to do this, from the perspective of a BASIC application. An error handling routine may be set up by using the system call 'os_erh' whose specification is: os_erh - set up error handler RST &20 DEFB &75 In: A - Call level (this is designed for use in system calls themselves and should be zero for normal use). B - Should again be zero (this is irrelevant in the current version of the Z88 ROM, but should be set to zero to allow the user program to work properly with future versions). HL - Address of new error handler, or else HL=0 means restore default system error handler, which essentially ignores errors. Out: Fc=0 always. A - Old call level. HL - Address of old error handler. ........CDE...../IXIY same AFB........HL/....... different afbcdehl different We can now consider the code the error handler should include. Fatal errors may be ignored (RET Z at the start), because there is no reasonable action to take anyway, except termination of this instantiation of the application. An RC.SUSP (&69) error code may be completely ignored by the error handler; this just means that BASIC has been suspended and re-entered or else the machine has been revived, either of which is unimportant. However it will lead to a system call being terminated, and if the machine is switched off the serial interface buffering may break down. so the main routine may have to be aware of it. An escape error, RC.ESC (&01), should result in a return to BASIC. Also, however the routine should acknowlege the escape, or else it will be passed back to BASIC and give an 'Escape' message, which would be a little clumsy. This may be done by the use of a particular case of the general escape-handling call 'os_esc' ("Examine Special Condition"). This call has the following spec: os_esc - Examine special condition RST &20 DEFB &6F The action depends on the value of the A register on entry: A=0 Test for escape. Returns Fc=1 if escape has been pressed, else Fc=0. Note that this resets the machine timeout. A=1 Acknowlege escape (ie. reset Escape flag) and also flush input buffer. Fz=1 if there was no escape to acknowlege, else Fz=0. Again, resets machine timeout. A=2 Set escape, ie. simulate an escape condition. A=3 Acknowledge escape without flushing input buffer. A=4 Test if escape detection is enabled or disabled. Fc=0 always, and A=5 if it is enabled, A=6 if it is disabled. A=5 Enable escape detection A=6 Disable escape detection. Unless otherwise stated, Fc=0 may be assumed. ......BCDEHL/IXIY same AF............../..... different afbcdehl different Finally, the error handler should respond to a KILL request. It could kill its application using the appropriate routine ('os_bye'), but it is essential to explicitly close files, deallocate memory and so forth first, otherwise files could be left open, and thus inaccessible to other processes, and memory could be 'lost' to the operating system by never being released. Rather than bother with doing all that in our own routine, we can simply pass the error code back to the BASIC error handler (its address having been returned by 'os_erh') which is designed to respond to a kill request correctly. Other errors we ignore (the reader may make it respond to other errors if desired, but it is probably best to handle most normal errors in the body of the main program). One possibility for dealing with normal errors is to use the call 'gn_esp' which provides an error string to go with a code: gn_esp - Return a pointer to a system error message RST &20 DEFB &09 DEFB &4C In: A - error message required Out: Fc=0 Fz=1 Error is fatal Fz=0 Useful error message BHL - extended pointer to error string A....CDE..../IXIY same ...FB.....HL/...... different afbcdehl different Notes: Error codes which produce messages are as follows: Return code Value Message ------------------------------------------------------------------------ RC.ESC &01 Escape RC.TIM &02 Timeout RC.ROOM &07 No room RC.EOF &09 End of file RC.FLF &0A Filter full RC.OVF &0B Overflow RC.SNTX &0C Bad syntax RC.WRAP &0D Wrap RC.PUSH &0E Cannot satisfy request RC.PRE &11 No room RC.ONF &12 File not found RC.RP &13 Read protected RC.WP &14 Write protected RC.USE &15 In use RC.FAIL &16 Cannot satisfy request RC.IVF &17 Bad filename RC.FTM &18 File type mismatch RC.EXIS &19 Already exists RC.DVZ &46 Divide by 0 RC.TBG &47 Number too big RC.NVR &48 -ve root RC.LRG &49 Log range RC.ACL &4A Accuracy lost RC.EXR &4B Exponent range RC.BDN &4C Bad number RC.DRAW &66 Redraw RC.QUIT &67 Unknown error RC.SUSP &69 Suspended To test the error handler, we need to include a pre-emptable call in the main routine. A suitable candidate is 'os_in'. The details of this call will not be dealt with here (see section 2.7 if interested); it essentially reads a character from the standard input, normally the keyboard (some key combinations, eg. shift-enter, return more than one byte so will provide material for two calls) and returns it in A. The point which concerns us here is that it can be pre-empted. Note that the stack pointer is stored at a fixed location rather than pushed in order to make it simple to take a longjump and unwind the stack. The program as a whole is as follows: 10 DIM code 150 20 REM system call manifests 30 gn_esp=&4C09 :REM pointer to system error message 40 gn_soe=&3C09 :REM write string at extended address 50 gn_nln=&2E09 :REM output carriage return and linefeed 60 os_erh=&75 :REM set error handler 70 os_esc=&6F :REM examine special conditions 80 os_in=&2A :REM read character from standard input 90 os_out=&27 :REM write character to standard output 100 REM return codes 110 rc_quit=&67 :REM kill request 120 rc_esc=&01 :REM escape 130 : 140 : 150 FOR pass=0 TO 2 STEP 2 160 P%=code 170 [ 180 OPT pass 190 LD HL,0 200 ADD HL,SP 210 LD (bstk),HL \save BASIC stack 220 LD SP,(&1FFE) \select safe stack 230 XOR A \new call level 240 LD B,A \B must be zero 250 LD HL,errhan \address of our error handler 260 RST &20:DEFB os_erh \install new error handler 270 LD (obou),A \save old error handler call level 280 LD (oerr),HL \save old error handler addres 290 CALL main \call main routine 300 .exit 310 LD HL,(oerr) \address of old error handler 320 LD A,(obou) \old call level 330 LD B,0 \zero B 340 RST &20:DEFB os_erh \reinstall BASIC error handler 350 LD SP,(bstk) \select BASIC stack 360 RET \return to BASIC 370 \ 380 .errhan \error handler code 390 RET Z \fatal error! 400 CP rc_esc 410 JR NZ,err1 \check for ESCAPE 420 RST &20:DEFB os_esc \acknowledge ESCAPE 430 LD A,rc_esc 440 CALL err2 \print error message 450 JR exit \exit to BASIC 460 .err1 470 CP rc_quit 480 JR NZ,err2 \KILL request 490 LD HL,(oerr) \reinstall old error handler 500 LD A,(obou) 510 RST &20:DEFB os_erh 520 LD SP,(bstk) \select BASIC stack 530 LD HL,(oerr) 540 LD A,rc_quit-1 \load A with rc_quit and 550 INC A \reset the zero flag 560 SCF \set the carry flag 570 JP (HL) \jump to BASIC's error handler 580 \ 590 .err2 \write error message if possible 600 RST &20:DEFW gn_esp \find system error message 610 RST &20:DEFW gn_soe \write it to standard output 620 RST &20:DEFW gn_nln \write new line 630 CP A \reset carry flag 640 RET 650 \ 660 .bstk DEFW 0 \storage for BASIC stack pointer 670 .obou DEFB 0 \old boundary 680 .oerr DEFW 0 \old error handler address 690 \ 700 \main routine starts here 710 .main 720 LD B,10 \loop ten times 730 .loop 740 LD A,B \loop value 750 ADD A,ASC"0"-1 \turn into an ASCII digit 760 RST &20:DEFB os_out \write digit 770 .in 780 RST &20:DEFB os_in \read a character 790 JR C,in \if carry set then read error code 800 DJNZ loop \repeat 810 RET 820 ] 830 NEXT pass 840 CALL code :REM run the machine code itself The reader may try running this program, and observe the effect of suspending and then either killing or re-entering, of pressing escape, and of switching the machine off and on again by pressing both shift keys. The above is a reasonable model for the sort of error handler that a user program should usually introduce. The benefit of an error handler is it allows you to deal with return codes like RC.SUSP, RC.DRAW, RC.ESC and RC.QUIT in one place, rather than having to check for all these codes whenever they could occur. In discussion of errors, the standard manifests are often used. Their values are usually given as well, for convenience, but a complete list is provided in Section 4.3.

2.4 Integer arithmetic

The first useful system calls which will be demonstrated are the integer arithmetic routines. The Z80 has hardware support for addition and subtraction of 8-bit and 16-bit quantities. However it does not support either multiplication or division, which are quite often required in application programs. Hence system calls are provided to multiply and divide 16-bit and 24-bit quantities. This thrilling example program will be a demonstration of one of these. 10 DIM code 200 20 gn_nln=&2E09 :REM newline 30 gn_d16=&7409 :REM 16 bit divide 40 gn_pdn=&1209 :REM binary to ASCII conversion 50 gn_sop=&3A09 :REM write to standard output 60 FOR pass=0 TO 2 STEP 2 70 P%=code 80 [ OPT pass 90 LD HL,0 100 ADD HL,SP \load HL with SP 110 LD SP,(&1FFE) \fetch safe stack 120 PUSH HL \save BASIC stack 130 CALL main 140 POP HL \restore BASIC stack 150 LD SP,HL \select BASIC stack 160 RET 170 \ 180 .main 190 LD HL,(dividend) \parameters in HL and DE 200 LD DE,(divisor) 210 RST &20:DEFW gn_d16 \divide routine 220 LD B,H 230 LD C,L \LD BC,HL 240 LD DE,buff \buffer for converted number 250 RST &20:DEFW gn_pdn \binary to ASCII conversion 260 XOR A \zero A 270 LD (DE),A \add a null to the end of the string 280 LD HL,buff \pointer to start of string 290 RST &20:DEFW gn_sop \write null-terminated string 300 RST &20:DEFW gn_nln \write a newline 310 RET 320 .dividend DEFW 0 330 .divisor DEFW 0 340 .result DEFW 0 350 .buff DEFM "abcabcabcabc" 360 ] 370 NEXT pass 380 !dividend=1000 :REM change these numbers 390 !divisor=29 :REM if you wish 400 CALL code This program sets up the code to use the sytem call 'gn_d16', ie. 16-bit division (unsigned). Try changing the numbers used. Note also that this call requires two extension bytes. The integer arithmetic routines are: gn_m16 unsigned 16 bit multiplication gn_d16 unsigned 16 bit division gn_m24 unsigned 24 bit multiplication gn_d24 unsigned 24 bit division gn_gdn convert ASCII string to binary number gn_pdn convert binary number to an ASCII string With any of the system calls, the alternate register set and the flag register may change. Errors are indicated in the usual way; the above divide routines, for instance, will set the carry flag on exit if a divide by zero is attempted, although our program does not check for the error. The final point to note in the above routines is the use of trios of registers to store 24-bit numbers. BHL and CDE are, by convention, the combinations generally used in the Z88. This means that, taking BHL as an example, the high order byte will be stored in B, the middle byte in H and the low order byte in L. Many system calls expect a pointer to be passed to them in a register trio. This is interpreted, in most cases (but NOT ALL) as follows (again, using BHL as an example). If B=0 then HL is regarded as a logical address, as on an ordinary Z80 system. If B is not zero, then B is taken to be a bank number, and the lower 14 bits of HL an offset within the bank (the top two bits of HL are ignored). This allows the same interface to refer to logical or physical addresses according to circumstances.

2.5 File input/output

The Z88 supports a fairly uniform device-independent i/o system, so although calls do exist which will explicitly send data to, say, the screen or the serial port (and these calls will be covered later), it is usually simplest to regard such devices as files and use a standard file i/o interface. Hence this section will cover file access from assembler, and the approach remains the same if device names are substituted for the filename. Major pseudo-files (the ".0" may be omitted if there are no other possible terminating numbers) are: :SCR.0 - The screen (write only) :PRT.0 - The printer (write only) :COM.0 - The serial interface :NUL.0 - Null device (output discarded, always end of file for input) :INP.0 - Standard input (read only). Normally the keyboard, but may be redirected by the CLI. :OUT.0 - Standard output (write only). Normally the screen, but again, may be redirected. Indeed, this approach can be used from BASIC; for example: 10 LF$=CHR$(10) 20 X%=OPENOUT(":COM.0") 30 PRINT#X%,"Hello"+LF$ 40 CLOSE #X% would send "Hello" to the serial port. The extra line feed character is necessary as both CR and LF are necessary for a 'true' newline when writing to an actual display device, as explained earlier. Accessing Files This can be broken into three basic stages: 1) Opening the file - The user provides a filename and access mode (eg. read, write or update); the 'open' routine prepares the file for processing and returns a 'handle' (which is simply a 16-bit value) unique to this access (ie. there may be more than one handle per file). The handle may now be used as a concise way of identifying the file access, rather than repeat a long filename each time. Note that the number of available handles is limited, so don't leave too many files open at once. 2) Performing the data transfer - this requires that the handle and any necessary data be specified. Access to RAM files is random. A sequential pointer into the file can be both read and written. Both byte by byte transfer, and transfer of whole blocks is possible. 3) Closing the file - this requires only the handle to be specified. The 'close' routine frees the file, which may now be used by other routines, and releases the handle and any memory used for buffers etc. We proceed to consider the relevant system calls. The call 'gn_opf' is used to open a file. The specification of this call is: gn_opf- Open a file RST &20 DEFB &09 DEFB &60 The call takes the following as input: BHL - Pointer to name of file to open. Recall the earlier discussion of extended pointers: B=0 means HL is interpreted as a logical Z80 address, otherwise B is a bank number and the bottom 14 bits of HL an offset within this bank. The filename may be terminated by, among other things, null (0), space (32) or Carriage Return (13). Unless the file is to be opened for output, the filename may also include wildcards, eg: "source.*", in which case the match which was most recently updated is selected. It will be shown later how to gain more explicit control of the wildcard handler. DE - Pointer to space to insert explicit filename - ie. one explicitly including the device and root directories. For instance the input filename "source.b" might become ":RAM.1/source.b" C - Maximum space to fill with this explicit filename. The open routine will write no more characters than this to (DE). A few points are worthy of note. The file will still be opened correctly even if the explicit name will not fit in 'C' characters. Also do not try C=0 in an attempt to surpress its generation entirely; this will be interpreted as 256 characters. The routine may try to insert a compressed version of the name, eg. if C=9 it may insert "/source.b". See spec of 'gn_fex' in section 4 for details of what might happen to the filename. A - Access mode: 1 - open for input 2 - open for output 3 - open for update There are other options. See section 2.15 (DORs) and the specification of gn_opf in section 4.2 Apart from writing the explicit filename to (DE), the routine provides the following output if the call was successful: Fc=0 IX - File handle for open file. B - Number of segments in the explicit filename (a segment in this context meaning a device, directory or file name, for instance the filename ":RAM.2/rootdir/extradir/file.ext" consists of 4 segments). C - Number of characters in explicit filename actually written to (DE). DE - Points beyond the last character of this explicit filename. If the call was unsuccessful then it returns: Fc=1 A - error code RC.BAD (&04) Bad arguments RC.IVF (&17) Invalid filename (eg. filename was ".wrong ") RC.ONF (&12) File not found RC.USE (&15) File already in use RC.FTM (&18) File type mismatch (ie. treating a directory as a file) RC.UNK (&03) Unknown request (ie. wrong A on entry). ............HL/...IY same AFBCDE..../IX... different afbcdehl different. After use, the file may be closed again by using 'gn_cl': gn_cl - Close file RST &20 DEFB &09 DEFB &62 In: IX - Handle of open file Out if close succeeded: Fc=0 IX=0 Out if close failed: Fc=1 IX - unchanged A - error code RC.HAND (&08) ....BCDEHL/....IY same AF............/IX.... different afbcdehl different. While a file is open, there are various calls to transfer data. The simplest are simply byte-at-a-time put and get. Versions with a timeout also exist, ('os_pbt' and 'os_gbt') which may be useful for transferring data to a printer, for instance - refer to section 4 for their specification. Those presented here will wait until input becomes available, which has a bearing on their use for keyboard input. Note that all calls doing or involving file reading (eg. 'os_in' and 'os_mv' respectively) are pre-emptable (see the section 2.3), so in addition to the errors given it may return: RC.ESC (&01) - Escape condition detected RC.SUSP (&67) - Process pre-empted or machine revived RC.DRAW (&66) - Process pre-empted and screen corrupted. os_gb- Get byte from file or device RST &20 DEFB &39 In: IX - Handle for open file (returned from previous 'gn_opf') Output if call succeeded: Fc = 0 A - Byte read from file or if call failed: Fc = 1 A - return code RC.EOF (&09) -End of file reached (this will occur on any calls following the one which read in the file's last character). RC.HAND (&08) - Bad handle supplied in IX (ie. one which does not refer to a currently open file). If the file is open, but for output, then RC.RP (see below), not this error, will be issued. RC.RP (&13) - File/device is read-protected (eg. ":SCR.0"). Plus pre-emption return codes. .....BCDEHL/IXIY same AF............./...... different afbcdehl different os_pb Put (write) byte to file or device RST &20 DEFB &3C In: A - byte to be written IX - handle of open file to which to write byte. Out if call succeeded: Fc=0 or if call failed: Fc=1 A - error code, one of: RC.HAND (&08) - bad handle RC.WP (&14) - write protected (eg. ":INP.0"). For moving large amounts of data between files/devices and memory, the os_mv call is quicker: os_mv - Move bytes between stream and memory RST &20 DEFB &45 In: BC - number of bytes to move IX - handle for relevant file DE=0 - Move data from memory, starting at (HL), to the file. HL=0 - Move data from file to memory, staring at (DE). Out if call was (perhaps partially) successful: Fc=0 BC - number of bytes not read (eg. if end of file was reached before the BC(in)'th character). DE(in)=0, HL - points to next byte to read, DE=0 HL(in)=0, DE - points to next byte to write, HL=0 Out if call failed: Fc=1 BC - number of bytes not read. A - error code, one of: RC.EOF (&09) - end of file reached at some stage. RC.HAND (&08) - IX contained a bad handle. RC.RP (&13) - attempt made to read from read-protected file. RC.WP (&14) - attempt made to write to write-protected file. Plus pre-emption return codes. ................/IXIY same AFBCDEHL/...... different afbcdehl same The following example program makes use of the calls os_fwm and os_frm. Full details can be found in section 4, but, in short, they provide a means to read and write the sequential pointer, extent, and end of file marker for an opened file. The program encrypts a file according to a cipher, and can decode the file at a later date. The program processes the file a byte at a time and hence is very slow, a more realistic approach to this problem would be to use os_mv to copy blocks of the file into your own RAM and process them there. 10 DIM code 400 20 DIM buff 25 :REM buffer for filename 30 DIM filename 40 :REM storage for filename 40 DIM cipher 25 :REM storage for cipher 50 REM system call manifests 60 os_gb=&39 :REM get byte 70 os_pb=&3C :REM put byte 80 gn_esp=&4C09 :REM pointer to system error message 90 gn_err=&4A09 :REM interactive error box 100 gn_soe=&3C09 :REM write string at extended address 110 gn_nln=&2E09 :REM output carriage return and linefeed 120 os_erh=&75 :REM set error handler 130 os_esc=&6F :REM examine special conditions 140 gn_opf=&6009 :REM open file 150 gn_cl=&6209 :REM close file 160 os_fwm=&4B :REM file write miscellaneous 170 os_frm=&48 :REM file read miscellaneous 180 fa_ptr=&01 :REM sequential pointer 190 fa_eof=&03 :REM end of file 200 rc_quit=&67 :REM KILL request 210 rc_esc=&01 :REM ESC 220 rc_eof=&09 :REM end of file 230 op_in=&01 :REM open for input 240 op_out=&02 :REM open for output 250 op_up=&03 :REM open for update 260 : 270 FOR pass=0 TO 2 STEP 2 280 P%=code 290 [ 300 OPT pass 310 LD HL,0 320 ADD HL,SP 330 LD (bstk),HL \save BASIC stack 340 LD SP,(&1FFE) \select safe stack 350 XOR A \new call level 360 LD B,A \B must be zero 370 LD HL,errhan \address of our error handler 380 RST &20:DEFB os_erh \install new error handler 390 LD (obou),A \save old error handler call level 400 LD (oerr),HL \save old error handler address 410 CALL main \call main routine 420 .exit 430 LD HL,(oerr) \address of old error handler 440 LD A,(obou) \old call level 450 LD B,0 \zero B 460 RST &20:DEFB os_erh \reinstall BASIC error handler 470 LD SP,(bstk) \select BASIC stack 480 RET \return to BASIC 490 \ 500 .errhan 510 RET Z \fatal error! 520 CP rc_esc 530 JR NZ,err1 \check for ESCAPE 540 RST &20:DEFB os_esc \acknowledge ESCAPE 550 CALL rep_err 560 JR exit 570 .err1 580 CP rc_quit 590 JR NZ,err2 \KILL request 600 LD HL,(oerr) \reinstall old error handler 610 LD A,(obou) 620 RST &20:DEFB os_erh 622 CALL rep_err 630 LD SP,(bstk) \select BASIC stack 640 LD HL,(oerr) 650 LD A,rc_quit-1 \load A with rc_quit and 660 INC A \reset the zero flag 670 SCF \set the carry flag 680 JP (HL) \jump to BASIC's error handler 690 .err2 700 CP A \ignore other errors 710 RET 720 \ 730 .bstk DEFW 0 \storage for BASIC stack pointer 740 .obou DEFB 0 \old boundary 750 .oerr DEFW 0 \old error handler address 760 \ 770 \main routine starts here 780 .main 790 LD HL,filename \file to encrypt 800 LD DE,buff \expansion buffer for name 810 LD BC,20 \B=0, C=20 size of buff 820 LD A,op_up \open for update 830 RST &20:DEFW gn_opf \open routine 840 JR NC,open_ok \check for errors 850 RST &20:DEFW gn_err \report errors in a box 860 JR exit \quit 870 .open_ok 880 LD HL,0 \set sequential pointer to 0 890 LD (ptr),HL 900 LD (ptr+2),HL 910 CALL up_ptr 920 LD HL,cipher \address of cipher 930 LD A,(type) \encode or decode? 940 CP 0 941 LD HL,0 942 LD (ptr),HL 943 LD (ptr+2),HL 944 CALL up_ptr 945 LD HL,cipher 950 JR Z,un_encode 960 .enc1 \encode 970 LD A,(HL) \cipher character 980 INC HL 990 CP 13 \check end of cipher string 1000 JR NZ,enc2 1010 LD HL,cipher \reset cipher position 1012 INC HL 1020 LD A,(HL) 1030 .enc2 1040 LD B,A \B=cipher character 1050 RST &20:DEFB os_gb \get byte 1060 JR C,enc3 \check for errors 1070 ADD A,B \encode 1080 CALL up_ptr \reset sequential pointer 1090 RST &20:DEFB os_pb \write back encoded byte 1100 CALL inc_ptr \increment sequential pointer 1110 JR enc1 \loop 1120 .enc3 1130 CP rc_eof \if error was eof then ignore 1136 JR enc1 1140 JR Z,enc4 1150 CALL rep_err \report other errors 1160 .enc4 1170 RST &20:DEFW gn_cl \close the file 1180 RET 1190 \ 1200 .un_encode 1210 .uen1 1220 LD A,(HL) 1230 INC HL 1240 CP 13 1250 JR NZ,uen2 1260 LD HL,cipher 1262 INC HL 1270 LD A,(HL) 1280 .uen2 1290 LD B,A 1300 RST &20:DEFB os_gb 1310 JR C,uen3 1320 SUB B \subtract to decode 1330 CALL up_ptr 1340 RST &20:DEFB os_pb 1350 CALL inc_ptr 1360 JR uen1 1370 .uen3 1380 CP rc_eof 1386 JR uen1 1390 JR Z,uen4 1400 CALL rep_err 1410 .uen4 1420 RST &20:DEFW gn_cl 1430 RET 1440 \ 1450 .up_ptr \update sequential pointer 1460 PUSH HL \save regs 1470 PUSH AF 1480 LD HL,ptr 1490 LD A,fa_ptr \sequential pointer 1500 RST &20:DEFB os_fwm \file write miscellaneous 1510 POP AF 1520 POP HL 1530 RET 1540 \ 1550 .inc_ptr \increment sequential pointer 1560 PUSH HL 1570 LD HL,(ptr) 1580 LD DE,1 1590 CP A 1600 ADC HL,DE 1610 LD (ptr),HL 1620 LD HL,(ptr+2) 1630 DEC DE 1640 ADC HL,DE 1650 LD (ptr+2),HL 1660 POP HL 1670 RET 1680 \ 1690 .rep_err \report an error 1700 RST &20:DEFW gn_esp 1710 RST &20:DEFW gn_soe 1720 RST &20:DEFW gn_nln 1730 RET 1740 \ 1750 .ptr DEFM "0123" 1760 .type DEFB 0 1770 \ 1780 ] 1790 NEXT pass 1800 PRINT "Size of code ";P%-code;" bytes." 1810 : 1820 CLS 1830 PRINT '' 1840 INPUT "Name:"$filename 1850 INPUT "Ciper:"$cipher 1860 INPUT "Encode=0, Decode=1:"?type 1870 CALL code

2.6 The screen driver

The Z88 incorporates a comprehensive screen driver, so that apart from printing normal characters, the user may, by sending suitable control codes to the screen: Set fonts and select bold or flash effect for characters; Switch the cursor on and off; Simulate the effects of special keys (caps lock, index etc.); Define, select and scroll windows; Select the horizontal and vertical print positions; Set justification or alignment; Set up user defined charcters; Make various strings of beeps; in fact, any graphic effects used by the built-in software with the single exception of the map (used in Pipedream - it will be shown later how to use this from assembler programs). The following standard control characters have a special effect: NUL (&00) Ignored SOH (&01) Escape character for special functions (see below). ENQ (&05) Escape character for printer filter (see section 2.8) BEL (&07) Make a beep BS (&08) Left HT (&09) Right LF (&0A) Down VT (&0B) Up FF (&0C) Clear screen DEL (&7F) Draw black box (currently, undefined codes do too). The character SOH (ASCII code 1) is used as an escape character to prefix the special character combinations. It may be followed either by a single special character, or if more than one parameter is to follow (for instance when setting the cursor position one would need three; one for the screen driver code and two for the coordinates), by a count byte which specifies the number of following parameters - this may be in the form of a binary number with the top bit set (ie. plus 128) or an ASCII code for a single decimal digit (clearly the latter is impossible for a count of more than 9). Thus the following sequences are equivalent - both move the cursor to character position (x,y). 1, '3','@', 32+x, 32+y 1, 128+3,'@', 32+x, 32+y Note: A character in single quotes is used in this section as a concise notation for a character constant, so for instance 'A' is the ASCII code for A, ie. &41 or 65 decimal. This translates into the rather less elegant ASC"A" when used in BASIC or assembler, so to send the former of the above sequences to the screen from BASIC one might do: x=3: y=4 VDU 1, ASC"3", ASC"@", 32+x, 32+y. The following escape sequences generate special printable characters: Sequence Description Width Boldable? ----------------------------------------------------------------------- 1, ' ' Exact space 1, '!' Bell symbol 3 1, ''' Grave accent yes 1, '*' Square yes 1, '+' Diamond yes 1, '-' SHIFT symbol 3 1, '|' Unbroken vertical bar yes 1, 224 SPACE symbol 3 1, 225 ENTER symbol 3 1, 226 TAB symbol 3 1, 227 DEL symbol 3 1, 228 ESC symbol 3 1, 229 MENU symbol 3 1, 230 INDEX symbol 3 1, 231 HELP symbol 3 1, 240 Outline arrow: left 2 1, 241 Outline arrow: right 2 1, 242 Outline arrow: down 2 1, 243 Outline arrow: up 2 1, 244 Bullet arrow: left 1, 245 Bullet arrow: right 1, 246 Bullet arrow: down 1, 247 Bullet arrow: up 1, 248 Pointer arrow: left yes 1, 249 Pointer arrow: right yes 1, 250 Pointer arrow: down yes 1, 251 Pointer arrow: up yes 1, '2', '*', char (where char is from 'A' to 'O') draws various characters such as arrows or box construction shapes (which are all boldable), with the following logic: Each of the bottom 4 bits of 'char' represents a direction: bit 0 decimal 1 Left bit 1 decimal 2 Down bit 2 decimal 4 Right bit 3 decimal 8 Up. If one bit is set, a pointer arrow in the relevant direction is drawn. If two bits are set, two sides of a square will be drawn. If three bits are set, a 'T' shape will be drawn. If all four bits are set, a cross will be drawn. For example, the corner generated by: VDU 1, ASC"2", ASC"*", ASC"F" makes a reasonable logical NOT sign. Text Effect Toggles These combinations toggle various display modes of the current window (applying to subsequently written characters): 1, 'B' Bold 1, 'C' Cursor 1, 'F' Flash 1, 'G' Grey 1, 'L' Caps lock 1, 'R' Inverse video 1, 'S' Vertical scrolling 1, 'T' Tiny font 1, 'U' Underline 1, 'W' Horizontal scrolling Rather than toggling these modes, they may be set or reset explicitly by prefixing them with '+' (on) or '-' (off), and a count byte of two to indicate that there are two parameters. For instance: 1, '2', '+', 'B' sets bold on. When written in this form, modes may be combined in a list, for example: 1, '5', '-', 'B', 'F', 'T', 'U' resets the bold, flash, tiny and underline toggles. Finally: 1,127 (DELete all toggle settings) resets all toggles to the system default ie. all toggles off. The cursor position (ie. the next print position) may be moved by the following sequences (x and y are the column and row respectively, with (0,0) being the top left of the current window): 1, '3', '@', 32+x, 32+y Move cursor to column x and row y. 1, '2', 'X', 32+x Move cursor to column x (same y as before). 1, '2', 'Y', 32+y Move cursor to row y (same x as before). The display modes are usually set at the time of writing text to the screen, however it is possible to apply the various effects to text already present. This approach is used in the menu system to highlight commands and by the filer to highlight files. The technique can only be used with the hardware attributes ie. flash, grey, reverse and underline. 1,'2','A',32+n Apply current toggles over next n characters. 1,'2','E',32+n Invert (EOR) current toggles over next n character. The following sequence inverts a 20 character bar at cursor position (0,0): 1, '2', '+', 'R', 1,' 3', '@', 32, 32, 1, '2', E', 52 Windows Windows on the Z88 are referred to by a single ASCII numeric character. Windows '1' to '6' are available for the user to redefine; '7' and '8' are already used by the system - window '7' is the topic area, window '8' is used by many of the system calls during error processing. Windows remember their toggle settings, but if the window area is overwritten (by text from an overlapping window, for example) then the window contents are lost. The following sequences manipulate the windows. (The # character is used because it is vaguely reminiscent of a window): 1, '7', '#', 'n', 32+x, 32+y, 32+w, 32+d, t This redefines window n, with the top left corner at (x,y) and horizontal and vertical sizes of w and d respectively. The values of x and y are offset from the top left corner of the application area ie. (10,0), but can be made absolute (ie. offset from the top left corner of the screen) by using 128+x,128+y in the above sequence. It is very important that windows do not go beyond the width of the screen. To avoid the OZ window windows should be no more than 94 6 pixel wide characters from the edge of the application window, or 104 from the leftmost edge of the screen. The OZ window contains special characters which control the LCD scanning and if these are disturbed the display will not work properly. If your window overlaps the OZ area these characters are almost certain to be disturbed. The "type" of the window is defined by the parameter t. bit 0 sets left and right bars on bit 1 sets shelf brackets on bits 2-6 ignored bit 7 must be set. The type parameter is optional. Using a count byte of '6' will define the window without bars or brackets. To direct output to a window use one of the three following sequences: 1, '2', 'H', 'n' 1, '2', 'I', 'n' 1, '2', 'C', 'n' The 'H' version will maintain display modes, whereas the 'I' version will reset them all (ie. cursor off, scrolling disabled). If it this first time the window has been selected (after being redefined) then the 'I' version will also clear the window. The 'C' version resets the display modes and clears the window. The shelf brackets are used by the system to create banners at the top of a window. (See the filer display for an example.) This effect is achieved by using inverted, tiny and underlined characters. This BASIC program demonstrates how to produce a bannered window. 10 VDU 1,ASC "7",ASC"#",ASC"1", 33, 32, 72, 40, 131 20 VDU 1,ASC"2",ASC"I",ASC"1" 30 VDU 1,ASC"4",ASC"+",ASC"T",ASC"U",ASC"R",1,ASC"2",ASC"J",ASC"C" 40 PRINT TAB(0,0);"CENTRAL BANNER";TAB(0,0); 50 VDU 1,ASC"2",ASC"A",72 60 VDU 1,ASC"7",ASC"#",ASC"1",33,33,72,39,129 70 VDU 1,ASC"2",ASC"I",ASC"1" 80 VDU 1,ASC"3",ASC"+",ASC"C",ASC"S" Note how the window is defined one character in from the edge of the screen to allow space for the vertical bar. It is then redefined to exclude the top line of the screen, thus the banner will be undisturbed by text written to the window. The centralisation is achieved by using the justification codes, which are as follows: 1, '2', 'J', 'N' Set normal justification (the default). 1, '2', 'J', 'C' Centre text between margins. 1, '2', 'J', 'L' Left align. 1, '2', 'J', 'R' Right align. 1, 'L', 32+n Set left margin to n (notice no count byte). 1, 'R', 32+n Set right margin to n (again, no count byte). The non-normal justification modes only work with directly output text, in particular the system input routine fails spectacularly if it used with these modes. It is also worth noting that if you output more characters than will fit between the margins then horizontal scrolling, and not wrapping, will occur. Miscellaneous Operations 1,254 Scroll current window downwards 1,255 Scroll current window upwards (usual direction). 1, '2', 'G', '+' Grey the current window 1, '2', 'G', '-' Ungrey the current window 1,'3',32+n,m Output n copies of the code m 1, '4', '!', 32+r, 32+m, 32+s generates a series of beeps (NOT a single beep), with mark m and space s (in 10ms ticks), and r cycles. Finally, the screen driver may create user defined characters by the following combination which specifies the rows on a 6x8 matrix: 1, 138, '=', charcode, r0, r1, r2, r3, r4, r5, r6, r7 Note that the count of 10 is specified in numeric form with the top bit set (138) as it will not fit in a single digit. If you use a smaller count the unspecified rows will be set to 0. This can be useful since most system characters have a blank bottom row. The number 'charcode' is the character code of the defined character and must be between 64 ('@') and 127 (DEL) inclusive. On an unexpanded machine (ie. without 128K or more in slot 1) only 16 characters can be defined without encroaching on the memory used for the PipeDream map.(Characters above the limit of 16, will have their definitions overwritten by map information when PipeDream is used. If the map width is 64 pixels or less then the full 64 user characters can be used.) r0 to r7 are the numeric representations of the top to bottom rows of the character, with the top bit set. Bit 5 is on the left edge of the character and bit 0 the right. The standard charcters have their left-hand rows blank, so if the user-defined characters are to sit alongside them, they should follow this convention. The characters coexist with the normal characters with the same code. Doing "VDU charcode" will print the system character, whereas the sequence: 1, '2', '?', charcode will print the user defined one. For instance 10 VDU 1, 138, ASC"=",ASC"@",155,155,128,132,132,145,142,128 20 VDU 1,ASC"2",ASC"?",ASC"@" will draw a little smiling face to the screen.

2.7 The keyboard decoder

When input is read from the keyboard, certain key combinations are translated as a two-byte sequence; so two get-byte operations are required to read the bytes generated. The generated bytes or byte pairs for the special keys are as follows: with width Shifted diamond square -------------------------------------------------------- ENTER 0D 00 D1 00 C1 00 B1 TAB 09 00 D2 00 C2 00 B2 DEL 7F 00 D3 00 C3 00 B3 LEFT 00 FC 00 F8 00 F4 00 F0 RIGHT 00 FD 00 F9 00 F5 00 F1 DOWN 00 FE 00 FA 00 F6 00 F2 UP 00 FF 00 FB 00 F7 00 F3 -------------------------------------------------------- Note: the codes in this table are used (sometimes without any zero prefixes) in other parts of the system. The following combinations generate the sub-32 control characters, in a fairly conventional way (remember that the diamond key is the equivalent of a control key when not used in a special menu sequence): decimal hex symbol keys ASCII Name 0 &00 NUL <>= Null 1 &01 SOH <>A Start of header 2 &02 STX <>B Start of text 3 &03 ETX <>C End of text 4 &04 EOT <>D End of transmission 5 &05 ENQ <>E Enquiry 6 &06 ACK <>F Acknowlege 7 &07 BEL <>G Bell 8 &08 BS <>H Backspace 9 &09 HT <>I Horizontal tabulation 10 &0A LF <>J Line feed 11 &0B VT <>K Vertical tabulation 12 &0C FF <>L Form feed 13 &0D CR <>M Carriage return 14 &0E SO <>N Shift out 15 &0F SI <>O Shift in 16 &10 DLE <>P Data link escape 17 &11 DC1 <>Q Device control 1 (XON) 18 &12 DC2 <>R Device control 2 19 &13 DC3 <>S Device control 3 (XOFF) 20 &14 DC4 <>T Device control 4 21 &15 NAK <>U Negative acknowlege 22 &16 SYN <>V Synchronous idle 23 &17 ETB <>W End of transmitted block 24 &18 CAN <>X Cancel line 25 &19 EM <>Y End of medium 26 &1A SUB <>Z Substitute 27 &1B ESC <>[ Escape 28 &1C FS <>\ File separator 29 &1D GS <>] Group separator 30 &1E RS <>£ Record separator 31 &1F US <>- Unit separator Most applications will use either os_in or gn_sip to take user input from the keyboard. When these calls are made remember that the the application can be preempted. os_in - Read character from standard input RST &20 DEFB &2A In: - Out if call succeeded: Fc=0 A - character read (or &00 to indicate a special key or a diamond command code will be returned in the next byte). Out if call was preempted: Fc=1 A - return code RC.SUSP (&69) - process suspended or machine revived RC.DRAW (&66) - process suspended and screen corrupted RC.QUIT (&67) - kill request RC.ESC (&01) - escape condition detected. .....BCDEHL/IXIY same AF............./...... different afbcdehl different Notes: This call waits until the result is available (see os_tin for a version incorporating a timeout) gn_sip is the standard system input line routine and used by most of the applications. It provides access to all the standard editing diamond commands which are: <>DEL Delete line <>D Delete to end of line <>G Delete character <>M Enter <>S Swap case <>T Delete word <>U Insert character <>V Insert/Overtype (see below for complications) <> left Start of line <> right End of line SHIFT left Previous word SHIFT right Next word Where appropriate the following editing commands should be implemented (this is machine convention): <>J Next option (where input is limited all the options can be cycled through using this command) <> up Top of current page <> down Bottom of current page SHIFT up Move up by a screenful SHIFT down Move down by a screenful TAB Next column or TAB <> TAB First column SHIFT TAB Previous column or TAB The basic specification of gn_sip is as follows: gn_sip RST &20 DEFB &09 DEFB &38 In: DE - buffer for input string A0 - set if buffer already contains data (null terminated) to be edited. The data will be written out by gn_sip A1 - set to force insert/overtype mode (see A2) A2 - 0=insert mode, 1=overtype mode. This is only relevant if A1 is set and is local to the routine A3 - return unexpected characters. This allows for diamond, square and shift sequences to be returned to the user for processing. A4 - return if a wrap occurs A5 - single line lock control. This limits the width of input to the width specified in L and performs horizontal scrolling if required. A6 - display in reverse mode. This inverts an area as long as the length of the buffer (or the line width) and then leaves the reverse mode toggle in the on state. It is not recommended. A7 - Allow for insert/overtype return. If A3=A7=1 then <>V will exit to the users code. This allows the insert/overtype mode to be global for an application ie. as in PipeDream. B - length of buffer C - cursor position (only relevant if A1=1). If C exceeds B then the cursor is placed at the end of the buffer. L - width of line (if A5=1) Out if call succeeded: Fc=0 B - length of line entered, including terminating null C - cursor position on exit A - character which caused end of input (see below) Out if call failed: Fc=1 A - return code RC.BAD (&04) - Bad arguments RC.WRAP (&0D) - wrapping has occured (only returned if A4=1) RC.SUSP (&69) - suspicion of suspension RC.DRAW (&66) - application screen needs redrawing RC.QUIT (&67) - kill request RC.ESC (&01) - escape (if escape detection enabled) ........DEHL/IXIY same AFBC......../...... different afbcdehl different Notes: The character which terminated input will usually be ENTER (&0D) or ESCAPE (&01) (if escape detection is disabled). However if A3=1 then many more key sequences will cause the routine to exit. All the single letter diamond codes will be return the control character (shown in the table above), except for <>D, <>G, <>S, <>T, <>U and <>V. These commands will carry out their normal editing functions. However, if <>S is used when the cursor is at the end of the buffer then it will cause and exit, returning A=&13. If A7=1 then <>V will have no effect in the input line but will return A=&16. This allows the user code to implement a insert/overtype mode which globally applies to all input in the application. This is done by checking for an exit with A=&16 and setting A1 and A2 appropriately whenver gn_sip is called. In addition to the control characters the ENTER, TAB, DELETE, and arrow keys all can cause exit. The values they return may be derived by looking at the first table in this section. The prefix zero byte is never returned, but the following byte is returned raw as a value in A. So, for example, if SHIFT up arrow was typed the routine would exit with A=&FB. Note that diamond left arrow would not return A=&F4 since it has a meaning within the input line routine (ie. move to the end of the line). When a diamond sequence is used (or a menu entry selected) os_in will return a zero followed by a command code on the next keyread. gn_sip will be terminated by a special keyboard sequence, if A3=1, by simply exiting with the command code in A. For more details on the menu system see section 3. The following example used a locked line width of 15 characters,. When ENTER is pressed the full input line is displayed. When the buffer is nearly full try preempting (press the square key on its own, for example) and you will see a slight flicker as gn_sip redisplays and scrolls the line. 10 DIM code 200 20 DIM buffer 30 :REM buffer for input string 30 gn_sip=&3809 :REM input line routine 40 gn_nln=&2E09 :REM newline 50 gn_sop=&3A09 :REM output line routine 60 os_out=&27 :REM output character 70 rc_susp=&69 :REM suspended error code 80 init=32 :REM mode for gn_sip (single line lock) 90 FOR pass=0 TO 2 STEP 2 100 P%=code 110 [ OPT pass 120 LD HL,0 130 ADD HL,SP \load HL with SP 140 LD SP,(&1FFE) \new stack 150 PUSH HL \save old stack 160 CALL main 170 LD HL,buffer \address of input string 180 RST &20:DEFW gn_nln \output a newline 190 RST &20:DEFW gn_sop \write out string 200 RST &20:DEFW gn_nln \another newline 210 POP HL \old stack 220 LD SP,HL \restore old stack 230 RET 240 \ 250 .main 260 LD A,12 \clear screen character 270 RST &20:DEFB os_out \write character 280 LD A,init \initial mode value 290 LD DE,buffer \buffer for input string 300 LD B,30 \maximum size of string 310 LD C,0 \cursor position on entry 320 .sip 330 LD HL,pos \prompt text 340 RST &20:DEFW gn_sop \write text 350 LD L,15 \maximum line width 360 RST &20:DEFW gn_sip \system input routine 370 RET NC \return if no errors 380 CP rc_susp \check for suspension 390 RET NZ \return if some other error 400 LD B,30 \reset buffer length 410 LD A,init OR 1 \tell routine buffer contains data 420 JR sip \re-enter routine 430 \ 440 .pos 450 DEFB 1 \screen driver prefix (SOH) 460 DEFM "3@ #" \cursor at (0,3) 470 DEFM "Input:" \prompt 480 DEFB 0 490 ] 500 NEXT 510 CALL code

2.8 The printer filter

All output sent to the ":PRT.0" (or simply ":PRT") device is routed via the printer filter. This is effectively the code translation system defined by the printer editor application; it allows the output sent to ":PRT.0" to be in a standard form while allowing control codes peculiar to a certain type of printer to be sent to the printer itself. Note that this is NOT the same as a more general type of 'filter' which will be discussed later. The printer filter has its own on/off control, so rather than sending codes to switch the printer on, one sends codes to switch the printer filter itself on or off. Switching the filter on and off also sends the printer on and off sequences, as defined in the Printer Editor. These should always be sent at the beginning and end of each printer sequence. If the filter is not switched on then everything sent (other than the on- message) is ignored. The control codes recognised by the printer filter are all prefixed by ENQ (ASCII code 5) and use the same mechanism for specifying the number of following parameters as the screen driver. Control codes 5, '[' Printer filter on 5, ']' Printer filter off 5, '2', 'P', 32+n Set page length to n lines 5, '2', 'H', 32+n Microspace n units 5, 'S' New slot, reset highlights that would be reset on CR 5, '3', '$', 'x', 'y' Send &xy to the printer. Toggle display modes 5, 'U' Underline 5, 'B' Bold 5, 'X' Extended sequence 5, 'I' Italics 5, 'L' Subscript (lowered text) 5, 'R' Superscript (raised text) 5, 'A' Alternate font 5, 'E' User defined The toggles can be automatically reset at the next slot or the next carriage return by setting the 'Off at CR' option in the Printer Editor to Yes. By default all the display toggles except A (alternate font) and E (user defined) are automatically reset at a new slot or a carriage return. The following codes, in addition to SP to DEL (&20-&7F) are sent to the printer: NUL (&00) BEL (&07) BS (&08) HT (&09) LF (&0A) VT (&0B) FF (&0C) CR (&0D) The printer output can be redirected using the CLI (command line interpreter), and other outputs can be redirected to the printer. The following diagram shows where redirection occurs. The use of the CLI is detailed in the next section (2.9) PrinterEd PANEL output redirected | | to the printer | | (.>:PRT) | | | | | | | | application-->-->-->printer filter-->-->-->comms port-->-->-->9 pin 'D' | | \-->-->redirected printer output (.=filename) The Printer Filter also provides character translations. This facility is used in the default driver to generate a pound sign, which requires a sequence of codes to make the printer change character sets. The UK machine has 9 translations and the international versions have a further 28. The translations may be set up using the PrinterEd application or by setting values directly with the os_sp call (see section 2.19 for more details).

2.9 Standard Input/Output and the CLI

The Z88 operating system has, at any one time, a standard input source and a standard output destination. A number of calls use the these standard i/o streams, for example: os_in read a character from standard input os_out write a character to standard output gn_sop write a string to standard ouput gn_soe write a string at extended address to standard output gn_sip fetch an input line using standard i/o gn_sdo write date to standard output Initially standard input and output are bound to the keyboard and screen respectively, but may be redefined to any file or device. The user may do this via the Command Line Interpreter (CLI). CLI commands are prefixed by a full stop and must be the first thing on the line. .<infile take input from file/device "infile" .>outfile send output to file/device "outfile" which binds the standard input to "infile" and the standard output to "outfile". Redirection can also be done T-fashion, where the ordinary streams remain in place, but copies of their contents are sent to a file or a device. .T<infile send copy of input to file/device "infile" .T>outfile send copy of input to file/device "outfile" The printer output can be redirected in both ways: .=prtfile redirect pre-filter printer output to file/device "prtfile" .T=prtfile as above, but also send the output to the printer filter Certain keystroke operations (such as square and diamond sequences) have a special representation which is used when input is redirected. These sequences are generated when input is T-redirected and can be used in ordinary redirection to simulate the effect of the keyboard. Note that there are some keyboard features which have no representation, such as: the CAPS LOCK key, shift or diamond with ESCAPE, the effect of holding down shift and diamond to stop scrolling. Sequence Significance ------------------------------------------------------------------------- # holding down square and pressing another key | holding down diamond and pressing another ~A pressing square and releasing it before another keypress ~C pressing diamond and releasing it before another keypress | [ ESCAPE ~S shift (only generated if shift had an effect) ~I INDEX key ~M MENU key ~H HELP key ~X DELETE key ~U up ~D down ~L left ~R right ## a single hash | | a single bar ~~ a single tilde (pronounced 'tilda' or 'twiddle') ------------------------------------------------------------------------- The CLI also has the following commands: .S suspend the current CLI but maintain all rebindings .D delay for n centiseconds (if ESCAPE is pressed during a delay then subsequent delays will fail for the rest of the current CLI) .J Jammer. Ignore all special sequences for the rest of the CLI (eg. after this command #B will be generate '#B' and not try and enter a BASIC) .*file Invoke a new CLI file. Note: The CLI command '..' was intended to produce a single dot. This in fact does not work. Any number of dots at the start of the line will be ignored. One way around this bug is to use '~.' at the start of a line to generate a full stop. Subsequent dots (ie. ones which do not appear as the first character of the line) are treated like ordinary alpha characters. CLI files can invoke other CLI files. A CLI is terminated when the end of a file is reached or a suspension command is issued. CLI's can be forceably removed one at a time by using shift and ESCAPE, or all current CLI's can be removed by using diamond and ESCAPE. Note that diamond must be actually held down while ESCAPE is pressed, ie. the usual latching operation does not apply. The CLI can be accessed by BASIC by using the *CLI command or the OSCLI command. For example, to create a 10 second delay: *CLI .D 1000 OSCLI ("*CLI .D 1000"):REM these two lines are equivalent Most CLI use will be in the form of executable files, consisting of lines of CLI commands and text. These can be generated in PipeDream and saved as text files. Or generated by hand and then executed. CLI files can be executed from the FILER (using diamond EX) or from BASIC using: OSCLI("*CLI .*"+fname$) where fname$ contains the filename Typical CLI files consist of short sequences like: .T>copyout send a copy of all output to "copyout" .S maintain this binding The binding (T-output to "copyout") will now stay in place until the CLI is terminated by SHIFT and ESC to remove the current CLI (or <>ESC, which removes all CLI's running). This situation will generally be undesirable, because it requires user intervention, since the program cannot simulate the effect of SHIFT ESC. Ideally the CLI should be avoided. Redirection can be easily achieved in applications by using file type i/o, however, if necessary the CLI can be accessed fairly directly from an application. The call dc_icl invokes a new CLI, and is effectively the equivalent of BASIC's *CLI. The call dc_rbd is used to directly rebind streams in the current CLI layer and can also terminate the CLI. The example program below starts a CLI, using dc_rbd, rebinds the T-output stream, and finally closes the stream and terminates the CLI. Refer to section 4 for details of the calls used: 10 DIM code 200 20 DIM buffer 255 30 gn_sop=&3A09 40 os_out=&27 50 os_tin=&2D 60 dc_rbd=&1C0C 70 dc_icl=&140C 80 gn_opf=&6009 90 gn_cl=&6209 100 op_out=2 110 FOR pass=0 TO 2 STEP 2 120 P%=code 130 [ OPT pass 140 LD HL,0 150 ADD HL,SP 160 LD SP,(&1FFE) 170 PUSH HL 180 CALL main 190 POP HL 200 LD SP,HL 210 RET 220 \ 230 .main 240 LD HL,cli_string \string to pass to the CLI 250 LD C,2 \length 260 LD B,0 \must be zero 270 RST &20:DEFW dc_icl \invoke new cli 280 LD BC,1 \dummy keyread to allow CLI to 290 RST &20:DEFB os_tin \be processed 300 LD A,op_out \open a file for output 310 LD DE,buffer+128 320 LD C,1 \smallest explicit filename length 330 LD B,0 340 LD HL,buffer 350 RST &20:DEFW gn_opf 360 JR NC,rebind 370 LD (exit0),A \exit it open failed 380 RET 390 .rebind 400 LD A,4 \T-ouptut 410 RST &20:DEFW dc_rbd \rebind to stream IX 420 JR NC,close_off 430 LD (exit),A 440 RST &20:DEFB gn_cl 450 RET \exit if failed, attempting to close file 460 .close_off 470 LD HL,message 480 RST &20:DEFW gn_sop \output message 490 LD IX,0 \close off 500 LD A,4 510 RST &20:DEFW dc_rbd \close file and quit CLI 520 RET NC 530 LD (exit2),A 540 RET 550 .message DEFM "This should go to the file and the screen." 560 DEFB 0 570 .cli_string 580 DEFM ".S" 590 DEFB 0 600 .exit0 DEFB 0 610 .exit DEFB 0 620 .exit2 DEFB 0 630 DEFM "Input:":DEFB 0 640 ] 650 NEXT 660 $buffer="Example" 670 !exit0=0 680 CALL code 690 PRINT '?exit0,?exit,?exit2:REM display any errors that occured

2.10 Memory management

This section explains how a program may use memory neatly. Using the BASIC application can to some extent insulate the programmer against the relative complexity of the machine's memory management by simply presenting 40K of contiguous RAM as per a normal Z80 system. Nonetheless, the routines described below may often be useful. A 'good application' does not rely on large (>256 bytes) chunks of contiguous memory - BASIC is exceptional in its requirements since it was not written specifically for the Z88. Binding of banks to segments was discussed earlier; it is now time to explain how a user program may effect such bindings. The answer, in hardware terms is quite simple to rebind one of the segments 0 to 3, the user writes the bank number to the relevant 'segment register' in the BLINK gate array, which is addressed by the Z80 i/o ports. This method is explained in Section 2.20 (and how to do it legally and quickly in section 2.19); however there are complexities involved, so it is recommended that the reader uses the system calls provided to do the job: os_mgb - Get current binding; ie. find which bank is currently bound to a particular segment. RST &20 DEFB &5A In: C - memory segment specifier (0,1,2 or 3) Out if call was successful: Fc=0 B - bank of memory currently bound to that segment. or if call failed: Fc=1 A - error code RC.BAD (&04) - C was not valid ......CDEHL/IXIY same AFB........../...... different afbcdehl different os_mpb - Set new binding, ie. bind a bank to a segment. RST &20 DEFB &5D In: C - memory segment specifier (0,1,2 or 3) B - Bank number to bind into this segment. Out if call succeeded: Fc=0 B - bank previously bound to the relevant segment. or if call failed: Fc=1 A - error code RC.BAD (&04) - invalid C. ......CDEHL/IXIY same AFB........../...... different afbcdehl different The call returns the previous binding in order to make it easy to restore the old binding afterwards via the following sequence: LD B,<new bank> LD C,<segment> RST &20 DEFB &5D ; os_mpb PUSH BC ; PUSH old binding onto stack . . . . POP BC ; and restore here RST &20 ; os_mpb DEFB &5D Most systems provide calls to allocate chunks of memory and free them after use. The Z88 is no exception; however the largest chunk of memory which can be allocated at any one time (and thus the largest chunk which can be guaranteed contiguous) is 256 bytes. The only way of ensuring a larger contiguous chunk is to set up the program as a so-called 'bad applications' (like BASIC) - this is wasteful of space and time (as the MOS reshuffles memory), hence the pejorative term attached. A well- written application should only use the small chunks of memory, which for most programs should not be difficult, and indeed should provide a useful discipline as it tends to lead to little wasted memory in comparison with, say, reserving a 100K chunk and probably leaving much of it unused. Reserved chunks of memory are allocated from (generally) larger 'pools' of memory. Hence, to reserve memory, the program must first allocate a memory pool. This is done by the call 'os_mop' (called, rather misleadingly, 'open memory'). This call allocates a memory pool, set initially at one page (256 bytes) of memory, and a handle to refer to it. The actual memory allocation is done with 'os_mal'. It is called with the relevant memory handle, and memory is allocated from the page allocated to the handle. If future calls of 'os_mal' exhaust this page, more pages will be allocated. The complement of 'os_mal' is 'os_mfr' (free memory), which, as expected, will free a chunk of memory. This may be a subset of a previously allocated chunk. Finally, the complement to 'os_mop' is 'os_mcl' (close memory). This frees any memory still allocated to the relevant handle, and releases the handle itself. The specification of these calls is presented below: os_mop - Open memory. That is, allocate a memory pool and a handle to refer to that pool. RST &20 DEFB &4E In: A - memory mask. This should be one of &00, &40, &80 and &C0. When calls are made to allocate memory using the handle supplied by this call, the upper two bits of the logical address of the allocated block are given by the upper two bits of this mask, in an obvious fashion. Thus, if we know that any allocated memory will be used in segment 2, then we could supply &80 to the 'os_mop' call to ensure that the addresses of memory blocks supplied do fall within segment 2. BC=0 - not necessary in the current version, but reserved for future expansion. Output if call succeeded: Fc=0 IX - memory handle or if call failed: Fc=1 A - error code, one of: RC.NA (&06) - handle not available RC.ROOM (&07) - no room. A...BCDEHL/....IY same ...F............/IX.... different afbcdehl different. os_mcl - close memory, ie. release any memory associated with the handle supplied, and release the handle itself. In: IX - memory handle (returned from previous 'os_mop' call) Out if call succeeded: Fc=0 IX=0 or if call failed: Fc=1 A - error code RC.HAND (&08) - IX(in) was not a valid memory handle ......BCDEHL/.....IY same AF............../IX.... different afbcdehl different os_mal - allocate memory RST &20 DEFB &54 In: IX - memory handle (returned from previous call of 'os_mop') BC - requested size of reserved memory (should be from 2 to 256 inclusive) A - should be zero (reserved for future expansion). Out if call succeeded: Fc=0 HL - address of memory allocated. Upper 2 bits will be defined by the memory mask given to the 'os_mop' call, so if this reflects the segment in which the memory will be used, this can be used as the logical address of the block once the relevant bank has been bound in. B - bank number of allocated memory. Notice that this bank is not automatically bound into the logical address space. C - segment specifier implied by HL. or if call failed: Fc=1 A - error code, one of: RC.HAND (&08) - IX is not a valid memory handle RC.ROOM (&07) - no room to allocate block. .........DE..../IXIY same AFBC.....HL/........ different afbcdehl different os_mfr - Free memory (may be a subset of previously allocated chunk) RST &20 DEFB &57 In: IX - memory handle (returned from previous 'os_mop' call) BC - size of memory to release AHL - extended address of memory to release. Out if call succeeded: Fc=0 or if call failed: Fc=1 A - error code RC.HAND (&08) - IX is not a valid memory handle RC.BAD (&04) - invalid AHL/BC/IX combination (eg. BC>256 or AHL points to a block not allocated from pool with handle IX etc.) .....BCDEHL/IXIY same AF.......... ../......... different afbcdehl different

2.11 The floating point package

Floating point numbers in the Z88 are stored as a 4-byte mantissa and a 1 byte exponent. The exponent byte is simply the exponent plus 127. The mantissa is a binary fraction in a standard form where the most significant bit is assumed 1 (ie. the number is greater than or equal to 0.5 and less than 1). This is analogous to a base 10 standard form where the mantissa is of the form 0.xyz.... (the usual choice is x.yz.... but this is a matter of human convention) - x cannot be zero, as if it were then the number would be represented with a mantissa of 0.yz... and the exponent incremented. Hence the most significant bit of the mantissa may be taken to be 1 when calculating its magnitude, and can actually store other information. It is used as the mantissa sign bit, where 1 means a negative number. For example the decimal number 13 is binary 1101 or 0.1101*10^100; this would be represented as a mantissa of 01010000......0000 (32 bits - note the zero msb for positive number) and an exponent of 10000011 (131 decimal). An exponent of zero is taken to mean that the mantissa is an integer - this allows integers which will fit in four bytes to be manipulated differently, avoiding irritating features such as 10 becoming 9.999998, whilst retaining the ability to cope with the large ranges which come with the floating point representation. Floating point numbers are conventionally stored with the least significant bit of the mantissa at the lowest address, and the exponent at the highest. However, since numbers are presented to and returned from the floating point package in registers, this convention may be flouted at the programmer's discretion. As mentioned earlier, the floating point package (hereinafter referred to as the FPP) uses a different RST call from the main system calls - the format of a floating point call is as follows: RST &18 DEFB <function code> so apart from the different restart number,much the same as the OS calls. Parameters are passed to and from the FPP in a rather bizarre collection of registers. The main register group is HLhlC (remember, lower case for alternate set), and where a second parameter is required it is passed in DEdeB. These divide into mantissa and exponent as follows: H D Most significant byte of mantissa L E h d l e Least significant byte of mantissa C B Exponent byte. There are certain exceptions, which are explained where relevant (for instance one operation returns the numeric value of a string, so takes a pointer to the string in HL as its parameter). Here is the full list of parameter bytes and the FPP functions which they invoke. 33 AND (Integer-only) bitwise logical AND. Input parameters in HLhlC and DEdeB. Result in HLhlC. 36 DIV (Integer-only) quotient after division. Input parameters in HLhlC (dividend) and DEdeB (divisor). Result in HLhlC. 39 EOR (Integer-only) bitwise logical exclusive OR. Input parameters in HLhlC and DEdeB. Result in HLhlC. 42 MOD (Integer only) remainder after division. Input parameters in HLhlC and DEdeB. Result in HLhlC. 45 OR (Integer only) bitwise logical OR. Input parameters in HLhlC and DEdeB. Result in HLhlC. 48 <= Test for less than or equal to. Input parameters in HLhlC and DEdeB (test is HLhlC <= DEdeB). Result in HLhlC (-1 for TRUE; 0 for FALSE). 51 <> Test for not equal to. Input parameters in HLhlC and DEdeB. Result in HLhlC (-1 for TRUE; 0 for FALSE). 54 >= Test for greater than or equal to. Input parameters in HLhlC and DEdeB (test is HLhlC <= DEdeB). Result in HLhlC (-1 for TRUE; 0 for FALSE). 57 < Test for less than. Input parameters in HLhlC and DEdeB (test is HLhlC < DEdeB). Result in HLhlC (-1 for TRUE; 0 for FALSE). 60 = Test for equality. Input parameters in HLhlC and DEdeB. Result in HLhlC (-1 for TRUE; 0 for FALSE). 63 * Multiplication. Input parameters in HLhlC and DEdeB. Result in HLhlC. 66 + Addition. Input parameters in HLhlC and DEdeB. Result in HLhlC. 69 > Test for greater than. Input parameters in HLhlC and DEdeB (test is HLhlC > DEdeB). Result in HLhlC (-1 for TRUE; 0 for FALSE) 72 - Subtraction. Input parameters in HLhlC and DEdeB (returns HLhlC - DEdeB). Result in HLhlC. 75 ^ Raise to power. Input parameters in HLhlC and DEdeB (returns HLhlC ^ DEdeB). Result in HLhlC. 78 / Division. Input parameters in HLhlC (dividend) and DEdeB (divisor). Result in HLhlC. 81 ABS Magnitude (ABSolute value). Input parameter in HLhlC. Result in HLhlC. 84 ACS Inverse cosine (Arc CoSine). Input parameter in HLhlC. Result in HLhlC (in radians). 87 ASN Inverse sine (Arc SiNe). Input parameter in HLhlC. Result in HLhlC (in radians). 90 ATN Inverse tangent (Arc TaNgent). Input parameter in HLhlC. Result in HLhlC (in radians). 93 COS Cosine. Input parameter in HLhlC (in radians). Result in HLhlC. 96 DEG Convert radians to degrees. Input parameter in HLhlC (in radians). Result in HLhlC (in degrees). 99 EXP Exponentiation (raise 'e' [2.718...] to power of argument). Input parameter in HLhlC. Result in HLhlC. 102 INT Integer truncation (floor truncation, ie. NOT rounding). Input parameter in HLhlC. Result in HLhlC. 105 LN Natural (Naperian or base 'e') logarithm. Input parameter in HLhlC. Result in HLhlC. 108 LOG Common (base 10) logarithm. Input parameter in HLhlC. Result in HLhlC. 111 NOT (Integer-only) bitwise logical NOT. Input parameter in HLhlC. Result in HLhlC. 114 RAD Convert degrees to radians. Input parameter in HLhlC (in degrees). Result in HLhlC (in radians). 117 SGN Sign (signum; ie. whether number is positive, negative or zero). Input parameter in HLhlC. Result in HLhlC (-1 for negative; 0 for zero; 1 for positive). 120 SIN Sine. Input parameter in HLhlC (in radians). Result in HLhlC. 123 SQR Square root. Input parameter in HLhlC. Result in HLhlC. 126 TAN Tangent. Input parameter in HLhlC (in radians). Result in HLhlC. 129 ZERO Returns the constant zero (as an integer, for the pedant). Input irrelevant. Result in HLhlC (all zero). 132 FONE Return the floating point representation of constant 1. Input irrelevant. Result in HLhlC (HL=hl=0; C=&80). 135 TRUE Return the integer representation of constant -1. Input irrelevant. Result in HLhlC (HL=hl=&FFFF; C=0). 138 PI Returns the floating point representation of pi (3.1415926...). Input irrelevant. Result in HLhlC. 141 VAL Returns the numeric value of a string. HL points to start of string (null-terminated). Result in HLhlC, and DE points to last character read; if the call succeeded this will be the terminator byte. If the call failed, HLhlC=0. 144 STR$ Return the string representation of a number. Number in HLhlC; DE points to space to insert string. The de register pair contains a format control variable which has the effect of the two middle bytes of the BASIC format control variable @%, ie: 1) The contents of e determine the maximum number of digits (ie. number of characters apart from decimal point or 'E') to be printed. The allowed range of values depends on the format selected (see below): General format: 1-10 Exponential format: 1-255 (leading zeros added beyond the 10th significant figure). Fixed format: 0-10 (in this case the number of digits printed AFTER the decimal point. 2) The contents of d determine the format of the resultant string for a given number; the options are as follows: d=0: General format: Integers are printed without a decimal point or exponent. Numbers between 0.1 and 1 will be printed with a decimal point but no exponent. Numbers less than 0.1 will be printed with a decimal point and exponent. Numbers greater than 1 will be printed without exponent unless this would involve more digits than allowed by the. contents of the 'e' register (see above), and outside this range, will be printed with an exponent. d=1: Exponential format - all numbers printed in exponential notation (eg. 1.0E0) . d=2: Fixed format - numbers are printed with a fixed number of decimal places. 147 FIX Truncate a number to an integer. Input parameter in HLhlC (if C=0 the call has no effect). Result in HLhlC. 150 FLOAT Convert from integer to floating point format. Input parameter in HLhlC (unless C=0 the call has no effect). Result in HLhlC. 153 TEST Test a number for zero and sign. Input parameters in HLhlC. Result in A (0 for zero; &40 for positive; &C0 for negative). 156 COMPARE Compare two numeric values Input parameters in HLhlC (left) and DEdeB (right). Result in A (0 if HLhlC=DEdeB; &40 if HLhlC > DEdeB; &C0 if HLhlC < DEdeB). 159 NEG Negate number, ie. result = -input. Input parameter in HLhlC. Result in HLhlC. 162 CALL Perform one of the above functions dependent on the operation code in A; eg: LD A,132 RST &18 DEFB 162 should call FONE and return 1 (floating point). This facility can be useful if the operation depends on some parameter, to avoid writing self-modifying code. If an FPP operation is completed successfully, it returns with Fz=1 and Fc=0. If the call failed for some reason, it returns Fz=0, Fc=1 and an error code in A, which may be one of: RC.DVZ (&46) Division by zero RC.TBG (&47) Too big RC.NVR (&48) Negative root RC.LGR (&49) Log range RC.ACL (&50) Accuracy lost RC.EXR (&51) Exp range. The following is a simple example program, which prints out the mathematical constant PI. It makes two FPP calls, one PI to get the value, then a STR$ to convert it to a string. This is then printed via the standard 'gn_sop' routine. 10 DIM code 100 20 gn_sop=&3A09 :REM write to standard output 30 gn_nln=&2E09 :REM newline 40 fp_pi=&8A :REM return floating point value of pi 50 fp_str=&90 :REM string representation 60 FOR pass=0 TO 2 STEP 2 70 P%=code 80 [ OPT pass 90 LD HL,0 100 ADD HL,SP \get stack pointer into HL 110 LD SP,(&1FFE) \select safe stack 120 PUSH HL \save old stack 130 CALL main 140 POP HL \get back old stack 150 LD SP,HL \restore old stack 160 RET 170 \ 180 .main 190 RST &18:DEFB fp_pi \get value of pi 200 LD DE,stringbuffer \buffer to expand string into 210 EXX \swap register sets 220 LD DE,&000A \format control (10 digits) in de 230 EXX \swap register sets 240 RST &18:DEFB fp_str \call STR$ 250 XOR A \zero A 260 LD (DE),A \add terminator for gn_sop 270 LD HL,stringbuffer \address of converted string 280 RST &20:DEFW gn_sop \write string to standard output 290 RST &20:DEFW gn_nln \write newline 300 RET 310 .stringbuffer 320 DEFM STRING$(15,"*") 330 ] 340 NEXT pass 350 CALL code

2.12 Time and date management

The applications within the Z88 frequently need to manipulate times and dates. The clock, calendar and diary are the obvious examples; also the filer stores 'last updated' dates in its filing system. Hence a fairly comprehensive set of routines are provided to handle them. Before discussing the routines themselves, it is worth explaining how dates and times may be represented. Within the machine, dates are represented by 3-byte unsigned integers which represent the number of days since the conventional day zero, which is Monday 23rd November, 4713 BC (!). This number of days figure is consistent from the point of view of the New Style (Gregorian) calendar, so will not tally with historical dates before 14th September 1752 (Britain) or 14th Oct 1582 (continental Europe) unless the dates have been retrospectively corrected (like George Washington's birth date). However it should be entirely correct from its chosen point of view, incorporating the following rules to deal with leap years: Every year is a normal year (365 days) except every 4th year is a leap year (366 days) except every 100th year is a normal year except every 400th year is a leap year except every 3200th year is a leap year except every 80000th year is a normal year. Routines are provided to convert between this rather inconvenient format and either: 1) A zoned integer representing a human-type date conveniently, split over registers as follows: C (bits 7...5) - the day of the week (1=Monday...7=Sunday) C (bits 4...0) - the day of the month (1...31 with obvious meaning) B - the month (1=Jan...12=Dec) DE - a signed year number relative to 0 AD (ie. normal) 2) An ASCII string, with various options (leading blanks, American format, century [ie. 88 or 1988] month in full [ie. Dec or December] etc). Times are internally represented as unsigned 3-byte integers representing the number of 10ms (hundredth of a second) intervals since the start of the day. Routines exist to convert between this format and an ASCII string, again with various options. Further routines are provided to read or set the machine time and date. The available routines are as follows, their specifications can be found in section 4. gn_gdt convert an ASCII string to an internal date gn_pdt convert an internal date to an ASCII string gn_die convert from internal to external format gn_dei convert from external to internal format gn_gmd fetch current machine date gn_pmd set current machine date gn_gtm convert an ASCII string to an internal time gn_ptm convert an internal time to an ASCII string gn_gmt fetch current machine time gn_pmt set current machine time gn_msc convert real time to elasped time gn_sdo output date and time to standard output Here is an example program which prints the current date: 10 DIM code 200 20 gn_gmd=&1809 :REM get machine date 30 gn_pdt=&0809 :REM put date in ASCII form 40 gn_sop=&3A09 :REM write string to standard output 50 gn_nln=&2E09 :REM write newline to standard ouput 60 FOR pass=0 TO 2 STEP 2 70 P%=code 80 [ OPT pass 90 LD HL,0 100 ADD HL,SP \load HL with SP 110 LD SP,(&1FFE) \load safe stack pointer 120 PUSH HL \save old stack pointer 130 CALL main \call main routine 140 POP HL \recall old stack pointer 150 LD SP,HL \restore old stack pointer 160 RET \return to BASIC 170 \ 180 .main 190 LD DE,datebuffer \buffer for date to be stored in 200 RST &20:DEFW gn_gmd \get machine date 210 LD HL,datebuffer \source date 220 LD A,240 \century output, C=interfield delimiter 230 LD B,15 \expanded day and month (no AD/BC) 240 LD C,ASC" " \interfield delimiter 250 LD DE,stringbuffer \buffer for converted date 260 RST &20:DEFW gn_pdt \put date in ASCII format 270 XOR A \zero A 280 LD (DE),A \place NULL at the end of the string 290 LD HL,stringbuffer \address of converted string 300 RST &20:DEFW gn_sop \output string 310 RST &20:DEFW gn_nln \newline 320 RET \finished 330 .datebuffer 340 DEFM "*****" 350 .stringbuffer 360 DEFM STRING$(20,"&") 370 ] 380 NEXT pass 390 CALL code

2.13 The Serial Interface

The Z88 has a single serial port which has to be shared by all applications, so it is important that applications use the system to access the port to avoid interfering with the correct operation of other applications running in the machine. The system runs the serial port using interupts and buffering, so most of its operation is transparent,but there are certain operations, such as directly controlling the RTS line (required for auto-dialling by some modems), which the system calls do not support and in these cases the hardware is accessed more directly (see section 2.20) The serial port specification is reproduced here for convenience: Baud rates: 75, 300, 600, 1200, 2400, 9600, 19200, 38400 Send and receive rates are independent. Parity: None, Odd, Even, Mark, Space Note the Z88 generates the appropriate parity, but will ignore, but not strip, the parity of incoming data. Flow control: Hardware handshaking or XON/XOFF. Timeout This defaults to 10 minutes. (ie. the Z88 will wait for input for 10 minutes before giving up.) Using the Serial Port The serial port is accessed by opening the device :COM, using gn_opf, and then using the standard file i/o routines: gn_gb, gn_pb, gn_gbt, gn_pbt and os_mv. The device should be closed after the operation is complete. For most applications this level of access should be sufficent, however, it may be necessary to change the serial port parameters from within an application. This is done by writing new values to the Panel, using the os_sp call, and then issuing a 'Soft Reset' command to the serial driver. Changing the Panel setting may affect other software in the machine, so should not be undertaken lightly. It would be appropriate to reset the Panel to its original setting after serial operations are complete. This can be achieved by first reading the Panel settings with os_nq. os_sp and os_nq are covered in detail in section 2.19. The 'Soft Reset' operation is carried out by using the os_si call. This call also provides other serial operations, which might be useful for some applications. os_si - serial interface RST &20 DEFB &8D In: L - reason code The reason codes are as follows: SI.HRD (&00) Hard reset the serial port SI.SFT (&03) Soft reset the serial port SI.INT (&06) Interupt entry point. DO NOT USE! SI.GBT (&09) Get byte from serial port SI.PBT (&0C) Put byte to serial port SI.ENQ (&0F) Status enquiry SI.FTX (&12) Flush Tx (transmit) buffer SI.FRX (&15) Flush Rx (receive) buffer SI.TMO (&18) Set timeout SI.HRD No parameters. This resets the UART in the gate array and performs a soft reset (see below). This call should not need to be used. AFBCDEHL/IXIY same .............../...... different afbcdehl different SI.SFT No parameters. This call should be used to install new panel settings or to when starting low level (ie. using SI.GBT, SI.PBT etc.) serial operations. It carries out the following: 1- Empty receive and transmit buffers 2- Reset the XON and XOFF flags 3- Reset baud rates, parity and flow control settings according to the PANEL values. 4- Assert RTS 5- Resets the serial port timeout to its default of 10 minutes. SI.INT This should not be used. SI.GBT - Get byte from serial port In: BC - timeout in centiseconds Out if call succeeded: Fc=0 A - byte received BC - remaining time Out if call failed: Fc=1 A - RC.TIME (if no data available before timeout) ....BCDEHL/IXIY same AF............/..... different afbcdehl different SI.PBT - Put byte to serial port In: A - byte to send BC - timeout in centiseconds. If BC=&FFFF then default timeout is used. Out if call succeeded: Fc=0 BC - remaining time Out if call failed: Fc=1 A - RC.TIME ....BCDEHL/IXIY same AF........... /...... different afbcdehl different This call will return immediately if there is space in the transmit buffer, otherwise it will wait until there is space for as long as the timeout. If the timeout is exceeded it will return with an error. SI.ENQ - Status enquiry In: - Out if call succeeded: D - number of full slots in the Tx (transmit) buffer E - number of empty slots in the Tx (transmit) buffer B - number of full slots in Rx (receive) buffer C - number of empty slots in Rx (receive) buffer A7 - Rx shift register full A6 - DCD interupt A5 - CTS interupt A4 - Tx register empty A3 - undefined A2 - Rx register full A1 - DCD level (inverse of the value on the D-connector) A0 - CTS level (inverse of the value on the D-connector) ............HL/IXIY same AFBCDE..../...... different afbcdehl different Notes: A slot, in this context, is the set of bits required to transmit one character. This will include 8 data bits plus start and stop bits. SI.FTX - Flush transmit buffer In: - Out: Fc=0 AFBCDEHL/IXIY same .............../...... different afbcdehl different SI.FRX - Flush receive buffer In: - Out: Fc=0 AFBCDEHL/IXIY same .............../...... different afbcdehl different SI.TMO - set default timeout In: BC - new value for default timeout Out: Fc=0 AFBCDEHL/IXIY same afbcdehl different If you set the default to &FFFF then when the defualt is used (by setting a timeout value of &FFFF in get and put routines) then the system will wait forever. A soft reset sets the default timeout to 10 minutes. This timeout is completely independent of the system timeout, which is set by the Panel. Flow Control Flow control can be controlled either by software or hardware. The hardware handshaking is always active, so if you want to use software exclusively you will need to wire a cable to set the handshaking lines high at all times. (Tie pins 5, 8 and 9 together on the Z88 D-connector.) An external device can ask the Z88 to stop sending by either sending an XOFF character or by de-asserting (bringing low) the CTS line. With software flow control transmission can only stop when the XOFF character has been processed, so there is potentially a slight delay in response, while previously sent characters are read. With hardware control transmission stop on the next character boundary. Transmission is resumed on the recipt of an XON character, in the case of software control, or be re-asserting CTS ie. bringing it high. The output buffer is around 95 bytes long. If software control is used the Z88 will send an XOFF to an external device once the receive buffer is more than half full. Characters will continue to be received until there are only 15 character spaces left in the buffer. At this point an XOFF will be sent for every character subsequently sent by the external device. If the receive buffer overflows then data is lost forever. The Z88 will send an XON when the receive buffer has been cleared to less than a quarter full. Under hardware control, the Z88 will de-assert (bring low) RTS when the receive buffer is less than half full and the re-assert (bring high) when it has become less than a quarter full. The input buffer is around 127 bytes long. Serial Port Lines 1 - unswitched +5v at 10 uA output 2 TxD transmit data output 3 RxD receive data input 4 RTS ready to send output 5 CTS clear to send input 6 - reserved for future use 7 GND 8 DCD data carrier detect input 9 DTR switched +5v at 1mA output Note: DTR is high when the machine is awake. The machine is always awake when the screen is active, but even if asleep the machine will wake every minute or so to carry out various housekeeping tasks, such as checking for alarms, and at these times DTR will go high. Pin 1 will show a signal if there is power available to the machine.

2.14 The wildcard handler

It was promised earlier that we would explain how to explicitly use the wildcard handler, so one may gain control over the match selected, or indeed get all possible matches. The wildcard system recognises the following character sequences: * match any number of characters (or none) ? match a single character // matches any number of directories (or none) Note: / and \ are interchangeable, but the system generates / Normal use of the wildcard handler involves specifying a wildcard string and then getting, one by one, the possible explicit filenames that match it (if any), which may then be used or discarded as required. The 'gn_opw' routine (open wildcard handler) is supplied with a pointer to a wildcard string. The call returns a wildcard handle which may then be used in calls of 'gn_wfn' (fetch next name from list) which, as its name suggests, will provide the next possible explicit filename which matches the original wildcard string, or 'end of file' if no more are available. These will appear in reverse order of 'last-updated' time, and a few options in the 'gn_opw' call are available to control the treatment of directories as files (see below). Finally, when use of this wildcard is completed, the wildcard handler is closed via the call 'gn_wcl'. The specification of these three calls is reproduced here for convenience: gn_opw - Open wildcard handler RST &20 DEFB &09 DEFB &52 In: BHL - Pointer to wildcard string. B=0 => HL=logical address. BHL must be greater than 255 A0 - scan direction; if set then 'directory/file' is returned before 'directory'; if reset the opposite is the case. A1 - Set to return parents (ie. directory names) A2-A7 - should all be reset Out if call succeeded: Fc=0 IX - wildcard handle for this wildcard string Out if call failed: Fc=1 A - return code; one of: RC.ROOM (&08) - Insufficient memory RC.IVF (&17) - Invalid wildcard string ....BCDEHL/...IY same AF............/IX... different afbcdehl different gn_wcl - Close wildcard handler RST &20 DEFB &09 DEFB &54 In: IX - wildcard handle Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code: RC.HAND (&08) - bad handle ......C....HL/...IY same AFB..DE..../IX... different afbcdehl different gn_wfn - Fetch next match for wildcard string RST &20 DEFB &09 DEFB &56 In: DE - Pointer to buffer for explicit name (should be greater than 255) C - Buffer size IX - handle for relevant wildcard Out if call succeeded: Fc=0 DE - Points to null-termination of explicit name B - number of segments in filename C - number of characters in explicit name A - DOR type Out if call failed: Fc=1 A - error code; one of: RC.BAD (&04) - Bad arguments RC.EOF (&09) - No more matches RC.HAND (&08) - Bad handle ......C....HL/IXIY same AFB..DE...../...... different afbcdehl different The following example program demonstrates the use of the above sequence in providing a complete catalogue of the RAM filing system, by matching to the wildcard string ":RAM.*//*". There is no pagewait built in - the reader may add this feature if desired, but one may always rely on shift-diamond to stop the scrolling. A dummy string is used on line 390 as a substitute for a define-storage directive, as explained in section 1. 10 DIM code 200 20 gn_opw=&5209 30 gn_wfn=&5609 40 gn_wcl=&5409 50 gn_nln=&2E09 60 gn_sop=&3A09 70 FOR pass=0 TO 2 STEP 2 80 P%=code 90 [ OPT pass 100 LD HL,0 ; save BASIC's stack 110 ADD HL,SP 120 LD SP,(&1FFE) ; and use new stack 130 PUSH HL 140 CALL main ; call the main routine 150 POP HL ; restore BASIC's stack 160 LD SP,HL 170 RET 180 .main 190 LD HL,wildstring ; address of wildcard string 200 XOR A ; zero A 210 LD B,A ; zero B 220 RST &20:DEFW gn_opw ; open wildcard handler 230 .nextname 240 LD DE,wildinsert ; buffer for returned filenames 250 LD C,30 ; maximum extent of buffer 260 RST &20:DEFW gn_wfn ; get next entry 270 JR C,close_exit ; is it the last entry 280 LD HL,wildinsert ; point HL at buffer 290 RST &20:DEFW gn_sop ; print filename 300 RST &20:DEFW gn_nln ; print a newline 310 JR nextname 320 .close_exit 330 RST &20:DEFW gn_wcl ; close wildcard handler 340 RET 350 .wildstring 360 DEFM ":RAM.*//*" ; change this if you like! 370 DEFB 0 380 .wildinsert 390 DEFM "RHUBARB!RHUBARB!RHUBARB!RHUBARB!" 400 ] 410 NEXT pass 420 PRINT '"--START--" 430 CALL code 440 PRINT "--FINISH--"

2.15 DORs

DOR may be thought of as standing for 'Directory Object Record' - although DOR was not intended as an acronym for this, this does provide quite a good explanation. DORs are a fairly general record structure well suited to displaying heierarchically organised information. They are used in the RAM filing system (but not, unfortunately, the EPROM filing system) and in application definitions. Some degree of understanding of them is necessary to extract certain information, such as last-updated dates, from the filing system. It is also important to have some background in them in order to fully understand section 3 (how to write an application). Format A DOR starts with the three link pointers which point to the parent, brother, and child respectively. These are all 3 byte physical addresses. If relatives do not exist then the link pointers are set to 0. The remainder of the DOR consists of a series of keyed subrecords. 3 bytes link to parent (0=none) 3 bytes link to brother (0=none) 3 bytes link to son (0=none) 1 byte DOR type byte eg. DM.ROM (&83) 1 byte length of DOR in bytes 1 byte Record key. This is usually a mnemonic eg. DT.NAM (&4E) (ASCII 'N' for Name) 1 byte Length of record entry in bytes n bytes Data specific to record . . More record entries (in the same form as above) . 1 byte &FF - The DOR terminator The above format may be useful if you want to set up static DOR structure. For example the header for application cards, and the headers for the internal applications, use a static DOR structure. On the whole, however, the internal arrangement of the DOR is transparent to the user and the DOR interface can be used throughout. This interface works with uses the following types and record keys: Note:In the following descriptions the ASCII character corresponding to a hexadecimal value is sometimes placed next to it within brackets. This is done to indicate the mnemonic nature of the values used. (&41,"A") represents the single hexadecimal value &41. Major Types DM.DEV (&81) - filing system ; system use only DM.CHD (&82) - character device ; system use only DM.ROM (&83) - ROM information Consists of minor types: DN.INF (&40,"@") - application information (optional) DN.HLP (&48,"H") - help information DN.NAM (&4E,"N") - name of application Note: It is not possible to add external device drivers to the system via a DOR, despite the major types shown above. Minor Types DN.FIL (&11) - file Consists of record types: DN.NAM (&4E,"N") - filename DN.CRE (&43,"C") - creation time DN.UPD (&55,"U") - update time DN.EXT (&58,"X") - extent DN.DIR (&12) - directory Consists of the record types: DN.NAM (&4E,"N") - directory name DN.CRE (&43,"C") - creation time DN.UPD (&55,"U") - update time DN.APL (&13) - application Consists of record types: DN.NAM (&4E,"N") - "APPL" DN.DEL (&7F) - deleted entry Record Types DT.NAM (&4E,"N") - name Name must be null terminated. Filenames have a fixed length of 17 characters so, if the real filename is shorter, you will need to ignore excess characters. A name record might look like this: &4E DEFB ASC"N" &05 DEFB 5 ; length (including terminator) &42 DEFB ASC"B" &69 DEFB ASC"i" &6C DEFB ASC"l" &6C DEFB ASC"l" &00 DEFB 0 ; terminator DT.UPD (&55,"U") - last updated date 6 bytes: 3 bytes internal time followed by 3 bytes internal date DT.CRE (&43,"C") - creation date 6 bytes: 3 bytes internal time followed by 3 bytes internal date DT.EXT (&58,"X") - extent of file 4 byte word (low byte first) DT.ATR (&41,"A") - attributes (these are not generally used) 2 bytes DT.HLP (&48,"H") - help (in external help DOR) 12 bytes Four pointers: Topics, commands, help and tokens. DT.INF (&40,"@") - information See section 3 for details of format DORs are manipulated using the os_dor call, whose specification is detailed here: os_dor - the DOR interface RST &20 DEFB &87 In: A - reason code HLIX - arguments Out if call succeeded: Fc=0 Returned values depend on A(in) Out if call failed: Fc=1 A - return code RC.HAND (&08) Reason codes are as follows: DR.GET (&01) - get handle for a DOR name DR.DUP (&02) - duplicate DOR DR.SIB (&03) - return brother DOR DR.SON (&04) - return child DOR DR.FRE (&05) - free DOR handle DR.CRE (&06) - create blank DOR DR.DEL (&07) - delete DOR DR.INS (&08) - insert DOR DR.RD (&09) - read DOR record DR.WR (&0A) - write DOR record And there specifications are: DR.GET (&01) - get a handle for a DOR name In: Fc=0 HL - pointer to a string Out if call succeeded: Fc=0 IX - DOR handle A - minor type Out if call failed: Fc=1 A - error code Notes: Do not use this call. Instead use gn_opf with A=6 to obtain a DOR handle. DR.DUP (&02) - duplicate DOR In: IX - DOR handle Out if call succeeded: Fc=0 BC - duplicate handle (IX is still valid) Out if call failed: Fc=1 BC=0 (IX is still valid) A - error code DR.SIB (&03) - return brother DOR In: IX - DOR handle Out if call succeeded: Fc=0 IX - next DOR handle (original IX invalid) A - minor type Out if call failed: Fc=1 A - return code RC.HAND (&08) - bad handle DR.SON (&04) - return child DOR In: IX - DOR handle Out if call succeeded: Fc=0 IX - child DOR handle (original IX invalid) A - minor type Out if call failed: Fc=1 A - return code RC.HAND (&08) - bad handle DR.FRE (&05) - free DOR handle In: IX - DOR handle Out if call succeeded: Fc=0 - success Out if call failed: Fc=1 A - error code RC.HAND (&08) - bad handle DR.CRE (&06) - create blank DOR In: IX - parent DOR B - minor type Out if call succeeded: Fc=0 IX - new DOR handle (original IX invalid) Out if call failed: Fc=1 A - return code RC.HAND (&08) - bad handle RC.BAD (&04) - bad arguments RC.ROOM (&07) - no room DR.DEL (&07) - delete DOR In: IX - DOR handle Out if call succeeded: Fc=0 - DOR deleted (original IX now invalid) Out if call failed: Fc=1 - (original IX now invalid) A - error code RC.HAND (&08) - bad handle DR.INS (&08) - insert DOR In: BC - parent DOR handle IX - new DOR handle Out if call succeeded: Fc=0 (original BC and IX are valid) Out if call failed: Fc=1 (original BC and IX are valid) A - return code RC.HAND (&08) - bad handle DR.RD (&09) - read DOR record In: B - record type C - buffer length DE - user buffer address IX - DOR handle Out if call succeeded: Fc=0 (original IX is valid) C - actual length of information Out if call failed: Fc=1 (original IX is valid) Note: This can be used for reading information on files, applications etc. DR.WR (&0A) - write DOR record In: B - record type C - buffer length DE - user buffer address IX - DOR handle Out if call succeeded: Fc=0 (original IX is valid) Out if call failed: Fc=1 (original IX is valid) A - return code RC.HAND (&08) - bad handle RC.BAD (&04) - bad arguments Example The following BASIC examle program reads the last updated date of a file, which can only be done by reading the DOR of the file. To get a DOR handle for a file, the user may use the 'gn_opf' call with A=6. This differs from the other options of gn_opf in that: 1) It does not open the file 2) It returns a DOR handle rather than a file handle. Note that the file should be closed before the call is made and that it is necessary to free the DOR handle after you have finished, by using os_dor with reason code DR.FRE (&05). 10 DIM code 400 20 gn_nln=&2E09 :REM newline 30 gn_sdo=&0E09 :REM print date in standard form 40 gn_opf=&6009 :REM open file 50 os_dor=&87 :REM DOR interface 60 dt_rd=&09 :REM DOR read 70 dt_fre=&05 :REM free DOR handle 80 FOR pass=0 TO 2 STEP 2 90 P%=code 100 [ OPT pass 110 LD HL,0 120 ADD HL,SP \load HL with SP 130 LD (BASICstack),HL \save old stack 140 LD SP,(&1FFE) \use new stack 150 CALL main 160 LD SP,(BASICstack) \restore old stack 170 RET 180 \ 190 .main 200 LD HL,filename \name of file to examine 210 LD DE,explicitname \expansion buffer for explicit name 220 LD B,0 230 LD C,20 \max. size of expanded name 240 LD A,6 \get DOR handle 250 RST &20:DEFW gn_opf \open file routine 260 LD A,dt_rd \read DOR record 270 LD B,ASC"U" \look at record U (Update time) 280 LD C,6 \max size of data to return 290 LD DE,datebuffer \return data at (DE) 300 RST &20:DEFB os_dor \DOR interface 310 LD A,dt_fre \free DOR handle 320 RST &20:DEFB os_dor \DOR interface 330 LD HL,datebuffer \HL points to internal date and time 340 RST &20:DEFW gn_sdo \write date and time 350 RST &20:DEFW gn_nln \write newline 360 RET 370 .BASICstack 380 DEFW 0 390 .filename 400 DEFM ":RAM.1/source.b" 410 DEFB 0 420 .explicitname 430 DEFM STRING$(20,"%") 440 .datebuffer 450 DEFM "^^**!!" 460 ] 470 NEXT pass 480 CALL code

2.16 Filters

Filters, as provided on the Z88, are a fairly general means of achieving simple context-independent transformations in a byte sequence. Their anticipated use is in simple text processing, but they may potentially be used for more complex tasks. To use a filter, the programmer sets up a 'filter data table' (FDT) which consists, apart from a few extra pieces of data (see later), of pairs of strings. Then a byte sequence may be written to the filter, and an occurence of one of the 'left-hand' strings will be replaced by the corresponding 'right-hand' string in the output. For example, suppose the FDT contains the following pairs of strings: Monday Mon Tuesday Tue Wednesday Wed Thursday Thu Friday Fri Saturday Sat Sunday Sun The program would first open the filter via. the call 'gn_flo', supplying the start address of the FDT; the call returns a filter handle (assuming the FDT is valid and enough handles and memory are available). The program may then push bytes into the filter using the 'gn_fpb' call. If the letters 'M','o','n','d','a','y' were pushed into the filter, then the characters pulled from the other end (via. 'gn_fgb') would be 'M','o','n' and then an 'End of file' return code, RC.EOF (&09), would be encountered. Thus the filter provides a convenient means of performing simple text processing. Because the filter can only search for strings to substitute amongst characters which are 'in' the filter, the normal use is to push in the entire input string and pull out the entire result, each in one go. Also note that the filter routine will select the first successful match in the FDT, so if one of the left hand side strings is an extension of another, the longer should come first if the obvious substitution is required. For instance: Mon Lundi Monday Lundi would result in the transformation of "Monday" into "Lundiday"; probably not what was intended. The full format of an FDT is now given. At the top is the following header: 2 bytes: Size of FDT 1 byte: Options for left-hand strings 1 byte: Options for right-hand strings The options for the strings may be made up of some combination of the following bit settings: 128: Table has top bit set charaters 64: Table has numeric data 32: Table has alphabetic data 16: Table has punctuation characters. Then come the entries, with the following format: 1 byte 1+m m bytes left-hand string 1 byte 1+n n bytes right-hand string. The 'length' bytes are actually a displacement to the character beyond the next string, hence they are one greater than the length of the string. Two restrictions: the FDT must not span a 16K boundary, and due to a software bug (see 'gn_opf' spec), the FDT must be addressed in segment 1. The following is a simple example program, which converts extended format dates, eg. 'Wednesday 14th December', into abbreviated dates, eg. 'Wed 14th Dec': 10 DIM winge &2000 20 REM Above line wastes 8K of memory to force FDT into segment 1 30 DIM code 600 40 FOR pass=0 TO 2 STEP 2 50 P%=code 60 [ OPT pass 70 EXX 80 LD HL,0 90 ADD HL,SP 100 LD SP,(&1FFE) ; Select safe stack 110 PUSH HL 120 EXX 130 CALL main ; Call main routine 140 EXX 150 POP HL 160 LD SP,HL ; Retrieve old stack 170 EXX 180 RET ; Return to BASIC 190 \ 200 .main 210 LD HL,fdt_address ; Start of Filter Data Table 220 LD A,4 ; Force buffer size to B 230 LD B,30 ; which is 30 bytes 240 RST &20 250 DEFB &09 260 DEFB &22 ; 'gn_opf' - Open filter 270 \ 280 LD HL,inputstring ; Start of unconverted string 290 .startinput 300 LD A,(HL) ; Get next input char 310 CP 0 ; Finish if null-termination 320 JR Z,endinput 330 RST &20 340 DEFB &09 350 DEFB &26 ; 'gn_flw' - Push byte into filter 360 INC HL ; Point to next input character 370 JR startinput ; Loop 380 \ 390 .endinput 400 RST &20 410 DEFB &09 420 DEFB &28 ; 'gn_flr' - Pull next char from filter 430 JR C,endoutput ; if end is reached, finish 440 RST &20 450 DEFB &27 ; 'os_out' - Write byte to screen 460 JR endinput ; Next char 470 .endoutput 480 \ 490 RST &20 500 DEFB &09 510 DEFB &24 ; 'gn_flc' - Close filter 520 RST &20 530 DEFB &09 540 DEFB &2E ; 'gn_nln' - Write newline 550 RET 560 \ 570 .fdt_address ; Start of FDT 580 DEFW fdt_end-fdt_address ; Total length word 590 DEFB 32 ; Options for LH strings (alphabetic) 600 DEFB 32 ; Options for RH strings (alphabetic) 610 \ 620 DEFB 7 ; Length+1 630 DEFM "Monday" ; First LH string 640 DEFB 4 ; Length+1 650 DEFM "Mon" ; First RH string 660 DEFB 8 ; (etc.) 670 DEFM "Tuesday" 680 DEFB 4 690 DEFM "Tue" 700 DEFB 10 710 DEFM "Wednesday" 720 DEFB 4 730 DEFM "Wed" 740 DEFB 9 750 DEFM "Thursday" 760 DEFB 4 770 DEFM "Thu" 780 DEFB 7 790 DEFM "Friday" 800 DEFB 4 810 DEFM "Fri" 820 DEFB 9 830 DEFM "Saturday" 840 DEFB 4 850 DEFM "Sat" 860 DEFB 7 870 DEFM "Sunday" 880 DEFB 4 890 DEFM "Sun" 900 \ 910 DEFB 8 920 DEFM "January" 930 DEFB 4 940 DEFM "Jan" 950 DEFB 9 960 DEFM "February" 970 DEFB 4 980 DEFM "Feb" 990 DEFB 6 1000 DEFM "March" 1010 DEFB 4 1020 DEFM "Mar" 1030 DEFB 6 1040 DEFM "April" 1050 DEFB 4 1060 DEFM "Apr" 1070 DEFB 4 ; This entry is, in fact, redundant 1080 DEFM "May" 1090 DEFB 4 1100 DEFM "May" 1110 DEFB 5 1120 DEFM "June" 1130 DEFB 4 1140 DEFM "Jun" 1150 DEFB 5 1160 DEFM "July" 1170 DEFB 4 1180 DEFM "Jul" 1190 DEFB 7 1200 DEFM "August" 1210 DEFB 4 1220 DEFM "Aug" 1230 DEFB 10 1240 DEFM "September" 1250 DEFB 4 1260 DEFM "Sep" 1270 DEFB 8 1280 DEFM "October" 1290 DEFB 4 1300 DEFM "Oct" 1310 DEFB 9 1320 DEFM "November" 1330 DEFB 4 1340 DEFM "Nov" 1350 DEFB 9 1360 DEFM "December" 1370 DEFB 4 1380 DEFM "Dec" 1390 .fdt_end ; End of FDT 1400 .inputstring 1410 DEFM "Wednesday 14th December" 1420 DEFB 0 ; Input string (null terminated) 1430 ] 1440 NEXT pass 1450 CALL code The calls associated with filters are now listed for convenience: gn_flo - Open filter RST &20 DEFB &09 DEFB &22 In: HL - pointer to filter table A - attribute byte; some combination of: 1 - Allow case equivalence on input 2 - Use table in reverse mode 4 - Force maximum buffer size to B bytes. B - Maximum buffer size. Must be <= 128, and is only relevant if A2 is set. Out if call succeeded: Fc=0 IX - filter handle Out if call failed: Fc=1 A - error code; one of: RC.ROOM (&07) - Out of memory RC.BAD (&04) - FDT structure invalid RC.HAND (&08) - Bad handle. A..BCDEHL/....IY same ...F.........../IX.... different afbcdehl different Bugs: FDT must be addressed in segment 1 or this call will fail. FDT must not cross a 16K boundary. gn_flc - Close filter RST &20 DEFB &09 DEFB &24 In: IX - filter handle Out if call succeeded: Fc=0 BC - number of input characters written to filter (provided at least one character has been read) DE - number of characters read from filter. Out if call failed: Fc=1 A - error code, RC.HAND (&08) - IX(in) was not a valid filter handle ............HL/IXIY same AFBCDE..../....... different afbcdehl different gn_flr - read from filter RST &20 DEFB &09 DEFB &28 In: IX - filter handle Out if call succeeded: Fc=0 A - character read Fz - 1 if character is converted, else 0 Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - bad filter handle RC.EOF (&09) - filter is empty .....BCDEHL/IXIY same AF............./...... different afbcdehl different gn_flw - Write character to filter RST &20 DEFB &09 DEFB &26 In: IX - filter handle A - character to write Out if call succeeded: Fc=0 A unchanged Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - bad filter handle RC.FLF (&0A) - filter is full A.. BCDEHL/IXIY same ...F.........../....... different afbcdehl different gn_fpb - Push character back into filter RST &20 DEFB &09 DEFB &2C Note that at most one character can be pushed back without an intervening read. In: IX - filter handle Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - bad filter handle RC.PUSH (&0E) - One character already pushed back or no character read from this filter. RC.EOF (&09) - filter is empty .....BCDEHL/IXIY same AF............./...... different afbcdehl different gn_flf - Flush filter RST &20 DEFB &09 DEFB &2A In: IX - filter handle Out of call succeeded: Fc=0 A - character from filter Fz - 1 if character is converted, else 0 Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - Bad filter handle RC.EOF (&09) - Filter is empty ......BCDEHL/IXIY same AF............../....... different afbcdehl different

2.17 Handling alarms

Alarms in the Z88 are organized as a linked list of 'alarm blocks' with the root at some fixed location. Each alarm block contains all the relevant data for the particular alarm: time, date, number of repeats; in fact much the same as the options given in the alarm display in the main alarm application. To set an alarm, a program: 1) Allocates an alarm block via 'gn_aab'. This is just a convenient instance of getting an area of memory, but you must use this call to allocate alarm blocks with this call and not use an ordinary memory allocation. 2) Explicitly inserts the necessary values into the block, such as the time of the alarm. 3) Links the alarm in the chain, via the 'gn_lab' call. The format of the alarm block is as follows: 3 bytes link to next block (set by system) 3 bytes Time of alarm (internal format) 3 bytes Date of alarm (internal format) 24 bytes Command line to execute (or comment). This should be null terminated. Note: The repeat time starts 33 bytes into the block, not after the terminator for the command line. 3 bytes Repeat time (in days) 3 bytes Repeat time (in centisecond ticks) 2 bytes Number of times to repeat 1 byte Repeat time units: 1 - seconds 2 - minutes 4 - hours 8 - days 16 - weeks 32 - months 64 - years 128 - never repeat 1 byte alarm status, some combination of: 1 - Bleep on expiry 2 - Execute command line on expiry 4 - Alarm has expired (set by system) 8 - Alarm is pending (set by system) The 'repeat time units' byte controls what is displayed in the alarm popdown window. It is important to choose a unit appropriate to the repeat time. If you use units of hours, with a repeat time of 2 minutes, then the repeat time will be displayed as 0 hours. Note: The repeat time must be at least ten seconds otherwise it may be difficult to enter the alarm popdown to cleart the alarm. The alarms set up are all lost when a system soft reset occurs, and unfortunately there is no way of saving and loading the currently set alarms. In addition, alarms are suppressed when the machine is in the alarm popdown. This is true even if the machine is actually asleep (ie. the screen is off). The following example sets up an alarm for the entirely arbitrary date and time of 21/05/3934 08:46:20. The alarm has a repeat time of 25 seconds and will occur three times (ie. number of repeats is two). Finally the type of alarm is ALARM and the bleeping is enabled: 10 DIM code 100 20 gn_aab=&6809 :REM allocate alarm block 30 gn_lab=&6C09 :REM link alarm block 40 os_mpb=&5D :REM memory put binding 50 : 60 FOR pass=0 TO 2 STEP 2 70 P%=code 80 [ 90 OPT pass 100 LD HL,0 110 ADD HL,SP \load HL with SP 120 LD (bstk),HL \save BASIC stack 130 LD SP,(&1FFE) \select safe stack 140 CALL main \call main routine 150 LD SP,(bstk) \select BASIC stack 160 RET \return to BASIC 170 \ 180 .main 190 RST &20:DEFW gn_aab \allocate alarm block 200 PUSH BC \save address of block 210 PUSH HL 220 LD C,3 \segment 3 230 RST &20:DEFB os_mpb \bind in bank with alarm block 240 PUSH BC \save old binding 250 LD A,H 260 OR &C0 \mask block address for segment 3 270 LD D,A 280 LD E,L \load block address into DE 290 LD HL,block \address of data for block 300 LD BC,43 \size of data 310 LDIR \copy data into block 320 POP BC \fetch old bindings 330 RST &20:DEFB os_mpb \restore old bindings 340 POP HL \fetch address of block 350 POP BC 360 RST &20:DEFW gn_lab \link block into alarm chain 370 RET 380 \ 390 .block 400 DEFM "000" \link to next block (set up by system) 410 DEFM "000" \time in internal format 420 DEFM "000" \date in internal format 430 DEFM "Message space 24 bytes.":DEFB 0 440 DEFB 0:DEFW 0 \repeat time in days 450 DEFB 0:DEFW 10 \repeat time in centiseconds 460 DEFW 2 \times to repeat 470 DEFB 1 \repeat time display unit 480 DEFB 1 \alarm status (bleep on expiry) 490 .bstk DEFW 0 \space to store BASIC stack pointer 500 ] 510 NEXT 520 CALL code The four alarm calls which would be useful to application programmers are reproduced here. gn_aab Allocate alarm block RST &20 DEFB &09 DEFB &68 In: - Out if call succeeded: Fc=0 BHL - Pointer to alarm block. Out if call failed: Fc=1 A - error code RC.ROOM (&07) ......CDE...../IXIY same AFB......HL/....... different afbcdehl different gn_lab - Link an alarm block into the alarm chain RST &20 DEFB &09 DEFB &6C In: BHL - address of alarm block (must have been allocated and filled with appropriate values) Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code RC.BAD (&04) .....BCDEHL/IXIY same AF............./....... different afbcdehl different Note: The system will not necessarily tell you if the alarm block is invalid. A bdly set up alarm block is likely to cause a crash. gn_uab - Unlink an alarm block from the alarm chain RST &20 DEFB &09 DEFB &6E In: BHL - address of alarm block Out if call succeeded: Fc=0 Out if call failed: Fc=1 RC.BAD Fz=1 - Alarm block is dequeued ....BCDEHL/IXIY same AF............/......... different afbcdehl different gn_fab Free alarm block RST &20 DEFB &09 DEFB &6A In: BHL - Physical pointer to alarm block Out if call succeeded: Fc=0 Out if call failed: Fc=1 A- error code RC.BAD (&04) - bad alarm block

2.18 Linked lists

The Z88 incorporates useful system calls for implementing linked list structures. These calls can be used to insert and delete members or fetch the next member. This is very valuable on the Z88, where the memory allocated to a process may be very highly fragmented. For setting up large symbol tables or other big data structures, linked lists are the only realistic arrangement. For instance, Pipedream uses linked lists of text blocks to store the current document. The conventional structure of a linked list is a set of records, each containing a pointer to the next record: ---------------------- addr1: | addr2 | Record...| ---------------------- ---------------------- addr2: | addr3 | Record...| ---------------------- addr3: (etc.) If one wanted to be able to step back as well as forwards, it would normally be necessary to store both a forward and a backward pointer within each record. The novel system used on the Z88 allows forwards and backwards steps while only necessitating the storage of one pointer in the block. You can't get something for nothing, and the price is that to step forward one needs not only the address of the current entry but also that of the previous entry. This is not normally a handicap as one will normally step through the linked list record by record, but it does mean that if you jump into the list without knowlege of an adjacent member, you are lost. The way this is implemented is to store within each record the XOR of the forward and backward pointers. In the case of the first and last records, one of these pointers does not exist - it is assumed zero when calculating the XOR expression. A typical arrangement is as follows: ---------------------------------------------------------------- addr(n) : | addr(n-1) XOR addr(n+1) | (n'th record) | ---------------------------------------------------------------- ---------------------------------------------------------------- addr(n+1): | addr(n) XOR addr(n+2) | (n+1'th record) | ---------------------------------------------------------------- ---------------------------------------------------------------- addr(n+2): | addr(n) XOR addr(n+3) | (n+2'th record) | ---------------------------------------------------------------- Then if we want to step forward from addr(n+1), then assuming we know addr(n), then we can find addr(n+2) as follows: addr(n+2) = [! addr(n+1)] XOR addr(n) Where '!' denotes 'the contents of', by analogy with the word indirection operator in BASIC, although in this case it is a 3 byte indirection rather than 4 byte. Stepping backwards is symmetrical (the advantage of using XOR rather than, say, addition). Thus only one 'fetch next entry' routine is needed and it can be used to move in either direction. To delete entry 'n+1' and link together entries 'n' and 'n+2', we can do: !addr(n) = [!addr(n)] XOR [addr(n+1)] XOR [addr(n+2)] !addr(n+2) = [!addr(n+2)] XOR [addr(n+1)] XOR [addr(n)] which again displays the attractive symmetry of the scheme. This is the algorithm used by 'gn_xdl'. If we now wanted to put entry 'n+1' back again, we could use the routine 'gn_xin', which will do: !addr(n+1) = [addr(n)] XOR [addr(n+2)] !addr(n) = [!addr(n)] XOR [addr(n+1)] XOR [addr(n+2)] !addr(n+2) = [!addr(n+2)] XOR [addr(n+1)] XOR [addr(n)]. The specification of the calls are given here and are followed by an example program. Note that all addresses are 3-byte and are assumed physical whatever the value of the most significant byte. gn_xnx - Index next entry in linked list RST &20 DEFB &09 DEFB &44 In: CDE - address of previous entry BHL - address of current entry Out: Fc=0 CDE - address of old current entry BHL - address of next entry Fc - 1 if BHL(in)=0, else 0. In this case CDE and BHL are the same. Fz - 1 if BHL(out)=0 ................/IXIY same AFBCDEHL/....... different afbcdehl different gn_xin - Insert an entry into a linked list RST &20 DEFB &09 DEFB &46 In: HL - pointer to 9-byte parameter block: (HL+0)...(HL+2) Address of block to insert (HL+3)...(HL+5) Address of previous block (HL+6)...(HL+8) Address of next block Out if call succeeded: Fc=0 Out if call cleared: Fc=1 A - return code: RC.BAD (&04) - if block to insert = 0. ........BCDEHL/IXIY same AF................/...... different afbcdehl different gn_xdl - Delete an entry from a linked list RST &20 DEFB &09 DEFB &48 In: CDE - previous entry BHL - entry to delete Out if call succeeded: Fc=0 CDE - Prior entry BHL - Entry after deleted entry Out if call failed: Fc=1 A - return code: RC.BAD (&04) - if BHL(in)=0 ........CDEHL/IXIY same AFB............/...... different afbcdehl different The following is a simple example demonstrating the linked list routines. It prompts the user to enter a line of input, which is stored in a buffer. If the line ends with newline, this is copied into a newly- allocated block of memory and this is inserted into a linked list. If the line ends in up arrow or down arrow (typically consisting only of one of these), then the 'gn_xnx' is used to select the next or previous entry, which is then printed. Pointers are maintained to the 'current' string and the 'next' string; either of these may be zero. They are updated after movement or insertion. Below them is an 'insert' field; this is temporary, to be used to hold the address of the current line for presentation to the 'gn_xin' call. The blocks of memory consist of a 3-byte link field followed by a null- terminated string. A special routine is used to add 3 to an extended pointer to take us from the start of the block to the start of the string. Note both 'current' and 'next' may become zero (they both start out this way); since 'current' is always the string to be printed, a junk string will result if an attempt is made to move backwards too far. Stepping forward too far will work OK. 10 os_mop=&4E 20 gn_soe=&3C09 30 gn_nln=&2E09 40 gn_sip=&3809 50 os_mal=&54 60 os_bde=&DA06 70 gn_xnx=&4409 80 gn_xin=&4609 90 os_mcl = &51 100 DIM code 600 110 FOR pass=0 TO 2 STEP 2 120 P%=code 130 [ OPT pass 140 EXX 150 LD HL,0 160 ADD HL,SP 170 LD SP,(&1FFE) ; Select safe stack 180 PUSH HL 190 EXX 200 CALL main ; Call main program 210 EXX 220 POP HL 230 LD SP,HL ; Retrieve old stack pointer 240 EXX 250 RET ; Return to BASIC 260 \ 270 .main 280 LD A,&C0 290 LD BC,0 300 RST &20 310 DEFB os_mop ; Allocate memory pool for use in linked list 320 RET C 330 .loop 340 LD A,(current+2) 350 LD B,A 360 LD HL,(current) ; Get extended pointer to 'current' block 370 CALL bhl_p3 ; Add 3 to take us past link field to string 380 RST &20 390 DEFW gn_soe ; Write the string 400 .loop2 410 RST &20 420 DEFW gn_nln ; Append a newline 430 LD DE,linebuffer 440 LD A,8 450 LD B,80 460 LD C,2 470 RST &20 480 DEFW gn_sip ; Get a line of input in 'linebuffer' 490 CP 255 500 JR Z,moveup ; Jump on if line ends in up-arrow 510 CP 254 520 JR Z,movedown ; or if it ends in down-arrow 530 CP 13 540 JP NZ,terminate ; If it does not end in newline, finish 550 LD A,B 560 LD (size),A ; Save size of input line 570 ADD A,3 ; 3 bytes for link field plus line length 580 DEC B 590 JR Z,terminate ; If line null (length=1 including NUL), end 600 LD B,0 610 LD C,A 620 XOR A 630 RST &20 640 DEFB os_mal ; Allocate memory to hold current line 650 JR C,terminat ; If no memory available, finish 660 LD A,B 670 LD (insert+2),A 680 LD (insert),HL ; Set up pointer to this block in 'insert' 690 CALL bhl_p3 ; Add 3 to BHL to take us past link field 700 EX DE,HL 710 LD HL,linebuffer 720 LD A,(size) ; Retrieve size 730 LD C,A 740 RST &20 750 DEFW os_bde ; Copy line from buffer to allocated block 760 LD HL,parmblock 770 RST &20 780 DEFW gn_xin ; Link in this block 790 LD HL,(insert) 800 LD (current),HL 810 LD A,(insert+2) 820 LD (current+2),A ; Update current pointers 830 JR loop2 ; Loop (without printing line again) 840 .moveup 850 LD DE,(next) 860 LD A,(next+2) 870 LD C,A 880 LD HL,(current) 890 LD A,(current+2) 900 LD B,A 910 RST &20 920 DEFW gn_xnx ; Get next entry (in this case, the previous) 930 LD (next),DE 940 LD A,C 950 LD (next+2),A 960 LD (current),HL 970 LD A,B 980 LD (current+2),A ; Update pointers 990 JP loop ; Loop 1000 .movedown 1010 LD DE,(current) 1020 LD A,(current+2) 1030 LD C,A 1040 LD HL,(next) 1050 LD A,(next+2) 1060 LD B,A 1070 RST &20 1080 DEFW gn_xnx ; Get next entry 1090 LD (current),DE 1100 LD A,C 1110 LD (current+2),A 1120 LD (next),HL 1130 LD A,B 1140 LD (next+2),A ; Update pointers 1150 JP loop ; Loop 1160 .terminate 1170 RST &20 1180 DEFB os_mcl ; Deallocate all memory 1190 RET ; Return 1195 ; *** Procedure to add 3 to BHL *** 1200 .bhl_p3 1210 LD DE,3 1220 ADD HL,DE ; Add 3 to HL 1230 RET NC 1240 INC B ; In case of carry, increment B 1250 RET ; Return 1260 .parmblock ; Parameter block for 'gn_xin' call 1270 .insert 1280 DEFM "***" 1290 .current 1300 DEFW 0 1310 DEFB 0 1320 .next 1330 DEFW 0 1340 DEFB 0 1350 .linebuffer ; Buffer for current line 1360 DEFM STRING$(80,"+") 1370 .size 1380 DEFB 0 ; Save area for size of current line 1390 ] 1400 NEXT pass 1410 PRINT"######START#####" 1420 CALL code 1430 PRINT"######FINISH####"

2.19 Miscellaneous useful routines

This section gives a little more information on useful routines which did not conveniently fit in any of the previous sections. The Map Area PipeDream uses a small area on the right hand edge of the screen to display a picture of what a page will look like when printed. This region is called the map area and consists of between 0 and 256 pixels across by 64 pixels down. It is theoretically possible to have a 624 by 64 Hires screen, but with current information graphics are limited to the map area only. It may be possible, with extreme care, to write directly to the map memory, by accessing the HIRES0 character set (see Appendix C). The call provided only allows you to write to the map area, not to read back from it. If you need to know about the contents of the map area then you should keep a copy of what you have written to it. The width of the map is limited to 96 pixels in an unexpanded machine (see Appendix C). The actual width will always be a multiple of 8, although the widths used by the os_map call will be one less than this (ie. 63 refers to an actual width of 64 pixels, numbered 0 to 63). The map call has the following specification os_map RST &20 DEFB &06 DEFB &F4 In: BC - reason code MP.WR (&01) - write a line to the map MP.DEF (&02) - define a map of specific width MP.GRA (&03) - define a map using the Panel width MP.DEL (&04) - delete a map Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - return code RC.BAD (&04) - bad parameters (errors are generally ignored) RC.UNK (&03) - value of BC is not valid MP.WR (&01) - write a line to the map In: A - window number (ASCII digit '1' to '6') DE - row of the map to be written (0 to 63, 0=top of screen) HL - points to bytes to be written Out: - A...BCDEHL/IXIY same ...F............/....... different afbcdehl different Notes: HL points to a series of bytes, where each byte represents 8 pixels on the map. Bit 7 is the left most pixel and the bytes are written onto the map from left to right. The number of bytes read from HL depends on the width of the map. MP.DEF (&02) - define a map of specific width In: A - window number (ASCII digit '1' to '6') HL - width of map to set up (0 to 255) Out: B - width of map in pixels C - width of map in 6 bit characters A.......DEHL/IXIY same ...FBC......../IXIY different afbcdehl different MP.DEF (&03) - define a map using the Panel width In: A - window number (ASCII digit '1' to '6') Out: as for MP.GRA Notes: This call simply reads the value of the map size entry in the panel and attempts to define a map of that width. MP.DEL (&04) - delete a map area In: A - window number (ASCII digit '1' to '6') Out: - Notes: At present there is only one map area and it is reclaimed for text by redefining a window which it sits in or by defining a window which overlaps it. However, to allow for future expansion this call should be used if a map area is to be reclaimed for text use. Enquiries The os_nq call is used to read a wide variety of system parameters, including information on the screen, memory, available streams and settings from the Panel and Printer Editor. A call is made by loading BC with a reason code and the other registers with values specific to that reason code. Register changes and output specifications are different for each reason code. os_nq RST &20 DEFB &66 In: BC - reason code ADEHLIX - parameters Out if call succeeded: Fc=0 Other registers - depends on reason code Out if call failed: Fc=1 A - return code RC.BAD (&04) - incorrect reason code or parameters RC.UNK (&03) - unknown request Notes: In the description of each reason code which follow, only successful calls are considered. In some cases it is possible that other error codes will be returned. With luck this should be clear from the context. Panel and Printer Editor See the next sub-section, Specifies, for details of reading the Panel and Printer Editor. Window Management NQ.WBOX (&8300) - return window information In: A - window id (ASCII '1'-'8' or A=0 for current window) Out: A - window id (ASCII '1'-'8') C - width B - depth E - offset from left of screen D - offset from top of screen ................/IXIY same AFBCDEHL/....... different afbcdehl different Notes: D and E return 0 always. ie. offset of start of window relative to start of window. NQ.WCUR (&8303) - return cursor information In: A - window id (ASCII '1'-'8' or A=0 for current window) Out: A - window id (ASCII '1'-'8') C - x coordinate of cursor B - y coordinate of cursor D - state information (bit 7 set if cursor is on) ................/IXIY same AFBCDEHL/....... different afbcdehl different NQ.RDS (&8306) - read text from the screen In: DE - pointer to a buffer to store text HL - number of bytes to read Out: - A..BCDEHL/IXIY same ..F............/...... different afbcdehl different Notes: This call reads text from the current window starting at the current cursor position. Screen locations which have not be written are read as NUL (not as SPC) and all the screen locations return some value (ie. if the window width is 40 characters then 40 bytes will be returned for that line). If more than a whole line of characters is to be read then reading resumes at the start of the next line. The cursor position is not affected by this call, but if a ludicrously large value of HL is used then the screen may be affected. Process Management Many of these calls simply fetch system handles. These cases have no input parameters and return the relevant handle in IX. NQ.AIN (&8600) - application enquiry Notes: For system use NQ.KHN (&8603) - read keyboard handle Notes: This is NOT equivalent to opening the device :INP NQ.SHN (&8606) - read screen handle Notes: It may be preferable to use gn_opf to open the device :SCR NQ.PHN (&8609) - read printer indirected handle NQ.NHN (&860C) - read nul handle Notes: It may be preferable to use gn_opf to open the device :NUL NQ.WAI (&860E) - Who am I? Notes: For system use NQ.COM (&8612) - read comms handle Notes: It may be preferable to use gn_opf to open the device :COM NQ.IHN (&8615) - read IN handle Notes: It may be preferable to use gn_opf to open the device :INP NQ.OHN (&8618) - read OUT handle Notes: It may be preferable to use gn_opf to open the device :OUT NQ.RHN (&861B) - read direct printer handle Memory Management NQ.MFS (&8900) - read free space information Note: For system use. NQ.SLT (&8903) - slot type enquiry Notes: For system use Director and CLI NQ.DCB (&8C00) - fetch current device In: - Out: BHL - device name (NUL terminated) NQ.DIR (&8C03) - fetch current directory In: - Out: BHL - directory name (NUL terminated) NQ.FNM (&8C06) - fetch current filename match string In: - Out: BHL - name match (NUL terminated) Notes: This refers to the setting of 'NAME MATCH' in the FILER. NQ.DMH (&8C09) - fetch Director special memory handle Notes: This is for use by the INDEX and not by applications! NQ.IN (&8C0C) - read input handle NQ.OUT (&8C0E) - read output handle NQ.PRT (&8C12) - read printer stream handle NQ.TIN (&8C15) - read input-T handle NQ.TOT (&8C18) - read output-T handle NQ.TPR (&8C1B) - read printer-T stream handle NQ.CHN (&8C1E) - read comms handle Specifies The os_sp call is a companion to os_nq and is used to set system parameters, notably the Panel and the PrinterEd settings. A call is made by loading BC with a reason code and the other registers with values specific to that reason code. Register changes and output specifications are different for each reason code. os_sp RST &20 DEFB &69 In: BC - reason code HL - address of data A - size of data Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - return code RC.BAD (&04) - incorrect reason code or parameters RC.UNK (&03) - unknown request Value Size in Meaning bytes System Values PA.GFI (&8000) 0 Go for it! PA.MCT (&8001) 1 Timeout in minutes PA.REP (&8002) 1 Repeat rate PA.KCL (&8003) 1 Keyclick "Y" or "N" PA.SND (&8004) 1 Sound "Y" or "N" PA.BAD (&8005) 1 Default bad process size (in K) Application Values PA.IOV (&8010) 1 Insert/Overtype "I" or "O" PA.DAT (&8011) 1 Date format "E" or "A" (European/American) PA.MAP (&8012) 1 PipeDream map "Y'" or "N" PA.MSZ (&8013) 1 Map size in pixels PA.DIR (&8014) 12 (or less) Default directory string PA.DEV (&8015) 16 (or less) Default device string Serial Values PA.TXB (&8016) 2 (or 1) Transmit baud rate (binary) PA.RXB (&8017) 2 (or 1) Receive baud rate (binary) PA.XON (&8018) 1 Xon/Xoff "Y" or "N" PA.PAR (&8019) 1 Parity "O", "E", "M", "S", "N" Odd, Even, Mark, Space, None Printer Editor Values PA.PTR (&8020) 9 (max) Printer name PA.ALF (&8021) 1 Allow linefeed "Y" or "N" PA.PON (&8022) 31 (max) Printer On Sequence PA.POF (&8023) 31 (max) Printer Off Sequence PA.EOP (&8024) 31 (max) End of page sequence Printer Editor Microspacing Values PA.MIP (&8025) 31 (max) HMI prefix sequence PA.MIS (&8026) 31 (max) HMI suffix sequence PA.MIO (&8027) 31 (max) HMI offset sequence Highlight values - each occupies 4 reason codes PA.ONn (base+0) 31 (max) Highlight ON sequence PA.OFn (base+1) 31 (max) Highlight OFF sequence PA.CRn (base+2) 1 Off at CR "Y" or "N" PA.SPn (base+3) 1 Highlight n special character The printer editor has a facility for overprinting characters, which is most often used to implement underlining on certain printers, by printing a character followed by a backspace and an underline character. The user enters a ? (query), to represent the character being printed, in the ON sequence (the OFF sequence being blank), and this sequence is output for every character with the current character being substituted for the query. Internally the printer editor usually represents the current character as an &FF. (It will show up like this in the printer editor, if a string is installed, updated, and the printer editor is KILLed and re-entered.) However, because it might be necessary to use an &FF in your overprinting sequence it is possible to change the internal representation of the current character to some other value. If you want to use an overprinting sequence you must set up PA.SPn yourself and use your value in the ON string to represent the current character. To implement backspace underlining you might set the ON sequence and special character like this: PA.ON1 255, 8 , 95 ; eg. ?, BS, "_" PA.SP1 255 ; special highlight character The bases for each highlight are as follows: PA.ON1 (&8028) Underline PA.ON2 (&802C) Bold PA.ON3 (&8030) Extended sequence PA.ON4 (&8034) Italics PA.ON5 (&8038) Subscript PA.ON6 (&803C) Superscript PA.ON7 (&8040) Alternate font PA.ON8 (&8044) User Defined Translations: PA.FRn (base+0) 31 (max) Translate from character PA.TOn (base+1) 31 (max) Translate to sequence PA.TR1 (&8048) Row 1, Entry A PA.TR2 (&804A) Row 1, Entry B PA.TR3 (&804C) Row 1, Entry C PA.TR4 (&804E) Row 2, Entry A PA.TR5 (&8050) Row 2, Entry B PA.TR6 (&8052) Row 2, Entry C PA.TR7 (&8054) Row 3, Entry A PA.TR8 (&8056) Row 3, Entry B PA.TR9 (&8058) Row 3, Entry C PA.FRn will normally be one character to long. If there are more characters in this sequence then the then the first character is replaced by the translation string but the others are printed as well. International versions of the machine have an extra page of 28 translations. The reason codes for these translations start at &8080 and continue up to &80B6 in steps of two, as per above. The settings do not come into effect until os_sp is called with the reason code PA.GFI and A=0, which installs both the panel and printer editor values. No error checking is done, so make sure the values written are valid. In addition to the panel and printer editor, the following parameters can be set. SP.DEV (&8C00) set current device SP.DIR (&8C03) set current directory SP.FNM (&8C06) set current name match These three calls expect HL to point to a null terminated string. All the panel and printer editor codes can be used to read values via os_nq in the following manner: os_nq RST &20 DEFB &66 In: BC - PA.xxx A - number of bytes to read DE - buffer for bytes to be read Out: A - number of bytes actually read Interupts Two system calls are available to enable and disbable interupts. It is very important to use these calls and not use the Z80 interupt instructions becuase of a hardware bug in the Z80 which allows for interupts to occur while reading the interupt status. Note that disabling interupts should be avoided if possible and that NMI interupts will still occur. If a HALT is executed when interupts are disabled the system will stop. (HALT should not be used anyway, nor should EI, DE, or IMn.) oz_di - disable interrupts CALL &51 In: - Out: A - old I register Fc - old interrupt status (Fc=1 disabled; Fc=0 enabled). ......BCDEHL/IXIY same AF............../....... different afbcdehl different oz_ei - Restore old interrupt state CALL &54 In: A - old I register Fc - old interrupt status (Fc=1 disabled; Fc=0 enabled). Out: - AFBCDEHL/IXIY same afbcdehl different Thus one might do <oz_di> PUSH AF <operations requiring disabled interrupts> POP AF <oz_ei> and be assured the interrupt status is preserved. Save and Restore The os_sr call was designed for internal use, but has a three rather handy features which can be used by applications: os_sr RST &20 DEFB &6C In: A - reason code BCDEHLIX - arguments Out if call succeeded: Fc=0 Returned values depend on A(in) Out if call failed: Fc=1 A - return code RC.UNK (&03) - unkown request RC.BAD (&04) - bad parameters RC.HAND (&08) - bad handle Reason Codes are as follows: SR.SUS (&01) - save user screen In: - Out if call succeeded: IX - handle for saved screen Out if call failed: Fc=1 A - return code RC.HAND (&08) - this will most probably occur when there is not enough memory to save the screen. Notes: This is a very powerful call, but should not be over used. Most of the time it is perfectly possible to regenerate a screen without resorting to this kind of sledge-hammer approach, requiring around 2Kbytes a time. It is probably preferable to use this call, rather than making your application one which has its screen saved automatically, if there are only a few situations in your application where saving the screen is essential. One final point is that it is essential to check that the call was successful and to try and cope if it fails, as it is likely to do on a unexpanded or heavily used machine. SR.RUS (&02) - restore user screen In: IX - handle for saved screen Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - return code RC.HAND (&08) - handle was not valid SR.WPD (&03) - write parameter data SR.RPD (&04) - read parameter data These are the mailboxing reason codes and are described in section 3 under the heading 'Passing Information between applications' SR.FUS (&05) - free user screen In: IX - handle for saved screen Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - return code RC.HAND (&08) Notes: This performs the function of freeing the memory that was used to save a screen but without actually affecting the screen image. SR.CRM (&06) - card removal SR.CIN (&07) - card insertion These reason codes are for system use only. SR.PWT (&08) - page wait In: - Out if call succeeded: Fc=0 A - character read from the keyboard Out if call was preempted: Fc=1 A - return code RC.SUSP (&69) - process suspended or machine revived RC.DRAW (&66) - process suspended and screen corrupted RC.QUIT (&67) - kill request RC.ESC (&01) - escape condition requested Notes: This call displays the page-wait message at the edge of the screen and then does the equivalant of os_in. It is very important that you check for all the pre-emption codes when using this call (if they are not coped with in an error handling). Some of the most tiresome bugs in the machine were caused by lack of care with the page wait call. One final point to note is that when output is redirected, page waits are conveniently supressed. SR.RND (&09) - random number In: - Out: DEBC - random number Notes: This value only changes when certain operating system functions occur in between calls to os_sr. Repeated fetching a random number may well result in the same value being returned! Fast Code Some operation, such as bank switching, can take up quite a lot of processing time and this can create problems. In order to speed up such tasks the operating system provides a facility for making certain system calls operate more rapidly. The fast code call provides a fragment of code to perform a particular task and places it at an address supplied by the application. The current version of the Z88 has only one fast code routine which is to rapidly switch banks. os_fc RST &20 DEFB &8A In: A=0 - this is in fact a reason code to select the bank switching code C - segment specifier (segment that code is bind bank to) HL=0 - the fast code will end with a RET (ie. use CALL to access fast code) HL<>0 - the fast code will end with a JP to the address (static) defined by HL (ie. use JP to access fast code) Out: Fc=0 A - code size ....BCDEHL/IXIY same AF............/...... different afbcdehl different The bank switching code can now be called by at the address supplied in DE, and has the following interface: In: A - bank to bind into segment (segment specified by os_fc parameter) Out: - AFBCDEHL/IXIY same afbcdehl same Notes: Bank switching is a fairly simple process of updating a softcopy and writing to a hardware register. The reason for using the fast code interface is that on any future versions of the machine the application will still work even though the bank switching process may be different. For this reason at least 30 bytes should be available for the code. Since the actual length is returned memory can be reclaimed if necessary. One final point to bear in mind is that you must not place the fast code in the segment which it is re-binding!

2.20 Direct use of hardware

It is generally good programming practice to use the operating system calls provided rather than manipulating the hardware directly; it avoids compatibility problems with future releases, and treading on the toes of the operating system. We are therefore NOT going to encourage the reader to poke to the screen or disable the operating system! However we shall mention three circumstances where direct manipulation of hardware (by which we mean writing directly to registers in the gate array) may be justified. Bank Switching Firstly, the system calls inevitably require a significant time overhead while they decode their input parameters and perform general housekeeping. This could have an effect on some programs which need to switch banks around a great deal, so we present here the method for binding banks via hardware. It should not be necessary to use this code becuase of the existance of the fast code interface described in section 2.19 This section is included to provide some explanation as to how things work. If you want your code to be compatible with future versions of the machine you should only use the fast code interface. To rebind one of the segments 0-3, the program writes the bank number into the relevant 'segment register'; there are four segment registers, one for each segment. These are addressed by the Z80 i/o ports &D0 to &D3. Thus: LD A,20 ; do not use this code OUT (&D3),A ; under any circumstances will bind segment 3 to bank 20. As mentioned earlier, the segment registers are write-only, and the operating system needs 'soft copies' of the current binding so it can restore the old bindings correctly after it has rebound banks at the start of os calls or interrupt service routines. These soft copies are stored at machine locations &4D0 to &4D3. (In fact &400 to &4FF is devoted to softcopies.) It is VITAL that the desired bindings are stored here BEFORE actually binding the bank; if the bank were bound first, then in the event of an interrupt ocurring immediately after the output instruction, the interrupt service routine would rebind the bank to the contents of the soft copy (assuming it does rebind the relevant segment), and the desired change would be lost. The equivalent of an os_mpb would be: LD BC,&4D0+segment ; address of soft copy LD A,bank ; bank number LD (BC),A ; update the softcopy OUT (C),A ; bind in the bank Similarly, the equivalent of the os_mgb would be simply to read the soft copy: LD A,(&4D0+segment) EPROMs No convenient system call is provided for blowing individual bytes to EPROM, and such a routine will be necessary in Section 3, where we want to be able to create our own custom applications on EPROM. Hence we detail a method for doing so by directly manipulating the gate array registers. To write a byte to EPROM, the relevant physical address must be in slot 3 (ie. banks &C0-&FF). The user first sets up the correct programming signals for the type of EPROM concerned. This is done by writing to the gate array register 'EPR', addressed by i/o port &B3. This may be split up into three zones: Bits 7+6: Length of programming pulse: %00: 4.88us %01: 312.5us %10: 2.5ms %11: 10ms Bits 5-3: Programming signals during 'delay' period Bits 2-0: Programming signals during 'porch' period. The value written to this register should be &48 for a standard 32K EPROM and &69 for a standard 128K EPROM. To enter EPROM-programming mode, the user must set bits 1 and 3 in the 'COM' (command) register addressed by i/o port &B0. The meaning of the lower 6 bits are as follows: Bit 0: Switch display on or off (1=on). Bit 1: Sets Vpp (ie. EPROM programming voltage to slot 3) on or off (1=on). Bit 2: Controls the binding of the bottom 8k of segment 0 (0=bank 0, used when machine is reset; 1=bank 20, which is the normal state). Bit 3: When 1, sets EPROM programming mode - also switches display off. Bit 4: When 1, resets the hardware timer. Bit 5: Multiplies EPROM programming delay by 3 (used in overprogramming - see later). Bit 6: Speaker control (see below) Bit 7: Speaker control The program should then write to the relevant address, as normal. The normality is only from the point of view of the program - in fact writing to a slot 3 address while in EPROM programming mode causes the processor to be suspended and BLINK to take over while the byte is programmed. Next, reset the above two bits to exit EPROM programming mode. The byte should then be checked to see if it has been written, and if not, EPROM mode reentered and another attempt made to blow the byte. No more than 75 attempts should be made to blow the byte. Once verification is successful, the byte should be overprogrammed - ie. bits 1,3 and 5 should be set in the 'COM' register and the byte written as many times as it took to program in the first place. A typical code fragment to blow a byte in C to logical address (HL) - assumed to be bound to an EPROM address - is reproduced below. Note that when the filer writes to the EPROM it does so by blowing blocks of bytes, rather than writing one byte at a time, and is subsequently a great deal faster. LD B,75 ; Maximum number of attempts LD A,&48 ; For 32K EPROMs OUT (&B3),A ; Set EPROM programming signals .proloop LD A,&0E OUT (&B0),A ; Set Vpp and PROGRAM bits LD (HL),C ; Write byte LD A,&04 OUT (&B0),A ; Reset Vpp and PROGRAM bits LD A,(HL) CP C ; Verify byte JR Z,byteok DJNZ proloop < give an error - unable to write byte> .byteok LD A,75 SUB B LD B,A ; Same number of times it took to program .ovploop LD A,&2E OUT (&B0),A ; Set Vpp, PROGRAM and OVERPROGRAM LD (HL),C ; Write byte LD A,&04 OUT (&B0),A ; Reset Vpp, PROGRAM and OVERPROGRAM DJNZ ovploop ; Repeat LD A,&05 OUT (&B0),A ; Turn screen back on. RET Serial Lines It is sometimes desirable to explicitly manipulate the RTS line of the serial interface (eg. when autodialling modems). This may be done by manipulating bit 3 of the 'RXC' (receiver control) register in the gate array, addressed by i/o port &E2. It is again important that the soft copy is updated first. The bits have the following meaning: bit 7: Short word select (insert extra bit to align data). bit 6: Loop-back (connect transmit to receive). bit 5: Reset UART. bit 4: Set auto-RTS mode. bit 3: Toggle RTS line. bits 2-0: Baud rate. Bit 3 is XORed with the normal state of the RTS line, as defined by receive data register full and auto RTS. This bit therefore toggles the RTS line. A code fragment to achieve this might be: LD BC,&04E2 ; address of soft copy LD A,(BC) ; fetch old value OR 8 ; or AND 247 to reset LD (BC),A ; update soft copy OUT (C),A ; update hardware The TxD (transmit data) line can be inverted by changing the state of bit 3 of the transmit control register (i/o port &E4, softcopy &4E4) in a similar way to the RTS line. The value of the RxD line (receive data) can be found by reading bit 4 of the extended receive data register (i/o port &E1, softcopy &4E1 (the softcopy will not necessarily be up to date). Speaker The top two bits of the COM register (i/o port &B0, soft copy &4B0) control what signal is attached to the internal speaker: Bit 7 Bit 6 Effect 0 0 Speaker line low 0 1 Speaker line high 1 0 Speaker line oscillates at 3200 Khz 1 1 Speaker line attached to Tx data (Tx data still output to comms. port) Unused I/O ports The i/o ports &F0 to &FF are not used by the system and so are available to hardware devlopers. The softcopy page (&400) is entirely reserved for i/o port use and should be used. (You must not use this memory for any other purpose.) Remember that when updating a softcopy it must always be done before updating the port because of the possibility of an interupt occuring and receiving bogus information about the state of a port. If you do want to extra i/o ports then please liase with Cambridge Computer Ltd in order to avoid potential clashes with other hardware devlopers. Generating External Interupts Sadly, it is not viable for external hardware to generate interupts with which the Z88 can deal.

Section 3 - How to write an application


The most unusual facet of the Z88 is its ability to deal with many
applications and suspend and reactivate them as required. An
application may be set up on a ROM or EPROM card and inserted into one of
the slots at the front of the machine. If the appropriate data structures
are set up on the card, then the operating system can make application(s)
appear like the built-in programs, such as Pipedream.
There are essentially two types of application, good and bad. Most
applications are 'good': they only take RAM as they need it, using the
memory management routines, and do not demand more than 256 bytes of
contiguous RAM. There is also a facility for 'bad' applications which
demand a larger contiguous address space, up to a full 64K Z80 address
space - this was designed in order to be able to run standard BBC BASIC
which was included as a binary image designed for a normal Z80 system.
There is also an 'ugly' application type, which is similar to 'bad' but
killed on pre-emption, ie. a bad popdown. None of the inbuilt
applications is of this type.
Applications are set up as linked lists of DORs (see section 2.15), one
list for each ROM card (including the internal one in Slot 0). The
application DORs contain such vital information as where the application
entry point is and whether caps lock should be set on entry; more of this
below. The root of each link is the 'front DOR' (geddit?). The first
application is linked as a son to this DOR, and subsequent applications
are linked together as brothers. For instance, in the internal ROM, the
arrangement of application DORs is as follows:


 FRONT DOR
       |
       |
       |
   INDEX ---- DIARY ---- PIPEDREAM . . . . . . TERMINAL ---- IMP/EXP
   DOR        DOR        DOR                   DOR           DOR


3.1 Application data structures

When the operating system searches for applications (eg. when it wants to set up the index display), it looks at each slot in turn, starting at slot 0 (internal). In the case of an external slot (1...3) it looks at the card header. If it contains "OZ" (uppercase) at the very top of the slot's memory, then the card is taken to be a ROM or application EPROM, otherwise it is ignored. For filing system EPROMs 'oz' is is placed in the top two bytes. The full header which a ROM card should incorporate right at the top of its address space is as follows. Addresses are given as offsets within the top bank, and any physical addresses contained in the below data structure should use bank numbers RELATIVE to the bottom of the card, so that the card will work in any slot. For instance the top bank is referred to as bank &3F. &3FF8 %0xxxxxxx These 4 bytes form a card ID code &3FF9 %0xxxxxxx This should be unique to the card - &3FFA %0xxxxxxx apply to Cambridge Computer Ltd for an &3FFB %1xxxxxxx ID for a commercially released card. &3FFC &02 Size of card in banks (&02 for a 32K card) &3FFD &00 Subtype of card (future expansion) &3FFE 'OZ' (&4F, &5A) ROM / application EPROM header. As detailed above, the card ID must be obtained from Cambridge Computer Ltd. If two commercially released cards shared an ID, then inserting both into the machine could cause serious problems for the card manager, and it is most important that this should not happen. The bits explicitly given above will be true for all external IDs - alternative settings of these bits are reserved for future internal applications. Next, the card manager expects to find the application front DOR starting in the top bank of the slot, at bank offset &3FC0. This section should be identical for all application cards, apart from the address of the first application DOR. 3 bytes 0 Link to parent (ie. none) 3 bytes 0 Link to brother (this may point to the Help front DOR) 3 bytes x Link to son (this should be the physical address of the first application DOR). 1 byte &13 DOR type (ROM front DOR) 1 byte 8 DOR length 1 byte 'N' Key for name field 1 byte 5 Length of name and terminator 5 bytes 'APPL',0 Null-terminated name 1 byte &FF DOR terminator. The 3 byte links consist of a 2 byte offset within a bank (low byte, high byte) followed by a bank number. Bank 0 should be avoided, because of the special meaning of the null pointer. It is recommended that all the application header information, including menus and help, are placed in the top one or two banks. Note that the space between the application front DOR and the card header above it should be padded with zeros. Note: The bank numbers should be offset from the top bank. ie. the top bank should be &3F, the one below &3E etc. In a 128K EPROM the lowest bank should be thought of as &38 and not as &00. The application DOR(s) may then be placed wherever is convenient, and if there is more than one, chained together via the brother links. An application DOR contains various information relevant to running the application, and may contain menu and help information as well. The format of an application DOR is as follows: 3 bytes 0 Link to parent (none) 3 bytes x Link to brother (this will be 0 if this is the only application on the card) 3 bytes 0 Link to son 1 byte &83 DOR type (application ROM) 1 byte x Total DOR length 1 byte '@' Key to info section 1 byte Length of info section 2 bytes 0 Reserved for future expansion 1 byte 'x' Application key letter, ie. the letter used in the square sequence to enter the application. If several applications share a letter, then the first is entered by [] x, the next by [] Zx, the next by [] ZZx, the next by [] ZZZx, etc. 1 byte x Contiguous RAM size required, in 256-byte pages (eg. 32 for 8K). This is relevant only to bad applications and must be between 8K and 40K (ie. this byte ranges from 32 and 160). See the section below on bad applications. Use 0 to make your application the deafult size. 2 bytes 0 Estimate of environment overhead, ie. how much memory is needed to save the state of the application each time it is exited. The MOS will attempt to 'learn' from experience how much is actually required, and this is best set to zero. 2 bytes x Unsafe (ie. not preserved over pre-emption) workspace size required (see below). 2 bytes x Safe (ie. preserved over pre-emption) workspace size required. These workspaces are allocated from the system half of segment 0, downwards from &1FFD, safe workspace at the top. They are used when it is important that the workspace is not bound to awkward segments, but rather remains stable in the logical address space. 2 bytes x Entry point for application code (logical). Must be in segment 3. 1 byte x Desired binding of Segment 0 on entry 1 byte x Desired binding of Segment 1 on entry 1 byte x Desired binding of Segment 2 on entry 1 byte x Desired binding of Segment 3 on entry . If the application is a bad one, then certain segments will be bound to RAM on entry, depending on the size of contiguous RAM selected (see above). The desired binding of such segments must be set to zero to avoid conflict (zero binding is regarded as null and ignored). 1 byte x Application type byte 1. Set it to a combination of the following bits. Note the bits are not all orthogonal, eg. if 4 is set 8 must be too. 1 Good application 2 Bad application 4 Ugly application (bad popdown) 8 Popdown (kill on pre-emption) 16 Only one instantiation of this application is allowed (eg. Diary). 32 MOS should attempt to preserve screen over pre-emption. This should not be set unless absolutely necessary, ie. when the application cannot reasonably redraw its own screen in response to an RC.DRAW return. Programmer laziness is not sufficient grounds! The memory overhead involved will make the application virtually unusable on an unexpanded machine. 64 File manager ID (for internal use - do not set!). 128 Application should auto-boot. This would be very dangerous to set. Also remember that the default command input could be BOOT.CLI which may not suit the application. 1 byte x Application type byte 2; given by some combination of: 1 Set CAPS lock on entry 2 Set inverted CAPS lock on entry (should not be set without 1 set too or the effect is undefined). 128 Ignore error returns - internal use only, DO NOT SET. 1 byte 'H' Key to help section 1 byte Length of help section 3 bytes xxx Pointer to topics (see later) 3 bytes xxx Pointer to commands (see later) 3 bytes xxx Pointer to application help (see later) 3 bytes xxx Pointer to token base (see later) If no menus, help or diamond sequences are required then the above should all point to three zeros. 1 byte 'N' Key to name section 1 byte x Length of name x bytes "xxxxx",0 Null-terminated application name. If this too long then it will look rather odd in the Index. 12 characters is about the maximum sensible length. 1 byte &FF DOR terminator byte. Menu, Topic, Help (MTH) Each application can have a set of 'topics' associated with it. These topics apppear in the topic window on the left hand edge of the screen, below the application name. Normally all the topics are in grey but when the menu key is pressed the a topic name is highlighted and a menu display is brought up. The menu consists of up to three columns of commands, which may be selected with a highlight bar or by using a special keyboard sequence also shown on the menu. Each topic and each command may have a page of help text associated with it and in addition the application itself may have a page of help text, perhaps for a general description of its function. An extra information topic, not displayed in the topic window, may be added to provide access to a series of help pages which are not directly associated with keyboard commands. Topic information The 'pointer to topics' is of the form of a 2 byte offset in a bank, followed by a bank number. The pointer should point to a series of topic definitions arranged like this: 1 byte 0 Start marker x bytes <first defn> x bytes <second defn> . . . x bytes <last defn> 1 byte 0 End marker. Where each definition has the following form: a 1 byte b-a length byte n bytes 'xxxxxxxx' Topic name 1 byte Optional nul terminator (see below) 2 bytes xx Optional pointer to topic help page, if any. This is a 2-byte pointer relative the start of the help text. Note: it is stored High byte then low byte ie. the reverse of the conventional order. 1 byte x Topic Attribute byte 1 byte b-a length byte b The topic attribute byte: bit 0 set for an 'An' topic eg. 'An Edit topic' (default is 'A') bit 1 set for an information topic. (see below) bits 2-3 should be 0 bit 4 set if topic entry has a pointer to a help page bits 5-7 should be 0 The topic name terminator can simply be a null, but in fact any control character (ie. &00-&1F) will serve perfectly well. Therefore it is possible to use the attribute byte, or the high byte of the help pointer (hence the high byte, low byte ordering - although this won't apply if the help text length is more than 8K!) to terminate the name. The system looks at the attribute and help pointers by counting back from the end of the topic entry, so there is no confusion between a terminator and an attribute byte or help pointer. Command Information The 'pointer to commands' is of the form of a 2 byte offset in a bank, followed by a bank number. The pointer should point to a series of command definitions arranged like this: 1 byte 0 Start marker x bytes def(1,1) First command of first topic x bytes def(1,2) Second command of first topic . . . x bytes def(1,x) Last command of first topic 1 byte 1 End of this topic x bytes def(2,1) First command of first topic . . . x bytes def(2,x) Last command of second topic 1 byte 1 End of this topic . . . . . . x bytes def(x,1) First command of last topic x bytes def(x,2) Second command of last topic . . . x bytes def(x,y) Last command of last topic 1 byte 0 End of all topics. Each of these definitions should have the following form: a 1 byte b-a Length byte 1 byte x Command code (see below) x bytes 'xxxx',0 Null-terminated keyboard sequence for command. x bytes 'xxxx' Command name. The total length of the name and keyboard sequence must be 25 characters or less. Any control characters which you need to use must be brought in via tokens. 1 byte 0 Optional nul terminator (see below) 2 bytes xx Optional pointer to command help page, if any. This is a 2-byte pointer relative the start of the help text. Note: it is stored High byte then low byte ie. the reverse of the conventional order. 1 byte x Command attribute byte 1 byte b-a Length byte b The command attribute byte: bit 0 set to make this entry the first in a new column. Note: the last entry in a column has no special attribute. bit 1 should be set to 0 bit 2 set to make a hidden entry. The command name and key sequence will not appear on the menu display. This is useful for commands which need to key sequences. eg. in PipeDream <>TAB is a hidden entry which does the same thing as <>CFC, by having the same command code. Hidden entries may have Help text, but this text will never be displayed. Note: A hidden entry must never be the last in a topic. If it is the Help system will not be fully traversable and the system will crash if the user moves through help in a certain way. bit 3 set to make safe. Setting this bit forces to use the user to use the keyboard sequence rather than selecting a the command with the highlight bar in the menu display. This is used by the system for the Index commands <>KILL and <>PURGE. An attempt to access these commands through the highlight bar results in a double bleep. These entries must have their command name in tiny text. This has to be done using a token, since control characters will terminate a command name. bit 4 set if command entry has a pointer to a help page. bits 5-7 should be set to 0 Note: The same trick of treating the attribute byte, or high byte of the help pointer, as a terminator can be used in menu entries. The command name terminator simply needs to be in the range (&00-&1F) The format of the keyboard sequence is as follows: For a conventional diamond sequence, simply use the upper case letters of the sequence. eg. for the PipeDream new command the entry would be the following bytes "BNEW",0 For commands using other keys the keyboard decoder codes are used. For example SHIFT up arrow is represented by a key sequence consisting of one byte, &FB and the terminator, &00. The system automatically generates the graphic sequence from this code. There are some exceptions: ESC &1B SPACE &E0 ENTER &E1 TAB &E2 DEL &E3 When an application reads the keyboard, with os_in or os_tin, then valid keyboard sequences are intercepted so instead of returning the raw characters, the routine will return a zero, indicating a command (the NUL can never be read in as a normal character). The next keyboard read will then return the relevant command code as specified in the above definition. (The system input line routine also returns command codes, although in a different way - see section 2.7 for details.) One way of dealing with this behaviour is for the user to set up a 'read character' routine which will return normal characters without change, but intercept and obey command sequences. The following code fragment indicates a possible arrangement: os_in =&27 rdch RST &20 DEFB os_in ; Perform 'os_in' RET C ; Return if end of file etc. OR A RET NZ ; Return if charater not NUL RST &20 DEFB os_in ; Another 'os_in' OR A RET Z ; Return if a NUL has been read <deal with command A> Help Information The 'pointer to application help' is of the form of a 2 byte offset in a bank, followed by a bank number. The pointer should point to a series of help pages definitions arranged like this: n bytes help page for the application (or 0 for a blank page) n bytes help entry (see below) . . . n bytes last help entry Each help entry will produce one page of help text. It consists of a series of lines, where DEL (&7F) is used for newline, terminated by a NUL (&00). The DEL character is used for newline to facilitate embedding screen driver codes into the help text. For example you can use special symbols (eg. the ENTER graphic (sequence 1, 225)), arrow symbols or box characters. The help page is displayed with centred justification switched on, but you could include a command to switch off centering in your help strings. This should only be done where necessary, to avoid disrupting the the overall style of the help pages. The pointer to the help text, which is an offset from the base of the help text, defined in the application DOR, in topic and command entries, should point to the first character of the first line. A help entry might look like this: helppage DEFM "This is a simple help page" DEFB &7F DEFM "and this is the second line" DEFB 0 Token Information The 'pointer to token base' is of the form of a 2 byte offset in a bank, followed by a bank number. The token table format is shown below. base 1 byte Recursive token boundary (1-127) 1 byte Number of tokens (1-127) 2 bytes first-base pointer to first token 2 bytes second-base pointer to second token . . 2 bytes last-base pointer to n th token 2 bytes end-base pointer to byte after n th token first n bytes first string (no terminators) second n bytes second string . . . last n bytes last string end Note: the token table must not be allowed to cross a bank boundary. Tokens may be embedded in the help text or within the text for topic names and command names. The first token has a code of &80 and subsequent tokens count up from here. Recursive tokens may contain tokens in their text. Tokens above the boundary level set by the 'first recursive token' may contain tokens themselves, providing those tokens are below the boundary. For example: tok_base 1 byte &04 Recursive token boundary 1 byte &05 Number of tokens 2 bytes tok0-tok_base 2 bytes tok1-tok_base 2 bytes tok2-tok_base 2 bytes tok3-tok_base 2 bytes tok4-tok_base 2 bytes end-tok_base tok0 4 bytes 'file' tok1 5 bytes ' the ' tok2 5 bytes 'EPROM' tok3 2 bytes &01,'T' Token to toggle tiny text mode tok4 4 bytes ' ',&80,'s ' Token for "files " end Information Topics The information topic is selected by pressing HELP from within the application. If the topic is present then a menu of commands will be brought up. In an application with no other topics, pressing MENU will bring up the information topic name in the topic window. The command entries for an information topic differ from ordinary commands in that there is no keyboard sequence associated with them. The command entries should look like this: a 1 byte b-a Length byte 1 byte 0 Null command code x bytes 0 Null keyboard sequence x bytes 'xxxx' Command name. The total length of the name must be 25 characters or less. 1 byte 0 Optional terminator (see above) 2 bytes xx Pointer to help page 1 byte x Command attribute byte 1 byte b-a Length byte b The attribute byte should always be &10 (help page) or &11 (help page and first entry in a new column). Help Front DOR It is possible to use the brother link in the front DOR, at &3FC0, to point to a Help front DOR. The front DOR looks like this: 3 bytes 0 Link to parent 3 bytes 0 Link to brother 3 bytes x Link to son ( this points the first Help DOR) 1 byte &13 DOR type (application front DOR) 1 byte 8 DOR length 1 byte 'N' Key for name record 1 byte 5 Length of name and terminator 5 bytes 'HELP',0 Null terminated name 1 byte &FF DOR terminator A Help DOR entry looks like an application DOR entry, but without the information record: 3 bytes 0 Link to parent (none) 3 bytes x Link to brother (points to another Help DOR or set to zero) 3 bytes 0 Link to son 1 byte &83 DOR type (application ROM) 1 byte x Total DOR length 1 byte 'H' Key to help section 1 byte Length of help section 3 bytes xxx Pointer to topics 3 bytes xxx Pointer to commands 3 bytes xxx Pointer to application help 3 bytes xxx Pointer to token base 1 byte 'N' Key to name section 1 byte x Length of name x bytes "xxx",0 Null-terminated application name. Must be the name of an existing application. 1 byte &FF Special terminator byte. The Help in a Help DOR takes priority over the Help in the application DOR, but for ROM applications it is not necessary to use a seperate DOR. The use of the Help DOR is mainly in providing Help for internal applications or applications in other ROM cards. If you intend to provide Help for an internal application you will need to provide all the same command codes for the commands. Therefore, it will be necessary to examine the structure of the internal application carefully. This is readily achieved by opening the application, with gn_opf and A=6 to get a DOR handle, and reading the addresses of the Help structures out of the Help record. Limitations and Conventions There are some limitations and conventions regarding menu topic and help; the limitations are: 1) Diamond sequences should consist of no more than 5 characters, although these may be special characters like arrow keys and delete. 2) The first possible match will be selected, so if a diamond sequence 'AB' exists, there is no point having 'ABC' as it cannot be selected. 3) No command code should be zero, as two zeros from 'os_in' are used to represent NUL. Note also that the space, TAB, ENTER and the arrow keys all return command codes and you must make sure you do not clash with these. See section 2.7, on the Keyboard Driver, for details of these codes. 4) There should be no more than 7 topics per application and no more than 24 commands per topic. Each command name should consist of no more than 25 characters. 5) If any commands in a topic have help, then it is strongly recommended that all the topics have help, even if this is just a blank page (set the help pointer to point to a NUL) and the topic itself should have help. and the conventions are: 1) Make the first letter of a diamond sequence the first letter of the topic name (eg. Pipedream has BNEW in the Blocks topic). 2) If topic names coincide with the internal ones, then the order should be the same, eg. CURSOR before FILES. 3) Commands associated with the arrow keys should be in the order: Right, Left, Up and Down. Bad Applications An application can be made 'bad' (or a popdown 'ugly') if it requires a large area of contiguous memory since the Z88 cannot allocate more than 256 bytes to an application at any time. Although this appears to be very restrictive most programs can be made to work under this regieme. All the internal applications, bar BBC BASIC, are 'good', and BBC BASIC is only bad because it was not written for the Z88 but simply imported. If, however, you are simply unable to sensibly write an application without large contiguous areas, you may have to make your application bad. Bad application memory is provided from &2000 upwards. The amount given depends on the value of the 'Contiguous RAM size' byte in the application header. If this is set to zero then the default value is used (40K in an expanded machine, 8K in an unexpanded machine). The value of the default can be changed, via the PANEL, but bear in mind this is something which effects the whole system, and should not be undertaken lightly. When a bad application is first entered IX points to an information block as follows: 1 byte &03 Number of information bytes 1 byte xx Start page of contiguous memory (will be &20) 1 byte xx End page of contiguous memory (this page can't be used). eg. &C0 for a 40K bad application 1 byte xx Usually zero. Reserved for future expansion. Whenever BASIC is exited an area of unused memory, between the program and the stack, is given back to the system. When BASIC is re-entered memory is found to fill this hole back up again. It is this mechanism that allows several 40K BASIC's to coexist on a 128K Z88. All the BASIC's have a 40K contiguous space while they are active and running, but when inactive they occupy considerably less memory. To give memory back to the system the enquiry entry point is used. The enquiry entry point is three bytes above ther application entry point and must be in segment 3, which effectively forces all entry points into segment 3. Normally the enquiry entry point should simply return with the Fc=1. However, if Fc=0 then the memory starting from the address in BC and ending at the address in DE (not including the byte at (DE)) is given back to the system. The following code might be used: app_entry JP main_entry ; this instruction is 3 bytes long enquire_entry LD BC, (hole_start) LD DE, (hole_end) OR A ; clear carry RET Bad applications not giving memory back to the system (and all other applications) should use the following code for the enquiry entry point: enquire_entry SCF RET Bad applications place a lot of strain on the system, by using large areas of memory. A 40K bad application which gives no memory back to the system is unlikely to coexist well with other applications, so it is strongly recommended that if you musr write bad applications you give memory back to the system. It is almost always possible to design data structures so that a contiguous block can be kept empty. Programmer laziness is not an excuse to use a 40K bad application by default! Passing Information between Applications The Z88 provides a facility for applications to pass on information to the next application started up. This is used by PipeDream and the Diary to read the name of a marked file from the Filer. The way this works is that the Filer drops the address and length of the name of the last marked file into a mailbox from time to time. When the Filer is exited (eg. via ESC) the system looks at the mailbox and if there is something there it copies the information to a safe area of memory. When another application is started it can ask the system if there is any mail. All mail is tagged by a name, which describes the type of information, and an application must give the system the name of the information it is after. If information is present then the system copies it from the safe area into an area of the application's memory. The interface for mailboxing is provided by the os_sr call: os_sr RST &20 DEFB &6C In: A = SR.WPD (&03) - write parameter data DE name of information type (null terminated) or zero to emty mailbox BHL - address of information C - length of information Out: Fc=0 - successful and In: A = SR.RPD (&04) - write parameter data DE - name of information type (null terminated) BHL - address of buffer to for information C - maximum size of buffer Out if successful: C - number of bytes actually present Out if call failed: Fc=1 - information type is not present The system uses two information types, filenames and dates. These have the information type names of 'NAME' and 'DATE' (both null terminated). If you transfer information of one of these types then you must use the appropriate name otherwise the internal applications will not be able to collect your mail. Similarly you won't be able to receive mail from the Filer, Calendar, Diary etc. unless you try and read mail with these names. If do use the mail system please inform Cambridge Computer Ltd of the information type name you use (which should be a mnemonic upper case alpha string), so that other applications can share data with yours. The internal applications use mail as follows: Filer exports filenames Diary imports filenames, imports dates, exports dates PipeDream imports filenames Calendar imports dates, exports dates Alarm imports dates Envrionment on Entry to an Application When you enter an application the stack pointer is already established and you will have a stack space of the order of 1.5K The memory used for the stack is a shared resource, however. The 2K space between &1800 and &2000 is used for the following purposes: 1. As the system stack. This will usually be quite lightly used, but can be quite big in some circumstances. 2. For the safe and unsafe workspace which applications request. The bigger these are, the less stack you will have. 3. For storing mailbox information between application switches. If a lot of length mail is being passed around, the maximum stack size is reduced. We recommend that mail is restricted to a maximum length ot 64 bytes. Even with all these demands it is unlikley that you will have much less than 1.5K, but clearly if you might need a larger stack you will need to make your own arrangements, such as allocatiing memory and implementing your own stack procedures, or if really necessary using a bad application to provide contiguous memory for a stack. If you change the stack pointer within you application you must save the old stack pointer and restore it before making any system calls. (This is what BASIC has to do.) If your application requires access to other banks in your card then you can use the bank switching call, os_mpb, to bind them in. However, your card could be placed in any of three slots so you need to determine where your card is. This can be simply done by using os_mgb while running ROMed code and then using bank offsets relative to this value.

3.2 An example application

We present here a BASIC program which blows into an EPROM card two applications and external help for an internal application. The program simply needs to be RUN and a blank EPROM (ie. fresh from an eraser and not examined by the filing system) put into slot 3. The program asks you to type 'Yes' and ENTER before it actually begins to blow the code. When blowing is complete (it takes around 3 minutes in all) the Z88 bleeps until you press space. At this point you must enter the Index, open the flap and remove the card. Now close the flap again. This must be done before the card is used otherwise the card manager becomes confused, since you have effectivly inserted a new card without using the flap. The program provides a reasonable developement enviroment for applications and you are welcome to use it if you want to develop software on the Z88. If you do you will probably need to modify it to blow files into the EPROM since only a very small application source and object will fit within a 40K BASIC. We would recommend using a development system based on a bigger machine, with a full screen and a macro cross assembler. Blowing EPROMs can be done using a conventional EPROM programmer and a special adaptor to connect a Z88 EPROM card to a DIL socket. Such an adaptor is available from Cambridge Computer Limited. The macros used in this program should be fairly easy to reproduce. One other tool, which is included in the program, is a BASIC line editor, which uses the CLI. This is essential if you are developing large programs in BASIC. The card facilities are as follows: EP-Fetch This is a good Popdown which facilitates fetching files from EPROM. A window is displayed with up to 7 EPROM files and their lengths in bytes. A highlight bar can be moved up and down the list, causing scrolling when it reaches the top or bottom of the window. The highlighted file can be fetched by striking ENTER, at which point you are prompted for a filename, with the input line initially containing the EPROM filename. The default device will be that set by the PANEL. A nice feature of the program is that old versions of files can be also be fetched. Finally, the program attempts to check for a valid EPROM, by looking at the header for the EPROM identifier 'oz' See Appendix F for the internal format of an filing system EPROM. Useless This is a good application which simply loops on every keypress and indicates whether it has detected a menu command. The purpose here is to a) demonstrate a card with more than one application and b) to show hidden and safe commands in operation. Note that the read character routine (rdch) used by 'Useless' and 'EP-Fetch' is a fudge in that it does not distinguish between commands, which are zero prefixed, and control codes. eg. <>B, in 'Useless' has the same effect as <>ALIAS or TAB (TAB is the alias, or to <>ALIAS) . Commercial applications must not allow this ambiguity. External Help for Imp-Export An information topic is provided, via a Help front DOR, for the import export popdown. 10 REM some definitions 20 null$="" 30 h$=CHR$(&7F) 40 s$=CHR$(1) 50 cr$=CHR$(13) 60 n$=CHR$(0) 70 selc$=CHR$(1)+"2C" 80 selh$=CHR$(1)+"2H" 90 ix_hlp_bas=0:app_hlp=0 :REM fix for help macro 100 gn_sip=&3809 110 gn_opf=&6009 120 gn_cl=&6209 130 os_mv=&45 140 gn_rbe=&3E09 150 gn_nln=&2E09 160 gn_pdt=&1209 170 gn_pdn=&1209 180 os_bye=&21 190 gn_sop=&3A09 200 os_mpb=&5D 210 os_in=&2A 220 os_out=&27 230 os_pb=&3C 240 os_erh=&75 250 os_esc=&6F 260 gn_err=&4A09 270 rc_quit=&67 280 rc_draw=&66 290 rc_esc=&01 300 rc_susp=&69 310 DIM code 5000 320 list=0 :REM set to 1 for a listing 330 FOR pass=4 TO 6+list STEP 2+list 340 P%=code 350 O%=code 360 [ OPT pass 370 \ 380 LD HL,0 \set up a safe stack 390 ADD HL SP 400 LD SP (&1FFE) 410 PUSH HL 420 CALL lex 430 POP HL 440 LD SP,HL 450 RET 460 \ 470 \ 480 \Main routine - write a block into a bank of the EPROM 490 .lex 500 LD A,(bank) 510 OR &C0 520 LD B,A 530 LD C,3 540 OPT FNsys(os_mpb) \bind EPROM to bank 3 550 PUSH BC 560 LD DE,(off_in_bank) 570 LD HL,(start) 580 LD A,D 590 OR &C0 600 LD D,A 610 CALL write_block 620 POP BC 630 OPT FNsys(os_mpb) 640 RET 650 \ 660 \ 670 \ 680 \Write a byte to the EPROM 690 .write_block 700 PUSH BC 710 LD C,A 720 LD B,75 ; Maximum number of attempts to blow byte 730 LD A,&48 ; Set EPROM programming signals 740 OUT (&B3),A ; (for 32K EPROMs only - use A=&69 for 128Ks) 750 .proloop 760 LD A,&0E 770 OUT (&B0),A ; Set Vpp and PROGRAM bits 780 CALL epr_wrt 790 CALL epr_vfy 800 JR Z,byteok 810 DJNZ proloop 820 .byteok 830 LD A,&04 840 OUT (&B0),A ; Reset Vpp and PROGRAM (not screen) 850 LD A,75 860 SUB B 870 LD B,A ; Overprogram byte the same number of times 880 .ovploop 890 LD A,&2E 900 OUT (&B0),A 910 CALL epr_wrt 920 LD A,&04 930 OUT (&B0),A 940 DJNZ ovploop 950 LD A,&05 ; Turn the screen 960 OUT (&B0),A ; back on again 970 POP BC 980 XOR A 990 LD (exit),A 1000 RET 1010 \ 1020 .epr_wrt ; copies a block of bytes 1030 PUSH BC 1040 PUSH DE 1050 PUSH HL 1060 LD BC,(length) 1070 LDIR 1080 POP HL 1090 POP DE 1100 POP BC 1110 RET 1120 \ 1130 .epr_vfy ; verifies a block of bytes 1140 PUSH BC 1150 PUSH DE 1160 PUSH HL 1170 LD BC,(length) 1180 .vfy_loop 1190 LD A,(DE) 1200 CP (HL) 1210 JR NZ,vfy_exit 1220 INC DE 1230 INC HL 1240 DEC BC 1250 LD A,B 1260 OR C 1270 JR NZ,vfy_loop 1280 .vfy_exit 1290 POP HL 1300 POP DE 1310 POP BC 1320 RET 1330 \ 1340 \Application data structures 1350 .org4000 1360 DEFB 0 1370 .app_dor 1380 DEFW 0:DEFB 0 \link to parent 1390 DEFW app2_dor-org4000:DEFB &3F \link to brother 1400 DEFW 0:DEFB 0 \link to son 1410 DEFB &83 \application DOR 1420 DEFB axz-axa 1430 .axa 1440 DEFB ASC"@" \information record 1450 DEFB axj-axi 1460 .axi 1470 DEFW 0 \reserved 1480 DEFB ASC"M" \application key letter 1490 DEFB 0 \contiguous RAM 1500 DEFW 0 \environ overhead estimate 1510 DEFW 0 \unsafe workspace 1520 DEFW ep_safe_ws \safe workspace 1530 DEFW &C000 \Entry point 1540 DEFB 0 \ Segment 0 1550 DEFB 0 \Segment 1 1560 DEFB 0 \Segment 2 1570 DEFB &3E \Segment 3 1580 DEFB 8 \popdown 1590 DEFB 1 \CAPS LOCK on entry 1600 .axj 1610 DEFB ASC"H" \help record 1620 DEFB 12 \length 1630 DEFW app_top-org4000:DEFB &3F \topic pointer 1640 DEFW app_com-org4000:DEFB &3F \commands pointer 1650 DEFW app_hlp-org4000:DEFB &3F \help pointer 1660 DEFW tok_bas-org4000:DEFB &3F \token pointer 1670 OPT FNname("EP-Fetch") \name of application 1680 DEFB &FF \end of DOR 1690 .axz 1700 \ 1710 ] 1720 hb=app_hlp :REM fix the macro 1730 [ OPT pass 1740 .app_top 1750 DEFB 0 \start of topics 1760 OPT FNtopic("Commands",0,0) \ordinary topic with no help 1770 DEFB 0 \end of topics 1780 \ 1790 .app_com 1800 DEFB 0 1810 OPT FNcommand("Fetch a file",CHR$(&E1),13,0,0) 1820 OPT FNcommand("Re-scan "+CHR$(&82),CHR$(&D1),2,0,0) 1830 OPT FNcommand("Escape",CHR$(&1B),1,0,0) 1840 OPT FNcommand("Previous file",CHR$(&FF),&FF,0,1) \new column 1850 OPT FNcommand("Next file",CHR$(&FE),&FE,0,0) 1860 DEFB 0 \end of commands 1870 \ 1880 .app_hlp 1890 DEFB &7F 1900 DEFM "This is a utility to make fetching"+CHR$(&84) 1910 DEFB &7F 1920 DEFM "from "+CHR$(&82)+"s rather easier. Old versions of" 1930 DEFB &7F 1940 DEFM CHR$(&84)+"are shown in "+CHR$(&83)+"tiny text"+CHR$(&83)+" and" 1950 DEFB &7F 1960 DEFM "preceeded by --" 1970 DEFB 0 1980 \ 1990 .tok_bas \token table 2000 DEFB 4 \make token 5 recursive 2010 DEFB 5 \5 tokens in all 2020 DEFW tok0-tok_bas 2030 DEFW tok1-tok_bas 2040 DEFW tok2-tok_bas 2050 DEFW tok3-tok_bas 2060 DEFW tok4-tok_bas 2070 DEFW end-tok_bas 2080 .tok0 DEFM "file" 2090 .tok1 DEFM " the " 2100 .tok2 DEFM "EPROM" 2110 .tok3 DEFB 1:DEFM "T" \token for tiny text 2120 .tok4 DEFM " "+CHR$(&80)+"s " 2130 .end 2140 \ 2150 .hlp_fdor \Help front DOR - for external help 2160 DEFW 0:DEFB 0 \no parent 2170 DEFW 0:DEFB 0 \no brother 2180 DEFW hlp_dor_ix-org4000:DEFB &3F \son is a help DOR 2190 DEFB &13 \ROM type 2200 DEFB hfz-hfa \length 2210 .hfa 2220 OPT FNname("HELP") \name 2230 DEFB &FF 2240 .hfz 2250 \ 2260 .hlp_dor_ix 2270 DEFW 0:DEFB 0 \no parent 2280 DEFW 0:DEFB 0 \no brother 2290 DEFW 0:DEFB 0 \no son 2300 DEFB &83 \application type 2310 DEFB ihz-iha 2320 .iha 2330 DEFB ASC"H" \help record 2340 DEFB 12 2350 DEFW ix_top_bas-org4000:DEFB &3F \topic pointer 2360 DEFW ix_com_bas-org4000:DEFB &3F \commands pointer 2370 DEFW ix_hlp_bas-org4000:DEFB &3F \help pointer 2380 DEFW 0:DEFB 0 \null pointer since no tokens 2390 OPT FNname("Imp-Export") \name of application 2400 DEFB &FF \end of DOR 2410 .ihz 2420 ] 2430 hb=ix_hlp_bas :REM fix macro 2440 [ OPT pass 2450 .ix_top_bas 2460 DEFB 0 2470 OPT FNtopic("Info",xh0,&12) \info topic with help 2480 DEFB 0 2490 \ 2500 .ix_com_bas 2510 DEFB 0 2520 OPT FNcommand("Batch receive",null$,0,xh1,&10) 2530 OPT FNcommand("End Batch",null$,0,xh2,&10) 2540 OPT FNcommand("Receive file",null$,0,xh3,&10) 2550 OPT FNcommand("Send file",null$,0,xh4,&10) 2560 OPT FNcommand("File device",null$,0,xh5,&11) \new column 2570 DEFB 0 2580 \ 2590 .ix_hlp_bas 2600 DEFM h$+"The Imp-Export popdown is used for passing" 2610 DEFM h$+"information between the Z88 and another computer."+n$ 2620 .xh5 2630 DEFM h$+"Imp-Export fetches Z88 files from the default device" 2640 DEFM h$+"and saves incoming files there too, unless a device" 2650 DEFM h$+"is explicitly mentioned in a filename. The default" 2660 DEFM h$+"is determined by the setting in the Panel."+n$ 2670 .xh0 2680 DEFM h$+"This topic describes each Imp-Export option."+n$ 2690 .xh1 2700 DEFM h$+"The Z88 will receive files, using the name sent by the" 2710 DEFM h$+"other computer, until an End of Batch is received."+n$ 2720 .xh2 2730 DEFM h$+"This tells the other computer that the Z88 has finished" 2740 DEFM h$+"sending a batch of files."+n$ 2750 .xh3 2760 DEFM h$+"This receives just one file from the other computer, but" 2770 DEFM h$+"allows you to supply a name for the incoming file."+n$ 2780 .xh4 2790 DEFM h$+"This sends a file to the other computer." 2800 DEFM h$+"If wildcards are used then all matches are sent" 2810 DEFM h$+"as a batch."+n$ 2820 \ 2830 .app2_dor \second application 2840 DEFW 0:DEFB 0 \no parent 2850 DEFW 0:DEFB 0 \no brother 2860 DEFW 0:DEFB 0 \no son 2870 DEFB &83 \application type 2880 DEFB a2z-a2a 2890 .a2a 2900 DEFB ASC"@" \information record 2910 DEFB a2j-a2i 2920 .a2i 2930 DEFW 0 \reserved 2940 DEFB ASC"U" \key letter 2950 DEFB 0 \contiguous memory 2960 DEFW 0 \environ overhead 2970 DEFW 0 \unsafe workspace 2980 DEFW 0 \safe workspace 2990 DEFW app2_entry \entry point 3000 DEFB 0 \segment 0 3010 DEFB 0 \segment 1 3020 DEFB 0 \segment 2 3030 DEFB &3E \segment 3 3040 DEFB 1 \good application 3050 DEFB 0 \no caps state 3060 .a2j 3070 \ 3080 DEFB ASC"H" \help record 3090 DEFB 12 \length 3100 DEFW a2_top_bas-org4000:DEFB &3F \pointer to topics 3110 DEFW a2_com_bas-org4000:DEFB &3F \pointer to commands 3120 DEFW a2_hlp_bas-org4000:DEFB &3F \pointer to help 3130 DEFW tok_bas-org4000:DEFB &3F \uses same token table as EP-Fetch 3140 \ 3150 OPT FNname("Useless") \application name 3160 DEFB &FF \end of DOR 3170 .a2z 3180 \ 3190 .a2_top_bas 3200 DEFB 0 3210 OPT FNtopic("Commands",0,0) 3220 DEFB 0 3230 \ 3240 .a2_com_bas 3250 DEFB 0 3260 OPT FNcommand(CHR$(&83)+"Safe Command","SAFE",1,0,8) \safe command 3270 OPT FNcommand("Hidden",CHR$(&E2),2,0,4) /hidden command 3280 OPT FNcommand("Aliased command","ALIAS",2,0,0) 3290 DEFB 0 3300 \ 3310 .a2_hlp_bas \null table 3320 DEFB 0 3330 DEFB 0 3340 DEFB 0 3350 \ 3360 \ 3370 \ 3380 .org7FC0 \application front DOR 3390 DEFW 0:DEFB 0 \no parent 3400 DEFW hlp_fdor-org4000:DEFB &3F \HELP front DOR 3410 DEFW app_dor-org4000:DEFB &3F \first application 3420 DEFB &13 \ROM type 3430 DEFB imz-ima 3440 .ima 3450 OPT FNname("APPL") \name 3460 DEFB &FF 3470 .imz 3480 .org7FF8 \ROM header 3490 DEFB ASC"Z" \ ID codes 3500 DEFB ASC"Z" \ apply to Cambridge Computer Ltd 3510 DEFB ASC"Z" \ for commercial release 3520 DEFB &80 3530 DEFB &02 \size of card in banks 3540 DEFB &00 \reserved 3550 DEFM "OZ" \ROM identifier 3560 .header_end 3570 \ 3580 \* Application code - will be relocated at &C000 * 3590 ] 3600 P%=&C000 :REM addresses from &C000 3610 prog_start=O% :REM used when blowing card 3620 [ OPT pass 3630 CALL apfetch_start \start application 3640 SCF \enquiry entry point 3650 RET 3660 .apfetch_start 3670 XOR A 3680 LD B,A 3690 LD HL,err_han 3700 OPT FNsys(os_erh) \install an error handler 3710 LD A,5 3720 OPT FNsys(os_esc) \enable escape detection 3730 CALL ap_main \call main part of application 3740 .app_ex 3750 XOR A \exit application with no errors 3760 OPT FNsys(os_bye) 3770 \ 3780 .err_han \error handler 3790 RET Z \return if fatal error 3800 CP rc_esc \check for escape 3810 JR NZ,err1 3820 OPT FNsys(os_esc) \acknowledge escape 3830 CP A 3840 RET 3850 .err1 3860 CP rc_quit \check for KILL request 3870 JR NZ,err0 3880 XOR A 3890 OPT FNsys(os_bye) 3900 .err0 \don't process other errors 3910 CP A 3920 RET 3930 \ 3940 .peek \return in A value at BHL in EPROM 3950 PUSH BC \BHL is a 24 bit address and has to 3960 PUSH HL \be converted into a physical address 3970 XOR A \zero A 3980 SLA H:RL A 3990 SLA H:RL A 4000 SRL H:SRL H \get top 2 bits in A 4010 SLA B:SLA B \Shift B along 4020 OR B 4030 AND 7 \no EPROM bigger than 128K 4040 ADD A,&C0 \slot 3 4050 LD B,A 4060 OPT FNsys(gn_rbe) \read byte at extended address 4070 POP HL 4080 POP BC 4090 RET 4100 \ 4110 .ap_main \main part of application 4120 CALL scan_eprom \check EPROM is there and scan it 4130 .main_loop 4140 PUSH DE \save base of list and position of bar 4150 LD A,(oldbase) 4160 CP D 4170 JR Z,skip_disp7 \if base unchanged then don't update screen 4180 LD A,D \update the screen and (oldbase) 4190 LD (oldbase),A 4200 CALL disp7 4210 .skip_disp7 4220 CALL bar \invert on the highlight bar 4230 POP DE 4240 CALL main_in \read keys 4250 JR main_loop \repeat 4260 \ 4270 .inv_mes 4280 DEFM "Valid EPROM not present":DEFB 7:DEFB 0 4290 .invalid 4300 LD HL,inv_mes 4310 OPT FNsys(gn_sop) 4320 CALL rdch \read a key 4330 JR NC,scan_eprom \no error 4340 CP rc_susp 4350 JR Z,scan_eprom 4360 POP HL \an error so let's quit 4370 JP app_ex 4380 .scan_eprom \start of scanning routine 4390 LD HL,set_scr \initialise screen 4400 OPT FNsys(gn_sop) 4410 LD B,&FF 4420 LD HL,&3FFE 4430 OPT FNsys(gn_rbe) \examine card type 4440 CP ASC"o" \should be "oz" for EPROM 4450 JR NZ,invalid 4460 INC HL 4470 OPT FNsys(gn_rbe) 4480 CP ASC"z" 4490 JR NZ,invalid 4500 LD A,1 \valid EPROM so let's scan it 4510 LD HL,0 \set space used to 0 4520 LD B,L 4530 .scan_loop 4540 LD (dmax),A 4550 PUSH BC 4560 PUSH HL 4570 LD (parm+1),A \entry to be examined 4580 CALL diren \routine to read entry (parm+1) 4590 POP HL 4600 POP BC 4610 JR C,scan_ex \exit if last entry 4620 LD DE,(len) \add length to space used 4630 LD A,(len+2) 4640 LD C,A 4650 CALL add 4660 LD A,(dmax) 4670 INC A \increment number of files count 4680 CP 255 4690 JR NZ,scan_loop 4700 .scan_ex 4710 LD A,(dmax) 4720 DEC A 4730 LD (dmax),A \adjust dmax 4740 LD (used),HL 4750 LD C,B 4760 LD B,0 4770 LD (used+2),BC \load (used) with space used 4780 LD A,3 \select window 3 4790 CALL sel_win 4800 LD HL,used_mes \bytes used message 4810 OPT FNsys(gn_sop) 4820 LD HL,used \start conversion of number 4830 LD DE,buff 4840 XOR A 4850 OPT FNsys(gn_pdn) \convert number 4860 XOR A 4870 LD (DE),A \add null at the end of the number string 4880 LD HL,buff 4890 OPT FNsys(gn_sop) \output number 4900 LD DE,&0100 \base of list=1, bar is on 0th line 4910 XOR A 4920 LD (oldbase),A \make oldbase=0 to force screen update 4930 INC A 4940 CALL sel_win \select window 1 4950 RET 4960 \ 4970 .main_in 4980 CALL rdch 4990 CP 13 \ENTER (used to fetch a file) 5000 JR NZ,no_ent 5010 CALL fetch 5020 CALL bar \remove bar (it'll be put back in a moment) 5030 RET 5040 .no_ent 5050 CALL bar \remove bar (in case it is moved) 5060 CP 255 \up arrow 5070 JR NZ,not_up 5080 XOR A \A=0 5090 CP E \is the bar on the top line 5100 JR Z,mov_up \if it is we want ot scroll the list 5110 DEC E \move the bar down a line 5120 RET 5130 .mov_up 5140 LD A,1 \are we at the top of the list 5150 CP D 5160 RET Z \return if we are 5170 DEC D \decrement base of the list 5180 RET 5190 .not_up 5200 CP 254 \down arrow 5210 JR NZ,not_down 5220 LD A,(dmax) \a complication if EPROM has less 5230 DEC A \than 6 files 5240 CP E 5250 RET Z 5260 LD A,6 \is bar at bottom of the screen 5270 CP E 5280 JR Z,mov_down \if it is then scroll down 5290 INC E \move bar down 5300 RET 5310 .mov_down 5320 LD A,(dmax) \check for the end of the list 5330 SUB 6 \adjust for position of bar 5340 CP D 5350 RET Z \exit if end of list 5360 INC D \move down the list 5370 RET 5380 .not_down 5390 CP 2 \shift ENTER (to force a re-scan) 5400 JP Z,scan_eprom 5410 CP 1 \check for ESCape 5420 RET NZ 5430 POP HL:RET \quit 5440 \ 5450 .disp7 \display 7 directory entries from D 5460 LD A,1 5470 CALL sel_win \select window 1 5480 LD A,12:OPT FNsys(os_out) \clear the screen 5490 LD B,7 \counter 5500 .disp7_loop 5510 LD (parm),DE 5520 PUSH DE 5530 PUSH BC 5540 CALL diren \read entry 5550 JR C,disp7_con 5560 LD A,(buff) \check to see if file is deleted 5570 CP 0 5580 JR NZ,disp7_blk 5590 LD HL,grey \if it is add prefix and make tiny 5600 OPT FNsys(gn_sop) 5610 .disp7_blk 5620 LD HL,buff+1 \skip first character 5630 OPT FNsys(gn_sop) 5640 LD HL,tab1 \position cursor on the right 5650 OPT FNsys(gn_sop) 5660 LD HL,len \length at (len) 5670 LD DE,buff 5680 XOR A 5690 OPT FNsys(gn_pdn) \convert number 5700 XOR A 5710 LD (DE),A \add null terminator 5720 LD HL,buff 5730 OPT FNsys(gn_sop) \write out size 5740 LD HL,endl \end of line (resets toggles) 5750 OPT FNsys(gn_sop) 5760 OR A \clear carry (to be sure) 5770 .disp7_con 5780 POP BC 5790 POP DE 5800 RET C 5810 INC D 5820 DJNZ disp7_loop 5830 RET 5840 \ 5850 .bar \invert bar on line E (0=first line) 5860 PUSH DE 5870 PUSH AF 5880 LD HL,bar1 5890 OPT FNsys(gn_sop) \sends SOH,'3;,'@','X' 5900 LD A,32 5910 ADD A,E 5920 OPT FNsys(os_out) \send x position 5930 LD HL,bar2 5940 OPT FNsys(gn_sop) \invert whole line 5950 POP AF 5960 POP DE 5970 RET 5980 .bar1 5990 DEFM s$+"3@"+CHR$(32)+CHR$(0) 6000 .bar2 6010 DEFM s$+"2+R"+s$+"2E"+CHR$(32+48)+s$+"2-R"+CHR$(0) 6020 \ 6030 \ 6040 \Get a directory entry (parm+1) 6050 \Return length in (len) and filename in buff 6060 \position in EPROM in (parm) and (parm+2) 6070 \if error then Fc=1 on exit 6080 \ 6090 .diren 6100 LD BC,0:LD HL,0 \Start of EPROM 6110 .loop CALL peek:CP &FF:JR Z,error 6120 \ 6130 PUSH BC:LD BC,(parm):DJNZ skip:JP found 6140 .skip LD (parm),BC 6150 POP BC \position in EPROM (top bit only!) 6160 INC A 6170 LD C,0:LD D,0:LD E,A 6180 CALL add \add length of name to BHL 6190 CALL peek:LD E,A:CALL inc 6200 CALL peek:LD D,A:CALL inc 6210 CALL peek:LD C,A:CALL inc:CALL inc 6220 CALL add \BHL should now point to next name length! 6230 JP loop 6240 .error 6250 SCF 6260 RET 6270 \ 6280 .found \file is found so extract name and length 6290 POP BC \restore position in EPROM 6300 CALL peek \get string length 6310 CALL inc 6320 LD DE,buff 6330 .sloop 6340 DEC A:LD (len),A 6350 CALL peek:CALL inc \copy filename into buffer 6360 LD (DE),A:INC DE 6370 LD A,(len):CP 0:JR NZ,sloop 6380 LD A,0:LD (DE),A \insert terminator 6390 CALL peek:LD (len),A:CALL inc \get length of file 6400 CALL peek:LD (len+1),A:CALL inc \only 3 bytes could be relevant 6410 CALL peek:LD (len+2),A:CALL inc 6420 CALL inc 6430 LD A,0:LD (len+3),A 6440 SLA H:RL A \address in EPROM 6450 SLA H:RL A \to be stored at (parm) (3 byte address) 6460 RL B:RL B 6470 OR B 6480 AND 7 6490 ADD A,&C0 6500 LD (parm+2),A 6510 SRL H:SRL H 6520 LD A,H 6530 AND &3F 6540 LD (parm),HL 6550 RET 6560 \ 6570 \ 6580 .add \ADD CDE to BHL 6590 LD A,L:ADD A,E:LD L,A \this could be done by ADD HL,DE! 6600 LD A,H:ADC A,D:LD H,A 6610 LD A,B:ADC A,C:LD B,A 6620 RET 6630 \ 6640 .inc \increment BHL 6650 PUSH AF 6660 PUSH DE:PUSH BC 6670 LD E,1:LD D,0:LD C,0 \LD CDE,1 6680 CALL add \call add 6690 LD A,B:POP BC:LD B,A 6700 POP DE:POP AF:RET 6710 \ 6720 .rdch \read a character 6730 OPT FNsys(os_in) \never returns zero (unless null) 6740 JR NC,rdch2 \prefixes are stripped out 6750 CP rc_susp 6760 JR Z,rdch \if suspension then loop back 6770 SCF \another error so exit 6780 RET 6790 .rdch2 6800 CP 0 6810 RET NZ \no prefix so return 6820 OPT FNsys(os_in) \read second byte 6830 RET 6840 \ 6850 .sel_win \select window A 6860 LD HL,sel 6870 OPT FNsys(gn_sop) 6880 ADD A,ASC"0" 6890 OPT FNsys(os_out) 6900 RET 6910 \ 6920 .yes_no \answer a yes or no question 6930 PUSH HL \exits with Fz=1 if yes or Fz=0 if no 6940 OPT FNsys(gn_sop) 6950 LD H,D 6960 LD L,E 6970 OPT FNsys(gn_sop) 6980 POP HL 6990 CALL rdch 7000 RET C 7010 CP 13 7020 JR NZ,yes_no_a 7030 LD A,E 7040 CP yes_mes MOD 256 \set Fz before exit 7050 RET 7060 .yes_no_a 7070 OR 32 \force to lower case 7080 CP ASC"y" \compare to y 7090 JR NZ,yes_no_b 7100 LD DE,yes_mes 7110 JR yes_no 7120 .yes_no_b 7130 CP ASC"n" 7140 JR NZ,yes_no \if neither Y or N pressed then no change 7150 LD DE,no_mes 7160 JR yes_no 7170 .yes_mes \text for Yes 7180 DEFM "Yes":DEFB 0 7190 .no_mes 7200 DEFM "No ":DEFB 8:DEFB 0 \text for No 7210 \ 7220 .fetch \fetch a file from EPROM 7230 PUSH DE 7240 LD A,2 7250 CALL sel_win \select side window 7260 OPT FNsys(gn_sop) 7270 LD HL,cursor \switch on the cursor 7280 OPT FNsys(gn_sop) 7290 CALL getname \get the filename 7300 JR C,fetch_x \if an error then report and exit 7310 CALL getfile \get the file 7320 .fetch_x 7330 LD HL,done_mes \message for success 7340 JR NC,fetch_ok 7350 LD HL,fail_mes \message for failure 7360 .fetch_ok 7370 OPT FNsys(gn_sop) \display 'Done' or 'Failed' 7380 LD HL,cursor 7390 OPT FNsys(gn_sop) 7400 LD A,1 7410 CALL sel_win 7420 POP DE 7430 RET 7440 \ 7450 .getname \this routine inserts the name into 7460 LD A,D \into an input buffer 7470 ADD A,E \to allow for editing 7480 LD (parm+1),A 7490 CALL diren \get entry 7500 RET C 7510 OPT FNsys(gn_nln) 7520 LD C,0 7530 LD DE,buff+1 7540 .sip_loop 7550 LD HL,prompt 7560 OPT FNsys(gn_sop) 7570 LD A,33 7580 LD L,30 7590 LD B,50 7600 OPT FNsys(gn_sip) 7610 JR NC,nam_ok 7620 CP rc_susp 7630 JR Z,sip_loop 7640 SCF 7650 RET \some error 7660 .nam_ok \we now have a name 7670 LD B,0 \HL=logical address 7680 LD HL,buff+1 \address of filename 7690 LD DE,buff+128 \buffer for explicit filename 7700 LD C,30 \arbitrary size for explicit filename 7710 LD A,1 \open for input 7720 OPT FNsys(gn_opf) \attempt to open file 7730 JR C,fet_ok2 \if it didn't open it should be OK! 7740 OPT FNsys(gn_cl) \it opened, so close 7750 OPT FNsys(gn_nln) 7760 LD HL,exists \ask if user want to overwrite file 7770 LD DE,no_mes \initial state='No' 7780 CALL yes_no \'Yes'/'No' prompt routine 7790 JR Z,fet_ok2 7800 SCF \user said 'no' so exit 7810 RET 7820 .fet_ok2 \open for input failed so open for output 7830 LD B,0 7840 LD HL,buff+1 7850 LD DE,buff+128 7860 LD C,30 7870 LD A,2 \open for output 7880 OPT FNsys(gn_opf) 7890 RET NC 7900 OPT FNsys(gn_err) \produce an error box 7910 SCF 7920 RET 7930 \ 7940 .getfile \this involved routine fetches a file 7950 LD HL,(parm):LD B,0 \from EPROM using os_mv 7960 LD DE,(len):LD A,(len+2):LD C,A \the main complication is dealing 7970 CALL add \start+len \with files which cross bank 7980 LD A,B \boundaries 7990 CP 0:JR NZ,bigchnk 8000 LD A,H 8010 CP &3F 8020 JR Z,endchnk 8030 JR NC,bigchnk 8040 \does not cross a bank boundary so copy into file 8050 .endchnk 8060 LD A,(parm+2):LD B,A \bank number 8070 LD C,2 8080 OPT FNsys(os_mpb) 8090 LD BC,(len) \number of bytes to transfer 8100 LD DE,0 8110 LD HL,(parm) 8120 LD A,H 8130 OR &80 8140 LD H,A 8150 OPT FNsys(os_mv) 8160 JR C,fet_fail \the failure will be No Room 8170 OPT FNsys(gn_cl) 8180 OR A \clear carry 8190 RET \now we have finished 8200 \ 8210 .bigchnk 8220 LD DE,(parm):LD HL,16*1024 8230 CP A \clear carry 8240 SBC HL,DE 8250 LD A,(len):SUB L:LD (len),A 8260 LD A,(len+1):SBC A,H:LD (len+1),A 8270 LD A,(len+2):SBC A,0:LD (len+2),A 8280 LD A,(parm+2):LD B,A 8290 INC A:LD (parm+2),A 8300 LD C,2 8310 OPT FNsys(os_mpb) \bank switch 8320 LD B,H:LD C,L:LD DE,0 8330 LD HL,(parm) 8340 LD A,H:OR &80:LD H,A 8350 OPT FNsys(os_mv) \move to the end of the bank 8360 JR C,fet_fail 8370 LD HL,0:LD (parm),HL 8380 JP getfile 8390 .fet_fail \report os_mv error and exit 8400 OPT FNsys(gn_err) 8410 OPT FNsys(gn_cl) 8420 SCF 8430 RET 8440 \ 8450 .set_scr 8460 OPT FNwind(1,0,0,94,8,0) \clear whole screen 8470 OPT FNwind(1,1,0,48,8,3) 8480 DEFM s$+"2JC"+s$+"3+TU"+"EPROM FILES"+cr$ 8490 DEFM s$+"R"+s$+"2A"+CHR$(32+48) 8500 OPT FNwind(1,1,1,48,7,1) \window within a window 8510 OPT FNwind(2,51,0,40,8,3) 8520 DEFM s$+"2JC"+s$+"3+TU"+"STATUS"+cr$ 8530 DEFM s$+"R"+s$+"2A"+CHR$(32+40) 8540 OPT FNwind(2,51,3,40,5,1) 8550 DEFM s$+"2C2"+s$+"S" \scrolling 8560 OPT FNwind(3,51,1,40,2,1) \this window for 'bytes used' message 8570 DEFB 0 8580 \ 8590 .cursor \toggle cursor 8600 DEFM s$+"C"+n$ 8610 .grey 8620 DEFM s$+"t--"+n$ \indicate deleted file 8630 .tab1 8640 DEFM s$+"2X"+CHR$(32+40)+n$ \tab for file size 8650 .endl 8660 DEFM CHR$(10)+cr$+s$+"2-T"+n$ \end of line string 8670 .sel 8680 DEFM s$+"2H"+n$ \select window string 8690 .used_mes 8700 DEFM cr$+"Bytes used:"+n$ 8710 .prompt 8720 DEFM cr$+"Fetch as:"+n$ 8730 .exists 8740 DEFM cr$+"Overwrite existing file:"+n$ 8750 .done_mes 8760 DEFM cr$+CHR$(10)+"Done"+n$ 8770 .fail_mes 8780 DEFM cr$+CHR$(10)+"Failed"+CHR$(7)+n$ 8790 \ 8800 .app2_entry \second application 8810 JP app2_main \simply demonstrates SAFE 8820 SCF \and HIDDEN commands 8830 RET 8840 .app2_main 8850 LD HL,app2_scr \set up screen 8860 OPT FNsys(gn_sop) 8870 .app2_loop 8880 CALL rdch \incest! 8890 JR NC,app2_1 8900 CP rc_draw 8910 JR Z,app2_main \start again if screen corrupted 8920 CP rc_quit 8930 JR NZ,app2_loop 8940 OPT FNsys(os_bye) \exit on KILL request 8950 .app2_1 8960 LD HL,none_mes \default message 8970 CP 1 \code for safe command 8980 JR NZ,app2_2 8990 LD HL,safe_mes 9000 .app2_2 9010 CP 2 9020 JR NZ,app2_3 \code for alias (TAB or <>ALIAS) 9030 LD HL,alias_mes 9040 .app2_3 9050 OPT FNsys(gn_sop) \display command type 9060 OPT FNsys(gn_nln) \newline 9070 JP app2_loop \do it again 9080 \ 9090 .none_mes 9100 DEFM "No command"+n$ 9110 .safe_mes 9120 DEFM "Safe"+n$ 9130 .alias_mes 9140 DEFM "Alias"+n$ 9150 .app2_scr 9160 OPT FNwind(1,0,0,94,8,0) 9170 DEFM s$+"3+CSUseless!"+cr$+CHR$(10)+n$ 9180 \ 9190 ] 9200 ep_safe_ws=280 :REM this is a bit excessive really 9210 P%=&1FFE-ep_safe_ws :REM should allocate buffer 9220 [ OPT pass 9230 .parm DEFM "0123" 9240 .len DEFM "0123" 9250 .dmax DEFB 0 9260 .used DEFM "0123" \space used on EPROM 9270 .oldbase DEFB 0 9280 .buff 9290 ] 9300 P%=O% :REM workspace for EPROM code 9310 [ OPT pass 9320 .prog_end 9330 .bank DEFB 0 \EPROM bank to write 9340 .off_in_bank DEFW 0 \address in bank 9350 .start DEFW 0 \start address (logical) 9360 .length DEFW 0 \number of bytes 9370 .exit DEFB 0 9380 DEFM "extra space" 9390 ] 9400 NEXT 9410 PRINT"START?" 9420 REPEAT 9430 INPUT "Type 'Yes' to go..."A$ 9440 UNTIL A$="Yes" 9450 PRINT "Blowing ROM header":VDU 7 9460 A=INKEY(200) 9470 ?bank=&3F 9480 !off_in_bank=&3FF8 9490 !start=org7FF8 9500 !length=header_end-org7FF8 9510 CALL code 9520 IF ?exit=255 THEN END 9530 PRINT "Blowing data structures":VDU 7 9540 A=INKEY(200) 9550 ?bank=&3F 9560 !off_in_bank=0 9570 !start=org4000 9580 !length=org7FC0-org4000 9590 CALL code 9600 PRINT "Blowing Front DOR":VDU 7 9610 A=INKEY(200) 9620 ?bank=&3F 9630 !off_in_bank=&3FC0 9640 !start=org7FC0 9650 !length=org7FF8-org7FC0 9660 CALL code 9670 PRINT "Blowing application code" 9680 VDU 7:A=INKEY(200) 9690 ?bank=&3E 9700 !off_in_bank=0 9710 !start=prog_start 9720 !length=prog_end-prog_start 9730 REM 9740 CALL code 9750 REPEAT:VDU 7 :REM beep until space 9760 A=INKEY(50) 9770 UNTIL A=32 9780 END 9790 : 9800 DEF FNhelp(a) :REM macro for HELP pointer 9810 [ OPT pass 9820 DEFB (a-hb) DIV 256 9830 DEFB (a-hb) MOD 256 9840 ] 9850 =pass 9860 : 9870 DEF FNsys(a) :REM macro for system call 9880 IF a>255 THEN [ OPT pass:RST &20:DEFW a:]:=pass 9890 [ OPT pass:RST &20:DEFB a:]:=pass 9900 : 9910 DEF FNtopic(name$,help,attribute) :REM macro for topic entry 9920 LOCAL len 9930 len=LEN(name$)+3 9940 IF attribute AND &10 THEN len=len+2 9950 [ OPT pass 9960 DEFB len 9970 DEFM name$ 9980 ] 9990 IF attribute AND &10 THEN a=FNhelp(help) 10000 [ OPT pass 10010 DEFB attribute 10020 DEFB len 10030 ] 10040 =pass 10050 : 10060 DEF FNcommand(name$,key$,com,help,attribute) :REM macro for command entry 10070 LOCAL len 10080 len=len+LEN(name$)+LEN(key$)+5 10090 IF attribute AND &10 THEN len=len+2 10100 [ OPT pass 10110 DEFB len 10120 DEFB com 10130 DEFM key$:DEFB 0 10140 DEFM name$ 10150 ] 10160 IF attribute AND &10 THEN a=FNhelp(help) 10170 [ OPT pass 10180 DEFB attribute 10190 DEFB len 10200 ] 10210 =pass 10220 : 10230 DEF FNname(name$) :REM DOR name record 10240 [ OPT pass 10250 DEFB ASC"N" 10260 DEFB LEN(name$)+1 10270 DEFM name$ 10280 DEFB 0 10290 ] 10300 =pass 10310 : 10320 DEF FNwind(n,x,y,w,d,a) :REM window definition 10330 [ OPT pass 10340 DEFB 1 10350 DEFM "7#" 10360 DEFB n+ASC"0" 10370 DEFB 32+x 10380 DEFB 32+y 10390 DEFB 32+w 10400 DEFB 32+d 10410 DEFB 128+a 10420 DEFM s$+"2C":DEFB ASC"0"+n \select the window 10430 ] 10440 =pass 10450 END 10460 DEF PROCE(B) 10470 REM BASIC line editor (use PROCE(line number)) 10480 IF B=0 THEN ENDPROC 10490 A=OPENOUT":RAM.0/EE.CLI" 10500 B$=":RAM.0/E.CLI" 10510 PRINT#A,".>"+B$ 10520 PRINT#A,".J","LIST"+STR$(B),"PROCF" 10530 CLOSE#A 10540 *CLI .*:RAM.0/EE.CLI 10550 ENDPROC 10560 DEF PROCF 10570 A=INKEY(0) 10580 A=OPENIN B$ 10590 INPUT#A,A$,A$ 10600 CLOSE#A 10610 A=OPENOUT B$ 10620 PRINT#A,".J",A$ 10630 PTR#A=PTR#A-1 10640 BPUT#A,0 10650 CLOSE#A 10660 VDU 8 10670 OSCLI"*CLI .<"+B$ 10680 ENDPROC

Section 4 - System calls reference


4.1 System calls finder

This brief list of topics is intended to make it easier to find a system call to perform the required function. The calls are listed in alphabetical order in the main body of section 4. For floating point routines see section 2.11. Alarms - alarm handling os_alm - allocate alarm block gn_aab - free alarm block gn_fab - link alarm block in chain gn_lab - unlink alarm block gn_uab - process expired alarm gn_alp Allocate - allocate alarm block gn_aab - allocate memory os_mal - allocate tri-handle os_gth Application - enter application os_ent - exit application os_exit, os_bye - name application dc_nam - poll for application os_poll Arithmetic - unsigned 16-bit multiplication gn_m16 - unsigned 16-bit division gn_d16 - unsigned 24-bit multiplication gn_m24 - unsigned 24-bit division gn_d24 Binding - get old binding os_mgb - put new binding os_mpb Bleep - make a bleep os_blp Bypass - bypass values in byte sequence gn_skc - bypass delimiters in byte sequence gn_skd Card - examine process card usage os_use Classify - classify character gn_cls CLI - CLI interface os_cli Close - close file os_cl, gn_cl - close filter gn_flc - close memory os_mcl - close wildcard handler gn_wcl Copy - move bytes os_bde, os_bhl Dates - convert ASCII string to internal date gn_gdt - convert internal date to ASCII string gn_pdt - convert from internal to external format gn_die - convert from external to internal format gn_dei - fetches current machine date gn_gmd - set machine date gn_pmd Delay - fixed time delay os_dly Delete - file delete os_del, gn_del Division - unsigned 16-bit division gn_d16 - unsigned 24-bit division gn_d24 DOR - DOR interface os_dor Enter - enter application os_ent EPROM - transfer files to/from EPROM os_epr Erase - file delete os_del Errors - set error handler os_erh - get error context os_erc - examine special condition os_esc - display system error box gn_err - return pointer to system error message gn_esp Escape - examine special condition os_esc Examine - examine input os_xin - examine special condition os_esc - examine process card usage os_use Exit - exit application os_exit, os_bye Extended addresses - move bytes with extended address os_bhl, os_bde - write string from extended address gn_soe - read byte at extended address gn_rbe - write byte at extended address gn_wbe - compare strings at extended address gn_cme File - file close os_cl, gn_cl - file delete os_del, gn_del - transfer file to/from EPROM os_epr - file get byte os_gb - file get (with timeout) os_gbt - file open os_op, gn_opf - file put byte os_pb - file put (with timeout) os_pbt - file read misc. os_frm - file write misc. os_fwm - file rename os_ren, gn_ren - file multiple r/w os_mv Filenames - produce compressed filename gn_fcm - produce explicit filename gn_fex - open wildcard handler gn_opw - close wildcard handler gn_wcl - fetch next name from list gn_wfn - parse string for correctness of filename gn_prs - parse string for correctness of filename segment gn_pfs - match filename segment to wildcard string gn_wms - read/write filename segments gn_esa Filters - open a filter gn_flo - close a filter gn_flc - push character into filter gn_flw - read character from filter gn_flr - flush filter gn_flf - push back character into filter gn_fpb Find - search for value in byte sequence gn_skt Free - free alarm block gn_fab - free memory os_mfr - free tri-handle os_fth Get - allocate alarm block gn_aab - allocate memory os_mal - allocate tri-handle os_gth Input - read from standard input os_in - read from standard input (with timeout) os_tin - input line from keyboard gn_sip Keyboard - purge keyboard buffer os_pur - input line from keyboard gn_sip Linked lists - index next entry gn_xnx - insert entry gn_xin - delete entry gn_xdl - link alarm block in chain gn_lab - unlink alarm block gn_uab Map - Pipedream map os_map Memory - allocate memory os_mal - close memory os_mcl - free memory os_mfr - open memory os_mop Move - move bytes os_bde, os_bhl - move to/from file os_mv Multiplication - unsigned 16-bit multiplication gn_m16 - unsigned 24-bit multiplication gn_m24 Name - name application dc_nam - rename file gn_ren, os_ren Newline - output CR/LF pair to standard output gn_nln Noise - make a bleep os_blp Numbers - convert ASCII string to internal number gn_gdn - convert number to ASCII string gn_pdn Off - switch off os_off Open - open file os_op, gn_cl - open filter gn_flo - open memory os_mop - open wildcard handler gn_opw Output - write to stdout os_out - output to printer os_prt - output CR/LF pair to stdout gn_nln - output string to stdout gn_sop - output string from extended address to stdout gn_soe Panel - set panel os_sp Pipedream - Pipedream map control os_map Poll - poll for application os_poll Printer - output to printer os_prt Purge - purge keyboard buffer os_pur Quit - exit application os_exit Rename - file rename os_ren, gn_ren Search - search for value in byte sequence gn_skt Serial interface - misc serial interface function os_si Set - set panel os_sp Skip - bypass characters in byte sequence gn_skc - bypass delimiters in byte sequence gn_skd Sound - make a bleep os_blp String - output string to stdout gn_sop - output string from extended address to stdout gn_soe - compare strings at extended address gn_cme Switch off - switch off os_off Time - alarm handling os_alm - fixed time delay os_dly - hardware time manipulation os_ht - convert ASCII string to internal time gn_gtm - convert internal time to ASCII string gn_ptm - output date and time to standard output gn_sdo - fetch machine time gn_gmt - set machine time gn_pmt - convert real time to time to elapse gn_msc Tri-handle - allocate tri-handle os_gth - free tri-handle os_fth - verify tri-handle os_vth Use - examine process card usage os_use Verify - verify tri-handle os_vth Wildcards - open wildcard handler gn_opw - close wildcard handler gn_wcl - fetch next name from list gn_wfn - match filename segment to wildcard string gn_wsm

4.2 System calls reference

The following is an alphabetic list of all the 'RST 20'-entered system calls, with interface specifications, and in many cases, further explanation. A lot of these are designed for use in the system and are not particularly useful for user programs; indeed many of them may be positively dangerous. Even if the call appears not to provoke any ill effects, it is quite likely that subtle problems such as inconsistencies in the memory map may have appeared, and might ultimately cause a crash, perhaps a year hence! In view of the complexity of the machine and the typical time between resets, the user should be mindful of the problems and restrict use of the dangerous calls to 'healthy experimentation'. Particularly dangerous calls are marked "(*system*)" at the top of the page and explicit information witheld; these should be avoided in any commercial applications. As a general rule, 'gn' and 'fp' calls are safe, 'os' calls divide up about 50-50 into safe and unsafe, and only a few of the 'dc' calls should be used. (*SYSTEM*) dc_alt - Pass an alternative character RST &20 DEFB &0C DEFB &1A dc_bye - Exiting current application RST &20 DEFB &0C DEFB &08 dc_ent - Enter new application RST &20 DEFB &0C DEFB &0A dc_gen - Screen SOH (special character) call RST &20 DEFB &0C DEFB &20 dc_icl - Invoke new CLI RST &20 DEFB &0C DEFB &14 In: HL - points to a null terminated string C - length of string B=0 Out: Fc=0 - successful Fc=1 - failed (this could be RC.ROOM (&07)) Notes: This gives the CLI a string to execute. It is used by BASIC's *CLI command and by the Alarm. Via this call all the CLI facilities can be accessed, though often it will be necessary to use the call to execute a file containing a list of CLI commands. (*SYSTEM*) dc_in - Read from CLI RST &20 DEFB &0C DEFB &06 dc_ini - Initialise the Director RST &20 DEFB &0C DEFB &06 dc_nam - Name current application RST &20 DEFB &0C DEFB &0C In: HL - points to a null terminated name Out: - A...BCDEHL/IXIY same ...F............/....... different afbcdehl different Notes: This provides text for the 'Your Ref.' column in the Index. Up to 15 characters can be displayed. (*SYSTEM*) dc_nq - Handle Director/CLI enquiries RST &20 DEFB &0C DEFB &18 dc_out - Write to CLI RST &20 DEFB &0C DEFB &10 dc_pol - Poll for card usage RST &20 DEFB &0C DEFB &22 dc_prt - Print to CLI RST &20 DEFB &0C DEFB &12 dc_rbd - Rebind streams RST &20 DEFB &0C DEFB &1C In: A - identifier for stream to rebind: A=0 - input stream (ie. new source of input) A=1 - output stream (ie. new destination for output) A=2 - printer stream (ie. new destination for output) A=3 - input stream T (ie. copy of input) A=4 - output stream T (ie. copy of output) A=5 - printer stream T (ie. copy of output) IX - file handle of required rebinding. IX=0 indicates the file is to be closed Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - return code RC.FAIL (&16) - no CLI currently running RC.BAD (&04) - bad arguments (*SYSTEM*) dc_scn - Scan for card usage RST &20 DEFB &0C DEFB &24 dc_sp - Handle Director/CLI settings RST &20 DEFB &0C DEFB &18 dc_xin - Examine CLI input RST &20 DEFB &0C DEFB &1D gn_aab - Allocate alarm block RST &20 DEFB &09 DEFB &68 In: - Out if call succeeded: Fc=0 BHL - Pointer to alarm block. Out if call failed: Fc=1 A - error code: RC.ROOM (&07) ........CDE.../IXIY same AFB.......HL/...... different afbcdehl different (*SYSTEM*) gn_alp - Process an expired alarm RST &20 DEFB &09 DEFB &70 Notes: Internal alarm manipulation. gn_cl - Close file RST &20 DEFB &09 DEFB &62 In: IX - Handle of open file Out if call succeeded: Fc=0 IX=0 Out if call failed: Fc=1 IX - unchanged A=RC.HAND (&08) ......BCDEHL/...IY same AF........... ../IX... different afbcdehl different. gn_cls - Classify character RST &20 DEFB &09 DEFB &30 In: A - character to classify Out: F - flags indicate classification, as follows: Fc=0 ; Fz=0 Neither alphabetic or numeric Fc=0 ; Fz=1 Numeric (0...9) Fc=1 ; Fz=0 Upper case letter (A...Z) Fc=1 ; Fz=1 Lower case letter (a...z) A...BCDEHL/IXIY same ...F............/....... different afbcdehl different gn_cme - Compare null-terminated strings, one local, one extended RST &20 DEFB &09 DEFB &42 In: BHL - extended pointer (B=0 => logical, else physical) to string 1 DE - logical pointer to string 2. Both strings null- terminated. Out: Fz=0 if strings are different Fz=1 if strings are the same. A...BCDEHL/IXIY same ...F............/...... different afbcdehl different gn_d16 - 16-bit unsigned division RST &20 DEFB &09 DEFB &74 In: HL - dividend DE - divisor Out if call succeeded: Fc=0 HL - quotient DE - remainder. Out if call failed: Fc=1 A - error code: RC.FAIL ($16) - division by zero attempted .....BC......./IXIY same AF....DEHL/....... different afbcdehl different. gn_d24 - 24-bit unsigned division RST &20 DEFB &09 DEFB &78 In: BHL - dividend CDE - divisor Out if call succeeded: Fc=0 BHL - quotient CDE - remainder. Out if call failed: Fc=1 A - error code: RC.FAIL ($16) - division by zero atempted. ................/IXIY same AFBCDEHL/....... different afbcdehl different. gn_dei - Convert zoned format date to internal format RST &20 DEFB &09 DEFB &16 In: C7...C5 - day of the week (1=Monday...7=Sunday; 0 unspecified, ie machine will work it out for you - in other cases the day must be correct or an error will occur) C4...C0 - date (1...31 with obvious meaning) B - month (1=January...12=December) DE - signed year number relative to 0AD. Out if call succeeded: Fc=0 ABC - internal format date. Out if call failed: Fc=1 (no error code) - invalid date given (eg. day inconsistent with date in C, or B not in range 1...12). gn_del - Delete a file RST &20 DEFB &09 DEFB &64 In: BHL - Extended pointer to filename. Must be >255. The filename may be terminated by space or a control character (eg. NUL [0] or CR [13]). Out if call succeeded: Fc=0. Out if call failed: Fc=1 A - error code, one of: RC.ONF (&12) - file not found. RC.IVF (&17) - invalid filename. RC.USE (&15) - file in use, ie. has been opened and not closed. .....BCDEHL/IXIY same AF............./...... different afbcdehl different. gn_die - Convert internal format date to zoned format RST &20 DEFB &09 DEFB &14 In: ABC - internal format date (as returned by 'gn_gmd' for instance) Out if call succeeded: Fc=0 A - Number of days in month (eg. 28 if date is in Feb 1989) C7...C5 - Day of the week (1=Monday...7=Sunday) C4...C0 - Date (1...31 with obvious meaning) B - Month (1=January...12=December) DE - Signed year number relative to 0 AD. Out if call failed: Fc=1 (no error code) - date out of range (eg. beyond 32767 AD) ..............HL/IXIY same AFBCDE....../....... different afbcdehl different gn_err - Display an interactive error box. RST &20 DEFB &09 DEFB &4A In: A - error message to display Out: Fc=1 A - return code RC.SUSP - user wants to continue RC.DRAW - user wants to continue and screen needs redrawing RC.QUIT - user does not want to continue .....BCDEHL/IXIY same AF............./...... different afbcdehl different Note: See list of error's, which have associated messages in the description of gn_esp. Calling gn_err with an invalid error message will cause a box with the message: "Internal error- Press Q to quit- fatal error" to be displayed, and will generate a RC.QUIT. If you have an error handler in place, which handles RC.QUIT, then that code will be called. gn_esa - read and write to filename segments RST &20 DEFB &09 DEFB &5E In: A - command A7 - set to write, clear to read A0 - set for extension, clear for name HL/DE - filename and buffer pointers A7=0 (reading) - HL=filename, DE=buffer A7=1 (writing) - HL=new segment, DE=buffer B - segment number (+/- 64) Positive indexes start at the device name (B=0) Negative indexes start at the filename (B=255) C - Limit of space to use (only relevant for writing) (C should not be zero) A7=0 (reading) Out if call succeeded: Fc=0 Out if call failed: Fc=1 RC.IVF - bad filename ......BCDEHL/IXIY same AF............../........ different afbcdehl different A7=1 (writing) Out if call succeeded: Fc=0 B - number of segments returned C - number of characters returned DE - indexes terminating null of filename Out if call failed: Fc=1 ................HL/IXIY same AFBCDE....../......... different afbcdehl different Notes: To write a segment a valid filename must be placed in the buffer pointed to by DE and the replacement segment is pointed by HL. If the new segment is an extension then then the first three characters are used. gn_esp - Return a pointer to a system error message RST &20 DEFB &09 DEFB &4C In: A - error message required Out: Fc=0 Fz=1 Error is fatal Fz=0 Useful error message BHL extended pointer to error string A....CDE..../IXIY same ..FB......HL/...... different afbcdehl different Notes: Error codes which produce messages are as follows: Return code Value Message ------------------------------------------------------------------- RC.ESC &01 Escape RC.TIM &02 Timeout RC.ROOM &07 No room RC.EOF &09 End of file RC.FLF &0A Filter full RC.OVF &0B Overflow RC.SNTX &0C Bad syntax RC.WRAP &0D Wrap RC.PUSH &0E Cannot satisfy request RC.PRE &11 No room RC.ONF &12 File not found RC.RP &13 Read protected RC.WP &14 Write protected RC.USE &15 In use RC.FAIL &16 Cannot satisfy request RC.IVF &17 Bad filename RC.FTM &18 File type mismatch RC.EXIS &19 Already exists RC.DVZ &46 Divide by 0 RC.TBG &47 Number too big RC.NVR &48 -ve root RC.LRG &49 Log range RC.ACL &4A Accuracy lost RC.EXR &4B Exponent range RC.BDN &4C Bad number RC.DRAW &66 Redraw RC.QUIT &67 Unknown error RC.SUSP &69 Suspended gn_fab - Free alarm block RST &20 DEFB &09 DEFB &6A In: BHL - Physical pointer to alarm block Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code: RC.BAD (&04) - bad alarm block gn_fcm - compress a filename RST &20 DEFB &09 DEFB &4E In: BHL - source pointer (HL>255, B=0 means local string). Source string should be terminated by a control character DE - destination pointer 0= stream whose handle is IX 1= filter whose handle is IX >255= place string at (DE) IX - source handle (if HL=0 or HL=1) C - limit of space to use. Out if call succeeded: Fc=0 DE - points to the character after the last one written by the routine (DE(in)>255) B - number of segments returned C - number of characters in compressed space. Out if call failed: Fc=1 A - return code RC.BAD (&04) - bad parameters ............HL/IXIY same AFBCDE..../....... different afbcdehl different Notes: If the filename cannot be reproduced in the space available, ie. C(in), then on exit C=1, B=0 and the expansion buffer( stream or filter) will be given a NULL. Setting C(in)=18 will guarantee the return of the a filename and extension. gn_fex - expand a filename RST &20 DEFB &09 DEFB &50 In: BHL - source pointer (HL>255, B=0 means local string). Source string should be terminated by a control character DE - destination pointer 0= stream whose handle is IX 1= filter whose handle is IX >255= place string at (DE) IX - source handle (if HL=0 or HL=1) C - limit of space to use. Out if call succeeded: Fc=0 DE - points to the character after the last one written by the routine (DE(in)>255) B - number of segments returned C - number of characters in compressed space. A7 - set if wildcards were used A6 - set if device name specified A5 - set if wild directory specified (ie. use of //) A4 - set if parent directory specified (ie. use of ..) A3 - set if current directory specified (ie. use of .) A2 - set if explicit directory specified A1 - set if filename specified A0 - set if extension specified Out if call failed: Fc=1 RC.IVF (&17) - invalid filename RC.EOF (&09) - blank filename RC.BAD (&04) - bad parameters ............HL/IXIY same AFBCDE...../...... different afbcdehl different Notes: The routine does not process wildcards, but passes them to the output buffer. gn_flc - Close filter RST &20 DEFB &09 DEFB &24 In: IX - filter handle Out if call succeeded: Fc=0 BC - number of input characters written to filter (provided at least one character has been read) DE - number of characters read from filter. Out if call failed: Fc=1 A - error code, RC.HAND (&08) - IX(in) was not a valid filter handle ............HL/IXIY same AFBCDE...../...... different afbcdehl different gn_flf - Flush filter RST &20 DEFB &09 DEFB &2A In: IX - filter handle Out of call succeeded: Fc=0 A - character from filter Fz - 1 if character is converted, else 0 Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - Bad filter handle RC.EOF (&09) - Filter is empty ....BCDEHL/IXIY same AF............/...... different afbcdehl different gn_flo - Open filter RST &20 DEFB &09 DEFB &22 In: HL - pointer to filter table A - attribute byte; some combination of: 1 - Allow case equivalence on input 2 - Use table in reverse mode 4 - Force maximum buffer size to B bytes. B - Maximum buffer size. Must be <= 128, and is only relevant if A2 is set. Out if call succeeded: Fc=0 IX - filter handle Out if call failed: Fc=1 A - error code; one of: RC.ROOM (&07) - Out of memory RC.BAD (&04) - FDT structure invalid RC.HAND (&08) - Bad handle. A..BCDEHL/....IY same ..F............/IX.... different afbcdehl different Bugs: FDT must be addressed in segment 1 or this call will fail. FDT must not cross a 16K segment boundary. gn_flr - read from filter RST &20 DEFB &09 DEFB &28 In: IX - filter handle Out if call succeeded: Fc=0 A - character read Fz - 1 if character is converted, else 0 Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - basd filter handle RC.EOF (&09) - filter is empty ....BCDEHL/IXIY same AF............/....... different afbcdehl different gn_flw - Write character to filter RST &20 DEFB &09 DEFB &26 In: IX - filter handle A - character to write Out if call succeeded: Fc=0 A unchanged Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - bad filter handle RC.FLF (&0A) - filter is full A..BCDEHL/IXIY same ..F............/...... different afbcdehl different gn_fpb - Push character back into filter RST &20 DEFB &09 DEFB &2C In: IX - filter handle Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - bad filter handle RC.PUSH (&0E) - One character already pushed back or no character read from this filter. RC.EOF (&09) - filter is empty ....BCDEHL/IXIY same AF............/...... different afbcdehl different Notes: At most one character can be pushed back without an intervening read. gn_gdn - Converts an ASCII string to a binary number RST &20 DEFB &09 DEFB &10 In: HL - source pointer 0= read from stream whose handle is IX 1= read from filter whose handler is IX >255= string pointed to by HL DE - destination pointer 2= number returned in BC >255= store result at address pointed to by DE ( (DE)=low byte, (DE+1)=high byte) B - maximum number of characters to be read IX - source handle (if HL=0 or HL=1) Out if call succeeded: Fc=0 A - character which stopped conversion (space or control character) HL - indexes character which stopped conversion (if HL(in)<2) or points to character which stopped conversion (if HL(in)>255) BC - contains result (if DE(in)=2) Out if call failed: Fc=1 A - return code; one of: RC.OVF (&0B) - Result overflowed space (returned value worthless) RC.EOF (&09) - Reading an empty stream or filter. Fz=1 if conversion routine did recognise a number ........DE..../IXIY same AFBC....HL/....... different afbcdehl different Notes: The character which stops conversion will be the first non-decimal digit. If the last readable character was numeric the routine will set Fz=1 and exit with A=0. gn_gdt - Convert an ASCII string to an internal binary date RST &20 DEFB &09 DEFB &06 In: HL - source pointer 0= read from stream whose handle is IX >255= string pointed to by HL DE - destination pointer 2= result returned in ABC >255= store result at address pointed to by DE A3 - set to force American format (MM/DD/YY) A4 - set to force European format (DD/MM/YY) A5 - set to force C to be the delimeter Other bits of A are ignored IX - source handle (if HL=0) B - maximum number of characters to be read C - optional delimeter Out if call succeeded: Fc=0 HL - index to last character read (if HL(in)=0) or pointer to last character read (if HL(in)>255) ABC- date in internal format (if DE(in)=2) or unchanged (if DE(in)>255) DE - DE(in)+3 (if DE(in)>255) Out if call failed: Fc=1 A - return code; one of: RC.ROOM (&07) - Insufficent memory to make conversion RC.HAND (&08) - Handles not available for conversion RC.BAD (&04) - Bad parameters RC.SNTX (&0C) - Bad syntax ................/IXIY same AFBCDEHL/....... different afbcdehl different Notes: Setting A3=0 and A4=0 will mean that the PANEL settings for date format will be used. [Check this, though] A3=A4=1 will use European format. The routine will return a syntax error (RC.SNTX) if A5=1 and the character passed in C(in) is not the terminator of the string to be converted. gn_gmd - Get (read) machine date in internal format. RST &20 DEFB &09 DEFB &18 In: DE - pointer to write machine date. If DE>255 then the date is written here, otherwise the result is returned in ABC. Out: If DE(in)>255, then DE=DE(in)+3; ABC=ABC(in) If DE(in)<255, then DE=DE(in); ABC=result ............HL/IXIY same AFBCDE...../...... different afbcdehl different. gn_gmt - Get (read) machine time in internal format RST &20 DEFB &09 DEFB &1A In: C - LSB of system date returned from 'gn_gmd' (optional) DE - pointer to write system date; if DE>255 then result is written here, else the result is placed in ABC. Out: Fz - 0 (ie. NZ) if C(in) is inconsistent with time read, else 1. DE - DE=DE(in) if DE<256, else DE=DE(in)+3 ABC - ABC=ABC(in) if DE>255 else contains the result ............HL/IXIY same AFBCDE...../...... different afbcdehl different gn_gtm - convert an ASCII string to a time in internal format RST &20 DEFB &09 DEFB &0A In: HL - source pointer 0= read from stream whose handle is IX 1= read from filter whose handle is IX >255= string pointed to by HL DE - destination pointer 2= result returned in ABC >255= store result at address pointed to by DE IX - source handle (if HL=0 or HL=1) Out if call succeeded: Fc=0 HL - unchanged (if HL(in)=0 or HL(in)=1) [really..check!] or HL points to terminating character (if HL(in)>255) ABC - result (if DE(in)=0) or preserved (DE(in)>255) Out if call failed: Fc=1 A - return code RC.SNTX (&0C) - Bad syntax ........DE..../IXIY same AFBC....HL/......... different afbcdehl different Notes: Accepts HH(:)MM(:SS)(:CC) gn_lab - Link an alarm block into the alarm chain RST &20 DEFB &09 DEFB &6C In: BHL - address of alarm block (must have been allocated and filled with appropriate values) Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code RC.BAD (&04) - .....BCDEHL/IXIY same AF............./....... different afbcdehl different Notes: A badly formed block is likely to crash the machine. The routine does not check the validity of the alarm block. gn_m16 16-bit unsigned multiplication. RST &20 DEFB &09 DEFB &72 In: HL - multiplicand DE - multiplier Out: Fc=0 HL - product (least significant 16 bits - no warning is given of overflow). A..BCDE..../IXIY same ..F........HL/...... different afbcdehl different. gn_m24 24-bit unsigned multiplication RST &20 DEFB &09 DEFB &76 In: BHL - multiplicand CDE - multiplier Out: Fc=0 BHL - product (least significant 24 bits - no warning is given of overflow). A....CDE..../IXIY same ..FB......HL/....... different afbcdehl different. gn_msc - Miscellaeneous time operations RST &20 DEFB &09 DEFB &20 In: A=0 - Convert source time to elapsed time BHL - Source time days CDE - Source time centiseconds A=1 - Update base time BHL - Minutes offset C - Seconds offset Out if A(in)=0 and call succceeded: Fc=0 BHL - minutes still to elapse C - seconds still to elapse A - centiseconds still to elapse Out if A(in)=0 and call failed: Fc=0 A=RC.FAIL (&16) - time given to routine has already passed Out if A(in)=1: - ................/IXIY same AFBCDEHL/....... different afbcdehl different Notes: Applications should not use this call with A(in)=1, the system uses the call in this way to help maintain the time over soft reset. The call's function is to indicate how much time there is to elapse between the time given and the current time. The call compensates for its own running time gn_nln Send newline (CR/LF) to standard output RST &20 DEFB &09 DEFB &2E In: - Out if call succeeded: Fc=0 Out if call failed: Fc=1 A- error code; one of: RC.HAND (&08) - bad handle RC.WP (&14) - write protected. Both these errors occur when the standard output is incorrectly rebound. ....BCDEHL/IXIY same AF............/...... different afbcdehl different gn_opf Open file (filename may include wildcards) RST &20 DEFB &09 DEFB &60 In: BHL - Pointer to name of file to open. . DE - Pointer to space to insert explicit filename C - Maximum space to fill with this explicit filename. See spec of 'gn_fex' in section 4 for details of what might happen to the filename A - Access mode: 1 - open for input 2 - open for output 3 - open for update 4 - open memory (not for application use) 5 - Create Directory (Returns DOR handle) 6 - return DOR information (Returns DOR handle) Out if call succeeded: Fc=0 IX - File handle for open file. B - Number of segments in the explicit filename C - Number of characters in returned at (DE) DE - Points beyond the last character of this explicit filename. Out if called failed: Fc=1 A = error code; one of: RC.BAD (&04) Bad arguments RC.IVF (&17) Invalid filename (eg. filename was ".wrong ") RC.ONF (&12) File not found RC.USE (&15) File already in use RC.FTM (&18) File type mismatch (ie. treating a directory as a file) RC.UNK (&03) Unknown request (ie. wrong A on entry). ............HL/...IY same AFBCDE...../IX... different afbcdehl different. gn_opw - Open wildcard handler RST &20 DEFB &09 DEFB &52 In: BHL - Pointer to wildcard string. B=0 => logical address. BHL must be >255 A0 - scan direction; if set then 'directory/file' is returned before 'directory'; if reset the opposite is the case. A1 - Set to return parents (ie. directory names) A2->A7 - should all be reset Out if call succeeded: Fc=0 IX - wildcard handle for this wildcard string Out if call failed: Fc=1 A - return code; one of: RC.ROOM - Insufficient memory RC.IVF - Invalid wildcard string ....BCDEHL/....IY same AF............/IX.... different afbcdehl different gn_pdn - Write number as decimal ASCII string RST &20 DEFB &09 DEFB &12 In: HL=2, BC=value to convert HL>255, HL points to 32 bit number to convert DE=0, result to stream IX DE=1, result to filter IX DE>255, result placed at (DE) A0 - set to disable leading zero blanking A1 - set to output leading space A2 - set to output trailing space A4 to A7 - numeric field width (0=as large as is required) IX - optional result handle Out if call succeeded: Fc=0 DE - points to character after result (if DE(in)>255) Out if call failed: Fc=1 A - error code ....BC....HL/IXIY same AF....DE..../....... different afbcdehl different gn_pdt - Write internal date as ASCII string RST &20 DEFB &09 DEFB &08 In: HL>255, HL points to 3 byte internal date DE=0, result to stream IX DE=1, result to filter IX >255, result placed at (DE) A0 - set to disable zero blanking A1 - set to output leading space A2 - set to output trailing space A3 - set to force American format (day, month, date, year) A4 - set to force European format (day, date, month, year) A5 - set to force C as inter-field delimiter A6 - set for date suffix output [what is this?] A7 - set for century output B0 - set for text month, reset for numeric month B1 - set for expanded day, reset for 3 letter day B2 - set for expanded month, reset for 3 letter month B3 - set for day output [clarify this] B4 - set for BC/AD C - optional inter-field delimiter (relevant if A5=1) IX - optional result handle Out if call succeeded: Fc=0 DE - points to next character after result (if DE(in)>255) Out if call failed: Fc=1 A - return code RC.BAD (&04) - bad parameters RC.ROOM (&07) - no room to perform conversion ....BC....HL/IXIY same AF....DE...../..... different afbcdehl different gn_pfs - Parse filename segment RST &20 DEFB &09 DEFB &5A In: BHL - points to filename segment (B=0, HL points to local string) Out if call succeeded: Fc=0 A0 - set if extension used A1 - set if filename used A2 - set if explicit directory used A3 - set if current directory (.) used A4 - set if parent directory (..) used A5 - set if wildcard directory (//) used A6 - set if device specified A7- set if wildcards used BHL - points to terminating character Out if call failed: Fc=1 A - return code RC.IVF (&17) - poor syntax RC.EOF (&09) - blank segment ....BCDE..../IXIY same AF........HL/....... different afbcdehl different Notes: The source pointer (BHL) may point to the ":" or "\" at the start of the segment. gn_pmd - Put (set) machine date RST &20 DEFB &09 DEFB &1C In: HL=2, 3 byte internal date in ABC HL>255, HL points to 3 byte internal date Out: - AFBCDEHL/IXIY same afbcdehl different Notes: The date is used throughout the machine and should only be altered if the user of the machine is fully aware of the fact. gn_pmt - Put (set) machine time RST &20 DEFB &09 DEFB &1E In: HL=2, 3 byte internal time in ABC HL>255, HL points to 3 byte internal time E - least significant byte of assumed date Out: Fz=0 - time is inconsistent with assumed date (E(in)) ....BCDEHL/IXIY same AF............/...... different afbcdehl different Notes: The time is used throughout the machine and should only be altered if the user of the machine is fully aware of the fact. gn_prs - Parse filename RST &20 DEFB &09 DEFB &58 In: BHL - pointer to filename (B=0, HL points to a local string). Name terminated by a control character. Out if call succeeded: Fc=0 A0 - set if extension used A1 - set if filename used A2 - set if explicit directory used A3 - set if current directory (.) used A4 - set if parent directory (..) used A5 - set if wildcard directory (//) used A6 - set if device specified A7- set if wildcards used B - number of segments in filename C - length of filename including terminator ........DEHL/IXIY same AFBC......../...... different afbcdehl different gn_ptm - Write internal time as ASCII string RST &20 DEFB &09 DEFB &0C In: HL - source pointer (must be >255) DE=0, IX result to stream IX DE=1, IX result to filter IX DE>255, result at (DE) A0 - set to display leading zeros A1 - set to output a leading space A2 - set to output a trailing space A3 - must be set to zero A4 - set to display centiseconds (only if A5=1) A5 - set to display seconds A6 - military format (ie. no delimiters) A7 - set to force AM/PM (as opposed to 24 hour format) Out if call succeeded: Fc=0 DE - points to character after result (if DE(in)>255) Out if call failed: Fc=1 A - return code RC.ROOM (&07) - there is not enough room to convert time ....BC....HL/IXIY same AF....DE...../...... different afbcdehl different gn_rbe - Read byte at extended address RST &20 DEFB &09 DEFB &3E In: BHL - pointer to the byte to read (B=0, local byte) Out: A - byte at address ..FBCDEHL/IXIY same A............../...... different afbcdehl different gn_ren - Rename file RST &20 DEFB &09 DEFB &66 In: HL - pointer to filename of file to be renamed (HL>255) DE - pointer to replacement name (must be just "filename.ext" , it must not include device or directory segments) Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - errror code RC.ONF (&12) - file not found RC.IVF (&17) - invalid filename RC.EXIS (&19) - file with replacement name already exists RC.USE (&15) - file is in use and cannot be renamed ....BCDEHL/IXIY same AF............/...... different afbcdehl different gn_sdo - Send date and time to standard output RST &20 DEFB &09 DEFB &0E In: HL - points to 6 byte date and time. First 3 bytes are time in internal format, second 3 bytes are date in internal format. Out: - AFBCDEHL/IXIY same afbcdehl different gn_sip - system input line routine RST &20 DEFB &09 DEFB &38 In: DE - buffer for input string A0 - set if buffer already contains data (null terminated) to be edited. The data will be written out by gn_sip A1 - set to force insert/overtype mode (see A2) A2 - 0=insert mode, 1=overtype mode. This is only relevant if A1 is set and is local to the routine A3 - return unexpected characters. This allows for diamond, square and shift sequences to be returned to the user for processing. A4 - return if a wrap occurs A5 - single line lock control. This limits the width of input to the width specified in L and performs horizontal scrolling if required. A6 - display in reverse mode. This inverts an area as long as the length of the buffer (or the line width) and then leaves the reverse mode toggle in the on state. It is not recommended. A7 - Allow for insert/overtype return. If A3=A7=1 then <>V will exit to the users code. This allows the insert/overtype mode to be global for an application ie. as in PipeDream. B - length of buffer C - cursor position (only relevant if A1=1). If C exceeds B then the cursor is placed at the end of the buffer. L - width of line (if A5=1) Out if call succeeded: Fc=0 B - length of line entered, including terminating null C - cursor position on exit A - character which caused end of input Out if call failed: Fc=1 A - return code RC.BAD (&04) - Bad arguments RC.WRAP (&0D) - wrapping has occured (only returned if A4=1) RC.SUSP (&69) - suspicion of suspension RC.DRAW (&66) - application screen needs redrawing RC.QUIT (&67) - kill request RC.ESC (&01) - if escape detection is enabled ...........DEHL/IXIY same AFBC.........../........ different afbcdehl different Notes: Refer to section 2.7 for more detail on the use of gn_sip gn_skc - Skip character RST &20 DEFB &09 DEFB &32 In: A - value to bypass HL=0 - read from stream IX HL=1 - read from filter IX HL>255 - read from (HL) IX - optional handle Out if call successful: Fc=0 HL - points to character which stopped processing (if HL(in) >255) Out if call failed: Fc=1 A - return code ....BCDE..../IXIY same AF........HL/...... different afbcdehl different Notes: This call is somewhat unreliable. It's action is intended to bypass the character, specified by A, in a sequence. Great care should be taken if you intend to use it. gn_skd - Skip to delimiter RST &20 DEFB &09 DEFB &34 In: HL=0 - read from stream IX HL=1 - read from filter IX HL>255 - read from (HL) A - terminator IX - optional handle Out if call succeeded: Fc=0 HL - points to character which stopped processing (if HL(in) >255) A - terminator Fz=1 - terminator seen Fz=0 - terminator not seen Out if call failed: Fc=1 A - error code Notes: Delimiters are tab, space and comma. If the A is set to one of the delimiters then that delimiter is treated as a terminator. gn_skt - Skip to value RST &20 DEFB &09 DEFB &36 In: A - character to search for BC - max. number of characters to search (BC=0 means an unlimited search) HL=0 - read from stream IX HL=1 - read from filter IX HL>255 - read from (HL) IX - optional handle Out if call successful: Fc=0 HL - points to character which stopped processing (if HL(in) >255) BC - zero if BC(in)=0 or else decremented by the number of characters read before processing stopped. A - character to search for Fz=1 - if character found Out if call failed: Fc=1 A - return code ........DE..../IXIY same AFBC....HL/...... different afbcdehl different gn_soe - Write string from extended address RST &20 DEFB &09 DEFB &3C In: BHL - pointer to null terminated string (B=0, HL points to local string) Out: BHL - points to null AFBCDE...../IXIY same ............HL/....... different afbcdehl different gn_sop Write string RST &20 DEFB &09 DEFB &3A In: HL - points to local null-terminated string Out: HL - points to the null AFBCDE..../IXIY same ............HL/...... different afbcdehl different gn_uab - Unlink an alarm block from the alarm chain RST &20 DEFB &09 DEFB &6E In: BHL - address of alarm block Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code ....BCDEHL/IXIY same AF............/...... different afbcdehl different gn_wbe - Write byte at extended address RST &20 DEFB &09 DEFB &40 In: BDE - pointer to address (B=0, DE is local) A - character to write Out: - AFBCDEHL/IXIY same afbcdehl different gn_wcl - Close wildcard handle RST &20 DEFB &09 DEFB &54 In: IX - wildcard handle Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code: RC.HAND - bad handle ......C....HL/....IY same AFB..DE...../IX... different afbcdehl different gn_wfn - Fetch next match for wildcard string RST &20 DEFB &09 DEFB &56 In: DE - Pointer to buffer for explicit name (should be >255) C - Buffer size IX - handle for relevant wildcard Out if call succeeded: Fc=0 DE - Points to null-termination of explicit name B - number of segments in filename C - number of characters in explicit name A - DOR type Out if call failed: Fc=1 A - error code; one of: RC.BAD (&04) - Bad arguments RC.EOF (&09) - No more matches RC.HAND (&08) - Bad handle ......C....HL/IXIY same AFB..DE...../...... different afbcdehl different gn_wsm - Match filename segment to wildcard string RST &20 DEFB &09 DEFB &5C In: HL - pointer to wildcard string (must be >255) DE - pointer to buffer for filename (must be >255) Out if matched: Fz=1 HL - points to wildcard seperator DE - points to filename seperator Out if not matched: Fz=0 HL - same DE - same ....BC......./IXIY same AF....DEHL/...... different afbcdehl different Notes: This matches (or attempts to) a single segment of a filename. gn_xdl - Delete an entry from a linked list RST &20 DEFB &09 DEFB &48 In: CDE - previous entry BHL - entry to delete Out if call succeeded: Fc=0 CDE - Prior entry BHL - Entry after deleted entry Out if call failed: Fc=1 A - return code: RC.BAD (&04) - if BHL(in)=0 ......CDEHL/IXIY same AFB........../...... different afbcdehl different gn_xin - Insert an entry into a linked list RST &20 DEFB &09 DEFB &46 In: HL - pointer to 9-byte parameter block: (HL+0)...(HL+2) Address of block to insert (HL+3)...(HL+5) Address of previous block (HL+6)...(HL+8) Address of next block Out if call succeeded: Fc=0 Out if call cleared: Fc=1 A - return code: RC.BAD (&04) - if block to insert = 0. ......BCDEHL/IXIY same AF............../...... different afbcdehl different gn_xnx - Index next entry in linked list RST &20 DEFB &09 DEFB &44 In: CDE - address of previous entry BHL - address of current entry Out: Fc=0 CDE - address of old current entry BHL - address of next entry Fc=1 if BHL(in)=0, else 0. In this case CDE and BHL are the same. Fz=1 if BHL(out)=0 ................/IXIY same AFBCDEHL/....... different afbcdehl different (*SYSTEM*) os_alm - Alarm manipulation RST &20 DEFB &81 (*SYSTEM*) os_axp - Allocate explicit page. RST &20 DEFB &06 DEFB &D2 os_bde - Copy bytes to extended address RST &20 DEFB &06 DEFB &DA In: C - number of bytes to move (0...255) HL - Logical 'from' pointer BDE - Extended 'to' pointer. Out: Fc=0 - always A...BCDEHL/IXIY same ...F................/......... different afbcdehl different Notes: This call moves C bytes from (HL) to (BDE), like an extended LDIR. os_bhl - Copy bytes from extended address RST &20 DEFB &06 DEFB &DC In: C - number of bytes to copy (0...255) BHL - extended 'from' pointer DE - logical 'to' pointer Out: Fc=0 - always. A...BCDEHL/IXIY same ....F.........../...... different afbcdehl different Notes: This call copies C bytes from (BHL) to (DE) like an extended LDIR. (*SYSTEM*) os_bix - Bind in extended address RST &20 DEFB &60 os_blp Bleep RST &20 DEFB &06 DEFB &D8 In: A - sound count B - space count C - mark count Out: Fc=0 - always ..........DEHL/IXIY same AFBC........../......... different afbcdehl different. (*SYSTEM*) os_box - Restore bindings after 'os_bix' RST &20 DEFB &63 os_bye - Exit application RST &20 DEFB &21 In: A - error message to display on termination. If A=0, no message. Out: - Notes: This is the call which the user should use to terminate his own application; in particular in the error handler when responding to a <>KILL request. Note that this call does NOT automatically close files and deallocate memory - the application must do this before making the call. Register changes are not fixed, as the code previously executing is not re-entered. (*SYSTEM*) os_cl Internal close RST &20 DEFB &06 DEFB &E8 Notes: This is an internal call to close a file - the user should use 'gn_cl' instead. (*SYSTEM*) os_cli CLI interface RST &20 DEFB &84 (*SYSTEM*) os_del - File delete RST &20 DEFB &06 DEFB &E6 Notes: This is the internal delete and should not need to be used. os_dly - delay a given period RST &20 DEFB &06 DEFB &D6 In: BC - delay in centiseconds Out if call failed: Fc=1 BC - remaining time out A - error code RC.ESC(&01) - escape RC.SUSP(&69) - process suspended RC.TIME(&02) - timeout ..........DEHL/IXIY same AFBC........../......... different afbcdehl different (*SYSTEM*) os_dom - Open director memory RST &20 DEFB &06 DEFB &FE Note: This is a call for Index use only and must not be used by applications. os_dor - the DOR interface RST &20 DEFB &87 In: A - reason code HLIX - arguments Out if call succeeded: Fc=0 Returned values depend on A(in) Out if call failed: Fc=1 A - return code RC.HAND (&08) RC.ROOM (&07) Reason codes are as follows: DR.GET (&01) - get handle for a DOR name DR.DUP (&02) - duplicate DOR DR.SIB (&03) - return brother DOR DR.SON (&04) - return child DOR DR.FRE (&05) - free DOR handle DR.CRE (&06) - create blank DOR DR.DEL (&07) - delete DOR DR.INS (&08) - insert DOR DR.RD (&09) - read DOR record DR.WR (&0A) - write DOR record Notes: The individual call specifications are detailed in section 2.15 (*SYTEM*) os_ent - Enter an application RST &20 DEFB &06 DEFB &FA (*SYSTEM*) os_epr - eprom program routine RST &20 DEFB &06 DEFB &FO Notes: This call is of no use to applications. (*SYSTEM*) os_erc - Get error context RST &20 DEFB &72 os_erh - set error handler RST &20 DEFB &75 In: A - Call level (this is designed for use in system calls themselves and should be zero for normal use). HL - Address of new error handler, or else HL=0 means restore default system error handler, which essentially ignores errors. B=0 Out: Fc=0 A - Old call level. HL - Address of old error handler. ......CDE..../IXIY same AFB......HL/...... different afbcdehl different os_esc - Examine special condition RST &20 DEFB &6F In: A=0 - Test for escape. Returns Fc=1 if escape has been pressed, else Fc=0. Note that this resets the machine timeout. A=1 - Acknowlege escape and also flush input buffer. Fz=1 if there was no escape to acknowlege, else Fz=0. Again, resets machine timeout. A=2 - Set escape, ie. simulate an escape condition. A=3 - Reset escape without flushing input buffer. A=4 - Test if escape detection is enabled or disabled. Fc=0 always, and A=5 if it is enabled, A=6 if it is disabled. A=5- Enable escape detection A=6 - Disable escape detection. Unless otherwise stated, Fc=0 may be assumed. ....BCDEHL/IXIY same AF............/...... different afbcdehl different (*SYSTEM*) os_exit - Quit process RST &20 DEFB &06 DEFB &F6 os_fc - Select fast code RST &20 DEFB &8A In: A=0 (for fast bank switching) DE - address to copy code into HL=0 - for code to terminate with RET HL<>0 for code to terminate with JP to value passed in HL C - segment for bank switching Out: Fc=0 - unless A(in)<>0 ....BCDEHL/IXIY same AF............/...... different afbcdehl different Notes: See section 2.19 and 2.20 for more details on bank switching. This after the call the following interface is esablished: In: A - bank to bind Out: A..BCDEHL/IXIY same ..F............/..... different afbcdehl same (*SYSTEM*) os_fn - Miscellaneous os functions RST &20 DEFB &7B Notes: Not useful to applications. os_frm - File read miscellaeneous RST &20 DEFB &48 In: A - reason code FA.PTR (&01) - sequential pointer FA.EXT (&02) - extent (size) of file FA.EOF (&03) - end of file enquiry FA.BST (&04) - buffer status (system use) IX - file handle DE=0 Out if call succeeded: Fc=0 DEBC - contain 32 bit result (for FA.PTR, FA.EXT, FA.BST) Fz=1 - end of file (for FA.EOF) Fz=0 - not end of file (for FA.EOF) Out if call failed: Fc=1 A - error code RC.HAND - bad handle ............HL/IXIY same AFBCDE..../....... different afbcdehl different (*SYSTEM*) os_fth - Free tri-handle RST &20 DEFB &06 DEFB &DE Notes: Tri-handle are for internal use. os_fwm - File write miscellaeneous RST &20 DEFB &4B In: A - reason code FA.PTR (&01) - sequential pointer FA.EXT (&02) - extent (size) of file HL - points to a 4 byte vector containing value to be written (low byte first). IX - file handle Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code RC.HAND (&08) - bad handle RC.BAD (&04) - bad reason code in A ......BCDEHL/IXIY same AF............../...... different afbcdehl different os_gb - Get byte from file or device RST &20 DEFB &39 In: IX - Handle Output if call succeeded: Fc = 0 A - Byte read from file Out if call failed: Fc = 1 A - error code, one of: RC.EOF (&09) - End of file reached RC.HAND (&08) - Bad handle supplied in IX RC.RP (&13) - File/device is read-protected (eg. ":SCR.0"). Plus pre-emption return codes. ....BCDEHL/IXIY same AF............/...... different afbcdehl different os_gbt - file get byte with timeout RST &20 DEFB &3F In: BC - timeout in ticks IX - file handle Out if call succeeded: Fc=0 - if byte read OK A - byte read from file Out if call failed: Fc=1 - exception condition A - error code RC.EOF (&09) - end of file RC.NA (&06) - not available RC.HAND (&08) - bad handle RC.TIME (&02) - timeout RC.ESC (&01) - escape RC.SUSP (&69) - m/c revived nr process preempted RC.DRAW (&66) - redraw screen RC.RP (&13) - read protected ........DEHL/IXIY same AFBC......../...... different afbcdehl different (*SYSTEM*) os_gth - allocate tri-handle RST &20 DEFB &06 DEFB &E2 Note: Tri-handle are used internally, but are not useful for applications. os_ht - hardware time manipulation RST &20 DEFB &06 DEFB &F2 In: A - reason code HT.RES (&01) - reset timer HT.RD (&02) - read timer HL - pointer to a 5 byte buffer Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - return code A..........HL/IXIY same ..FBCDE..../....... different afbcdehl different os_in - Read character from standard input RST &20 DEFB &2A In: - Out if call succeeded: Fc=0 A - character read Out if call was preempted: Fc=1 A - return code RC.SUSP (&69) - process suspended or machine revived RC.DRAW (&66) - process suspended and screen corrupted RC.QUIT (&67) - kill request RC.ESC (&01) - escape condition detected. ....BCDEHL/IXIY same AF............/...... different afbcdehl different Notes: This call waits until the result is available (see os_tin for a version incorporating a timeout) (*SYSTEM*) os_isq - Initialise prefix sequence RST &20 DEFB &06 DEFB &D0 os_mal - allocate memory RST &20 DEFB &54 In: IX - memory handle (returned from previous call of 'os_mop') BC - requested size of reserved memory (should be from 2 to 256 inclusive) A - should be zero (reserved for future expansion). Out if call succeeded: Fc=0 HL - address of memory allocated. Upper 2 bits will be defined by the memory mask given to the 'os_mop' call, so if this reflects the segment in which the memory will be used, this can be used as the logical address of the block once the relevant bank has been bound in. B - bank number of allocated memory. Notice that this bank is not automatically bound into the logical address space. C - segment specifier implied by HL. Out if call failed: Fc=1 A - error code, one of: RC.HAND (&08) - IX is not a valid memory handle RC.ROOM (&07) - no room to allocate block. ........DE..../IXIY same AFBC....HL/...... different afbcdehl different os_map - Pipedream map control RST &20 DEFB &06 DEFB &F4 In: BC - reason code MP.WR (&01) - write a line to the map MP.DEF (&02) - define a map of specific width MP.GRA (&03) - define a map using the Panel width MP.DEL (&04) - delete a map Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - return code RC.BAD (&04) - bad parameters (errors are generally ignored) RC.UNK (&03) - value of BC is not valid Notes: See section 2.19 for more detail. os_mcl - Close memory (free memory pool) RST &20 DEFB &51 In: IX - memory handle (returned from previous 'os_mop' call) Out if call succeeded: Fc=0 IX=0 Out if call failed: Fc=1 A=error code, RC.HAND (&08) - IX(in) was not a valid memory handle ....BCDEHL/....IY same AF............/IX... different afbcdehl different os_mfr - Free memory RST &20 DEFB &57 In: IX - memory handle (returned from previous 'os_mop' call) BC - size of memory to release AHL - extended address of memory to release. Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code; one of: RC.HAND (&08) - IX is not a valid memory handle RC.BAD (&04) - invalid AHL/BC/IX combination (eg. BC>256 or AHL points to a block not allocated from pool with handle IX etc.) ....BCDEHL/IXIY same AF............/...... different afbcdehl different os_mgb - Get current binding. ie which bank is bound to a segment. RST &20 DEFB &5A In: C - memory segment specifier (0,1,2 or 3) Out if call succeeded: Fc=0 B - bank of memory currently bound to that segment. Out if call failed: Fc=1 A - error code RC.BAD (&04) - C was not valid ......CDEHL/IXIY same AFB........../...... different afbcdehl different os_mop - Open memory (allocate memory pool) RST &20 DEFB &4E In: A - memory mask. This should be one of &00, &40, &80 and &C0. The mask is ORed with the high byte of the address returned by allocation calls with this handle. BC=0 - reserved for future expansion. Output if call succeeded: Fc=0 IX=memory handle Out if call failed: Fc=1 A - error code, one of: RC.NA (&06) - handle not available RC.ROOM (&07) - no room. A..BCDEHL/....IY same ..F............/IX.... different afbcdehl different. os_mpb - Set new binding, ie. bind a bank to a segment. RST &20 DEFB &5D In: C - memory segment specifier (0,1,2 or 3) B - Bank number to bind into this segment. Out if call succeeded: Fc=0 B - bank previously bound to the relevant segment. Out if call failed: Fc=1 A - error code, which will be RC.BAD (&04) ie. invalid C. ......CDEHL/IXIY same AFB........../...... different afbcdehl different os_mv - Move bytes between stream and memory RST &20 DEFB &45 In: BC - number of bytes to move IX - handle for relevant file DE=0 - Move data from memory, starting at (HL), to the file. HL=0 - Move data from file to memory, staring at (DE). Out if call was (perhaps partially) successful: Fc=0 BC - number of bytes not read (eg. if end of file was reached before the BC(in)'th character). DE(in)=0, HL - points to next byte to read, DE=0 HL(in)=0, DE - points to next byte to write, HL=0 Out if call failed: Fc=1 BC - number of bytes not read (eg. if end of file was reached before the BC(in)'th character). A - error code, one of: RC.EOF (&09) - end of file reached at some stage. RC.HAND (&08) - IX contained a bad handle. RC.RP (&13) - attempt made to read from read-protected file. RC.WP (&14) - attempt made to write to write-protected file. Plus pre-emption return codes. ................/IXIY same AFBCDEHL/........ different afbcdehl same os_nq - Enquire RST &20 DEFB &66 In: BC - reason code Out: Depends on BC and other arguments Notes: See section 2.19 for full details of this call os_off - Switch machine off RST &20 DEFB &06 DEFB &EC In: - Out: - A..BCDEHL/IXIY same ..F............/...... different afbcdehl different Notes: This call really does turn the machine off. (*SYSTEM*) os_op - Internal open RST &20 DEFB &06 DEFB &EA os_out - Write character to standard output RST &20 DEFB &27 In: A - character to be written Out: Fc=0 always A..BCDEHL/IXIY same ..F............/...... different afbcdehl different os_pb - File put byte RST &20 DEFB &3C In: A - byte to be written IX - handle Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code, one of: RC.HAND (&08) - bad handle RC.WP (&14) - write protected (eg. ":INP.0"). os_pbt - file put byte with timeout RST &20 DEFB &42 In: A - byte to send BC - timeout in centiseconds IX - handle Out if call succeeded: BC - time remaining Fc=0 - byte written OK Out if call failed: Fc=1 BC - remaining time in centiseconds A - error code RC.NA (&06) - not available RC.HAND (&08) - bad handle RC.TIME (&02)- timeout RC.WP (&14) - write protected ........DEHL/IXIY same AFBC......../...... different afbcdehl different (*SYSTEM*) os_poll - Poll for an application RST &20 DEFB &06 DEFB &FC Notes: This is used by the Index. os_prt - send character directly to printer filter RST &20 DEFB &24 In: A - character to be sent to printer filter Out if call succeeded: Fc=0 Out if call failed: Fc=1 A - error code BC - remaining time RC.NA (&06) - not available RC.WP (&14) - write protected ....BCDEHL/IXIY same AF............/...... different afbcdehl different os_pur - purge keyboard buffer RST &20 DEFB &33 In: - Out if call succeeded: Fc=0 - keyboard purged Out if call failed: Fc=1 A - error code A..BCDEHL/IXIY same ..F............/...... different afbcdehl different (*SYSTEM*) os_ren - file rename RST &20 DEFB &06 DEFB &E4 os_sci - alter screen info RST &20 DEFB &06 DEFB &D4 In: A - reason code SC.LR1 (&01) - LORES1 (512 byte granularity) SC.LR0 (&02) - LORES0 (4K granularity) SC.HR0 (&03) - HIRES0 (8K granularity) SC.HR1 (&04) - HIRES1 (2K granularity) SC.SBR (&05) - screen base (2K byte granularity) BHL - new address Out if call succeeded: Fc=0 BHL - old address os_si - serial interface RST &20 DEFB &8D In: L - reason code Out: Depends on reason code and other arguments Notes: Full details of this call are found in section 2.13 os_sp - specify RST &20 DEFB &69 In: BC - reason code HL - pointer to data (sometimes BHL) A - length of data (sometimes not used) Out: Depends on reason code and other arguments. Notes: Full details of this call are provided in section 2.19 os_sr - save and restore RST &20 DEFB &6C In: A - reason code SR.SUS (&01) - save user screen SR.RUS (&02) - restore user screen SR.WPD (&03) - write parameter data (system use) SR.RPD (&04) - read parameter data (system use) SR.FUS (&05) - free user screen SR.CRM (&06) - remove card (system use) SR.CIN (&07) - insert card (system use) SR.PWT (&08) - page wait SR.RND (&09) - somtimes a random number BCDEHLIX - arguments Out if call succeeded: Fc=0 Returned values depend on A(in) Out if call failed: Fc=1 A - return code RC.UNK (&03) - unkown request RC.BAD (&04) - bad parameters RC.HAND (&08) - bad handle Page Wait can return all the standard pre-emption codes. (*SYSTEM*) os_stk - Stack file current process RST &20 DEFB &06 DEFB &F8 os_tin - Read character from standard input, with timeout RST &20 DEFB &2D In: BC - timeout in centisecond ticks Out if call succeeded: Fc=0 A - character read Out if call was preempted: Fc=1 A - return code; one of: RC.SUSP (&69) - process suspended or machine revived RC.DRAW (&66) - process suspended and screen corrupted. RC.QUIT (&67) - KILL request RC.ESC (&01) - escape condition detected RC.TIME (&02) - call has timed out os_ugb - unget byte RST &20 DEFB &36 Notes: This call got ungot from the system. (*SYSTEM*) os_use - Fetch information about process card useage RST &20 DEFB &06 DEFB &EE os_ust - update small timer RST &20 DEFB &78 In: BC - new timer value Out if call succeeded: Fc=0 BC - old timer value Fz=0 old timer non-zero Fz=1 old timer zero A - RC.TIME (&02) (*SYSTEM*) os_vth - verify tri-handle RST &20 DEFB &06 DEFB &EO (*SYSTEM*) os_wait - wait for event RST &20 DEFB &7E os_wrt - write token RST &20 DEFB &06 DEFB &CC In: A - token (top bit set) Out if call succeeded: Fc=0 A..BCDEHL/IXIY same ..F............/...... different afbcdehl different (*SYSTEM*) os_wsq - write to prefix sequence RST &20 DEFB &06 DEFB &CE os_wtb - write token base RST &20 DEFB &06 DEFB &CA In: BHL - token table base address. See section 3 for format of token table. Out if call succeeded: Fc=0 BHL -> old token base Note: The tokens are local and independent of the MTH tokens. os_xin - examine input RST &20 DEFB &30 In: IX - channel Out if call succeeded: Fc=0 - input MAY BE available Out if call failed: Fc=1 A - error code RC.EOF (&09) - os_in will not return immediately if called RC.HAND (&08) - bad handle ....BCDEHL/IXIY same AF............/.... different afbcdehl different

4.3 System manifests

Sytem Error Codes &00 0 RC.OK Success! &01 1 RC.ESC Escape condition &02 2 RC.TIME Timeout &03 3 RC.UNK Unknown request &04 4 RC.BAD Bad argument(s) &05 5 RC.MS Bad memory segment specifier &06 6 RC.NA Call not implemented &07 7 RC.ROOM No room &08 8 RC.HAND Bad handle &09 9 RC.EOF End of file (or end of filter etc.) &0A 10 RC.FLF Filter full &0B 11 RC.OVF Overflow &0C 12 RC.SNTX Syntax error &0D 13 RC.WRAP Wrap condition met &0E 14 RC.PUSH Pushback error (can only pushback one character) &0F 15 RC.ERR Internal error &10 16 RC.TYPE Unexpected type &11 17 RC.PRE Cannot pre-empt &12 18 RC.ONF Object (ie. file or directory) not found &13 19 RC.RP Read protected &14 20 RC.WP Write protected &15 21 RC.USE In use &16 22 RC.FAIL General failure to perform request &17 23 RC.IVF Invalid filename &18 24 RC.FTM File type mismatch &19 25 RC.EXIS File already exists &32 50 RC.ADDR Bad address &33 51 RC.SIZE Bad size &34 52 RC.BANK Bad bank &35 53 RC.FRM Frame error &36 54 RC.PAR Parity error &46 70 RC.DVZ Division by zero &47 71 RC.TBG Too big &48 72 RC.NVR Negative root &49 73 RC.LGR Log range &50 74 RC.ACL Accuracy lost &51 75 RC.EXR Exp (exponent function) range &52 76 RC.BDN Bad number &66 102 RC.DRAW Application preempted and screen corrupted &67 103 RC.QUIT Request application to quit &69 105 RC.SUSP Application suspended or machine revived. Operating system calls The following is a list of manifests for the operating system calls. It is recommended that you use manifests, not the numbers directly, and that you define a macro in your assembler to decide whether the call requires one or two bytes following the restart. In BBC BASIC the manifests can be BASIC variable and a function can be used as a macro. When this function is present the source of a system looks like this: OPT FNsys(gn_opf) 60000 DEF FNsys(a) 60010 IF a>255 THEN [ OPT pass:RST &20:DEFW a:]:=pass 60020 [ OPT pass:RST &20:DEFB a:]:=pass dc_alt = &1A0C dc_bye = &080C dc_ent = &0A0C dc_gen = &200C dc_icl = &140C dc_in = &0E0C dc_ini = &060C dc_nam = &0C0C dc_nq = &180C dc_out = &100C dc_pol = &220C dc_prt = &120C dc_rbd = &1C0C dc_scn = &240C dc_sp = &180C dc_xin = &1D0C gn_aab = &6809 gn_alp = &7009 gn_cl = &6209 gn_cls = &3009 gn_cme = &4209 gn_d16 = &7409 gn_d24 = &7809 gn_dei = &1609 gn_del = &6409 gn_die = &1409 gn_err = &4A09 gn_esa = &5E09 gn_esp = &4C09 gn_fab = &6A09 gn_fcm = &4E09 gn_fex = &5009 gn_flc = &2409 gn_flf = &2A09 gn_flo = &2209 gn_flr = &2809 gn_flw = &2609 gn_fpb = &2C09 gn_gdn = &1009 gn_gdt = &0609 gn_gmd = &1809 gn_gmt = &1A09 gn_gtm = &0A09 gn_lab = &6C09 gn_m16 = &7209 gn_m24 = &7609 gn_msc = &2009 gn_nln = &2E09 gn_opf = &6009 gn_opw = &5209 gn_pdn = &1209 gn_pdt = &0809 gn_pfs = &5A09 gn_pmd = &1C09 gn_pmt = &1E09 gn_prs = &5809 gn_ptm = &0C09 gn_rbe = &3E09 gn_ren = &6609 gn_sdo = &0E09 gn_sip = &3809 gn_skc = &3209 gn_skd = &3409 gn_skt = &3609 gn_soe = &3C09 gn_sop = &3A09 gn_uab = &6E09 gn_wbe = &4009 gn_wcl = &5409 gn_wfn = &5609 gn_wsm = &5C09 gn_xdl = &4809 gn_xin = &4609 gn_xnx = &4409 os_alm = &81 os_axp = &D206 os_bde = &DA06 os_bhl = &DC06 os_bix = &60 os_blp = &D806 os_box = &63 os_bye = &21 os_cl = &E806 os_cli = &84 os_del = &E606 os_dly = &D606 os_dom = &FE06 os_dor = &87 os_ent = &FA06 os_epr = &F006 os_erc = &72 os_erh = &75 os_esc = &6F os_exit = &F606 os_fc = &8A os_fn = &7B os_frm = &48 os_fth = &DE06 os_fwm = &4B os_gb = &39 os_gbt = &3F os_gth = &E206 os_ht = &F206 os_in = &2A os_isq = &D006 os_mal = &54 os_map = &F406 os_mcl = &51 os_mfr = &57 os_mgb = &5A os_mop = &4E os_mpb = &5D os_mv = &45 os_nq = &66 os_off = &EC06 os_op = &EA06 os_out = &27 os_pb = &3C os_pbt = &42 os_poll = &FC06 os_prt = &24 os_pur = &33 os_ren = &E406 os_sci = &D406 os_si = &8D os_sp = &69 os_sr = &6C os_stk = &F806 os_tin = &2D os_ugb = &36 os_use = &EE06 os_ust = &78 os_vth = &E006 os_wait = &7E os_wrt = &CC06 os_wsq = &CE06 os_wtb = &CA06 os_xin = &30

Appendix A - Z80 Instruction Set


In the following list, which is intended for reference only, the
following shorthands for groups of possible operands are used:

n       1-byte number
nn      2-byte number
r       register; one of A, B, C, D, E, H, L
rr      register pair, one of BC, DE, HL and either AF or SP
        according to circumstances
s       General operand; a register or (HL), and in some
        circumstances a 1-byte number
e       1-byte number representing displacement from
        current (after execution of current instruction) PC
cc      Condition; one of Z (zero), NZ (not zero), C (carry),
        NC (no carry), P (plus), M (minus), PO (parity odd),
        PE (parity even)

ADC HL,rr       Add with Carry Reg. pair rr to HL
ADC A,s         Add with Carry operand s to A
ADD A,n         Add value n to A
ADD A,r         Add Reg. r to A
ADD A, (HL)     Add (HL) to A
ADD A, (IX+d)   Add (IX+d) to A
ADD A, (IY+d)   Add (IY+d) to A
ADD HL, rr      Add Reg. pair rr to HL
ADD IX, rr      Add Reg. pair rr to IX
ADD IY, rr      Add Reg. pair rr to IY
AND s           Logical 'AND' operand s with A
BIT b, (HL)     Test bit b of (HL)
BIT b, (IX+d)   Test bit b of (IX+d)
BIT b, (IY+d)   Test bit b of (IY+d)
BIT b, r        Test bit b of Reg. r
CALL cc, nn     Call subroutine at location nn if condition cc is true
CALL nn         Unconditionally call subroutine at location nn
CCF             Complement carry flag
CP s            Compare operand s with A
CPD             Compare (HL) with A; decrement HL and BC
CPDR            Compare (HL) with A; decrement HL and BC;
                repeat until BC=O or A=(HL)
CPI             Compare (HL) with A; increment HL and
                decrement BC
CPIR            Compare (HL) and A; increment HL,
                decrement BC, repeat until BC=0
CPL             Complement A (1's comp)
DAA             Decimal adjust A
DEC s           Decrement operand s
DEC IX          Decrement IX
DEC IY          Decrement IY
DEC rr          Decrement Reg. pair rr
DI              Disable interrupts (Do not use, a call is provided.)
DJNZ e          Decrement B and Jump relative e iff B=O
EI              Enable interupts (Do not use, a call is provided.)
EX (SP),HL      Exchange (SP) and HL
EX(SP), IX      Exchange (SP) and IX
EX (SP), IY     Exchange (SP) and IY
EX AF, AF'      Exchange the contents of AF and af
EX DE, HL       Exchange the contents of DE and HL
EXX             Exchange the contents of BC, DE, HL with contents of
                bc, de and hl respectively

HALT            HALT (wait for interrupt or reset)
                Warning! Do not use the HALT instruction in
                code for the Z88.

IM O            Set interrupt mode O (Do not use)
IM 1            Set interrupt mode 1 (Do not use)
IM 2            Set interrupt mode 2 (Do not use)
IN A, (n)       Load A from input port n
IN r, (C)       Load the Reg. r from input port (C)
INC (HL)        Increment (HL)
INC IX          Increment IX
INC (IX+d)      Increment (IX+d)
INC IY          Increment IY
INC (IY+d)      Increment (IY+d)
INC r           Increment Reg. r
INC rr          Increment Reg. pair rr
IND             Load (HL) with input from port (C), decrement HL
                and B
INDR            Load (HL) with input from port (C), decrement HL
                and decrement B, repeat until B=O
INI             Load (HL) with input from port (C); increment HL
                and decrement B
INIR            Load (HL) with input from port (C), increment HL
                and decrement B, repeat until B=O
JP (HL)         Unconditional Jump to (HL)
JP (IX)         Unconditional Jump to (IX)
JP (IY)         Unconditional Jump to (IY)
JP cc, nn       Jump to location nn if condition cc is true
JP nn           Unconditional jump to location nn
JP C, e         Jump relative to PC+e if Fc=1
JR e            Unconditional Jump relative e
JP NC, e        Jump relative e if Fc=0
JR NZ, e        Jump relative e if non zero, Fz=0
JR Z, e         Jump relative e if zero, Fz=1
LD A, (BC)      Load A with (BC)
LD A, (DE)      Load A with (DE)
LD A, I         Load A with I
LD A, (nn)      Load A from location nn
LD A, R         Load A with Reg. R
LD (BC), A      Load (BC) with A
LD (DE), A      Load (DE) with A
LD (HL), n      Load (HL) with value n
LD rr, nn       Load Reg. pair rr with value nn
LD HL, (nn)     Load HL from location nn
LD (JHL), r     Load (HL) with Reg. r
LD I, A         Load I with A
LF IX, nn       Load IX with value nn
LD IX, (nn)     Load IX from location nn
LD (IX+d), n    Load (IX+d) with value n
LD (IX+d), r    Load (IX+d) with Reg. r
LD IY, nn       Load IY with value nn
LD IY, (nn)     Load IY from location nn
LD (IY=d),n     Load  (IY+d) with value n
LD (IY+d),r     Load  (IY+d) with Reg. r
LD (nn), A      Load  (nn) with A
LD (nn), rr     Load  (nn) with Reg. pair rr
LD (nn), HL     Load  (nn) with HL
LD (nn), IX     Load  (nn) with IX
LD (nn), IY     Load  (nn) with IY
LD R, A         Load R with A
LD r, (HL)      Load Reg. r with  (HL)
LD r, (IX+d)    Load Reg. r with  (IX+d)
LD r, (IY+d)    Load Reg. r with  (IY+d)
LD r, n         Load Reg. r with value n
LD r, r'        Load Reg. r with Reg. r'
LD SP, HL       Load SP with HL
LD SP, IX       Load SP with IX
LD SP, IY       Load SP with IY
LDD             Load (DE) with (HL), decrement DE, HL and BC
LDDR            Load (DE) with (HL), decrement DE, HL and BC;
                repeat until BC=O
LDI             Load (DE) with (HL), increment DE, HL, decrement BC
LDIR            Load (DE) with (HL), increment DE, HL,
                decrement BC and repeat until BC=O
NEG             Negate A (2's complement)
NOP             No operation
OR s            Logical 'OR' operand s with A
OTDR            Load output port (C) with (HL) decrement HL and B,
                repeat until B=O
OTIR            Load output port (C) with (HL), increment HL,
                decrement B, repeat until B=O
OUT (C), r      Load output port (C) with Reg. r
OUT (n), A      Load output port (n) with A
OUTD            Load output port (C) with location (HL), decrement
                HL and B
OUTI            Load output port (C) with location (HL), increment
                HL and decrement B
POP IX          Load IX with top of stack
POP IY          Load IY with top of stack
POP qq          Load Reg. pair qq with top of stack
PUSH IX         Load IX onto stack
PUSH IY         Load IY onto stack
PUSH qq         Load Reg. pair qq onto stack
RES b, m        Reset bit b of operand m
RET             Return from subroutine
RET cc          Return from subroutine if condition cc is true
RETI            Return from interrupt
RETN            Return from non maskable interrupt
RL s            Rotate left through carry operand s
RLA             Rotate left A through carry
RLC (HL)        Rotate (HL) left with carry
RLC (IX+d)      Rotate (IX+d) left with carry
RLC (IY+d)      Rotate (IY+d) left with carry
RLC r           Rotate Reg. r left with carry
RLCA            Rotate A left with carry
RLD             Rotate left decimal between A and (HL)
RR s            Rotate right through carry operand s
RRA             Rotate right A through carry
RRC m           Rotate operand m right with carry
RRCA            Rotate right with carry A
RRD             Rotate right decimal between A and location (HL)
RST p           Restart at location p
SBC A, s        Subtract operand s from A with carry
SBC HL, rr      Subtract Reg. pair rr from HL with carry
SCF             Set carry flag (Fc=1)
SET b, (HL)     Set bit b of (HL)
SET b, (IX+d)   Set bit b of (IX+d)
SET b, (IY+d)   Set bit b of (IY+d)
SET b, r        Set bit b of Reg. r
SLA s           Shift operand s left arithmetic
SRA s           Shift operand s right arithmetic
SRL s           Shift operand s right logical
SUB s           Subtract operand s from A
XOR s           Exclusive 'OR' operand s and A

Appendix B - Z88 Character Set

        Hex     Decimal Code    Description

        &20     32              Space
        &21     33      !
        &22     34      "
        &23     35      #
        &24     36      $
        &25     37      %
        &26     38      &
        &27     39      '
        &28     40      (
        &29     41      )
        &2A     42      *
        &2B     43      +
        &2C     44      ,
        &2D     45      -
        &2E     46      .
        &2F     47      /
        &30     48      0
        &31     49      1
        &32     50      2
        &33     51      3
        &34     52      4
        &35     53      5
        &36     54      6
        &37     55      7
        &38     56      8
        &39     57      9
        &3A     58      :
        &3B     59      ;
        &3C     60      <
        &3D     61      =
        &3E     62      >
        &3F     63      ?
        &40     64      @
        &41     65      A
        &42     66      B
        &43     67      C
        &44     68      D
        &45     69      E
        &46     70      F
        &47     71      G
        &48     72      H
        &49     73      I
        &4A     74      J
        &4B     75      K
        &4C     76      L
        &4D     77      M
        &4E     78      N
        &4F     79      O
        &50     80      P
        &51     81      Q
        &52     82      R
        &53     83      S
        &54     84      T
        &55     85      U
        &56     86      V
        &57     87      W
        &58     88      X
        &59     89      Y
        &5A     90      Z
        &5B     91      [
        &5C     92      \
        &5D     93      ]
        &5E     94      ^
        &5F     95      _
        &60     96      <>'     `
        &61     97      a
        &62     98      b
        &63     99      c
        &64     100     d
        &65     101     e
        &66     102     f
        &67     103     g
        &68     104     h
        &69     105     i
        &6A     106     j
        &6B     107     k
        &6C     108     l
        &6D     109     m
        &6E     110     n
        &6F     111     o
        &70     112     p
        &71     113     q
        &72     114     r
        &73     115     s
        &74     116     t
        &75     117     u
        &76     118     v
        &77     119     w
        &78     120     x
        &79     121     y
        &7A     122     z
        &7B     123     {
        &7C     124     |
        &7D     125     }
        &7E     126     ~
        &7F     127     DEL     Delete
        &A0     160     <>SPACE Exact Space
        &A3     163     £       Pound

Appendix C - Miscellaneous Features


The Expanded and non-Expanded Machine

When a Z88 has 128K or more of RAM in slot 1 it becomes an expanded
machine. The differences between the expanded and non-expanded are as
follows:

Property                Expanded        Non-Expanded
-------------------------------------------------------------------------

Size of BASIC           40K             8K
Maximum map width       256 pixels      80 pixels
User characters         64              16 (but see below)
Value of EOF#-1         -1              0
-------------------------------------------------------------------------


Some users will want to use extra memory, for filing for example, without
the burden of a 40K BASIC. If all your BASIC programs fit comfortably
within the ordinary 8K it is unhelpful to have to carry the extra 32K
around, thus using slot 2 or slot 3 to expand the memory does not expand
the machine, but just increases memory size. (Note that using Slot 3 for
RAM causes a very heavy power drain on the Z88, due to special hardware
for programming EPROMs.) The unexpanded machine can use 64 user
characters, but if an 80 pixel map is used the last 48 of these will be
overwritten by map information when PipeDream is used. Reducing the map
width to 64 pixels, or not using the map at all, allows for free use of
all 64 user characters. The file attributes for BASIC's -1 channel all
hold interesting information:

Attribute       Information
-------------------------------------------------------------------------

PTR#-1          high word= no. of free handles in the
                system
                low word= ROM version code
                (= &03 for 2.2 and 3.0 versions)
EXT#-1          free memory
                this is an approximation and should be
                interpreted with care. It is similar to the
                memory free indicators given by
                PipeDream and the Diary.

EOF#-1          True=Expanded, False=Un-Expanded
-------------------------------------------------------------------------


This information can be accessed by applictions by using the os_frm, to
read file attributes, and specifying a handle of &FFFF in IX. Note that
although the sequential pointer (PTR) and extent (EXT) are returned as 4
byte values in DE and BC, the end of file (EOF) result is returned in Fz,
with Fz=1 for end of file (ie. TRUE or -1) and Fz=0 otherwise (ie. FALSE
or 0).


Extra CLI features CLI

square+S                T-redirect output to :RAM.-/S.sgn
square+K                T-redirect input to  :RAM.-/K.sgn
square+P                T-redirect output to :PRT.0

If there is an EPROM, which contains a file called "boot.cli", present in
slot 3 at the time of a reset then it will be loaded into :RAM.- and
executed as a CLI file.

Note: :RAM.- files are dangerous under the current version of the
operating system (2.2/3.0) - see next section for details.


Memory for Files and Applications

Each RAM slot has an allocation of space which is useable by the RAM
device associated with that slot. This allocation is always less than
total RAM available in that slot. Applications (and the special device
:RAM.-) can use the remaining memory and the memory allocated to files.
Because of this approach the values given for free memory are sometimes
confusing. The free memory indicators return space available to
applications, which will generally be rather less than that available to
files.

The device :RAM.- can use memory from anywhere in the system, and as
such is it very useful for large files. Due to a bug in the current
operating system (version 2.2/3.0) if any files are stored in the device
when a Soft Reset occurs, the system becomes badly confused, and is it
very likely that a crash will follow sometime later. If you need to use
:RAM.- files, then it is vital that they are deleted immediately after
use. The features decscribed in the previous section all use :RAM.- and
if these operations are used then the files generated should be deleted
at the first oppurtunity.


The Screen and its Intricacies

The screen is generated from a 2K file containing character codes,
window and status information  with four more files which contain
definitions of those characters. The four character sets are called
LORES0, LORES1, HIRES0 and HIRES1 where the LORES character sets consist
of 6 by 8 pixel characters (used for printing characters) and the HIRES
characters are 8 by 8 (used for the PipeDream map and the OZ window).

Each character position on screen is defined by two bytes in the screen
file. The format of these two bytes is like this:

Attribute 1 (even address)

        7       6       5       4       3       2       1       0
-------------------------------------------------------------------------
        ch7     ch6     ch5     ch4     ch3     ch2     ch1     ch0
-------------------------------------------------------------------------

Attribute 2 (odd address)

        7       6       5       4       3       2       1       0
-------------------------------------------------------------------------
        sw1     sw2     hrs     rev     fls     gry     und     ch8
-------------------------------------------------------------------------


The Attribute information makes the following contribution:

        5       4       3       2       1       0   7-0
        hrs     rev     fls     gry     und     ch8-ch0    Description
-------------------------------------------------------------------------
        0       v       v       v       v       000-1BF    LORES1
        0       v       v       v       v       01C-1FF    LORES0
        1       1       1       v       v       000-1FF    LORES CURSOR
        1       1       0       1               xxx-xxx    Null Character
        1       0       v       v               000-2FF    HIRES0
        1       0       v       v               300-3FF    HIRES1

x=don't care. v=valid (ie. the attribute depends on the value of the bit).

The addresses of the screen files are held in the gate array, but a
system call is provided for reading and writing to the appropriate
registers:

os_sci - alter screen information

RST &20
DEFB &06
DEFB &D4

In:
BHL - new address to be written into register
A     - reason code

SC.LR1(&01) - LORES0 (512 byte granularity ie. ignore bits 0-8 of address)
SC.LR0(&02) - LORES1 (4K byte granularity ie. ignore bits 0-11 of address)
SC.HR0(&03) - HIRES0 (8K byte granularity ie. ignore bits 0-12 of address)
SC.HR1(&04) - HIRES1 (2K byte granularity ie. ignore bits 0-10 of address)
SC.SBR(&05) - SCREEN (2K byte granularity ie. ignore bits 0-10 of address)

Out if call succeeded:
Fc=0
BHL - old value of register

Out if call failed:
Fc=1
A - return code
RC.FAIL  given for a invalid reason code

Notes:
The system make the following use of the four character sets:

LORES1  This contains the system character set. ie bold, tiny, normal
        characters and the additional special printable characters.
LORES0  This is used for the 64 user defined characters.
HIRES0  This set is used for the map area.
HIRES1  This set holds the characters for the OZ window.


Uses of the Screen Registers

Obviously, any routines which look directly at screen information are
very hardware specific. Use of the os_sci call means that in the future
such routines would not cause code to fail, in the way that writing
directly to hardware registers might, but if the system hardware
arrangement did change any code which relies on the screen information
almost certainly not work properly. Therefore it makes sense that code
using the os_sci call checks for error returns and can cope with the
concept of the screen information being unavailable.

It is safe to move the address of the LORES0 and possibly HIRES0 as well.
The other areas are safe to read (find the address by using os_sci with a
dummy address and writing the old values straight away), but could only
be moved with extreme care. The reason for this is that in order to
synchronise the display certain pixel patterns must be present on the
extreme right (they not actually displayed). If these patterns are not
present the screen tends to turn black and start to flicker violently.
The screen file is terminated by a the null character (shown in the
attribute table above).


PipeDream Format

The PipeDream file format is designed to facilitate manipulation by other
programs. No control characters, or characters above 127 in value are
used in the file, unless they have been explicitly typed in, apart from
Carriage Return characters which are used as separators. The files are
sequential, and can be edited by any text editor.

PipeDream uses a special form of construct to add special information
about the structure of the file. All constructs begin and end with an
ASCII percent sign '%'.  Immediately after the first percent sign come
one or more alphabetic characters which identify the construct. Between
these characters and the final percent sign can come any information
which is relevant to the construct.

At the start of the file is a group of constructs that identify what the
values of Options Page parameters are for the file. This is followed by
the main body of the file.

Each column in the file is preceded by a column construct which gives
details about the column followed by all the slots in the column.  Every
slot that is defined in that column has an entry.

Each entry is separated from the next one with a Carriage Return (CR)
character. On each line there can be a collection of constructs giving
details about the slot.

For each column in the file there is a column construct, followed by all
the entries for that column, until the end of file.

Options Page construct

%OP%parameter value CR

parameter       two characters identifying the parameter
value           the value of the parameter
CR              Carriage Return (&0D)

The various parameter values are:

AM      Auto/manual
BM      Bottom margin
BO      Borders
DE      Title
DP      Decimal places
FM      Footer margin
FO      Footer
HE      Header
HM      Header margin
IW      Insert on wrap
JU      Justify
LM      Left margin
LP      Leading characters
LS      Line spacing
MB      Minus/brackets
PA      Pages
PL      Page length
PS      Page no. start
RC      Rows/columns
TM      Top margin
TN      Text/numbers
TP      Trailing characters
WR      Wrap

Column construct

%CO: column, width, wrap-width%

column          letter identifying the column
width           decimal number giving the column width
wrap-width      decimal number giving the wrap-width

These constructs can appear anywhere in the entry for each slot:

%B%     bracket format
%C%     centre align
%Dn%    decimal places, n gives the number
%DF%    floating format
%F%     free align
%Hn%    highlight character: n gives the number
%JL%    justify left
%JR%    justify right
%L%     left align
%LC%    leading character format
%LCR%   LCR align
%Pn%    page break: n gives the argument, 0 for unconditional
%PC%    per cent character
%R%     right align
%TC%    trailing character format
%V%     slot is a number slot


General format:

construct text construct text ... CR

construct       one of the constructs above
text            ASCII characters being the slot contents
CR              Carriage Return (&0D)


Diary Format   Diary Format

Each day the diary uses (an active day) is headed by a date. This date is
in the form:

%dd,mm,yyyy

The % character is represented as %%

The remaineder of a diary file is simply straight text.

Appendix D - Glossary of terms


Application - A program set up according to the appropriate machine
conventions.

Bank - 16K chunk of memory, always on a 16K boundary in the logical
address space.

Binding - this refers to switching a bank to a segment or to switching a
file device to an particular stream with the CLI.

BLINK - the name given to the Z88's gate array chip. The chip handles the
screen, serial port, keyboard, memory switching, interupts etc.

Card Manager - this refers to the code which deals with inserting and
removing ROM cards. The basic rules are that all interchange should be
done from the Index and that the machine is switched ON.  If an
application is using a ROM card then the card's slot is displayed in the
next to the application name in the Index window. RAM cards can only be
inserted, never removed. EPROMs can be inserted and removed at most
times, but obviously not while the Filer is actively reading or writing
slot 3.

CLI - Command Line Interpreter. This is used to simulate the keyboard and
to rebind streams.

Director - synonym for 'Index' (the name used for it while much of the
code was being written, hence 'dc' calls not 'ic' calls).

DOR - node in tree structure used in applications and the RAM filing
system.

Explicit Filenames - a filename which includes the device and root
directories. For example "queries.pip" might become
":RAM.1/office.dir/queries.pip"

Extension - up to three letters after the filename, usually used to
classify the file. eg. you might add .pip to all your PipeDream files to
aid identification.

Filter - conceptual 'object' (actually a set of routines) which perform
transformations in a byte sequence. Also see Printer Filter.

Handle - A handle is a 16 bit value, usually returned in IX, used in
file-like operations (eg. files, filters etc.) to direct input and
output. For example, a handle is returned when a file is opened and is
subsequently used whenever the file is accessed. A file can have several
handles associated with it and attributes like the sequential pointer
into a file are associated with each handle and not with the file itself.
There are a finite number of handles in the machine, initially around
100, and the current number available is reflected in the high word of
the sequential pointer associated with a handle value of -1. (In BASIC
PTR#-1.)

Logical address - Address which the Z80 CPU 'sees', ie. a standard 16-bit
Z80 address. See also Physical address.

MOS - machine operating system. Also referred to as OZ.

Page - 256 byte chunk of memory, on a 256-byte boundary. This is the
basic unit of memory on which the allocation routines operate.

Physical address - 24-byte address referring to an actual memory location
(only 22 bits are significant, giving an addressing range of 4Mb).

Pre-emption - The suspension of a process, normally prior to the main
body of a system call. See also Logical address.

Printer Filter - This is a filter controlled by the Printer Editor. It
allows applications to use special printing effects by specifying filter
codes and letting the filter generate codes appropriate to the printer
actually in use.

Process- A particular instantiation of a program (normally an
application).

Segment -
1) One of the four 16K portions of the logical memory map.
2) One of the portions of a filename separated by '/', ie. a file or a
   directory name.

Slot -
1) One of the three physical holes in the front of the machine, which can
   accept a RAM, EPROM or ROM card. The internal memory of the machine is
   thought of as Slot 0.
2) The transmission frame for a character of data in the serial system.
   ie. the byte to be transmitted plus appropraiate start and stop bits.
3) Concept used in PipeDream. Roughly approximates to a spreadsheet cell.

Stream - This is a communication channel with an associated handle.

Appendix E - Hardware Connections


The Z88 connects to external electrical devices by using the serial port,
the card slots at the front of the machine and via the expansion port
(situated below the ENTER key on the right hand side of the machine).
Note that in some models of the Z88 the expansion port does not have a
removable plastic cover and on these machine the expansion port edge
connector is not gold plated. In the following descriptions signals
postfixed by a bold L are active low signals.

The Serial Port

1 -     unswitched  +5v at 10 uA        output
2 TxD   transmit data                   output
3 RxD   receive data                    input
4 RTS   ready to send                   output
5 CTS   clear to send                   input
6 -     reserved for future use
7 GND
8 DCD   data carrier detect             input
9 DTR   switched +5v at 1mA             output

Note: DTR is high when the machine is awake. The machine is always awake
when the screen is active, but even if asleep the machine will wake every
minute or so to carry out various housekeeping tasks, such as checking
for alarms, and at these time DTR will go high. Pin 1 will show a signal
if there is power available to the machine.


The Slot Connections

        RAM/ROM RAM/ROM Eprom   Pins    Pins    Pins
                                for     for     for
Slot    Slot 1  Slot 2  Slot 3  32K     128K    32K
pins    Signals Signals Signals EPROM   EPROM   RAM

   1    A16     A16     A16     -       24      -
   2    A15     A15     A15     -       3       -
   3    A12     A12     A12     2       4       2
   4    A7      A7      A7      3       5       3
   5    A6      A6      A6      4       6       4
   6    A5      A5      A5      5       7       5
   7    A4      A4      A4      6       8       6
   8    A3      A3      A3      7       9       7
   9    A2      A2      A2      8       10      8
  10    A1      A1      A1      9       11      9
  11    A0      A0      A0      10      12      10
  12    D0      D0      D0      11      13      11
  13    D1      D1      D1      12      14      12
  14    D2      D2      D2      13      15      13
  15    SNSL    SNSL    SNSL    -       -       -
  16    GND     GND     GND     14      16      14
  17    GND     GND     GND     14      16      14
  18    A14     A14     A14     27      29      1
  19    VCC     VCC     VPP     1       1       -
  20    VCC     VCC     VCC     28      32      -
  21    VCC     VCC     VCC     -       -       28
  22    WEL     WEL     PGML    -       31      -
  23    A13     A13     A13     26      28      26
  24    A8      A8      A8      25      27      25
  25    A9      A9      A9      24      26      24
  26    A11     A11     A11     23      25      23
  27    POE     POE     POE     -       -       22
  28    ROE     ROE     EOE     22      2       -
  29    A10     A10     A10     21      23      21
  30    SE1     SE2     SE3     20      22      20
  31    D7      D7      D7      19      21      19
  32    D6      D6      D6      18      20      18
  33    D3      D3      D3      15      17      15
  34    D4      D4      D4      16      18      16
  35    D5      D5      D5      17      19      17
  36    A17     A17     A17     -       -       -
  37    A18     A18     A18     -       -       -
  38    A19     A19     A19     -       -       -

See next section for explanation of SNSL line.



The Expansion Port Connections

        Component       Tracks
        Side            Side
        A       Edge    B

        GND     1       SNSL    see below
        A11     2       +12v
        A12     3       A10
        A13     4       A9
        A14     5       A8
        A15     6       A7
        clock   7       A6
        D4      8       A5
        D3      9       A4
        D5      10      A3
        D6      11      A2
        VCC     12      A1
        D2      13      A0
        GND     14      GND
        D0      15      D7
        D1      16      M1L
        INTL    17      FLP     (flap switch)
        slot    18      slot
        HALTL   19      NMIL
        MREQL   20      WRL
        IORQL   21      RDL
        MAWL    22      RESETL  Resets Z88 (2 pulses required)
        -BT     23      SVCC    5.4v while the machine is 'on.'
        GND     24      SNSL



SNSL allows the machine to be automaticly placed into comotose state buy
causing a 'power fail interupt' when an edge connector is plugged into to
the expansion slot of the Z88.


EPROM format EPROM format

&0000   File entry
.       File entry
.
.       Latest file entry
        &FF's until
&3FC0   &00's until
&3FF7   &01
&3FF8   4 byte random id
&3FFC   size of card in banks
&3FFD   sub-type &7E for 32K cards, and &7C for 128K cards
&3FFE   'oz'  (Note lower case to distinguish from ROMs.)

A file entry has the form:

1 byte          n               length of filename
1 byte          x               '/' for latest version, &00 for old version
n-1 bytes       'xxxx'          filename
4 bytes         m               length of file
mbytes          x,x,x,x...      body of file





Machine States

One of the interesting features of the Z88 is its ability to selectively
shut down parts of the internal circuitry when it is not required and
thus maximise battery life. There are four operational states:

1) Active       The Z80 clock is running and the display is on. ie. the Z80
                running program instructions.

2) Snooze       The Z80 clock stopped but the display still on. eg. the
                Z88 waiting for keyboard input. This state can be
                achieved by executing a HALT instruction. (This
                must only be done by the operating system, never
                by the user.)

3) Doze:        The Z80 clock running and the display off. eg.
                programming an EPROM.

4) Coma:        The Z80 clock stopped and the LCD display off. eg. Z88
                shut down.

The maskable interupt, INTL, is used by the BLINK chip to attract the
processor's attention. The Z80 will read the interupt register in BLINK
to determine the source. There are two other cause of INTL which are to
indicate battery low condition and a keyboard scan request.

The non-maskable interupt, NMIL, this interupt will cause the machine to
enter the coma state, by saving the current state of the machine and then
executing a HALT instruction. NMIL is generated by when the timeout
counter in BLINK (as set by the Panel) expires, when the the flap at the
front of the machine is opened, or when in response to the SNSL signal.
The SNSL signal flags a power failure or the plugging in of a memory
card or device on the expansion port.

The RSTL usually needs to be pulled low twice. The first action starts
the processor clock if, as is the case most of the time, the machine is
in the snooze (or even coma) state and the second which actually
initiates the reset process. The BLINK chip is also reset by this line.
The reset button on the side of the machine simply brings RSTL low, and
subsequently the button generally needs to be pressed twice.