Z88 Developers' Notes
(C) Copyright Cambridge Computer Ltd. 1988
Authors: John Harrison
Matthew Elton
Consultants: Matthew Soar
Trinity Concepts Ltd
Jim Westwood
Graham French
(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.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
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.
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.
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.
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$
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.
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.
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.
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
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.
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
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).
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
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
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
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
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.
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--"
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
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
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
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####"
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!
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.
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
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.
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
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
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
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
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
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
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.
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.
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.