You are on page 1of 1114

Index to the

Spectrum
ROM
For machine code programmers

As listed in "The Spectrum ROM Disassembly",


By Dr Ian Logan & Dr Frank O'Hara
published by Melbourne House Publishers, 1983

Compiled by Francis G Miles, 1989


Edited by Matthew Logue and Rui Ribeiro, 2021

1
Index to the Spectrum ROM
For machine code programmers

By Francis G Miles

2
© Lookback books

This text was gathered from various issues of the digital


magazine ‘Outlet’, from issue 51 onwards, where it was
published in November 1991.

Francis G. Miles had the foresight to share it in a


digital magazine that has ensured it's survival into the
modern Spectrum community, where we hope it is
enjoyed and makes it into future generations alike.

Lookback books 2021

3
To Francis - The Author.

Francis G. Miles has contributed a lot to the ZX Spectrum


community, via paper and digital magazines over the years and
deserves for this book to be published.

4
INTRODUCTION
"The Spectrum ROM Disassembly", by Dr Ian Logan & Dr
Frank O'Hara, published by Melbourne House Publishers,
1983, is now out of print and unobtainable, as far as I know.
To publish an index to it may therefore seem a little
perverse. However, my index is as much an index to the
Spectrum ROM itself as to the Melbourne House
publication: I have tried to compile it in a form that will be
useful to Spectrum owners even if they haven’t got a copy
of the book. The vital ROM listing is reproduced as an
Appendix to the index.

The index couldn't possibly have been written without


constant use of the work done by Logan & O'Hara; for
example all the labels used to refer to the ROM are taken
straight from the "Disassembly". It contains many
references to and comments on the "Disassembly"; see for
example under "misprints". If you_do have a copy of the
book these references will be useful, if you don't you may
still find some of them enlightening.

5
The items indexed are of five kinds:

1. Labels. Under each label name, every call or jump to


that label from any part of the ROM is listed, and where
relevant a list of "Rems" indexes every mention of it in
“ROM Disassembled". There is also a description of the
operation of the subroutine labelled, or a cross-reference
to the subroutine under which the action of that section is
described.

A label is counted as a_subroutine if it is CALLed from


some point in the ROM - though other addresses can be
CALLed from machine code, with suitable precautions, and
in a few cases these are suggested in the index. Subroutines
are listed under their labels, not under the cross-head titles
used in Disassembly" as headers for each section of the
listing.

A label counts as an_exit routine if it includes or


terminates with a RET. It is sometimes, but by no means
always, safe to call these from machine code.

2. The cross-head titles used as headers in "Disassembly",


unless they are very similar to the first label: generally
these are merely cross-referenced to the first label used in
the listing, but in a few cases they are treated as "topics",
see 4. below.

3. Every key code on the "old" Spectrum. Letter and digit


key codes are indexed under "letter keys" and "digit keys";
including for example the special use of the E key in “E-
format" numbers. The other uses eg of the "E" key with
6
symbol shift etc will be found indexed under ">= key", "REM
key", "TAN key", “ATN key". "Commands, functions and
operators" are indexed under the corresponding key, but
there is an index entry with that title which gives a
summary table.

For all but the most straightforward key codes, an


indication is given of how the code is implemented in ROM.
4. Topics, mostly terms mentioned in "Disassembly"
which aren't self-explanatory and aren't normal terms of
assembly language or machine code programming. There
are a few additional topics: see for example "BASIC
interpreter", "Chebyshev polynomials", "misprints", a full
list of all the misprints encountered in "Disassembly",
"mistakes in ROM", "one-byte binary arithmetic", "useful
routines", a list of subroutines which machine code
programmers may find useful.

5. In the case of system variables, the index shows every


case in which a value is written to the variable, and,
separately, every case in which its value is read by the
ROM, as well as each mention in the "Rems". For the five
"flag" system variables FLAGS, FLAGS2, FLAGX, P FLAG
and TV FLAG, this information is shown for each individual
bit of the variable.

Two general notes on the system variables may be


inserted here:

5.1. All the flag svs except P FLAG contain unused bits:

FLAGS bit 4
FLAGS2 bits 7, 6 and 5
7
FLAGX bits 4, 3 and 2
TV FLAG bits 7, 6, 2 and 1

These 11 spare flags can sometimes be used by machine


code programmers. Beware! Some of them are used by the
128K Spectrum versions, and some may be pre-empted by
hardware operating systems, eg FLAGS bit 4 is certainly
used by the DISCiPLE disc operating system.
5.2. The listing in "Disassembly" contains many
instructions relating to the system variables which appear
at first sight to be impossible, eg in 1CBE CLASS 09: RES 6,
(P FLAG). RES 6,(nnnn) isn't in Z80 assembly language.

Reference to the machine code will show that such


instructions are expressions for RES 6,(IY+87d) and similar:

FLAGS is IY+1
TV FLAG is IY+2
FLAGS2 is IY+48d
FLAGX is IY+55d
P FLAG is IY+87d

Machine code programmers can also make use of such


IY instructions - so long as the value of IY itself, 5C3Ah/
23610d, isn't disturbed.

8
The alphabetical order observed throughout is as
follows:

letters A-Z, ignoring spaces and the difference between


upper and lower case except when two items are otherwise
identical, when lower case comes first, and a space
precedes both upper and lower case
the numeral digits 0 to 9; even where A-F indicate hex
digits, they precede the digit 0
other symbols in Spectrum character code order, except
that "underline" and "up arrow" are listed under "U", and
the “hatch" symbol code 23h/35d is called (hatch) - these
symbols cannot be printed by this word processor!
Numbers are suffixed h for hex, d for decimal, b for
binary. They should be presumed to be hex if no indication
is given, unless they are written in full, eg "sixteen" means
10h/
16d. In many cases both hex and decimal forms are given,
eg 10000h/65536d; or binary and hex forms are so given,
eg 10001100b/8Ch.
Throughout the index "the notes" means the notes or
"Rems" attached to the listing in "Disassembly": "the index"
means this index.

9
A

ABS key (BD) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The G key in E mode without shifts produces the function
ABS; it requires one numeric operand X, and the value of the
function is the same as X except that it is always positive: ABS
(-X) = X.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code BD first to 0E, then to
EA, and adds the priority 10h/16d. Code + priority 10EAh are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following ABS is evaluated.
When the code is taken off the machine stack (2734 S
LOOP), it is converted (2773 S TIGHTER) from EA to 2A, the
calculator offset for 346A abs.

abs subroutine 346A


Called from 0028 FP CALC by offset 2A; the executive
routine for the ABS function. Not otherwise called from ROM,
but can be called direct from m/c.
Replaces a number X by its absolute value ABS X.
Input parameters: HL points to the first byte of the FP
number X; if the call is from FP CALC this will be the last
number on the calculator stack, but this isn't necessary for
direct calls.
Action: load FF into the B register.
Exit: through 3474 NEG TEST (346E negate), which given
FF in B zeroes the sign bit of either small integer or full format
FP numbers.
Output parameters: B holds FF
- A holds zero
- the sign of the FP number has been made positive if
necessary.

10
Called from:
2320 CIRCLE
23A3 DR SIN NZ (3 times)
23C1 DR PRMS (twice)
247D CD PRMS1
2DF2 PF NEGTVE
3783 get-argt
37AA cos
Rems:
346E negate resets sign bit
3474 NEG TEST point of entry; B becomes 80
3483 INT CASE zeroes C register for abs

ABSOLUTE MAGNITUDE FUNCTION see 346A abs

ACS key (B6) see also commands, functions and operators,


KEYBOARD SCANNING, 0246 extended mode table (c)
The W key in E mode with either shift produces the
function ACS; it requires one numeric operand X, and the
value of the function is the number in radians whose COS is
X. If you are unsure about radians, see index entry on 3783
get-argt.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B6 first to 07, then to E3,
and adds the priority 10h/16d. Code and priority 10E3h are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following ACS is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E3 to 23, the calculator
offset for 3843 acs.

acs subroutine 3843


Called from 0028 FP CALC by offset 23; the executive
routine for the ACS function. Finds the number Y = arc cos X
whose COS is X. Y is in radians; if you are unsure about
radians, see index entry on 3783 get-argt. Not called from
ROM;

11
can be used in m/c either through the calculator or direct.
Input parameters: the number X whose arc cos is to be
found must be the last value on the calculator stack, even for
direct calls.
Action: use the calculator to find arc sin X and subtract
it from pi/2.
Exit: RET
Output parameters: arc cos X is left as the last number
on the calculator stack
- mem-0 to mem-2 corrupted.
Rems:
3449 series-06 indirectly uses Chebyshev polynomials

ADD BACK subroutine 3004 see also precision


In rounding decimal numbers, the usual rule is simply to
drop the last digit if it is four or less, but to increase the
last remaining digit if the last one is five or more.
With binary numbers the rule is similar but simpler;
drop the last digit, and increase the last remaining if the last
digit was one. In other words, drop the last digit but add it to
the rest of the number. This is what this subroutine is for - it is
only called if the last digit was one, so all it actually does is
increment the four-byte mantissa of a FP number by one.
Input parameters: D'E'DE contain the four mantissa bytes
of a FP number.
Action: increment E and then D, returning after each if
te result isn't zero; FF would become zero
- increment E'
- jump on to ALL ADDED if the result isn't zero
- increment D'.
_300D_ALL_ADDED: exchange back to the main registers.
Exit: RET.
Output parameters: same as input, except for the increase
in the mantissa
- the Z flag is set if all four bytes went from FF to 00.
Called from:
2FE5 ONE SHIFT

12
3186 NORML NOW

ADD CHAR subroutine 0F81


Adds a character at the cursor position to the current
BASIC line in the editing or input area in the work space. 1219
RAM SET makes it the output routine of the "R" channel on
start-up from the initial channel information list 15AF; this
channel is used to output from sources other than the
keyboard, eg in copying from the program to the editing area.
Not otherwise called from ROM, but used as exit routine
by other subroutines.
Input parameters: A holds the character byte
- 5C5B K CUR holds the address where it is to go; but
this address is occupied by the following character.
Action: zero bit zero of 5C41 MODE; cancels extended
mode
- call 1652 ONE SPACE to make a space at the address in
K CUR.
_0F8B_ADD_CH_1 (entry here from 0F6C ED CONTR, with
the space already made): insert the byte
- move on the pointer
- load the pointer back into K CUR.
Exit: RET.
Output parameters: A unchanged
- HL decremented by 1
- DE holds the cursor address
- BC corrupted.
Exit from:
0F38 ED LOOP (twice)
107C ED GRAPH

ADD CH 1 0F8B (0F81 ADD CHAR)


Exit from:
0F6C ED CONTR
0F81 ADD CHAR

addend see also CALCULATE

13
Addend, augend, dividend, minuend, multiplicand,
subtrahend are all rather old-fashioned jargon terms; perhaps
it helps to know that the Latin endings "-and, -end" mean
"what has to be done", as in "agenda", things to be done.
_Addend: number to be added - to some other number,
the
_augend - making a_sum.
_Subtrahend: number to be subtracted - from some other
number, the_minuend - making a_difference.
_Multiplicand: number to be multiplied - by some other
number, the_multiplier - making a_product.
_Dividend: number to be divided - by some other number,
the_divisor - making a_quotient.
In C = A + B, B is the addend, A the augend.
In binary FP calculator operations, the number to be
operated_with is always last value on the calculator stack, and
the number to be operated_on second last; in the addition
operation, of course, they are interchangeable, but
pedantically the addend means the last value, eg at 3014
addition.
2FDD SHIFT FP smaller number is in addend position
2FFB ZEROS 4/5 zeroes addend
3004 ADD BACK zeroes addend on overflow
3014 addition addend is at top of stack, augend second
303E FULL ADDN smaller number put in addend position
3055 SHIFT LEN addend adjusted by right shift

ADDEND 0 2FF9 (2FDD SHIFT FP)


Jumps from:
2FDD SHIFT FP

addition subroutine 3014


Called from 0028 FP CALC by offset 0F; the executive
routine for the "+" operator. Adds an addend Y to an augend X;
see the index entry "addend" for these tiresome expressions.
Both X and Y must be in FP format, and they must be held
consecutively, with the five bytes of Y following those of X.

14
Called many times from ROM, always through the
calculator; could also be called direct.
Addition of full format FP numbers isn't at all a simple
operation: see the index entry on 303E FULL ADDN. If both
numbers to be added are in "small integer" format, and the
result is also within "small integer" range, things are much
simpler, and this is the action described here. However, there
is a serious error over the "wrong number", -10000h/-65536d;
see under "wrong number".
Input parameters: HL and DE address the first bytes of X
and Y respectively; if the routine is called through 0028 FP
CALC they are the last two values on the calculator stack, but
this isn't necessary for direct calls, provided the numbers are
held consecutively. They may be in either small integer or full
FP format.
Action: check the exponent bytes of X and Y
- if either is non-zero jump on to 303E FULL ADDN;
either X or Y isn't in small integer format
- (both are small integers) get the value of X; if X is
negative this will be 10000h/65536d minus its absolute value,
and the same for Y
- get the sign byte of Y
- get the value of Y
- add the values, with result Z, say; if X and Y are
both positive and Z is "big", ie too big for a small integer,
this produces a carry. But if X and Y are both negative, carry is
produced if Z is small, and_not produced if Z is big. If one is
negative and the other positive, Z is bound to be small;
there will be carry if Z is positive, no carry if it is negative
- add the two sign bytes, with the carry from the last
result; the sign bytes are 00 for positive and FF for negative,
so
+X +Y = +Z (Z small) gives 00 + 00 + 00 = 00
+X +Y = +Z (Z big) gives 00 + 00 + 01 = 01
+X -Y = +Z (Z small) gives 00 + FF + 01 = 00
+X -Y = -Z (Z small) gives 00 + FF + 00 = FF
-X +Y = +Z (Z small) gives FF + 00 + 01 = 00
-X +Y = -Z (Z small) gives FF + 00 + 00 = FF

15
-X -Y = -Z (Z small) gives FF + FF + 01 = FF
-X -Y = -Z (Z big) gives FF + FF + 00 = FE

- rotate the result of the addition right, with lo bit


going to the hi bit and into the carry
- use ADC A,00 to add back the carry, so

00000000b/00h -> 00000000b/00h -> 00000000b/00h


00000001b/01h -> 10000000b/80h -> 10000001b/81h
11111111b/FFh -> 11111111b/FFh -> 00000000b/00h
11111110b/FEh -> 01111111b/7Fh -> 01111111b/7Fh

- if the result isn't zero, jump on to ADDN OFLW; Z


isn't a small integer
- (result is a small integer) use SBC A,A to subtract
the carry from zero; this restores the former value 00 or FF,
thus giving the correct sign byte for Z
- put the sign byte in the second byte of the result
- put the absolute value Z in the third and fourth bytes
- return.
_363C_ADDN_OFLW: set the pointers back to the first bytes
of X and Y. [For the remarks in the notes on -65536d see
under "wrong number".]
Exit: RET if the whole operation involved nothing but
small integers.
- if either of the input values was in full FP format,
exit to 303E FULL ADDN, which carries out full exponent/
mantissa addition.
- if the result of the trial addition was too big,
through ADDN OFLW into FULL ADDN.
Output parameters (all cases): HL points to the first
byte of the result
- DE to the first byte after it, which will now be the
stack end if the result is on the calculator stack.
The calculator and calculator memory aren't used.
Called from:
03F8 BEEP
1DAB NEXT

16
235A C ARC GE1
23A3 DR SIN NZ
23C1 DR PRMS (4 times)
2425 ARC LOOP
2439 ARC START (twice)
2497 DRAW SAVE
25F8 S RND
2CDA NXT DGT 1
2D40 NXT DGT 2
2DA2 FP TO BC
3449 series-06
3453 G LOOP
36C4 EXP
373D GRE 8
3783 get-argt (3 times)
37B7 C ENT
37FA CASES (twice)
3833 asn (twice)
Rems:
2D8E INT STORE no twos complementing required
2F9B PREP ADD is called by addition for FP numbers
2FBA FETCH TWO is called by addition for FP numbers
3004 ADD BACK effect of overflow in FP numbers
300F SUBTRACT changes sign and goes into addition
303E FULL ADDN explains operation of FP number
addition
316E SHIFT ONE 5th mantissa byte always 0 after addition
335B CALCULATE example of binary operation

addition of line or variable see 08B6 ME CONTRL under


0605
SAVE ETC

ADDN OFLW 303C (3014 addition)


Jumps from:
3014 addition

17
ADD REP 6 309F (303E FULL ADDN)
Jumps from:
3055 SHIFT LEN

address dropping
This term is used for various programming devices by
which a subroutine is made to return to an address different
from its normal return point.
a) when the subroutine call instruction is followed by a
string of "literals" or "offsets" read by the subroutine -
chiefly 0028 FP CALC, also 335B CALCULATE and 0008
ERROR 1; see the index entries.
b) when a subroutine has been called from within a
subroutine; return address 1 is below return address 2 on the
stack, so POPping address 2 means return will be to address 1.
c) when the address of the start of a loop has been
PUSHed on the stack, so that looping can be effected over
long distances by RETs instead of JRs or JPs. The looping
address must be dropped to escape from the subroutine.
d) by resetting the stack pointer.
0055 ERROR 3 machine stack reset to error address
1024 ED ENTER two addresses dropped
1031 ED EDGE to return via 0F38 ED LOOP
15AB MAIN ADD2 report address dropped
1BEE CHECK END two return addresses dropped
1C11 CLASS 05 drop 1B52 SCAN LOOP
1C4E CLASS 02 drop 1B52 SCAN LOOP
1C96 PERMS drop 1B52 SCAN LOOP
1CF0 IF drop 1B76 STMT RET
1FC3 UNSTACK Z flag signals dropping of return 1
33A2 fp-calc-2 temporarily drop 3365 RE ENTRY
369B end-calc drop 3365 RE ENTRY

address of BASIC line see BASIC line

address table for calculator see_Table_of_addresses


_(calculator_functions) under "tables"

18
algorithms
An algorithm is a series of simple arithmetical steps
which by repetition perform a complex calculation; long
division is a familiar example. Most programming can be
regarded as the invention of algorithms operable by the
computer.
24B7 DRAW LINE contains one from ZX 80 ROM
24CB DL LARGER it starts here
24EC D L PLOT continued

ALL ADDED 300D (3004 ADD BACK)


Jumps from:
3004 ADD BACK

ALPHA subroutine 2C8D see also 2C88 ALPHANUM, 2D1B


NUMERIC
Checks if a given character is a letter of the alphabet.
A useful routine for m/c programmers.
Input parameters: character code in A.
Action: compare A with 41 "A", 5B "Z"+1, 61 "a" and 7B
"z"+1; complement the carry flag as required for a correct
result.
Exit: RET.
Output parameters: carry is set if A is a letter of the
alphabet, A-Z or a-z, otherwise NC
- all registers unchanged.
Called from:
1F6A DEF FN 1
1F86 DEF FN 3
20ED IN ITEM 3
27BD S FN SBRN
28B2 LOOK VARS
34BC usr-$

alphabetic characters see 2C8D ALPHA

19
ALPHANUM subroutine 2C88 see also 2C8D ALPHA, 2D1B
NUMERIC
Checks if a given character is a letter of the alphabet
or a digit.
Input parameters: character code in A.
Action: call 2D1B NUMERIC
- complement the carry flag
- return if this makes carry, indicating a digit.
Exit: RET if NUMERIC signals a digit
- otherwise into 2C8D ALPHA to check for alphabetic
codes.
Output parameters: carry set if A is a letter of the
alphabet or a digit, 0-9, A-Z or a-z, otherwise NC
- all registers unchanged.
Called from:
2684 S ALPHNUM
28B2 LOOK VARS
28D4 V CHAR
2913 V SPACES
2943 V PASS
2B1F L TEST CH

alphanumeric characters see 2C88 ALPHANUM

alternate registers see also 0C1B PO SAVE and 0C88 PO SCR


2 (in 0C55 PO SCR)
M/c programmers should beware of the 24B7 DRAW LINE
routine, which switches registers each time a pixel is plotted;
if an odd number is plotted they remain switched on return.
The note at 34B3 usr-no is a little puzzling at first
sight: experience soon shows that the BASIC_will crash if USR
routines use the HL' register and don't reset it to 2758h, but
why is this? and when does HL' originally get set to 2758? The
other alternate registers can be used quite freely.
The explanation lies in the fact that the only way to
use the Spectrum for machine code programming is through a
USR routine, and USR is a function. As a function,

20
it is always read as part of the evaluation of an
expression, by the 24FB SCANNING routines;
therefore it is always evaluated by the call to fp-calc-2
in 274C S STK LIST;
and the return address from this call, 2758h, is put in
HL' for the duration of the USR function at 3362 GEN ENT 2,
and recovered from HL' at 369B end-calc.
If there is any other number in HL' it will return
there, with unpredictable results - unless the value of HL' is
craftily calculated; why not return to some chosen address? It
is perhaps a pity that the ROM doesn't reset HL' on return
from a USR function, as it does reset IY, in 2D2B STACK BC,
but it isn't quite easy to see how this could be done.
The alternate register instruction EXX is used in
0C3B PO SAVE (twice)
0C88 PO SCR 2 (twice)
11B7 NEW (twice)
11EF RAM DONE (twice)
15E6 INPUT AD
15F2 PRINT A 2
15F7 CALL SUB
18C1 OUT FLASH (twice)
24D4 D L DIAG
24DB D L HR VT
24EC D L PLOT
2DF8 PF POSTVE (twice)
2E1E PF SAVE (twice)
2E6F PF MEDIUM (twice)
2E7B PF BITS (twice)
2ECF PF FRACTN (twice)
2EDF PF FRN LP (twice)
2EEF PF FR EXX (twice)
2F2D PF COUNT (twice)
2FBA FETCH TWO (twice)
2FE5 ONE SHIFT (twice)
2FF9 ADDEND 0
2FFB ZEROS 4/5

21
3004 ADD BACK
300D ALL ADDED
303E FULL ADDN (twice)
3055 SHIFT LEN (twice)
307C TEST NEG (4 times)
309F ADD REP 6
30A3 END COMPL
30F0 MULT LONG (4 times)
3114 MLT LOOP (twice)
311B NO ADD (twice)
3125 STR MULT (4 times)
3146 OFLW1 CLR (twice)
3151 OFLW2 CLR (twice)
315E SKIP ZERO
316E SHIFT ONE (4 times)
3186 NORML NOW (twice)
3195 OFLW CLR (4 times)
31AF division (4 times)
31D2 DIV LOOP (twice)
31DB DIV 34TH (twice)
31E2 DIV START (4 times)
31F2 SUBN ONLY (twice)
31FA COUNT ONE (twice)
3362 GEN ENT 2 (twice)
3365 RE ENTRY
3380 FIRST 3D (twice)
338E ENT TABLE
33A2 fp-calc-2
33C8 STK CONST (twice)
33DE FORM EXP (twice)
341B stk-zero (4 times)
367A dec-jr-nz (twice)
3686 JUMP
3687 JUMP 2
368F jump-true (twice)
369B end-calc (twice)

22
ALTER STREAM SUBROUTINE see 2070 STR ALTER

AND key (C6) see also commands, functions and operators,


KEYBOARD SCANNING, logical value, 026A symbol code table
The Y key with symbol shift produces the operator AND;
it must be both preceded and followed by an expression with
a logical value. String expressions may not occur_after the
AND, only before.
The effect of the operator AND is to give a value to the
expression "X and Y", or "X$ and Y": it is zero or "" (null
string) if Y has logical value zero, otherwise it is X or X$.
This isn't the same as the Z80 assembly code AND.
On execution - after the first expression has been
evaluated in 268D S DECIMAL, 25B3 S QUOTE or 26C9 S
LETTER -
24FB SCANNING quickly leads to 2723 S OPERTR. Here the
token code C6 is looked up in the table of operators at 2795
and the table of priorities at 27B0, giving a value 03C8 in BC:
03 being the priority and C8 the code of the AND operator.
Now this op code is put on the machine stack in 2790 S NEXT.
When the code comes off the calculator stack in 2734 S
LOOP, C8 is converted (2773 S TIGHTER) to 08, the literal for
the 3524 no-&-no executive routine, or to 10h, the literal for
352D str-&-no.

ARCCOS FUNCTION see 3843 acs

arc counter see 247D CD PRMS1

arc drawing loop see 2382 DRAW, 2420 DRW STEPS

ARC END 245F (2420 DRW STEPS)


Jumps from:
2420 DRW STEPS

ARC LOOP 2425 (2420 DRW STEPS)


Jumps from:

23
2439 ARC START

arcs see 247D CD PRMS1, 2420 DRW STEPS

ARCSIN FUNCTION see 3833 asn

ARC START 2439 (2420 DRW STEPS)


Jumps from:
2420 DRAW STEPS

ARCTAN FUNCTION see 37E2 atn

argument of DEF FN statements, functions, operations


The_arguments of a function, etc, are the numbers or
strings on which it operates: X and Y in sin X, abs Y, X + Y,
SCREEN$ (X,Y), or X$ and Y$ in X$ + Y$, CODE Y$, &c.
_Operands is perhaps a pedantically more correct term
when speaking of operators rather than functions, but there's
not much in it.
24FB SCANNING operation stacked while 2nd argument
read
2712 S CONT 2 paths rejoin after argument read
2734 S LOOP operation scanned but not yet argument
2773 S TIGHTER numeric argument required after AND
27BD S FN SBRN evaluated by SCANNING
27D0 SF BRKT 1 if "()" there are no arguments
27D9 SF ARGMTS reads arguments of DEF FN
2802 SF ARGMT1 saves pointer to 1st argument in DEF FN
2831 SF VALUES evaluated by SCANNING
2843 SF ARG LP discriminate numbers from strings
2852 SF ARG VL evaluate
2885 SF R BR 2 look at character after last argument
288D SF VALUE seek and evaluate
28B2 LOOK VARS searches for arguments
28E3 V TEST FN searches for arguments in run time
2951 STK F ARG checks arguments of DEF FN for vble
names

24
295A SFA LOOP go to next argument

arithmetic operations see addition, subtract, multiply,


division
Strictly speaking there are other arithmetic operations,
eg raising to a power: but these four basic ones are what the
notes always mean by the term.

arithmetic overflow see overflow

"Arithmetic overflow" quoted at 31AD as the error report for


REPORT 6; should read "Number too big".

ARITHMETIC ROUTINES see 2D4F e-to-fp, 335B


CALCULATE
This heading is used at 2D4F e-to-fp for the routines
which precede the calculator subroutine: as well as the
fundamental operations of addition, subtraction,
multiplication and division, they include manipulation of
numbers on and off the stack, conversion of digital or E-
format numbers to FP numbers, etc.
Introduction - summary of routines available.
3014 addition - first major arithmetic routine
3155 TEST NORM normalization common to all arithmetic
routines

arrays see also strings, variables


Arrays are a special kind of variable, differing from
"simple" variables
1. because they cannot be declared simply by a LET, INPUT
or READ command; the array must be declared before being
given any values, by a separate DIM command which
establishes the array in the variables area with all its values
or_elements set to zero or "space".
2._numeric_arrays have more than one numeric value,
arranged in_dimensions; simple numeric variables only have
one numeric value.

25
3._string_arrays consist of a string or strings of a fixed
length; if more than one, all are of the same length and they
are similarly arranged in dimensions. A simple string variable
has only one string value, and its length can be changed by
giving it a new value.
String arrays are arrays of characters; their last
dimension gives the number of characters in each string
element.
4. Only part of an array can be used at a time: if A and B
are arrays, you cannot command LET A = B, even if they have
the same number of dimensions. Some BASICs allow this, but
not Spectrum BASIC. You must use_subscripts to identify
which element of the array you want to use: eg LET A(2,3,4) =
B(4,3,2). With string arrays this is also true, except that the
last dimension needn't be specified, so that one-dimensioned
string arrays_can be used all at once.
In the variables area, arrays are identified by the
first three flag bits of the "variable letter" or "discriminator
byte", like other types of variable; see under "variables".
For all arrays, the first four_leading_bytes are the
_initial_parameters:
byte 1 is the array letter with its flags
bytes 2 and 3 give the length of the array in the
variables area, excluding the first three bytes.
byte 4 gives the number of dimensions.
These are followed by two bytes for each dimension of
the array, giving the dimension number. Each dimension can
be of any size.
For numeric arrays:
byte 1 has the flags 100b and the letter code
the dimension numbers are followed by the elements of
the array, each one a 5-byte FP number; the elements are in
"alphabetical order" of their subscripts, eg (2,4,3) precedes
(4,2,3) but follows (2,3,4).
For string arrays:
byte 1 has the flags 110b and the letter code

26
the dimension numbers are followed by the elements of
thearray, each a single character code, in the same kind of
"alphabetical order" as for numeric arrays.
For the same number of dimensions a numeric array
takes up nearly five times as much RAM as a string array.
0652 SA DATA handling save/load of arrays
0672 SA V OLD loading with already existing name
0685 SA V NEW save/load of arrays
0692 SA DATA 1 set pointer to show start of array
0808 LD CONTRL loading of array
0819 LD CONT 1 check size of array
082E LD DATA loading of array
084C LD DATA 1 make space for new array when loading
1C22 VAR A 1 can't give value to array till dimensioned
1C30 VAR A 2 finds address and length of element
1C46 VAR A 3 puts element params in STRLEN and
26C9 S LETTER parameters of entry put on stack
2713 S CONT 3 parenthesis may indicate element of array
28B2 LOOK VARS discriminate arrays from num variables
28DE V STR VAR set flag for strings and string arrays
2900 V EACH test for strings and string arrays
2934 V SYNTAX presence of "(" signals array
294B V END output parameters for arrays of LOOK VARS
2996 STK VAR finds address of individual element
29AE SV ARRAYS gets dimension no and checks subscript
29C0 SV PTR gets ready to evaluate subscript
29C3 SV COMMA loop to count elements
29D8 SV CLOSE checks string arrays for slicing
29EA SV LOOP ")" marks end of subscripts
29FB SV MULT compute each subscript in element count
2A12 SV RPT C distinguishes numeric from string array
2A22 SV NUMBER marks required element in number
array
2A2C SV ELEM$ marks element in string array, and stacks
its string parameters
2A49 SV SLICE? returns after last subscript
2AB1 STK STO 0 first byte of string parameters zero for

27
elements of strings
2B66 L EXISTS splits numeric vbles from strings/arrays
2B72 L DELETE$ value written in work space and copied

array strings see arrays, strings

ASCII letter
ASCII stands for American Standard Codes for
Information Interchange, and ASCII codes are the most
widely-used set of one-byte codes for computer characters;
used by the Spectrum except for the codes for 96d "`" and
127d "". However in the notes all it means really is "character
codes", which see.

ASN key (B5) see also commands, functions and operators,


KEYBOARD SCANNING, 0246 extended mode table (c)
The O key in E mode with either shift produces the
function ASN; it requires one numeric operand X, and the
value of the function is the number arc sin X whose SIN is X. X
is in radians, see the index entry on 3783 get-argt.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B5 first to 06, then to E2,
and adds the priority 10h/16d. Code and priority 10E2 are now
pushed on to the machine stack (270D S PUSH PO) while the
expression following ASN is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E2 to 22, the calculator
offset for 3833 asn.

asn subroutine 3833


Called from 0028 FP-CALC with offset 22; executes the
ASN command, ie finds arc sin X, the number in radians
whose SIN is X. For radians, see the index entry on 3783 get-
argt. Called once in ROM, through the calculator; can also be
called from m/c direct.

28
Given X such that Y = arc sin X, then
SIN Y = X, and
COS Y = SQR(1 - X**2),
a standard identity, which is easily worked out using the
calculator's multiply and sqr routines.
Given both SIN Y and COS Y, the calculator now finds TAN
(Y/2) from another trigonometrical identity

SIN Y
TAN (Y/2) = -----------
(1 + COS Y)

(misprinted in the note).


Now the atn subroutine finds Y/2 by the use of Chebyshev
polynomials; doubling this gives the required result Y.
Input parameters: the FP number X whose arc sin is to be
found must be the last number on the calculator stack, even
for direct calls.
Action: use the calculator to find, as explained above:
- COS Y from SQR(1 - X**2)
- TAN (Y/2) from the formula shown
- Y/2 by the atn function
- finally Y.
Exit: RET.
Output parameters: the last number on the calculator
stack is now the number whose SIN is X
- mem-0 to mem-2 were corrupted in the sqr subroutine.
Called from:
3843 acs

assignment of variable value see 2AFF LET, variable

AT key (AC) see also control codes, KEYBOARD SCANNING,


026A
extended mode table (e)
The I key with symbol shift produces the print control

29
item code AT. AT can only be used within a PRINT etc
statement, and must be followed by two parameters separated
by a comma.
Execution is from within the PRINT executive routine 1FCD
PRINT;
each new expression following the PRINT command is
checked by a call to 1FFC PR ITEM 1 from 1FE5 PRINT 3. If it is
an AT, the AT control character 16h/22d and the two
parameters are sent through the output routine.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 16h for AT into the control
character table at 0A11 produces an indirect jump to 0A75
(0A21 + 54) PO 2 OPER. See the index description of this
subroutine under 09F4 PRINT OUT for the rather tricky way
in which it collects the two parameters and sends execution to
0A87 PO CONT, which finally resets the print position to
execute the AT command.
The AT control character 22d can be used from BASIC:
10 LET a$=CHR$ 22 + CHR$ 7 + CHR$ 13 + "hello!"
20 PRINT a$
has just the same effect as
10 PRINT AT 7,13;"hello"
This is much more useful when printing from m/c.
007D SKIP OVER skip twice, when setting CH ADD
0A6D PO TV 2 requires two operands
0A7D PO TV 1 control code saved in TVDATA
0A80 PO CHANGE next character is operand
0A87 PO CONT code and first operand now in TVDATA
0C55 PO SCR part skipped when handling INPUT ... AT
0E00 CL SCROLL entry point to scrolling subroutine
when handling INPUT ... AT
0F38 ED LOOP handled here in EDITOR routine
103E ED EDGE 1 placing of cursor in EDITING
1FFC PR ITEM 1 code & 2 operands prepared for printing
201E PR AT TAB prints code and both operands
2089 INPUT statement can include ATs &c

30
ATN key (B7) see also commands, functions and operators,
KEYBOARD SCANNING, 0246 extended mode table (c)
The E key in E mode with either shift produces the
function ATN; it requires one numeric operand X, and the
value of the function is arc tan X, the number whose TAN is
X. X is in radians, see the index entry on 3783 get-argt.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B7 first to 08, then to E4,
and adds the priority 10h/16d. Code and priority 10E4 are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following ATN is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E4 to 24, the calculator
offset for 37E2 atn.
Introduction: calculation uses Chebyshev polynomials

atn subroutine 37E2


Called from 0028 FP CALC with the offset 24; executes
the ATN command, ie finds the number Y = arc tan X whose
TAN is X. X is in radians, see the index entry on 3783 get-argt.
Called once from ROM, through the calculator; can also be
called direct from m/c programs.
This is the fundamental inverse trig function in the
Spectrum system, used by both the ACS and ASN routines.
The value of arc tan X ranges
from -pi/2 for X = minus infinity
through -pi/4 for X = -1,
zero for X = zero,
+pi/4 for X = 1,
to +pi/2 for X = plus infinity.
This range of X has to be reduced for the Chebyshev
polynomials, and it is done by using the trigonometrical
identity
ATN X = pi/2 - ATN (1/X)
derived from TAN Y = cot (pi/2 - Y).
So if X is more than 1, the subroutine calculates -ATN

31
(1/X) instead, which is the same as ATN (-1/X); 1/X of course is
less than 1 in this case. Then it adds this to pi/2. And if X is
negative and less than one, it calculates
-pi/2 - ATN (1/X).
So the first step replaces ATN X with ATN Y', where Y' =
either X or -1/X, and Y' now ranges only from -1 to +1. Y' is
_not the result Y = arc tan X.
A second step changes the argument of the function
again, by using instead of Y' another value Z such that
Z = 2Y'**2 - 1; the range of Z is still -1 to +1.
The notes don't explain why this step is taken; possibly
the book of functions referred to at the bottom of page 226
(Abramowitz &
Stegun,_Handbook_of_Mathematical_Functions, Dover
1965) explains it, but the reason is no doubt to make use of a
set of already worked out, or just simpler, Chebyshev
polynomials.
The series-0C subroutine in any case works directly from
Z to (ATN Y')/Y', so all that remains to do is multiply by Y', and
add pi/2 or -pi/2 if necessary.
The twelve Chebyshev constants used are:

FP form Decimal
1. 60 B2 -0.0000000002
2. 63 0E 0.0000000010
3. 65 E4 8D -0.0000000066
4. 68 39 BC 0.0000000432
5. 6B 98 FD -0.0000002850
6. 6E 00 36 75 0.0000019105
7. 70 DB E8 B4 -0.0000131076
8. 73 42 C4 0.0000928715
9. 76 B5 09 36 BE -0.0006905975
10. 79 36 73 18 5D 0.0055679210
11. 7C D8 DE 63 BE -0.0529464623
12. 80 61 A1 B3 0C 0.8813735870

Input parameters: the FP number X must be the last


number on the calculator stack, even for direct calls.
Action: call 3297 RE STACK to put X in full FP format

32
- check its exponent
- if the exponent is less than 81 jump on to SMALL; X is
less than 1
- (X > 1, so the arc tan of -1/X is found instead) use
the calculator to get -1/X
- get pi/2
- if -1/X is negative jump on to CASES; ie if X is
positive.
- (X is negative) change pi/2 to -pi/2
- jump on to CASES.
_37F8_SMALL (X is less than 1, leave it alone: Y' = X):
put zero on the calculator stack.
_37FA_CASES (the top value on the stack is either zero or
pi/2 or -pi/2, and the next one is Y' = -1/X or Y' = X):
calculate Z = 2Y'**2 - 1; retaining the two values already on
the stack
- use the series generator to make (atn Y')/Y'
- multiply this with the Y' on the stack
- add the value below it; this gives the required
result.
Exit: RET (from 37FA CASES).
Output parameters: the last number on the calculator
stack is the number whose TAN is X
- mem-0 to mem-2 corrupted, by the series subroutine.
Called from:
3833 asn
Rems:
3449 series-0C uses twelve Chebyshev polynomials
page 226 BASIC demo of series generation

ATTR key (AB) see also colours, commands, functions and


operators, KEYBOARD SCANNING, 0246 extended mode table
(c)
The L key in E mode with either shift produces the
function ATTR; it requires two numeric operands (X,Y), which
must be in brackets, and the value of the function is the code
number for the attributes of position (X,Y) on the screen.

33
On execution, 24FB SCANNING indexes into the scanning
function table at 2596 to find the executive routine 2672 S
ATTR.
2580 S ATTR S finds value of ATTR (x,y)
26C3 S NUMERIC sets flag for numeric result

attribute address, attribute area, attribute bytes,


attribute value see colours

ATTR P system variable 5C8D see also colours


Bytes: 1
Holds the "permanent" attributes, ie those which are
used to colour the screen as a whole, or print items in the
absence of special instructions: 5C8F ATTR T, which is always
used for actual printing, holds the colours specified by print
instructions, or is copied from ATTR P in the absence of any.
Sometimes handled as a 2-byte value with the next
system variable 5C8E MASK P.
The bits of ATTR P represent fbpapink. They are set to
00111000b/38h on start-up by 1219 RAM SET, black (7) INK on
white (0) PAPER
- the bits are copied from ATTR T, and MASK P is copied
from MASK T, by 1296 PERMS whenever the INK to OVER
codes are used as_autonomous BASIC commands, ie not
merely as part of a PRINT or INPUT command
- they are read and copied into ATTR T, and MASK T is
copied from MASK P, by 0D4D TEMPS in executing a PRINT or
INPUT command; individual bits in ATTR T may then be
changed by subordinated INK to OVER commands
- they are read and poked into addresses in the
attributes area by 0D6E CLS LOWER or 0E4D CL LINE 2 when
executing CLS routines.
Written by:
1219 RAM SET
1C96 PERMS (2 bytes)
Read by:
0D4D TEMPS (2 bytes)

34
0D6E CLS LOWER
0E4D CL LINE 2
Rems:
0CD2 PO SCR 3 values in lower screen may be corrected
on scrolling
0D6B CLS used to set attribute bytes
1646 CHAN S 1 set on exit through 0D4D TEMPS

ATTR T system variable 5C8F see also colours


Bytes: 1
Holds the "temporary" attributes, ie those which are
used when characters are printed. These are copied from
5C8D ATTR P but may then be modified by INK to OVER
commands in the PRINT or INPUT command. Sometimes
handled as a 2-byte value with the next system variable 5C90
MASK T.
The bits of ATTR T represent fbpapink. They are set to
00111000b/38h on start-up by 1219 RAM SET - black (7) INK on
white (0) PAPER
- they are copied from ATTR P, and MASK T is copied from
MASK P, by 0D5B TEMPS 1 whenever a PRINT or INPUT
command is executed, and also whenever channel 2 "S" is
opened to print on the main screen; so if printing is
interrupted, eg to print the "scroll?" message or to scroll the
screen , ATTR T and MASK T must be saved
- they are adjusted to take account of local INK to OVER
commands in 2246 CO TEMP 9 and following
- they are poked to the attributes area by 0BDB PO ATTR
and following.
Written by:
0C88 PO SCR 2 (2 bytes)
0D2D PO SCR 4B (2 bytes)
0D5B TEMPS 1 (2 bytes)
1219 RAM SET
18C1 OUT FLASH (bit 7 set)
2287 CO TEMP E (using 226C CO CHANGE)
Read by:

35
OBDB PO ATTR (2 bytes; NB misprints)
0C88 PO SCR 2 (2 bytes)
0D02 PO SCR 4 (2 bytes)
18C1 OUT FLASH (2 bytes)
1C96 PERMS (2 bytes)
2246 CO TEMP 9 Rems:
0D6E CLS LOWER copied from BORDCR
1646 CHAN S 1 set on exit through 0D4D TEMPS
2258 CO TEMP B altered on colour change

augend see addend

auto
Abbreviation used in this index to mean "contains a jump
back to the same routine"

AUTO LIST subroutine 1795 see also TV FLAG bit 4


Produces an_automatic_listing, with the_current_line on
screen, ie the line with the > cursor. Calling this routine has
exactly the same effect as the LIST command, except that
LIST sets the current line number to zero. Try RANDOMIZE
USR 6037.
The routine first calculates a suitable line number from
which to start the listing, which is kept in 5C6C S TOP. The
actual listing is performed by a call to 1835 LIST ALL from
17ED AUTO L 4. With the "auto listing" flag set this will keep on
listing and scrolling without any "scroll?" prompt till the
current line is on screen, so the line number in S TOP needn't
be calculated precisely provided it isn't too high.
The routine uses the current line number for S TOP
provided it is before the line number already in S TOP,
otherwise moves S TOP on to the line number of each
following line till it finds one whose start is less than 02C0h/
704d bytes above the start address of the current line. There
are 704d bytes in the 22d screen lines of one full screen
display; some of them will be tokens, requiring more bytes,

36
but there will also be spaces at the ends of the lines, requiring
less.
If scrolling is required, it is performed by
0CD2 PO SCR 3 in 0C55 PO SCR,
called by 0B93 PR ALL 1 in 0B7F PR ALL,
the exit from 0B65 PO CHAR and 0B24 PO ANY,
which is called by 0AD9 PO ABLE,
which is called by 09F4 PRINT OUT,
the channel output routine of 0010 PRINT A 1,
called by 1835 LIST ALL,
in the house that Jack built.
The test whether scrolling is required is made by PO SCR
3 itself, before the character has been printed on screen; but
the test includes a check of 5C67 BREG, which is one till the
current line has been printed on screen.
If BREG is zero, the current line has been printed, and
a return from AUTO LIST is required. But the test is made in a
sub-sub-sub-sub-sub-subroutine, nested six deep! This
situation is handled by saving the stack pointer in a system
variable 5C3F LIST SP at the start of AUTO LIST, before any of
the subroutine calls is made, and retrieving it for the return
from PO SCR 3.
[The subroutine call at 17ED AUTO L 4 is to the address
1833, the LD E,01 one line before the line labelled LIST ALL in
"ROM Disassembled"; this line really ought to have a label of
its own. The looping jump within the section LIST ALL is
correctly shown as returning to 1835 LIST ALL.]
Input parameters: none.
Action: save the stack pointer in 5C3F LIST SP
- flag "auto list" in TV FLAG bit 4 and zero its other
bits
- call 0DAF CL ALL to clear the upper screen; zero in TV
FLAG bit zero flags for upper screen
- set TV FLAG bit zero; lower screen now
- get the value from 5C6B DF SZ; the number of lines in
the lower screen
- call 0E44 CL LINE to clear this many lines in the

37
lower screen
- flag "upper screen" again in TV FLAG bit zero and
"screen clear" in FLAGX bit zero
- if the current line number in 5C49 E PPC is less than
the screen start line number in 5C6C S TOP, jump on to AUTO
L2
- (the current line is beyond the screen start line)
call 196E LINE ADDR to get the address of the start of the
current line
- subtract it from 02C0h/704d to get a test value for
the new screen start
- call 196E LINE ADDR again to get the address of the
start of the S TOP line.
_17CE_AUTO_L_1: call 19B8 NEXT ONE to get the start
address of the next line; the provisional screen start
- add the test value: it is 2C0 minus the current start,
so the addition gives
2C0 - (current start - screen start)
and there will be carry if this is_positive
- if there is carry jump on to AUTO L 3; the current
line start is less than 704 bytes further down than the
provisional screen start
- (the provisional screen start is too high) put its
line number in S TOP and loop back to AUTO L 1 to move it on
one line.
_17E1_AUTO_L_2 (the current line is above the screen
start): put the current line number in S TOP.
_17E4_AUTO_L_3 (either way, S TOP now holds the line
number from which to start the listing): call 196E LINE ADDR
to make sure the starting line number is the number of an
actual line; use the next actual line start if not, LINE ADDR
returns it in DE.
_17ED_AUTO_L_4: call 1833 LIST ALL [see note above] to
print the listing; if scrolling is necessary it will scroll till
the current line is on screen, without any "scroll?" prompt,
and

38
return using the 5C3F LIST SP stack pointer when scrolling is
complete; with one bound Jack was free, the return is right
out of the AUTO LIST subroutine
- (no scrolling was needed) flag "screen not cleared" in
TV FLAG bit 4.
Exit: RET, from 17ED AUTO L 4
- or via the sextuple return to the 5C3F LIST SP
address.
Output parameters: none.
Called from:
106E ED LIST
12A2 MAIN EXEC
Rems:
1386 MAIN 9 no listing till requested

AUTO L 1 17CE (1795 AUTO LIST)


Jumps from:
auto

AUTO L 2 17E1 (1795 AUTO LIST)


Jumps from:
1795 AUTO LIST

AUTO L 3 17E4 (1795 AUTO LIST)


Jumps from:
17CE AUTO L 1

AUTO L 4 17ED (1795 AUTO LIST)


Exit from:
1795 AUTO LIST
17E4 AUTO L 3

automatic line number, automatic listing see TV FLAG bit 4

39
B
backspace see CURSOR LEFT, 1007 ED LEFT

base address of channel, etc see channel, etc

BASIC INTERPRETER see also commands, functions and


operators, 1B8A LINE RUN, 24FB SCANNING
"ROM Disassembled" isn't very helpful in explaining how
the ROM actually performs the commands, etc, in a BASIC
program.
What happens is this:
The main execution loop is entered at 12A9 MAIN 1 in
12A2 MAIN EXEC after the start-up routines finish. MAIN EXEC
isn't a subroutine - it has no RET, indeed there is no return
address on the stack for it to return to - but the "beginning" of
ROM operation which calls all the subroutines. It extends to
15AB MAIN ADD 2, which loops back to MAIN EXEC if there
has been no loop back already. There is therefore no exit and
no output parameters.
All routes through the ROM return one way or another to
1303 MAIN 4, even for example NEW, though in this case most
of the system is reset first.
The loop sequence is:
- call 1795 AUTO LIST to give a listing,
then 0F2C EDITOR to prompt an input of BASIC,
then 1B17 LINE SCAN to check the syntax of the line just
input, by a "dummy run" with the syntax checking flag set;
EDITOR doesn't check syntax, the only errors it reports are
“Out of memory" and "Out of screen". Until LINE SCAN is
called there is still no RET address on the stack, and nothing
PUSHed, so the return address from this call to LINE SCAN is
the bottom address on the machine stack, and is at the stack

40
position to which the error stack pointer 5C3D ERR SP was
pointed by 1219 RAM SET. So
not only does LINE SCAN itself return to here in the normal
course of events, but most REPORT calls through 0008
ERROR 1 return here as well; if the error report isn't FF "OK",
execution returns to MAIN 2, the EDITOR is called again to
show the input line with an error cursor
- if it is a program line, with line number, add it to
the program (155D MAIN ADD) and make a listing
- if it is a direct command, with no line number, execute
it by a call to 1B8A LINE RUN, misprinted PROG RUN in the
listing. See under LINE RUN for a description of the way it
works the BASIC commands and functions
- if the command contains a RUN or GO TO command,
continue execution still within the LINE RUN loop; but now
1BB3 LINE END will loop on into following numbered BASIC
lines till the end of the program is reached. When LINE RUN
is called there is again no RET address on the stack, and
nothing PUSHed, so the return address from this call to LINE
RUN, address 1303 MAIN 4, is now the "error address"
- on return to the main loop, print an error report,
which may be "OK", and loop back to wait for another input.
_Action:
_12A2_MAIN_EXEC (the loop re-entry point from MAIN 3 or
MAIN ADD2 after a line has been put in the program): put 2 in
5C6B DF SZ; set the lower screen to two lines, one blank and
one for input
- call 1795 AUTO LIST for an automatic listing.
_12A9_MAIN_1 (the entry point on start-up): call 16B0 SET
MIN to clear the editing area, work space and calculator stack.
_12AC_MAIN_2 (the entry point after a command has been
executed and the error report printed): call 1601 CHAN OPEN
with stream zero to open channel K for keyboard input
- call 0F2C EDITOR, which handles the typing in of a
BASIC line, with or without line number, in the lower screen;
execution doesn't return from the EDITOR loop till you press
ENTER

41
- call 1B17 LINE SCAN to check the syntax of the line
just input
- check the hi bit of 5C3A ERR NR; the only error number
which has its hi bit set is FF "OK"
- if there is no error jump on to MAIN 3
- (there is an error) check bit 4 of FLAGS2
- if it is zero jump on to MAIN 4; input is coming from
some peripheral, not from the keyboard
- get the start address of the error line from 5C59 E
LINE
- call 11A7 REMOVE FP to take the FP number forms out of
the line; see under "number marker"
- put FF in 5C3A ERR NR; errors aren't reported till run
time
- loop back to MAIN 2; this time, when EDITOR prints out
the edit line there will be an error address in 5C5F X PTR
which will produce a flashing "?" as an error cursor.
_12CF_MAIN_3 (no error in input line) put a pointer in
5C5D CH ADD from 5C59 E LINE; the start of the line just
input
- call 19FB E LINE NO to see if it has a line number -
it returns zero if it doesn't find one
- if there is one jump on to MAIN ADD
- (no line number) if the edit line is just a newline
jump back to MAIN EXEC, without an error report
- (the edit line is a BASIC command, without line
number) check bit zero of FLAGS2; zero if the screen is
cleared
- clear the upper screen if it needs it, and the lower
screen anyway
- set the scroll counter 5C5C SCR CT at 19h less the
current print position line number; see DISPLAY AREA. If a
scroll is called for this will put the current print position at
the top of the screen
- set the "run" flag FLAGS bit 7
- put the "OK" message number in 5C3A ERR NR
- set the statement counter 5C44 NSPPC to one

42
- call 1B8A LINE RUN (misprinted PROG RUN) to execute
the un-numbered BASIC command.
_1303_MAIN_4 [the HALT at the start of MAIN 4 is a little
mysterious; it doesn't enable the interrupt, as the notes might
imply. I think its purpose is to allow one more call to the
maskable interrupt after execution is complete, making sure
that the lower screen flags and system variables are ready to
print the error report]: zero bit 5 of FLAGS "waiting for a key"
- if bit 1 of FLAGS2 is set call 0ECD COPY BUFF to clear
it; "something in the printer buffer"
- increment the error number; "OK" will go to zero, "1"
-> "9" to one -> 9, and the lettered reports A -> R will go to
0A -> 1B.
_1313_MAIN_G (this entry point is used by REPORT G): zero
5C71 FLAGX; "editing mode" in bit 5
- zero 5C55 X PTR; no error cursor
- zero 5C0B DEFADD; not using DEF FN variables
- make sure the keyboard stream zero has the right
channel offset
- call 16B0 SET MIN to clear the editing area, work
space and calculator stack
- zero bit 5 of FLAGX; again! an error
- call 0D6E CLS LOWER to clear the lower screen
- set bit 5 of TV FLAG; "lower screen needs clearing"
- adjust the report code, if it is report A to R, so
that the correct letter will be printed by OUT CODE, which
adds 30h/48d.
_133C_MAIN_5: call 15EF OUT CODE to print the report
code
- call 0010 PRINT A 1 to print a space
- call 0C0A PO MSG to print the report message
- and again to print "comma space"
- call 1A1B OUT NUM 1 to print the line number
- call 0010 PRINT A 1 to print a comma
- call 1A1B OUT NUM 1 to print the statement number;
seven calls to four different subroutines to print a one-line
message! But m/c printing on screen often involves such

43
complexities
- call 1097 CLEAR SP to clear the editing area; surely
redundant after the call to SET MIN in MAIN G
- if the error number is FF "OK" jump on to MAIN 9
- if it was 08 "STOP" jump on to MAIN 6
- if it was_not 14 "BREAK" jump on to MAIN 7.
_1373_MAIN_6 (STOP or BREAK): increment the statement
number in 5C67 SUBPPC, so that CONTINUE will resume
execution from the next statement; all interruptions_except
STOP and BREAK resume from the statement where they
stopped, which presumably contained an error.
_1376_MAIN_7: make a byte counter three
- make end pointers for copying from 5C44 NSPPC to
5C70
OSPCC; this will copy
5C42 NEWPPC lo -> 5C6E OLDPPC lo
5C43 NEWPPC hi -> 5C6F OLDPPC hi
5C44 NSPPC -> 5C70 OSPCC
- check the hi bit of NSPPC
- if it is zero jump on to MAIN 8; there is a statement
number in the lo bit of NSPPC, indicating a jump to NEWPPC/
NSPPC, since NSPPC hi will be FF unless the last command
called for a jump
- add three to the 5C44 NSPPC pointer; now the copying
will be from the current line/statement OLDPPC/OSPCC:
5C45 PPC lo -> 5C6E OLDPPC lo
5C46 PPC hi -> 5C6F OLDPPC hi
5C47 SUBPPC -> 5C70 OSPCC
[I don't know why OSPCC ends -PCC when all the others
end -PPC, but it is the same in all Spectrum literature.]
_1384_MAIN_8: copy the three bytes.
_138C_MAIN_9: load FF into NSPPC; "no jump"
- zero bit 3 of FLAGS; "K mode"
- zero bit 3 of FLAGS; "K mode"
- jump back to MAIN 2.
_1391 [Here the report messages are interpolated - see
"tables" in this index.]

44
_155D_MAIN_ADD ( jump to here from MAIN 3 if the editing
input had a line number; the edit line has passed syntax, has a
newline at the end, and has had its FP number forms inserted,
but - its line number is in decimal digits, and it should be
in the hi-lo form in which it is kept in the program area; the
line number is in BC anyway
- it needs its length bytes to be inserted after the line
number):
- put the current line number in 5C4A E PPC
- get a pointer to the start of the line from 5C5D CH
ADD; it was put there, after the line number, by the call to
19FB E LINE NO in MAIN 3
- put the address 1555 REPORT G "No room for line" on
the stack; the stack is empty, so this is temporarily the error
address. Unlike all the other REPORT routines it makes no call
to 0008 ERROR 1 and just jumps to MAIN G; the only possible
error is in the call to MAKE ROOM in MAIN ADD1
- subtract the line start address from the end of the
editing area stored in 5C61 WORKSP, less one; this is the line
length, excluding its line number and length bytes but
including the newline at the end
- call 196E LINE ADDR to check if there is a line in the
program with the same number
- if not jump on to MAIN ADD1
- call 1988 NEXT ONE and 19E8 RECLAIM 2; this deletes
the old line from the program.
_157D_MAIN_ADD1: if the line length is one, just the
newline, jump on to MAIN ADD2; deleting the old line is all
that is required. [A BASIC line consisting of a space only, or
colour controls only, is copied to the BASIC program: such
lines can be used to punctuate or colour code BASIC
programs]
- increase the length by four for the line number and
length bytes
- move the BASIC pointer returned by LINE ADDR back to
the byte before the insertion
- save the pointer from 5C53 PROG

45
- call 1655 MAKE ROOM to make a space in the program at
the pointer
- restore PROG from the stack; if the line inserted was
the first line, 1664 POINTERS called by MAKE ROOM has
moved PROG up
- get the line length of the line in the editing area,
without its line number
- copy the BASIC to the program area, from the end
backwards; the end is two before the address in 5C61
WORKSP, ie omitting the newline
- get the line number from 5C49 E PPC
- copy the length bytes in lo-hi order and the line
number in hi-lo order backwards into the remaining four
bytes.
_15AB_MAIN_ADD2: drop the REPORT G error address and
jump back to MAIN EXEC.

BASIC line, BASIC line number, BASIC statement see also


5C49 E PPC, 1695 LINE NO, 24FB SCANNING, 5C6C S TOP,
5C3C TV FLAG/4
Lines are input to the lower screen, or brought down
from the program for revision there, by the 0F2C EDITOR
subroutine. The new line or the line being revised is first
copied or input to the editing area, and printed from there to
the lower screen. When ENTER is hit, the line is checked for
syntax by 1B17 LINE SCAN; if it isn't correct, execution returns
immediately to EDITOR and the line is displayed again with an
error cursor. When it has passed syntax, if it has a line
number it is transferred into the program area by 155D MAIN
ADD; see BASIC execution above.
The format of the_BASIC_line_in_the_program_area is:
1st two bytes:_line_number, as a two-byte integer but
reversed; hi byte followed by low byte, converted to this form
by 157D MAIN ADD1
3rd and 4th bytes:_length, including the final newline
but excluding the line number and length bytes
followed by the bytes of the line: any number in decimal,

46
binary or E-format is followed by the number marker 0Eh/14d
and then by five bytes giving the FP format of the number
concluded by 0Dh/13d newline.
Variable letters in the BASIC line in the program area
may be upper or lower case, and don't have the type flags in
their first three bits, and numeric variables or expressions
other than simple numbers aren't followed by the FP form.
The format of the line in the editing area differs as
follows:
the line number is an ordinary BASIC decimal
the length bytes are omitted
the number markers and FP forms are omitted.
_Statement_numbers aren't recorded anywhere, but
counted up as required in a_statement_counter by counting
colon characters from the end of the line.
System variables holding line numbers: E PPC, NEWPPC,
NXTLIN, OLDPPC, PPC, S TOP;
holding statement numbers: NSPPC, OSPCC, SUBPPC;
holding pointers to the BASIC program: CH ADD,
DATADD, DEFADD, KCUR, PROG, X PTR.
_Terminators, marking the end of statements or lines, are
the newline 0Dh marking the end of the line
the colon marking the end of a statement
THEN is also taken as a statement terminator for most
purposes, but not in counting statement numbers.
Another form of BASIC line is the "expression", string
or numeric, typed in by the user in response to an INPUT
command:
it cannot have a line number or contain BASIC commands
it is input not to the editing area but to the work
space, where it is evaluated by SCANNING and its value given
to a variable; but as before, it is copied as it is input from the
work space to the lower screen.
The EDITOR subroutine is used to produce the input just
as in typing in BASIC commands.
Introduction considered by BASIC INTERPRETER
0020 NEXT CHAR steps along as line interpreted

47
007D SKIP OVER return if end reached
0692 SA DATA 1 checks end of SAVE etc array statement
06C3 SA CODE check for end of statement
0723 SA LINE 1 finds LINE line number
0873 LD PROG check start line saved with program
08B6 ME CONTRL part ii merges lines into old program
08D2 ME NEW LP checks number of new line
08D7 ME OLD LP compares number of old line
08DF ME OLD L1 finds place for new line
08EB ME NEW L2 enter new line
092C ME ENTER NC flag signals BASIC line
093E ME ENT 1 makes room for new line
0958 ME ENT 3 copies into program and clears work
space
0A3D PO RIGHT same as "PRINT OVER 1; CHR$32"
statement
0F2C EDITOR enters line in main execution
0F81 ADD CHAR adds byte to line
0FA9 ED EDIT edit key puts in editing area
0FF3 ED DOWN next line number found
1031 ED EDGE checks for start of line
103E ED EDGE 1 addresses consecutive characters in
1059 ED UP line numbers checked
10A8 KEY INPUT edit or INPUT line copied to screen
111D ED COPY prints line in lower screen
1150 ED BLANK blanks out old line in lower screen
117C ED C DONE exit after line copied
11A7 REMOVE FP removes FP numbers from line
12AC MAIN 2 calls EDITOR to write line
12CF MAIN 3 sorts program lines from direct commands
1303 MAIN 4 reports after command line executed
133C MAIN 5 line/statement number included in report
1376 MAIN 7 store CONTINUE line/statement number
155D MAIN ADD adds line to program
157D MAIN ADD1 copies line from work space into
program
168F LINE ZERO holds zeroes for line number

48
1691 LINE NO A use zero if no suitable line number
1795 AUTO LIST evaluates line number etc for each line
17CE AUTO L 1 find first line to show on screen
17E4 AUTO L 3 collect first line to show on screen
17ED AUTO L 4 skipped if scroll needed for current line
181F LIST 4 handles line number in LIST xxxx
1822 LIST 5 finds line start for listing
1835 LIST ALL loop to list BASIC lines
1855 OUT LINE prints out line
1865 OUT LINE1 prints line number
1894 OUT LINE4 loop to print each command of line
18B4 OUT LINE6 line print finished
190F LN FETCH finds number of line
191C LN STORE puts line number in system variable
1925 OUT SP 2 prints whole line
192B OUT SP 1 prints digit of line number
196E LINE ADDR finds address of line start from number
1974 LINE AD 1 checks number found with number given
1980 CP LINES compares line numbers
198B EACH STMT finds D'th statement
1990 EACH S 1 looks at each statement
1998 EACH S 2 finds end of statement
19AD EACH S 5 loop at end of statement
19B1 EACH S 6 checks for end of line
19B8 NEXT ONE find start of next BASIC line
19D5 NEXT O 3 skip line number in reading BASIC
19D6 NEXT O 4 get length of BASIC line
19DB NEXT O 5 first byte of next line found
19FB E LINE NO reads line number in editing area
1B17 LINE SCAN checks each statement for syntax
1B28 STMT LOOP loop to handle statements
1B29 STMT L 1 identifies and executes each statement
1B76 STMT RET end of statement execution
1B7D STMT R 1 check for new line execution
1B8A LINE RUN entry point for execution of line
1B9E LINE NEW jump to other than next line
1BB3 LINE END goes to next line unless program finished

49
1BBF LINE USE prepares to execute next line
1BD1 NEXT LINE reads line for interpretation
1BEE CHECK END checks for end of statement
1BF4 STMT NEXT finds next statement or next line
1C11 CLASS 05 save address of line start
1C96 PERMS changes colours as directed by current stmt
1CDE FETCH NUM checks for end of line
1CF0 IF skips line if false
1D00 IF 1 executes next statement if true
1D03 FOR loop values for statement already on stack
1D34 F L&S gets line and statement number
1D64 F LOOP checks each line in program
1D7C F FOUND line holding NEXT found
1D86 LOOK PROG checks for more statements in line
1D8B LOOK P 1 checks each line in program
1DA3 LOOK P 2 checks each statement in line
1DAB NEXT looping line and statement number found
1DEC READ 3 move along READ statements
1DED READ check if another DATA statement required
1E0A READ 1 get pointer to READ statement
1E27 DATA handling DATA statements
1E2C DATA 1 check end of statement
1E37 DATA 2 passed by in run time
1E39 PASS BY skips DEF FN or DATA statements
1E42 RESTORE operand read as line number
1E45 REST RUN put DATADD before line start
1E5F CONTINUE jumps to line/stmt in OLDPPC/OSPCC
1E67 GO TO finds next line (statement 0) to execute
1E73 GO TO 2 finds next line number to be executed
1EED GO SUB increments statement number
1F23 RETURN line and statement number from GO SUB
stack
1F60 DEF FN passed by in run time
219B IN VAR 6 find length of line
21AF IN NEXT 1 considers INPUT statement
21E1 CO TEMP 1 look at next byte in statement
268D S BIN copies FP number into line

50
27BD S FN SBRN finds user-defined function in BASIC line
28FD V RUN look for vble in execution of line
294B V END after vble search, CH ADD addresses next
code in BASIC line
2996 STK VAR called from DIM just to check line syntax
29AE SV ARRAYS checks line for subscript
29C3 SV COMMA finds subscript error if line executed
29D8 SV CLOSE line may still call for slicing
2AFF LET for new variable, DEST points to line
2C9B DEC TO FP numbers in line converted to FP form
2D3B INT TO FP integers in line converted to FP form

BASIC program see program

BASIC statement see BASIC line

BC SPACES subroutine 0030


Makes spaces in the work space. The 1655 MAKE ROOM
subroutine will make spaces anywhere in the RAM, moving up
the calculator stack etc and resetting the pointers. But space
is so often required specifically in the work space that this
restart routine is worth having.
An empty workspace as set by 16B0 SET MIN has zero
length: 5C61 WORKSP and 5C63 STKBOT hold the same
address, preceded in memory by a newline and 80-byte
marking the end of the editing area.
MAKE ROOM operates by LDDR, and the new "space" isn't
blanked; so the "space" made by this subroutine is actually a
copy of the following bytes. Because of the DEC HL near the
start of 169E RESERVE this space is always inserted_before the
last byte in the workspace. If five spaces are made in an empty
workspace, the result will be (the new bytes are underlined)
... NL_80_a_b_c_d 80 A B C D E
where "a" is at the address in 5C61 WORKSP, and A B C D E
are the first five bytes of the calculator stack; the NL and first
80-byte are the end of the editing area, a b c d are copies of A
B C D.

51
Further calls to BC SPACES will again copy the last byte
of the work space; it will always be an 80-byte unless it is
overwritten at some stage, or until 16B0 SET MIN is called to
delete the work space altogether.
Input parameters: BC holds the number of bytes space
required; it mustn't be zero!
Action: stack the number required and the address from
5C61 WORKSP and jump on to RESERVE.
_169E_RESERVE (it cannot be called direct, as implied by
the header, because it POPs bytes before its RET): get the top
of the work space from 5C63 STKBOT
- move back one byte; to the 80-byte in the editing
area, or the last byte of the work space
- call 1655 MAKE ROOM to make BC spaces below this
pointer
- restore 5C61 WORKSP; if the workspace was empty its
start address will have been moved up by MAKE ROOM
- (HL was left by the LDDR in MAKE ROOM pointing to the
byte before the new spaces, NL in the example) increment HL
twice and transfer it to DE; now DE points to the first byte of
the new space, "a" in the example
- similarly point HL at the start of the calculator
stack.
Exit: RET from RESERVE.
Output parameters: BC unchanged: HL and DE as
described
above.
Called from:
0621 SA SPACE
08B6 ME CONTRL
211C IN PR 2
255D S SC ROWS
25BE S Q AGAIN
2634 S INKEY$
2B72 L DELETE$
359C strs-add
35C9 chrs

52
35DE val
361F str$
3645 read-in

BE AGAIN 03F2 (03B5 BEEPER)


Jumps from:
03D6 BE H&L LP

BE END 03F6 (03B5 BEEPER)


Jumps from:
03D6 BE H&L LP

BEEP key (D7) see also commands, functions and operators,


KEYBOARD SCANNING, 0246 extended mode table (c)
The Z key in E mode with either shift produces the
command BEEP. The command must be followed by two
numeric expressions, separated by a comma. It is read by
1B29 STMT L 1 referring through the syntax offset table 1A48
to the syntax parameter table at 1A7A. 1AE3 P BEEP collects
the parameters by
1C7A CLASS 08, then causes a jump via 1C10 CLASS 00 and
1C16 JUMP C R to the executive routine 03F8 BEEP.

BEEP subroutine 03F8 see also timing


Calculates a beep of given pitch and duration.The actual
beep is executed by 03B5 BEEPER, into which this routine
exits.
Only used to execute the BEEP command; can be called
from m/c, but it is simpler to call 03B5 BEEPER direct
This routine is concerned only with calculating, from
the pitch P, as defined in the Spectrum Manual, Chapter or
Part 19, and the duration t in seconds, the parameters for
BEEPER:
the number of complete loudspeaker on/loudspeaker off
cycles required, f * t, where f is the frequency in hertz, and
the length of the timing loop in units of four T states;

53
this takes the form of a delay counter for the sound generation
loop.
P and t are of course input as part of the BASIC
command, and are therefore found on the calculator stack.
The routine checks that P and t are within acceptable limits.
P as input in BASIC is zero for middle C, plus or minus
one for each semitone above or below middle C; P must be
between -60d and +69d inclusive, but needn't be a whole
number. There are twelve semitones in an octave; doubling
the frequency increases the pitch by an octave, so the
increase in frequency
for one semitone is an increase by a factor 1 + K where
(1 + K)**12d = 2, ie
K = twelfth root of 2 minus one
(badly printed in the notes).
Thus, since 261.63d hertz is the frequency of middle C,
f = 261.63d * (1 + K)**P.
The routine first calculates f in several stages:
- separate P into an integer part and a fractional part,
i and p
- multiply the fractional part p by the constant K as
described above; actually a slightly lower value is used, to
allow for a slight "overhead" in the timing loops. This gives an
adjustment factor to increase the frequency allowing for the
fraction-of-a-semitone increase in pitch required
- reject i if it is below -60d or above 69d
- reduce i by twelve till it is less than twelve, keeping
count; the count indicates the octave within which the pitch
falls, the reduced i indicates the nearest semitone below the
pitch P = i + p
- index with the reduced i into a table which gives the
correct pitch for each semitone of the middle C octave, as a
list of twelve constants
- increment this semitone frequency by 1 + pK, the
fractional adjustment
- double the frequency once for each octave recorded in
the count.

54
The calculation is organised in such a way as to produce
correct results for either negative or positive i.
Next it checks t; eleven-second beeps aren't allowed.
Now f * t is calculated and stacked; this is the first
of the two parameters for BEEP.
Now the counter for the timing loop is calculated.
Each delay is four T states, and the delay is used twice
in each cycle; one second on the Spectrum is 3,500,000d T
states; and the operation of the loops takes a total of 241d T
states: so the value of the counter should be
(3,500,000d/f - 241d)/8 = 437,500d/f - 30.125d
This is the second parameter for BEEP; rejected if it
comes out too big, indicating that P was more than +69d.
[By my calculations this parameter is slightly
incorrect; see under 0385 BEEPER below.]
A last check is made of f * t; if it is zero no beep is
made, but no error reported, allowing for BEEP x,y
commands where y may be zero.
Input parameters: The last number on the calculator stack
is read as P, the next as t. They must be on the calculator
stack even for direct calls from m/c.
Action: use the calculator to split P into a fractional
and integer part
- get pK + 1; the fraction-of-a-semitone adjustment for
the eventual frequency, see above
- get the exponent of the integer part i; now in mem-0
- if it isn't zero report "Integer out of range"; i must
be in small integer form
- get its sign byte; 00 for positive, FF for negative
- get its lo byte and shift its hi bit into carry; there
will be carry if the byte is more than 7Fh/127d - but if the
sign is negative this means carry for -80h/128d or more
- now SBC A,A; giving zero if there was no carry, FF if
there was
- if the result doesn't match the sign byte report
"Integer out of range"; a positive byte more than 7F or a
negative one less than -80h

55
- get the hi byte
- if this doesn't match the sign byte report "Integer
out of range"; the hi byte should be 00 for positive, FF for
negative too
- add 3Ch/60d to the lo byte
- if the result is "positive" jump on to BE i OK;
"positive" means the hi bit is zero, so the jump is only made
for positive i of 43h/67d or less
- (i is negative, or positive > 43h/67d) if the addition
didn't produce a change in parity report "Integer out of
range";
this rejects negative values of i from -80h/128d to -3Bh/59d, ie
any negative value is now correct, but incorrect positive
values 46h/70d to 7Fh/127d have still passed - they will be
rejected
when FIND INT2 is called later to get 437,500d/f - 30.125 as a
small integer.
_0425_BE_i_OK: make an octave counter FAh/minus 6
_0427_BE_OCTAVE (the entry value for the pitch is now the
result of the last addition, the integer part i of the pitch
plus 3Ch/60d, and ranges from one to BBh/187d. Values from
one to 3Bh/59d represent negative i, below middle C; 3Ch/
60d is
middle C itself, zero; values from 3Dh/61d upwards represent
positive values, some of which are incorrect): increment the
counter
- take 0Ch/12d from the pitch value
- if it is still positive or zero loop back to BE OCTAVE
- add back 0C/12h; now i is a semitone value within the
octave, ie between one and twelve, the octave count is
between minus six and plus eleven
- call 3406 LOC MEM with the semitone value to index
into the table at 046E
- call 33B4 STACK NUM to put the FP number from the
table on the calculator stack; the frequency of the
corresponding semitone in the octave of middle C, call it C
- use the calculator to multiply C by the fractional

56
pitch adjustment still on the stack
- add the octave count to the exponent of C; this
doubles the frequency once for each count of the octaves
- use the calculator and call 1E94 FIND INT1 to get INT
t into a register; t is the length of the BEEP in seconds as
originally input from BASIC
- if INT t exceeds ten seconds report "Integer out of
range"
- use the calculator to figure f * t; the first BEEP
parameter
- figure 437,500d/f - 30.125; the second BEEP parameter
- call 1E99 FIND INT2 to put the 2nd parameter in a 2-
byte register; if it won't go, because the pitch P was too high,
this reports "Integer out of range"
- call 1E99 FIND INT2 to get the first BEEP parameter
- move the parameters into the registers where BEEPER
expects to find them
- check the first BEEP parameter for zero
- if it was zero return without going through BEEPER
- decrement it; because BEEPER counts from zero.
Exit: into 03B5 BEEPER, but RET if the BEEP is zero.
Output parameters: DE holds the number of cycles
- HL holds the length of the timing loop in T states/4
- P and t have been removed from the calculator stack
- mem-0 is corrupted.
Rems:
03B5 BEEPER controlled by this command routine
03D6 BE H&L LP timing loop controlled by parameter
03F6 BE END completed
33B4 STACK NUM called by

BEEPER subroutine 03B5


Activates the loudspeaker for a given number P of pulses
each of given duration D, half sound and half silence; D is in
units of four T states, see timing.
In the execution of the BEEP command from BASIC, P
and D are calculated by the subroutine BEEP from the BASIC

57
input of pitch and total duration, but the subroutine is also
called direct from 0F38 ED LOOP to make the keyboard
"click", and from 107F ED ERROR and 1167 ED FULL to make a
"rasp", with preset values of P and D.
It can similarly be called from m/c programs, with P and
D calculated in the program or outside it, or determined by
good old trial and error!
The interrupt is disabled during BEEPER, since the
irregular length of the interrupt routines would otherwise
disturb the pitch.
For simplicity, decimal numbers are used unless
otherwise indicated throughout the rest of this description:
The hi byte and the lo byte of the duration D are used
as separate delay counters for the delay loop BE H&L LP. The
hi byte H is used as it stands but the lo byte is divided by 4,
giving L = INT (lo byte/4) and a remainder byte r; the value
used for r is 3 minus the remainder on dividing the lo byte by
four, so that D = 256 * H + 4 * L + 3 - r.
The delay loop BE H&L LP is in two parts. The first part
takes 16 T states each time its counter X, say, doesn't reach
zero, and 11 when it does; a total of 16 * (X - 1) + 11 = 16 * X
- 5 T states. The second part adds 21 T states to the total
delay of the first; resets the counter for the first to 63; and
loops back with a second counter Y, say, to repeat. Thus the
first turn through the whole loop takes 16 * X - 5 + 21 = 16 *
(X + 1) T states, and the other Y - 1 turns take 16 * (63 + 1) =
16 * 64 each; so the total delay of the loop is
16 * (X + 1) + 16 * 64 * (Y - 1)
T states.
When the speaker is turned off, X and Y are given the
values L + 1 and H + 1, so the delay works out at
16 * L + 32 + 16 * 64 * H = [256 * H + 4 * L + 3] * 4 + 20
T states. The expression in [] is equal to the duration
parameter D, plus r.
When the speaker is on, L is incremented, adding 16 T
states; so the delay is 4 * (D + r) + 20 for speaker off and 4 *
(D + r) + 36 for speaker on.

58
For each cycle of the on/off loop after the first,
starting when the speaker is turned on by OUT (FE),A:
- reset the hi counter, jump on to BE AGAIN where the lo
counter is incremented, and loop; this takes 44 T states
- delay by 3 - r NOPs, ie 4(3 - r) T states, plus 8 T
states to increment the counters, 4 * (D + r) + 36 in the delay
loop, 7 to flop the OUT byte, and 11 for the OUT itself: total
from OUT "on" to OUT "off",
44 + 4 * (3 - r) + 8 + 4 * (D + r) + 36 + 7 + 11 =
4 * D + 118
- now reset the hi and lo counters, check and decrement
the pulse counter, and loop; 60 T states this time
- again delay by 3 - r NOPs, 8 T states to increment the
counters, only 4 * (D - r) + 20 in the delay loop this time, 7
to flop the OUT byte, and 11 for the OUT itself: total from OUT
"off" to OUT "on",
60 + 4 * (3 - r) + 8 + 4 * (D + r) + 20 + 7 + 11 =
4 * D + 118 again.
Thus the whole loop takes 8 * D + 236 T states: however
D is calculated on the assumption it takes 8 * D + 241. Either
my calculations are incorrect or the Spectrum BEEP is slightly
out of tune!
The OUT to port FE on each half loop is a code with the
present border colour in the three lo bits so that the border
colour isn't changed by the output, and either one in bit 4 to
turn the loudspeaker on, or zero to turn it off. See "ports".
Input parameters: DE holds the number of pulses P
- HL holds the duration D of each pulse.
Action: disable the interrupt
- halve the lo byte of the duration twice; making L = lo
byte/4, so the loop counter is now 256 * H + L
- reverse the original value of L; ie subtract it from
100h, 100h - L
- AND this with 3; this gives the remainder on dividing
100h - L by 4, so it neatly finds r, 3 minus the remainder
- add r to the address of BE IX+3 in IX; thus adding r
NOPs to the loop starting at BE IX+0

59
- get the present border colour from 5C48 BORDCR
- AND it with 00111000b/38h; isolating the PAPER colour
- shift it 3 times right; making the PAPER number
- AND it with 00001000b/08; this is now the OUT byte,
with the BORDER colour, bit 3 set for "MIC off", and bit 4 zero
for "speaker off"
_03D1_BE_IX+3, etc: delay by up to 3 NOPS.
_03D4_BE_IX+0 (on the first entry, the delay counter
holds the small number 3 - r, and the loudspeaker is off ):
increment each byte of the delay counter
_03D6_BE_H&L_LP: decrement the lo counter
- if it hasn't gone to zero loop back to BE H&L LP
- put 63d in the lo counter
- decrement the hi counter
- if it hasn't gone to zero loop back to BE H&L LP
- XOR the OUT byte with 00010000b/10h to flop the
speaker on/off bit 4; on the first loop it is now "on"
- OUTput it to port FE
- restore the hi byte of the delay counter to its
original value H
- if bit 4 of the OUT byte is set jump on to BE AGAIN;
the speaker is on
- (speaker off ) if the pulse counter has reached zero,
jump on to BE END
- (more pulses to come) restore the lo byte of the delay
counter to its original value L
- decrement the pulse loop counter
- jump back to the start of the delay loop IX; one of BE
IX+3 to BE IX+0 depending on the remainder byte r.
_03F2_BE_AGAIN (speaker on): restore the lo byte of the
delay counter to its original value L
- increment L once; to compensate for the absence of the
pulse counter check
- jump back to IX.
_03F6_BE_END (the pulse counter has gone to zero) re-
enable the interrupt and finish.
Exit: RET.

60
Output parameters: none
- interrupt is re-enabled.
Called from:
0F38 ED LOOP
107F ED ERROR
1167 ED FULL
Exit from:
0427 BE OCTAVE (03F8 BEEP)

BE H&L LP 03D6 (03B5 BEEPER)


Jumps from:
auto (twice)

BE i OK 0425 (03F8 BEEP)


Jumps from:
03F8 BEEP

BE IX+0 03D4, BE IX+1 03D3, BE IX+2 03D2, BE IX+3 03D1


(03B5
BEEPER)
Jumps from:
03D6 BE H&L LP (a jump to IX calculated in 03B5
BEEPER)

BE OCTAVE 0427 (03F8 BEEP)


Jumps from:
auto

BIN key (C4) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The B key in E mode without shift produces the function
BIN; not strictly a function, it must be followed by a string of
zeros and ones which are to be read as a binary number. On
execution, 24FB SCANNING indexes into the scanning
function table at 2596 to find the executive routine 268D S
BIN. This doesn't actually convert the binary number to 5-

61
byte FP form: it is done by 2C9B DEC TO FP, study of which
will show that
(a) the binary form may not include a fractional point
(b) it can have no more than 16 binary digits - up to FFFFh/
65535d.
These limitations aren't mentioned in the handbooks!

binary format of numbers see BIN key

binary operation see also "+" (2B) after end of alphabet


This term is used in the notes to refer to operations
which have two arguments placed one before and one after
the operator, as opposed to_unary_operations which have
only one placed after the operator. Addition is a binary
operation;
"unary minus" as in -12 is a unary operation. Nothing to do
with operating with binary numbers!
Because the argument before the operator may itself be
an expression containing functions or operators, binary
operators have to be given a priority ranking; functions and
unary operators have these too, but it could have been
avoided for them. In fact all the functions have the maximum
possible priority, but not all the unary operators.
The distinction is made in a different way when
referring to the operation subroutines called by the calculator
literals; here_binary_operations means those which are
entered with two arguments on the calculator stack - they are
literals 1 to 17h - and_unary_operations those which are
entered with only one.
The difference is in the stack pointers: HL always
points to the the sole argument of a unary operator, but to the
"second value", ie the last but one on the stack, for a binary
operator. DE always points five bytes further on, which is the
stack end in 5C65 STKEND for a unary operator but the first
byte of the last value for a binary operator.
2712 S CONT 1 next code may be binary operator
2723 S OPERTR codes alloted to binary operators

62
2734 S LOOP machine stack holds operators and
functions
3293 RE ST TWO full FP forms required for binary ops
335B CALCULATE eg addition
3380 FIRST 3D HL points to first operand, DE to second
("last value")
33A1 delete counts as binary; "conjuring trick" result
343C exchange - a binary operation
346A abs - a unary operation
3524 no-&-no - a binary operation
352D str-&-no - a binary operation
359C strs-add - a binary operation
3851 to-power - a binary operation

binary point see "." (2D) after end of alphabet

binary representation of numbers see BIN key

BIN DIGIT 2CA2 (2C9B DEC TO FP)


Jumps from:
auto

binding see 24FB SCANNING

BIN END 2CB3 (2C9B DEC TO FP)


Jumps from:
2CA2 BIN DIGIT

BITS ZERO 3283 (3214 truncate)


Jumps from:
3272 NIL BYTES

blanking of BASIC lines


If the line just copied to the lower screen for editing
is shorter than whatever was there before, the difference is
filled with spaces.
1150 ED BLANK jump forward if not required

63
block graphic characters see graphics keys

block of data see program area

BORDCR system variable 5C48 see also 2294 BORDER,


colours
Bytes: 1
Contains the attributes for the lower screen, in the
usual fbpapink format. Despite its name BORDCR isn't
normally used to colour the border, only the lower screen;
but its PAPER colour is set to match the colour of the border.
It is read by
0E44 CL LINE to colour the lower screen after clearing, and
by
0D4D TEMPS whenever attributes are required for the lower
screen.
The ROM never puts FLASH or BRIGHT 1 in BORDCR, nor
any INK colour except black or white; it is set to 00111000b,
black ink on white paper, by 1219 RAM SET, and set to the
paper colour of user's choice, with black or white contrasting
ink, by 2294 BORDER.
However its paper colour is output to port FE by 0F35
SA/LD RET to restore the border colour after sending stripes
to it, and by 03B5 BEEPER to avoid changing the border when
outputting speaker signals
POKing BORDCR, 23624d, from BASIC or m/c can
produce a lower screen of different colour from the border,
with any coloured ink and even with FLASH or BRIGHT. The
border won't be affected. However, in the 128K Spectrum any
key input will change the border to match the paper colour of
the lower screen.
Written by:
1219 RAM SET
22A6 BORDER 1
Read by:
03B5 BEEPER

64
053F SA/LD RET
0D4D TEMPS
0E4D CL LINE 2
Rems:
0D6E CLS LOWER copied to ATTR T
2294 BORDER saved in BORDER 1

BORDER key (E7) see also commands, functions and


operators,
KEYBOARD SCANNING
The B key in K mode produces the command BORDER; it
must be followed by a numeric expression whose value is
positive and less than eight. If it isn't an integer, it will be
rounded to a value from zero to seven when it is taken off the
stack by 2DD5 FP TO A. The command is read by 1B29 STMT L
1 referring through the syntax offset table 1A48 to the syntax
parameter table 1A7A.
1AF5 P BORDER causes a jump via 1C82 CLASS 06, which
collects the colour parameter, 1C10 CLASS 00 and 1C16 JUMP
C R to the executive routine 2294 BORDER.
Unlike the colour commands INK to OVER, BORDER
cannot be "embedded" in a PRINT command.
03B5 BEEPER border colour moved to bits 2-0 of A
03D6 BE H&L LP border unchanged by OUT (FE) A
04D0 SA FLAG border made RED
04DB SA LEADER border striped RED/CYAN by "edges"
04EA SA SYNC 1 border on RED, off CYAN
04F2 SA SYNC 2 mic off and border CYAN
0507 SA START mic on and border BLUE
0511 SA BIT 2 mic off and border YELLOW
051C SA OUT border alternates BLUE/YELLOW
053F SA/LD RET border restored to original colour
0556 LD BYTES border made WHITE then RED
056C LD START if "edge" found, then border CYAN
058F LD SYNC changes border to BLUE/YELLOW
05E3 LD EDGE 2 enterd with border colour in C register
05ED LD SAMPLE border changed when "edge" found

65
11CB START/NEW make border white on start-up

BORDER subroutine 2294 see also colours


Sets PAPER and contrasting INK for the border; the
executive routine of the BORDER command. Not otherwise
called from ROM. Can be called from m/c with the colour on
the calculator stack, but it is simpler to call 229B (OUT
(+FE),A), with the colour code in A instead of on the calculator
stack.
Besides sending the colour to the output port to change
the border on screen, the subroutine makes an attribute byte
and stores it in 5C48 BORDCR, in the usual fbpapink format,
with the border colour in pap, contrasting black or white in
ink, and zeroes in f and b: this is used to colour the lower
screen.
Input parameters: the colour specification is the last
number on the calculator stack; it must be 7 or less.
Action: call 1E94 FIND INT1 to get the colour byte from
the stack
- if it is 8 or more report "Integer out of range"
- output it to port FE
- move it three bits left to the pap position, with zero
to the lo bits; 00000pap becomes 00pap000
- read bit 5, the hi bit of the colour number
- if it is set jump on to BORDER 1; white, yellow, cyan
or green
- (magenta, red, blue or black) XOR the colour byte with
00000111b/07h; this reverses ink from white to black.
_22A6_BORDER_1: put the colour byte in BORDCR.
Exit: RET.
Output parameters: A contains the colour byte.

BORDER 1 22A6 (2294 BORDER)


Jumps from:
2294 BORDER

BOTH NULL 3572 (353B no-l-eql)

66
Jumps from:
356B SECND LOW

brackets see also 28h "(" and 29h ")" at end of alphabet
20C1 IN ITEM 1 handles INPUT followed by brackets

BREAK key (no code) see also KEYBOARD SCANNING


BREAK is "space" with caps shift on the original
Spectrum, but most later models have a separate BREAK key,
though caps shift space still works. Pressing the BREAK key
produces zero signals in port 7FFE and FEFE. They are read
by subroutine 1F54 BREAK KEY, see below.
0525 SA 8 BITS return if pressed
053F SA/LD RET read for last time
056B LD BREAK return if pressed
05E3 LD EDGE 2 zero flag set by
05ED LD SAMPLE read with EAR through port 7FFE
0C88 PO SCR 2 jump to REPORT D if pressed
0EFD COPY L 1 stop motor etc if pressed
133C MAIN 5 continue from next statement after BREAK
1376 MAIN 7 jump after BREAK indicated by NSPPC
1B76 STMT RET tested after each statement
1B7D STMT R 1 continues if no BREAK

'BREAK - CONT repeats' message (144F) see 0552 and 0D00


REPORT D

'BREAK into program' message (14CC) see 1B7B REPORT L

BREAK KEY subroutine 1F54


Returns with NC if BREAK is down. Can be used to break
into m/c programs; often useful for debugging.
BREAK is produced by "space" with caps shift: caps shift
zeroes bit zero of port FEFE, space zeroes bit zero of port
7FFE, so only if both signals are being received does BREAK
count as being pressed.

67
Routines in 05ED LD SAMPLE and 0C88 PO SCR 2 read
port FFFE only and accept either BREAK or SPACE as
effective BREAKs to stop reading of cassette tape or to stop
listing on the "scroll?" message. This is done direct, without
calling BREAK KEY.
Input parameters: none.
Action: read port 7FFE, bit zero; space key
- if it isn't zero return with carry; no break
- read port FEFE, bit zero, caps shift; if BREAK is
being pressed, both bits are zero.
Exit: RET.
Output parameters: A corrupted, others unchanged
- returns with NC signalling BREAK.
Called from:
0EFD COPY L 1
1B76 STMT RET

BREG system variable 5C67; nb equal to STK END hi + 1


1. The "B register" of the calculator, see 0028 FP CALC,
used mainly as a loop counter.
2. Also used as temporary store for the current line flag
when automatic listing, see 1835 LIST ALL; one till the current
line has been printed on screen, then zero.
Bytes: 1
Written by:
1865 OUT LINE 1
335E GEN ENT 1
367A dec-jr-nz
Read by:
0C55 PO SCR
338E ENT TABLE (2 bytes at STK ENDhi, so BREG is hi
byte)
33A2 fp-calc-2
Rems:
335B CALCULATE use of
335E GEN ENT 1 dual use of
3362 GEN ENT 2 entry point when BREG is counter

68
3453 G LOOP left undisturbed

BRIGHT key (DC) see also colours, commands, functions


and operators, KEYBOARD SCANNING, 0246 extended mode
table (c)
The B key in E mode with either shift produces the token
BRIGHT. It can be used either as a BASIC command or as a
print control item within a PRINT etc statement. In either case
it causes any new printing on the screen to appear with
enhanced or reduced brightness; it must be followed by a
parameter, one for "BRIGHT on", zero for "off", eight for "no
change" in the new position on the screen.
As a command, it is read by 1B29 STMT L 1 referring
through the syntax offset table 1A48 to the syntax parameter
table 1A7A. 1AEE P BRIGHT causes a jump to 1C96 CLASS 07
(PERMS), the executive routine for all the colour item
commands INK to OVER.
As a print control item, execution is from within the
PRINT executive routine 1FCD PRINT; each new expression
following the PRINT command is checked by a call to 1FFC PR
ITEM 1 from 1FE5 PRINT 3. If it is BRIGHT, this in turn calls
21F2 CO TEMP 3, from 2024 PR ITEM 3; here the token code
DC is converted to the "embedded" control code 13h, which is
then sent through the output routine followed by the
parameter one or zero.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 13h for BRIGHT into the
control character table at 0A11 produces an indirect jump to
0A7A (0A1E + 5C) PO 1 OPER. See the index description of this
subroutine for the rather tricky way in which it collects the
parameter and sends execution to 0A87 PO CONT, which
finally executes the BRIGHT command.

BRIGHT control code (13h) see also 0382 K 8 & 9 under 0333
K DECODE
The 9 key in E mode without shift produces the print

69
control item "BRIGHT on": PRINT "[E mode 9] hello" has just
the same effect as PRINT BRIGHT 1;"hello", and [E mode 9] in
the BASIC program will cause everything following it to be
listed BRIGHT. Similarly the 8 key in E mode produces
BRIGHT off; and with caps shift, though not with symbol shift,
the 9 and 8 keys produce FLASH on or off respectively. There
is no provision for control items similar to BRIGHT 8 or
FLASH 8.
The keyboard scanning routines initially return codes as
follows:
Key 9 without shift, BRIGHT on: 03
Key 8 without shift, BRIGHT off: 02
Key 9 with caps shift, FLASH on: 01
Key 8 with caps shift, FLASH off: 00
These values are also put in 5C08 LAST K.
However these codes are changed by 10A8 KEY INPUT,
which puts 13h in the A register for BRIGHT or 12h for FLASH,
and 00 or 01 for off or on in the C register. The channel
address is switched so that A and C are returned in series, and
both are put in the BASIC line.
When the BASIC line is printed out by 1855 OUT LINE,
either in a listing or in the lower screen for editing, the
BRIGHT or FLASH command is implemented for the
remainder of the print, but the presence of the codes isn't
otherwise indicated.
It can be detected by cursor moves: the cursor moves over the
two bytes in one jump when going left, but takes two jumps
going right due to a mistake in the ROM, see 100C ED RIGHT.
DELETE must be used twice to delete the control code and its
parameter.
Such BRIGHT and FLASH controls can be incorporated in
variable names, eg to give them prominence in the listing:
they are ignored when reading the variable for evaluation, but
see the minor error described under 28B2 LOOK VARS.
The BRIGHT or FLASH control codes 13h/19d and 12h/18d
can be incorporated in strings:
10 LET a$=CHR$ 18 + CHR$ 1 + "hello!"

70
20 PRINT a$
has just the same effect as
10 PRINT FLASH 1;"hello!"
and similarly with CHR$ 19 for BRIGHT. This is much more
useful when printing from m/c.
007D SKIP OVER only one skip forward
0382 K 8 & 9 token codes become 00-03
0A6D PO TV 2 controls need 1 operand
0A7A PO 1 OPER changes output routine
0A87 PO CONT jump for 1-operand keys
103E ED EDGE 1 not split from parameter by cursor
10A8 KEY INPUT changed to final code
10FA KEY CONTR not handled by
1894 OUT LINE4 error marker is "?" with FLASH
18C1 OUT FLASH sets ATTR T direct for error marker
1909 OUT C 2 prints flashing CEGKL cursor
2211 CO TEMP 5 sorted out from other control characters
2273 CO TEMP C mask prepared for ATTR T
2287 CO TEMP E bit 7/6 of mask set

buffer see printer buffer, 2DE3 PRINT FP

BYTE COMP 3564 (353B no-l-eql)


Jumps from:
3575 SEC PLUS

"Bytes: " message 09EC see cassette messages

BYTE ZERO 327E (3214 truncate)


Jumps from:
auto

71
C

CALCULATE subroutine 335B see also 0028 FP CALC


For the way the subroutine operates, see 0028 FP CALC;
here I shall confine myself to describing how the calculator is
used. This is explained in "ROM Disassembled", at 335B
CALCULATE, but the explanation a) assumes that you
remember how floating-point numbers are formatted in the
Spectrum, and b) makes some vital omissions.
1. FP numbers always consist of five bytes.
1.1 Full form. The algorithm is as follows:
Divide any number by two repeatedly till the result is
less than one but more than or equal to a half. The result m is
the_true_mantissa; the number of divisions is e, the_true
_exponent. If the number is less than a half to start with,
multiply instead of dividing, and count e as negative.
m can have a maximum of 8 hex digits.
Now the five bytes of the FP number in the Spectrum
system are:
Byte 1: the true exponent + 80h; the_exponent_byte
Byte 2: the first byte of the true mantissa; as it stands
for negative numbers, less 80h for positive numbers; the_sign
_byte
Bytes 3-5: the remaining bytes of the mantissa.
Positive powers of 2 (including 1 = 2**0) have zero in
the mantissa bytes:
2**X = 2**(X + 1) * 0.8h
= FP 8(X+1) 00 00 00 00
See the Appendix to this index for BASIC programs to
calculate FP number forms.
The FP number may be doubled or halved simply by
incrementing or decrementing the exponent byte; this trick is
used several times in the ROM.
Reference is occasionally made in the notes to the

72
_true_exponent and_true_mantissa: the true exponent means
the exponent without the deduction of 80h, and the true
mantissa the mantissa without the zeroing of the sign bit for
positive numbers. These forms are never stored, only created
for use in particular calculations.
Forms are similarly sometimes prepared in which the
mantissa isn't within the range half -> one; shifting the
mantissa bits to the right and incrementing the exponent, or
to the left and decrementing the exponent, providing there
are leading zeroes, doesn't change the number. See the
examples below; 73d = 2**8 times 0.49h or 2**7 times 0.92h.
The binary expression of these two mantissas is
49h = 01001001b
92h = 10010010b
Correction of such shifted forms, ie shifting them back
so that the mantissa comes within the normal range, is
referred to as_normalization: this term usually includes the
addition of 80h to the true exponent and marking the hi bit of
the mantissa as a sign bit.
Apart from their use in the calculator, remember that
every number in a BASIC program is followed by 0E and then
the five-byte FP form of the number in the representation of
the line in the program area; see under number marker.
1.2 Small integer FP format. There is an alternative
form for whole numbers less than 10000h/65536d:
byte 1: 00
byte 2: 00 for positive, FFh for negative
byte 3: low byte
byte 4: high byte
byte 5: immaterial, usually zero
Negative numbers must be subtracted from 10000h
before bytes 3 and 4 are calculated in this format.
NB 1.2.1. If the first byte is zero, the number must be in
small integer format - in_full_FP_format this would be a
number less than 2 to the power -80h/128d, which isn't
allowed.
NB 1.2.2. The second byte is the_sign_byte - its bit 7, the

73
_sign_bit, is 1 for negative numbers, 0 for positive, in either
format.
NB 1.2.3. Some 5-byte numbers aren't in FP format - the
_string_parameters of a variable or print string - although they
are handled in many similar ways. They too may have 00 in
the first byte. See under strings for a further discussion of
these.
NB 1.2.4. There is a third format, a_compressed form,
which is used in the stk-data and stk-zero routines, and is
usually called the_stk-data_form in this index; it is derived
from the expanded format, but takes up less room in the
ROM, from 2 to maximum 5 bytes. See under stk-data.

_Examples:
73d = 49h
= 2**8 * 0.49h
= 2**7 * 0.92h
full FP format: 87 12 00 00 00
small integer format: 00 00 49 00 00

500d = 1F4h
= 2**8 * 1.F4
= 2**9 * 0.FA
full FP format: 89 7A 00 00 00
small integer format: 00 00 F4 01 00

pi = 3.1415927d
= 2**2 * 0.C90FDAA2h
full FP format: 82 49 0F DA A2

e**pi = 23.140693d
= 2**5 * 0.B92023A8h
full FP format: 85 39 20 23 A8

-41764d = -A324h = 10000h - 5CDCh


= -2**10h * 0.A324h
full FP format: 90 A3 24 00 00
small integer format: 00 FF DC 5C 00

2. The calculator is used by calling restart 0028 FP CALC,

74
following the RST 28h command with at least one_literal,
single bytes from the list at 32D7 which signal the calculations
to be performed. The last literal must be 38h end-calc. The
literals are sometimes called_calculator_offsets in the notes.
Subroutines which can be called by literals following FP
CALC are shown in this index with their names in lower case.
Most of them can also be called direct from m/c, and some are
so called by the ROM.
Any operands for the calculations must first be placed
on the_calculator_stack, see below, and the result of the
calculator operations is left as the_last_value on the stack.
There is also a special_memory for the calculator, see below.
The calculator stack and memory are in general
undisturbed by the other operations of the ROM, and RST 28h
can be used repeatedly, perhaps with manipulations of the
stack or memory values by other m/c routines, without
breaks in the continuity of the calculation.
There are literals for unconditional and conditional
jumps over the literal string, and a looping literal 35 dec-jr-
nz which works much like DJNZ, using the system variable
5C67 BREG as DJNZ uses the B register.
An alternative way of using the calculator is by literal
3B fp-calc-2; see the index entry for it, and 274C S STK LIST
under 24FB SCANNING for the only ROM example. This
performs the calculation signalled by whatever literal it finds
in the B register on entry to the restart 28h. The advantage is
that a sequence of calculations can be built up by the m/c
program itself on the machine stack, rather than being preset
by the programmer.
NB 2.1. Division by zero will produce an error report.
Division by zero can be avoided by use of literal 30_not: for
examples, see 238D DR 3 PRMS under 2382 DRAW and 384A
sqr.
NB 2.2. One of the fanciest examples of calculation in the
ROM is the sequence from 2382 DRAW to 2477 LINE DRAW. If
you can figure this out you will have mastered the FP
calculator! The use of the series generator to calculate

75
Chebyshev polynomials is perhaps even fancier, see the index
entry, but isn't likely to be imitated by most m/c
programmers.
3. The calculator stack. All the calculations of the FP
calculator operate on the top one or two numbers on the
calculator stack, which extends upwards from the address in
5C63 STKBOT to the address in 5C65 STKEND. Unlike the
machine stack, the calculator stack is "the right way up": the
number on the top of the stack is actually higher in memory
than the number below it.
Each number takes up 5 bytes, and the size of the stack
is limited by the overall memory; if a calculation loop leaves
numbers on the stack, it should be cleared either by the literal
02 delete, or by calling 16C5 SET STK, to avoid piling up the
stack, which can lead to memory overflow. Otherwise the
stack is generally undisturbed, and you can make repeated
use of RST 28h in the confidence that the numbers you need
will still be on the stack.
Reference is often made in the notes to the_last_value
on the calculator stack, ie the last value to be placed there;
it is the first to come off. It is also sometimes called the_top
or_topmost_number, or the_first_number.
As a rule, calculator operations involving two operands
operate_with the last value_on the second last; thus in
subtraction X - Y or division X/Y, Y is the last value. The
notes often introduce hideous confusion by calling Y in such
operations the_second_operand, so that the second operand is
the last value and the first operand is the second value on the
stack. In the case of addition and multiplication X and Y are
interchangeable, but even here, inexcusably, the notes persist
in using these baffling expressions.
The last value is a 5-byte number, usually in full FP
format; on exit from the calculator routines its first byte
address is held in HL, and the address following its fifth byte
in DE. It may represent the parameters of a string; if so, bit 6
of FLAGS is set. If this flag is zero, the last value is read as
representing a number.

76
Numbers can be put on the stack in FP form by:
3.1. calling 2D28 STACK A, which puts the byte in A on the
top of the stack as a positive integer;
3.2. calling 2D2B STACK BC, which puts the two-byte positive
integer in BC on top of the stack;
3.3. calling 33B4 STACK NUM, which copies a FP number
constructed elsewhere in RAM to the last value position on
the stack;
3.4. calling 2AB6 STK STORE, which puts on the stack a
number
whose bytes are held
byte 1 in A
byte 2 in E
byte 3 in D
byte 4 in C
byte 5 in B
If byte 1 is to be zero, you can call 2AB1 STK ST 0 instead.
3.5. using literal 34 stk-data; it requires reformatting of
the FP number; see 33C6 stk-data
3.6. using literals A0 to A4, stk-zero, stk-one, stk- half,
stk-pi/2, stk-ten, which each put one of the five standard
constants on the stack; you can also make your own list of
standard constants and use the ROM routines to index into
your list and put any of them on the stack, see 341B stk-zero.
Numbers may be got from the stack after the CALCULATE
routine has terminated by:
3.7. reading the 5 bytes of the FP number from the stack:
after literal 38h end, the HL register holds the address of the
first byte of the last value
3.8. calling 2DD5 FP TO A or 2DA2 FP TO BC: the integer
value of the last FP number on the stack will be returned in
the A register, for single-byte integers, or BC register
respectively.
These routines don't produce error reports, so they won't
crash your system, but both return with C if the number is too
big and with NZ if it is negative, so you can trap errors for
yourself

77
3.8.1 calling 1E94 FIND INT1 and 1E99 FIND INT2 instead;
but if the value isn't a positive number within the capacity of
the register, these subroutines will crash your system and
print "Integer out of range"
3.9. calling 2307 STK TO BC, which puts the top_two values
on the stack in the BC register, top number to B. They must be
positive integers less than 256d. This is useful for drawing on
the screen; eg it can be followed directly by 22DC PLOT, which
plots a point at y=B, x=C
3.10. calling 2BF1 STK FETCH, which takes the last value from
the stack and puts it in the AEDCB registers, cf 3.4 above
3.11. calling 2DE3 PRINT FP, which prints the top number of
the stack on the screen; you can print any integer by PUSH
BC/CALL STK BC/CALL PRINT FP, or the result of using
CALCULATE by CALL PRINT FP immediately.
3.12. You can also print an integer directly from BC by
calling 1A1B OUT NUM 1; or an integer at the address held in
HL_in_reversed_form by calling 1A28 OUT NUM 2. OUT NUM 1
prints the number without leading spaces, OUT NUM 2 with
them - ie the integer always fills four spaces. These routines
are designed tobe used for line numbers, and don't work if the
integer is bigger than 270Fh/9999d, but it isn't too hard to
write your own "Chinese copy" of them with an extra couple
of lines in 1A30 OUT NUM 3 to take care of one more digit.
Other routines are used by the ROM, but these are the
most useful for machine code programming.
Most of the calculations delete the numbers that they
use, or replace them with others; you can save the number at
the top of the stack by duplicating it with literal 31 duplicate,
or by putting it in the calculator memory.
For a simple example of the use of the stack, see 34AC
peek.
4. The calculator memory consists of thirty bytes starting at
the address in 5C68 MEM; this is usually 5C92 MEMBOT,
though occasionally it is relocated to an ad hoc position to
avoid disturbing the MEMBOT values.
The thirty bytes can hold six FP numbers in locations

78
referred to as_mem-0,_mem-_1,_..._mem-5; the literals C0 to C5
st-mem-0/5 copy a number from the top of the stack to the
corresponding memory location, without deleting it from the
stack, literals E0 to E5 get-mem-0/5 copy a number from the
memory location to the top of the stack, without deleting it
from the memory, or deleting the number which was on top
before, and which goes to second place. If the memory is
relocated, more than six numbers can be held, see under
342D st-mem.
The location of any number in the memory can be found
by a call to 3406 LOC MEM.
"ROM Disassembled" fails to warn you that several of the
calculation routines use the memory, and therefore will
corrupt anything you have put in the locations used:
- all memory locations are corrupted by the BASIC CIRCLE
and DRAW routines, and all except mem-0 by 2DE3 PRINT FP;
but otherwise mem-4 and mem-5 are safe
- mem-0 to 3 are all corrupted by 06 to-power, 26 exp, 28
sqr
- mem-0 to 2 are all corrupted by 1F sin, 20 cos, 21 tan,
22 asn, 23 acs, 24 atn, 25 ln, 86/88/8C series-06/08/0C
- mem-0 and mem-1 are both corrupted by 3C e-to-fp
- mem-0 alone is corrupted by 27 int or 39 get-argt, and
also by syntax checking of numbers in BASIC
- mem-0, mem-1 and mem-2 literals are also used by
FOR...NEXT loops. In this case mem-0 is corrupted, but the
other two are used only after the memory has been relocated,
so the normal locations are undisturbed.
Introduction routines for all functions
0028 FP CALC entry to calculator
0053 ERROR 2 clears calculator stack
03F8 BEEP converts duration/pitch to timer/cycles
0427 BE OCTAVE converts last value to frequency
06C3 SA CODE zero start placed on stack
06F0 SA CODE 2 zero length placed on stack
0723 SA LINE 1 line number passed to stack
11A7 REMOVE FP removes FP numbers from BASIC line

79
1313 MAIN G calculator stack cleared
169E RESERVE makes room below calculator stack
16BF SET WORK work space and calculator stack cleared
16C5 SET STK only stack cleared, MEM set to MEMBOT
171E STR DATA stream number taken from calculator
stack
18B6 NUMBER moves past FP representation in BASIC
line
19FB E LINE NO stack temporarily relocated in memory
1A15 E L 1 stack replaced
1C30 VAR A 2 variable values put on stack
1C79 NEXT 2NUM single expression result placed on stack
1CE6 USE ZERO zero placed on stack
1CF0 IF last value is logical value of IF statement
1D03 FOR stack already holds VALUE and LIMIT
1D10 F USE 1 one placed on stack
1D16 F REORDER memory relocated in control variable
1DAB NEXT loop variables read from relocated memory
1E7A OUT parameters fetched from stack
1E85 TWO PARAM get two numbers from stack
1E94 FIND INT1 last value put in A register
1E99 FIND INT2 last value put in BC register
1FC3 UNSTACK Z avoids loading stack in syntax check
1FFC PR ITEM 1 parameters of AT put in BC via stack
200E PR ITEM 2 parameter of TAB put in BC via stack
2070 STR ALTER stream number put on calculator stack
2148 IN VAR 2 removes FP forms from BASIC line
21FC CO TEMP 4 control parameter fetched via stack
22D4 POINT LP pixel bit put on stack
22DC PLOT pixel coordinates fetched from stack
2307 STK TO BC gets two values from stack to B & C
2314 STK TO A gets value from stack to A
2320 CIRCLE radius obtained from stack
233B C R GRE 1 x 4 in four bytes by rewriting exponent
235A C ARC GE1 manipulates exponent of number in
memory
23A3 DR SIN NZ exponent read from (HL)

80
23C1 DR PRMS values loaded to stack then to memory
2420 DRW STEPS values in stack and memory
2425 ARC LOOP coordinates and steps remain on stack
247D CD PRMS1 on entry, last three values are X,Y,Z;
six parameters stored in memory
2497 DRAW SAVE arc count saved while memory filled
24B7 DRAW LINE line increments are on top of stack
24FB SCANNING value of expression is left as last value
2522 S 2 COORD puts 2 coordinates on stack
25F8 S RND calculates RND from SEED
2627 S PI pi/2 doubled by manipulating exponent
268D S BIN FP form put in BASIC line with syntax check
26B6 S SP SKIP FP number read from BASIC line
26C9 S LETTER 5-byte number or string parameters
stacked
2713 S CONT 3 discriminates string or numeric value
2734 S LOOP result so far is last value on stack
274C S STK LST converts op code to literal
275B S SYNTEST status of last value must match op code
2764 S RUNTEST status of last value recorded in flag
27D9 SF ARGMTS arguments of FN filled with FP numbers
2831 SF VALUES arguments of DEF FN filled with FP nos
and string parameters
2852 SF ARG VL indirect deletion of last value
296B SFA CP V6 skip over FP numbers in DEF FN
statement
29A1 SV SIMPLE$ puts string parameters on stack
2A22 SV NUMBER five bytes per number in numeric array
2A2C SV ELEM$ string parameters put on stack
2A49 SV SLICE? string parameters are on stack
2A52 SLICING string parameters on stack changed
2AB1 STK ST 0 puts FP no held in AEDCB on stack
2AB6 STK STORE 5-byte number copied above stack
2ACD INT EXP2 expression fetched to BC via stack
2B59 L NUMERIC can still read FP number after deletion
2B72 L DELETE$ string parameters fetched from stack
2BA6 L ENTER used to copy from stack to variables area

81
2BF1 STK FETCH gets last value from stack to AEDCB
2C9B DEC TO FP change decimal/BIN/E mode no to FP
format
2CB8 NOT BIN integer part of number made last val
2CDA NXT DGT 1 mem-0 repeatedly divided by 10d to give
digits added to last value one by one
2D18 E FP JUMP x times 10 to power m made last value
2D22 STK DIGIT puts decimal character code on stack
2D28 STACK A stacks integer value in A
2D2B STACK BC stacks integer value in BC
2D3B INT TO FP converts BASIC integer to FP on stack
2D40 NXT DGT 2 makes last value digit + 10*last digit
2D4F E TO FP puts xEm on calculator stack
2D55 E SAVE sign flag put in calculator memory
2D60 E LOOP stack holds interim values
2D7B E END delete looping value after exit from loop
2D7F INT FETCH collects small integer from stack
2D8C P INT STO puts positive no in small integer format
2D8E INT STORE puts number in small integer format
2DA2 FP TO BC puts last value in BC
2DAD FP DELETE can still read last value after deletion
2DC1 LOG (2**A) used to estimate print length in BASIC
2DE3 PRINT FP prints out last value
2DF8 PF POSTVE save offset in HL' while number printed
2E01 PF LOOP checks for small integer
2E24 PF SMALL mistake; stack not cleared
2E56 PF LARGE forms true exponent
2E6F PF MEDIUM true mantissa and true exponent
2E7B PF BITS mantissa rotated bitwise into print buffer
2ECB PF MORE last value replaced by memory value
2ECF PF FRACTN zero exponent with shift of mantissa
2F2D PF COUNT restores offset to HL'
2F9B PREP ADD prepares FP number for addition
2FAF NEG BYTE restores exponent and original exponent
2FBA FETCH TWO numbers from stack into H'B'C'CB and
L'D'E'DE
2FDD SHIFT FP shifts mantissa R or L for addition

82
3004 ADD BACK manipulating exponent or mantissa may
cause overflow
3014 addition adds two last values on stack
303C ADDN OFLW mistake; can stack incorrect result
303E FULL ADDN small integers re-stacked in full FP
form and added (sum not a small integer)
3055 SHIFT LEN shift mantissa right for addition
307C TEST NEG negate negative nos and correct sign byte
30C0 PREP M/D make true mantissa for mult/division
30CA multiply try short multiplication first
30EA MULT RSLT result put on stack
30EF MULT OFLW overflow; restore stack pointer
30F0 MULT LONG numbers restacked in full FP format
and HL' saved
3125 START MULT add exponents
313B MAKE EXPT prepare to convert true to FP exponent
313D DIVN EXP exp incremented by 80h & checked - 3
bytes
3146 OFLW1 CLR exponent checked for overflow
3151 OFLW2 CLR exponent saved
3159 NEAR ZERO exponent tested for zero
315E SKIP ZERO normalise exponent
316C NORMALISE normalise mantissa
316E SHIFT ONE shift mantissa L, decrement exponent
3186 NORML NOW mantissa goes past 0.5d; increment
expt
3195 OFLOW CLR result copied to stack from 5 registers
31AF division mantissas divided by subtraction
31FA COUNT ONE mistake; 34th bit precision missed
3214 truncate check exponent for short-cut results
3221 T GR ZERO mistake; can't handle -65536
323F T SMALL test for small integer format
3267 T STORE result put on stack
326C T EXPNENT check for very large number
326D X LARGE exponent over A9 means no fractional
part
3272 NIL BYTES zeroes mantissa bits for integer result

83
3293 RE ST TWO changes two small integers to full format
3297 re-stack changes small integer to full format
32B2 RSTK LOOP shift mantissa L (nb misprint),
decrement exponent
32BD RS STORE stack new exponent
32D7 look-up table of literals
33A1 delete changes meaning of pointers, not stack
33A2 fp-calc-2 jumps to subroutine indexed by B register
33A9 TEST 5 SP tests if room for another value on stack
33B4 STACK NUM copies FP number on stack from
memory
33C0 duplicate copies FP number on stack from memory
33C6 stk-data FP number in 2-5 literals must follow
literals 34 stk-data or 86/88/8C series;
A0-A4 stk-zero etc supply these from table
33C8 STK CONST saves next literal in H'L'
33DE FORM EXP counts literals supplied, adds zero bytes
3406 LOC MEM finds memory location serial A
340F get-mem five bytes moved to stack from memory
341B stk-zero puts constant from table on stack
342D st-mem five bytes moved from stack to memory
343C exchange two last values exchanged
3449 series-06 calculates Chebyshev polynomials
3453 G LOOP uses BREG as loop counter
346A abs zeroes sign bit
346E negate manipulates sign bit/byte
3474 NEG TEST manipulates sign byte of full FP form
3483 INT CASE manipulates sign byte for small integer
3492 sgn replaces X with sgn X
34A5 in last value changed from port number to input
34AC peek last value replaced by PEEK
34B3 usr jumps to usr value with return to STACK BC
34E9 TEST ZERO checks for zero FP number
34F9 greater-0 last value is returned as 1 or zero
3501 not last value zero -> one, other -> zero
3506 less-0 last value is returned as 1 or zero
3507 SIGN TO C carry made marker of sign byte

84
350B FP 0/1 last value is set to 1 or zero by carry flag
3524 no-&-no last value is X or zero
352D str-&-no returns with string parameters as last val
353B no-l-eql value in B discriminates 12 variants
354E NU OR STR discriminates string from numeric ops
3559 STRINGS get string parameters from stack
3588 STR TEST zero stacked if strings tested
359C strs-add string parameters put on stack
35BF STK PNTERS points HL and DE at last value
35C9 chr$ string parameters put on stack
35DE val result put on stack as number (val) or string
parameters (val$)
361F str$ resulting string parameters put on stack
3645 read-in resulting string parameters put on stack
3669 code result put on stack
3671 STK CODE last value given by STACK A
3674 len returns string length as last value
367A dec-jr-nz works like djnz with loop count in BREG
3686 jump makes unconditional jump
3687 JUMP 2 length of jump is in following literal
368F jump-true jumps if last value non-zero
369B end-calc jumps to address in H'L'
36A0 n-mod-m values from stack replaced by quotient
and remainder
36AF int last value replaced by its integer part
36C4 exp calculates Chebyshev polynomials
3705 N NEGTV exponent adjusted for negative result
370C RESULT OK result left as last value
370E RSLT ZERO last value made zero
3713 ln calculates Chebyshev polynomials
371C VALID manipulates exponent byte
373D GRE.8 last value is determined function of result
3783 get-argt transforms radians into fractions of pi/2
37AA cos result is last value
37B5 sin calculates Chebyshev polynomials
37B7 C ENT common to cos and sin
37DA tan result is last value

85
37E2 atn calculates Chebyshev polynomials
3833 asn result is last value
3843 acs result is last value
386C LAST return with last value zero or 1

calculation of random numbers see 2F58 S RND

calculator memory, calculator offset, calculator stack see


335B CALCULATE

CALL JUMP subroutine 162C


Consists of a single JP (HL) command. Can be used
anywhere in m/c as equivalent to a "computed GO SUB"
command:
compute HL, then call 162C CALL JUMP.
Called from:
15F7 CALL SUB
Exit from:
1615 CHAN FLAG

CALL SUB 15F7 (0010 PRINT A 1)


Exit from:
15E6 INPUT AD
15F2 PRINT A 2 (0010 PRINT A 1)

capital letters see character codes

CAPS LOCK key (06) see also FLAGS2 bit 3, KEYBOARD


SCANNING,
0260 control code table (d)
The 2 key with caps shift effects CAPS LOCK; bit 3 of
FLAGS2 is flopped. When it is on, any keyboard input which
would have been in L mode will be in C mode, ie all letters are
upper case, but digit and token keys aren't affected.
There is a separate key for CAPS LOCK on most later
models of the Spectrum, but caps shift 2 still works. There is
no SYMBOL LOCK on any model.

86
034F K KLC LET if set return with "main code"
10A8 KEY INPUT handled in this subroutine
10DB MEY M&CL flag (bit 3 of FLAGS2) flopped

CAPS SHIFT key see KEYBOARD SCANNING

C ARC GE1 235A (2320 CIRCLE)


Jumps from:
233B C R GRE 1

carriage return key see ENTER

carry from calculations see precision

CASES 37FA (37E2 atn)


Exit from:
37E2 atn
37F8 SMALL

CASSETTE HANDLING ROUTINES see 04C2 SA BYTES

cassette messages 09A1


The messages "Program: ", "Bytes: ", etc which are
printed on screen during a LOAD, and "Start tape, then press
any key." which prompts a SAVE.
Read by:
078A LD TYPE
0970 SA CONTRL

CAT key (CF) see also commands, functions and operators,


KEYBOARD SCANNING, 0284 extended mode key table (f )
The 9 key in E mode with symbol shift produces the
command CAT. As implemented on the standard Spectrum,
the command accepts no parameters, though most
peripherals that give it some purpose in life require at least a
numeric expression.

87
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1B14 P CAT causes a jump via 1C10 CLASS 00 and 1C16 JUMP C
R to the executive routine 1793 CAT ETC, which however
produces no action with the standard Spectrum.

CAT ETC subroutine 1793


The executive routine of the FORMAT, MOVE, ERASE and
CAT commands; not called otherwise from ROM. Merely gives
the report "Invalid stream", unless Interface I or some
analogous peripheral is connected.

CA=10*A+C subroutine 2F8B


Multiply A by 10 and add C. Used in 2DE3 PRINT FP to
convert a 4-byte hex fraction into decimal digits.
If the fraction is less than one, say 1/p, multiply it
by 10; now if 10/p is still less than one, the first digit of
the decimal fraction is zero. If 10/p is more than one, it will
still be less than ten, and its integer part is the first digit
of the decimal fraction. Repeat the process with the fractional
part for the next digit.
The fraction is in D'E'DE. This subroutine is called for
each of the bytes in turn, starting with the lowest, with the C
register initially zero. Any "carry" from the multiplication is
returned in C ready to be added to the next highest byte. Eg if
the bytes were
_hex |_decimals
26 0D FE 62 | 038 013 254 098
the results would be
62 * 0A + 00 = 03D4| 98 * 10 + 00 = 980
= D4 lo and 03 hi| = 212 lo and 003 hi
FE * 0A + 03 = 09EF| 254 * 10 + 03 = 2543
= EF lo and 09 hi| = 239 lo and 009 hi
0D * 0A + 09 = 008B| 13 * 10 + 09 = 139
= 8B lo and 00 hi| = 139 lo and 000 hi
26 * 0A + 00 = 017C| 38 * 10 + 00 = 380
= 7C lo and 01 hi| = 124 lo and 001 hi
leaving
7C 8B EF D4 | 124 139 239 212

88
as the fraction - which expressed as a binary string is ten
times the original - and bringing out a carry of 01 which is
printed as the next digit of the decimal representation.
Input parameters: A and C each contain two hex digits,
that in C being the "carry" from a previous operation.
Action: put A in L with zero H
- multiply HL by 10 in six bytes; clever! LD DE,10d/CALL
HL=HL*DE is also six bytes, but much slower
- add C
- put H in C and L in A.
Exit: RET.
Output parameters: HL equals the result CA
- B and DE unchanged.
Called from:
2EEF PF FR EXX (twice)

CD PRMS1 subroutine 247D


Sets the_initial_parameters_for_CIRCLE_and_DRAW; but
not used for DRAWing straight lines.
The subroutine starts from the input values
Z = the radius (CIRCLE)
or approximate diameter (DRAW) of the curve
G = the angle in radians through which the curve will
turn; in the case of CIRCLE, G = 2pi. If you aren't sure about
radians, see under 2783 get-argt.
Other values are on the calculator stack and in the
calculator memory, but they aren't used.
The parameters required are the_arc_count a, the_number
of short straight lines which
must be drawn to approximate the required curve; these are
often called_"arcs", in quotes
various functions of G and a: G/a, SIN (G/2a), COS (G/
a), SIN (G/a). G/a is of course the angle in radians through
which each "arc" will turn; SIN (G/a) is a close approximation
to the length in pixels of each arc.
The first approximation to the arc count is got by

89
calculating half the absolute value of G times the square root
of Z:
[ABS (G * SQR Z)]/2
which gives a count producing a length of about twice the
square root of Z for each "arc"; say 18d pixels length for a
circle which nearly fills the screen. The "arcs" used in DRAW
are shorter, because Z is the radius of the curve, not its
diameter, but the difference isn't very great.
The modified value taken for the arc count is the
nearest integer multiple of 4 above this; but if the modified
value comes out at more than 252d, it is given 252d as a
maximum value. This can just happen, given a long line and a
large angle. The difference made by these corrections of the
arc count aren't perceptible on screen.
Input parameters: Z is last value on the stack, G is held
in mem-5.
Action: use the calculator to figure in succession
SQR Z,
(SQR Z)/2,
(G * SQR Z)/2, and
a = ABS [(G * SQR Z)/2]
- call 2DD5 FP TO A take the approximate arc count off
the calculator stack into the A register
- if this makes a carry jump on to USE 252; a > 255d
- AND the arc count with 11111100b/FCh; giving a
multiple of four
- add four
- if there is no carry jump on to DRAW SAVE; a is still
less than 256d.
_2495_USE_252 (a is more than 252d): make it FCh/252d;
the maximum value.
_2497_DRAW_SAVE (the label is omitted by a misprint):
call 2D2B STACK A to put the arc count on the calculator stack
- use the calculator to figure and load into the
calculator memory
SIN (G/a)
SIN (G/2a)

90
G/a
SQR[1 - SIN**2(G/a)] = COS (G/a).
Exit: RET, from 2497 DRAW SAVE.
Output parameters: the B register holds the arc count a
- mem-0 holds G/a
- mem-1 holds SIN (G/2a)
- mem-2 holds zero, unchanged from input
- mem-3 holds COS (G/a)
- mem-4 holds SIN (G/a)
- mem-5 holds G, unchanged from input
- the calculator stack is unchanged from input.
Called from:
233B C R GRE 1
23C1 DR PRMS
Rems:
2320 CIRCLE initial parameters set by
235A C ARC GE1 arc count recovered from stack
2382 DRAW initial parameters set by
2439 ARC START draws single arc
245F ARC END uses coordinates of last arc drawn
2477 LINE DRAW draws last arc

C ENT 37B7 (37B5 sin)


Exit from:
37AA cos
37B5 sin

CH ADD system variable 5C5D


Bytes: 2
The BASIC pointer: the address, usually in the program
area, currently reached by the BASIC interpreter. It is one of
the "fourteen pointers" which must be adjusted each time
space is made or reclaimed in the ROM, see 1664 POINTERS.
Written by:
0078 TEMP PTR2
0090 SKIPS
12CF MAIN 3

91
166B PTR NEXT
198B EACH STMT
199A EACH S 3
19FB E LINE NO
1BD1 NEXT LINE
2174 IN VAR 5
21B9 IN ASSIGN
26B6 S SD SKIP
2831 SF VALUES
288D SF VALUE (twice)
29E0 SV CH ADD
35DE val
360C V RPORT C (twice)
Read by:
0008 ERROR 1
0018 GET CHAR
0074 CH ADD+1
155D MAIN ADD
166B PTR NEXT
1D34 F L&S
2129 IN PR 3
35DE val
Rems (this list omits references to the note "Advance CH
ADD" which is written against almost every call to NEXT
CHAR):
0020 NEXT CHAR incremented
007D SKIP OVER amended
1B17 LINE SCAN skipped over line number
1BD1 NEXT LINE as usual, points to character considered
1DEC READ 3 used as pointer to DATA list
1DED READ saved in X PTR
1E0A READ 1 set; fetched and stored in DATADD; set again
268D S DECIMAL incremented by 0077 TEMP PTR1
2831 SF VALUES steps through FN statement
28AB FN SKIPOVER left undisturbed
28B2 LOOK VARS points to first letter of variable
294B V END points to next after name of variable

92
CH ADD+1 subroutine 0074
Increment system variable 5C5D CH ADD and read its
new
value. The subroutine is also entered at 0077 TEMP PTR1,
when HL
already holds the value of CH ADD, and at 0078 TEMP PTR2
when it
has already been incremented.
Input parameters: none.
Action: get the address from CH ADD.
_0077_TEMP_PTR1: increment it.
_0078_TEMP_PTR2: put the incremented address in CH
ADD
- read the byte at that address.
Exit: RET from TEMP PTR2.
Output parameters: HL holds the new BASIC pointer
- A holds the byte at the pointer.
Called from:
0020 NEXT CHAR
1E0A READ 1 (as 0077 TEMP PTR 1 and as 0078 TEMP
PTR 2)
250F S QUOTE S (twice)
268D S BIN (as 0077 TEMP PTR 1)
2D40 NXT DGT 2
Rems:
2D3B INT TO FP repeated calls fetch digits of integer

CHAN FLAG subroutine 1615 see also 1601 CHAN OPEN,


channels and streams.
Sets flags according to the channel selected, and sets
5C8F ATTR T as appropriate; for channel K, to print in the
lower screen, it must use the BORDER colour and contrasting
ink, for channel S, to print in the main screen, it must use the
ink and paper colours from 5C8D ATTR P.
Can be regarded merely as an alternative entry point to

93
1601 CHAN OPEN, called when the channel address is already
known: so used in the ROM after a second channel has been
opened temporarily, the address of the first one being saved
on the stack.
If the selected channel isn't K, S or P, the only effect
is to reset bit 4 of FLAGS2; "not using channel K". This applies
to channel R.
Input parameters: HL holds a pointer to the channel
information area, marking the base address of the selected
channel.
Action: put the address in 5C51 CURCHL
- zero bit 4 of FLAGS2; "not using channel K"
- move the pointer on four places; now it is on the
channel code letter
- get the letter
- call 16DC INDEXER to index into the channel code look-
up table at 162D; it collects an offset
- if INDEXER returns with no carry, return; the channel
code isn't K, S or P
- add the offset to its address in the table to get a
jump address CHAN K, CHAN S or CHAN P.
_162C_CALL_JUMP: jump to the indexed address.
_1634_CHAN_K: set TV FLAG bit 0; "using lower screen"
- zero FLAGS bit 5; "ready for a key"
- set FLAGS2 bit 4; "using channel K"
- jump on to CHAN S 1.
_1642_CHAN_S: zero TV FLAG bit 0; "using main screen"
_1646_CHAN_S_1: zero FLAGS bit 1; "printer not in use"
- exit to 0D4D TEMPS.
_164D_CHAN_P: set FLAGS bit 1; "printer in use"
- return.
Exit: RET from CHAN P in the case of P or any other than
K or S
- into 0D4D TEMPS, setting 5C8F ATTR T, in the case of
channels K and S.
Output parameters: none
- BC is undisturbed

94
- so is A in the case of channel P.
Called from:
0FA9 ED EDIT
361F str$
365F R I STORE
Exit from:
1610 CHAN OP 1 (1601 CHAN OPEN)

CHAN K 1634 (1615 CHAN FLAG)


Jumps from:
162D channel code look-up table

channel address, channel area, channel code see channels


and streams

channel code look-up table see tables

channel data see channels and streams

channel flags see 1615 CHAN FLAG

channel information, channel information area see


channels and streams

channel K
One of the four standard channels whose data are in the
initial channel information table at 15AF, copied to the
channel area on start-up in 1219 RAM SET.
Takes input from the keyboard (10AB KEY INPUT) and
outputs to the screen (09F4 PRINT OUT); TV FLAG bit zero is
set by 1634 CHAN K, so this means the lower screen.
It is indexed by three of the "reserved" streams, FDh/
minus 3, zero and 01; the minus stream cannot be used from
BASIC, the other two can, but stream zero is set again for
channel K every time a report is printed at 1313 MAIN G.
Opened with stream FD:
0970 SA CONTRL opened to print message

95
0C88 PO SCR 2 opened to print "scroll?"
0D94 CL CHAN opened after CLS
Opened with stream zero:
0C55 PO SCR opened to test for scroll
106E ED LIST opened after listing
12AC MAIN 2 opened for EDITOR
Opened with stream 01:
2089 INPUT opened for INPUT
Rems:
107F ED ERROR skipped if not open
1615 CHAN FLAG flag reset for all but K
1634 CHAN K flag set
1701 CLOSE 2 can be CLOSEd
1736 OPEN channel code must be K, S or P
213A IN VAR 1 if using, change error pointer
2161 IN VAR 4 jump if not using
21D6 IN CHAN K sets Z flag if open

CHANNEL K FLAG SUBROUTINE see 1634 CHAN K in 1615


CHAN FLAG

channel P
One of the four standard channels whose data are in the
initial channel information table at 15AF, copied to the
channel area on start-up by 1219 RAM SET. The channel for
the ZX printer.
The input routine is 15C4 REPORT J, ie no input. Output
to 09F4 PRINT OUT; FLAGS bit 1 is set by 164D CHAN P, so
output goes to the ZX printer.
It is indexed by the "reserved" stream 03.
Opened by the ROM only at 1FC9 LPRINT, but can be
opened from BASIC or m/c with stream 3, or indexed with
streams 4 -> 15d.

CHANNEL P FLAG SUBROUTINE see 164D CHAN P in 1615


CHAN FLAG

96
channel R
One of the four standard channels whose data are in the
initial channel information table at 15AF, copied to the
channel area on start-up by 1219 RAM SET. The channel for
output to the editing area or work space.
The input routine is 15C4 REPORT J, ie no input. Output
is to 0F81 ADD CHAR, which adds the output character to the
address in 5C5B K CUR, the cursor position either in the line
being edited in the editing area or the input line in the work
space; as it doesn't output to screen or printer, the usual flag
and colour settings aren't needed, and it isn't handled by 1615
CHAN FLAG.
It is indexed by the "reserved" stream FFh/minus one
Opened by the ROM at 0FA9 ED EDIT to copy the edit line
from the BASIC program to the editing area and at 361F str$ to
copy a string expression to the work space for evaluation. It
isn't used for the actual output of keystrokes, which is
handled both for line editing and input of expressions by
direct calls to ADD CHAR from 0F38 ED LOOP.
Cannot be opened from BASIC; could be used from m/c.

channel S
One of the four standard channels whose data are in the
initial channel information table at 15AF, copied to the
channel area on start-up by 1219 RAM SET.
The input routine is 15C4 REPORT J, ie no input; output
is to the screen (09F4 PRINT OUT). TV FLAG bit zero is
cleared by 1642 CHAN S, so this means the main screen.
It is indexed by two of the "reserved" streams, FEh/
minus 2 and 02; the minus stream cannot be used from
BASIC, but the 02 stream can.
Opened with stream FE:
0767 LOOK H opened for load messages
0C88 PO SCR 2 opened after "scroll?"
0DAF CL ALL opened to clear screen
Opened with stream 02:
1FCD PRINT opened to print on screen

97
Rems:
1701 CLOSE 2 can be CLOSEd
1736 OPEN channel code must be K, S or P

channels and streams, see also 1601 CHAN OPEN


Expressions such as "can't" and "must" in the following
apply to BASIC programming and calls to ROM. Streams and
channels can be altered by m/c programming in ways not
described here, and if you understand what you are doing the
results may not be disastrous.
1. A channel is a pair of subroutines, one for input of
data and the other for output, with a one-byte label. The
subroutine addresses and the label are stored together in the
channel information area,_output_address followed by_input
_address followed by the channel code or_label_byte, making
five bytes in all; the first address of this five-byte sequence - a
pointer to the first byte of the output address - is stored as the
current_channel_address in system variable 5C51 CURCHL.
The_channel_code, which for the standard channels is
one of the letters K, S, R or P, is used in 1615 CHAN FLAG
whenever the channel is selected, to set flags controlling
output to the screen or ZX printer.
The_standard_channels are stored in the_channel
_information_area, whose start address or_base_address is kept
in 5C4F CHANS; usually 5CB6 unless a Microdrive is
connected.
They can be placed by m/c programming anywhere in RAM,
provided the base address of the current one is in CURCHL.
The channels are used by calling 15E6 INPUT AD for input
of a byte and 0010 PRINT A 1 for output of the byte in the A
register. The effect of such calls is to call whatever
subroutines are addressed by the current channel for input
and output respectively.
The input routine can also be called through
15DE WAIT KEY, which collects the byte from the
keyboard, or
3645 read-in, which will collect a byte as a string from

98
any channel given its stream number on the calculator stack.
The output routine can also be called through
15F2 PRINT A 2: the same as 0010 PRINT A 1, but called as
a subroutine
0C3B PO SAVE: "recursive", using the main registers;
PRINT A 1 uses the alternate registers
15EF OUT CODE: converts digits into their character
codes.
The_initial_channel_data for four standard channels are
loaded at start-up or NEW by 1219 RAM SET:
channel K input from keyboard, output to lower screen
channel S output to main screen
channel R output to add codes at the address marked by
5C5B K CUR in the editing area or work space
channel P output to ZX printer.
The data is at 15AF and following.
The input subroutine of the last three is an error
report, so any attempt to input to these channels just
produces an error message "Invalid I/O device". The output of
all except R is 09F4 PRINT OUT; this will print to the main
screen, the lower screen or the ZX printer depending on the
flag settings made depending on the channel letter by 1615
CHAN FLAG. The output routine of R is 0F81 ADD CHAR,
which adds a code to a string in the editing area or the work
space, but doesn't put anything on screen.
2. The streams are an indexing system for the channels:
channels can only be opened or closed by reference to their
stream number, by calling 1601 CHAN OPEN with the A
register holding the stream number.
A stream is a 2-byte number stored in the stream
information area. This 2-byte number, the_stream_data, is an
offset, which when added to the address in CHANS and
decremented by one gives the address of the first byte of the
channel information for the channel attached to that stream.
If the stream data is zero, it signals that that stream is closed.
The_stream_data_area or_stream_information_area
consists of the 26h/38d bytes starting at 5C10 STRMS. Each

99
stream's data takes two bytes, so there is room for 13h/19d
streams, the three "private" streams and the sixteen "available"
streams zero to 15d. The "private" streams are streams FDh/
minus 3 to FFh/minus one; they cannot be tampered with by
the BASIC user, and are used by the Spectrum for its "private
affairs". Because of the
"private" streams the base address for indexing is STRMS+6,
5C16; adding this to twice the stream number gives the
address of the first byte of stream data.
The stream information area is loaded at start-up with
14d bytes from the_initial_stream_data at 15C6. This leaves
zeroes in streams 4 to 15d, which are therefore initially
closed. Stream 00 is reloaded with one at 1313 MAIN G every
time a report is printed, resetting stream 00 to the K channel.
A stream is_closed if its stream data is zero. Streams
-3 to +3 cannot be closed; the minus numbers because they
cannot be named except by the ROM, the others because 16E5
CLOSE bypasses any attempt to close them.
A stream is_opened by putting a 2-byte offset in its
stream data which addresses a channel with non-zero data.
Streams -3 to +3 are permanently open; the minus streams
can't be changed, and stream 00 is frequently reset to channel
K. The others can be opened to channels K, S or P. This is
done from BASIC by the OPEN # command executed by 1736
OPEN: OPEN # X,Y$ labels the channel Y$ with stream number
X.
On the simple Spectrum only commands for channels K,
S and P will be accepted. With Interface 1 attached there are
new channels:
channel M outputs data to Microdrive
channel N outputs to a network
channels T and B output to the RS232 interface, in
different formats
Many add-on peripherals such as disc operating systems
have contrived yet other channels.
Introduction input/output information vectored through
0A6D PO TV 2 output address temporarily changed

100
0A75 PO 2 OPER output address temporarily changed
0A7A PO 1 OPER output address temporarily changed
0A80 PO CHANGE changes output address in CURCHL
0A87 PO CONT output address restored
0D94 CL CHAN opens chan K and restores output address
0DA0 CL CH A restores input address
0DAF CL ALL opens chan S and restores output address
0FA9 ED EDIT current channel saved while channel R
used
1105 KEY DATA input address temporarily changed
110D KEY NEXT input address restored
1113 KEY CHAN reloads input address
1113 KEY CHAN input address set in
1219 RAM SET sets CHANS, loads initial chan & str data
1313 MAIN G restores stream 00 to chan K
15AF initial channel information
15C6 initial stream data
15D4 WAIT KEY calls current input routine
15DE WAIT KEY1 calls current input routine
15E6 INPUT AD finds input address
15F2 PRINT A 2 finds output address
15F7 CALL SUB calls input/output routine as pointed to
1601 CHAN OPEN chan pointed to by str no made current
1610 CHAN OP 1 uses str data to find chan address
1615 CHAN FLAG sets flags for screen/printer chans
16E5 CLOSE puts zero in stream data for strs 4-15
1701 CLOSE 2 get channel code from stream data
1716 close stream look-up table
171C CLOSE STR recovers stream data address
171E STR DATA gets stream data from stream number
1725 REPORT O "Invalid stream"
1727 STR DATA1 indexes into stream information
1736 OPEN opens stream 00-15 for channel K, S or P
1756 OPEN 1 puts stream data in str info area
175D OPEN 2 checks for null channel code
1767 OPEN 3 gets stream data from look-up table
177A open stream look-up table

101
1793 CAT ETC no valid stream in standard Spectrum
17F5 LLIST sets stream 03 (ZX printer)
17F9 LIST sets stream 02 (main screen)
17FB LIST 1 opens channel and checks for stream change
1814 LIST 4 if no change in stream
1FC9 LPRINT sets stream 03 (ZX printer)
1FCD PRINT sets stream 02 (main screen)
1FCF PRINT 1 opens channel to screen or ZX printer
2024 PR ITEM 3 checks for stream change
2070 STR ALTER executes "# X,Y$"
361F str$ saves current channel and opens channel R
3645 read-in inputs through specified channel

CHANNEL S FLAG SUBROUTINE see 1642 CHAN S in 1615


CHAN FLAG

CHAN OPEN subroutine 1601 see also channels and streams


Opens the input/output channel corresponding to a
stream number input in the A register.
Essential for m/c programs if any printing on screen is
required. Call it with A = 2 to print in the upper screen, A =
zero or one for the lower screen.
Input parameters: A holds the stream number, FD to 10h.
Action: double A; two bytes per stream
- add it to 5C16; the address of stream zero
- read the two bytes of stream data
- if both bytes are zero report "Invalid stream"; closed
stream.
_1610_CHAN_OP_1: decrement the stream data
- add it to the address in 5C4F CHANS; this is the
channel address for the new channel.
Exit: into 1615 CHAN FLAG.
Output parameters: HL holds the new address for 5C51
CURCHL.
Called from:
0767 LD LOOK H
0970 SA CONTRL

102
0C55 PO SCR
0C88 PO SCR 2 (twice)
0D94 CL CHAN
0DAF CL ALL
0FA9 ED EDIT
12AC MAIN 2
17FB LIST 1
1FCF PRINT 1
2070 STR ALTER
2089 INPUT
361F str$
3645 read-in
Exit from:
1059 ED UP
106E ED LIST
Rems:
1736 OPEN channel code K, S or P required

CHAN OP 1 1610 (1601 CHAN OPEN)


Jumps from:
1601 CHAN OPEN

CHAN P 164D (1615 CHAN FLAG)


Jumps from:
162D channel code look-up table

CHANS system variable 5C4F


Bytes: 2
Base address of the channel information area; see
channels and streams.
The lowest of the fourteen system pointers which must be
adjusted each time space is made or reclaimed in the ROM,
see
1664 POINTERS, it will only be changed to "make room" when
Interface 1 is connected, to make room for the "Microdrive
maps". Without Interface 1, it holds the address 5CB6 just
above the system variables.

103
Written by:
1219 RAM SET
166B PTR NEXT
Read by:
1113 KEY CHAN
1610 CHAN OP 1
1701 CLOSE 2
1736 OPEN

CHAN S 1642 (1615 CHAN FLAG)


Jumps from:
162D channel code look-up table

CHAN S 1 1646 (1615 CHAN FLAG)


Jumps from:
1634 CHAN K

character see character code

character area (ie screen position holding character) see


DISPLAY AREA

character array see arrays, strings

"Character array: " message 09DA see cassette messages

character bit
This seems to be a misprint for "character byte", ie
"pixel byte", at ODD9 CL SET. See DISPLAY AREA.

character code see also 0018 GET CHAR, 0020 NEXT CHAR,
KEYBOARD SCANNING
[If a character has some special use besides its obvious
one, eg "E" in E-format numbers, you will find at least a cross-
reference in this index under the character name.]
The_codes of individual_characters are listed in

104
Appendix A of the old Spectrum handbook, Part 27 of the Plus
2 handbook. For the procedure by which they are derived
from keystroke inputs, see KEYBOARD SCANNING. The "final
code" of the last effective keystroke is always to be found in
5C08 LAST K.
1. The capital letters and digits are main codes, read
directly from 0205 main key table (a) by 032C K MAIN. But the
keys are also used for tokens, symbols and UDG graphics.
Further decoding (0333 K DECODE) depends on the mode
and shift:
Letter keys:
C mode or CAPS SHIFT: no more decoding (034C K KLC
LET)
E mode: look-up table b (no shift) or c (0341 K E LET)
G mode: add 4Fh/79d to character code (0333 K DECODE)
K mode: add A5h/165d to character code (0364 K TOKENS)
L mode: add 20h/32d to character code (034C K KLC LET)
SYMBOL SHIFT: look-up table e (034C K KLC LET)
Digit keys:
C mode or CAPS SHIFT: look-up table d (039D K KLC DGT)
E mode: no shift,
zero -> 7 are PAPER colours
8 and 9 are BRIGHT off/on (0382 K 8 & 9)
with caps shift,
zero -> 7 are INK colours
8 and 9 are FLASH off/on (0382 K 8 & 9)
with symbol shift, look-up table f
G mode: 0 and 9 are DELETE and GRAPHICS (0389 K GRA
DGT)
1 -> 8 are graphics (0389 K GRA DGT)
K or L mode: no more decoding (039D K KLC DGT)
SYMBOL SHIFT: take 10h from main code, with exceptions
(039D K KLC DGT)
2. The BASIC lines stored in the program area consist
essentially of strings of one-byte character and token codes.
The codes for_upper_and_lower-case letters are identical
except that bit 5 is set for the lower-case letters, making them

105
20h/32d higher than the corresponding upper-case:
01000001b/41h/65d "A" to 01011010b/5Ah/90d "Z"
01100001b/61h/97d "a" to 01111010b/7Ah/122d "z"
Each_digit_code is 30h/48d higher than its
value:
00110000b/30h/48d "zero" to 00111001b/39h/57d "9"
Letters can be made uniformly upper-case by AND
11011111b/DFh/223d and made uniformly lower-case by OR
00100000b/20h/32d.
3. When the letters are used for variable names the first
three bits are changed, and all letters are made lower case.
4. Printable codes: this means different things in
different parts of the notes. In 007D SKIP OVER and its related
routines it means any code from 21h "!" upwards, ie any letter,
digit, symbol, graphic or token, but_not the space and not
colour or position control codes. The space is printed, of
course, but is skipped in reading BASIC, eg in variable "long
names". At 09F4 PRINT OUT the space is printable, but not
control codes. Similarly at 0A69 PO QUEST and 0AD9 PO
ABLE.
5. The token codes can be classified as
_command_codes: CE DEF FN to FF COPY
_functions A5 RND to C2 CHR$
a few others: "adverbs" such as STEP, CODE, LINE
"terminators", THEN.
Rems ["Get next character" and similar in the notes,
annotating calls to 0018 GET CHAR or 0020 NEXT CHAR,
haven't been indexed in this long list]:
Introduction: returned by keyboard routine
0010 PRINT A 1 code held in A
0018 GET CHAR check for printable character
001C TEST CHAR check for printable character
007D SKIP OVER skips control parameters and flags
everything below 21h "!"
0333 K DECODE handles G mode
0341 K E LET handles E mode

106
034F K KLC LET handles K/L/C modes and SYMBOL
SHIFT
0367 K DIGIT separate according to mode
0382 K 8 & 9 BRIGHT and FLASH
0389 K GRA DGT block graphics
039D K KLC DGT modes K, L, C
0621 SA SPACE ten characters allowed for file name
0652 SA DATA is it 'DATA'?
0716 SA LINE is it "LINE"?
0767 LD LOOK H ten characters of name matched
078A LD TYPE checked and printed simultaneously
07A6 LD NAME check old against new
07AD LD CH PR print ten new
0909 ME OLD V2 point to first character of name
0912 ME OLD V3 letter-by-letter comparison of vble
names
09F4 PRINT OUT entered holding in A
0A3D PO RIGHT print invisible space
0A4F PO ENTER carriage return empties print buffer
0A69 PO QUEST "?" printed for "unprintable" codes
0A6D PO TV 2 control codes saved
0A75 PO 2 OPER code of AT or TAB saved
0A7D PO TV 1 control code saved
0A80 PO CHANGE make next code count as operand
0A87 PO CONT deal with AT
0AC2 PO TAB deal with TAB
0AD9 PO ABLE prints a code
0B24 PO ANY jump forward with all but graphics codes
0B38 PO GR 1 construct graphic characters
0B3E PO GR 2 graphic code determined
0B52 PO T&UDG tokens and UDG codes separated
0B6A PO CHAR 2 jump if not space
0B76 PO CHAR 3 put 8*code in HL, add to character base
0B7F PR ALL prints all characters
0BB7 PR ALL 4 prints character
0BC1 PR ALL 5 set attribute after printing character
0C10 PO TOKENS prints token codes

107
0C22 PO EACH print characters of tokens
0C3B PO SAVE single character printed "recursively"
0C41 PO SEARCH initial character found in table
0C44 PO STEP finds next after inverted character
0C55 PO SCR called on carriage return
0E0D CL SCR 2 line holds 20h characters
0E19 CL SCR 3 count characters remaining in third
0F38 ED LOOP sorts out control and edit codes
0F81 ADD CHAR adds code to EDIT or INPUT line
0F8B ADD CH 1 enters code
0F92 ED KEYS code indexes jump to editing routine
100C ED RIGHT put cursor after current character
1015 ED DELETE reclaim one character
101E ED IGNORE key inputs ignored
103E ED EDGE 1 jump if not control code
10A8 KEY INPUT checks LAST K and corrects control
codes
111B KEY DONE return with code in A
11A7 REMOVE FP check characters for number marker
1313 MAIN G adds 7 to ASCII value for report number
15DE WAIT KEY1 loops till it gets an acceptable code
15EF OUT CODE called with code in A
1767 OPEN 3 channel code used in look-up table
178B OPEN END channel code must be single character
1865 OUT LINE1 pointer set on first character of line
1894 OUT LINE4 loop to print characters of BASIC line
18B6 NUMBER looks for character 0E (number marker)
18C1 OUT FLASH prints a flashing cursor
18E1 OUT CURS uses C, E, G, K or L for cursor
18F3 OUT C 1 chooses cursor character according to mode
1925 OUT SP 2 prints all characters and tokens in BASIC
192B OUT SP 1 digit printed
1937 OUT CHAR discriminated from other keys
1937 OUT CHAR entry point for characters and tokens
195A OUT CH 1 discriminates " from other characters
1968 OUT CH 2 signal characters in L mode
196D OUT CH 3 prints character

108
1998 EACH S 2 loop to look at each character in line
19CE NEXT O 2 looks for last in variable long name
19FB E LINE NO reads digits of line number
1A30 OUT NUM 3 prints digits of line number
1A42 OUT NUM 4 prints digits of line number
1B17 LINE SCAN each in turn addressed by CH ADD
1B29 STMT L 1 check for newline or ":"
1BD1 NEXT LINE pointer set before first of line
1D64 F LOOP lower case set by OR 20h
1D96 LOOK PROG on entry, code in E register
1F86 DEF FN 1 must be a letter
1FF2 PRINT 4 return if ')'
1FFC PR ITEM 1 handles AT
200E PR ITEM 2 handles TAB
2045 PR END Z return if ')'
2048 PR ST END return if carriage return
204E PR POSN 1 handles PRINT comma
20C1 IN ITEM 1 jumps depending on code read
20ED IN ITEM 3 jump if not a letter
211C IN PR 2 two characters printed on screen
21B9 IN ASSIGN fetch code, check for STOP/ENTER
24FB SCANNING reads first character of expression
24FF S LOOP 1 index into table with character
250F S QUOTE 3 step through chars to find close quote
2522 S 2 COORD checks for "(" and ")"
2535 S SCRN$ S character set holds 96 characters
254F S SCRN LP character on screen checked with char
set
255D S SC ROWS converts counter to character code
2573 S SCR NXT loop through 96 characters in char set
257D S SCR STO stack character
25CB S Q COPY copy characters to work space
2634 S INKEY$ character is string length one
2684 S ALPHNUM check if alphanumeric
268D S DECIMAL read expression starting with a digit
26B6 S SD SKIP step through till number marker found
26DF S NEGATE char checked for various function codes
109
2723 S OPERTR checks for binary operators
27D0 SF BRKT 1 error unless "("
27E4 SF BRKT 2 error unless ")"
2814 SF CP DEF checks function name for "$"
283E L CHAR each code of "long name" made lower case
2843 SF ARG LP checks for "$" or number marker
2852 SF ARG VL checks after FP no for ")", ","
28AB FN SKPOVR steps over spaces/control codes in DEF
FN
28EF V RUN/SYN blanks character code
2900 V EACH checks codes in variable name
2912 V MATCHES check next
2913 V SPACES checks codes, skipping spaces and making
all lower case by OR 20h
2934 V SYNTAX fetch code, check for "("
294B V END points to last code of variable name
296B SFA CP VR checks for ")" in DEF FN
29C3 SV COMMA checks for comma and ")"
29D8 SV CLOSE checks for ")" and TO
29E0 SV CH ADD gets char and moves back to previous
29EA SV LOOP checks for ")" and TO
2A12 SV RPT C checks for ")"
2A2C SV ELEM$ checks for ")" and comma
2A49 SV SLICE? checks for "("
2A7A SL RPT C single character slice
2B0B L EACH CH characters in variable name counted
2B0C L NO SP checks codes in variable name
2B1F L TEST CH checks alphanum or "$"
2B29 L SPACES marks codes in variable name
2B3E L CHAR all codes made lower case
2C88 ALPHANUM check for digit or letter
2C8D ALPHA check for letters
2CB8 NOT BIN looks for digits to print
2CDA NXT DGT 1 picks up decimal digit and adds in to
FP number
2CEB E FORMAT check for 'E' or 'e'
2CFE SIGN DONE read first digit of E format number

110
2CFF ST E PART check for digit
2D1B NUMERIC discriminates digit codes
2D22 STK DIGIT replace digit code by digit value
2D3B INT TO FP fetch and save digits
2D40 NXT DGT 2 reads digit codes from BASIC
2DE3 PRINT FP converts FP number to string of
decimal digits
2DF2 PF NEGTVE prints code "-" for negative numbers
2E24 PF SMALL mem-5 holds number of digits to print
2E56 PF LARGE not more than 8 digits in E-format
2E8A PF BYTES two-digit bytes converted to 1-digit
2EA1 PF DIGITS jump if digit not zero
2EA9 PF INSERT counting digits
2EB8 PF ALL 9 nine digits stored in print buffer
2EDF PF FRN LP last digit rounded in print buffer
2EEF PF FR EXX store digit count and last digit
2F0C PF ROUND round eighth digit in print buffer
2F18 PF RND LP final 0 or 10d not valid digit
2F25 PF R BACK overflow makes 1 more before point
2F2D PF COUNT number of digits to be printed
2F46 PF NOT E initial zero if none before point
2F4A PF E SBRN print digits before the point
2F52 PF OUT LP put digit in A for printing
2F59 PF OUT DT print digit
2F5E PF DC OUT puts fraction point code in A
2F64 PF DEC 0S prints point and puts code for "0" in A
2F6C PF E FRMT prints code "E" in E-format numbers
2F83 PF E POS prints code "+" in E-format numbers
35DE val adds newline to end of expression
361F str$ room made in work space for each character
3669 code returns first character code in A$

character form see character set

character line see DISPLAY AREA

character pointers

111
Used in 2991 SFA END to mean "the addresses of the
variable letter and the following character in the program
area or work space which were pushed on the stack in 28B2
LOOK VARS, before the jump to 2951 STK F ARG from 2853 V
TEST FN".

characters see character code

character set 3D00 (confusingly called "character area" in


0B65 PO CHAR)
The character set consists of 96d_character_forms, one
form for each character code from 20h/32d space to 7Fh/127d
copyright symbol inclusive: each form consists of eight
consecutive bytes, one for each pixel line of the character. See
the description on page 221 of the notes, or in Chapter/Part 16
of the Spectrum handbooks.
The "base address" of the character set in 5C36 CHARS
isn't 3D00 as you might expect, but 3C00; to find any
character
form in the set you add 8 times its code to the base address,
but the first code is character 20h, so you find it by
3C00 + 8 * 20 = 3D00.
The set contains only the letters, digits and symbols,
not the graphic forms which are drawn as required by ROM,
the user-defined graphics or of course any tokens. The
character set, being in ROM, cannot be changed: the user-
defined graphics are put in RAM precisely so that they can be
changed.
0B38 PO GR 1 graphics constructed ad hoc in MEMBOT
area
0B4C PO GR 3 construction of graphics
0B65 PO CHAR form found in character set
0B76 PO CHAR 3 finds character form in char set or UDGs
0B7F PR ALL on entry DE holds address of char form
0BB7 PR ALL 4 screen byte XORed with form byte
0BC1 PR ALL 5 next pixel byte of
11EF RAM DONE forms A to U copied to UDG

112
2535 S SCRN$ prepare to check screen pixel byte against
first form byte of 60h/96d characters
254F S SCRN LP check both direct and inverse
255D S SC ROWS check remaining 7 bytes of form/screen
2573 S SCR NXT loop on to next form
3D00 character set holds 60h/96d codes 20h -> 7Fh

character strings see strings

CHARS system variable 5C36


Bytes: 2
The base address of the character set: since the set
consists of characters 32 -> 127d, CHARS is set 8 * 32 = 256d/
100h_below the first character form. Normally set at 3C00;
can
be set at 100h below any m/c character set.
Written by:
1219 RAM SET
Read by:
0B65 PO CHAR
2535 S SCRN$ S

Chebyshev polynomials see also series-06


This is a way of finding approximations to complicated
functions by a long series of simple calculations. The
calculations used are essentially only addition, subtraction
and multiplication. It is an example, a very difficult one, of an
algorithm; see algorithms in the index.
A "function of z" means an expression like SIN z or z**2
or 7z + 14, which has z as a variable and takes different values
for different values of z.
It is a mathematical fact that any function whatever of
z can be_approximated_within_a_limited_range of z by an
expression of the type
A + Bz + Cz**2 + Dz**3 ... + Pz**N,
it being understood that z is the only variable in the
expression; the_coefficients A, B, C ... P are actual numbers

113
determined for the approximation of this particular function.
Some of the constants A, B etc may be zero or negative, and
often are. Such an expression is called a_polynomial_in_z.
Generally speaking the range of z, ie the range of values for
which the polynomial gives a good approximation to the
function, is limited to -1 < z < 1.
N will be quite a small number, depending on the
closeness of the approximation desired, the range to be
covered, and the degree of complication of the function of z.
In the ROM, the highest value of N used is 12d, for LN z and
ATN z.
An illustration may help: if you draw a line from London
to Manchester on the map, and another line from London to
Birmingham, and measure the angle between them, you won't
get exactly the same angle as if you drew the lines on the
ground;
because the world is round and your map is flat. But for
distances of a few hundred miles, the straight-line
approximation, using a polynomial in which the highest value
of N is one, ie Y = A + Bz, gives an approximation to the
curved lines on the ground good enough for navigation.
If you were sailing from Yokohama to San Francisco
across the Pacific, you would have to allow for the world being
round. Navigators use formulae which assume the world is
spherical, and get good results over any distance by using
circles to approximate the curves through which their craft
are travelling. Circles represent polynomial approximations in
which the highest value of N is two, Y = A + Bz + Cz**2.
This isn't really exact either, because the world isn't
a perfect sphere, but slightly oval; not even quite
symmetrical, but slightly pear-shaped. However the difference
isn't big enough to matter: if the ship is an inch or two too far
to the left as it sails into San Francisco Bay the navigator isn't
going to get the sack for it

Pafnuti Chebyshev was a nineteenth-century Russian

114
mathematician who devised a method for calculating any
function to any required degree of approximation by using
polynomials in this way. To explain the method in full, even if
I knew how to, would take rather a lot of space, but references
are given under 3449 series-06 and on page 226 of the
notes_(Handbook_of
_Mathematical_Functions by M Abramowitz and I A Stegun,
Dover 1965, especially pages 82 and 795).
The method uses a standard series of_Chebyshev
_polynomials, polynomials whose coefficients are the same
whatever the function being approximated, and hence called
_Chebyshev_coefficients; and it also finds a set of_Chebyshev
_constants which are special to the particular function.
The expansion at the bottom of page 227 of the notes
shows the first twelve Chebyshev polynomials and their
Chebyshev coefficients; the way the coefficients run in the
standard polynomials - they are the same for any function - is
a little easier to follow if you tabulate them by themselves,
leaving out all the zs, plus and minus signs, etc:

2048 5632 5632 2464 440 22


1024 2560 2240 800 100 2
512 1152 864 240 18
256 512 320 64 2
128 224 112 14
64 96 36 2
32 40 10
16 16 2
8 6
4 2
2
2
yea(This last 2 isn't used as a
coefficient)

You will find, if you check it, that each number in this
table is
the sum of all the numbers below it in its own column,
plus all the numbers in the previous column, starting
115
from two below it.
Also you can see from the expansion on page 227 of the
notes that in each of the Chebyshev polynomials
- the powers of z are all odd for the first, third, fifth
... polynomial and all even for the second, fourth, sixth ...
- the coefficients in each polynomial have alternately +
and - sign, always starting with +.
All this is expressed mathematically by the "recurrence
relation" given in the header to 3449 series-06 on page 198
T(n+1)(z) = 2*z*T(n)(z) - T(n-1)(z)
or in words, each Chebyshev polynomial after the first three
is 2z times the last one minus the one before that. See also the
index under 3449 series-06.
Using either the table or the recurrence relation, you
can figure for yourself any number of Chebyshev polynomials.
You would have to know that the first three are Y = 1 and Y =
2z and
Y = 4z**2 - 2.
To get the final approximation for the function of z you
calculate as many polynomials in the series as you require and
multiply each of them by the correct constant in the series for
the particular function; then you add all these products
together for the result.
The Chebyshev constants are different for each function
of z: the correct constants for each ROM function are shown
in decimals in the BASIC demo programs on pages 223-226 of
the Appendix, and in FP form in the various calls to series-06;
both versions have been copied in this index under the
function subroutines concerned.
The method by which the constants are calculated is
indicated in the footnotes on pages 223-226, not very clearly;
and it is also not clear whether this method can be
generalized to other functions, or if so how.
The approximations based on Chebyshev polynomials as
used by the Spectrum ROM don't depart from the curves they
are mimicking by more than about one in ten million, say six
inches in a thousand miles, within the range in which they

116
are used; outside this range Pafnuti rapidly loses control, and
they may diverge quite far quite quickly.
The same SERIES GENERATOR procedure can be used for
any function at all; provided you are clever enough to figure
out the right constants for your function!
Introduction used for SIN, exp, ln, atn
36C4 EXP used
3713 ln used
37BF sin used
37E2 atn used
Appendix BASIC demonstration programs

CHECK END subroutine 1BEE


Called for syntax checking when the end of a command
statement ought to have been reached. Only executed during
syntax checking; in run time, it returns immediately.
In syntax checking it reports "Nonsense in BASIC" if it
doesn't find a statement "terminator"; if it does, it drops a
return address to make a "double return" to the statement
loop.
All the calls are from subroutines executing BASIC
commands, or from the command class routines subordinate
to them, listed in the parameter table at 1A7A.
None of these command routines are called in a
straightforward way: they are all entered by the computed
indirect jump from 1B55 GET PARAM, which is within the
"statement loop". This loop is itself part of the subroutine
1B8A LINE RUN, but for syntax checking it is also entered from
1B17 LINE SCAN, called from main execution by 12AC MAIN 2.
Thus on entry to this CHECK END subroutine the
addresses on the machine stack are
- top: the return address to the calling subroutine. The
notes are mistaken in saying that 1B52 SCAN LOOP stacked by
1B55 GET PARAM is dropped in CHECK END; it has been
dropped already either in 1C11 CLASS 05 or in the calling
routine
- second: 1B76 STMT RET put here by 1B29 STMT L 1, the

117
looping address of the LINE SCAN loop
- third: return address to 12AC MAIN 2.
Input parameters: none.
Action: if the flag indicates "run time" return
- drop the return address.
Exit: into 1BF4 STMT NEXT; it returns to 1B76 STMT RET.
Output parameters: none
- the return address is now 12AC MAIN 2.
Called from:
(0605 SAVE ETC from 1CDB CLASS 0B:)
0692 SA DATA 1
06A0 SA SCR$
06F9 SA CODE 4
0716 SA LINE
0723 SA LINE 1
(17F9 LIST:)
1822 LIST 5
(direct from CLASS routines:)
1C11 CLASS 05
1C4E CLASS 02
1C96 PERMS
(1D03 FOR:)
1DO3 FOR
1D10 F USE 1
(1DED READ:)
1E1E READ 2
(1E27 DATA:)
1E2C DATA 1
(1F60 DEF FN:)
1FBD DEF FN 7
(1FCD PRINT:)
1FCF PRINT 1
(2089 INPUT:)
2096 INPUT 1
(2320 CIRCLE:)
2320 CIRCLE
(2382 DRAW:)

118
2382 DRAW
238D DR 3 PRMS
(2C02 DIM:)
2C05 D RPORT C

checking syntax see errors

CHR$ key (C2) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The U key in E mode without shift produces the function
CHR$; it requires one numeric operand X, less than 256d, and
the value of the function is the character whose code is INT X.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code C2 first to 13h, then to
EF, and adds the priority 10h/16d. Code and priority 10EF are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following CHR$ is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from EF to 2F, the calculator
offset for 35C9 chr$.
2707 S NO TO $ number result from string argument

chr$ subroutine 35C9


Called from 0028 FP CALC with the literal 2F; the
executive routine of the CHR$ function. Not called otherwise
from ROM. Could be called from m/c direct, to convert a
character code X to a character.
Input parameters: X must be the last value on the
calculator stack, even for direct calls; it needn't be an
integer, but must be positive and less than 256d.
Action: call 2DD5 FP TO A to get the number in A
- if it returns with carry or NZ report "Integer out of
range"; either negative or too big
- call 0030 BC SPACES to make a space in the work space
- put the character code in it
- call 2AB2 STK STORE to put the string parameters of
this single character on the calculator stack.

119
Exit: RET.
Output parameters: the last value on the calculator stack
is now the string parameters of a single character, code X, in
the work space
- HL and DE point respectively to the last value and to
the stack end.

CIRCLE key (D8) see also commands, functions and


operators,
KEYBOARD SCANNING, 0246 extended mode key table (c)
The H key in E mode with either shift produces the
command CIRCLE. It must be followed by three parameters X,
Y, z;
X and Y are the pixel coordinates of the centre of the circle
and z the radius in pixels.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AE7 P CIRCLE causes jumps via 1C16 JUMP C R to
1CBE CLASS 09 which handles any colour controls and
gets X and Y
1C11 CLASS 05 which merely checks syntax
and to the executive routine 2320 CIRCLE.

CIRCLE subroutine 2320


Called only from the statement loop by 1AE7 P CIRCLE in
the syntax parameter table; executes the CIRCLE X,Y,Z
command.
See the BASIC model in the Appendix of the notes, page 228.
Cannot easily be called direct from m/c, because it is
normally entered with only the first two parameters on the
stack
and begins by reading the radius Z from BASIC. M/c
programmers should call 232D (RST 28 FP CALC) with all
three parameters on the calculator stack, Z as last value.
The radius Z is put on the stack and re-stacked as a
full-form FP number; its sign is ignored. If the radius is less

120
than one, the circle is just plotted as a point at X,Y.
Now call 247D CD PRMS1, with 2pi in mem-5 as the value
of G, the angle through which the line drawn is to turn. It
returns the arc count a and SIN pi/a. The arc count is the
number of "arcs" to be drawn to approximate the circle, and Z
times SIN pi/a is half the length of each arc in pixels; there is a
misprint here in the notes. Other parameters are returned,
but not used till the exit routine. If the arc length is less than
one, again the circle is plotted as a point.
The rest of the routine prepares parameters for the exit
routine 2420 DRW STEPS, which actually draws the circle:
- the coordinates sa, sb of the start point of the DRAW:
sa = Z + X, directly to the right of the centre and at the
distance of the radius, sb = Y - Z SIN pi/a, half an arc length
directly below the level of the centre
- the coordinates of the end point: in the case of a
circle the start point is also the end point
- the displacements in the x and y direction of the first
arc to be drawn: this will be vertically upwards to the level of
the centre and the same distance beyond, so the x
displacement is zero and the y displacement is 2Z SIN pi/a.
X, Y, Z and Z SIN pi/a are already on the stack, and it
is simple to calculate sa, sb, and 2Z SIN pi/a. The arc count a is
also already on the machine stack.
Input parameters: X and Y must be the last two items on
the calculator stack, even for direct calls. The BASIC pointer
in 5C5D CH ADD points to the comma after Y.
Action: read the BASIC
- if the code isn't a comma report "Nonsense in BASIC"
- move the pointer on
- call 1C82 EXPT 1NUM to evaluate the expression for Z
- call 1BEE CHECK END; this must be the end of the
statement
- use the calculator to get ABS Z and re-stack it as a
full-form FP number
- check the exponent of Z
- if it is more than 80h jump on to C R GRE 1; Z is one

121
or more
- (Z < 1) delete Z from the stack
- exit to 22DC PLOT; the circle is just plotted as a
point at X, Y.
_233B_C_R_GRE_1: use the calculator to get pi/2 from the
table of constants
- change its exponent to 83h; making it 2pi
- put 2pi in mem-5
- call 247D CD PRMS1 to calculate the "input parameters
for CIRCLE and DRAW", the arc count and the various
functions of
G; here G = 2pi
- save the arc count on the stack
- get SIN pi/a from mem-1 and multiply it by Z; this is
half the arc length
- if its exponent is 80h or more jump on to C ARC GE1;
the arc half-length is more than half
- (arc length < 1) delete the arc half-length
- exit to 22DC PLOT; the circle is just plotted as a
point at X, Y.
_235A_C_ARC_GE1: use the calculator to figure sb = Y - Z
SIN pi/a and sa = Z + X
- by duplicating, exchanging and use of mem-0, make the
stack hold, from the top, sa, sb, sb, sa, sb, sa
- put zero in mem-1 for the exit routine
- increment the exponent of mem-2; doubling the arc
half-length so it is now the length required by the exit
routine. [The listing shows "INC (mem-2-1st)" but the machine
code actually reads "INC (IY+62h)": since MEMBOT is IY+58h,
the byte incremented is the tenth in memory, ie the first byte
of the third 5-byte number, the exponent byte of mem-2.]
- call 1E94 FIND INT1 twice to take the top two values
sa and sb off the stack
- put them in 5C7D COORDS for the exit routine
- recover the arc count for the exit routine.
Exit: into 2420 DRW STEPS to draw the circle, or
- into 22DC PLOT if the radius is zero or the arc length

122
is too small.
Output parameters: B holds the arc count
- the parameters for PLOT or DRW STEPS are left in
memory locations and on the calculator stack
- corrupts all memory locations mem-0 -> mem-5.
Rems:
22DC PLOT used twice by
2420 DRW STEPS angle of arc is 2pi
247D CD PRMS1 called by with X Y Z on stack
Appendix: BASIC demo program

CL ADDR subroutine 0E9B


Finds the screen display address of the first pixel of a
given screen line number, counted from one at the bottom of
the screen to 18h/24d at the top. Can be called from m/c.
Each "third" of the screen starts at an address with 00
in its lo byte: 4000, 4800 and 5000; the eight line starts in
each third have lo bytes 00, 20h, 40h ... E0h. See DISPLAY
AREA.
Divide the screen line number counted from zero at the
_top of the screen by 8, giving a quotient q with remainder r.
Then the hi byte of the display address will be 40h + 8q
- and the lo byte will be the r'th of the line start
bytes, r times 20h.
The ROM is a good example of "one-byte binary
arithmetic", see the index entry for others
The screen line number is a number of at most 5 binary
digits, say 000XYPQRb. XYPQR represent digits which may be
zero or one: PQRb is the remainder r on dividing by eight,
XYb is the quotient q. Three right rotations give PQR000XYb;
ANDing with 11100000b/E0h gives PQR00000b, which is
already 20h * r, the required lo byte. Now 000XYPQRb is
recovered and ANDed again, this time with 00011000b/18h,
giving 000XY000b; this in turn is ORed with 01000000b
giving 010XY000b, which is none other than the required hi
byte 40h + 8q.
Input parameters: B holds the line number.

123
Action: subtract the line number from 18h/24d; now it
counts from zero at the top to 17h/24d at the bottom
- save a copy of the line number
- rotate it three times right
- AND the result with 11100000b/E0h; this is the lo byte
- recover the line number and AND it with 00011000b/18h
- OR the result with 01000000b/40h; this is the hi byte.
Exit: RET.
Output parameters: HL holds the display address, the
start of line B
- B unchanged.
Called from:
0CD2 PO SCR 3
0DEE CL SET 1
0E00 CL SCROLL
0E44 CL LINE

CL ALL subroutine 0DAF


Clears the whole display area. From m/c it is usually
better to call 0D6B CLS, to reform the lower part of the
screen.
Input parameters: none.
Action: put zero in 5C7D COORDS; the PLOT position
- reset FLAGS2 bit zero; "the screen is cleared"
- call 0D94 CL CHAN to correct channel K and set the
lower screen print position to 1721h, ie 0,0; see DISPLAY
AREA
- call 1601 CHAN OPEN with stream minus 2 to open
channel S
- call 0D4D TEMPS to copy the permanent colours into
the temporary ones
- call 0E44 CL LINE to clear 18h/24d lines and colour
them with the permanent attributes
- put the correct output address in channel S; it might
not have been restored after 0A6D PO TV 2 and following
- put one in the scroll counter 5C8C SCR CT; corrected
later by 12CF MAIN 2 or 20AD INPUT 2

124
- load the upper screen print position 1821h, ie 0,0;
see DISPLAY AREA.
Exit: into 0DD9 CL SET and 0ADC PO STORE, which load
values into system variables controlling the display.
Output parameters: BC holds the print position for the
first character on the screen.
Called from:
0D6B CLS
12CF MAIN 3
1795 AUTO LIST

CLASS 00 subroutine 1C10


A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop. It is called for commands which mustn't have operands,
such as COPY, or those whose operands have all been
collected by other routines and mustn't have any more.
It merely sets the Z flag, which has the effect of
calling 1BEE CHECK END two lines further down; in syntax
checking, this will make an error report unless a colon or
newline is found.
Exit: into 1C11 CLASS 05.
Called from syntax parameter table by:
(no parameters wanted:)
1A8A P STOP
1A8D P RETURN
1AA8 P NEW
1AB8 P CONT
1ABE P CLS
1AD6 P COPY
1B14 P CAT
(all parameters fetched:)
1A7D P GO TO
1A86 P GO SUB
1A98 P NEXT
1AB1 P POKE
1AC1 P PLOT

125
1AC5 P PAUSE
1AE3 P BEEP
1AF1 P OUT
1AF5 P BORDER
1AFC P OPEN
1B02 P CLOSE
1B06 P FORMAT
1B0A P MOVE
1B10 P ERASE
Exit from:
1C0D CLASS 03

CLASS 01 subroutine 1C1F


A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop. It is called for LET to identify the variable in assignment
in case it is an old variable, see under variables. It is also
called direct from other parts of ROM where a variable must
be found.
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on a variable
letter.
Action: call 28B2 LOOK VARS, which reads the variable
letter, checks if the variable has been used before, and finds
its address in the variables area if so.
Output parameters: as for LOOK VARS.
Exit: into 1C22 VAR A 1; see under LOOK VARS.
Called from syntax parameter table by:
1A7A P LET
Called direct by:
1DED READ
20D8 IN ITEM 2
20ED IN ITEM 3

CLASS 02 subroutine 1C4E


A "command class" subroutine, called from the syntax

126
parameter table 1A7A by 1B55 GET PARAM in the statement
loop. It is called only for the LET command, to evaluate the
expression after LET X = in the BASIC line and give its value to
the variable in assignment.
Input parameters: none
- the BASIC pointer 5C5D CH ADD is on the start of the
expression after LET X =.
Action: drop the return address; no more return to 1B52
SCAN LOOP, return now will be to 1B76 STMT RET
- call 1C56 VAL FET 1 to evaluate the expression
- call 1BEE CHECK END; must be the end of the statement.
Exit: RET, to 1B76 STMT RET.
Called from syntax parameter table by:
1A7A P LET

CLASS 03 subroutine 1C0D


A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop.
For commands which may or may not be followed in the
BASIC line by one numeric parameter; assigns zero if none
given.
Input parameters: none
- the BASIC pointer 5C5D CH ADD is on the character
after the command.
Action: call 1CDE FETCH NUM; if the pointer is on a
numeric expression it gets the value, if on a terminator it
supplies the value zero.
Exit: into 1C10 CLASS 00; it checks that nothing follows
the parameter.
Called from syntax parameter table by:
1AAB P RUN
1AB5 P RANDOM
1AB8 P CLEAR
1ACF P RESTORE

CLASS 04 subroutine 1C6C

127
A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop.
Called for the FOR and NEXT commands; finds the variable
named in the BASIC line and checks it is a valid looping
variable.
Input parameters: none
- the BASIC pointer 5C5D CH ADD is on the variable
name.
Action: call 2B82 LOOK VARS, which finds the variable
letter in the variables area
- OR the letter with 10011111b/9Fh; this will make FF if
the hi bits are 011 or 111, a "short numeric variable" name
- if incrementing the result doesn't make zero, report
"Nonsense in BASIC".
Exit: into 1C22 VAR A 1, which makes the variable ready
for assignment; see under 28B2 LOOK VARS.
Called from syntax parameter table by:
1A90 P FOR
1A98 P NEXT
Rems:
1DAB NEXT variable is already determined

CLASS 05 subroutine 1C11


A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop.
Called when all parameter collecting, etc, which can be done
from the parameter table is complete and it is time to break
out to the executive routine.
This is sometimes done through the class routines which
exit into CLASS 05, 1C0D CLASS 03 or 1C10 CLASS 00, but:
- CLASS 03 collects a parameter, or supplies zero if none is
given in the BASIC
- CLASS 00 rejects further parameters with an error
report.
CLASS 05 is neutral about this, leaving such decisions

128
to the executive routine itself.
The example given in the notes is PRINT; a full list is
CIRCLE: there must be a third parameter, but it is left
to the executive routine
DATA and READ: identifying and reading the values is left
to the executive routine
DEF FN: there are parameters, but they are only read
when checking syntax.
DIM: must be followed by a variable name, but it is left
to the executive routine to identify it
DRAW: there may or may not be a third parameter
FOR ... TO x: may be followed by STEP or not
INPUT: may be followed by a string before the variable to
be assigned, or not
LIST and LLIST: may be followed by a line number or a
stream
PRINT and LPRINT, which can stand alone or be followed
by an expression
REM: may be followed by anything or nothing
THEN: "IF xxxx THEN" is good syntax by itself, though it
has no particular effect. Indeed, IF USR x THEN can be used
to operate a m/c routine.
Input parameters: the Z flag shows NZ on a direct call or
Z on entry from CLASS 00 or CLASS 03.
Action: drop the 1B52 SCAN LOOP return address; there
will be no more parameters from the parameter table 1A7A.
The return address on the machine stack is now 1B76 STMT
RET.
- if the Z flag is set call 1BEE CHECK END to report
"Nonsense in BASIC" if the end of the BASIC statement hasn't
been reached; the flag shows NZ when the routine is called
from GET PARAM, because of the DEC B at the end of GET
PARAM, so CHECK END isn't called. It is Z on entry from
CLASS 00 or CLASS
03; in these cases the statement must be at an end.
_1C16_JUMP_CR (this label is never used in ROM, despite
having a cross-heading to itself ): get the next two bytes from

129
the syntax parameter table 1A7A - the address of the executive
subroutine - and jump to that address.
Exit: the RET is actually a jump to the address in BC.
Output parameters (they were read by 0018 GET CHAR at
theend of 1B55 GET PARAM
- or, if entry was from 1C0D CLASS 03,
they were read by GET CHAR in 2734 S LOOP
on the exit from 24FB SCANNING
called by 1C82 EXPT 1NUM,
called by 1CDE FETCH NUM,
called by 1C0D CLASS 03):
- HL holds the address of the last character code read
from BASIC -_except if FETCH NUM had to use a zero, in
which case HL points to the first byte of the zero on the
calculator stack
- A holds the value of the code.
Called from syntax parameter table by:
1A81 P IF
1A90 P FOR
1A9C P PRINT
1A9F P INPUT
1AA2 P DIM
1AA5 P REM
1AAE P LIST
1AC8 P READ
1ACC P DATA
1AD2 P DRAW
1AD9 P LPRINT
1ADC P LLIST
1AE7 P CIRCLE
1AF9 P DEF FN
Exit from:
1C10 CLASS 0

CLASS 06 subroutine 1C82 (also called EXPT 1NUM)


A "command class" subroutine, called from the syntax

130
parameter table 1A7A by 1B55 GET PARAM in the statement
loop.
Called for commands which must be followed in the BASIC
line by one numeric parameter. Also called direct as EXPT
1NUM whenever a numeric expression is to be read from
BASIC and put on the calculator stack.
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on the start of
the expression in BASIC.
Action: call 24FB SCANNING to get the value of the
expression
- if bit 6 of FLAGS is set report "Nonsense in BASIC"; a
string value is an error.
Exit: RET.
Output parameters: HL points to the first byte of the
last value on the calculator stack, the value of the expression
- DE holds the stack end.
Called from syntax parameter table by:
1A7D P GO TO
1A81 P IF
1A86 P GO SUB
1A90 P FOR (twice)
1AC5 P PAUSE
1AF5 P BORDER
1AFC P OPEN
1B02 P CLOSE
Called as EXPT 1NUM by:
06E1 SA CODE 1
06F5 SA CODE 3
0723 SA LINE 1
1814 LIST 2
1C7A EXPT 2NUM
1D03 FOR
200E PR ITEM 2
2070 STR ALTER
21FC CO TEMP 4
2320 CIRCLE

131
238D DR 3 PRMS
2ACD INT EXP2
Exit from:
1C7A EXPT 2NUM
1CDE FETCH NUM

CLASS 07 subroutine 1C96 (also called PERMS) see also


colours
A "command class" subroutine, called from the syntax
parameter table 1A7A; unlike most of the other CLASS
subroutines, it is also the executive routine for the command,
and doesn't return to the parameter table.
The executive routine for the INK to OVER commands
when used as free-standing BASIC commands, ie not as part of
a PRINT etc command; the header note is a little obscure. Not
otherwise called from ROM.
The actual colouring of the screen is performed by 0BDB
PO ATTR, which uses the fbpapink byte from 5C8F ATTR T to
make the PAPER, INK, FLASH and BRIGHT attributes and the
flags from
5C91 P FLAG for INVERSE, OVER and PAPER and INK 9 - see
colours.
Individual items of the permanent attributes kept in
5C8D ATTR P and putting the result in ATTR T, or for
INVERSE and BRIGHT simply by changing P FLAG.
Input parameters: none
- the lo byte of 5C74 T ADDR, which holds the next
address in the parameter table, is used as an index for the six
INK to OVER commands
Action: zero bit zero of 5C3C TV FLAG to signal "main
screen"
- call 0D4D TEMPS to put the ATTR P attributes in ATTR
T; any permanent attributes which aren't changed by
execution will be used as they stand
- drop the return to the statement loop; return will be
to 1B76 STMT RET
- take 13h from 5C74 T ADDR lo; it held EC -> F1 for the

132
six INK to OVER parameter calls, so subtracting 13h gives D9 ->
DE, the token codes for INK to OVER
- call 21FC CO TEMP 4 to convert these to the control
codes 10h -> 15h, read the parameter zero, one or 8 from the
BASIC and output control code and parameter through the
current channel; nothing happens immediately on screen as a
result, but if the upper or lower screen channel is open this
makes the required changes in ATTR T/MASK T
- call 1BEE CHECK END to report "Nonsense in BASIC" if
this isn't the end of the statement; remember we are handling
INK to OVER as free-standing commands
- copy ATTR T/MASK T to ATTR P/MASK P; the two bytes
are handled together as a single sv
- get the byte from 5C91 P FLAG; it controls "PAPER and
INK 9", INVERSE and OVER, its even bits flag the temporary
attributes, its odd bits the permanent ones
- rotate it one bit to the left; putting the temporary
attributes in the "permanent" position
- XOR/AND/XOR the original byte with the result, using
the mask 10101010b/AAh; the mask causes copying where it
has zero "holes", in its even bits, so the temporary attributes
from the original are copied back to the new byte. See masks.
Exit: RET to 1B76 STMT RET, to read the next statement.
Output parameters: none.
Called from syntax parameter table by:
1AEB P INK
1AEC P PAPER
1AED P FLASH
1AEE P BRIGHT
1AEF P INVERSE
1AF0 P OVER

CLASS 08 subroutine 1C7A (also EXPT 2NUM)


A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop.

133
Called for commands which must be immediately followed in
the BASIC line by two numeric parameters, separated by a
comma: POKE and OUT.
Also, as EXPT 2NUM, used as an exit routine when two
numeric expressions are to be read from BASIC.
Input parameters: none
- the BASIC pointer 5C5D CH ADD is on the start of the
first numeric expression.
Action: call 1C82 CLASS 06 (EXPT 1NUM) to get the value
of the first expression
- if the next code isn't 2C comma report "Nonsense in
BASIC"
- move on the pointer.
Exit: into EXPT 1NUM again for the second expression.
Output parameters: as for CLASS 06.
Called from syntax parameter table by:
1AB1 P POKE
1AF1 P OUT
Exit as EXPT 2NUM from:
1C79 NEXT 2NUM
1CD6 CL 09 1 (1CBE CLASS 09)

CLASS 09 subroutine 1CBE


A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop.
Called for commands which must be followed in the BASIC
line by two numeric parameters, separated by a comma, and
which draw on the screen; PLOT, DRAW, CIRCLE. These
mustn't change the attributes where they mark the screen,
unless instructions are included in the command; so the
"default" commands PAPER 8;
FLASH 8; BRIGHT 8 and NOT PAPER 9 are executed before
any such instructions are looked for, and before the
parameters are picked up. INK 8 isn't executed.
The effect is complicated. All_controls embedded in the
DRAW etc command will be implemented, including INK.

134
However, any_command except an INK command affecting
the whole screen,
for example PAPER 0:, won't affect DRAW etc unless it is
otherwise implemented, eg by CLS; in other commands of the
PRINT type, INK to OVER commands are executed in the
printed area of the screen even though the rest of the screen
hasn't yet been changed.
Input parameters: none.
Action: in syntax checking, jump on to CL 09 1
- zero bit zero of TV FLAG; "upper screen"
- call 0D4D TEMPS to make 5C8F ATTR T hold the
"permanent" values
- OR 5C90 MASK T with 11111000b/F8h; leaving the INK
part unchanged but putting ones in PAPER, BRIGHT and
FLASH -
this means they won't be changed on screen
- zero bit 6 of 5C91 P FLAG; this ensures that P FLAG
doesn't call for PAPER 9 (contrast)
- read the next code in BASIC.
_1CD6_CL_09_1: call 21E2 CO TEMP to execute any
embedded
INK to OVER commands found in the DRAW etc command.
Exit: into 1C7A EXPT 2NUM, to get two coordinates on to
the calculator stack; the third coordinate of CIRCLE, and of
DRAW if any, is handled by the executive routines.
Output parameters: none.
Called from syntax parameter table by:
1AC1 P PLOT
1AD2 P DRAW
1AE7 P CIRCLE

CLASS 0A subroutine 1C8C (also EXPT EXP) see also strings


A "command class" subroutine, called from the syntax
parameter table 1A7A by 1B55 GET PARAM in the statement
loop.
Called for commands which must be followed by a string
expression in the BASIC line. Also called direct as EXPT EXP

135
from 0605 SAVE ETC to get the file name from the BASIC line.
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on the start of
the expression.
Action: call 24FB SCANNING to read the expression
- if bit 6 of FLAGS isn't zero report "Nonsense in
BASIC"; it must be a string.
Exit: RET.
Output parameters; HL points to the first byte of the
string parameters on the calculator stack
- DE holds the stack end.
Called from syntax parameter table by:
1AFC P OPEN
1B06 P FORMAT
1B0A P MOVE (twice)
1B10 P ERASE
Called as EXPT EXP from:
0605 SAVE ETC

CLASS 0B subroutine 1CDB


A "command class" subroutine, called from the syntax
parameter table 1A7A; unlike most of the other CLASS
subroutines
it jumps straight out to 0605 SAVE ETC, the executive routine
for the four SAVE/LOAD commands.
Called from syntax parameter table by:
1ADF P SAVE
1AE0 P LOAD
1AE1 P VERIFY
1AE2 P MERGE

CL ATTR subroutine 0E88 see also colours, DISPLAY AREA


Given the address of a line start in the display area
- calculates the address of the corresponding attribute
byte in the attributes area
- calculates the number of attribute bytes from this one
to the end of the attributes area inclusive, used as a counter

136
in scrolling or rewriting the attribute bytes.
It is occasionally useful for m/c programmers, but note
the slightly off-beat input parameters.
1. The line starts in the display area have 40h, 48h or 50h
for their hi byte (for each of the thirds), and 00, 20h, 40h,
... E0h for their low byte.
In the attributes area the line start addresses are
5800, 5820, ... 58E0 in the first third, 5900, ... 59E0 in the
second, and 5A00, ... 5AE0 in the last; so the lo byte is
already correct for the attributes area. See DISPLAY AREA for
the display addresses and colours for the attributes.
For the hi byte, the input parameter value is 08 more
than the line start, ie 48h, 50h or 58h; for the corresponding
attribute hi byte 58h, 59h or 5Ah we require to calculate
LET H = 57h + (H - 40h)/8.
The first seven lines of the subroutine perform some
simple "one-byte arithmetic". Ignore the confusing notes!
Transform the equation above:
LET H = H/8 + 57h - 40h/8
= H/8 + 4Fh
= H/8 - 1 + 50h.
Divide H by 8, subtract one and OR the result with
01010000b/50h to produce the required hi byte.
2. The calculation of the number of remaining attribute
bytes starts with the line number B, counted from the bottom
of the screen; it is simply 20h * B, because there are 20h/32d
bytes in each attribute line. Its maximum value is 24 * 32 =
768d, too big for one-byte arithmetic but easily
accommodated in two bytes.
Input parameters: HL holds the line start, the address of
the first pixel byte of the character, plus 800h; on both calls
8 pixel-line loops have been completed. [The notes call this, a
little fancifully, the "ninth pixel byte address" - it is where
the ninth pixel byte would be if there was one]
- B holds the screen line number; one -> 18h/24d, from
the bottom
- C always holds zero.

137
Action: rotate the hi byte of the line start three times
right; divide by eight
- subtract one
- OR with 50h; this is the required hi byte
- save both bytes
- double the line number five times; this is the number
of attribute bytes in B lines.
Exit: RET
Output parameters: DE holds the attribute address
- BC and HL hold the number of attribute bytes.
Called from:
0E19 CL SCR 3
0E4D CL LINE 2

CL CHAN subroutine 0D94


"Housekeeping" after the screen has been cleared. The
main task is to correct the input and output addresses in
channel K; they could have been changed during output of
print control parameters.
Input parameters: none.
Action: call 1601 CHAN OPEN with stream minus 3 to open
channel K
- load the correct output address 09F4 PRINT OUT for the
channel
- clear the carry flag.
_0DA0_CL_CHAN_A: put this address in the two bytes of
the channel
- load the input address 10A8 KEY INPUT
- reverse the carry flag
- if it now shows carry jump back to CL CHAN A to put
the address in the next two bytes of the channel
- (both channel addresses loaded) load the print
position 1721h, the start of the second line from the top of the
lower screen.
Exit: into 0DD9 CL SET, which loads the svs which control
the print position.
Output parameters: BC holds the lower screen print

138
position.
Called from:
0DAF CL ALL
Exit from:
0D8E CLS 3 (0D6B CLS)

CL CHAN A 0DA0 (0D94 CL CHAN)


Jumps from:
auto

CLEAR key (FD) see also commands, functions and


operators,
KEYBOARD SCANNING
The X key in K mode produces the command CLEAR; it
requires one numeric parameter, the setting for 5CB2
RAMTOP, but it doesn't have to be supplied from BASIC - if
none is supplied, the existing value is used.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1ABB P CLEAR causes a jump via 1C0D CLASS 03 - which gets
the parameter if supplied - and 1C16 JUMP C R to the
executive routine 1EAC CLEAR.

CLEAR subroutine 1EAC


Called only from the syntax parameter table 1A7A; the
executive routine of the CLEAR command. Reclaims the
whole of the variables area and GO SUB stack, and clears the
screen. The calculator stack and work space aren't reclaimed -
they should be clear already.
Also the exit routine from the 1EA1 RUN subroutine,
which enters at 1EAF CLEAR RUN (misprinted in 1EA1 RUN);
the RUN command never supplies a new 5CB2 RAMTOP
address.
Better not called from m/c! because the stack pointer
will probably be badly placed.
[The note in the Rems at the end of this subroutine

139
refers to the fact that although statements can be entered in a
BASIC line after a RUN command - and their syntax may be
passed correct - there is no way they can be executed. This
doesn't apply to the CLEAR command.]
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on the code after
CLEAR.
Action: call 1E99 FIND INT2 to get the new 5CB2 RAMTOP
value from BASIC; if none is given, it will use zero.
_1EAF_CLEAR_RUN (entry here from 1EA1 RUN, always
with a
zero value for 5CB2 RAMTOP): if the RAMTOP value isn't zero
jump
on to CLEAR 1
- (zero RAMTOP value) use the value already in 5CB2
RAMTOP.
_1EB7_CLEAR_1: call 19E5 RECLAIM 1 with the first byte to
delete taken from 5C4B VARS and the first to be left alone the
one below that in 5C59 E LINE, the 80-byte at the end of the
variables; this reclaims the variables area
- call 0D6B CLS to clear the screen
- get the value from 5C65 STKEND
- add 32h/50d to allow for extra space on the stack and
machine stack
- subtract the 5CB2 RAMTOP value
- if the 5CB2 RAMTOP value is lower report "RAMTOP no
good"
- get the value from 5CB4 P RAMT
- subtract the RAMTOP from that
- if the 5CB2 RAMTOP value is higher report "RAMTOP no
good".
_1EDC_CLEAR_2 (misprinted): - put the new value in 5CB2
RAMTOP
- get the 1B76 STMT RET return address for this
subroutine off the stack
- get the address below it off the stack; the return
address to 1303 MAIN 4

140
- put the stack end marker 3E at the new RAMTOP
- point the stack pointer at the address below RAMTOP
- push the MAIN 4 address on to the new stack
- keep the present stack pointer address in 5C3D ERR SP.
Exit: effectively a RET, though in order to meddle with
the stack pointer this is done periphrastically by POP DE/EX
DE,HL/JP (HL). This will be to 1B76 STMT RET.
Output parameters: none.
Called from:
1ABB P CLEAR (syntax parameter table)
Rems:
1EA1 RUN exits to perform CLEAR 0

clear display see DISPLAY AREA

clearing editing area see 1097 CLEAR SP

clearing input area see 1097 CLEAR SP

CLEARING THE WHOLE DISPLAY AREA SUBROUTINE see


0DAF CL ALL

CLEAR LINES SUBROUTINE see 0E44 CL LINE

CLEAR PRB subroutine 0EDF


Clears the ZX printer buffer, ie zeroes all its bytes.
Input parameters: none.
Action: get the printer buffer zero address 5B00
- put its zero lo byte in 5C80 PR CC.
_0EE7_PRB_BYTES: put zero in each byte of the buffer; a
DJNZ loop with zero counter makes 256d turns
- zero bit 1 of FLAGS2; "print buffer empty"
- set the print position at 21h, the equivalent of minus
one.
Exit: into 0DD9 CL SET, which sets the print position svs
and flags.
Output parameters: C holds 21h for "printer position".

141
Called from:
0EFD COPY L 1
1219 RAM SET
Exit from:
0EDA COPY END (0EAC COPY, 0ECD COPY BUFF)

CLEAR PRINTER BUFFER SUBROUTINE see 0EDF CLEAR


PRB

CLEAR RUN 1EAF (1EAC CLEAR)


The entry point to 1EAC CLEAR from the 1EA1 RUN
subroutine.
Exit from:
1EA1 RUN (misprinted CLEAR 1)

clear screen see DISPLAY AREA

CLEAR SP subroutine 1097


Reclaims the editing area or the work space, depending
on the setting of FLAGX bit 5 - the editing area if the flag is
set.
Input parameters: none.
Action: call 1190 SET HL, which points HL and DE at the
last and first address of the area signalled by the flag
- call 19E5 RECLAIM 1 to reclaim the area
- point 5C5B K CUR at the first address of the area
- zero 5C41 MODE signalling "not E or G mode".
Exit: RET.
Output parameters: HL is saved, otherwise none.
Called from:
133C MAIN 5
0FA9 ED EDIT
Exit from:
0FA9 ED EDIT (twice)

CLEAR 1 1EB7 (1EAC CLEAR)


Jumps from:

142
1EAF CLEAR RUN

CLEAR 2 1EDC (1EAC CLEAR; misprinted CLEAR 3)


Jumps from:
1EB7 CLEAR 1

click on keyboard see 5C39 PIP

CL LINE subroutine 0E44


Clears the bottom lines of the display. The number L of
lines to be cleared is given on input. Each character position
on screen is cleared by setting all its pixels to zero; then the
attribute bytes are set to the screen colours.
See DISPLAY AREA for the way the display is arranged in
thirds of eight lines each. If you divide L by eight, the
quotient is T, the number of complete thirds to be cleared at
the bottom of the screen, and the remainder is S, the number
of
odd lines to be cleared above them. L is never more than a 5-
bit
binary number; if it is 000XYPQRb, then T = XYb and S =
PQRb;
XYPQR each being either zero or one.
Go eight times through the following loop; on each n'th
turn of the loop all the n'th pixel lines in all L lines are
zeroed. The address in the display area, say M, is initially
that of the line start of the L'th line from the bottom of the
screen, but it gets moved on every time a byte is zeroed:
- AND 000XYPQRb with 00000111b and rotate the result
right three times; this gives PQR00000b, which is 20h/32d
times S, and therefore the number of characters in S lines; see
"one-byte arithmetic" for other examples of this type of
calculation
- put zero in this number of bytes starting at the
address M: this clears all the n'th-row pixel bytes of the S odd
lines; or if S is zero, 256d bytes are zeroed, ie a whole third.
- when all these have been cleared, add 701h to the last

143
M address cleared; this gets the address of the first of the
n'th row of pixel bytes in the next third
Eg the last third-row pixel byte of the first third is
4200h + 8 lines of 20h bytes - 1 = 42FF
add 701h
42FF + 701h = 4A00
the first third-row pixel byte of the second third.
- decrement 000XYPQRb; if PQRb, the number S of odd
lines, isn't zero, this leaves XYb unchanged, but if it is zero
it makes PQRb into 111b and decrements XYb
- AND the result with 11111000b/F8h, which zeroes S and
leaves 000XY000b, decremented if S was zero
- if this isn't zero, jump back to the start of the loop;
S is now zero, so this time it will clear the whole n'th row of
the XYb'th third.
- after one, two or three thirds, XY and so 000XY000b
will be zero; recover the line number L, the same
000XYPQRb as
we started with, and the original M address, and add 100h to
M;
this gives the start address for the next, (n + 1)'th, row of
bytes for the whole L lines. Start all over again, and repeat
the loop for all eight pixel rows.
The rest of the subroutine sets the attribute bytes for
the L lines; this is quite straightforward.
Input parameters: B holds the number L of lines to be
cleared.
Action: call 0E9B CL ADDR to get the start address M
- make a loop counter for eight turns of the loop.
_0E4A_CL_LINE_1: stack L, M and the loop counter
- put L in the A register.
_0E4D_CL_LINE_2: move S = PQRb into the hi bytes; this is
the count of pixel bytes to be zeroed starting from M
- zero the byte at M
- use LDIR with a count of S - 1 to zero the following
bytes; if S is zero, "S - 1" becomes 255d
- add 701h to the last M; the start of the next third

144
- decrement 000XYPQRb; it decrements XYb if PQRb is
zero
- AND the result with 11111000b/F8h
- if the result 000XY000b isn't yet zero, jump back to
CL LINE 2 to zero another third, still in the same turn of the
loop
- (the result is zero; all the n'th-row pixel bytes have
been cleared) recover the start address
- increment it by 100h; it addresses the next row of
pixel bytes
- recover the counter and 000XYPQRb
- decrement the counter
- if it isn't yet zero jump back to CL LINE 1 for the
next turn of the loop
- (all pixel bytes zeroed) call 0E88 CL ATTR, which
returns the address of the first attribute byte to be cleared
and the number to be cleared
- get the attribute bytes from 5C8D ATTR P
- if TV FLAG bit zero is zero jump on to CL LINE 3;
these are the correct attributes for the main screen
- (lower screen) get the attribute bytes from 5C48
BORDCR
_0E80_CL_LINE_3: poke the attributes with the new value
- recover the print position line number
- set the print position column number to 21h.
Exit: RET
Output parameters: BC is the print position; the start of
the first cleared line.
Called from:
0D6E CLS LOWER
0DAF CL ALL
1795 AUTO LIST
Exit from:
0DFE CL SC ALL
0E00 CL SCROLL

CL LINE 1 0E4A (0E44 CL LINE)

145
Jumps from:
0E4D CL LINE 2

CL LINE 2 0E4D (0E44 CL LINE)


Jumps from:
auto

CL LINE 3 0E80 (0E44 CL LINE)


Exit from:
0E4D CL LINE 2 (0E44 CL LINE)

clock see 5C78 FRAMES, time period

CLOSE (hatch) key (D4) see also commands, functions and


operators, KEYBOARD SCANNING, 0284 extended mode key
table (f )
The 5 key in E mode with symbol shift produces the
command CLOSE (hatch). It requires a single numeric
parameter, the stream number.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1B02 P CLOSE causes a jump via 1C82 CLASS 06 - which gets
the parameter - 1C10 CLASS 00 and 1C16 JUMP C R to the
executive routine 16E5 CLOSE.

CLOSE subroutine 16E5 see also channels and streams


Called only by the statement loop from the syntax
parameter table 1A7A; executes the command CLOSE (hatch).
CLOSEs the nominated stream, ie zeroes its stream data.
Only streams 04 -> 15 can be closed from BASIC. Streams -03
->
-01 cannot be handled, because a negative number will
produce an error report from 1E94 FIND INT1 called
(misprinted) from STR DATA when it is called at the start of
the routine. Streams 00
-> 03 cannot be closed, because instead of zeroing the stream

146
bytes the subroutine reloads the standard offsets for channels
K, K, S and P.
Input parameters: none
- the stream number is last value on the calculator
stack.
Action: call 171E STR DATA, which gets the stream number
off the stack and finds the address of the first byte of the
stream data at that stream address
- call 1701 CLOSE 2; this is quite meaningless with the
standard Spectrum
- load a two-byte zero, which will be put in the stream
data if closure is accepted
- add A3E2 to the stream byte address, making a carry if
the address is 5C1E or more; 5C1E is the base address of
stream
04
- if there is carry jump on to CLOSE 1
- (the stream was 00 -> 03, HL is now FFF8, FFFA, FFFC
or FFFE) add 15D4; the result is 15CC, 15CE, 15D0 or 15D2,
which
are the addresses for the four streams in the "initial stream
data" table in the ROM
- get the two-byte offset for the stream data from the
table.
_16FC_CLOSE_1: put either zero, if closure is accepted,
or the data from the initial stream table if not into the stream
bytes.
Exit: RET.
Output parameters: none.
Rems:
1736 OPEN skips channel check if stream closed

closed streams see channels and streams

CLOSE STR 171C (1701 CLOSE 2)


Exit from:
1701 CLOSE 2

147
close stream look-up table 1716 see tables

CLOSE 1 16FC (16E5 CLOSE)


Exit from:
16E5 CLOSE

CLOSE 2 subroutine 1701


In the Spectrum without Interface 1, this routine
achieves nothing.
Input parameters: BC holds the stream data for the
stream; see channels and streams
- HL holds the address of the first byte of the stream
selected by the CLOSE command.
Action: find the appropriate channel data in the channel
area
- check the channel code with the "close stream look-up
table"; there are three entries, all of which lead to the same
jump address 171C. There is no end marker, so if the channel
code was anything but K, S or P the program would crash; but
it
cannot be on the standard Spectrum
- jump on to CLOSE STR.
_171C_CLOSE_STR: clear the machine stack.
Exit: RET.
Output parameters: HL (unchanged) holds the address of
the selected stream; confusingly called the "channel
information
pointer" in the note.
Called from:
16E5 CLOSE

CLS key (FB) see also commands, functions and operators,


KEYBOARD SCANNING
The V key in K mode produces the command CLS, which
accepts no parameters.

148
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1ABE P CLS causes a jump via 1C10 CLASS 00 and 1C16 JUMP C
R to the executive routine 0D6B CLS.

CLS subroutine 0D6B


Called by the statement loop from the syntax parameter
table to execute the command CLS; also called on start-up and
NEW. Clears the whole screen and reforms the input area.
Useful for m/c programs.
Input parameters: none.
Action: call 0DAF CL ALL to clear the screen.
Exit: into 0D6E CLS LOWER, which clears the lower
screen,
makes it two lines, corrects and opens channel K and sets the
print position at the top left of the cleared screen.
Output parameters: none.
Called from:
1219 RAM SET
1EB7 CLEAR 1

CL SC ALL subroutine 0DFE


Entry point for 0E00 CL SCROLL after the "scroll?"
prompt. Scrolls 23d lines up by one line, all but the bottom
line with the "scroll?" prompt in it .
Called from:
0CD2 PO SCR 3

CL SCROLL subroutine 0E00


Scrolls the screen once, ie moves a given number L of
screen lines, counting from one at the bottom to 18h/24d at
the top, up by eight pixel lines. See scrolling, and for the
screen layout see DISPLAY AREA.
This involves different treatment
- for the seven "odd" lines of each third of the display
after the first; all the top bytes of all the characters are

149
moved back 20h places, ie up one line, then all the second-
from- the-top bytes, etc down to the eighth byte;
- for the top "first" lines of the second and the last
third; the bytes of the first 20h characters at the top of each
third boundary are moved back 720h bytes instead of 20h, to
put them in the last line of the previous third;
- for the "deleted" line above the top line to be
scrolled, line L - 1; it disappears, overwritten by the next
line.
If L is the top line on the screen, its pixel bytes are
"copied to the ROM"; the display area is the first section of
RAM. Nothing can actually be copied to the ROM, so any pixel
rows which are scrolled off the screen are simply thrown away
The start address of the top line L is found by calling
0E9B CL ADDR; call it M.
The main loop of the subroutine first moves the first
row of pixels in all the lines to be scrolled, starting at M,
then the second row starting at M + 100h, etc - eight rows in
all in eight turns of the loop.
The line number L is used in a somewhat devious way as
a counter, quite similar to its use in OE44 CL LINE. L has at
most five binary digits 000XYPQRb, where XYb is the number
of complete thirds to be scrolled and PQRb is the number of
lines to be scrolled in the incomplete top third - or zero if all
eight lines of this third are to be scrolled.
- AND 00000111b/07h with L, giving zero for a "first"
line - but L is kept unchanged
- the first part of the loop deals with "first" lines: it
moves 20h/32d bytes back 720h bytes, overwriting those in
the last line of the previous third, and decrements L. Since L
was the "first" line of a third, PQRb was zero; thus PQRb goes
to
111b/07 and XYb gets decremented
- the second part of the loop ANDs L with 00000111b/7h,
giving the number of "odd" lines remaining to be scrolled in

150
this third, and multiplies the result by 20h/32d for the number
of characters in these lines by rotating 00000PQRb three
times
right to PQR00000b; see "one-byte arithmetic" for similar
examples. Then it moves this number of pixel bytes back by
20h/32d
- the third part of the loop moves the pixel address M on
by 700h bytes. M ended up as one after the last byte moved,
which was the last of that pixel row in a third. If it was say the
fourth pixel row of the second third, its last byte was at 4BFF,
and M was 4C00, so adding 700h makes it 5300 - which is as it
should be, the first fourth-row byte of the last third.
- AND L with 11111000b, which will give zero if XYb is
zero, ie if the third just completed was the last; if it wasn't
the last, then jump back to the first part of the loop - but
still in the same turn of the loop.
If it was the last, all the first-row pixels have been
moved; the original start address M is recovered from the
machine stack and its hi byte incremented to give the start
address of the second row pixels, L is reset to its starting
000XYPQRb value, and the whole loop is gone through again.
And then again for the third row, and so on till eight loops are
complete.
Scrolling the attributes is a much simpler operation,
since the attributes for the whole screen are stored
consecutively and it is just a matter of moving as many as
necessary back 20h bytes.
[The attributes for the top line on the screen, if that
is scrolled, are actually moved into the display area, so that
they momentarily become visible as pixels in the last pixel
row on the display; but they are cleared so quickly this is
almost impossible to spot. White PAPER and black INK,
00111000b, produces a row of 32d thin dashes.]
The entry point 0DFE CL SC ALL is used after the
"scroll?" prompt to scroll the whole screen.
Input parameters: B holds the line number L of the top
line to be scrolled; this is also the number of lines to be

151
scrolled.
Action: call 0E9B CL ADDR to get the start address M
- make a loop counter of eight for the eight pixel rows.
_0E05_CL_SCR_1: stack L, M and the loop counter
- AND L with 00000111b/07; if line L is a "first" line
this gives zero
- restore L
- if the flag shows NZ jump on to CL SCR 3; L is an
"odd" line.
_0E0D_CL_SCR_2 ("first" lines): add F8E0 to M; the same
as subtracting 720h, making an address to which the pixels
will be copied
- make a copying counter 20h
- decrement the L counter
- copy 20h/32d pixel bytes back to the copy addresses.
_0E19_CL_SCR_3 ("odd" lines): add FFE0 to M; the same as
subtracting 20h, making an address to which the pixels will be
copied
- AND the L counter with 00000111b/07 and rotate the
result three times right; making PQR00000b = 20h * PQRb,
the number of remaining bytes in this third
- move back this number of pixel bytes
- increment the resulting address M by 700h
- AND the L counter with 11111000b/F8h; is XYb now zero?
- if the result isn't zero jump back to CL SCR 2; there
are more thirds to be treated in this turn of the loop
- (bottom third has been scrolled) recover M from the
stack
- add 100h for the next pixel row
- recover the counters
- decrement the loop counter
- if it isn't yet zero jump back to CL SCR 1 for another
turn of the loop
- (all eight pixel rows have been moved) call 0E88 CL
ATTR; it returns HL with the first address in the attribute area
and BC with the number of attributes to be changed
- add FFE0 to the attribute address; it sets an address

152
20h bytes above
- move all the attribute bytes back by 20h bytes
- load a counter with one to clear one bottom line.
Exit: into 0E44 CL LINE, which clears the bottom line -
it was copied into the next one up, but nothing was copied
into it except the attribute row.
Output parameters: B holds 01, to clear the last line.
Called from:
0D2D PO SCR 4B

CL SCR 1 0E05 (0E00 CL SCROLL)


Jumps from:
0E19 CL SCR 3

CL SCR 2 0E0D (0E00 CL SCROLL)


Jumps from:
0E19 CL SCR 3

CL SCR 3 0E19 (0E00 CL SCROLL)


Jumps from:
0E05 CL SCR 1

CL SET subroutine 0DD9 see also DISPLAY AREA


Finds the start address in the display area or in the
printer buffer corresponding to a given print position on
screen or column number.
Input parameters: BC holds the print position line/column
number.
Action: get the start address of the print buffer 5B00
- if bit 1 of FLAGS is set jump on to CL SET 2; the ZX
printer is being used, column numbers are the only ones to be
considered
- if bit zero of TV FLAG is set jump on to CL SET 1; the
upper screen is being used
- (lower screen) add the value from 5C6B DF SZ to the
print position line number and subtract 18h; line 18h means
the top line of the lower display, which holds DF SZ lines, so L

153
- 18h + DF SZ gives the line number counting from the bottom
of the screen.
_0DEE_CL_SET_1: call 0E9B CL ADDR to find the display
address of the line start of the given line number.
_0DF4_CL_SET_2: subtract the print position column
number
from 21h; this converts it to a tab value on the line
- add the result either to the line start address found
in CL SET 1, or to the printer buffer start address.
Exit: into 0ADC PO STORE which loads the print position
svs with the appropriate values.
Output parameters: HL holds the required address in the
printer buffer or display area
- BC still has the print position.
Called from:
0D2D PO SCR 4B
117E ED C END
20AD INPUT 2
2161 IN VAR 4
Exit from:
0A3A PO BACK 3
0A4F PO ENTER
0ABF PO AT SET
0C55 PO SCR
0D6B CLS
0DA0 CL CHAN A
0DAF CL ALL
0EE7 PRB BYTES

CL SET 1 0DEE (0DD9 CL SET)


Jumps from:
0DD9 CL SET

CL SET 2 0DF4 (0DD9 CL SET)


Jumps from:
0DD9 CL SET

154
CLS LOWER subroutine 0D6E
Clear and reform the lower screen.
Input parameters: none.
Action: zero bit 5 of TV FLAG; "don't clear lower screen
on next keystroke"
- set bit zero; "use lower screen"
- call 04D4 TEMPS to use the permanent colours; they
will be got from 5C48 BORDCR
- get the value from 5C6B DF SZ as counter; the number
of lines in the lower screen
- call 0E44 CL LINE with this counter; it blanks all the
characters in the lower screen, but if DF SZ was more than
two it will put the wrong attributes in lines above the bottom
two
- get 5AC0; a pointer to the first attribute byte of the
bottom two lines and so the first whose attribute is_not to be
changed
- get the upper screen attributes from 5C5D ATTR P
- reduce the line count in B by one; CL LINE has left B
holding the number of lines cleared
- jump on to CLS 3; this is a DJNZ, which immediately
jumps back to CLS 1 decrementing the line count again, so
now it is the number of lines less_two.
_0D87_CLS_1: make a byte counter 20h/32d for the
attribute bytes of the line.
_0D89_CLS_2: move the pointer back
- put the ATTR P attribute in the byte at the pointer
- decrement the byte counter and jump back to CLS 2 if
it isn't yet zero.
_0D8E_CLS_3: decrement the line counter and jump back
to CLS 1 if it isn't yet zero
- (all attributes corrected) load two into 5C6B DF SZ;
the size of the lower screen.
Exit: into 0D94 CL CHAN, which resets channel K and
makes the print position the first in the lower screen, and
then to
0DD9 CL SET to set the print position svs.

155
Output parameters: none.
Called from:
10A8 KEY INPUT
12CF MAIN 3
1313 MAIN 6
2089 INPUT
20AD INPUT 2
Exit from:
0D6B CLS

CLS 1 0D87 (0D6E CLS LOWER)


Jumps from:
0D8E CLS 3

CLS 2 0D89 (0D6E CLS LOWER)


Jumps from:
auto

CLS 3 0D8E (0D6E CLS LOWER)


Jumps from:
0D6E CLS LOWER

CL 09 1 1CD6 (1CBE CLASS 09)


Jumps from:
1CBE CLASS 09

C-mode see 5C41 MODE

CO CHANGE subroutine 226C see also colours, masks


Used to set bits in the colour system variables 5C8F
ATTR T, 5C90 MASK T and 5C91 P FLAG.
The mask in B has zero "holes" in the bits which the
subroutine is to set or zero in (HL). The setting byte in A
contains the required settings of these bits - the other bits in A
are immaterial. Eg:
B = 11010111
A = 11001011

156
will set bit 3 and zero bit 5 of (HL), leaving the others
undisturbed.
The subroutine also increments HL, which is convenient
for dealing with the colour system variables.
Can be used in m/c to change any bits in any byte.
Input parameters: HL holds the address of the byte to be
changed
- B holds the mask
- A holds the setting byte.
Action: XOR/AND/XOR (HL) with A using B as mask
- increment HL
- copy the mask to A.
Exit: RET.
Output parameters: (HL) is changed as required
- A and B now both hold the "mask"
- HL is incremented by 1.
Called by:
2258 CO TEMP B (twice)
2287 CO TEMP E
Exit from:
2228 CO TEMP 6
2287 CO TEMP E

CODE key (AF) see also commands, functions and


operators,
KEYBOARD SCANNING, 0246 extended mode table (b)
The I key in E mode without shift produces the dual-
purpose token CODE.
As a function, it requires one string operand X$, and
the value of the function is the character code of X$(1), or
zero if X$ = "".
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code AF first to 00, then to
DC,
and adds the priority 10h/16d. Code and priority 10DC are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following CODE is evaluated.

157
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from DC to 1C, the calculator
offset for 3669 code.
However, if CODE is used as an "adverb" in a SAVE/LOAD/
VERIFY command, it isn't a function and isn't read by
SCANNING;
this use of CODE is handled by the 0605 SAVE ETC subroutine
at
06C3 SA CODE.
0710 SA TYPE 1 type in header is 3
07AD LD CH PA loading handled in 07CB VR CONTRL
07CB VR CONTRL used to load CODE
26DF S NEGATE handles functions CODE to NOT

code see also channels and streams, character code,


colours, control codes, KEYBOARD SCANNING

code subroutine 3669


Called from 0028 FP CALC by offset 1C; the executive
routine of the CODE function. Finds the character code of the
first character of a string.
Not called otherwise from ROM; can be called from m/c
either by RST 28h or direct, but there are usually easier ways!
Input parameters: none
- the string parameters must be the last value on the
calculator stack.
Action: call 2BF1 STK FETCH to put the string parameters
into the AEDCB registers
- if the length BC is zero jump on to STK CODE with zero
- (non-null) get the code from the start address DE.
_3671_STK_CODE: exit.
Exit: into 2D28 STACK A, which puts the code or zero on
the calculator stack as a small integer.
Output parameters: code in A.

COLLECT A LINE NUMBER SUBROUTINE see 1695 LINE NO

158
COLLECT CHARACTER RESTART see 0018 GET CHAR

COLLECT NEXT CHARACTER RESTART see 0020 NEXT


CHAR

colon see ":" (code 3A) after end of alphabet

colour control codes/parameters, colour item commands


see colours

COLOUR ITEM ROUTINES see 21E2 CO TEMP 2

colours
Screen colours are controlled by the_attributes.
The_attributes_area, 5800 to 5AFF inclusive, one
attribute byte for each of the 300h/768d character positions
on the screen, takes up much less memory than the display
area, which has eight bytes for each character. Logically
enough, the first 20h/32d attribute bytes control the colours
of the first line of characters in the display and so on.
In each attribute byte the bits represent fbpapink:
bit 7 is on for FLASH
bit 6 is on for BRIGHT
bits 5 to 3 hold the PAPER (000 BLACK to 111b/7d WHITE)
bits 2 to 0 hold the INK (000 BLACK to 111b/7d WHITE)
The attributes are set from the system variables 5C8D
ATTR P, 5C8F ATTR T, and 5C48 BORDCR.
The_permanent_colours, which are used to colour the upper
part of the screen as a whole, are kept in ATTR P, and
the_temporary_colours, those to be used for the next
character printed, in ATTR T. The permanent attributes for
the lower screen are are kept separately in
BORDCR, with the same PAPER colour as the border.
The attributes don't control INVERSE and OVER which
are signalled by flags in 5C91 P FLAG, and the border, apart
from the lower screen, is coloured by quite different means,
see 2294 BORDER.

159
Whenever a character is printed, the corresponding
attribute byte is loaded from ATTR T with the temporary
colours, by 0BDB PO ATTR.
When INK, PAPER, FLASH, BRIGHT, INVERSE, OVER - the
_INK_to_OVER tokens - are used as commands, ie opening a
BASIC statement on their own, the executive routine 1C96
CLASS 07 (PERMS) changes both the permanent attributes in
ATTR P and the temporary attributes in ATTR T.
However, if a PRINT command is followed by an INK to
OVER item - these aren't now commands
but_embedded_colour_items or_colour_control_items - the call
to 21F2 CO TEMP 3 from 2024
PR ITEM 3 prints out the necessary_control_codes, see below;
these are executed by 0A6D PO TV 2, which exits from 0A87
PO
CONT to 2211 CO TEMP 5, where a mask is prepared, and
finally the temporary colours are set in ATTR T by 2287 CO
CHANGE.
If the PRINT command doesn't specify any of INK to
OVER,
then it is the permanent colour values in ATTR P which are to
be used, so each time a PRINT command is executed (1FCF
PRINT 1) a
call is made to 0D4D TEMPS to put the ATTR P values in ATTR
T;
if they aren't changed by an embedded colour item, these are
the values which will be implemented in printing the
character.
Some of the "embedded print items" can be substituted
by keying codes directly, all in E mode except INVERSE VIDEO
and TRUE VIDEO - you can even colour code your BASIC in
this way,
see variables:
INK by keying the INK colour with caps shift
PAPER by keying the PAPER colour without shift
FLASH key 9 (on) or key 8 (off ) with caps shift
BRIGHT key 9 (on) or key 8 (off ) without shift

160
INVERSE VIDEO and TRUE VIDEO are separate keys on
later models, but on the older Spectrum they can be got by
keying 4 (on) or 3 (off ) in L mode with caps shift - and this
still works
with the new models. It isn't possible to key in OVER (1Bh) in
this way.
These keystrokes are coded by 0367 K DIGIT with
character codes as follows:
FLASH 00 off, 01 on
BRIGHT 02 off, 03 on
TRUE VIDEO 04
INVERSE VIDEO 05
PAPER 10h BLACK to 17h WHITE
INK 18h BLACK to 1Fh WHITE
However they are recoded by 10A8 KEY INPUT into two
_control_codes each, the same as those produced by the
embedded colour items described above:

off on
INK 10h/16d colour no
PAPER 11h/17d colour no
FLASH 12h/18d 0 1
BRIGHT 13h/19d 0 1
INVERSE 14h/20d 0 1
(OVER 15h/21d 0 1)

All these, including OVER, can be incorporated in


strings by concatenating their control codes as CHR$ X + CHR$
Y,
or of course used in m/c programs. And the colour 8 "leave
the present attribute as it is" can be used with all the controls
except INVERSE and OVER; colour 9 "black or white
contrasting" only with PAPER and INK.
007D SKIP OVER skips parameters of INK to OVER
0367 K DIGIT keys 0-9 in E mode control INK to OVER
0382 K 8 & 9 checks for BRIGHT and FLASH
06A0 SA SCR$ the attributes are saved by SAVE SCREEN$
09F4 PRINT OUT A may hold INK to OVER on entry

161
0A11 control character table incl INK to OVER
0A6D PO TV 2 INK to OVER require one operand
0A7A PO 1 OPER entry to PO TV 2 for colour controls
0A7D PO TV 1 control saved in TV DATA
0A80 PO CHANGE channel output address changed to
fetch
parameter
0A87 PO CONT fetch parameter
0BC1 PR ALL 5 ready to set attribute byte
0BDB PO ATTR attribute set by ATTR T
0BFA PO ATTR 1 contrast with PAPER for INK 9
0C08 PO ATTR 2 attribute put on screen
0CD2 PO SCR 3 finds attribute position
0CF0 PO SCR 3A two lines in lower screen filled with
attributes
0D02 PO SCR 4 lower screen cleared with permanent cols
0D6B CLS all attributes set with ATTR P
0D6E CLS LOWER lower screen cleared with border
colours
0D89 CLS 2 attributes set backwards from line end
0E19 CL SCR 3 scroll all attributes up 20h/32d places
0E4D CL LINE 2 finds attribute posn and gets attr value
0E80 CL LINE 3 copy BC attributes
0E88 CL ATTR finds attribute address for given position
0F38 ED LOOP editing INK to TAB (AT and TAB in error)
0F6C ED CONTR gets parameter code
1031 ED EDGE cursor skips over parameters
103E ED EDGE 1 cursor left (AT, TAB included in error)
1051 ED EDGE 2 control code deleted by DELETE
10A8 KEY INPUT handles colour control parameters
10FA KEY CONTR handled by, except FLASH, BRIGHT,
INVERSE
1105 KEY DATA returns code & parameter in two passes
111D ED COPY permanent colours to print in lower screen
1150 ED BLANK spaces in permanent paper colour
1219 RAM SET PAPER 7 INK 0, FLASH/BRIGHT 0, on start-
up

162
1937 OUT CHAR handles print controls in listing BASIC
1CBE CLASS 09 default colours "8" set up (DRAW etc)
1C96 CLASS 07 (PERMS) executes colour item commands
1CD6 CL 09 1 look for temporary colours (DRAW etc)
2024 PR ITEM 3 outputs INK to OVER and one parameter
2089 INPUT can be embedded items in INPUT statement
20C1 IN ITEM 1 deals with embedded items
21E1 CO TEMP 1 handles embedded items, ATT P etc
21E2 CO TEMP 2 check for embedded item
21F2 CO TEMP 3 parameter read
21FC CO TEMP 4 outputs control code and parameter
2211 CO TEMP 5 alters attribute svs as needed
2211 CO TEMP 5 makes mask for INVERSE/OVER
2228 CO TEMP 6 deals with INVERSE/OVER
2234 CO TEMP 7 deals with PAPER/INK
223E CO TEMP 8 only allows PAPER/INK 0 to 9
2244 REPORT K invalid colour
2246 CO TEMP 9 handles colours 8 and 9
2258 CO TEMP B adjusts mask for PAPER/INK 8 or 9
2273 CO TEMP C deals with FLASH/BRIGHT
2287 CO TEMP E FLASH/BRIGHT 8
2580 S ATTR S executes ATTR (x,y)
28AB FN SKPOVR skips all control codes
295A SFA LOOP skips all control codes
2B0B L EACH CH ignores colour codes
2B0C L NO SP step over codes following INK to OVER

colour system variables see 5C8D ATTR P, 5C8F ATTR T,


colours, 5C91 P FLAG

column number see DISPLAY AREA

comma see "," (code 2C, at end of alphabet), control


characters (PRINT comma, code 06)

command (BASIC), command classes see 1B8A LINE RUN,


1C10

163
CLASS 00 to 1CDB CLASS 0B, commands, functions and
operators

COMMAND CLASSES 01, 02 AND 04 see 1C1F CLASS 01

command class tables 1C01 see 1B8A LINE RUN, commands,


functions and operators, tables

command codes, command routines


Ie the codes and routines which execute BASIC
commands.
A reference to the appropriate routine will be found in this
index under the "key" of each command; see also 1B8A LINE
RUN.

commands, functions, and operators see also 1B8A LINE


RUN,
24FB SCANNING
Keys CE DEF FN -> FF COPY are all_commands; they can
start a BASIC line, they aren't evaluated as expressions - ie
read as numbers or values of string variables - and all the
commonly used ones are read as the value of their keys in K-
mode, indeed all the letter keys in K-mode produce
commands.
Commands are interpreted and executed by 1B28 STMT LOOP
(see
under 1B8A LINE RUN) through the tables at 1A48 and
following.
BASIC commands are classified according to the operands
which they require; 1B55 GET PARAM (see under 1B28 LINE
RUN), in
conjunction with the 1A7A syntax parameter table and the
CLASS
00-0B routines ensure that the parameters are fetched and
stored
suitably before the jump to the executive routine. There is a
list of the classes on page 86 of the notes, but it doesn't give

164
the subroutine addresses; they are in the table at 1C01:
1C10 CLASS 00 no more operands
1C1F CLASS 01 one variable
1C4E CLASS 02 one expression
1C0D CLASS 03 numeric expression, default zero
1C6C CLASS 04 one-character variable
1C11 CLASS 05 optional operand or operands
1C82 CLASS 06 numeric expression
1C96 CLASS 07 handles colour items
1C7A CLASS 08 two numeric expressions with comma
1CBE CLASS 09 same with optional colour items
1C8C CLASS 0A string expression
1CDB CLASS 0B handles cassette routines
Keys A5 RND -> C4 BIN except for AC AT and AD TAB are
_functions, which in most cases are followed by_arguments;
the
arguments may be numbers or strings, depending on the
function.
The functions are evaluated through 24FB SCANNING and the
following routines into a numeric or string value. A function
together with its arguments if any is an_expression, treated
syntactically as a number or a string.
Keys C5 OR -> C9 <> , and the arithmetic symbols 28 ( to
47 / and 3C < to 3E > are_binary_operators, which resemble
functions in being evaluated to a numeric or string value, but
differ from them in coming_between their operands, ie the
number
or string produced depends on what precedes as well as what
follows. "Operand" means much the same as "argument"; the
notes usually call them operands for operators and arguments
for functions, but the distinction isn't a real one - indeed, in
maths, operators_are functions. The word "operations" is
sometimes used in the notes to mean "operators and
functions".
Operators therefore must have a priority ranking. They
are mostly handled by 2713 S CONT 3 and the following
routines:

165
the priority table is at 2780. NOT is described as a "unary
operator", and "+" and "-" can also be used as unary operators;
there is no distinction between unary operators and
functions,
except that all functions have maximum priority and unary
operators don't.
For the other tokens - AT, TAB, LINE, THEN, TO, STEP -
see under the "key" entry in this index
Alphabetical list of tokens and symbols:

token/ |code |priority, |arguments


symbol | |Command, |command |
| |Function, |class| |
| |Operator, | |literal |
| |Separator | | | |
| | |main | | |executive SR |
| | |key | | | |
| | | |mode | | | |
---------+--+-+-+------------+--+--+--------------+------------
+ |2B|O|K|K/L sym sh |P6|0F|3014 addition |two num
| |O| | | |17|359C strs-add |two str
| |F| | | | |25AF S U PLUS |num
- |2D|O|J|K/L sym sh |P6|03|300F subtract |two num
unary - |2D|O|J|K/L sym sh |P9|1B|346E negate |num
* |2A|O|B|K/L sym sh |P8|04|30CA multiply |two num
/ |2F|O|V|K/L sym sh |P8|05|31AF division |two num
up arrow |5E|O|H|K/L sym sh |PA|06|3851 to-power |two num
= |3D|O|L|K/L sym sh |P5|0E|353B nos-eql |two num
| | | | | |16|353B str-eql |two str
> |3E|O|T|K/L sym sh |P5|0C| |two num
| | | | | |14|353B str-eql |two str
< |3C|O|R|K/L sym sh |P5|0D| |two num
| | | | | |15|353B str-eql |two str
<= |C7|O|Q|K/L sym sh |P5|09| |two num
| | | | | |11|353B str-eql |two str
>= |C8|O|E|K/L sym sh |P5|0A| |two num
| | | | | |12|353B str-eql |two str
<> |C9|O|W|K/L sym sh |P5|0B| |two num
| | | | | |13|353B str-eql |two str
. |2E|F|M|K/L sym sh | | |268D S DECIMAL|num
, |2C|S|N|K/L sym sh | | | |
|06| |N|K/L sym sh | | |0AF5 PO COMMA |two any
" |22|F|P|K/L sym sh | | |25B3 S QUOTE |any
( |28|F|8|K/L sym sh | | |25EB S BRACKET|any
ABS |BD|F|G|E no shift | |2A|346A abs |num
ACS |B6|F|W|E either sh | |23|3843 acs |num
AND |C6|O|Y|K/L sym sh |P3|08|3524 no-&-no |two num
| | | | |P3|10|32F7 str-&-no |str & num
ASN |B5|F|Q|E either sh | |22|3833 asn |num
AT |AC|-|I|K/L sym sh | | |0A75 PO 2 OPER|two num
ATN |B7|F|E|E either sh | |24|37E2 atn |num
ATTR |AB|F|L|E either sh | | |2672 S ATTR |two num
BEEP |D7|C|Z|E either sh |C8| |03F8 BEEP |two num

166
BIN |C4|F|B|E no shift | | |268D S DECIMAL|bin digits
BORDER |E7|C|B|K |C6| |2294 BORDER |num
BRIGHT |DC|C|B|E either sh |C7| |1C96 PERMS |num
CAT |CF|C|9|E sym shift |CA| |1793 CAT ETC |str
CIRCLE |D8|C|H|E either sh |C9| |2320 CIRCLE |three num
CHR$ |C2|F|U|E no shift | |2F|35C9 chr$ |num
CLEAR |FD|C|X|K |C3| |1EAC CLEAR |none or num
CLOSE # |D4|C|5|E sym shift |C6| |16E5 CLOSE |num
CLS |FB|C|V|K |C0| |0D6B CLS |none
CODE |AF|F|I|E no shift | |1C|3669 code |str
CONTINUE |E8|C|C|K |C0| |1E5F CONTINUE |none
COPY |FF|C|Z|K |C0| |0EAC COPY |none
COS |B3|F|W|E no shift | |20|37AA cos |num
DATA |E4|C|D|E no shift |C5| |1E27 DATA |strs or nums
DEF FN |CE|C|1|E sym shift |C5| |1F60 DEF FN |str or num
DIM |E9|C|D|K |C5| |2C02 DIM |num or str
DRAW |FC|C|W|K |C9| |2382 DRAW |2/3 num
ERASE |D2|C|7|E sym shift |CA| |1793 CAT ETC |str
EXP |B9|F|X|E no shift | |26|36C4 exp |num
FLASH |DB|C|V|E either sh |C7| |1C96 PERMS |num
FN |A8|F|2|E sym shift | | |25F5 S FN |str or num
FOR |EB|C|F|K |C4| |1D03 FOR |num
FORMAT |D0|C|0|E sym shift |CA| |1793 CAT ETC |str
GO SUB |ED|C|H|K |C6| |1EED GO SUB |num
GO TO |EC|C|G|K |C6| |1E67 GO TO |num
IF |FA|C|U|K |C6| |1CF0 IF |num
IN |BF|F|I|E either sh | |2C|34A5 in |num
INK |D9|C|X|E either sh |C7| |1C96 PERMS |num
INKEY$ |A6|F|N|E no shift | | |2634 S INKEY$ |none
INPUT |EE|C|I|K |C5| |2089 INPUT |num or str
INT |BA|F|R|E no shift | |27|36AF int |num
---------+--+-+-+------------+--+--+--------------+------------

token/ |code |priority, |arguments


symbol | |Command, |command |
| |Function, |class| |
| |Operator, | |literal |
| |Separator | | | |
| | |main | | |executive SR |
| | |key | | | |
| | | |mode | | | |
---------+--+-+-+------------+--+--+--------------+------------
INVERSE |DD|C|M|E either sh |C7| |1C96 PERMS |num
LEN |B1|F|K|E no shift | |1E|3674 len |str
LET |F1|C|L|K |C1| |2AFF LET |nu or str
LINE |CA| |3|E sym shift | | | |
LIST |F0|C|K|K |C5| |17F9 LIST |none or num
LLIST |E1|C|V|E no shift |C5| |17F5 LLIST |none or num
LN |B8|F|Z|E no shift | |25|3713 ln |num
LOAD |EF|C|J|K |CB| |0605 SAVE ETC |str
LPRINT |E0|C|C|E no shift |C5| |1FC9 LPRINT |any
MERGE |D5|C|Y|E either sh |CB| |0605 SAVE ETC |str
MOVE |D1|C|6|E sym shift |CA| |1793 CAT ETC |str
NEW |E6|C|A|K |C0| |1187 NEW |none
NEXT |F3|C|N|K |C4| |1DAB NEXT |num
NOT |C3|F|S|K/L sym sh | |30|3501 not |num
OPEN # |D3|C|4|E sym shift |C6| |1736 OPEN |num & str
OR |C5|O|U|K/L sym sh |P2|07|351B or |two num
OUT |DF|C|O|E either sh |C8| |1E7A OUT |two num

167
OVER |DE|C|N|E either sh |C7| |1C96 PERMS |num
PAPER |DA|C|C|E either sh |C7| |1C96 PERMS |num
PAUSE |F2|C|M|K |C6| |1F3A PAUSE |num
PEEK |BE|F|O|E no shift | |2B|34AC peek |num
PI |A7|F|M|E no shift | | |2627 S PI |none
PLOT |F6|C|Q|K |C9| |22DC PLOT |two num
POINT |A9|F|8|E sym shift | | |267B S POINT |two num
POKE |F4|C|O|K |C8| |1E80 POKE |two num
PRINT |F5|C|P|K |C5| |1FCD PRINT |num or str
RANDOMIZE|F9|C|T|K |C3| |1E4F RANDOMIZE|none or num
READ |E3|C|A|E no shift |C5| |1DED READ |strs or nums
REM |EA|C|E|K |C5| |1BB2 REM |any
RESTORE |E5|C|S|E no shift |C3| |1E24 RESTORE |num
RETURN |FE|C|Y|K |C0| |1F23 RETURN |none
RND |A5|F|T|E no shift | | |25F8 S RND |num
RUN |F7|C|R|K |C3| |1EA1 RUN |none or num
SAVE |F8|C|S|K |CB| |0605 SAVE ETC |str
SCREEN$ |AA|F|K|E either sh |- | |2535 S SCRN$ S|two num
SGN |BC|F|F|E no shift | |29|3492 sgn |num
SIN |B2|F|Q|E no shift | |1F|37B5 sin |num
SQR |BB|F|H|E no shift | |28|384A sqr |num
STEP |CD| |D|K/L sym sh | | |1D03 FOR |num
STOP |E2|C|A|K/L sym sh |C0| |1CEE STOP |none
STR$ |C1|F|Y|E no shift | |2E|361F str$ |num
TAB |AD| |P|E no shift | | |0A75 PO 2 OPER|two num
TAN |B4|F|E|E no shift | |21|37DA tan |num
THEN |CB|S|G|K/L sym sh | | | |
TO |CC| |F|K/L sym sh | | |1D03 FOR |num
USR |C0|F|L|E no shift | |2D|34B3 usr-no |num
| | | | | |19|34BC usr-$ |str
VAL |B0|F|J|E no shift | |1D|35DE val |str
VAL$ |AE|F|J|E either sh | |18|35DE val$ |str
VERIFY |D6|C|R|E either sh |CB| |0605 SAVE ETC |str
---------+--+-+--------------+--+--+--------------+------------

COMPARE LINE NUMBERS SUBROUTINE see 1980 CP LINES

COMPARISON OPERATIONS see 353B no-l-eql

compiling BASIC see BASIC INTERPRETER

'complete' simple strings see 2AFF LET

CONTINUE key (E8) see also commands, functions and


operators, KEYBOARD SCANNING
The C key in K mode produces the command CONTINUE,
which accepts no parameters.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
168
1AB8 P CONT causes a jump via 1C10 CLASS 00 and 1C16 JUMP
C R to the executive routine 1E5F CONTINUE.

CONTINUE subroutine 1E5F


Called only by the statement loop through the syntax
parameter table 1A7A to execute the CONTINUE command.
Jumps to the last recorded line and statement numbers.
Input parameters: none
Action: get the line and statement numbers in svs 5C6E
OLDPPC and 5C70 OSPCC; see 1376 MAIN 7 under 12A2 MAIN
EXEC for
the way these are prepared for CONTINUE.
Exit: into 1E73 GO TO 2, which puts the numbers in svs
5C42 NEWPPC and 5C44 NSPPC and returns into the
statement loop
at 1B76 STMT RET.
Output parameters: none.

control characters (control codes/keys) see also colours,


editing
The expressions "control characters" and "control codes/
keys" are used in the notes to refer to three different kinds of
code:
1. The_print_items which can follow a PRINT command:
they include
the_"INK_to_OVER" colour controls D9 INK, DA PAPER, DB
FLASH, DC BRIGHT, DD INVERSE, DE OVER, which are
discussed separately under "colours"
the_position_controllers AC AT and AD TAB
the controls 06_PRINT_comma, 3B_semicolon and 27h ';
these are also position controllers.
INK to INVERSE can all also be used as free-standing
BASIC commands, but OVER and the position controllers
cannot.
2. The_embedded_print_items: the digit keys in extended
mode can produce the eight paper and ink colours, FLASH
and BRIGHT. They put a control code in the BASIC line

169
followed by a colour or on/off parameter: the codes are 10h
INK, 11h PAPER,
12h FLASH and 13h BRIGHT. Code 06 PRINT comma can be
input similarly, by the fairly well-known trick of keying E-
mode 6 and then DELETE: E-mode 6 is the colour control for
yellow paper, and puts the control codes 11h, 06 in the BASIC
line. DELETE deletes the 11h leaving the 06 isolated, which
will now be interpreted as a PRINT comma.
These inputs work directly on the BASIC line which
includes them, but if they are in a string they are incorporated
in the string whenever it is used, and to some extent the same
is true of variable names, see the index entry "variables".
The 8 and 9 colours, INVERSE, OVER, AT, TAB and the
newline cannot be input direct from the keyboard in this way,
but they can be input by other channels or of course used
from machine code.
3. The_editing_controls, 07 EDIT, 08 -> 0B cursor shifts,
0C DELETE, 0D ENTER: none of these can be used as BASIC
commands or can follow a PRINT command. One of them, 08
cursor left, can be concatenated in strings, sometimes
usefully, but not the others.
AT and TAB each require two numeric_operands or
_parameters. The second parameter of TAB isn't put in the
BASIC
- the single BASIC parameter is made into two bytes by the
ROM -
but must be incorporated in string concatenations or m/c
printing. The colour items have one parameter each; PRINT
comma, semicolon and 27h ' of course have none.
The implementation of the colour controls in all their
different forms is explained in the index under colours.
The implementation of the position controllers is in the
PRINT command routine 1FCD PRINT, by calls from 1FE5
PRINT 3 to 204E PR POSN 1 and 1FFC PR ITEM 1.
204E PR POSN 1 outputs codes to 0010 PRINT A 1 as
follows:
3B semicolon: no output - this just means "no tab"

170
2C comma: outputs 06 PRINT comma
27 ': outputs 0D newline
1FFC PR ITEM 1 outputs codes to 0010 PRINT A 1 as
follows:
AC,X,Y (AT X,Y): 16h,X,Y; 16h is the control code for AT
AD,X (TAB X): 17h,0,Y; 17h is the control code for TAB, Y
is X modulo 32d
This is similar to the implementation of the colour items.
Like the colour items, the position control codes can be
concatenated into strings:
10 LET h$="hello"+CHR$ 13+"there"
20 PRINT h$
will print
hello
there
Code 13d is the newline produced by 27h '. This kind of thing
can also be done extensively in printing from m/c.
The edit controls are read from the input by the 0F2C
EDITOR routine at 0F38 ED LOOP. The input is usually the
keyboard, which actually cannot produce some of the codes
which the ROM is designed to handle, eg 0D newline; but with
Interface 1 and other peripherals, or indeed from m/c, other
inputs are possible, which could produce these codes. All the
keyboard codes cause a jump from ED LOOP to 0F92 ED
KEYS, where they index into the editing keys table to produce
their effects.
The codes are handled as follows:
07 EDIT (0FA9 ED EDIT): copies the current BASIC line, if
any, to the editing area and lower screen, with editing cursor.
08 left arrow (backspace, 1007 ED LEFT): moves the
cursor left in the line in the lower screen, unless it is already
at line start; if the line contains "invisible" colour control
codes with a parameter, it goes over both.
09 right arrow (100C ED RIGHT): moves the cursor right,
unless it reaches a newline. There is no provision for
"invisible" colour controls, probably a programming mistake.

171
0A down arrow (0FF3 ED DOWN): increments the current
line and lists BASIC again without disturbing the editing line.
0B up arrow (10F9 ED UP): finds the number of the
previous line, makes it the current one, and lists BASIC again
without disturbing the editing line.
0C DELETE (1015 ED DELETE): does exactly the same as
the left arrow, and then reclaims one character If the
backspace jumped over an "invisible colour" parameter, it is
the control code that is deleted, leaving the parameter zero to
07, which rather unexpectedly appears in the editing line as a
"?"; except
in the case of code 06, see above.
0D ENTER (1024 ED ENTER): clears the stack and returns
from the editing routine.
None of these puts any code in the editing line or
outputs anything to 0010 PRINT A 1, not even ENTER; but
there is a newline permanently at the end of the editing area.
In output to the screen by 09FD PRINT OUT all three
kinds of control code are handled together, by indexing into
the control character table 0A11. Several of the entries in this
table jump out to 0A69 PO QUEST, and merely print a "?", all
those whose address and offset add to 0A69. The only
effective codes, apart from the colour controls are:
06 PRINT comma: 0A5F PO COMMA does a tab to the
next line start or half-line.
08 backspace: 0A23 PO BACK 1 moves the print position
back one, up a line if necessary unless already on the top line.
[Note the error, described under 09F4 PRINT OUT.]
09 right arrow; can only be output by m/c or string
concatenation, of doubtful usefulness. 0A3D PO RIGHT prints
an invisible space, which does nothing except move the print
position one place to the right. [Note the error, described
under 09F4 PRINT OUT.]
0D newline: 0A4F PO ENTER moves the print position to
the start of the next line, and empties the printer buffer, if the
printer is being used.
16h AT: 0A75 PO 2 OPER collects the two parameters by

172
juggling with the channel output address, ending up in 0A87
PO CONT. Then the print position is recalculated using the AT
parameters.
17h TAB: much like AT.
007D SKIP OVER skips parameters of AT and TAB
0260 control codes table (d)
09F4 PRINT OUT may enter with control code in A
0A11 control character table
0A6D PO TV 2 two operands for AT and TAB
0A7D PO TV 1 saves AT/TAB in TV DATA
0A80 PO CHANGE channel address changed to fetch
operand
0A87 PO CONT control AT is dealt with
OAC2 PO TAB fetch first parameter of TAB
0F38 ED LOOP AT and TAB included in error
1031 ED EDGE left-moving cursor skips over parameters
103E ED EDGE 1 skip two parameters for AT and TAB
1051 ED EDGE 2 control code deleted by DELETE
1937 OUT CHAR prints control codes
1FE5 PRINT 3 output position control codes
1FFC PR ITEM 1 outputs AT and two parameters
200E PR ITEM 2 outputs TAB and two parameters
204E PR POSN 1 handles semicolon, comma
2061 PR POSN 2 handles "'"
2089 INPUT can be embedded items in INPUT statement
20C1 IN ITEM 1 deals with embedded items
21B2 IN NEXT 2 handles any more position controls
28AB FN SKPOVR skips all control codes
295A SFA LOOP skips all control codes

CONTROL CHARACTERS WITH OPERANDS ROUTINE see


0A75 PO 2 OPER

control codes/keys see control characters

control variable of FOR ... NEXT loop see 1D93 FOR, 1DAB
NEXT

173
coordinates see DISPLAY AREA; of arc see 247D CD PRMS1

COORDS system variable 5C7D


Bytes: 2
Holds the X (hi) and Y (lo) pixel coordinates of the
last point plotted or drawn on the screen. Sometimes read as
two 1-byte values.
Written by:
235A C ARC GE1
0DAF CL ALL
22E5 PLOT SUB
Read by:
23C1 DR PRMS (twice)
2439 ARC START (twice)
245F ARC END (twice)
24DF D L STEP
Rems:
233B C R GRE 1 parameters copied to
2382 DRAW point coordinates held by, on entry
24B7 DRAW LINE coordinates of line start held in
24CB DL LARGER saved in H'L'

COPY key (FF) see also commands, functions and operators,


KEYBOARD SCANNING
The Z key in K mode produces the command COPY,
which accepts no parameters.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AD6 P COPY causes a jump via 1C10 CLASS 00 and 1C16 JUMP
C R to the executive routine 0EAC COPY.

COPY subroutine 0EAC


Called only from the statement loop, through the syntax
parameter table 1A7A; the executive routine for the COPY

174
command. Outputs a copy of the main screen - the top 16h/
22d
lines - to the ZX printer, if connected. The printer buffer
isn't used - the pixel bytes are sent direct to the printer from
the screen.
Because of the eccentric layout of the display area this
isn't straightforward; see DISPLAY AREA. The printer must be
fed first with the 20h/32d pixel bytes of the first row of the
first line of characters, then the second row to eighth row of
the first line, then the first row of the second line, etc.
The actual output of a row of 20h/32d pixel bytes_is
straightforward; it is achieved by a call to 0EF4 COPY LINE
with the first pixel address in HL. The pixel line number is
also given in B, but this is only so that the printer can be
slowed down at the end of each line of characters. COPY is
concerned mainly with getting HL right for each pixel line.
The start of each pixel row is 100h/256d on from the one
before, see DISPLAY AREA.
After eight pixel rows have been output, the start of
the first pixel row of the next line must be found. Add 20h to
the lo byte of the last line start. The lo bytes of the starts
of character rows are always:
00h for the first line in a third
20h " " second " " " "
etc
E0h " " eighth " " " "
so adding 20h will make a carry only on the eighth line.
Subtract the new lo byte from itself, with_reversed
carry from the addition; this gives
00000000b - 00000000b - 0 = 00000000b after the
eighth, or
PQRS0000b - PQRS0000b - 1 = 11111111b after all the others.
AND this with 11111000b/F8h, giving 00000000b after
the
eighth line, but 11111000b/F8h after each of the others; add
this to the hi byte.
The hi byte has gone up by one for each pixel row, so

175
will have gone up by 8 after the eighth. Adding F8 is the same
as subtracting 8, so it is set back to its start value each time
until the end of the third is reached. Now it is allowed to
remain incremented by eight, giving the start address of the
next third.
Input parameters: none.
Action: disable the interrupt
- make a pixel line counter B0h/176d; the number of
pixel lines in 16h/22d screen lines
- make a pointer 4000h; the first address of the display
area.
_0EB2_COPY_1: call 0EF4 COPY LINE to output 20h bytes
- recover the pointer
- increment its hi byte; ie add 100h for the next pixel
row
- AND the result with 00000111b/07 to get the remainder
on dividing by eight
- if this doesn't make zero jump on to COPY 2
- (the hi byte is a multiple of eight, eight pixel rows
have been output) add 20h to the hi byte
- reverse the carry flag
- make a byte 00 if the addition made carry, FF if not
- AND this byte with F8h/minus 08; making zero if there
was carry
- add the result to the hi byte; start on a new third if
there was carry, see above.
_0EC9_COPY_2: loop back to COPY 1 counting down the
pixel
line counter.
Exit: into 0EDA COPY END in 0ECD COPY BUFF, which
switches off the motor, enables the interrupt and clears the
printer buffer.
Output parameters: none.

COPY BUFF subroutine 0ECD


Prints out the contents of the printer buffer, 256d
bytes; they are the eight rows of pixel bytes, 32d in each row,

176
of a single line of print.
Input parameters: none.
Action: disable the interrupt
- make a pointer 5B00; the start of the print buffer
- make a pixel line counter 08.
_0ED3_COPY_3: call 0EF4 COPY LINE to output 32d pixel
bytes
- loop back to COPY 3 counting down the pixel line
counter.
_0EDA_COPY_END: output 00000100b/04 to port FB to
stop
the motor
- enable the interrupt.
Exit: into 0EDF CLEAR PRB, which zeroes all the bytes of
the printer buffer and resets the print position svs.
Output parameters: none.
Called by:
0B7F PR ALL
1303 MAIN 4
Exit from:
0A4F PO ENTER

COPY END 0EDA (0ECD COPY BUFF)


Jumps from:
0EC9 COPY 2

copying over
An expression used only at 117C ED C DONE, meaning
copying the edit line or INPUT expression to the lower screen.

COPY LINE subroutine 0EF4 see also ports


Sends a line of 20h/32d pixel bytes to the ZX printer.
Input parameters: HL holds a pointer to the first pixel
byte; this may be in the display area or the printer buffer
- B holds the number of the pixel row, counted from
eight down to one.
Action: set the carry flag if the row number is two or

177
less
- make a row flag byte with SBC A,A; FFh if the carry
was set, otherwise zero
- AND 00000010b/02 makes 02 if carry was set,
otherwise
zero
- output the row flag byte to port FB; zero does
nothing, 02 slows the printer motor for the last two lines.
_0EFD_COPY_L_1: call 1F54 BREAK KEY
- if there is no BREAK jump on to COPY L 2
- (BREAK pressed) output 04 to port FB; it stops the
motor
- enable the interrupt
- call 0EDF CLEAR PRB to zero the print buffer
- exit to "BREAK - CONT repeats".
_0F0C_COPY_L_2: get a byte from port FB and double it
- if this sets the hi bit, return; the printer isn't
connected
- if the doubling made NC, loop back waiting to COPY L
1; the printer is connected but not outputting its "ready"
signal
- (printer ready) make a byte counter 20h/32d.
_0F14_COPY_L_3: get a pixel byte
- move on the pixel byte pointer
- make a bit counter 08 for the eight individual pixels
of the byte.
_0F18_COPY_L_4: shift the row flag byte once to the left,
with its hi bit going into the carry
- shift the pixel byte once to the left, with the carry
from the last shift into its lo bit and its hi bit going into
the carry
- shift the row flag byte back to the right, with its hi
bit coming from the carry: the flag remains as it was, except
that the latest bit from the pixel byte to be printed goes into
its hi bit.
_0F1E_COPY_L_5: input from port FB
- if bit zero is zero jump back to COPY L 5; wait till

178
the printer is ready
- output the row flag; with hi bit set for a "dot" pixel
and clear for a "blank" pixel
- jump back to COPY L 4 counting the bit counter down to
zero
- (eight bits output) decrement the byte counter
- if it isn't yet zero jump back to COPY L 3 for the
next pixel byte.
Exit: RET.
Output parameters: none.
Called from:
0EB2 COPY 1
0ED3 COPY 3

COPY L 1 0EFD (0EF4 COPY LINE)


Jumps from:
0F0C COPY L 2

COPY L 2 0F0C (0EF4 COPY LINE)


Jumps from:
0EFD COPY L 1

COPY L 3 0F14 (0EF4 COPY LINE)


Jumps from:
0F1E COPY L 5

COPY L 4 0F18 (0EF4 COPY LINE)


Jumps from:
0F1E COPY L 5

COPY L 5 0F1E (0EF4 COPY LINE)


Exit from:
0F18 COPY L 4 (0EF4 COPY LINE)
Jumps from:
auto

COPY 1 0EB2 (0EAC COPY)

179
Jumps from:
0EC9 COPY 2

COPY 2 0EC9 (0EAC COPY)


Jumps from:
0EB2 COPY 1

COPY 3 0ED3 (0ECD COPY BUFF)


Jumps from:
auto

COS key (B3) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The W key in E mode without shift produces the function
COS; it requires one numeric operand X, and the value of the
function is the cosine of X.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B3 first to 04, then to E0,
and adds the priority 10h/16d. Code and priority 10E0 are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following COS is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E0 to 20, the calculator
offset for 37AA cos.

cos subroutine 37AA


Called from 0028 FP CALC with the literal 20; the
executive routine of the COS function. Also called through the
calculator in ROM to calculate TAN, and in figuring the
parameters for arc drawing. Can be called from m/c either
through the calculator or directly. Given X, returns COS X. X
must be in radians, see under 3783 get-argt.
X is first converted to a "reduced argument" V by 3783
get-argt and then its absolute value is taken. In this form V
measures the angle not in degrees or radians but as a fraction
of a right angle. V is the acute angle whose COS is equal to
COS X, in absolute values; mem-0 will hold one if V represents

180
an obtuse angle, which will have a negative cosine, and zero if
V
represents an acute angle. V is then changed to its
complementary angle, 1 - V with these units, or pi/2 - X in the
original units:
COS V = SIN (1 - V).
So the sign is adjusted according to the value in mem-0
and the remainder of the calculation performed by the sin
subroutine.
Input parameters: X must be the last number on the
calculator stack, even for direct calls.
Action: use the calculator with get-argt and abs, to find
ABS V
- if V > 1 (obtuse angle), exit to C ENT with ABS V - 1,
the acute angle whose SIN is equal to COS V in absolute
values,
and one in mem-0 for a negative result
- (acute angle) exit with 1 - ABS V and zero in mem-0.
Exit: to 37B7 C ENT, which finds SIN (1 - V). See under
37B5 sin.
Output parameters: HL and DE still point to the end of
the calculator stack
- the last value is the "reduced argument", with its
sign corrected
- mem-0 to mem-2 have been corrupted; mem-3 to mem-5
undisturbed.
Called from:
23C1 DR PRMS
37DA tan
Rems:
3449 series-06 indirectly used to calculate cos
3783 GET ARGT transforms argument to fraction of 90
deg
37B7 C ENT common to cos and sin

CO TEMP A 2257 (09F4 PRINT OUT)


Jumps from:

181
2246 CO TEMP 9 (twice)

CO TEMP B 2258 (09F4 PRINT OUT)


Jumps from:
2246 CO TEMP 9

CO TEMP C 2273 (09F4 PRINT OUT)


Jumps from:
2211 CO TEMP 5

CO TEMP D 227D (09F4 PRINT OUT)


Jumps from:
2273 CO TEMP C CO TEMP E 2287 (09F4 PRINT OUT)
Jumps from:
227D CO TEMP D

CO TEMP 1 21E1 (21E2 CO TEMP 2)


Jumps from:
21E2 CO TEMP 2 (twice)

CO TEMP 2 subroutine 21E2 see also colours, control codes


Executes a series of INK to OVER colour control items
when they follow a PLOT, DRAW or CIRCLE command; not
after PRINT or INPUT commands, which call CO TEMP 3
directly for each such item from 2024 PR ITEM 3.
Input parameters: 5C5D CH ADD holds the current
address
in the BASIC line
- A holds the token code at this address.
Action: call 21F2 CO TEMP 3 and return if it returns with
carry; A wasn't in the range INK to OVER
- (CO TEMP 3 found a control item and executed it; the
next character is the one following the parameter of the last
control item) get the next character
- if it is 2C comma or 3B semicolon jump back to 21E1 CO
TEMP 1
- (every such control must be followed by comma or

182
semicolon) report "Nonsense in BASIC".
_21E1_CO_TEMP_1 (after a comma or semicolon) get the
next code in the BASIC
- re-enter CO TEMP 2.
Exit: RET, after all successive colour items have been
executed.
Output parameters: 5C5D CH ADD points to the next byte
of the BASIC statement after all INK to OVER commands have
been dealt with
- A holds the byte.
Called from:
1CD6 CL 09 1 (misprinted CO TEMP)
Rems:
21E1 CO TEMP 1 main entry for COLOUR ITEM routines

CO TEMP 3 subroutine 21F2 see also colours, control codes


Outputs to the current channel a single colour control
item, ie INK to OVER, when they follow a PRINT, INPUT,
PLOT, DRAW or CIRCLE command; not when they are used
as free-standing BASIC commands.
The token codes and the resulting control code output to
the channel are as follows:

Token Token code Control code


INK D9 10
PAPER DA 11
FLASH DB 12
BRIGHT DC 13
INVERSE DD 14
OVER DE 15

ie in each case the control code is the token code less C9.
Input parameters: A holds the token code at the address
in 5C5D CH ADD, the BASIC pointer.
Action: return with carry unless the token is in the
range D9 INK -> DE OVER
- (INK to OVER) move the BASIC pointer on to the
parameter.
183
_21FC_CO_TEMP_4 (entry here from 1C96 CLASS 09
(PERMS),
which makes its own arrangements for getting the op code
and parameter pointer): subtract C9 to get the control code
- call 1C82 EXPT 1NUM to get the parameter from the
BASIC line to the calculator stack
- call 1E94 FIND INT1 to get it from the stack
- call 0010 PRINT A 1 to output control code and
parameter in succession.
Exit: RET.
Output parameters: none.
Called from:
1C96 PERMS (entry at 21FC CO TEMP 4)
2024 PR ITEM 3
21E2 CO TEMP 2

CO TEMP 4 subroutine 21FC (21F2 CO TEMP 3)


The entry point to CO TEMP 3 from 1C96 PERMS.
Exit from:
21F2 CO TEMP 3

CO TEMP 5 2211 (09F4 PRINT OUT)


Exit from:
0A87 PO TEMP (in PRINT OUT, misprinted CO TEMPS)

CO TEMP 6 2228 (09F4 PRINT OUT)


Jumps from:
2211 CO TEMP 5

CO TEMP 7 2234 (09F4 PRINT OUT)


Jumps from:
2211 CO TEMP 5

CO TEMP 8 223E (09F4 PRINT OUT)


Jumps from:
2234 CO TEMP 7

184
CO TEMP 9 2246 (09F4 PRINT OUT)
Jumps from:
223E CO TEMP 8

counter (length of save/load block) see program/data blocks

counter of variable name see variables

counter (screen line) see 5C8C SCR CT

COUNT ONE 31FA (31AF division)


Jumps from:
31E2 DIV START

CP LINES subroutine 1980


Checks a BASIC line to see if it has a given line
number. Can be used in conjunction with 19B8 NEXT ONE to
search through the program area for a given line number or
the next one after it, as in 1974 LINE AD 1.
Input parameters: HL points to the first byte of a hi-lo
line number
- BC holds a number for comparison.
Action: if B doesn't match the hi byte return with NZ
- now match C with the lo byte and return.
Exit: RET.
Output parameters: all registers except A unchanged
- Z if the number matches.
Called from:
1974 LINE AD 1
1855 OUT LINE

C R GRE 1 233B (2320 CIRCLE)


Jumps from:
2320 CIRCLE

CURCHL system variable 5C51


Bytes: 2

185
Holds a pointer to the output address of the current
channel; see channels and streams. It is changed whenever
the channel is changed.
One of the "fourteen pointers" which must be adjusted
each time space is made or reclaimed in the RAM, see 1664
POINTERS.
Written by:
1615 CHAN FLAG
166B PTR NEXT
Read by:
0D94 CL CHAN
0DAF CL ALL
0A80 PO CHANGE
0FA9 ED EDIT
15E6 INPUT AD
15F2 PRINT A 2
21D6 IN CHAN K
361F str$
3645 read-in
Rems:
1601 CHAN OPEN makes selected channel the current one
365F R I STORE restores channel after reading data

current channel see channels and streams, 5C51 CURCHL

current line cursor, current line marker see TV FLAG bit 4,


1795 AUTO LIST, and ">" (code 3E, after end of alphabet)

current line number see TV FLAG bit 4, 1795 AUTO LIST

cursor, cursor address see current line cursor, editing


cursor under 5C5B K CUR, error cursor under "?" (code 3F)
after end of alphabet

cursor down key (0A) see also control characters,


KEYBOARD SCANNING, 0260 control code table
The 6 key with caps shift in K, L or C mode effects the

186
"cursor down" control in editing mode. The current line
number is increased and a new automatic listing is made. It
has no effect otherwise, eg in normal INPUT mode, except
that it is used as a makeshift STOP key in INPUT ... LINE
inputs, see 0FF3 ED DOWN. If code 0A is encountered in
outputting by 09F4 PRINT OUT it is printed as a "?" by 0A69
PO QUEST.
Later models of the Spectrum mostly have a special
“down arrow" key, but caps shift 6 still works.
0260 control code table (d)
0A15 control character table
0F92 ED KEYS jumps to executive routine
0FA0 editing keys table
0FF3 ED DOWN executive routine

CURSOR DOWN EDITING see 0FF3 ED DOWN

cursor left key (08) see also control characters


The 5 key with caps shift in K, L or C mode effects the
"cursor left" or "backspace" control in editing or INPUT mode.
The cursor moves left.
If code 08 is encountered in outputting by 09F4 PRINT
OUT it produces a backspace by 0A23 PO BACK 1 on screen or
printer.
This is the only one of the cursor shift keys whose
control code can occasionally be useful for concatenation in
BASIC strings, or in m/c screen printing.
Later models of the Spectrum mostly have a special “left
arrow" key, but caps shift 5 still works.
0260 control code table (d)
0A13 control character table
0A23 PO BACK executive routine, output to screen
0F92 ED KEYS jumps to executive routine (channel)
0FA0 editing keys table
1007 ED LEFT executive routine, output to channel

CURSOR LEFT EDITING see 1007 ED LEFT

187
CURSOR LEFT SUBROUTINE see 0A23 PO BACK 1

cursor position see system variable 5C5B K CUR

cursor right key (09) see also control characters,


KEYBOARD
SCANNING, 0260 control code table
The 8 key with caps shift in K, L or C mode effects the
"cursor right" control in editing or INPUT mode. The cursor
moves right. If code 09 is encountered in outputting by 09F4
PRINT OUT it produces an "invisible space", see 0A3D PO
RIGHT; but this facility, of questionable usefulness anyway, is
spoiled by an error, see under 09F4 PRINT OUT.
Later models of the Spectrum mostly have a special
"right arrow" key, but caps shift 8 still works.
0260 control code table (d)
0A14 control character table
0A3D PO RIGHT executive routine, output to screen
0F92 ED KEYS jumps to executive routine
0FA0 editing keys table
100C ED RIGHT executive routine, output to channel

CURSOR RIGHT EDITING see 100C ED RIGHT

CURSOR RIGHT SUBROUTINE see 0A3D PO RIGHT

cursor up key (0B) see also control characters, KEYBOARD


SCANNING, 0260 control code table
The 7 key with caps shift in K, L or C mode effects the
"cursor up" control in editing mode - it has no effect
otherwise, eg in INPUT mode. The previous line number is
made current and a new automatic listing is made. If code 0B
is encountered in outputting by 09F4 PRINT OUT it is printed
as a "?" by 0A69 PO QUEST.
Later models of the Spectrum mostly have a special "up
arrow" key, but caps shift 7 still works.

188
0260 control code table (d)
0A16 control character table
0F92 ED KEYS jumps to executive routine
0FA0 editing keys table
1059 ED UP executive routine

CURSOR UP EDITING see 1059 ED UP

189
D

DAA (decimal adjust instruction) see 2EBA FP BYTES under


2DE3 PRINT FP

DATA key (E4) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode key table (b)
The D key in E mode without shift produces the token
DATA. It has two unrelated uses, as a command or as an
"adverb" in SAVE/LOAD commands.
In commands, the_DATA_statement must include a_DATA
_list, one or more string or numeric expressions, separated by
commas. They will be skipped in BASIC execution unless a
READ statement is encountered, when they will be allocated
in sequence to the variable names in the READ statement: the
string/numeric status of the variable must match for every
item.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1ACC P DATA causes a jump via 1C11 CLASS 05 and 1C16 JUMP
C R to the executive routine 1E27 DATA.
If DATA is used as part of a SAVE/LOAD/VERIFY
statement, it isn't a command and isn't read by the statement
loop; this use of DATA is handled by the 0605 SAVE ETC
subroutine at 0652 SA DATA. The "search" routine of 1D86
LOOK PROG performed during
execution of a READ command doesn't notice these
occurrences of
E4 DATA, because 1990 EACH S 1 is programmed to look only
at the first token of a BASIC statement.
0652 SA DATA 'SAVE/LOAD &c fname DATA'
1D86 LOOK PROG finds occurrences of DATA
1DEC READ 3 reading of DATA list

190
1DED READ search program for DATA list
1E08 REPORT E "Out of DATA" message
1E0A READ 1 value from DATA list assigned to variable
1E39 PASS BY skips over any statement with DATA
294B V END mistake: simple string can be saved as DATA
(cannot be LOADed)

DATA subroutine 1E27


Called only by the statement loop, 1C16 JUMP C R, from
1ACC P DATA in the syntax parameter table. "Executes" a DATA
statement, though it does nothing. In syntax checking, the
statement is checked for expressions and correct separators;
in run time, it just jumps forward to the next statement. For
the significance of DATA statements in run-time, see 1DED
READ.
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on the byte after
the DATA command.
Action: in run-time, jump on to DATA 2.
_1E2C_DATA_1: (syntax checking) call 24FB SCANNING to
read the expression; it will produce error reports if it finds
an incorrect expression
- check that the next character is a comma
- if it isn't, call 1BEE CHECK END which reports
"Nonsense in BASIC" if this isn't the end of the statement and
makes a double return if it is
- (a comma was found) get the next code from BASIC
- loop back to DATA 1 for the next DATA item.
_1E37_DATA_2: make a byte E4 DATA; the token to be
passed by.
Exit: in syntax checking, into CHECK END, which will
return into 1876 STMT RET
- in run-time, into 1E39 PASS BY, which moves the BASIC
pointer on to the next statement
Output parameters: A holds E4 on exit into PASS BY.
Rems:
1E2C DATA 1 checking for syntax

191
1E37 DATA 2 passed by in run-time

data block (save/load) see program/data block

DATADD system variable 5C57


Bytes: 2
Holds the address after the last DATA item read from the
BASIC program; used in executing READ commands to find
the next DATA item. One of the "fourteen pointers" which
must be adjusted each time space is made or reclaimed in the
RAM, see 1664 POINTERS.
Written by:
1219 RAM SET
166B PTR NEXT
1E0A READ 1
1E45 REST RUN
Read by:
1DED READ

DATA list see DATA key

DATA 1 1E2C (1E27 DATA)


Jumps from:
auto

DATA 2 1E37 (1E27 DATA)


Jumps from:
1E27 DATA

DECIMAL 2CCB (2C9B DEC TO FP)


Jumps from:
2CB8 NOT BIN

decimal, decimal point see "." (code 2E) after end of


alphabet

decimal adjust instruction (DAA) see 2EBA FP BYTES under

192
2DE3 PRINT FP

decimal numbers see also BIN key, E-format numbers,


number marker
Decimal numbers aren't some special kind of numbers
like rational numbers, just a way of writing down any kind of
real numbers; the same numbers can also be written in other
ways, eg in binary. Ten still has factors two and five, however
they are written down.
The BASIC can handle numbers written in decimal form,
in _E_format or in_binary_format, but not in hex format. Any
number except a line number mentioned in the BASIC
program, in whatever format it is entered, is followed in the
representation of the line in the program area by six bytes
which are never printed out: the first is 0E,
the_number_marker, the other five are the
number in full FP format.
26C3 S NUMERIC numeric result on SCANNING
2C9B DEC TO FP converted to FP form
2CB8 NOT BIN considered as integer + fraction
2CCB DECIMAL check for digit after decimal point
2CCF DEC RPT C zero stacked for 0.xyz
2CD5 DEC STO 1 saves 1 as FP number
2CDA NXT DGT 1 loop adding digit*memory/10 to number

DECIMAL TO FLOATING POINT SUBROUTINE see 2C9B


DEC TO FP

dec-jr-nz subroutine 367A


Called from 0028 FP CALC with offset 35 followed by a
"distance literal" XX. Operates like DJNZ, counting down to
zero
and making a looping jump forward or back by XX bytes,
forward
if less than 80h, backward if >= 80h; an auto jump to the
distance literal itself would be FF. The jump must land on a

193
calculator literal, though it can pass over 38 end-calc and/or
new calls to 0028 FP CALC.
The system variable 5C67 BREG is used to store the
counter, which is loaded from the B register whenever 0028
FP CALC is called. The calculator can be used without loading
BREG, ie leaving it undisturbed, by calling 3362 GEN ENT 2.
Only used once in the ROM, in the series generator; but
useful for m/c programmers who use the calculator. No
obvious point in calling it direct.
Most assembler programs can calculate the distance
literal for you: write something like
LOOP ...
35; dec-jr-nz
LPRET LOOP - LPRET - 1; distance literal
Input parameters: HL' is a "return pointer" to the
address of the byte following 35 dec-jr-nz
- 5C67 BREG already holds the counter.
Action: change to the alternate registers and stack the
return pointer
- decrement 5C67 BREG and recover the return pointer
- if BREG didn't reach zero, exit to 3687 JUMP 2 with
the alternate registers; this executes the jump
- (BREG is zero) increment the return pointer; it skips
the distance literal and now addresses the byte following dec-
jr-nz XX
- restore the original registers.
Exit and output parameters: if BREG reduced to zero, HL'
now points to the next literal after the distance literal; RET to
the calculator loop at 3365 RE ENTRY
- if not, HL' still points to the distance literal; into
3687 JUMP 2 with the registers switched to alternates.
Called from:
3453 G LOOP
Rems:
3686 JUMP used by

declared variable see variables

194
decoding key input see KEYBOARD SCANNING, 0333 K
DECODE

DECREASE THE COUNTER SUBROUTINE see 367A dec-jr-nz

DEC RPT C 2CCF (2C9B DEC TO FP)


Jumps from:
2CFF ST E PART

DEC STO 1 2CD5 (2C9B DEC TO FP)


Jumps from:
2CB8 NOT BIN

DEC TO FP subroutine 2C9B


Reads a decimal, binary, or E-format number in the
BASIC line and puts it on the calculator stack in five-byte
FP form, ready for insertion by 268D S DECIMAL in the five
spaces following the number marker.

Reading_binary_integers from the BIN format is fairly


straightforward, though executed by very concise coding. BIN
DIGIT cannot read numbers with fractional parts such as BIN
1.101, nor numbers greater than 1111,1111,1111,1111b/FFFFh; none
of the handbooks mentions these limitations.
The integer part of_decimal_numbers is also handled
quite simply. Evaluation of the fractional part is a little more
complex: one is put in the calculator memory for a "decimal
unit", which is then successively reduced to 0.1, 0.01, 0.001,
etc. Each successive unit is multiplied by the next digit in the
fractional part to give successive components of the fraction,
which are accumulated in the calculator's last value.
The fullest form of_E_format can be seen by commanding
PRINT (10**-20)/7: it comes out as 1.4285714E-21. This number
is in four parts, each of which must be considered:
"1" is the integer part of the mantissa
".4285714" is the fractional part of the mantissa

195
"E" signals that the number so far is a mantissa; if
there is no "E" the reading of the number is complete
"-21" is the decimal exponent; a fractional part isn't
accepted.
The value required is M times 10**X, where X is the
exponent and M is the mantissa: in the example, X =
1.4285714, ie 10/7, and M = -21.
All numbers are read as positive, though there is
provision for reading negative exponents in E-format
numbers; if the number is negative it was preceded by a
"unary minus", which has already been read and stacked as an
operator before this subroutine was called.
Input parameters: the BASIC pointer in 5C5D CH ADD is
on the first code of the number as recorded in the BASIC line
- A holds the byte at the pointer.
Action: if the first code isn't C4 BIN, jump on to NOT
BIN
- (BIN format) make a zero accumulator to evaluate the
BIN expression.
_2CA2_BIN_DIGIT: read the next digit and subtract 31h;
giving FF with carry if the digit is 30h zero, 00 with no carry
if it is 31h one
- ADC A,00 gives zero for both digits; but with carry
for zero
- if it didn't give zero, jump on to BIN END; there are
no more binary digits to be evaluated
- (it is a digit) put the accumulator in HL
- reverse the carry flag; now C for one and NC for zero
- ADC HL,HL shifts all the binary digits of the
accumulator left one place and adds the new digit in at the
end
- if the result is more than FFFF, report "Number too
big"
- store the incremented accumulator and loop back to BIN
DIGIT for more digits.
_2CB3_BIN_END (all binary digits read): exit to 2D2B
STACK BC with the accumulator in BC.

196
_2CBB_NOT_BIN: if the first character is 2E point, jump
on to DECIMAL
- (first character is a digit) call 2D3B INT TO FP,
which converts any digit characters up to the first non-digit to
an integer stored in FP format on the calculator stack
- if the next character isn't 2E point, jump on to E
FORMAT
- (point) get the next character and call 2D1B NUMERIC
- if it isn't a digit jump on to E FORMAT
- (it is a digit) jump on to DEC STO 1.
_20CB_DECIMAL (decimal point has been found as the first
character of the number in BASIC): get the next character and
call 2D1B NUMERIC; NC shows a digit
_2CCF_DEC_RPT_C: on C, report "Nonsense in BASIC"
- put a zero on the calculator stack for the integer
part.
_2CD5_DEC_STO_1 (there are digits after a decimal point):
put one in the calculator memory for the decimal unit.
_2CDA_NXT_DGT_1: read the next character
- call 2D22 STK DGT, which puts the value of a single
digit on the calculator stack
- if it wasn't a digit jump on to E FORMAT
- use the calculator to divide the decimal unit by ten
and return the reduced unit to memory
- multiply the new digit by the decimal unit
- add it to the running total on the stack; the original
last value was the integer part of the number, or zero
- move on the BASIC pointer
- jump back to NXT DGT 1. [The label is in the wrong
place: the code would work slightly faster if the loop was to
the line after NXT DGT 1. But no harm done.]
_2CEB_E_FORMAT (non-digit and non-point found, before
or after a point): if the next character isn't "E" or "e",
return; the full evaluation is on the calculator stack.

_2CF2_SIGN_FLAG ("E" found: the next character must be


either "+", "-" or a digit. "XeM" and "Xe+M" are both accepted):

197
make a sign flag FF for positive
- read the next character
- if it is 2B "+" jump on to SIGN DONE
- if it isn't 2D "-" jump on to ST E PART
- (it is "-") increment the sign flag; 00 for negative.
_2CFE_SIGN_DONE ("+" or "-" read): move on the BASIC
pointer.
_2CFF_ST_E_PART (the pointer is past E): call 2D1B
NUMERIC and if the character isn't a digit, report "Nonsense
in BASIC"
- call 2D3B INT TO FP to read the exponent on to the
calculator stack
- call 2DD5 FP TO A to get it off again
- if FP TO A returned with carry report "Number too
big"; more than 255d
- also if AND A shows it more than 127d; actually the
exponent mustn't be more than 38d. The exit routine checks
this,
but A > 127d would be read as negative
- check the sign flag; if it shows positive jump on to E
FP JUMP
- (negative sign flag) negate the exponent.
_2D18_E_FP_JUMP: exit.
Exit and output parameters:
- from BIN END, with the number in BC into 2D2B STACK
BC, which puts it on the calculator stack.
- from E FORMAT, RET; the number is already on the
stack.
- from E FP JUMP, into 2D4F E TO FP with the mantissa on
the stack and the exponent in the A register; it calculates M
times 10**X.
Called from:
268D S DECIMAL

DE,(DE+1) subroutine 2AEE


A kind of two-byte version of LD A,(DE): given any

198
address, loads DE with the value of the two_following bytes. If
the address is the sign byte (second byte) of a number in
“small integer" form, the number is returned in DE.
Input parameters: address in DE.
Action: load E with the value in address DE+1, and D with
the value in address DE+2.
Exit: RET.
Output parameters: DE holds the result
- HL holds the pointer moved on twice.
Called from:
29FB SV MULT
2A2C SV ELEM$

DEFADD system variable 5C0B


Bytes: 2
Holds the address of the arguments of a DEF FN if one is
being evaluated; otherwise zero. If nested DEF FNs are
evaluated, a series of DEFADD values is stacked on the
machine stack with a zero at the bottom.

Only the hi byte needs to be checked for zero, because


the address, if any, is in the RAM and hence more than 4000.
Zero signals "we aren't at present evaluating a DEF FN".
Written by:
1313 MAIN G
288D SF VALUE (twice)
Read by:
288D SF VALUE
2951 STK F ARG
Checked for zero:
28E3 V TEST FN

DEF FN key (CE) see also commands, functions and


operators,
KEYBOARD SCANNING, 0284 extended mode table (f )
The one key in E mode with symbol shift produces the
command DEF FN. The "DEF FN statement" must consist of:

199
a single-letter variable name, with "$" if the result of
the defined function is a string
followed by a pair of brackets "()"
which may or may not contain a list of arguments, single-
letter numeric or string "dummy" variable names separated by
commas; the_operands or_parameters
followed by "=" and an expression, which must match in
string/numeric status the name of the DEF FN function. This
expression may contain variables matching those before the
"="; any such variables are read as "dummy" variables.
In this index I sometimes refer to the "left side" and
the "right side" of DEF FN statements: I don't think this
terminology is ever used in the notes.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A. 1AF9 P DEF FN causes a jump via 1C11 CLASS 05 and
1C16 JUMP C R to the executive routine 1F60 DEF FN.

1D86 LOOK PROG finds occurrences of DEF FN


1F7D DEF FN 2 if followed by (), no parameters
1F86 DEF FN 3 loops to handle parameters
1F94 DEF FN 5 comma indicates more parameters
26C9 S LETTER vbles in FN are first looked for in DEF FN
27BD S FN SBRN search made for DEF FN
27F7 SF RUN search made for DEF FN
2808 SF FND DF search made for DEF FN
2814 SF CP DEF check for match with FN
2825 SF NOT FD no match; prepare for more searching
2831 SF VALUES match; find value of FN
2852 SF ARG VL evaluates string/numeric argument in
2885 SF R BR 2 check for ")"
288D SF VALUE handles evaluation of function
28AB FN SKPOVR looks for end of DEF FN
28B2 LOOK VARS called to find arguments of DEF FN
28E3 V TEST FN searches for arguments of DEF FN
28EF V RUN/SYN find variable if not in DEF FN
2951 STK F ARG if in DEF FN, look there first for

200
variable names

DEF FN subroutine 1F60


Called by the statement loop from the syntax parameter
table 1A7A. Executes the DEF FN command. Not otherwise
called from ROM.
In run time, the DEF FN statement is skipped. In syntax
checking, apart from checking the various separators, the
syntax of the arguments and of the function expression, the
subroutine puts a number marker and a 5-byte blank after
each argument on the left side, before the comma or ")" and
after the argument letter and "$" if any. This is put even after
string arguments;
such markers are never put after ordinary BASIC variables.
When the FN command is executed in run-time, these spaces
are filled with the current FP values of numeric arguments or
pairs of string parameters for string arguments.
Input parameters: the BASIC pointer in 5C5D CH ADD is
on the first code after the DEF FN command
- A holds the code at the pointer.
Action: in syntax checking, jump on to DEF FN 1
- (run time) make a token byte CE DEF FN and exit to
1E39 PASS BY.
_1F6A_DEF_FN_1: set bit 6 of FLAGS; "numeric status"
- call 2CBD ALPHA; if the character isn't a letter,
report "Nonsense in BASIC"
- move on the pointer and read the character
- if it isn't 24h $ jump on to DEF FN 2
- (string function) zero bit 6 of FLAGS; "string status"
- move on the pointer and read the character.
_1F7D_DEF_FN_2: if the character isn't 28h ( report
"Nonsense in BASIC"
- move on the pointer and read the character
- if it is 29h ) jump on to DEF FN 6; the function has
no arguments.
_1F86_DEF_FN_3: call 2CBD ALPHA; it returns with carry
for a letter.

201
_1F89_DEF_FN_4: if there is no carry report "Nonsense in
BASIC"
- move on the pointer and read the character
- if it isn't 24h $ jump on to DEF FN 5
- (it is "$") move on the pointer and read the
character.
_1F94_DEF_FN_5 (the manoeuvres with EX DE,HL in DEF
FN 4
have left DE pointing to the first address after the argument
letter, or after "$" if it is there): call 1655 MAKE ROOM to
make six spaces after the argument
- put a number marker 0E in the first space
- if the next character isn't comma jump on to DEF FN 6
- (comma after argument signals more arguments) move
on the pointer and read the character
- jump back to DEF FN 3 for more arguments.
_1FA6_DEF_FN_6 (no more arguments): if the character at
the pointer isn't 29h ) report "Nonsense in BASIC"
- if the next one isn't 3D = report "Nonsense in BASIC"
- move on the pointer and read the character
- stack the string/numeric status flag
- call 24FB SCANNING; it will report any errors in the
right side of the DEF FN statement, and return a status flag
- XOR this flag with the one from the stack; the result
will have bit 6 zero if they match
- AND the result with 01000000b/40h; making zero if the
flags match.
_1FBD_DEF_FN_7: report "Nonsense in BASIC" if the flag
shows NZ
- call 1BEE CHECK END, which reports an error if this
isn't the end of the statement and makes a double return to
1B76
STMT RET if it is.
Exit: in syntax checking, through 1BEE CHECK END to
1B76
STMT RET
- in run time, into 1E39 PASS BY, which finds the next

202
statement.
Output parameters: A holds CE DEF FN on exit to PASS BY.

DEF FN 1 1F6A (1F60 DEF FN)


Jumps from:
1F60 DEF FN

DEF FN 2 1F7D (1F60 DEF FN)


Jumps from:
1F6A DEF FN 1

DEF FN 3 1F86 (1F60 DEF FN)


Jumps from:
1F94 DEF FN 5

DEF FN 4 1F89 (1F60 DEF FN)


Jumps from:
1F6A DEF FN 1

DEF FN 5 1F94 (1F60 DEF FN)


Jumps from:
1F89 DEF FN 4

DEF FN 6 1FA6 (1F60 DEF FN)


Jumps from:
1F7D DEF FN 2
1F94 DEF FN 5

DEF FN 7 1FBD (1F60 DEF FN)


Jumps from:
1F7D DEF FN 2
1FA6 DEF FN 6 (twice)

delay see KEYBOARD SCANNING, time period

DELETE key (0C) see also control characters, KEYBOARD


SCANNING, 0260 control code key table

203
The zero key with caps shift in K, L or C mode effects
the DELETE control in editing or INPUT mode. Later models
of the Spectrum mostly have a special DELETE key, but caps
shift zero still works. The character before the cursor is
deleted. Nothing to do with the “delete" subroutine!
If code 0C is encountered in output by 09F4 PRINT OUT,
a "?" is output by 0A69 PO
QUEST.
0260 control code table (d)
0A17 control character table
0F92 ED KEYS jumps to executive routine
0FA5 editing keys table
1015 ED DELETE executive routine
Rems:
0389 K GRA DGT key zero read as
1051 ED EDGE 2 control character deleted by

delete subroutine 33A1


Called frequently in the ROM from 0028 FP CALC with
literal 02. Deletes the last value on the calculator stack.
As it consists of a single RET at 33A1 there would be
little point in calling it from elsewhere!
The real action is in 335B CALCULATE: on entry or return
to CALCULATE, the end of the calculator stack 5C65 STKEND
is given the value in DE at 3365 RE ENTRY. But CALCULATE
treats any operation with a literal less than 18h as a binary
operation
(3380 FIRST 3D): for unary operations, it sets HL on the first
byte of the last value and DE on the stack end, but for binary
operations it sets DE on the start of the last value and HL on
the second last.
Thus merely by entering FIRST 3D with a literal less
than 18h, the address to be put in 5C65 STKEND is moved
back five bytes, and what was the last value will be ignored by
subsequent calculator operations. No further action is
required, so the "subroutine address" read from the table by
338E ENT TABLE need merely be a RET.

204
The number isn't deleted in the sense of being
reclaimed, and it can still be read from the DE pointer, or
using 5C65 STKEND as a pointer, until it is really deleted by
16C5 SET STK. This is done several times in the ROM, see
below.
Called from:
0427 BE OCTAVE
1CF0 IF (34E9 TEST ZERO used on already deleted value)
1D16 F REORDER
1D34 F L&S (twice; two deleted numbers copied to
looping variable)
1DAB NEXT
2320 CIRCLE
233B C R GRE 1 (3 times)
235A C ARC GE1 (twice)
238D DR 3 PRMS
23A3 DR SIN NZ (4 times)
23C1 DR PRMS (8 times; exponent of deleted value read)
2425 ARC LOOP (twice)
2439 ARC START
245F ARC END (twice)
2497 DRAW SAVE (3 times)
25F8 S RND
2B59 L NUMERIC (on exit, copies deleted number to vble)
2CD5 DEC STO 1
2D7B E END
2DAD FP DELETE (deleted small integer put in BC)
2DE3 PRINT FP
2DF8 PF POSTVE
2EO1 PF LOOP (deleted value checked for small integer)
2ECB PF MORE
2F2D PF COUNT
3449 series-06
3453 G LOOP
36A0 n-mod-m
370E RSLT ZERO
371C VALID

205
3783 get-argt
385D XISO
386A ONE
Rems:
2E24 PF SMALL mistake: a delete literal omitted, with
unfortunate results for the STR$ function
2E56 PF LARGE exponent of number deleted in 2E01 PF
LOOP
is tested
2E6F PF MEDIUM still using number deleted in
2E01 PF LOOP
342D stk-mem last value must be deleted if not required

deleted string or variable see 19E5 RECLAIM 1

DELETE EDITING SUBROUTINE see 1015 ED DELETE

DEST system variable 5C4D see also 28B2 LOOK VARS, 5C72
STRLEN, variables.
Bytes: 2
The address of the "variable in assignment", eg the
variable named on the left-hand side of a LET command. The
actual address held depends on whether the variable has
already been declared:
- if the variable name hasn't been used before, DEST is
the address of the first letter of the "variable in assignment"
name in the BASIC program.
- if it has been used before, DEST is the address in the
variables area of the "destination variable" to which the new
value will be copied; before the first byte of the old FP
number value, or on the first character of the old string value.
One of the fourteen system pointer variables set by 1664
POINTERS whenever space is made or reclaimed in RAM.
Also used in 2BC6 L STRING as an ad hoc temporary store
for HL.
Written by:
166B PTR NEXT

206
1C46 VAR A 3
2BC6 L STRING
Read by:
166B PTR NEXT
1DAB NEXT
2AFF LET
2B29 L SPACES
2B4F L SINGLE
2B72 L DELETE$ (unnecessary)
2BC0 L NEW$
2BC6 L STRING
Rems:
1C22 VAR A 1 finds value for
2BA3 L IN W/S used to copy new variable to variable area
2BAF L ADD$ shifted to point to variable name
2BEA L FIRST end of handling new variables

destination address of variables see variables

destination pointer (of save/load block) see program area

destination variable see 28B2 LOOK VARS

DF CC system variable 5C84 see DISPLAY AREA


Bytes: 2
The first pixel address in the display area of the
current print position in the upper part of the screen.
Written by:
0ADC PO STORE
Read by:
0B03 PO FETCH
Rems:
0D2D PO SCR 4B value put in S POSN by 0DD9 CL SET

DF CCL system variable 5C86 see DISPLAY AREA


Bytes: 2
The first pixel address in the display area of the

207
current print position in the lower part of the screen.
Written by:
0AF0 PO ST E
Read by:
0B03 PO FETCH

DF SZ system variable 5C6B see also DISPLAY AREA


Bytes: 1
The number of lines currently in the lower part of the
screen, including one blank line. Always reset to two lines on
start-up and whenever the screen is cleared: expanded as
necessary by scrolling up when the bottom line has filled up
by 0D1C PO SCR 4A.
Because line numbers in the "print position" system are
counted from the bottom of the screen, it is also the line
number of the top line of the lower display.
Written by:
0D1C PO SCR 4A
0D8E CLS 3
1219 RAM SET
12A2 MAIN EXEC
2096 INPUT 1 (ignore: a misprint for TV FLAG)
Read by:
0AAC PO AT ERR
0C55 PO SCR
0CD2 PO SCR 3
0D02 PO SCR 4
0D1C PO SCR 4A
0D6E CLS LOWER
0DD9 CL SET
1795 AUTO LIST
1835 LIST ALL
2096 INPUT 1

diagonal move see 24B7 DRAW LINE

DIFFER subroutine 19DD

208
Calculates the length of a sector of memory for
reclaiming purposes - the number of bytes from one "start" to
another.
Can be called from m/c to perform any subtraction BC =
HL - DE, provided it is remembered that it returns with HL
and DE exchanged.
Input parameters: DE holds the first "start", HL the
second.
Action: subtract DE from HL and put the result in BC
- add back DE to give the original value
- exchange HL and DE.
Exit: RET.
Output parameters: BC holds the length required. The
original values of HL and DE are returned reversed - the first
"start" in HL Called from:
19E5 RECLAIM 1
Exit from:
19DB NEXT O 5 (19B8 NEXT ONE)

digit keys see character codes

DIM key (E9) see also commands, functions and operators,


KEYBOARD SCANNING
The D key in K mode produces the command DIM; it
must be followed by
the array name, one letter with or without "$";
a parenthesis with one or more numbers separated by
commas, the dimensions of the array.
The effect of the command is to "declare" an array, ie
to put the array in the variables area with all its values
zeroed.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A. 1AA2 P DIM causes a jump via 1C11 CLASS 05 and 1C16
JUMP C R to the executive routine 2C02 DIM.

209
DIM subroutine 2C02
Called only by the statement loop from the table at 1AA2
P DIM; executes the DIM command by constructing an empty
array of the specified dimensions in the variables area.
The number of dimensions is limited to 255d and the size
of each dimension to FEFFh/65279d: effectively there is no
limit on the dimension size other than the amount of memory
available.
[Experimentalists may like to try entering
10 DIM a(1,1,1,1,1,...1)
255d "1"s will pass syntax, 256d will produce a flashing
error cursor on the last dimension.
Similarly, DIM a(65279) gives the error report "Out of
memory", DIM a(65280) gives "Subscript wrong".]
The space actually required in the variables area for
the new array is
- 5 bytes for each element of a numeric array, or one for
each character of a string array
- plus a byte for the variable letter
- plus two bytes for the array length
- plus two bytes for the size of each dimension.
This is calculated as follows: read each dimension from
BASIC, and PUSH each dimension on the machine stack as it is
read.
Multiply the first dimension by the element length, five
for numeric elements or one for characters; this is the length
required for all the elements of the first dimension.
Now multiply this product by the second dimension;
dimensions (x,y) will take up the space of x * y * element
length in the variables area. And so on. As each dimension is
counted and calculated, the accumulated product indicating
the length required is retained on the calculator stack.
The total length of the array in the variables area is
now figured as the accumulated product of dimensions, plus
four for the leading bytes, plus two bytes for each dimension
size POPped from the machine stack and recorded in the
empty array.

210
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on the letter of
the array name in BASIC.
Action: call 28B2 LOOK VARS, which searches the
variables area for an array of the same name and returns with
a motley array of output parameters, repeated here for
convenience from the entry under LOOK VARS:
HL holds a start address for the variable
C holds a "discriminator byte" abcxxxxx:
a is one for syntax checking, zero for run time
bc is 00 for numeric arrays, 10 for string arrays
xxxxx indicate the variable letter, zeroes in syntax
checking
the zero flag shows Z if there was a "(" after the
variable letter in the BASIC or the "$" after it, NZ if there
wasn't
the carry flag shows NC if a matching variable was found,
C if it wasn't.
_2C05_D_RPORT_C: if the zero flag shows NZ report
"Nonsense in BASIC"; the DIM variable must have a
parenthesis
- in run time jump on to D RUN
- (syntax checking) zero bit 6 of C; now bc will be 00,
and the whole byte 10000000b
- call 2996 STK VAR, see under 28B2 LOOK VARS, which
does all the necessary syntax checking
- call 1BEE CHECK END, which reports an error if this is
not the end of the statement, or makes a double return to
1B76 STMT RET if it is.
_2C15_D_RUN (run time): if the carry flag is set jump on
to D LETTER; no array was found with a matching name
- (there is an array with matching name, and HL has the
address of its letter in the variables area) call 19B8 NEXT ONE
and 19E8 RECLAIM 2; this deletes the old array.
_2C1F_D_LETTER: set bit 7 of the discriminator byte; it
is now in the correct form for a variable letter, 100xxxxx for a
number array, 110xxxxx for a string array

211
- make a one-byte dimension counter set to zero and a
two-byte length accumulator set to one; assuming an array of
characters
- if bit 6 of the variable letter is zero make the
accumulator five; number array, five bytes for each value.
_2C2D_D_SIZE: save the accumulator.
_2C2E_D_NO_LOOP: move the BASIC pointer on to the first
code of the dimension
- set a limit value for the dimension of FF00
- call 2ACC INT EXP1 to read the dimension
- if it returns with carry report "Subscript wrong"
- put the dimension size on the machine stack; a
dimension size goes on the stack for every turn of the loop
- increment the dimension counter
- call 2AF4 GET HL*DE to multiply the dimension size by
the accumulator
- read the next character
- if it is 2C comma loop back to D NO LOOP
- if it isn't 29h ) report "Nonsense in BASIC"
- (all dimensions multiplied together) move on the BASIC
pointer
- make the dimension counter a two-byte counter;
required for the next operations even though it is still < 256d
- increment the dimension counter by two; to allow for
the four leading bytes
- double it
- add the accumulated element length count
- if this produces carry report "Out of memory"; a carry
from this total, indicating length over 65535d bytes, could give
quite a small number in HL, so the error must be reported
now
- put a pointer on the 80-byte before 5C59 E LINE; the
end of the variables area
- call 1655 MAKE ROOM to make a space there of the total
length required; it will report "Out of memory" if necessary
- put the variable letter, the length excluding the
first three leading bytes, and the dimension counter in the

212
first four spaces cleared
- put zero in the last byte of the cleared space
- if bit 6 of the variable letter is zero jump on to DIM
CLEAR; this is a number array
- (string array) put 20h space in the last byte.
_2C7C_DIM_CLEAR: copy the last byte backwards to as
many empty spaces as indicated by the length; zeroes if it is a
numeric array, spaces if it is string.
_2C7F_DIM_SIZES: recover a dimension off the stack
- enter it in the next two bytes of space, working
backwards; they come off the stack in reverse order
- decrement the dimension counter
- jump back to DIM SIZES till the counter reaches zero.
Exit: RET.
Output parameters: none.
Rems:
2996 STK VAR used only to check syntax

DIM CLEAR 2C7C (2C02 DIM)


Jumps from:
2C2E D NO LOOP

dimension of array see arrays, 2C02 DIM

dimension size, DIM limit see 2ACC INT EXP1


2ACC INT EXP1 and 2ACD INT EXP2 are used for reading
array dimensions and subscripts from BASIC, and are entered
with a limit value in HL; if the value read from BASIC is above
the given limit, they report "Subscript wrong".
For subscripts (29FB SV MULT) the dimension read from
the dimension list in the variables area is used as a dimension
limit; for dimensions, when the array is being declared by a
DIM command, FF00 is used (2C2E D NO LOOP). No array
could possibly have a dimension of this size without
producing a memory overflow; the real limitation on
dimension sizes is the amount of memory available.

213
DIM SIZES 2C7F (2C02 DIM)
Jumps from:
auto

direct commands (BASIC)


A BASIC statement input in editing mode without a line
number. It is read both for syntax checking and execution
exactly like a BASIC line; indeed numbered program lines are
only executed by jumps from execution of direct commands
such as RUN, GO TO.

12A2 MAIN EXEC controls execution of


12CF MAIN 3 edit line interpreted first

disable see interrupt

discarding see address dropping

discriminator byte of BASIC variable see variable

DISPLAY AREA 4000 see also upper screen


1. The display area consists of 1800h/6072d bytes,
extending from the start of the RAM at 4000h to 57FFh. Each
byte carries eight pixels of the screen, so there are C000h/
49152d pixels in all.
The addresses are the_screen_addresses or_pixel
_addresses, and the bytes they hold are the_character_bytes or
_pixel_bytes; "pixel line" is occasionally used as synonymous
with "pixel byte", eg in 0B75 PR ALL, but this is wrong and
confusing.
_Pixel is short for "picture element": a_pixel_bit is a
single binary bit, and the corresponding pixel is a dot on the
screen, printed in INK colour if the bit is a one and in PAPER
colour if it is a zero, adjusted for INVERSE and FLASH.
The following description of the display area uses hex
numbers; despite their relative unfamiliarity they expose the
logic of the arrangement much better than decimals. The

214
screen is divided into_thirds each of 8_character_lines
or_screen_lines; these contain 40h_pixel_lines or_pixel_rows,
each containing 20h pixel bytes, 100h pixels: each 'third' is
therefore 100h characters, 800h pixel bytes, and 4000h
pixels.
In each third the sequence of pixel bytes in the memory
is: first the 100h bytes giving the top row of pixels in all the
eight lines of the 'third', then 100h bytes with the second row
of all the eight lines, etc. Thus:

First line of characters:


(00) (01) (02) (1F)
(4000) 00000000 00000000 00000000 ... 00000000 (401F)
(4100) 00000000 00000000 00000000 ... 00000000 (411F)
(4200) 00000000 00000000 00000000 ... 00000000 (421F)
(4300) 00000000 00000000 00000000 ... 00000000 (431F)
(4400) 00000000 00000000 00000000 ... 00000000 (441F)
(4500) 00000000 00000000 00000000 ... 00000000 (451F)
(4600) 00000000 00000000 00000000 ... 00000000 (461F)
(4700) 00000000 00000000 00000000 ... 00000000 (471F)
Second line of characters:
(00) (01) (02) (1F)
(4020) 00000000 00000000 00000000 ... 00000000 (403F)
(4120) 00000000 00000000 00000000 ... 00000000 (413F)
etc, to
(4720) 00000000 00000000 00000000 ... 00000000 (473F)
Third line: Fourth line: Fifth line:
(4040) ... (405F) (4060) ... (407F) (4080) ...
(409F)
(4140) ... (415F) (4160) ... (417F) (4180) ...
(419F)
to to to
(4740) ... (475F) (4760) ... (477F) (4780) ...
(479F)
Sixth line: Seventh line:
(40A0) ... (40BF) (40C0) ... (40DF)
(41A0) ... (41BF) (41C0) ... (41DF)
to to
(47A0) ... (47BF) (47C0) ... (47DF)
Eighth line:
(E0) (E1) (E2) (FF)
(40E0) 00000000 00000000 00000000 ... 00000000 (40FF)
(41E0) 00000000 00000000 00000000 ... 00000000 (41FF)
(42E0) 00000000 00000000 00000000 ... 00000000 (42FF)
(43E0) 00000000 00000000 00000000 ... 00000000 (43FF)
(44E0) 00000000 00000000 00000000 ... 00000000 (44FF)
(45E0) 00000000 00000000 00000000 ... 00000000 (45FF)
(46E0) 00000000 00000000 00000000 ... 00000000 (46FF)
(47E0) 00000000 00000000 00000000 ... 00000000 (47FF)

215
The second and last thirds are similarly organized, with
all the addresses increased by 800h and 1000h respectively.
Try the command:
FOR f=16384 TO 22527: POKE f,PIXELS: NEXT f
with various values in PIXELS: BIN 10101010/170d, BIN
11001100/
204d and BIN 11110000/240d are obvious ones to start with.
This will probably help you to get a grip on the above
explanation.
2. There are at least three_coordinate_systems for
locations on the screen.
The two used in BASIC are
the_AT/TAB_coordinates, in which character lines are
numbered from zero at the top of the screen to 23d at the
bottom, and columns or tabs from zero on the left-hand edge
to 31d at the right
the_pixel_coordinates, numbered from zero at the bottom
of the screen to 176d at the top and from zero on the left to
255 on the right, as for BASIC PLOT commands.
But in ROM the_print_position_coordinates or_position
_values are more often used for character coordinates; in
these the character_line_numbers are from one at the bottom
of the screen to 18h/24d at the top, and the_column_numbers
from one at the end of the line to 20h/32d at the beginning.
There are two complications:
2.1. Of the 18h/24d print lines on the screen, normally
16h/22d lines are used as the_upper_part, where program
listings and most program outputs are displayed, and two
lines as the _lower_part where the editing or INPUT display
appears; the top line of the lower part is always left blank, so
there is effectively only one. But this_lower_screen is
automatically expanded if more lines are needed; see
scrolling.
The upper and lower screen have their_print_lines
numbered separately, in both cases starting from 18h at the
top:

216
so after a CLS the upper screen lines run from 18h/24d down
to 03, the lower screen from 18h/24d to 17h/23d.
2.2. The print position held in system variables is
always the last position printed: to print a new position the
column number is decremented, and if it reaches zero a new
line is started. When a new line is started, the column number
put in the system variables is 21h/33d. Column number zero is
never stored.
Position values and AT/TAB coordinates both give
positions for_character_areas of eight pixel bytes each, which
are not consecutive bytes in the display area.
3. The same output routine 09F4 PRINT OUT is used to
print on all parts of the screen, and on the ZX printer; it is the
output routine of all the standard channels except channel
"R".
However, when the channel is selected, various flags are
adjusted by 1615 CHAN FLAG, including TV FLAG bit zero
which is set for channel K to print to the lower part of the
screen, cleared for channel S to print to the upper part. So all
keyboard input (channel K) appears first in the lower screen.
You can print in the lower screen from BASIC or m/c by
selecting streams zero or one, or in the upper by selecting
stream two, by PRINT #0 and PRINT #2, or from m/c by a call
to
1601 CHAN OPEN with zero, one or two in the A register.
4. Three current position values and two display area
addresses are stored at all times in system variables, and
updated by 0ADC PO STORE each time the position value
changes:
5C88 S POSN holds the position value in the upper screen
5C8A S POSNL holds the one in the lower screen
5C7F P POSN holds the column number only of the
position
in the print buffer, not of course a screen address; this is a
one-byte sv
5C84 DF CC holds the display area address corresponding
to the first byte of the upper screen print position

217
5C86 DF CCL holds the display area address
corresponding to the lower screen print position
Other system variables holding position information are:
5C82 ECHO E is a back-up for the lower screen position
values, used to restore it after the flashing "?" error cursor
has been printed.
5C7D COORDS holds the PLOT coordinates of the last
point
PLOTted or DRAWn on screen.
5C6B DF SZ holds the number of print lines currently
being used in the lower screen; this is also the position value
line number of the blank top line of the lower screen.
5C8D ATTR P holds the attributes to be used in printing
the next character in the upper screen; in the lower screen
the colours in 5C48 BORDCR are used. See colours.
Introduction output may be to upper or lower screen
06A0 SA SCR$ display + attributes are 1B00 bytes
0767 LD LOOK H display filename on screen
09F4 PRINT OUT output may be to upper or lower screen
0A23 PO BACK 1 manipulates position values: mistake
0A38 PO BACK 2 col no 21h for start of line
0A4F PO ENTER down a line, test for 'scroll?'
0A5F PO COMMA manipulates column number
0A87 PO CONT coordinates of AT become position values
0AAC PO AT ERR lower screen may need scrolling for AT
0AC3 PO FILL figure no of spaces to TAB
0ADC PO STORE loads svs for upper screen
0AF0 PO ST E loads svs for lower screen
0B03 PO FETCH position values got from svs
0B52 PO T&UDG position values stacked
0B65 PO CHAR "character area", read "character set"
0B76 PO CHAR 3 position values unstacked
0B7F PR ALL "destination address" means address of the
pixel byte in the display area, "line &
column values" means position values
0B93 PO ALL 1 scrolling may be required after down
move

218
0BA4 PR ALL 2 "pixel line" here means "pixel byte";
prints eight for each character
0BB7 PR ALL 4 display address on 100h for each pixel
0BC1 PR ALL 5 position values and display address
incremented after each print
0BDB PO ATTR converts display address to attribute
0C55 PO SCR test if print position requires scroll
0C88 PO SCR 2 lower to be cleared after keystroke
0CD2 PO SCR 3 scrolling display area is complex
0CF0 PO SCR 3A gets last line of upper screen
0D02 PO SCR 4 handles scrolling of lower screen
0D1C PO SCR 4A handles scrolling of lower screen
0D2D PO SCR 4B signals "using lower screen"
0D4D TEMPS permanent colours for upper screen in
ATTR P,
for lower in BORDCR
0D5B TEMPS 1 different handling of P FLAG
0D6B CLS lower screen reformed after clearing screen
0D6D CLS clears whole screen and resets lower to 2 lines
0D6E CLS LOWER clears lower screen
0D8E CLS 3 set lower screen at two lines
0DA0 CL CHAN A lower print line set at 17h/23d
0DAF CL ALL 24d lines cleared, start posn value is 1821h
0DD9 CL SET finds line number in lower screen from line
number on screen
0DEE CL SET 1 gets line start address from position
value
0DF4 CL SET 2 adds current TAB for display address
0DFE CL SC ALL count of lines to be scrolled
0E00 CL SCROLL prepare to scroll eight pixel lines
0E05 CL SCR 1 checks for scrolling across thirds
0E0D CL SCR 2 moving across thirds boundaries
0E19 CL SCR 3 scrolling within thirds
0E44 CL LINE bottom B lines cleared
0E4A CL LINE 1 clear eight pixel line
0E4D CL LINE 2 loop to clear pixel row of a whole third;
uses BORDCR for lower screen attributes

219
0E80 CL LINE 3 line start position is column 21h
0E88 CL ATTR converts display to attribute address
0E9B CL ADDR finds start address of Bth line from
bottom
0EAC COPY all pixel bytes sent to printer
0EB2 COPY 1 complex loop; sends 20h bytes to printer
for each pixel line, jumping correctly to
next line and to next third when necessary
0EC9 COPY 2 counts 176 pixel lines
0ECD COPY BUFF eight pixel lines sent to printer
0ED3 COPY 3 counts eight pixel lines
0EF4 COPY LINE sends one line of 20h pixels to printer
10A8 KEY INPUT print edit line to screen with new cursor
and clear lower screen if required
111D ED COPY copy edit or input line to lower screen
1150 ED BLANK overwrite surplus of old line with spaces
117C ED DONE edit or INPUT line copied to lower screen
117E ED C END old position values saved in ECHO E
1219 RAM SET clear and set screen with copyright
message
12A2 MAIN EXEC make lower display 2 lines
12CF MAIN 3 on direct command, clear lower screen and
upper if appropriate
1313 MAIN G clear lower screen
15D4 WAIT KEY screen clearing signal on entry
1634 CHAN K signal "using lower screen"
1795 AUTO LIST clear both parts of screen
1CBE CLASS 09 set flag for main screen
1EAC CLEAR clears display
1EB7 CLEAR 1 calls 0D6B CLS to clear display
2089 INPUT embedded print items in lower screen
2096 INPUT 1 resets upper print position if lower
screen was expanded
20AD INPUT 2 restore flag to "main screen"
2161 IN VAR 4 input line displayed and ECHO E used
22AA PIXEL ADD finds display address of pixel bit from
PLOT coordinates (Ath bit of (HL))

220
22CB POINT SUB checks if a pixel is INK or PAPER
22DC PLOT plots pixel at given coordinates
22E5 PLOT SUB sets counter to find pixel bit
22F0 PLOT LOOP makes pixel zero at given address
22FD PL TST IN pixel zero or one depending on INVERSE
2535 S SCRN$ S find character code given AT/TAB address
254F S SCRN$ LP screen pixel byte matched with char
form
255D S SC ROWS checks seven more pixel bytes with form
2580 S ATTR S find attribute code given AT/TAB address
361F str$ work space is output to as if screen

division subroutine 31AF see also precision


Called frequently in ROM from 0028 FP CALC with the
literal 05; not otherwise called from ROM. Can be called direct
from m/c.
Executes the "M/N" operator; divides the last value N on
the calculator stack into the second last M.
This calculator subroutine doesn't itself use the
calculator.
Consideration of the structure of FP numbers suggests
that division or multiplication is really rather simpler than
addition or subtraction; see 335B CALCULATE. Essentially
what is required is
- divide the mantissa of M by the mantissa of N
- subtract the exponent of N from the exponent of M.
We are of course talking "true mantissas" and "true
exponents" here: FP numbers as stored have their sign
indicated by zeroing the hi bit of the true mantissa for positive
numbers, and the true exponent incremented by 80h.
The actual division of the mantissas, call them M' and
N', is done by repeated subtraction, analogous to the
schoolbook long division algorithm, what the notes
call_restoring_division.
Long division is simpler in binary arithmetic than in decimals:
take N' from M'
if there is carry, it "doesn't go", so add it back, put a

221
zero on the result, shift N' right and subtract again;
if there is no carry, put a one on the result, shift N'
right and subtract again.
Like long division, the algorithm isn't self-terminating
unless N' divides M' exactly or N' is a power of two; you can go
on with a "recurring binary fraction" as long as you like, with
ever-increasing apparent precision in the result. But it isn't
worth going on to more precision than you had in the first
place.
The mantissas are 32-digit binary numbers, so the 33rd
digit of the result is significant. The 34th digit is also
significant if N' is greater than M'; for both M' and N' are
between half and one, so the result mantissa has one leading
zero in this case, which will be shifted out of its hi bit when it
is normalized, with the 33rd put in the lo bit. Then the 34th
should be used to round the 33rd.
So the subtracting loop is turned 33d times, putting 32d
digits in the result mantissa and the 33rd in the carry flag on
the stack; then it is turned a 34th time, without altering the
digits in the result mantissa, but putting the 34th digit on the
stack on top of the 33rd.
[However, there is an error in COUNT ONE, where the
programmer made a jump back to DIV START when he should
have jumped to DIV 34TH instead; so the right shift of N'
doesn't get done on this last subtraction, and the 34th bit
always comes out zero even when it should be one. Thus in
practice the ROM only gives 33-digit precision even though it
is at pains to provide 34 digits. The effect of the error isn't
often seen, but if you type
PRINT 10**8 * (0.1 - 1/10)
you will find the answer comes out as one, which is wrong - it
should be zero.]
The only serious complication is that even after the
exponents have been put on the machine stack out of the way,
thirteen bytes have to be handled simultaneously: four each
for the mantissas of M, N and M/N, plus a loop counter.
However, the long division can just be handled by repeated

222
subtraction of D'E'DE from H'L'HL, with the result going into
B'C'CA and the counter in B; A' isn't used!
The rest is just careful checking to reject overflow
results, make sure the precision of the result is neither
inadequate or redundant, and "normalize" the result, ie adjust
its sign bit and exponent to standard FP number format and
make its mantissa come out between a half and one.
Input parameters: HL points to the first byte of M, DE to
the first of N; they must be FP numbers consecutive in RAM, N
after M. For a call through the calculator they must be the two
last values on the calculator stack, but for a direct call they
could be located anywhere in RAM; not in the ROM, because
the position at HL is overwritten with the result.
Action: call 3293 RE ST TWO to reformat M and N in full
FP format; it reformats them at any position, not necessarily
on the stack
- make a zero sign byte; its hi bit will be set for a
negative result
- call 30C0 PREP M/D to check and modify N: if N is
negative, it makes the sign flag negative, and it sets the hi
bit of the mantissa to make the true mantissa N'
- if N is zero report "Number too big"; can't divide by
zero
- call PREP M/D again with M: if M is negative, it
changes the sign flag again, and it makes the true mantissa M'
- if M is zero, return: the result is zero, and the
deletion of N from the stack will be automatic on arrival at
3365 RE ENTRY
- stack the "return address" pointer in HL' and the
pointers to M and N; all the two-byte registers are going to be
needed
- call 2FBA FETCH TWO, which gets M and N into ten
registers: M into H'B'C'CB, N into L'D'E'DE; and puts the +/-
sign of the result in the second byte of the M position/result
position, from which it will be retrieved right at the end of
the exit routine in 3195 OFLOW CLR
- put the exponent bytes H' and L' on the machine stack

223
- transfer M' to H'L'HL
- zero the lo byte of the result mantissa in A
- make a bit counter DFh/minus 33d to produce 33d digits
of the result
- jump on into the loop at DIV START.
_31D2_DIV_LOOP (the carry flag shows in reverse the
result of subtracting N' in D'E'DE from M' in H'L'HL, zero if it
"won't go" or set if it will): rotate carry flags left in turn
into A, C, C', B', each one picking up in turn as its lo bit the
hi bit of the one before; thus the result in B'C'CA is shifted
one bit left with the new digit going into its lo bit.
_31DB_DIV_34TH (N' should be shifted right before going
round the loop again. But it is difficult to shift four bytes
right without losing precision in the last bit. So craftily
shift M' left instead, with zero going into the last bit):
double H'L'HL
- if this makes carry jump on to SUBN ONLY; M' is
effectively 33 digits and N' is only 32, so whatever the result
of the next subtraction treat it as producing no carry and put
a one on the result.
_31E2_DIV_START: take N' in D'E'DE from M' in H'L'HL
- if there is no carry jump on to NO RSTORE
- (there is carry) add N' back to M' again; "won't go"
- clear the carry flag; signalling "digit zero" for the
result
- jump on to COUNT ONE.
_31F2_SUBN_ONLY (only entered from DIV 34th): take N'
from M'; the next line will set carry and put one in the result
without regard for the carry from this subtraction.
_31F9_NO_RSTORE (don't add back): set the carry flag;
signalling "digit one" for the result.
_31FA_COUNT_ONE: increment the bit counter; this doesn't
affect the carry flag, but it does affect the other flags
- if its hi bit is still set loop back to DIV LOOP; it
is still less than zero
- (33d or 34d turns of loop complete) stack the carry
flag; it represents the 33rd or 34th digit

224
- if the bit counter is zero jump back to DIV START; the
loop has been turned 33d times [should have jumped to DIV
34TH, see above]
- (34th turn complete; the 33rd and 34th digits are both
on the machine stack in the form of carry flags) move the
result mantissa into D'E'DE
- recover the 34th and 33rd digits and rotate them into
bits 6 and 7 of B'; making a five-byte mantissa D'E'DEB', which
will be normalized by the exit routine
- recover the exponent of N and the exponent of M and
the pointer to the result; it will go wherever M was on entry
- subtract exp M - exp N for the result exponent; these
are standard form exponents, with their hi bit set, but the
result of the subtraction is a true exponent and the
subtraction will set the carry flag for a negative result.
However it is one less than the true exponent which actually
corresponds to the mantissa value: the true mantissas M' and
N' were both numbers between a half and one, so the result
mantissa should be between a half and two. But the mantissa
actually resulting is between a
quarter and one, so in the build-up of its digits it has been
shifted one place right. This is corrected in the exit routine.
Exit: to 313D DIVN EXPT, which makes various checks and
"normalizes" the result into standard FP format before putting
it back in the five bytes of the M position - now the last value
on the calculator stack, if it was on the stack.
Output parameters: D'E'DEB' holds the 5-byte result
mantissa;_not in standard form, its hi bit doesn't show its sign
and may even be a leading zero
- HL points to the result position
- the byte after the result pointer holds the result
sign byte, with hi bit marking the sign
- A holds the true exponent, less one
- the carry flag is set if the exponent is negative
- there are two numbers on the machine stack, the top
one a pointer to the N position, the next down the "return
address" pointer

225
- the calculator memory hasn't been disturbed.
Called from:
0427 BE OCTAVE
23A3 DR SIN NZ
23C1 DR PRMS
247D CD PRMS1 (twice)
2497 DRAW SAVE
2CDA NXT DGT 1
2D6D E DIVSN
36A0 n-mod-m
37DA tan
37E2 atn
3833 asn
385D XISO
Rems:
2F9B PREP ADD lists main arithmetic operations
2FBA FETCH TWO sign of result saved in sign byte of M
30C0 PREP M/D sets true mantissa, handles sign & zero
316E SHIFT ONE correct precision in rounding
313D DIVN EXPT used by both multiply and division

DIV LOOP 31D2 (31AF division)


Jumps from:
31FA COUNT ONE

DIVN EXPT subroutine 313D


Corrects the exponent of the result of the multiply and
division subroutines. Also carries out some tests for
arithmetic overflow and underflow - see under overflow in the
index.
The routine is quite short, only 24d bytes to the exit
into TEST NORM, but so concise that rather a lot of
explanation is required.
Correcting the true exponent to the full FP form means
essentially flopping its hi bit, because a negative true
exponent will have its hi bit set and a positive one won't, but
in FP form this is reversed.

226
This would be simple enough, but for the overflow tests:
the exponent byte overflows if it is less than zero or more than
FF in standard form, ie if the true exponent is less than -80h
or more than +7F. However even a +80h true exponent will be
acceptable if the mantissa resulting from division is less than a
half, because in normalization the mantissa will be shifted left
and the exponent decremented. There will be at most one
such shift and decrement; both input mantissas are between a
half and one, so the result whether of multiplication or
division can't be less than a quarter.
The resulting difficulties are handled by entering the
routine with the exponent byte one less than its "true" value
and with the carry flag signalling whether it is to be read as
positive or negative. The entry value may be anything from 00
- FF:
- values 00 -> 7F are changed to 80 -> FF, then
incremented to 81 -> 00. 81 -> FF are accepted; they are within
the accepted range of positive exponents, and if they are
negative ones, with true exponent minus FFh -> minus 81h,
the result will be indistinguishable from zero in the Spectrum
system and will be set to zero by 3155 TEST NORM. Zero is
accepted as a negative exponent, but rejected as a positive
exponent unless the mantissa will need shifting, which will
reduce it to FF
- values 80 -> FF are changed to 00 -> 7F and rejected if
they are meant for positive; they would represent true
exponents of 81h -> 100h, which are overflows. If they are
meant for negative they are incremented to 01 -> 80h and
accepted, with the carry flag cleared on entry to TEST NORM.
This is no longer read as a flag for "positive", it means "no
need to test for underflow".
There is only one possibility of overflow missed by
these tests: a number whose exponent is made FF in standard
format and whose mantissa before normalizing is FF FF FF FF,
with a 33rd bit which will cause rounding up. This is handled
in the exit routine by 3186 NORML NOW.
Input parameters: D'E'DE holds the mantissa of the result

227
- A holds its exponent, less one; both are in "true"
form
- HL points to the first byte of the result position:
normally the last but one number on the calculator stack,
though
it will become the last after the exit routines are completed
- the byte after the result pointer holds the sign byte
of the result, with its hi bit set for negative
- the carry flag shows C if the exponent is negative
- there are two numbers on the machine stack, the top
one a pointer to the second number input for the operation,
the multiplier/divisor, the next down the "return address"
pointer.
Action: rotate the hi bit of the exponent into carry,
flop it and rotate it back; the input carry flag is recovered
unchanged from these three operations [and_the_positive/
negative_flag_is_also_unchanged_from_its_input_value; neither
rotations nor CCF affect it. It would have been nice if Logan
and O'Hara had thought to mention this in their notes. It took
me the best part of a week to figure out what was happening]
- if the P/N flag shows hi bit zero jump on to OFLW1
CLR; ie if the exponent as input was 7F or less
- (exponent input 80h or more) if the carry flag
indicates a positive exponent report "Number too big"; true
exponent more than +80h
- (negative exponent, now 00 -> 7F) clear the carry
flag; now the only way carry can be set is if the input
exponent was flagged as negative with a "true" value less than
80h,
jumping past this point with the P/N flag. These would be very
small numbers, so from here on carry means "test for
underflow".
_3146_OFLW1_CLR: increment the exponent; in the case of
multiplication it reverses the DEC A in 313B MAKE EXPT, for
division the input exponent was one too small for its
mantissa,

228
see 31FA COUNT ONE under 31AF division [this also required
more explanation than it gets from Logan and O'Hara!]
- if this doesn't make zero jump on to OFLW2 CLR; the
exponent is either 01 -> 7F with carry, a number probably to
be equated with zero; or 01 -> FF without carry, an acceptable
negative or positive exponent
- (the exponent is zero) if the carry flag shows C jump
on to OFLW2 CLR; INC A doesn't change the carry flag, so
carry is unchanged from input, the exponent is negative and
the number is very small. The carry flag now signals "test for
underflow"
- (the incremented exponent is zero, and marked as
positive) if the hi bit of D' is set report "Number too big";
there will be no left shift of the mantissa to reduce exponent
00 to FF.
_3151_OFLW2_CLR: put the exponent byte in the result
pointer address and the 5th mantissa byte B' in A.
Exit: into 3155 TEST NORM, which checks for arithmetic
underflow, completes "normalization", ie shifts the mantissa
left with decrement of the exponent till it has no leading
binary zeroes and corrects its sign bit, and puts the result on
the stack.
Output parameters: HL points to the first byte of the
result FP form
- the byte after the result pointer holds a sign flag
- D'E'DEA holds the 5-byte mantissa
- the carry flag set signals "test for underflow", ie a
normalized exponent of zero or less
- there are still two numbers on the machine stack, the
top one a pointer to the second number input for the
operation,
the multiplier/divisor, the next down the "return address"
pointer.
Exit from:
313B MAKE EXPT (30CA multiply)
31FA COUNT ONE (31AF division)

229
DIV START 3132
Jumps from:
31AF division
31FA COUNT ONE

DIV 34TH 31DB (31AF division)


Label not used in the ROM. It should have been! There is
a mistake in ROM at 31FA COUNT ONE, see under 31AF
division.

D L DIAG 24D2 (24B7 DRAW LINE)


Jumps from:
24CE D L LOOP

D LETTER 2C1F (2C02 DIM)


Jumps from:
2C15 D RUN

D L HR VT 24DB (24B7 DRAW LINE)


Jumps from:
24CE D L LOOP

DL LARGER 24CB (24B7 DRAW LINE)


Jumps from:
24B7 DRAW LINE

D L LOOP 24CE (24B7 DRAW LINE)


Jumps from:
24EC D L PLOT

D L PLOT 24EC (24B7 DRAW LINE)


Exit from:
24F7 D L RANGE

D L RANGE 24F7 (24B7 DRAW LINE)


Jumps from:
24DF D L STEP

230
D L STEP 24DF (24B7 DRAW LINE)
Jumps from:
24D4 D L DIAG

DL X GE Y 24C4 (24B7 DRAW LINE)


Jumps from:
24B7 DRAW LINE

D NO LOOP 2C2E (2C02 DIM)


Jumps from:
auto

DOUBLE A 338C (335B CALCULATE)


Jumps from:
3380 FIRST 3D

double quotes see " (code 22h) at end of alphabet

DRAW key (FC) see also commands, functions and


operators,
KEYBOARD SCANNING
The W key in K mode produces the command DRAW,
which can be followed by print items, but must be followed by
two or three numeric parameters X,Y,Z; each may be negative.
X and Y are the number of pixels to the right (X) and up (Y) to
be covered by the DRAWn line, and Z the anticlockwise angle
in radians through which the line is to turn - assumed zero, ie
a straight line, if no value is given. See under 2783 get-argt if
you aren't sure about radians. The start of the line is the last
position set by PLOT or reached by DRAW or CIRCLE.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AD2 P DRAW causes jumps to 1CBE CLASS 09, which effects
FLASH

231
8; BRIGHT 8; PAPER 8 and then executes any embedded
colour items
before putting X and Y on the stack, and via 1C11 CLASS 05 to
1C16 JUMP C R and the executive routine 2382 DRAW.

DRAW subroutine 2382


Called only from the statement loop by 1AD2 P DRAW in
the syntax parameter table; the executive routine of the
DRAW command. Draws an arc on screen using parameters
- X: number of pixels to move right, or left if negative
- Y: number of pixels to move up, or down if negative
- G: angle in radians to turn to left, or right if
negative - see under 2783 get-argt if you aren't sure about
radians.
Not easy to call direct from m/c; better to call 2394
(RST 0028h) with the three parameters already on the stack if
an arc is required. For a straight line, either do the same with
zero as last parameter, or call 2477 LINE DRAW with only two
parameters on the stack. Beware! These routines, and any
using
24B7 DRAW LINE, may switch to the alternate registers.
If a curved arc isn't called for, there is nothing for
the subroutine to do: it merely exits to 2477 LINE DRAW,
which will draw straight lines without further preparation.
Nearly all the subroutine is concerned with calculating
parameters to draw an arc, first for 247D CD PRMS1 - which
calculates an "arc counter" a and the turning angle for each
"arc" - and then for 2420 DRW STEPS, which actually draws
the "arcs". "Arc" in quotes means a short straight-line
approximation to a small arc.
CD PRMS1 requires as parameters only G, the turning
angle, which is given from BASIC, and the approximate
diameter of the circle of which the line to be drawn will form
a part.
The true length of this diameter, by fairly simple
trigonometry, is
SQR (X**2 + Y**2)/SIN (G/2)

232
using absolute values for X, Y and G. The approximation uses
X + Y instead of SQR (X**2 + Y**2), which is rather crude - it is
always too big, and can be as much as 1.414d... (root two)
times
too big when X and Y are equal. But it is only used to calculate
the_number_of_"arcs" which will be drawn to approximate to
the curve, and this isn't at all a critical figure. The program
nearly always draws more and shorter "arcs" for a DRAW
curve than for a CIRCLE.
DRW STEPS requires as input parameters
- the PLOT coordinates X0, Y0 from which drawing is to
start; easy, these are safely in 5C7D COORDS
- the PLOT coordinates X0 + X, Y0 + Y where it is to end;
these are the start coordinates plus the X, Y input from BASIC,
which are still on the stack
- the x and y displacements to draw the first straight-
line "arc", called U and V in the notes; these will be repeated a
times turning through T = G/a each time.
The only ones which are hard to figure are U and V. The
calculation for them is also hard to explain - the notes do
their best, but without a diagram and without proper
mathematical typography it was impossible to be really clear.
A little spelling out may help:
The_length of the first of the a steps will be longer
than it would be if you took a steps along the straight line
from start to finish positions. If the length of this straight
line is L, the length of the steps would be L/a, but the length
of our circuitous steps comes out as L * W where W is more
than 1/a, in fact

SIN (G/2a)
W = ----------
SIN (G/2)

SIN (T/2)
= --------- (using T = G/a as
above).
SIN (G/2)
233
The smaller G and T are, the nearer will W be to 1/a and
this length to L/a, but it will always be a little longer - a
straight line is after all the shortest distance between two
points.
The_direction of the first step will be somewhere to the
right of the straight-line direction, assuming that the angle G
represents a turn to the left, and vice versa. The angle of this
aiming off comes out as
G/2 - G/2a = G/2 - T/2
This is a small angle if G and a are small, not many
steps to turn through a small angle, and bigger if they are
bigger, more steps for a bigger angle; as you would expect.
To turn a direction whose coordinates are x, y through
an angle F without any change in the length, the standard
formulas for the new direction P, Q are
P = y SIN F + x COS F
Q = y COS F - x SIN F
But of course U and V are reduced in length, compared
with the X and Y of the straight line step L, by the reduction
factor W. Putting XW, YW in the above equations, with G/2 - T/
2 for F, you get the equations given in the
notes,
U = YW SIN (G/2 - G/2a) + XW COS (G/2 - G/2a)
V = YW COS (G/2 - G/2a) - XW SIN (G/2 - G/2a)
The program uses the calculator:
- to get W, which is put in mem-1;
- to get X*W, which goes in mem-2;
- to get Y*W, which stays on the stack;
- to get G/2 - T/2, which the notes call F; SIN F goes in
mem-5 and COS F in mem-0;
- to put X*W in mem-2 again, because 37BF sin overwrote
mem-0 to mem-2, and now Y*W in mem-1;
- to calculate U as Y*W SIN F + X*W COS F and put it in
mem-1;
- to calculate V as Y*W COS F - X*W SIN F and put it in
mem-2.

234
Everything needed by DRW STEPS is now in place. If after
all this the first "arc" step comes out at less than one pixel the
subroutine again exits to LINE DRAW to draw a straight line.
Input parameters: 5C7D COORDS holds the last PLOT
position
- HL points to the first byte of the second last value X
on the calculator stack, DE to the last value Y
- the BASIC pointer in 5C5D CH ADD is on the character
after the Y parameter.
Action: read the character at CH ADD
- if it is 2C comma jump on to DR 3 PRMS
- (there is no third operand) call 1BEE CHECK END, which
in syntax checking reports "Nonsense in BASIC" if this isn't the
end of a statement or makes a double return to the statement
loop at 1B76 STMT RET if it is
- (run time) exit to 2477 LINE DRAW to draw a straight
line.
_238D_DR_3_PRMS (there is a third parameter): move on
the BASIC pointer
- call 1C82 EXPT 1NUM to read the next expression on to
the calculator stack; the turning angle G
- call 1BEE CHECK END; with effect as above
- (run time; the stack holds, from the top, G, Y, X) use
the calculator to get SIN (G/2)
- if it isn't zero jump on to DR SIN NZ; see under 3501
not for the calculator equivalent of JR NZ,DR SIN NZ
- (SIN G/2 is zero) delete it and exit to 2477 LINE DRAW
to draw a straight line.
_23A3_DR_SIN_NZ (the stack holds, from the top, SIN G/2,
Y and X): calculate the absolute value of
(ABS X + ABS Y)/SIN (G/2),
the very approximate diameter of the circle; called Z in the
notes
- re-stack Z to ensure it is in full FP form; now the
stack holds Z, SIN G/2, Y, X; the first two are the required
initial parameters for CD PRMS1
- if the exponent of Z is 81h or more jump on to DR

235
PRMS; ie if Z is one or more
- (Z less than one) delete Z and SIN G/2 from the stack
- exit to 2477 LINE DRAW to draw a straight line.
_23C1_DR_PRMS: call 247D CD PRMS1; it returns the
calculator stack unchanged, Z, SIN G/2, Y, X, with the arc-
count a in the B register
mem-0 = the angle T = G/a through which each "arc" must
turn
mem-1 = SIN (T/2)
mem-3 = COS T
mem-4 = SIN T
- save the arc count on the machine stack
- use the calculator again to figure the initial
displacements U and V explained above. The steps are
- (down to st-mem-1) calculate W = SIN (T/2)/SIN (G/2)
and put it in mem-1
- (down to st-mem-2; the stack holds W, Y, X) calculate
X * W and put it in mem-2
- (down to multiply; the stack holds X * W, X, Y)
calculate Y * W
- (down to st-mem-0; the stack holds Y * W, Y, X)
calculate F = G/2 - T/2, put SIN F in mem-5 and COS F in
mem-0
- (down to st-mem-1; the stack holds COS F, X * W, Y *
W, Y, X) put X * W in mem-2 and Y * W in mem-1; this couldn't
be
done before because 37BF sin and 37AA cos corrupt mem-0 to
mem-2
- (down to st-mem-1; the stack holds Y * W, Y, X)
calculate U, the x displacement for the first "arc":
U = YW SIN F + XW COS F
and put it in mem-1
- (down to st-mem-2; the stack holds U, Y * W, Y, X)
calculate V, the y displacement:
V = YW COS F - XW SIN F
and put it in mem-2
- (to end-calc; the stack holds V, Y, X) calculate ABS U

236
+ ABS V and immediately delete it! This leaves Y, X on the
stack, and ABS U + ABS V in the FP number location pointed
to by 5C65 STKEND
- if the exponent of ABS U + ABS V is less than 81h exit
to 2477 LINE DRAW; draw a straight line, the "arc" length is
less than one
- use the calculator just to exchange X and Y on the
stack
- call 2D28 STACK A to put the x coordinate from 5C7D
COORDS of the last PLOT/DRAW on the calculator stack;
now it holds X0, X, Y

- use the calculator to put X0 in mem-0


- find X + X0, the x coordinate of the end of the curve
- call 2D28 STACK A to put Y0 from 5C7D COORDS on to
the stack; now the stack holds Y0, Y, X0 + X
- put it in mem-5
- find Y + Y0
- exit from the calculator with Y0, X0, Y0 + Y, X0 + X
on the stack
- recover the arc count.
Exit: to 2477 LINE DRAW if a straight line results,
otherwise into 2420 DRW STEPS.
Output parameters: for DRW STEPS, B holds the arc count
- HL and DE hold pointers to the second last and last
values on the stack
- the stack holds, from the top, the start coordinates
of the line to be drawn Y0, X0, and its end coordinates Y + Y0,
X + X0
- mem-1 holds U
- mem-2 holds V
- mem-3 still holds COS T from CD PRMS1
- mem-4 still holds SIN T from CD PRMS1
for LINE DRAW, 5C7D COORDS holds the coordinates of
the start of the line, the calculator stack holds the DRAW
operands Y, X.
Rems:

237
22DC PLOT main routine called as subroutine by DRAW
(actually 24EC D L PLOT in 24B7 DRAW LINE)
2307 STK TO BC used in line drawing subroutine
(24B7 DRAW LINE)
2320 CIRCLE jumps into (actually 2420 DRW STEPS)
233B C R GRE 1 jumps into (actually 2420 DRW STEPS)
235A C ARC GE1 jumps into (actually 2420 DRW STEPS)
2420 DRW STEPS requires inputs as above
2439 ARC START uses approximate value found by
247D CD PRMS1 called by DR PRMS, set initial parameters
24B7 DRAW LINE called by

DRAW LINE subroutine 24B7


Draws a straight line on screen, starting at the last
PLOT position, with DRAW coordinates X and Y; either or
both may be negative.
Each one-pixel_step is checked for_range, ie to ensure
that the line hasn't gone off the screen; the check of the y
coordinate is actually done in the 22E5 PLOT SUB subroutine
called from D L PLOT.
The algorithm for plotting these pixels is one of the
most elegant in the Spectrum ROM; it was taken over from the
old ZX80, see Appendix on pages 228-9 for BASIC
representations.
Two unit steps are computed, a_diagonal_step - one pixel
up or down and one left or right - and a_square_step, one pixel
vertically or horizontally only. If Y, the vertical displacement
in drawing the line, is greater in absolute value than X, the
horizontal one, the square step is made vertical, if X is
greater it is made horizontal. If Y is positive the step or steps
are made to go positive/upwards and if X is positive they are
made to go to the positive/right, otherwise downwards and/or
to the left.
Each position is now looked at successively to see which
unit step, diagonal or square, will move closer to the end
point of the line, and a pixel is plotted accordingly. The total
number T of steps to be taken is the larger of the absolute

238
values of X and Y; because both the diagonal and the square
step move in the "larger" direction. The number D of_diagonal
steps will be the smaller of these absolute values; because
only the diagonal steps move in the "smaller" direction. If T =
D all the steps will be diagonal.
T steps are taken with a "step indicator", adjusted for
each step, which signals whether a diagonal or square step
is to be taken. The step indicator:

- is set at T/2 initially


- is incremented on each step by D
- if this makes it bigger than T, signals a diagonal
step; if not, a square step
- on each_diagonal step is decremented by T.
After d diagonal and s square steps have been taken the
indicator will hold
T/2 ; initial value
plus D(d + s) ; D added on every step
minus T * d ; T deducted on diagonal steps
= D(d + s) - T(d - 1/2)
All these numbers are positive integers.
If this result is more than T,
ie if D(d + s) - T(d - 1/2) >= T, then
D(d + s) >= T(d + 1/2), so
D(d + s) > dT,
or, dividing both sides by Dd,

s T
1 + --- > ---
d D

But if the step indicator is less than T, a very similar


calculation will show that

s T
D(d + s) <= dT, or 1 + --- <= ---
d D

239
T/D represents the slope of the line from its start to
its finish, and each plot must stay as close to this slope as
possible: in the first case we want to reduce s/d, so we
increase d by taking a diagonal step; in the second we want to
increase s/d, so we increase s by taking a square step. QED!
The routine could be called from m/c, but beware! the
registers are switched to alternates on every turn of the step-
drawing loop; if there is an odd number of PLOTs they will be
switched on exit.
Input parameters: 5C7D COORDS holds the coordinates
X0, Y0 of the last plot on screen
- the DRAW operands X, Y are the last two values on the
calculator stack, their first bytes addressed by HL and DE
respectively.
Action: call 2307 STK TO BC, which puts ABS Y (hi) and
ABS X (lo) in BC, and SGN Y (hi) and SGN X (lo) in DE; DE thus
represents a diagonal unit step in the correct directions
- if ABS X is greater than or equal to ABS Y jump on to
DL X GE Y
- (ABS Y is greater, so the square step is vertical) put
ABS X in L; this is the number of diagonal steps
- stack the diagonal step
- put zero in E, making DE a vertical square step in the
correct direction
- jump on to DL LARGER.
_24C4_DL_X_GE_Y (ABS X >= ABS Y; the square step is
horizontal): if X and Y are both zero, return; there is no line
- put ABS Y in L (the number of diagonal steps) and ABS
X in B (the number of all steps)
- stack the diagonal step; it couldn't have been done
before the jump, because of the RET
- put zero in D, making DE a horizontal square step in
the correct direction.
_24CB_DL_LARGER: put B in H; now HL holds the total
number of steps T in H and the number of diagonal steps D in
L

240
- make a byte from B shifted one bit to the right; the
step indicator INT (T/2).
_24CE_D_L_LOOP: add D to the step indicator
- if there is carry jump on to D L DIAG; take a diagonal
step. The apparent complication of the increment producing
carry is self-correcting: such an increment is certainly big
enough to call for a diagonal step, and then subtracting T,
which is bigger than D, comes out the same as if there had
been no carry

- if the step indicator is now < T jump on to D L HR VT;


take a square step.
_24D4_D_L_DIAG: decrement the step indicator by T
- copy the diagonal step on the stack into B'C'
- jump on to D L STEP with the alternate registers.
_24DB_D_L_HR_VT: save the step indicator as it is
- put the square step in B'C'
_24DF_D_L_STEP (using the alternate registers): get
the PLOT coordinates from 5C7D COORDS
- add the y step to the y coordinate
- increment the x step; make it either zero, one or two
- add it to the x coordinate
- if this makes carry jump on to D L RANGE
- if it makes zero without carry report "Integer out of
range"; both the x coordinate and the step must be zero. The
step represents minus one, a step to the left, and the PLOT
coordinates represent a position on the left edge of the
screen. The line would go off the left of the screen.

_24EC_D_L_PLOT: decrement the x-coordinate; to its


original minus one, zero or one
- call 22E5 PLOT SUB, which puts a dot on the screen at
the coordinates in BC and puts the new coordinates in 5C7D
COORDS; it reports an error if the y coordinate is out of range
- put the adjusted step indicator back in A
- jump back to D L LOOP counting down the step counter
- (all T steps have been plotted) clear the machine

241
stack and return.
_24F7_D_L_RANGE (adding the incremented step to the x
coordinate made a C flag): if the zero flag shows Z jump back
to D L PLOT; the x coordinate will be decremented to FF, still
on screen

- report "Integer out of range"; the line has gone off


the right-hand side of the screen.
Exit: RET.
Output parameters: none
- 5C7D COORDS holds the new PLOT position
- the DRAW operands have been removed from the
calculator stack
- the registers may have changed to the alternates.
Called from:
2439 ARC START
2477 LINE DRAW
Rems:
22AA PIXEL ADD range check
2307 STK TO BC supplies diagonal step in DE
2382 DRAW part of DRAW COMMAND ROUTINE
Appendix BASIC demo subroutine (p. 229)

DRAW SAVE 2497 (247D CD PRMS1)


Misprint: the header is omitted in the notes.
Exit from:
247D CD PRMS1

dropping address see address dropping

D RPORT C 2C05 (2C02 DIM)


Jumps from:
2C2E D NO LOOP

DR PRMS 23C1 (2382 DRAW)


Jumps from:
23A3 DR SIN NZ

242
DR SIN NZ 23A3 (2382 DRAW)
Jumps from:
238D DR 3 PRMS

D RUN 2C15 (2C02 DIM)


Jumps from:
2C05 D RPORT C

DRW STEPS subroutine 2420


Exit routine from the DRAW and CIRCLE routines; the
"arc-drawing loop". Used only as an exit routine, but it could
be called from m/c with input parameters properly prepared.
It is entered with
- an "arc count" a, telling how many "arcs" are to be
drawn, see 247D CD PRMS1. As elsewhere, "arcs" in quotes
means
straight-line approximations to short pieces of arc
- the start coordinates of the arc in PLOT coordinates
- the end coordinates, ditto; for a CIRCLE, these will
be the same as the start coordinates
- the displacement coordinates for the first "arc" to be
drawn
- the sin and cos of the turning angle T which each
"arc" must make with the one before.
The subroutine draws a - 1 "arcs" of equal length, each
turning an angle T from the one before, and then a "closing
arc". The "arcs" are drawn by calls to 2487 DRAW LINE, which
uses the PLOT position X,Y in 5C7D COORDS and requires
DRAW parameters U, V on the calculator stack; the main loop
of the subroutine calculates U, V for each "arc" in turn. Then
the arc is drawn, leaving the new PLOT coordinates in 5C7D
COORDS.
The DRAW parameters are calculated as FP numbers,
which DRAW LINE rounds to integers, and the PLOT
coordinates in 5C7D COORDS are always integers. The
cumulative effect of these roundings would cause the curve to

243
wander from a true circular arc after relatively few steps if it
wasn't corrected, so the subroutine loop retains in the
calculator memory a "dead reckoning" version of the PLOT
coordinates in unrounded FP form, and uses these rather
than the rounded version in 5C7D COORDS to calculate the
next DRAW parameters. For clarity, these unrounded FP
PLOT coordinates are called Xf, Yf in the description below,
and the rounded integer coordinates are called Xi, Yi.

All the "arcs" are the same length, but each is turned
though an angle T relative to the last. The DRAW coordinates
U',V' for the next "arc" are calculated from U, V, those for the
last, using the same "rotation" formulas as were used in 2382
DRAW:
U' = U COS T - V SIN T
V' = U SIN T + V COS T.
Input parameters: B contains the arc-count a
- the last two values on the calculator stack are the
coordinates X, Y of the start of the arc; it doesn't matter
whether they are rounded or unrounded, in fact CIRCLE
inputs unrounded FP numbers and DRAW inputs rounded
integers
- 5C7D COORDS holds the same coordinates, rounded to
integers
- the third and fourth last values on the stack are the
coordinates of the end-point of the arc, which again may be
either FP numbers or integers
- mem-1 and mem-2 hold the displacements U, V for the
first "arc"
- mem-3 and mem-4 hold the SIN and COS of the turning
angle T.
The mem-1 to mem-4 values are all FP numbers, none of
them rounded to integers.
Action: reduce the arc count by one for a loop counter
- if this makes zero jump on to ARC END
- jump into the loop at ARC START.

244
_2425_ARC_LOOP (each turn of the loop returns here with
the stack holding, from the top, Yf, Xf, Yz, Xz;
Xf and Yf are the_unrounded coordinates of the PLOT
position fram which the last "arc" was drawn,
Xz and Yz are the end-point coordinates for the whole
curve;
U and V, the displacements used for the last "arc", are
in mem-1 and mem-2
COS T and SIN T are in mem-3 and mem-4):
- use the calculator to figure a pair of displacements
for the next "arc" using the formulas given above:
U' = U COS T - V SIN T
V' = U SIN T + V COS T
- put these U' and V' in mem-1 and mem-2 replacing U and
V; all these numbers are handled in FP form only, without
rounding. The calculator stack is left as it was on entry to ARC
LOOP.
_2439_ARC_START (on each entry to this point all eleven
input parameters - four on the stack, four in the memory, two
in 5C7D COORDS and the loop counter in the B register - are
as they were at the start, except moved on as necessary to
draw the next "arc"): stack the counter
- use the calculator to add U from mem-1 to Xf on the
calculator stack; this is the x coordinate of the next PLOT
- call 2D2B STACK A to get Xi from 5C7D COORDS
- subtract it from the next x coordinate; Xf + U - Xi is
the first DRAW parameter for the next "arc"
- use the calculator to add V from mem-2 to Yf from
mem-0; this is the y coordinate of the next PLOT
- call 2D2B STACK A to get Yi from 5C7D COORDS hi
- subtract it from the next y coordinate; Yf + V - Yi is
the second DRAW parameter for the next "arc"
- call 24B7 DRAW LINE to draw the "arc"; it gets its
PLOT coordinates from 5C7D COORDS,
takes the DRAW parameters from the top of the stack,
leaving Yf + V, Xf + U as the top values, and updates the PLOT
coordinates in 5C7D COORDS to the new rounded values

245
- jump back to ARC LOOP counting down the loop
counter
_245F_ARC_END (a - 1 turns of the loop have drawn all but
the last "arc"; as usual the unrounded coordinates for the next
PLOT are on top of the stack, and the so far unused Yz, Xz
coordinates for the end of the DRAWn line below them):
delete the top two values, leaving the Yz, Xz on top
- call 2D2B STACK A twice to get the last PLOT
coordinates in turn from 5C7D COORDS, and subtract each
from its corresponding end-point coordinate; the resulting
DRAW parameters for the final "arc" are left as the only values
on the stack.
_2477_LINE_DRAW: call 24B7 DRAW LINE to draw the final
"arc" to the end-point; it clears the stack and updates the
PLOT coordinates.
Exit: to 0D4D TEMPS, which cancels the temporary
colour settings from the DRAW or CIRCLE command.
Output parameters: none
- 5C7D COORDS holds the final PLOT position
- all the memory locations mem-0 to mem-5 have been
corrupted.
Exit from:
235A C ARC GE1 (2320 CIRCLE)
23C1 DR PRMS (2382 DRAW)
Rems:
2320 CIRCLE arc drawing loop used

DR 3 PRMS 238D (2382 DRAW)


Jumps from:
2382 DRAW

D SIZE 2C2D (2C02 DIM)


Jumps from:
2C1F D LETTER

duplicate subroutine (MOVE FP) 33C0


Called very frequently from 0028 FP CALC with the

246
literal 31; also called directly from ROM under the name MOVE
FP.
Copies five bytes starting at HL to the addresses
starting at DE. When called from 335B CALCULATE, this
duplicates the last value on the stack.
Can be called from m/c to copy five bytes - an FP number
or anything else - from any location to any other.
Input parameters: the source address in HL, the
destination in DE. The CALCULATE routine for unary
operations
points HL to the last value on the calculator stack and DE to
the location beyond the last value, but other addresses may
be supplied by m/c.
Action: call 33A9 TEST 5 SP, which reports "Out of
memory" if there is no room to extend the calculator stack,
and makes a counter of 5
- LDIR finishes the copying.
Exit: RET.
Output parameters: HL and DE are incremented by five;
when the call is from CALCULATE, DE will make a new stack
end.
Called as duplicate from:
03F8 BEEP
0427 BE OCTAVE
233B C R GRE 1
235A C ARC GE1 (twice)
238D DR 3 PRMS
23A3 DR SIN NZ
23C1 DR PRMS (3 times)
2425 ARC LOOP
2439 ARC START
247D CD PRMS1
2497 DRAW SAVE (4 times)
25F8 S RND
2D71 E TST END
2DE3 PRINT FP (twice)
2E01 PF LOOP

247
2E24 PF SMALL
3449 series-06
3453 G LOOP
36A0 n-mod-m
36AF int
36B7 X NEG
36C4 EXP (twice)
3713 ln
371C VALID
373D GRE.8
3783 get-arg (5 times)
37B7 C ENT (3 times)
37DA tan
37F2 atn
37FA CASES (3 times)
3833 asn (3 times)
384A sqr
3851 to-power
385D XISO
Called as MOVE FP from:
2981 SFA MATCH
33B4 STACK NUM
340F get-mem
342D st-mem

duration of beep see time

dynamic handling of strings see 19E5 RECLAIM 1

248
E
"E" or "e" see E-format number
E-format has nothing to do with the constant e =
2.718281828...d, except that in both cases e stands for
"exponent". For the constant, see under 36C4 EXP.

EACH STMT subroutine 198B


Dual purpose: looks through a BASIC line for
-_either a particular command token - eg DATA or DEF FN
- at the start of a statement; only the first code of each
statement is checked for a match with the given command, so
DATA in SAVE ... DATA etc won't be noticed
-_or a statement with a given number.
The subroutine steps through the statement to find its
terminator: new statements start after 3A colon or after CB
THEN, provided they aren't in quotes, ie provided an even
number of codes 22h double quote have been encountered
since the start of the statement. At each statement terminator
the statement counter is decremented till it reaches zero.
In stepping through the statements, the subroutine must
take account of 22h double quotes and the FP number forms
inserted after each number in the BASIC: colons or THENs
after an odd number of quotes, ie within a quotation, don't
count as terminators, and number forms may include bytes
which would be misread as quotes or terminators.
For this reason 0020 NEXT CHAR isn't used in EACH S 2,
because it assumes the FP number forms aren't in place: in
reading eg "AT 13,7", which appears in the BASIC line as
AT 13 , 7
AC 31 33 (0E 00 00 0D 00 00) 2C 37 (0E 00 00 07 00 00)
007D SKIP OVER would cause the first number marker to be
missed, and then the 0D would be read as a newline.
Input parameters:_either D holds a statement counter and

249
E holds zero; the statements are counted upwards from one at
the start of the line, and a line can't have more than 127d
statements, see 1B29 STMT L 1 under 1B8A LINE RUN
-_or D holds zero and E holds a code byte, the token to
be searched for
- HL holds a BASIC pointer on the address just before
the start of the line.
Action: put the BASIC pointer in 5C5D CH ADD
- make a "quotes" flag zero; any even value of the flag
means "not in quotes".
_1990_EACH_S_1: decrement the statement counter
- if it reaches zero return; this can't happen if a
token is being looked for, because the first decrement makes
the counter FF
- move on the BASIC pointer
- if the new code doesn't match the token byte jump on
to EACH S 3 - (it matches) clear the carry flag
[unnecessary!] and return.
_1998_EACH_S_2 (the loop returns to here from EACH S 6
below): increment the BASIC pointer and read the next code.
_199A_EACH_S_3: call 18B6 NUMBER to jump the BASIC
pointer over number markers and the following 5-byte
number
- put the pointer in 5C5D CH ADD
- if the character isn't 22h " jump on to EACH S 4
- (quotes character) decrement the quotes flag.
_19A5_EACH_S_4: if the character is 3A colon jump on to
EACH S 5
- if it isn't CB THEN jump on to EACH S 6.
_19AD_EACH_S_5 (colon or THEN): if bit zero of the quotes
flag is zero jump back to EACH S 1; the flag is even, this is
the end of a statement.
_19B1_EACH_S_6 (not colon or THEN, or in quotes): if the
character isn't 0D newline jump back to EACH S 2
- (newline) decrement the statement counter; this is the
end of the line
- set the carry flag and return; even if the quotes flag

250
is odd, which could only happen in a REM command.
Exit: RET, from EACH S 1 or EACH S 6.
Output parameters: HL holds the address sought, A the
byte at that address
- D is zero if the D'th statement has been found
- E is unchanged.
- the carry flag shows C if the end of the line has been
reached without a "find"
- the zero flag shows Z if the D'th statement has been
found, NZ if a match to E has been found.
Called from:
1BD1 NEXT LINE
1DA3 LOOK P 2
2825 SF NOT FD
Exit from:
1E39 PASS BY

EACH S 1 1990 (198B EACH STMT)


Jumps from:
19AD EACH S 5

EACH S 2 1998 (198B EACH STMT)


Jumps from:
19B1 EACH S 6

EACH S 3 199A (198B EACH STMT)


Jumps from:
1990 EACH S 1

EACH S 4 19A5 (198B EACH STMT)


Jumps from:
199A EACH S 3

EACH S 5 19AD (198B EACH STMT)


Jumps from:
19A5 EACH S 4

251
EACH S 6 19B1 (198B EACH STMT)
Exit from:
19A5 EACH S 4 (198B EACH STMT)

EAR bit/state see ports


Bit 6 of the input from port FE. Set for "off", zero for
"on".

ECHO E system variable 5C82


Bytes: 2
In 111D ED COPY, "echoes the end" of the last line of
BASIC in the lower screen which has just been overwritten by
a new one. It contains a position value in the lower screen
just after the end of the last line written there. When a change
is made in the line, or a new line is brought down for editing,
the lower screen isn't cleared, but the new version or line
simply overwrites the old; ECHO E is used to check whether
spaces are required to complete the overwriting
Written by:
0AF0 PO ST E
117E ED C END
Read by:
111D ED COPY
2161 IN VAR 4

ED AGAIN 0F30 (0F2C EDITOR)


Jumps from:
107F ED ERROR

ED BLANK 1150 (111D ED COPY)


Jumps from:
115E ED SPACES

ED C DONE 117C (111D ED COPY)


Jumps from:
1150 ED BLANK (twice)

252
ED C END 117E (111D ED COPY)
Exit from:
117C ED C DONE (111D ED COPY)
1167 ED FULL

ED CONTR 0F6C (0F2C EDITOR)


Jumps from:
0F38 ED LOOP

ED COPY subroutine 111D


Prints out one line from the editing or the input area
in the lower part of the screen.
BASIC input isn't made directly to the screen but to the
editing area when inputting a BASIC line, or to the work space
when responding to an INPUT command. All character inputs,
cursor moves, deletions, etc take effect in those areas first
and only afterwards are copied to the screen.
The copying to screen occurs every time 15D4 WAIT KEY
is called, ie whenever the Spectrum has dealt with a BASIC
command and is awaiting further input from the keyboard::
WAIT KEY sets the "copy line" flag, bit 3 of TV FLAG, called
"mode flag" in the notes, and its WAIT KEY 1 loop calls 10A8
KEY INPUT, which copies the line to lower screen first, before
checking the keyboard; ED COPY zeroes the flag. In effect, the
input line is recopied every time a key is pressed.
Most of the subroutine is concerned with juggling with
the print position values to make sure the new copy of the
input appears in the right place in the lower screen and
properly overwrites what was there before; the lower screen
isn't cleared between keystrokes, no doubt to avoid slowing
down the keyboard response. For the print position
coordinate system, see DISPLAY AREA.
Input parameters: none
- channel K is open, ie input from 10A8 KEY INPUT,
output to lower screen
- 5C82 ECHO E holds the print position after the end of
the existing display in the lower screen, which is to be

253
overwritten.
Action: call 0D4D TEMPS to use the permanent colours; in
5C48 BORDCR for the lower screen
- zero TV FLAG bits 3 and 5; "don't copy input line" and
"don't clear lower screen after keystroke"
- load the machine stack with:
the old value from 5C8A S POSNL; call it "S PL1". This is
always the first print position in the lower screen - but it may
be on any line of the screen except the top two
the old value from 5C3D ERR SP; the error address put
there by 0F30 ED AGAIN or 213A IN VAR 1
1167 ED FULL; the return address for the time being. This
is also put in 5C3D ERR SP as the return address after an
editing error, usually "Out of memory" or "Out of screen"
the print position from 5C82 ECHO E; on top of the stack
- call 1195 SET DE (misprinted SET HL) to get pointers
to the start and end of the editing area or work space,
whichever is being used
- call 187D OUT LINE2 to print out the line indicated by
these pointers
- call 18E1 OUT CURS to add the cursor if it is at the
end of the line; OUT LINE2 has already printed it if it is
anywhere else. This puts 5C8A S POSNL at the end of the
printed line ("S PL2").
- recover the 5C82 ECHO E value from the stack and stack
the line-end S PL2
- call 0D4D TEMPS again in case the input has changed
the colours.
_1150_ED_BLANK: check the print position line number
from
5C82 ECHO E hi against the one in S PL2 hi
- if 5C82 ECHO E has a bigger line number, ie is further
up the screen, jump on to ED C DONE; the new line is longer
- (5C82 ECHO E is lower down the screen) jump on to ED
C SPACES
- (both on same line) check the column numbers; lo bytes
- if 5C82 ECHO E is bigger, ie more to the left, jump on

254
to ED C DONE.
_115E_ED_SPACES: call 09F4 PRINT OUT to print a space
- jump back to ED BLANK.
_1167_ED_FULL (an error in the copying of the line to the
lower screen will cause a jump to here, but not an ordinary
syntax error in the line, which just produces a flashing cursor
from 187D OUT LINE2 called earlier; ED FULL is usually
entered as the result of an "Out of screen" error): call 03B5
BEEPER to make a "rasp"; the duration is set by 5C38 RASP,
which can be changed, but the pitch is fixed
- put FF in 5C3B ERR NR; "OK"
- get the current value of 5C8A S POSNL; "S PL4",
wherever output has got to when the error occurred
- jump on to ED C END.
_117C_ED_C_DONE (if the copied line was longer than the
overwritten one 5C8A S POSNL points to S PL2, the byte
after it; if not, spaces have been output to overwrite the old
line and 5C8A S POSNL points to "S PL3", the first print
position after the spaces): recover S PL2 which was stacked
near the end of ED COPY; the end of the line that was copied

- drop the return address ED FULL.


_117C_ED_C_END: recover the entry value of the stack
pointer and put it back in 5C3D ERR SP
- recover the starting print position S PL1
- call 0DD9 CL SET with it; the next line copied will go
to the same position
- put the new end-of-line value in 5C82 ECHO E; either S
PL2 or S PL4; misprinted "old value" in the notes
- zero 5C5F X PTR; in case a flashing cursor was
printed.
Exit: RET.
Output parameters: none
- 5C82 ECHO E holds the line-end position of the newly
copied edit/input line.
Called from:
10A8 KEY INPUT

255
2161 IN VAR 4

ED CUR subroutine 1011


Merely stores the current value of HL in 5C5B K CUR.
Exit from:
1007 ED LEFT
100C ED RIGHT

ED DELETE subroutine 1015


Called from the editing keys table 0FA5 by 0F92 ED KEYS
in response to 0C DELETE; part of the 0F2C EDITOR loop.
Deletes the character before the cursor in the BASIC line in
the editing area or work space. If the code before the cursor is
the parameter of an embedded colour control, including
TRUE VIDEO or INVERSE VIDEO, the colour control code is
deleted, leaving the parameter; this effect is produced in 1031
ED EDGE called by 1007 ED LEFT.

Not otherwise called from ROM; could be called from m/


c, but of doubtful usefulness.
Input parameters: HL points to the cursor position in the
editing area or work space
- the return address 0F38 ED LOOP is on top of the
stack.
Action: call 1031 ED EDGE, which moves the cursor left
- make a reclaim counter of one.
Exit: to 19E8 RECLAIM 2, which reclaims one character at
the cursor position; in the ROM call, it will return to 0F38 ED
LOOP.
Output parameters: HL holds the new cursor position in
the editing area or work space.

ED DOWN subroutine 0FF3


Called from the editing keys table 0FA5 by 0F92 ED KEYS
in response to 0A "down arrow"; part of the 0F2C EDITOR
loop. In editing, moves the current line down one BASIC line.
In input mode, no action except in INPUT ... LINE, when it

256
produces the "STOP in INPUT" error report; in INPUT ...
LINE_all tokens, including the STOP token, are accepted as
input.
Not otherwise called from ROM; could be called from m/
c, but of doubtful usefulness.
Input parameters: HL points to the cursor position in the
editing area or work space
- for the ROM call, the return address 0F38 ED LOOP is
on top of the stack.
Action: if bit 5 of FLAGX is set jump on to ED STOP;
input mode
- (editing mode) set a pointer on 5C49 E PPC
- call 190F LN FETCH to get the next line number after
the one in 5C49 E PPC and put it in it; the new "current line"
- exit to ED LIST.
_1001_ED_STOP: put the error number 10h in 5C3A ERR
NR; "STOP in input"
- exit to ED ENTER.
Exit: (editing mode) into 106E ED LIST, producing a new
listing. Return will be to 0F38 ED LOOP
- (input mode) into 1024 ED ENTER. With normal inputs
the error number is ignored and the EDITOR immediately
called again; but in INPUT ... LINE the error report gets
printed. The error stack pointer isn't changed for INPUT ...
LINE in 2129 IN PR 3, but remains on the usual address of 1303
MAIN 4; for other inputs it is changed to 213A IN VAR 1, where
error numbers are
cancelled.
Output parameters: none.

ED EDGE subroutine 1031


Moves the editing cursor one place left, except
- at the start of a line it doesn't move
- if it would land on the parameter of an embedded
control code it is moved back to the control code itself.
Rather than stepping back from the present cursor
position, the subroutine steps on from the start of the line

257
till it reaches the cursor position, retaining at each step a
record of the place where the cursor should go if the next step
turns out to be the cursor position; call it the "trial
position". This slightly roundabout method is used in case the
BASIC line or input contains embedded INK to TAB controls
such as 16h,x,y (AT x,y); actually embedded ATs and TABs are
impossible when input is from the keyboard, but they might
occur in inputs from peripherals, or of course from user m/c.
[I think the notes are wrong in calling this a mistake.]
Input parameters: HL holds the present cursor position in
the editing area or work space
- the address 0F38 ED LOOP is next below the return
address on the stack.
Action: set the carry flag and call 1195 SET DE; this
points DE at the address in 5C59 E LINE or 5C61 WORKSP,
making a trial position on the start address of the editing area
or work space
- check the start address against the cursor pointer;
the INC HL merely corrects for SCF/SBC/ADD, putting the
cursor pointer HL where it was before
- if the cursor is on the start, drop the return address
and return to 0F38 ED LOOP; no action, the cursor can't move
back.
_103E_ED_EDGE_1: put a new BASIC pointer on the trial
position
- step the pointer on one; keeping the trial position
unchanged
- read the character at the trial position
- AND it with 11111000b/F0h; the result will be 10h for
codes 10h -> 1Fh, the INK to TAB control codes
- if the result isn't 10h jump on to ED EDGE 2
- (control code found) move the pointer on again; the
trial position is still on the control code, the pointer is on
the first parameter
- subtract 17h from the control code and if there was
carry add one; this makes zero from 16h AT and 17h TAB only
- if it isn't AT or TAB jump on to ED EDGE 2

258
- (it was AT or TAB) move the pointer on again;
the trial position is still on the control code, the pointer is on
the second parameter.
_1051_ED_EDGE_2: check the pointer with the cursor
position
- move on the trial position to the pointer
- if the pointer is still before the cursor position
jump back to ED EDGE 1
- (the trial position is either just before the cursor
or only control code parameters are between them) return
with the trial position as next cursor position.
Exit: double return (to 0F38 ED LOOP) if the cursor is
already on line start
- otherwise normal RET from ED EDGE 2.
Output parameters: HL holds the new cursor position in
the editing area or work space.
Called from:
1007 ED LEFT
1015 ED DELETE

ED EDGE 1 103E (1031 ED EDGE)


Jumps from:
1051 ED EDGE 2

ED EDGE 2 1051 (1031 ED EDGE)


Exit from:
103E ED EDGE 1 (twice) (1031 ED EDGE)

ED EDIT subroutine 0FA9


Called only from the editing keys table 0FA5 by 0F92 ED
KEYS in response to 07 EDIT; part of the 0F2C EDITOR loop.
In editing, copies the current BASIC line to the lower screen;
in input mode it clears whatever has been input and waits for
a fresh input.
Not otherwise called from ROM; could be called from m/
c, but of doubtful usefulness.

259
The line is copied to the editing area by opening channel
R, which sends output to the editing area, and then
calling 1855 OUT LINE, the same routine used to print BASIC
lines to the main screen. OUT LINE expands all the tokens
- doesn't reproduce the hi-lo line number of the line in
the program area, but prints it as a decimal number with
leading spaces
- omits the length bytes of the line which follow the
line number in the program area
- and skips any number markers and FP forms in the line
as held in the program area.
If the line number matches the one in 5C49 E PPC, OUT
LINE will print the "current line" cursor ">" as part of the line.
This is of course not wanted in the lower screen printout, and
the programmer tried to sidestep it by decrementing E PPC
temporarily before calling OUT LINE.
[It usually works; but it fails if the last two lines in
your BASIC have consecutive numbers, and you delete the last
one, leaving its number in 5C49 E PPC, then press EDIT. Try
it:
type
9998 STOP
9999 STOP
and then type 9999 to delete the last line. Now EDIT will
bring down line 9998 with the current line cursor, and this
will give a syntax error unless it is deleted.
It would have been better to temporarily set the hi bit
of 5C49 E PPC, making an impossible line number.]
Input parameters: HL holds the cursor position in the
editing area or work space
- the return address 0F38 ED LOOP is on top of the
stack.
Action: get the current line number from 5C49 E PPC
- if FLAGX bit 5 isn't zero exit to 1097 CLEAR SP; input
mode, it will clear the work space
- (editing mode) call 196E LINE ADDR and 169B LINE NO
to find the start address and line number of the line in BASIC

260
matching or next after the line number in 5C49 E PPC; if there
are no more lines in the program, they return values for the
last line
- if this gives line zero exit to 1097 CLEAR SP to clear
the editing area; there are no lines to edit
- read the line length from the two bytes after the line
number
- add 0Ah/10d; to allow for expanding the tokens
- call 1F05 TEST ROOM, which will report "Out of
memory"
if there is no room for an editing area this size
- call 1097 CLEAR SP to clear the editing area
- save the address of the current channel from 5C51
CURCHL
- call 1601 CHAN OPEN with stream minus one to open
channel R; it outputs through 0F81 ADD CHAR to the
editing area/work space at the position marked by 5C5B K
CUR
- recover the pointer to the start of the line in the
program area; it was returned by 196E LINE ADDR called
above
- decrement the line number in 5C49 E PPC
- call 1855 OUT LINE, which prints out the line through
channel R into the editing area
- restore 5C49 E PPC [see note above]
- make a pointer from 5C59 E LINE; the start of the
editing area
- move it on four places; past the line number with its
leading spaces [the notes are wrong here; 1855 OUT LINE has
transformed the line]
- put this address, just after the line number, in 5C5B
K CUR; the editing cursor pointer
- get back the current channel address and call 1615
CHAN FLAG to reset the channel; no doubt to channel K.
Exit: RET; to 0F38 ED LOOP, in the ROM call.
Output parameters: none.

261
ED END 1026 (1024 ED ENTER)
Exit from:
1024 ED ENTER
107F ED ERROR

ED ENTER subroutine 1024


Called from the editing keys table 0FA5 by 0F92 ED KEYS
in response to 0D ENTER; part of the 0F2C EDITOR loop.
It appears in ROM as the exit routine from three other
sections of the EDITOR loop, 1001 ED STOP, 101E ED IGNORE
and 1076 ED SYMBOL; but only the first of these can be used
for keyboard inputs.
Not otherwise called from ROM; could be called from m/
c, to remove three return addresses from the machine stack,
putting the last of them into 5C3D ERR SP, and return to a
fourth.
When the EDITOR is called it first loads several
addresses on the machine stack:
- 0F38 ED LOOP, top of the stack, the looping return
address for each of the editing subroutines
- 107F ED ERROR, the temporary error address
- the value currently in 5C3D ERR SP; usually the address
at this value would be 1303 MAIN 4.
Below this is the return address for the EDITOR
subroutine, 12AC MAIN 2 if it was called from main execution
or 2148 IN VAR 2 or 215E IN VAR 4 if it was called from the
INPUT command routine.
ED ENTER exits from the 0F2C EDITOR routines as a
whole by dropping the top two addresses and restoring the
third to 5C3D ERR SP before returning.
Input parameters: none
- addresses as shown above on the machine stack.
Action: drop the top two addresses.
_1026_ED_END (entry point for 107F ED ERROR): recover
the third address and restore it to 5C3D ERR SP
- check the hi bit of 5C3A ERR NR
- if it is set, return; this must be "OK", FFh

262
- otherwise jump to the error address just restored;
this will arise for the "STOP in INPUT" error introduced at ED
STOP or produced by a STOP against ordinary inputs, but it
isn’t easy to see what other errors could arise at this point.
Exit: RET, from 1026 ED END, twice: once to the error
address, once to the routine which called the EDITOR loop.
Output parameters: none.
Exit from:
1001 ED STOP
101E ED IGNORE
1076 ED SYMBOL

ED ERROR subroutine 107F


There are no direct jumps to or calls of this label in
the ROM, but its address is put on the stack and marked as the
error address on entry, or reentry, to the 0F2C EDITOR loop
at 0F30 ED AGAIN. Thus any call to 0008 ERROR 1 or 0053
ERROR 2 or 0055 ERROR 3 during execution of the EDITOR
will jump to here. A list is given below of such "possible calls":
all of them arise either from attempts to print to an already
full screen, or from attempted expansion of a full memory.

Instead of printing an error report, as for normal


errors, the Spectrum gives a kind of sigh or groan - the "rasp"
- and jumps straight back into the EDITOR loop at 0F30 ED
AGAIN.
As most of the errors involve severe pressure on available
memory, attempting to do anything else might crash the
system.
A simple way to test the "rasp" is to command INPUT s,
then input repeated THENs: the quickest way to fill the lower
screen. When the lower screen is full up to the point of
leaving only one line in the upper, it groans; and groans again
each time you press any editing key, but doesn't refuse further
input. Press EDIT to delete the input.
Action: check FLAGS2 bit 4; if channel K isn't in use

263
exit to 1026 ED END. Other channels from peripherals or user
m/c produce no groan
- put FF in 5C3A ERR NR; cancel the error number
- call 03B5 BEEPER with a fixed pitch and the duration
specified in 5C38 RASP; groan.
Exit: to 0F30 ED AGAIN.
"Possible" calls from
("Out of screen"):
0C55 PO SCR (via 0FA9 ED EDIT, through more than one
different route to 09F4 PRINT OUT and 0B93
PR ALL 1)
(or via 106E ED LIST, 1795 AUTO LIST, 17ED
AUTO L 4, 1835 LIST ALL and 0010 PRINT A 1,
09F4 PRINT OUT etc)
"Out of memory":
1F05 TEST ROOM (via 0F6C ED CONTR and 1655 MAKE
ROOM)
(or via 0F81 ADD CHAR and 1652 ONE SPACE)
(or via 0FA9 ED EDIT)
Rems:
1024 ED ENTER return address discarded

ED FULL 1167 (111D ED COPY)


Very like 107F ED ERROR; no direct jumps or calls from
the ROM, but it is made the error address on entry to 111D ED
COPY, and any error in copying to the lower screen will jump
to here; I think only "Out of screen" errors are possible.
Eg input a BASIC line consisting of a line number, REM,
and enough THENs to more than fill the lower screen; now
press ENTER and it will go into the program; now press EDIT.
The Spectrum will copy as much as it can to the lower screen,
but it won't all fit in. You will get the "groan".
ED FULL exits to ED C END after the groan, ie regards
the copying as completed, but then exits normally into the
editing loop. You can move the cursor and even add more
THENs to your overlong BASIC line - but you get a fresh groan
for every editing command except ENTER.

264
It differs from ED ERROR essentially only in exiting to
117E ED C END instead of direct to the editing loop, so that the
machine stack can be properly rearranged.

edges, edge-type see 05E7 LD EDGE 1

ED GRAPH 107C see symbol code

ED IGNORE 101E
Deals with the parameters of 16h AT and 17h TAB codes
included in an INPUT command. Such inputs are impossible
from the keyboard, though the situation might arise with
input from peripherals such as Microdrives, or from user m/
c. The two parameters are discarded, without any error
report.
Jumps from:
0F38 ED LOOP

EDIT key (07) see also KEYBOARD SCANNING, 0260 control


codes
table (d)
The one key with caps shift in K, L or C mode effects
the EDIT control in editing mode; the "current" line of BASIC,
if any, is copied to the editing area and then displayed in the
lower screen. In input mode it merely deletes all input so far
and waits for new input.
Later models of the Spectrum mostly have a special EDIT
key, but "one" with caps shift still works.
0260 control code table (d)
0A12 control character table - ineffective
0F92 ED KEYS jumps to the executive routine
0FA0 editing keys table
0FA9 ED EDIT executive routine

editing see also control characters


_Editing_mode is signalled by bit 5 of FLAGX being
switched off; this is done each time execution passes through

265
1313 MAIN G, and is the normal mode of ROM operation. If
FLAGX bit 5 is set, the program is in input mode.
The_editing_area is the part of memory used to hold
the_edit_line, either a new line or a copy of the BASIC line
currently being edited, which can be changed by editing
commands. It is located at the address stored in 5C59 E LINE,
and is always followed by a newline and an 80-byte marker;
and
5C61 WORKSP marks the address just after this.
It is important to understand that all the editing
commands operate on this copy in the editing area; only after
they have been executed there is the line copied again to the
lower screen to display the effect.
The_editing_codes/keys are those covered by the editing
keys table 0FA0:
code 07 EDIT
code 08 left arrow
code 09 right arrow
code 0A down arrow
code 0B up arrow
code 0C DELETE
code 0D ENTER
code 0E SYMBOL SHIFT
code 0F GRAPHICS
but the last two cannot be used without peripherals.
Editing is executed by the 0F2C EDITOR loop, mostly
using the routines addressed by the table. This loop is also
used for inputs in response to INPUT commands, but in this
case the input is built up in the work space area and copied
from there to the lower display.
0F38 ED LOOP for edit keys jump to 0F92 ED KEYS
0F81 ADD CHAR code added to edit line
0F92 ED KEYS edit keys dealt with through table
0FA0 editing keys table
0FA9 ED EDIT different effect for editing and input
1031 ED EDGE line start marker at E LINE for editing
1097 CLEAR SP clears edit area

266
10A8 KEY INPUT edit line copied if "mode changed"
111D ED COPY copies editing area to lower screen
117C ED C DONE exit after copying to lower screen
1190 SET HL puts HL at last location of editing area
1195 SET DE puts DE at start of editing area
1219 RAM SET single newline in edit area on start-up
12A2 MAIN EXEC controls editing mode
133C MAIN 5 clear editing area after error report
157D MAIN ADD1 finds newline at end of editing area
1795 AUTO LIST misprint for "lower part of the screen"
1822 LIST 5 skip if checking syntax of edit line
19FB E LINE NO reads line number in editing area
1A1B OUT NUM 1 line number -2 in reports shown as zero
1B17 LINE SCAN edit area scanned by CH ADD
1B7D STMT R 1 flag signals running line in edit area
1B8A LINE RUN direct command in editing area has line
number -2
1BD1 NEXT LINE next line is same line in edit area
2174 IN VAR 5 flag reset to signal edit mode

editing area, editing codes, editing keys, editing mode see


editing

editing cursor see 5C5B K CUR

editing key table 0F99 see tables

EDIT KEY SUBROUTINE see 0FA9 ED EDIT

edit line, edit mode, editor see editing.


EDITOR subroutine 0F2C see also control characters,
editing,
2089 INPUT
Handles input into either the editing or the input area.
Performs execution of all the editing control characters by
jumps from the editing keys table 0FA0 into the executive
subroutines, each of which is discussed separately in this

267
index.
Input parameters: none.
Action: get the error stack pointer from 5C3D ERR SP and
stack it.
_0F30_ED_AGAIN (the reentry point of the EDITOR loop):
stack the address 107F ED ERROR (rasp and return to EDITOR)
- put it in 5C3D ERR SP; temporarily the error address.
_0F38_ED_LOOP: call 15D4 WAIT KEY, which copies the
edit line to the lower screen and waits till it gets a keystroke
- call 03B5 BEEPER with a fixed pitch and the duration
in 5C39 PIP; make a click
- put the address of 0F38 ED LOOP on the machine stack,
so all the control code subroutines will return here
- check the code from the keystroke
- if it is more than 17h exit to 0F81 ADD CHAR; it will
put the code of any digit, letter, symbol or token code in the
edit line at the position pointed to by 5C5B K CUR, and return
to 0F38 ED LOOP
- if the code is less than 07 do the same; it can only
be 06, the PRINT comma
- (07 -> 17h) if it is less than 10h jump on to ED KEYS;
an edit control code
- (10h -> 17h) set a counter to two
- if the code was less than 16h jump on to ED CONTR; an
INK to OVER colour control, eg yellow PAPER, produced by
key 6 in E mode with caps shift: the single key input is
transformed by 10A8 KEY INPUT and following into_two
keystroke outputs, the control code and its parameter. [The
notes are wrong in referring only to INK and PAPER, since
FLASH, BRIGHT and INVERSE, though not OVER, can also be
input from the keyboard in this way]

- (16h AT and 17h TAB; they cannot occur in an edit line


or INPUT as far as the unexpanded Spectrum is concerned,
but might be encountered in inputs from peripherals or user
m/c) make the counter three

268
- call 15D4 WAIT KEY to get the first parameter.
_0F6C_ED_CONTR: call 15D4 WAIT KEY to get the
parameter
- stack the control code and first parameter if any
- call 1655 MAKE ROOM with the counter to make spaces
at 5C5B K CUR
- get back the control code and first parameter and put
them in the spaces made
- exit to 0FB8 ADD CH 1 to write in the second and
update 5C5B K CUR; if there was only one parameter it
overwrites
the meaningless byte just written in.
[The subroutine 0F81 ADD CHAR is interpolated here; see
the separate index entry.]
_0F92_ED_KEYS (0F38 ED LOOP jumped on to here with
any of the control codes 07 EDIT -> 0F GRAPHICS): add the
code to the base address 0F99;_not the first address of the
table
- add the offset thus found in the editing keys table
0FA0 to its own address, to get the subroutine address
- get the editing cursor pointer from 5C5B K CUR and
make an indirect jump to the subroutine address; all these
subroutines return to 0F38 ED LOOP except ED ENTER,
unless there is an error, when they return to ED AGAIN to
restore the error address.

Exit: from 1026 ED END (1024 ED ENTER) when ENTER


pressed; the address dropping etc amounts to a simple return
from EDITOR.
Output parameters: none
- the error address is restored to its old value in ED
END (1024 ED ENTER).
Called from:
12AC MAIN 2
2148 IN VAR 2
215E IN VAR 3
Rems:

269
Introduction line is checked and stored, or executed
107F ED ERROR rasp and return to editor
1795 AUTO LIST used by
191C LINE STORE used by
2129 IN PR 3 preparation before calling

E DIVSN 2D6D (2D4F E TO FP)


Jumps from:
2D60 E LOOP

ED KEYS 0F92 (0F2C EDITOR)


Jumps from:
0F38 ED LOOP

ED LEFT subroutine 1007


Called only from the editing keys table 0FA1 by 0F92 ED
KEYS in response to 08 "left arrow"; part of the EDITOR loop.
In editing or input mode, moves the editing cursor one place
left; but makes no move at the start of the line, and jumps
over control codes and their parameters.

Not otherwise called from ROM; could be called from m/


c, but of doubtful usefulness.
Input parameters: HL holds the address of the cursor
position in the editing area or work space
- for the ROM call, the return address 0F38 ED LOOP is
on top of the stack.
Action: call 1031 ED EDGE, which returns the correct
cursor address for a cursor left move.
Exit: into 1011 ED CUR, which updates 5C5B K CUR.
Output parameters: HL holds the new cursor position in
the editing area or work space.

ED LIST subroutine 106E


Produces a listing after cursor moves up or down.
Input parameters: none
Action: call 1795 AUTO LIST

270
- set stream zero to reopen channel K.
Exit: into 1601 CHAN OPEN to open the channel; in the
ROM, return will be to 0F38 ED LOOP.
Output parameters: A holds zero, the stream number.
Exit from:
0FF3 ED DOWN
1059 ED UP

ED LOOP 0F38 (0F2C EDITOR)


The re-entry point to the EDITOR routine; it puts its
own address on the machine stack. Otherwise, see 0F2C
EDITOR.
Jumps (all indirect jumps) from:
0F8B ADD CH 1
0FA9 ED EDIT
1007 ED LEFT
100C ED RIGHT
1024 ED ENTER
1031 ED EDGE
106E ED LIST (via 1601 CHAN OPEN)

ED RIGHT subroutine 100C


Called only from the editing keys table 0FA1 by 0F92 ED
KEYS in response to 09 "right arrow"; part of the EDITOR
loop.
In editing or input mode, moves the editing cursor right.
Not otherwise called from ROM; could be called from m/
c, but of doubtful usefulness.
There is no provision for skipping over the parameters
of colour controls: it takes two cursor moves right to pass
these, but only one left.
Input parameters: HL holds the address of the cursor
position in the editing area or work space
- for the ROM call, the return address 0F38 ED LOOP is
on top of the stack.
Action: read the byte at the cursor
- if it is 0D newline return without any action; the

271
cursor is at the end of the line
- increment HL.
Exit: into 1011 ED CUR to put the address into 5C5B K
CUR; return will be to 0F38 ED LOOP, in the ROM.
Output parameters: HL holds the new cursor position in
the editing area or work space.

ED SPACES 115E (111D ED COPY)


Jumps from:
1150 ED BLANK

ED STOP 1001 (0FF3 ED DOWN)


Jumps from:
0FF3 ED DOWN

ED SYMBOL 1076 see symbol code

ED UP subroutine 1059
Called only from the editing keys table 0FA1 by 0F92 ED
KEYS in response to 0B "up arrow"; part of the EDITOR loop.
In editing, moves the current line up one BASIC line; in input
mode, no action at all.
Not otherwise called from ROM; could be called from m/
c, but of doubtful usefulness.
Input parameters: HL holds the address of the cursor
position in the editing area or work space
- for the ROM call, the return address 0F38 ED LOOP is
on top of the stack.
Action: if bit 5 of FLAGX is set return at once; input
mode
- (editing mode) get the current line number from 5C49 E
PPC
- call 196E LINE ADDR to find its start address in the
program area
- switch the pointers to get the start address of the
line before
- call 1695 LINE NO to find its line number

272
- call 191C LN STORE to put it in 5C49 E PPC.
Exit: into 106E ED LIST, producing a new listing. Return
will be to 0F38 ED LOOP.
Output parameters: none.

E END 2D7B (2D4F E TO FP)


Exit from:
2D71 E TST END (2D4F E TO FP)

E FETCH 2D6E (2D4F E TO FP)


Jumps from:
2D60 E LOOP

E FORMAT 2CEB (2C9B DEC TO FP)


Exit from:
2CB8 NOT BIN
2CDA NXT DGT 1

E-format numbers (in BASIC printout) see also 2DE3 PRINT


FP
Numbers larger than 99,999,999.5d or smaller than
0.00001d are printed by the BASIC in the form xEy: the value
of the number is 10**y * x, with x the mantissa, y the
exponent.
Either x or y or both may have a minus sign. The mantissa x
will be between one and ten, and it may have a decimal point
with up to seven figures after it; the exponent y never has a
decimal point, and can range from -39d to +38d.
Numbers may also be input in E-format: in this case x
and y can be of any reasonable size, but y must be an integer
and they must be input as decimals, not BIN or E-format
numbers or numeric expressions. Like any other numbers
they will be put in the BASIC as 5-byte FP forms, in this case
by 2C8B NOT BIN and 2CEB E FORMAT, and when they get
printed out it will be in decimal or E-format depending on
their magnitude; except in a BASIC line, where they will be
reproduced as input. The "E" may be input as "e" or "E".

273
2C8B NOT BIN looks for "e/E" in number expression
2CEB E FORMAT checks present character for "e/E"
2CFF ST E PART reads exponent
2D18 E FP JUMP assigns value of XeY
2D4F E TO FP converts to FP form
2DE3 PRINT FP printing of nos uses E format if necessary
2F2D PF COUNT needed if digit count > 8 (nb notes
misprinted) or < -4
2F4A PF E SBRN entry point for printing E format
2F6C PF E FRMT starts printing of E format
2F85 PF E SGN prints + or - and exponent

E FORMAT TO FLOATING POINT SUBROUTINE see 2D4F E


TO FP

E FP JUMP 2D18 (2C9B DEC TO FP)


Jumps from:
2CFF ST E PART

elements of array see arrays

E LINE system variable 5C59


Bytes: 2
The address of the start of the editing area. It is the
next address after the 80-byte marking the end of the
variables area; the end of the editing area is marked by a
newline and another 80-byte, and the next address is in 5C51
WORKSP.
One of the fourteen system pointers set by 1664
POINTERS.
Written by:
1219 RAM SET
166B PTR NEXT
Read by:
073A SA TYPE 0
084C LD DATA 1
0873 LD PROG

274
0FA9 ED EDIT
1195 SET DE
12AC MAIN 2
12CF MAIN 3
166B PTR NEXT
16B0 SET MIN
16D4 REC EDIT
19FB E LINE NO
1B8A LINE RUN
1EB7 CLEAR 1
2B29 L SPACES
2BC6 L STRING
2BEA L FIRST
2C2E D NO LOOP
Rems:
1031 ED EDGE put in DE to move cursor in edit line

E LINE NO subroutine 19FB


Reads the line number of the line in the editing area;
cf 1695 LINE NO, which reads the number of a line in the
program area.
The line number in the editing area is just a string of
decimal digits, as input; not as in the BASIC program area,
where it has been converted to a reversed 2-byte integer. This
is true even when the line has been copied back to the editing
area from the program; the number is converted back to
decimal form by the call to 1A28 OUT NUM2 from 1965 OUT
LINE1. The only difference from the input is that now the
number may have leading zeroes.
Input parameters: none.
Action: get a pointer from 5C59 E LINE on the start of
the line
- move it back one byte
- put it in 5C5D CH ADD as a BASIC pointer
- call 0020 NEXT CHAR to move it on to the start; this
method is used because there may be leading spaces before
the line number, and these will be skipped by NEXT CHAR.

275
The more straightforward LD HL,(E LINE)/CALL 0078 TEMP
PTR2 might leave the pointer on a space
- put the address of 5C92 MEMBOT in 5C65 STKEND;
making the calculator memory into a temporary calculator
stack. The stack is cleared on exit anyway, but this is done
because the subroutine will sometimes be called after an "Out
of memory" report has been made, with no room for the
normal stack
- call 2D3B INT TO FP; it reads the number on to the
temporary stack, or leaves zero there if there is no number
- call 2DA2 FP TO BC to put the digits into BC
- if the number is over FFFFh/65535d jump on with carry
to E L 1
- add D8F0h/55536d; making carry if the number is
10000d or more.
_1A15_E_L_1 (heading omitted by misprint: it should be on
the line "JP C,1C8A,REPORT C"): if carry is set report
“Nonsense in BASIC".
Exit: to 16C5 SET STK to restore the calculator stack.
Output parameters: BC holds the line number, or zero if
there is none; ie if a direct command has been entered
- mem-0 to mem-2 have been corrupted by being used as
a calculator stack
- the line number remains in mem-0, but is never used.
Called from:
12CF MAIN 3
1B17 LINE SCAN

E LOOP 2D60 (2D4F E TO FP)


Jumps from:
2D71 E TST END

E L 1 1A15 (19FB E LINE NO; heading omitted by misprint: it


should be on the line "JP C,1C8A,REPORT C")
Jumps from:
19FB E LINE NO

276
embedded colours, embedded control characters,
embedded print items see colours, control characters

E mode see 5C41 MODE

enable interrupt see interrupts

encoder (ZX printer) see ports

end-calc subroutine 369B


Must be called from 0028 FP CALC with literal 38 every
time the calculator is used - it makes the exit from the literal
string. Not otherwise called from ROM. Could be called direct,
for an indirect jump to HL', dropping the old return address.
Input parameters: the return address 3365 RE ENTRY is
on top of the machine stack, with the address of the last literal
below it; put there by 335E GEN ENT 2
- H'L' holds the return address from the call to 0028 FP
CALC; ie the address following the 38 end-calc literal.
Action: drop the 3365 RE ENTRY return address
- replace the last literal address on the stack with the
H'L' return address.
Exit: RET, to the address formerly in H'L'.
Output parameters: HL and DE point to the last FP
number on the stack and the stack end
- H'L' holds a previous literal address.
Calls: see under 0028 FP CALC; as it is always and only
called to exit from FP CALC it is unnecessary to give the long
list of occurrences.
Rems:
335B CALCULATE last literal must be 38h

END COMPL 30A3 (303E FULL ADDN)


Jumps from:
307C TEST NEG

end marker of GO SUB stack see GO SUB stack

277
end marker of tables see 16DC INDEXER

end-marker of variables area see 80-byte (after end of


alphabet)

end of BASIC line see ENTER key (0D)

END TESTS 358C (353B no-l-eql)


Exit from:
354E NU OR STR (353B no-l-eql etc)

"E" notation of BASIC numbers see E-format numbers

ENTER key (0D)


This isn't a token, a character, a command or a
function; it is handled in special ways wherever it is met with.
0D is returned as a "main code" by the keyboard scanning
routine, and isn't changed by 0333 K DECODE and 0367 K
DIGIT regardless of the state of the various flags; ENTER is
ENTER whatever shifts are pressed, even in E or G mode.
A press of ENTER during editing makes an exit from the
EDITOR loop; the 0D code isn't copied to the edit line. In the
case of new BASIC in the editing area, there is already an 0D
code at the end of the line - it is a fixture at the end of the
editing area. In the case of an expression input against an
INPUT command, it isn't required. Thus 0D will be found in
the program area always and only at the end of BASIC lines.
This use of ENTER is handled by a call from 0F92 ED
KEYS
through the editing keys table (0FA6) to 1024 ED ENTER. The
methods of handling it when encountered in other uses are
sufficiently indicated in the list of Rems below.
007D SKIP OVER return if encountered
07AD LD CH PR add carriage return to filename
0A4F PO ENTER empties print buffer during print
0C55 PO SCR called when handling

278
100C ED RIGHT returns if current character is ENTER
11A7 REMOVE FP returns if ENTER found
1219 RAM SET on start-up edit-line consists of single 0D
12CF MAIN 3 checks for null line
155D MAIN ADD line length calculated to carriage return
157D MAIN ADD1 checks for null line
16B0 SET MIN restores 0D at end of editing area
1835 LIST ALL printed
18A1 OUT LINE5 jump if ENTER found
19B1 EACH S 6 jumps back unless encountered
1B29 STMT L 1 treats as end of BASIC line
1BF4 STMT NEXT treats as end of BASIC line
1CDE FETCH NUM jumps forward on code 0D
1FF5 PRINT CR outputs carriage return
2048 PR ST END returns if ENTER found
2061 PR POSN 2 prints newline unless separator found
211C IN PR 2 automatically at end of INPUT prompt
21B9 IN ASSIGN signals return
250F S QUOTE S error if found after odd no of quotes
2712 S CONT 2 prints newline at end of expression
35DE val puts 0D at end of string in work space
as end marker

ENTER EDITING SUBROUTINE see 1024 ED ENTER

entering BASIC characters see 0F2C EDITOR

entries (INPUT) see 2089 INPUT

entry in table, entry of array see tables, arrays

ENT TABLE 338E (0028 FP CALC)


Exit from:
336C SCAN ENT
338C DOUBLE A (335B CALCULATE, 0028 FP CALC)

E PPC system variable 5C49

279
Bytes: 2
The number of the current line, ie
- it is brought down for editing if EDIT is pressed
- the ">" cursor must be printed against it in listings
- an "automatic listing" scrolls till it appears on
screen.
The number is stored right way round in E PPC but in
reverse in the BASIC program line; hence the reversed reading
in
1059 ED UP.
Written by:
0FA9 ED EDIT (lo byte written twice)
155D MAIN ADD
1822 LIST 5
Read by:
0FA9 ED EDIT
0FF3 ED DOWN
1059 ED UP (read, then hi byte read to L)
157D MAIN ADD1
1795 AUTO LIST
1855 OUT LINE
190F LN FETCH
Rems:
17E1 AUTO L 2 if < S TOP, put in S TOP

ERASE key (D2) see also commands, functions and


operators,
KEYBOARD SCANNING, extended mode table 0284
The 7 key in E mode with symbol shift produces the
command ERASE. The "ERASE statement" isn't accepted by
the plain Spectrum, without attachments of some kind; with
peripherals such as Interface 1 it requires to be followed by a
channel letter, stream number and filename, or similar.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.

280
1B10 P ERASE causes a jump via 1C8C CLASS 0A, which
collects the
channel letter, and 1C10 CLASS 00 to the executive routine
1793
CAT ETC - which however merely produces the error report
"Invalid stream".

ERR NR system variable 5C3A see also errors


Bytes: 1
The_error_number, one less than the report code
currently required; A is code 0A, other letters follow up to 1B
for R. So the error number of R is 1Ah/26d. The report
messages are stored in order of their report_codes at 1391 in
the ROM.
Initially set by 1219 RAM SET at FF to give report code
zero "OK". This is the only possible value of ERR NR with bit 7
set; this fact is used eg at 1026 ED END. It is again set to FF
each time a line is checked for syntax, by 1B17 LINE SCAN.
All calls to RST 0008 ERROR 1 are followed by a byte
from FF to 1A, which is loaded into ERR NR (0055 ERROR 3).
This byte is read and incremented when it is time to print the
error report, which is done at 1313 MAIN G and 133C MAIN 5.
ERR NR is IY + 0; the IY index marker is set on it at
1219 RAM SET, and again in 2D2B STACK BC in case it has been
changed by a USR routine.
Written by:
0055 ERROR 3
1001 ED STOP
107F ED ERROR
1167 ED FULL
12AC MAIN 2
12CF MAIN 3
1B17 LINE SCAN
2148 IN VAR 2
Read by:
1026 ED END
12AC MAIN 2

281
1303 MAIN 4
133C MAIN 5
Rems:
1219 RAM SET loaded into IY; ERR NR is IY+0
2D2B STACK BC ditto; in case IY had been changed by a
USR routine

error address, error pointer see errors, 5C3D ERR SP, 5C5F
X PTR (the terms are used ambiguously in the notes and may
mean either of these)

error cursor, error handling, error marker, error messages


see errors

error number see 5C3A ERR NR

error register
Used in 2ACC INT EXP1 and in 2A52 SLICING and
following to mean the temporary "error flag" set up in the A
register; if it is still zero, it signals "no errors yet".

errors, see also 0008 ERROR 1, 2530 SYNTAX Z


Errors in BASIC input are checked by_syntax_checking;
this means making a "dry run" through the same routines as
those used in execution, but with bit 7 of FLAGS cleared;
constant reference to this flag, usually through the mini-
subroutine 2530 SYNTAX Z, enables parts of the execution to
be skipped or replaced with specifically syntax-checking
routines. Sometimes an error cannot be picked up till the
program is actually running, eg a number calculated by the
program which is out of its permitted range.
Detection of any error produces a jump to one of the
_report_routines; see the index entries for REPORT A etc.
Nearly all of these consist of a call to 0008 ERROR 1, which
puts the _error_address in 5C5F X PTR; ERROR 2 reads
the_error_number which must follow any call to ERROR 1;
ERROR 3 puts the error number in 5C3B ERR NR and sets the

282
stack pointer to the_error_return_address held in 5C3D ERR
SP. Thus return is made not to the point from which ERROR 1
was called but to the error return address.
[It is important to distinguish the error address and
the error return address, though the notes don't always
succeed in doing so; the error address is an address in the
work space or editing area where a flashing error cursor will
be inserted to signal the error, the error return address is the
address to which execution must go to signal the error.]
The error return address in run time is 1303 MAIN 4,
because execution of BASIC commands, which includes RUN
and therefore all program execution, is by the call to 1B8A
LINE RUN
(misprinted PROG RUN) just before MAIN 4. Whether the
program is interrupted by an error or goes to completion,
the_error_report is now printed - but this may be "OK", which
also counts as an error report.
The error number is one less than the_report_code, ie
the error number for REPORT 0 "OK" is FF, the number for
REPORT
1 "NEXT without FOR" is zero, etc. This number is used to
index
into the list of_report_messages at 1391 by 133C MAIN 5.
[Each of the report routines is only two bytes, and it
is worth duplicating any of them if the duplicate is within JR
range of only two exit jumps. Many such duplications actually
occur, but they aren't carried to their logical extreme; eg
there are seven JPs to 1C8A REPORT C within easy range of
06C3, where a duplicate could have been placed. One or two
other inconsistencies in the labelling suggest a certain lack of
coordination in the programming team.]
Syntax checking takes place when ENTER is typed in to
conclude input against the 0F2C EDITOR loop. While BASIC is
actually being typed in to the editing area and copied to the
lower screen, or an expression is being typed into the work
space in response to an INPUT command, no check is made of
the codes input and the only errors that are called are "Out of

283
memory" if there is no room for the new inputs, or "Out of
screen" if there is no room to print them on the screen. The
error address now is 107F ED ERROR or 1167 ED FULL; either
of these just makes a "rasp", without stopping the input or
printing an error message. There is one exception to this:
input of a "down arrow" during an INPUT ... LINE input will
break off the input with a "STOP in INPUT" message. This is
implemented by
0FF3 ED DOWN, 1001 ED STOP and 1026 ED END without any
actual call to the ERROR routines.
When 1024 ED ENTER makes the exit from the EDITOR
loop, it restores the error stack pointer to the value it had on
entry to the loop, but the EDITOR subroutine returns
normally whether there is an error or not.
On exit from the EDITOR loop after input of BASIC
program lines or commands, a call to 1B17 LINE SCAN from
12AC MAIN 2 takes care of the syntax checking: it makes a dry
run through the line with the syntax checking flag showing
"check syntax". The machine stack is again empty, and the
error address is the same as the return address, so LINE SCAN
will return into MAIN 2 whether or not an error is found, with
an error number in
5C3B ERR NR; if there was an error, there will be an address in
5C5F X PTR showing the location of the error in the editing
area. If the error number is anything but FF "OK", the EDITOR
is
called again from MAIN 2. This time, when the line is copied
to
the screen by 187D OUT LINE2 called from 111D ED COPY, it
will have a flashing_error_cursor in it at the 5C5F X PTR
address,
printed by 1894 OUT LINE4.
The returns from the EDITOR calls in the 2089 INPUT
command routine are a little different, though the effect is
similar. They are different again if input is being made
otherwise than from the keyboard, but this complication will
be ignored.

284
For an ordinary INPUT, not ... LINE, the return address
pointed to for error returns is set to 213A IN VAR 1; syntax is
checked by the call to 21B9 IN ASSIGN from 2148 IN VAR 2, just
after the call to EDITOR, so any errors will return to IN VAR 1,
going back into EDITOR with an error address and producing
a copy of the input with a flashing error cursor, just as for
inputs of BASIC. The error stack pointer is replaced in 2174 IN
VAR 5 before the second call to IN ASSIGN which actually
makes the assignment.
An INPUT ... LINE is the input of a string without its
bounding quotes, and no errors need be looked for; any codes
at all can be accepted between quotes. The error address
hasn't been changed and it isn't changed back; IN VAR 1 was
skipped, and the relevant part of IN VAR 5 is skipped as well.
[Actually, K-mode tokens cannot be typed normally into
an INPUT ... LINE, though symbol shift and E-mode tokens
can.
Even K-mode tokens can be typed in by preceding each one
by THEN and deleting THEN afterwards.]
The following list to a reasonable length, references to the
notes have been omitted if they merely comment on a call to
2530 SYNTAX Z or on jumps to the REPORT labels):
Introduction syntax checking
0008 ERROR 1 address of error to X PTR
0053 ERROR 2 error number to ERR NR
0427 BE OCTAVE out-of-range pitch values excluded
053F SA/LD RET carry flag marks loading error
05E3 LD EDGE 2 carry flag signals tape error
0652 SA DATA can't save a new array
0672 SA V OLD mistake in error checking
0692 SA DATA 1 array must end with ')'
0802 LD BLOCK return if no error
0F30 ED AGAIN sets error address 107F ED ERROR
1026 ED END jump if any errors
107F ED ERROR special error address for editing
111D ED COPY sets error address 1167 ED FULL
1167 ED FULL deals with errors on copying edit line

285
117C ED C DONE drops temporary error return
117E ED C END restores ERR SP
1219 RAM SET sets ERR SP at bottom of machine stack
12A2 MAIN EXEC loop produces error reports
12AC MAIN 2 scan edit line for errors
12CF MAIN 3 edit line has passed syntax
1303 MAIN 4 error return address during execution
1313 MAIN G report code made letter or number
133C MAIN 5 print report code and message
155D MAIN ADD will print "No room for line" not "Out of
memory" on memory overflow
15DE WAIT KEY1 error on input from peripherals
15F7 CALL SUB exit from printing routines if no error
1767 OPEN 3 continue if no error
1894 OUT LINE4 print error cursor
18C1 OUT FLASH prints error cursor
1B17 LINE SCAN entry to interpreter for syntax checking
1B6F SEPARATOR report routine only in run time
1BEE CHECK END error if not end of statement
1C59 VAL FET 2 syntax flag is in D register
1EDC CLEAR 2 save error return address in resetting
stack pointer
1F23 RETURN juggle with error return address in getting
GO SUB return address
1F36 REPORT 7 replace error return address before
calling 0008 ERROR 1
1FC3 UNSTACK Z drops address when checking syntax
2129 IN PR 3 error address changed for BASIC input
213A IN VAR 1 sets error address for input
2148 IN VAR 2 removes FP forms in line after error
2522 S 2 COORD exits into SYNTAX Z
2530 SYNTAX Z checks flag for syntax checking
2634 S INKEY$ direct check of FLAGS bit 7
2668 S SCREEN$ errors checked by 2522 S 2 COORD
2672 S ATTR errors checked by 2522 S 2 COORD
267B S POINT errors checked by 2522 S 2 COORD
27BD S FN SBRN checks syntax of FN statement

286
27D9 SF ARGMTS call to 24FB SCANNING checks syntax
of
expressions
2831 SF VALUES spaces for FP numbers already made in
DEF FN statement
2934 V SYNTAX deviation for syntax checking
2996 STK VAR when called from 2C02 DIM merely checks
syntax
29EA SV LOOP deviation for syntax checking
2A81 SL SECOND set A register to FF if out of range
2A94 SL DEFINE checks A "error register"
2ACC INT EXP1 zeroes A for "error register"
2AE8 I CARRY move carry flag into error register
2C9B DEC TO FP syntax checking includes inserting FP
number forms
2CA2 BIN DIGIT jump out with any character after BIN
except 0 or 1

error stack pointer see 5C3D ERR SP

ERROR 1 subroutine 0008


Deals with the printing of error reports. See errors
above for a general discussion.
Input parameters: the byte in the "return address"
immediately following the call to ERROR 1 holds the error
number.
Action: copy the address in the editing area or workspace
where the error was detected from 5C5D CH ADD into 5C5F X
PTR.
_0053_ERROR_2: POP the "return address"
- read the error number.
_0055_ERROR_3 (entry here from 1F15 REPORT 4 - "Out of
memory". No address is required in 5C5F X PTR, because this
error is only reported during execution; if it arises during
input the signal is a "rasp"): copy the error number into 5C3B
ERR NR
- reset the machine stack pointer from 5C3D ERR SP;

287
changing the return address.
Exit: into 16C5 SET STK (from 0055 ERROR 3), which
clears
the calculator stack.
Called by all the REPORT routines.

ERR0R 2 0053 (0008 ERROR 1)


Jumps from:
0008 ERROR 1

ERROR 3 0055 (0008 ERROR 1)


Exit from:
0053 ERROR 2 (0008 ERROR 1)
1F15 REPORT 4

ERR SP system variable 5C3D see also errors, GO SUB stack


Bytes: 2
The_error_stack_pointer: marks the address to which
execution is to return from 0008 ERROR 1. This return
address is on the machine stack, and the stack pointer setting
to make it the immediate return address is kept in ERR SP.
In run time and syntax checking by 1B17 LINE SCAN it is
the last address on the machine stack above the GO SUB
addresses, so 0008 ERROR 1 returns out of_all subroutines
into the main execution loop. But it is temporarily changed
while in editing mode to hold 107F ED ERROR or 1167 ED
FULL - errors in this mode merely produce a rasp and return
to the editing loop -
and in input mode to hold 213A IN VAR 1 at IN VAR 1.
Both ERR SP and 5C5F X PTR are called "error pointer" in
the notes, but they are quite different: 5C5F X PTR is an
address in the editing area or work space where an error has
been detected.
Written by:
0F30 ED AGAIN
1026 ED END
111D ED COPY

288
117E ED C END
1219 RAM SET
1EDC CLEAR 2
1EED GOSUB
1F23 RETURN
213A IN VAR 1
2174 IN VAR 5
Read by:
0055 ERROR 3
0F2C EDITOR
111D ED COPY
2129 IN PR 3

E SAVE 2D55 (2D4F E TO FP)


Jumps from:
2D4F E TO FP

e-to-fp subroutine 2D4F


In the ROM, always called direct as E TO FP. The option
to call it from 0028 FP CALC with the literal 3C does however
exist.
Converts "xEy", ie x times 10 to the power y, to full FP
format. To put it like this is, however, illogical; for x must
be in FP format already. It would be more logical to say: shifts
x by y_decimal places to the left, or shifts the decimal point
of x by y decimal places to the right.
Assuming y is positive, x must be multiplied by ten if
bit zero of the binary number y is set; again by a hundred if
its bit one is set; again by 10,000 = a hundred hundred if its
bit two is set; again by 100,000,000 = 10,000 * 10,000 if its
bit three is set; ... etc. If y is negative, the procedure is
the same except that x is to be divided by ten etc, instead of
multiplied.
Input parameters: A holds y; always an integer, 00 -> 7F
are read as positive, 80 -> FF as negative
- x is last value on the calculator stack
- HL and DE point to the last value on the stack and to

289
the stack end.
Action: get the sign of y in the carry flag; RLCA/RRCA
has just the same effect as BIT 7,A except that it works the
carry flag instead of the zero flag. The zero flag is needed
later, in E TST END
- if y is positive jump on to E SAVE
- (y is negative) negate y to get its absolute value;
the carry flag isn't changed
_2D55_E_SAVE: save the flags and y
- make a pointer on 5C92 MEMBOT; the address of mem-0
- call 350B FP 0/1; it reads the carry flag and puts a
sign flag in mem-0, zero for positive or one for negative, as a
small integer
- use the calculator to put z = ten on the calculator
stack; the stack holds, from the top, z, x
- recover the flags and y.
_2D60_E_LOOP: rotate the lo bit of y into the carry flag;
zero goes into the hi bit, and the zero flag shows Z if y is now
zero
- if the lo bit is zero jump on to E TST END
- save the flags and the rotated y
- use the calculator to put z in mem-1 and get the sign
flag from mem-0
- if the sign flag is one jump on to E DIVSN; negative
- multiply x by z; this effects the decimal shift left
of x corresponding to the set bit of y
- jump on to E FETCH.
_2D6D_E_DIVSN (the sign is negative): divide x by z; this
effects the decimal shift right of x corresponding to the set
bit of y.
_2D6E_E_FETCH: get z back from mem-1
- unstack the rotated y and the flags.
_2D71_E_TST_END: if the flag shows zero jump on to E
END;
all the remaining bits of y are zero
- (non-zero y) stack y and the flags
- use the calculator to square z: replacing ten by a

290
hundred, a hundred by 10,000, 10,000 by 100,000,000, and
so on.
This is done whether the last bit of y was set or not
- unstack y and the flags
- jump back to E LOOP.
_2D7B_E_END (no more set bits in y): delete z from the
calculator stack.
Exit: RET, at 2D7B E END.
Output parameters: the number xEy is last value on the
calculator stack
- mem-0 and mem-1 in the calculator memory corrupted.
Called by:
2E24 PF SMALL
2E56 PF LARGE
Exit from:
2D18 E FP JUMP
Rems:
350B FP 0/1 called by; puts flag in mem-0 in this case

E TST END 2D71 (2D4F E TO FP)


Jumps from:
2D60 E LOOP

exchange subroutine 343C


Called frequently from 0028 FP CALC by the literal 01 to
transpose the last two values on the calculator stack; also,
once, called direct as EXCHANGE.
A simple subroutine, which essentially only exchanges
five bytes from the addresses starting at HL with the five
starting at DE; it could be used from m/c for a much wider
range of applications, especially if called at 342E SWAP BYTE
with a counter already in B. If an even-numbered counter is
used HL and DE will end up exchanged.
Input parameters: DE and HL address the first bytes of
the numbers to be exchanged; when the subroutine is called
from the calculator they are the last and second last values on
the calculator stack.

291
Action: make a loop counter of five.
_342E_SWAP_BYTE: put the byte from DE into C and the
byte
from HL into A
- exchange DE and HL
- replace the bytes the other way round
- increment HL and DE
- loop back to SWAP BYTE counting down to zero
- exchange HL and DE back; assuming an odd number of
exchanges.
Exit: RET, from 343E SWAP BYTE.
Output parameters: HL and DE have been moved on five
bytes; if it was a calculator call DE is now on the stack end
- the two numbers are exchanged
- B zero, A and C corrupted.
Called with literal 02 by:
0427 BE OCTAVE
1736 OPEN
1D16 F REORDER (twice)
1DDA NEXT LOOP
235A C ARC GE1 (4 times)
23A3 DR SIN NZ (twice)
23C1 DR PRMS (6 times)
2439 ARC START
245F ARC END (twice)
247D CD PRMS1 (twice)
2497 DRAW SAVE (twice)
2D40 NXT DGT 2
2E01 PF LOOP
3453 G LOOP
36A0 n-mod-m
36B7 X NEG
371C VALID (3 times)
373D GRE 8 (twice)
37A1 ZPLUS
37DA tan
37E2 atn (twice)

292
37FA CASES
3851 to-power
385D XISO (twice)
Called as EXCHANGE by:
3543 EX OR NOT

executing see BASIC INTERPRETER, FLAGS bit 7

EXECUTIVE ROUTINES
A major section of ROM, see Introduction, from 11B7 NEW
to 1A42 OUT NUM 4: initialization, main execution loop,
channel and stream handling, reclaiming, listing BASIC, etc.

existing variable see 2AFF LET, variables

EXIT 36C2 (36AF int)


Exit from:
36B7 X NEG (twice) (36AF int)

EX OR NOT 3543 (353B no-l-eql etc)


Jumps from:
353B no-l-eql

EXP key (B9) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The X key in E mode without shift produces the function
EXP; it requires one numeric operand X, and the value of the
function is the constant e raised to the power X. For e see
under the exp subroutine below.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B9 first to 0A, then to E6,
and adds the priority 10h/16d. Code and priority 10E6 are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following EXP is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E6 to 26, the calculator
offset for 36C4 exp.

293
exp subroutine 36C4
Called from 0028 FP CALC with literal 26 as the
executive routine of the EXP function. Also used as exit from
3851 to-power, but not otherwise called from ROM. Could be
called direct from m/c Finds EXP X, ie e to the power X.
The number e is just the number 2.718281828...d, the
"exponential constant" - easy to remember, because of the
repeated "1828" - but it has the unique property that the
differential of e**x is e**x, which makes it amazingly useful in
the differential calculus and related topics, including
advanced trigonometry and statistics. For this reason e was
made the base of the "natural logarithms" LN x, ie if y = LN x
then x = e**y.
It has nothing much to do with E-format numbers; in both
cases E stands for "exponent".
In hex e is 2.ADF85458...h, as a full FP number it is 82
2D F8 54 58. Unlike the other important "transcendental
number" pi, it isn't included as a Spectrum function, but if
required in a BASIC program it is easily got as EXP 1.
The subroutine breaks down the calculation of e**X into a
series of steps, some of them quite simple:
1. First find Y = X/LN 2, ie multiply X by the FP form 81
38 AA 3B 29 which represents 1/LN 2. The result we are
looking
for will be 2**Y; for
if Y = X/LN 2, then
X = Y LN 2, and
e**X = [e**(LN 2)]**Y.
But e**(LN 2) = 2; this is what LN 2 means, so
e**X = 2**Y.
2. Split Y into an integer part N, which may be negative,
and a positive fractional part W.
Since Y = N + W,
2**Y = 2**N * 2**W.
3. 2**Y will be easy if we can find 2**W - just a matter of

294
adding positive or negative N to the exponent of the FP form
for
2**W. N is parked in the calculator memory for the moment.
4. 2**W requires Chebyshev polynomials. W is a positive
fraction, ie between zero and one, and 2**W is found by using
the series generator 3449 series-08 with eight Chebyshev
constants and a "reduced argument" Z = 2*W - 1, which is
between minus one and one.
[The notes don't explain the reduced argument; this
presumably fits a standard Chebyshev polynomial calculation
from the reference on page 223, see under Chebyshev
polynomials.]
The Chebyshev constants used are:

FP form Decimal
1. 63 36 0.000000001
2. 68 65 66 0.000000053
3. 6D 78 65 40 0.000001851
4. 72 60 32 C9 0.000053453
5. 77 21 F7 AF 24 0.001235714
6. 7B 2F B0 B0 14 0.021446556
7. 7E 7E BB 94 58 0.248762434
8. 81 3A 7E F8 CF 1.456999875

5. Recover N and add it to the exponent byte of 2**W: this is


the result required.
Input parameters: X is the last value on the calculator
stack; it must be on the calculator stack even for direct calls.
Action: use the calculator to restack X in full format;
unnecessarily, as the multiply operation will do this again
- put 1/LN 2 on the stack and multiply to get Y
- get N = INT Y and put it in mem-3
- take N from Y to get W; even if Y is negative, INT Y
is still less than Y, so N is positive in any case
- calculate the "reduced argument" Z = 2*W - 1
- calculate the Chebyshev result with the constants
shown above; the result is 2**W
- get N from mem-3
- exit from the calculator; N is on top of the stack and
295
2**W below it
- call 2DD5 FP TO A to get N into the A register
- if FP TO A returns with NZ jump on to N NEGTV; N is
negative
- if FP TO A returns with C report "Number too big"; N
is too big for a single register, ie N > 255d
- (N positive and N < 256d) add N to the exponent byte
of 2**W on the stack; this isn't a true exponent, but the true
exponent plus 80h/128d
- if this comes out less than 256d jump on to RESULT OK
- (result exponent more than 256d in FP form) report
"Number too big"; the result exponent is more than 127d as a
true exponent.
_3705_N_NEGTV: if FP TO A returned with carry jump on
to
RSLT ZERO; ABS N was too big for the A register, eg EXP -300.
This isn't an error but an "underflow" - the answer is zero,
within the margins of error
- take the FP form exponent of 2**W from ABS N
- if this doesn't set carry jump on to RSLT ZERO; ABS N
is at least 80h bigger than the true exponent, and as N is
negative this means underflow again
- (result ABS N - FP exponent, no carry) negate this
result; now FP exponent minus ABS N, which since N is
negative
is the result exponent.
_370C_RESULT_OK (there has been no overflow or
underflow): put the result exponent in its place on the
calculator stack and return.
_370E_RSLT_ZERO (there is underflow): use the calculator
to replace the last value with zero and return.
Exit: RET, from RESULT OK or RSLT ZERO.
Output parameters: EXP X remains as last value on the
calculator stack
- HL points to the last value and DE to the stack end
- mem-0 to mem-3 are corrupted.
Exit from:

296
3851 to-power
Rems:
Introduction Chebyshev polynomials used to implement
3449 series-06 uses Chebyshev polynomials
370C RESULT OK result is last value
370E RSLT ZERO if result is zero
Appendix (p. 224) BASIC demo program

expanded form of FP numbers see CALCULATE

EXPECT NUMERIC/STRING EXPRESSIONS SUBROUTINE


see 1C79 NEXT
2NUM

EXPONENTIAL see 36C4 exp

EXPONENTIATION SUBROUTINE see 3851 to-power

exponent of E-format (BASIC) number see E-format


numbers

exponent of FP number see CALCULATE

EXPRESSION EVALUATION, expression evaluator see 24FB


SCANNING
A major section of ROM, from 24FB SCANNING to 2D3B
INT TO FP; the SCANNING loop and its ancillaries.

expressions see also 24FB SCANNING


An expression may be
- an actual number, decimal, BIN or E-format
- a string between quotes
- a variable or array element, string or numeric
- a slice of a string or string array element
- or any combination of these made up of functions and
operators with arguments from this list

297
A_numeric_expression is one which has a_numeric_value
or _result, ie its value is a number; a_string_expression one
which has a_string_value or_result, ie its value is a string.
Bit 6 of FLAGS signals the_string/numeric_status or
_nature of the last value on the calculator stack, and bit 6 of
FLAGX signals the status of an input against the INPUT
command;
in both cases, set if it is meant for a FP number, zero if for
the parameters of a string.
Expressions may be compounded of_sub-expressions, eg
CHR$ (INT (PI * VAL X$) - ABS M).
[This useful term seems to be used only once in the
notes, at 2713 S CONT 3, reached just after an expression has
been evaluated: the notes explain that if the last evaluation
gave a string result a "(" may indicate a slice; though not for
example in PRINT s$ + (t$ AND Y). But they are wrong in
saying
"(" after a numeric expression introduces a sub-expression. It
is always an error, see under S CONT 3 in 24FB SCANNING.]
Introduction - comprehensive expression evaluator
175D OPEN 2 error if string expression is null
1767 OPEN 3 saves and restores length of expression
178B OPEN END expression must be single character
1C59 VAL FET 2 evaluate expression on R of LET etc
1C79 NEXT 2NUM points CH ADD to start of first
1C7A EXPT 2NUM evaluates two numeric with comma
1C82 EXPT 1NUM evaluates one numeric
1C8C EXPT EXP evaluates single string
1CDE FETCH NUM if none use zero
1CF0 IF expression between IF and THEN is last value
1FA6 DEF FN 6 definition of function handled as
2024 PR ITEM 3 check FLAGS 6 for status of expression
24FB SCANNING evaluates expressions
2634 S INKEY$ set flag for string result
26C3 S NUMERIC set flag for numeric result
26C9 S LETTER stacks numeric result
26DF S NEGATE op code flags: 6 set for string argument,

298
7 set for string result
2707 S NO TO $ two functions, CHR$ and STR$, make
string results from numbers
2712 S CONT 2 argument end may not be expression end
2713 S CONT 3 "(" signals sub-expression
2734 S LOOP priority of operation will be zero at end
of expression
2764 S RUNTEST set FLAGS 6 for evaluated expression
2773 S TIGHTER change op code for string arguments
2788 S NOT AND set op code flag for numeric expression
27E9 SF FLAG 6 flags status of FN expression
27F7 SF RUN makes temporary one-byte status flag
288D SF VALUE start of DEF FN function expression
after "="
2ACC INT EXP1 evaluates expression
2ACD INT EXP2 value made last value on stack
2BA6 L ENTER puts result from stack into vbles area
2E24 PF SMALL expressions which can give wrong results
35DE val string stripped of quotes considered as
numeric expression
360C V RPORT C string in work space evaluated as
expression

EXPT EXP subroutine see 1C8C CLASS 0A

EXPT 1NUM subroutine see 1C82 CLASS 06

EXPT 2NUM subroutine see 1C7A CLASS 08

extended mode see 5C41 MODE

EXX see alternate registers

299
F

'false' expression see logical value

fbpapink
Used in this index as abbreviation for the attribute
byte format: bits 7 and 6 set for FLASH and BRIGHT, then 3
bits each for PAPER and INK colours.

FETCH A VALUE SUBROUTINE see 1C56 VAL FET 1

FETCH NUM subroutine 1CDE


Evaluates a numeric expression in the BASIC statement,
if it finds one; if the end of the statement has been reached,
uses zero. In either case the result is put on the calculator
stack.
Input parameters: A holds the code at the BASIC pointer
in 5C5D CH ADD.
Action: if A is 0D newline exit into USE ZERO
- if it isn't 3A colon exit into 1C82 EXPT 1NUM, which
gets a number from the BASIC on to the calculator stack.
Exit: into 1C82 EXPT 1NUM if the code isn't a terminator
- if it is, into 1CE6 USE ZERO which puts zero on the
stack.
Output parameters: none.
Called from:
181F LIST 4
1C0D CLASS 03

FETCH TWO subroutine 2FBA


Loads the ten bytes of two FP numbers, the one starting
at HL into H'B'C'CB and the one starting at DE into L'D'E'DE.
The exponent bytes are in H' and L'; the last two bytes of the
number at HL are in CB, not BC as you might expect.

300
Sometimes it is convenient to move the low byte into A and
use B as a loop counter.
In the ROM the two input numbers are always the last
value on the calculator stack (DE) and the second last (HL),
but this isn't necessary for m/c calls.
The number at DE isn't changed, but the sign byte of the
number at HL is overwritten with a sign byte indicating what
will be the correct sign if the two numbers are multiplied or
divided; the bits apart from the hi bit are corrupted.
The subroutine is called at 2E6F PF MEDIUM merely to
get the mantissa of the last value.
Input parameters: DE and HL point to the two input
numbers; in calls from the calculator, DE to the last value on
the calculator stack and HL to the second last
- bit 7 of A indicates the sign which will result from
multiplication or division of the two numbers; set by 30C0
PREP
M/D, zero for positive and one for negative.
Action: juggle with the machine stack and EXX to read
each of the ten bytes into the appropriate place
- put A in the sign byte of the number which was
addressed by HL; the second last value.
Exit: RET.
Output parameters: AF and HL are restored unchanged
- the two input numbers are still at the same locations,
but the second byte of the one formerly addressed by HL now
holds the sign byte
- the two numbers have been loaded into the ten
registers thus:
from HL (second last value) in H'B'C'CB
from DE (last value) in L'D'E'DE
Called from:
2E6F PF MEDIUM
2ECF PF FRACTN
3055 SHIFT LEN
30F0 MULT LONG
31AF division

301
Rems:
3114 MULT LOOP mantissa of multiplicand is in D'E'DE
3125 STRT MULT mantissa of multipliier is in B'C'CA

F FOUND 1D7C (1D03 FOR)


Exit from:
1D64 F LOOP (1D03 FOR)

final code of key see KEYBOARD SCANNING, 0333 K


DECODE

FIND EACH STATEMENT SUBROUTINE see 198B EACH


STMT

FIND INT misprint in 1822 LIST 5 for 1E99 FIND INT2

FIND INT1 subroutine 1E94


Takes a positive integer, zero to FFh/255d, off the
calculator stack, with error messages if out of range or not
positive.
Cf: 2314 STK TO A - takes a one-byte number, positive or
negative, of absolute value up to 255d, from stack into A with
sign marker in the C register; reports an error only if the
absolute value is out of range
2DD5 FP TO A, which does most of the work for the
present subroutine - produces flags but no error messages.
The entry point FIND INT 2 similarly gets a positive
integer zero to FFFFh/65535d off the calculator stack, with
error messages.
Cf: 2307 STK TO BC - takes_two integers off the stack and
puts one each in B and C
2D7F INT FETCH - copies a small integer into DE,
without error reports or flags
2DA2 FP TO BC, which does most of the work for the
present subroutine - produces flags but no error messages.
Input parameters: none
- at least one number on the calculator stack.

302
Action: call 2DD5 FP TO A which puts the number,
rounded to an integer, in the A register; it flags carry if it is too
big and NZ if it is negative
- jump on to FIND I 1.
_1E99_FIND_INT2: call 2DA2 FP TO BC which puts the
number, rounded to an integer, in the BC register; it flags
carry if it is too big and NZ if it is negative
_1E9C_FIND_I_1: if the carry flag is set report "Integer
out of range"
- if the zero flag shows Z return
- (negative value flagged) report "Integer out of
range".
Exit: RET, from 1E9C FIND I 1.
Output parameters: the number is in A or BC, and is no
longer on the stack.
Called from:
0427 BE OCTAVE (twice)
171E STR DATA (misprinted STK TO A)
2070 STR ALTER
21FC CO TEMP 4
2294 BORDER
235A C ARC GE1 (twice)
2ACD INT EXP2
3645 read-in

FIND INT2 subroutine 1E99 see 1E94 FIND INT1


Called from:
0427 BE OCTAVE
06F9 SA CODE 4 (twice)
0723 SA LINE 1
1822 LIST 5 (misprinted FIND INT)
1E42 RESTORE
1E4F RANDOMIZE
1E67 GO TO
1E8E TWO P 1
1EAC CLEAR
1F3A PAUSE

303
200E PR ITEM 2
34A5 in
34AC peek
34B3 usr-no
Rems:
1E94 FIND INT1 twin subroutines

FIND I 1 1E9C
Exit from:
1E94 FIND INT1
1E99 FIND INT2

first byte of FP number, first value/number on calculator


stack, first operand see CALCULATE

FIRST 3D 3380 (0028 FP CALC)


Jumps from:
336C SCAN ENT

five byte FP form of numbers see CALCULATE

five call counter see KEYBOARD SCANNING, 5C00 KSTATE

flags see FLAGS, FLAGS2, FLAGX, P FLAG, TV FLAG


It is of course necessary to distinguish between the
machine code flags - the carry flag, Z/NZ flag, etc - and the
system flags held in the system variables, but there is seldom
any likelihood of confusion.
The machine code flags aren't referenced in this index,
any more than any other machine code terminology; with a
few exceptions, such as the alternate registers, the interrupt
instructions and the port I/O.
In most cases, each individual bit of a flag variable
may be considered a separate system variable.

FLAGS system variable 5C3B


Bytes: 1

304
FLAGS bit 7: syntax/run flag
On "executing"; off "syntax checking"
The whole BASIC program is read by the ROM twice at
least: once when it has just been input - actually as it is
being transferred from the editing area to the program area
for
"syntax checking" and insertion of the FP number forms - and
again when the program is being run or_executed. Many of
the routines are used for both purposes; when they need to
skip procedures not needed for one reading or the other, they
refer to bit 7 of FLAGS. In many cases this is done by calling
the mini-subroutine 2530 SYNTAX Z.
Turned on:
12CF MAIN 3
2174 IN VAR 5
360C V RPORT C
Turned off:
1B17 LINE SCAN
2148 IN VAR 2
35DE val
Read by:
1C59 VAL FET 2
1C96 PERMS
2530 SYNTAX Z
25DB S STRING
2634 S INKEY$
26C9 S LETTER
29FB SV MULT
Rems:
1B8A LINE RUN action depends on bit 7
1BB3 LINE END running needs address of next line
1C4E CLASS 02 different return address in run time
1E27 DATA skipped in run time
1E37 DATA 2 skip DATA statements in run time
25DB S_STRING string not stacked in syntax checking
268D S DECIMAL alternative routines for run and check

305
26B5 S STK DEC number read during execution
27BD S FN SBRN alternative routines for run and check
27F7 SF RUN in running, find DEF FN statement
294B V END output parameters have reflexes of bits 6, 7
2A52 SLICING effect depends on bit 7
28E3 V TEST FN search DEF FN statement only in run
time
28EF V RUN/SYN variable search skipped in checking
28FD V RUN search made in run time
294B V END variable letter flagged for syntax/run
2C05 D RPORT C skip DIM statements in run time

FLAGS bit 6: string/numeric status flag see expressions


On "last value on calculator stack numeric" off
"string". Cf FLAGX bit 6, which makes a similar signal for
inputs: on "expect numeric result", off "expect string".
Turned on:
1F6A DEF FN 1
26C3 S NUMERIC
2764 S RUNTEST
27E9 SF FLAG 6
28B2 LOOK VARS
Turned off:
1F6A DEF FN 1
25DB S STRING
2634 S INKEY$
2764 S RUNTEST
27E9 SF FLAG 6
28DE V STR VAR
2A49 SV SLICE?
2AA8 SL OVER
2AB2 STK ST $
Read by:
1C30 VAR A 2
1C56 VAL FET 1
1C59 VAL FET 2
1C82 EXPT 1NUM

306
1C8C EXPT EXP
1FA6 DEF FN 6
2024 PR ITEM 3
20D8 IN ITEM 2
20FA IN PROMPT
26C9 S LETTER
2713 S CONT 3
2734 S LOOP
275B S SYNTEST
2773 S TIGHTER
2852 SF ARG NL
2B66 L EXISTS
35DE val
Rems:
24FB SCANNING effect depends on bit 6
25BE S Q AGAIN jump on bit 6 (by SYNTAX Z) and reset
294B V END output parameters have reflexes of bits 6, 7

FLAGS bit 5: new key flag


On "dealing with new key"; off "no key yet".
Turned on:
0308 K END
Turned off:
0C88 PO SCR2
10A8 KEY INPUT
1303 MAIN 4
1634 CHAN K
1F4F PAUSE END
Read by:
10A8 KEY INPUT
1F49 PAUSE 2
Rems:
02BF KEYBOARD set to show "new key pressed"
1F3A PAUSE terminated by bit 5

FLAGS bit 4: not used. In the 128K versions this flag


signals "using 128K BASIC".

307
FLAGS bit 3: key mode flag
On "not in K mode"; off "in K mode"; used in decoding
main codes to final codes. Set or reset according to the last
character output.
Turned on:
0C88 PO SCR2
18F3 OUT C 1
Turned off:
1386 MAIN 9
18F3 OUT C 1
Read by:
02F1 K NEW with
034F K KLC LET
Rems:
0333 K DECODE read in D register (034F K KLC LET)

FLAGS bit 2: print mode flag


On "print L mode cursors" off "print K mode cursor";
used to determine whether a flashing K or a flashing E/L/C is
to be printed as cursor.
Turned on:
1881 OUT LINE3
1968 OUT CH 2
Turned off:
1881 OUT LINE3
1937 OUT CHAR
Read by:
18F3 OUT C 1

FLAGS bit 1: ZX printer flag


On "using channel 3 (ZX printer)"; off "using other
channel"
Turned on:
1219 RAM SET
164D CHAN P
Turned off:

308
1646 CHAN S 1
Read by:
0A23 PO BACK 1
0A4F PO ENTER
0A87 PO CONT
OADC PO STORE
0B03 PO FETCH
0B7F PR ALL
0BA4 PR ALL 2
0BC1 PR ALL 6
0C55 PO SCR
0DD9 CL SET

FLAGS bit 0: leading space flag


On "suppress leading space"; off "print leading space";
used in printing tokens.
Turned on:
0AC3 PO FILL
0B6A PO CHAR2
187D OUT LINE2
Turned off:
0B6A PO CHAR2
1865 OUT LINE1
Read by:
0C14 PO TABLE

FLAGS2 system variable 5C6A


Bytes: 1

FLAGS2 bit 7: not used

FLAGS2 bit 6: not used

FLAGS2 bit 5: not used

FLAGS2 bit 4: channel K flag


On "using channel K"; off "using other channel"; other

309
input channels can only be used when peripherals are
connected.
Turned on:
1634 CHAN K
Turned off:
1615 CHAN FLAG
Read by:
107F ED ERROR
12AC MAIN 2
213A IN VAR 1

FLAGS2 bit 3: caps lock flag see CAPS LOCK


On "in C mode, CAPS LOCK on"; off "CAPS LOCK off"
Flopped:
10DB KEY M&CL
Read by:
034F K KLC LET
18F3 OUT C 1

FLAGS2 bit 2: quotes flag


On "quotes have been opened"; off "out of quotes";
determines whether the colon and THEN are to be considered
as statement terminators.
Turned off:
1881 OUT LINE3
Flopped:
195A OUT CH 1
Read by:
1937 OUT CHAR
Rems:
198B EACH STMT temporary quotes flag in C register
199A EACH S 3 quotes on set by FF in C
19AD EACH S 5 reads temporary quotes flag in C

FLAGS2 bit 1: print buffer flag


On "something is in the printer buffer"; off "buffer
cleared"

310
Turned on:
0BA4 PR ALL 2
Turned off:
0EE7 PRB BYTES
Read by:
1303 MAIN 4

FLAGS2 bit 0: screen flag


On "something is on the screen"; off "screen cleared"
Turned on:
1795 AUTO LIST
Turned off:
0DAF CL ALL
Read by:
12CF MAIN 3

FLAGX system variable 5C71


Bytes: 1
All flags turned off by:
1313 MAIN G
1C22 VAR A 1

FLAGX bit 7: INPUT ... LINE flag


On "dealing with INPUT ... LINE"; off "not INPUT Turned
on:
20D8 IN ITEM 2
Turned off:
1313 MAIN G
1C22 VAR A 1
20ED IN ITEM 3
2174 IN VAR 5
Read by:
0F38 ED LOOP
1076 ED SYMBOL
20FA IN PROMPT
2129 IN PR 3
2174 IN VAR 5

311
FLAGX bit 6: input result string/numeric status flag
On "expect numeric result"; off "string result"; used by
the INPUT command.
Cf FLAGS bit 6, which records the status of the last
value on the calculator stack.
Turned on:
211A IN PR 1
Turned off:
1313 MAIN G
1C22 VAR A 1
20FA IN PROMPT
Read by:
21B9 IN ASSIGN/1C59 VAL FET 2
Rems:
1C56 VAL FET 1 bit 6 of FLAGS or FLAGX checked
1C59 VAL FET 2 bit 6 checked against FLAGS

FLAGX bit 5: input/editing flag


On "in input mode"; off "in editing mode". When the bit
is set, keyboard input is sent to the work space, when it is
zero to the editing area.
Turned on:
20FA IN PROMPT
Turned off:
1313 MAIN G (twice, apparently unnecessarily)
1C22 VAR A 1
2174 IN VAR 5
Read by:
0FA9 ED EDIT
0FF3 ED DOWN
1059 ED UP
1195 SET DE
1881 OUT LINE3
191C LN STORE
1937 OUT CHAR
Rems:

312
1031 ED EDGE line start address depends on state of
flag read in 1195 SET DE

FLAGX bit 4: not used

FLAGX bit 3: not used

FLAGX bit 2: not used

FLAGX bit 1: new variable flag


On "new variable"; off "variable is in variables area"
Turned on:
1C22 VAR A 1
Turned off:
1313 MAIN G
1C22 VAR A 1
Read by:
1DAB NEXT
2AFF LET

FLAGX bit 0: simple string flag see strings


On "complete simple string: old variable is to be
deleted"; off "slice or array element; overwrite old value"
Turned on:
1C30 VAR A 2
Turned off:
1313 MAIN G
1C22 VAR A 1
Read by:
2B72 L DELETE$
Rems:
2AFF LET set to delete old copy of variable

FLASH key (DB) see also colours, KEYBOARD SCANNING,


extended
mode key table (c)
The V key in E mode with either shift produces the token

313
FLASH. FLASH can be used either as a BASIC command or as
a print control item within a PRINT etc statement. In either
case it causes any new printing on the screen to flash on and
off, or to stop flashing, in that position on the screen. FLASH
must be followed by a parameter, one for "FLASH on", zero for
"off", eight for "no change".
CLS after a FLASH 1 command makes the whole of the
upper screen flash; flashing in the lower screen can only be
executed by the FLASH control code, see under BRIGHT
control code, or by
POKing 5C48 BORDCR. The flashing is executed by printing
alternately TRUE VIDEO and INVERSE VIDEO; the mechanism
which actually produces it is in the Spectrum hardware, not
under ROM control.
As a command, it is read by 1B29 STMT L 1 referring
through the syntax offset table 1A48 to the syntax parameter
table 1A7A. 1AED P FLASH causes a jump to 1C96 CLASS 07
(PERMS), the executive routine for all the colour item
commands INK to OVER.
As a print control item, execution is from within the
PRINT executive routine 1FCD PRINT; each new expression
following the PRINT command is checked by a call to 1FFC PR
ITEM 1 from 1FE5 PRINT 3. If it is FLASH, this in turn calls 21F2
CO TEMP 3 from 2024 PR ITEM 3; here the token code DB is
converted to the "embedded" control code 12h, which is then
sent through the output routine followed by the parameter
one or zero.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 12h for FLASH into the
control character table at 0A11 produces an indirect jump to
0A7A (0A1D + 5D) PO 1 OPER. See the index description of this
subroutine for the rather tricky way in which it collects the
parameter and sends execution to 0A87 PO CONT, which
finally executes the
FLASH command.
The effect of execution in every case is to set the hi

314
bit of 5C8F ATTR T, see under colours, and hence the hi bit of
the byte in the attributes area.

FLASH control code (12h) see BRIGHT control code

FLOATING POINT CALCULATOR, FLOATING POINT


NUMBERS
A major section of ROM from 32C5, the tables of
constants and calculation subroutine addresses to the end of
the character set at 3FFF. Contains the floating-point
calculator and most of its subroutines - see CALCULATE.

floating point forms in BASIC line see CALCULATE, number


in BASIC line

FLOATING POINT TO A SUBROUTINE see 2DD5 FP TO A

FLOATING POINT TO BC SUBROUTINE see 2DA2 FP TO BC

F LOOP 1D64 (1D03 FOR)


Jumps from:
auto

F L&S 1D34 (1D03 FOR)


Jumps from:
1D16 F REORDER

FN key (A8) see also commands, functions and operators,


KEYBOARD SCANNING, 0284 extended mode table (f )
The 2 key in E mode with symbol shift produces the
function FN. The_FN_statement must contain:
- a single letter function name, which must be followed
by "$" if the value of the function is a string; the name and
type of the function must be matched by a DEF FN statement
somewhere in the program.
- a pair of brackets "()"; these may be empty, or they

315
may contain any number of string or numeric arguments,
separated by commas.
On execution, 24FB SCANNING indexes into the scanning
function table at 2596 to find the executive routine 27BD S FN
SBRN, through 2672 S FN, which locates the corresponding
DEF FN statement and evaluates the function for the given
arguments.
26C9 S LETTER vbles looked for in DEF FN as well as
vbles area
27E9 SF FLAG 6 separates string result from numeric
2814 SF CP DEF check DEF FN for match with FN name
2831 SF VALUES arguments of FN evaluated
2852 SF ARG VL check for match of FN & DEF FN
arguments
2885 SF R BR 2 check for end of arguments in FN
288D SF VALUE evaluates FN function
28AB FN SKPOVR used by FN in searching DEF FN
statements
28E3 V TEST FN look for vble first in DEF FN statement

FN SKPOVR subroutine 28AB


Much like 0020 NEXT CHAR; finds the next "printable
character". However in reading a DEF FN statement NEXT
CHAR cannot be used, as it moves on 5C5D CH ADD, which is
marking the place in the FN statement.
Input parameters: HL is a pointer to the last character
read.
Action: increment the pointer and read its code
- if it is less than 21h jump back to FN SKPOVR; ie move
on again for a control code or space.
Exit: RET.
Output parameters: A and HL hold the code and address
of the next "printable" code.
Called from:
2814 SF CP DEF (twice)
2831 SF VALUES (twice)
2843 SF ARG LP

316
2852 SF ARG VL (twice)
295A SFA LOOP
296B SFA CP VR (twice)
Jumps from:
auto

FOR key (EB) see also commands, functions and operators,


KEYBOARD SCANNING
The F key in K mode produces the command FOR.
The_FOR _statement must have the form FOR f = x TO y STEP
z; only the STEP and its parameter are optional. See "FOR ...
NEXT loops".
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A78.
1A90 P FOR causes a jump via
1C6C CLASS 04: pick up the loop control variable
back to 1A90 P FOR, check for the "="
1C82 CLASS 06: evaluate the expression for VALUE
back to 1A90 P FOR, check for the "TO"
1C82 CLASS 06 again: evaluate the expression for LIMIT
1C11 CLASS 05: because there may be a STEP
into 1C16 JUMP C R which in turn jumps to the executive
routine 1D03 FOR.

FOR subroutine 1D03


Executes a FOR ... NEXT loop from BASIC. For the
terminology, see FOR ... NEXT loops. Called only from the
syntax parameter table at 1A90 P FOR.
Any or all of VALUE, LIMIT or STEP may be variables or
functional expressions in the BASIC. VALUE and LIMIT have
already been evaluated and their values put on the calculator
stack by the 1C82 CLASS 06 calls in the "syntax parameter"
routine 1A90 P FOR; STEP is evaluated by 1C82 EXPT 1NUM at
the start of the FOR subroutine.
In syntax checking, the subroutine just checks that

317
there is a statement terminator either immediately or after a
STEP parameter; the rest of the statement has already been
checked by 1A90 P FOR, and from F REORDER onwards the
subroutine is only executed in run time. Checking for "FOR
without NEXT" would be pointless while the program is still
being written.
In run time, it calls 2AFF LET to find or create the
loop control variable in the variables area. If a variable is
found with the letter to be used, it may be a simple numeric
variable, in which case its structure is changed: it must have a
letter with all three hi bits set, followed by eighteen bytes,
five each for the FP numbers VALUE, LIMIT and STEP, and
three for the looping line and statement number. See
variables
These eighteen bytes are temporarily marked as the
calculator memory; VALUE, LIMIT and STEP are put in its
mem-0 to mem-2 locations, the line number of the FOR
statement in the line number location and its statement
number incremented by one in the statement number
location. The line and statement numbers will be picked up by
the corresponding NEXT command when it
jumps back for another turn of the loop.
Now a trial call to 1DDA NEXT LOOP checks that at least
one pass will be required. If VALUE is already greater than
LIMIT, none will be required; this can happen eg with "FOR f =
st to 38", if the current value of "st" is 39 or more . If there isn't
going to be a pass, execution immediately jumps forward in
BASIC to the statement after the corresponding NEXT. The
BASIC is searched for the first NEXT statement using the same
looping variable; the line and statement number to be
jumped to, just
after this NEXT statement, are put in 5C42 NEWPPC and 5C44
NSPPC. The search starts from the FOR statement - all NEXTs
preceding it are ignored. If there is no such NEXT, this is a
"FOR without NEXT" error.
Input parameters: the BASIC pointer in 5C5D CH ADD is
on

318
the next BASIC character after the expression for LIMIT
- A holds the code at the pointer
- VALUE and LIMIT are on the calculator stack, LIMIT as
last value.
Action: if A doesn't hold CD STEP jump on to F USE 1
- (STEP is given in BASIC) move on the BASIC pointer to
the expression following CD STEP
- call 1C92 EXPT 1NUM to put the value of STEP from
BASIC on the calculator stack
- call 1BEE CHECK END; in syntax checking it reports
"Nonsense in BASIC" if this isn't the end of the statement, and
makes a double return to 1B76 STMT RET if it is
- (run time) jump on to F REORDER.
_1D10_F_USE_1 (no STEP given in BASIC): call CHECK END;
this must be the end of the statement
- (run time) use the calculator to put one on the stack
for STEP.
_1D16_F_REORDER (the calculator stack holds, from the
top, STEP, LIMIT, VALUE): use the calculator to reorder them
as VALUE, STEP, LIMIT
- call 2AFF LET to find or create the loop control
variable in the variables area and give it the value of VALUE
from the top of the stack
- put the variable pointer in 5C68 MEM; it is on the FP
number for the variable's value. This makes the variable a
temporary calculator memory, with VALUE in mem-0. There
is no reference to the memory in the subroutine itself, but it
is used in the call to 1DDA NEXT LOOP in F L&S
- read the variable letter and then set its hi bit;
simple numeric variable letters start 011, looping variables 111
- move the variable pointer on by six bytes; if it was a
simple variable, this now points just beyond it
- rotate the hi bit of the variable letter, as it was
read, into carry
- if carry is set jump on to F L&S; the variable is
already a looping variable
- (it is a simple numeric variable) call 1655 MAKE ROOM

319
with a counter of thirteen; to add space for two more FP
numbers and the line and statement numbers
- set the variable pointer on the first byte of the
temporary mem-1.
_1D34_F_L&S: delete LIMIT and STEP from the calculator
stack; for the moment this only means the stack end pointer is
moved down
- copy the ten bytes of LIMIT and STEP from the new
stack end to the temporary mem-1 and mem-2 of the looping
variable
- get the current line number from 5C45 PPC and put it
in the line number locations after mem-2
- get the current statement number from 5C47 SUBPPC,
increment it and put it in its location
- call 1DDA NEXT LOOP, which makes a trial subtraction
of VALUE from LIMIT to check whether a pass of the loop is
required
- if it returns with NC return; at least one pass will
be required
- (VALUE has already passed LIMIT; BASIC execution must
jump forward to the statement after the first matching NEXT)
get
the variable letter from 5C72 STRLEN; it was parked there by
1C46 VAR A 3 when the call to 1C6C CLASS 04 was made from
1A90 P FOR. This is the letter as it appears in the program
area, a simple letter code without discriminator bytes
- put the_current line number from 5C45 PPC in 5C42
NEWPPC; the FOR and NEXT statements may both be in the
same BASIC line
- negate the current statement number from 5C47
SUBPPC;
198B EACH STMT called from LOOK PROG below counts
statement numbers down to zero, so it requires a negative
value to start with the one after the FOR
- get the BASIC pointer from 5C5D CH ADD; it is on the
terminator after the FOR statement
- make a check byte F3 NEXT.

320
_1D64_F_LOOP: get an end-of-line pointer from 5C55
NXTLIN; it is on the start of the next line in the program area
- call 1D86 LOOK PROG to find the next F3 NEXT in the
program
- save the updated end-of-line pointer in 5C55 NXTLIN
- if the carry flag is set report "FOR without NEXT"; no
more NEXTs is the program at all
- (5C5D CH ADD is on a NEXT) move it on to the variable
letter
- OR the letter with 00100000b/20h to make it lower-case
- if the result matches the current letter jump on to F
FOUND
- (wrong NEXT) move the BASIC pointer on again and
jump back to F LOOP.
_1D7C_F_FOUND: move the BASIC pointer on to the
statement terminator after the NEXT statement
- reverse the negation of the statement number made to
suit EACH STMT
- put the statement number in 5C44 NSPPC; 5C42
NEWPPC
has already been filled by LOOK PROG.
Exit: RET, from F L&S if a pass is required, from F FOUND
if not. In either case RET is to 1B76 STMT RET.
Output parameters: none
- the parameters are in the looping variable
- if no pass is required, the line and statement numbers
following the NEXT statement are in 5C42 NEWPPC and 5C44
NSPPC
- the calculator memory is still located in the
variable; this will be corrected on return to the statement loop
by the call to 16BF SET WORK in 1B29 STMT L 1.

FORMAT key (D0) see also commands, functions and


operators,
KEYBOARD SCANNING, extended mode table 0284
The zero key in E mode with symbol shift produces the

321
command FORMAT. The "FORMAT statement" isn't executed
by the plain Spectrum, without attachments of some kind;
with peripherals such as Interface 1 it requires to be followed
by at least a channel letter.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1B06 P FORMAT causes a jump via 1C8C CLASS 0A - which
collects the letter - 1C10 CLASS OO and 1C16 JUMP C R to the
executive
routine 1793 CAT ETC - which however merely produces the
error report "Invalid stream".

FORM EXP 33DE (33C6 STK DATA)


Jumps from:
33C8 STK CONST

forms of numbers (integer and FP) see CALCULATE

FOR ... NEXT control variables see FOR ... NEXT loops,
variables

FOR ... NEXT loops


The terms used, eg in "FOR f = x TO y STEP z", are:
_Loop_control_variable or_looping_variable is f
_VALUE is x
_LIMIT is y
_STEP is z; if not specified, STEP is one.
The_looping_line_and_statement specify the next
statement after the FOR statement, read by the NEXT
statement to tell where to jump back. If the FOR statement is
the last on its line, this will address a non-existent statement,
but the NEXT routine can take care of this.
A_pass is one turn of the loop, up to "NEXT f".
The VALUE, LIMIT and STEP, and looping line and
statement, are all stored in eighteen bytes following the letter
of the looping variable in the variables area; VALUE, LIMIT

322
and STEP are in 5-byte FP format converted from the BASIC
input, which can be any kind of numeric expression -
decimal, BIN or E-format numbers, variables, functional
expressions - and can be negative or fractional numbers; they
aren't rounded to integers.
If the VALUE variable is given a new value within the
loop the looping takes account of the change, eg
FOR f = 1 TO 1: LET f = f - 1: NEXT f
will loop for ever. Changing the values of LIMIT and STEP in
similar ways doesn't have similar effects: the values of the
variables or expressions are changed for other purposes, but
the looping continues to use the old values.
1DAB NEXT adds STEP to VALUE and checks with LIMIT
1DDA NEXT LOOP checks LIMIT, with due regard to sign
of STEP
1DE2 NEXT 1 return with NC if loop possible
1DE9 NEXT 2 return with C if loop not possible
2900 V EACH checks vble letter flags for loop vble

fourteen pointers see 1664 POINTERS

FP throughout this index = "floating point"; see CALCULATE

FP A END 2DE1 (2DD5 FP TO A)


Exit from:
2DD5 FP TO A

FP CALC subroutine 0028


The_use of the floating point calculator is fully
discussed under CALCULATE. There you will find explained:
- the structure of FP numbers
- full format/small integers/stk-data format
- calculator literals
- the calculator stack, last value, first and second
operands, etc
- the calculator memory.
Also all significant Rems relating to FP CALC are listed

323
there. This entry confines itself to the actual operation of the
subroutine.
A call to RST 28 must be followed by at least one
_literal, a single byte which signals the calculation to be
performed. If there is more than one, the calculations are
performed in sequence, each one leaving its result on the
calculator stack, and operating on the values it finds there
from the preceding calculation. The last literal must be 38h/
56d, signalling "end-calc", to exit from the subroutine.
Subroutines which can be called by literals following FP
CALC are shown in this index with their names in lower case;
the first reference in each case is to the appropriate literal for
FP CALC. For a complete list, see the table in the notes, or the
ROM listing, at 32D7.
The notes make various distinctions between types of
literals and operations, which can be expanded a little:
_simple_literals call for a specific operation, eg "add"
_unary_operations are those which have only one operand,
eg "negate"
_binary_operations have two operands, eg "add",
"exchange"; sometimes the result is a single number,
sometimes not
_manipulatory_operations move numbers without
changing them, eg "get-mem", "exchange"
_comparison_operations are the twelve literals 09 no-l-eql
to 0E nos-eql and 11 str-l-eql to 16 strs-eql, see index entry
353B no-l-eql; a useful classification, but only obliquely used
in the notes. These are all simple binary operations
_multi-purpose_literals are those of which the hi digit
specifies a group of operations and the lo digit specifies
further, eg E0 to E5 specify "get-mem-0" to "get-mem-5"
_programming_literals don't affect the values on the stack
or in memory, but affect the calculation, eg "jump-true", "dec-
jr-nz"; this type isn't mentioned in the classification in the
notes, but it obviously needs a name.
The binary operations are literals 00 to 17h; if the

324
operation literal is less than 18h, HL and DE are moved back
five places so that HL points to the_second last number on the
calculator stack and DE to the last, instead of HL pointing to
the last and DE to the end of the stack from 5C65 STKEND
which is where they were placed when the calculator was
entered. In two cases this moving of the pointers is the only
point of the unary/binary distinction; the operators jump-true
and delete are included as binary merely to get the pointers
moved.
The multi-purpose literals all have their hi bit set:
81h series-01 -> 9Fh series-1F: only series-06, series-08
and series-0C are used in the ROM, but the others are
available
A0h stk-zero -> A4h stk-ten
C0h st-mem-0 -> DFh st-mem-1F: only up to st-mem-5 are
used in the ROM, but if memory is relocated the others are
available
E0h get-mem-0 -> FFh get-mem-1F: as for st-mem.
All these are handled in SCAN ENT. The literal is ANDed
with 60h, shifted right four times, and added to 7C, leaving
7C (twice 3E) for series
7E (twice 3F) for stk-const
80 (twice 40) for st-mem
82 (twice 41) for get-mem
ie twice the value of the literal in the table at 3353 and
following; this value is used to compute a jump to the
required subroutine from ENT TABLE. Before the jump is
made, the value of the last 5 bits is put in the A register, and
later used to operate the required variant of the multi-purpose
subroutine.
If only one operation is to be performed, the literal 3B
calling 33A2 fp-calc-2 may be used, with the B register holding
the actual operation literal. This allows a series of operations
to be assembled on the machine stack and called in sequence,
as from 274C S STK LIST.
Input parameters: for many operations one or two values
are needed on the calculator stack

325
- the B register can hold either an operation literal or
a loop counter; this will be kept, switching between the B
register and 5C67 BREG, till
either a literal 3B fp-calc-2 is encountered, when it
will be executed as an operation literal
or a literal 35 dec-jr-nz is encountered, when it will
be used as the loop counter.
Action: jump straight on to CALCULATE.
_335B_CALCULATE: call 35BF STK PNTRS which puts HL
pointing to the first byte of the last value on the calculator
stack and DE pointing to the stack end.
_335E_GEN_ENT_1 [the entry point for the first call from
3449 series-06. The notes don't succeed in explaining why the
more economical call to RST 28h wasn't used in this case. The
only possible reason is to avoid resetting HL and DE by STK
PNTRS; in fact every ROM call of series-06 immediately
follows a call of 300F subtract, which leaves HL and DE with
the identical settings. The call to STK PNTRS is unnecessary,
but so far as ROM is concerned there is no reason to avoid it.
Of course, one might construct a m/c program in which this is
desirable]: put the value of B in 5C67 BREG.
_3362_GEN_ENT_2 (the entry point for the looping calls
from 3449 series-06; this time for the clear reason that the 35
dec-jr-nz literal is being used, which uses 5C67 BREG for a
counter): put the "return address" of the subroutine in HL';
the byte at this address is the next literal to be executed.
_3365_RE_ENTRY (there is no direct call of or jump to RE
ENTRY in ROM, but its address is put on top of the machine
stack in 338E ENT TABLE, so all the subroutines called by
literals return to here, except of course 38 end-calc): load
5C65 STKEND with DE
- get the next literal from HL'
- move HL' on one to point to the next "return address".
_336C_SCAN_ENT (using the alternate registers; the entry
point from 33A2 fp-calc-2): if the literal is less than 80h jump
on to FIRST 3D; these are all the simple operations 00 jump-
true

326
-> 3D re-stack
- (multi-purpose literals 86 series-06 -> E5 get-mem-5)
save the literal
- AND it with 01100000b/60h; 8xh -> Fxh become 0xh ->
6xh
- shift four times right; now they become 00 -> 06
- add the result to 7C for the jump offset; 7C (twice
3E) for series-0x -> 82 (twice 41) for get-mem-x
- AND the saved literal with 00011111h/1Fh to get the
parameter in the A register; it serves different purposes in the
different multi-purpose subroutines
- jump on to ENT TABLE.
_3380_FIRST_3D (simple literals): if the literal is more
than 17h jump on to DOUBLE A; unary operations
- (binary operations) return from the alternate
registers
- copy HL to DE and add FFFB/minus 05 to HL; this
moves both back five places
- go back into the alternate registers.
_338C_DOUBLE_A (using the alternate registers; all
literals have now been made less than 80h/128d): shift the
literal one bit left; this doubles it and so makes an offset to
compute the jump to the 2-byte subroutine address in the
table.
_338E_ENT_TABLE (using the alternate registers): make a
base address 32D7 for the table
- add the offset and read the subroutine address
- get the address of the next literal into H'L and put
the address 3365 RE ENTRY on the stack; all the calculator
subroutines will return to RE ENTRY
- put the computed subroutine address on the stack over
RE ENTRY
- return from the alternate registers
- get the value of 5C67 BREG in the B register in case
it is needed for the comparison operations; BREG is two
above

327
5C65 STKEND, so LD BC,(STKENDhi), which would be better
written
LD BC,(BREG + 1), gets BREG in B.
Exit: RET at 33A1; since the subroutine address has just
been put on the stack, this RET is an indirect jump to the
subroutine. The RET is labelled "delete", because this single
RET is in fact also the delete subroutine; see index entry. In
the case of delete the RET operates twice in succession.
Output parameters: HL and DE point either to the last two
values on the stack or to the last and the stack end address in
5C65 STKEND
- B holds the value of 5C67 BREG; either a loop counter
or the literal of the operation to be performed by fp-calc-2
- HL' holds the "return address" from FP CALC; it
usually holds the next literal
- the calculator stack and the calculator memory aren't
disturbed, so repeated use of RST 28 FP CALC may be made
with
the same stack and memory.
Called from:
03F8 BEEP
0427 BE OCTAVE (3 times)
1736 OPEN
1CE6 USE ZERO
1CF0 IF
1D10 F USE 1
1D16 F REORDER
1D34 F L&S
1DAB NEXT
1DDA NEXT LOOP
2320 CIRCLE (twice)
233B C R GRE 1 (4 times)
235A C ARC GE1
238D DR 3 PRMS
23A3 DR SIN NZ
23C1 DR PRMS (4 times)
2425 ARC LOOP

328
2439 ARC START (3 times)
245F ARC END (3 times)
247D CD PRMS1
2497 DRAW SAVE
25F8 S RND
2627 S PI
274C S STK LIST
2B59 L NUMERIC
2CCF DEC RPT C
2CD5 DEC STO 1
2CDA NXT DGT 1
2D2B STACK BC
2D3B INT TO FP
2D40 NXT DGT 2
2D55 E SAVE
2D60 E LOOP
2D71 E TST END
2D7B E END
2DA2 FP TO BC (twice)
2DAD PP DELETE
2DC1 LOG(2**A)
2DE3 PRINT FP
2DF2 PF NEGTVE
2E01 PF LOOP
2E24 PF SMALL (twice)
2ECB PF MORE
2F2D PF COUNT
3588 STR TEST
36A0 n-mod-m
36AF int
36C4 EXP
370E RSLT ZERO
3713 ln
371C VALID (twice)
3783 get-argt
37AA cos
37B5 sin

329
37DA tan
37E2 atn
37F8 SMALL
3833 asn
3843 acs
384A sqr
3851 to-power

fp-calc-2 subroutine 33A2 see also CALCULATE


Called from 0028 FP CALC with literal 3B and a
calculator literal in 5C67 BREG.
Allows the calculator to be used for an operation found
from the program, ie not preset by the programmer. In the
ROM, used only in the 24FB SCANNING loop to work out
expressions from a pile of operations on the machine stack
marked with priorities to enable them to be evaluated in the
right order.
No point in calling it direct from m/c, but useful in
the sequence RST 0028/3Bh/38h.
Input parameters: the literal of the operation to be
performed must be in B on calling 0028 FP CALC: it is placed
in 5C57 BREG by 335E GEN ENT 1.
Action: drop the return address 3365 RE ENTRY; it will be
stacked again before RET is reached
- put the literal from 5C57 BREG in A.
Exit: to 336C SCAN ENT, which jumps to the appropriate
routine.
Output parameters: operation literal in A
- HL' still holds the address of the next literal.
Called from:
274C S STK LIST
Rems:
335B CALCULATE brief explanation
336C SCAN ENT finds required subroutine

FP DELETE 2DAD (2DA2 FP TO BC.)


Exit from:

330
2DA2 FP TO BC

FP forms in BASIC line see number marker

FP numbers see CALCULATE

FP TO A subroutine 2DD5
Puts a positive integer less than 100h/256d from the
calculator stack into the A register, without error reports but
with flags to indicate overflow etc.
Cf: 1E94 FIND INT1 - does the same but reports errors if
the number is negative or too big
2314 STK TO A - takes a one-byte number, positive or
negative, of absolute value up to 255d, from stack into A with
a sign marker; reports an error only if the absolute magnitude
is out of range.
Input parameters: none
- a number on the calculator stack.
Action: call 2DA2 FP TO BC, which rounds the number to
an integer, puts it in BC, setting carry if it is more than FFFFh/
65535d, and also puts its lo byte in A.
- if carry is set return; "too big"
- save the flags
- if B is zero jump on to FP A END
- (hi byte not zero, the number is still too big for A)
set the carry flag; leave the zero flag as made by FP TO BC
- return.
_2DE1_FP_A_END: recover the flags from FP TO BC.
Exit: RET.
Output parameters: BC holds bytes 3 and 4 of the FP
number after rounding down to an integer; if less than 65536d
it was put in the "small integer" form and BC is its value
- A also holds the lo byte
- the FP number has been taken off the stack.
- NC and Z flag a positive number less than 100h/256d
- NZ flags a negative number
- carry flags a number over FFh/255d.

331
Called from:
1E85 TWO PARAMS
1E94 FIND INT1
2314 STK TO A
247D CD PRMS1
2CFF ST E PART
2E24 PF SMALL
35C9 chrs
3624 EXP
Exit from:
2DC1 LOG (2**A)

FP TO BC subroutine 2DA2
Puts a positive integer less than 10000h/65536d from the
calculator stack into the BC register, without error reports but
with flags indicating overflow etc.
Cf: 1E99 FIND INT2 - does the same but with error
messages
2307 STK TO BC - takes_two integers off the stack and
puts one each in B and C
2D7F INT FETCH - copies a small integer into DE,
without error reports or flags.
Input parameters: none
- a number on the calculator stack.
Action: call 0028 FP CALC without any operation literal;
this points HL at the last value
- if the byte at HL is zero jump on to FP DELETE; the
last value is already in small integer form
- use the calculator to get INT (N + 0.5); this rounds
the number to the nearest integer_up_or_down and leaves it in
small integer form if it is within the range.
_2DAD_FP_DELETE: use the calculator to delete the last
value; its number form is still there, pointed to by DE
- read its exponent byte
- call 2D7F INT FETCH, which puts bytes 3 and 4 of the
FP form in DE and its sign byte in C; if it is a small integer
DE is its value

332
- if the exponent isn't zero set the carry flag; not a
small integer
- read the hi bit of the sign byte; Z for positive, NZ
for negative
- move the values to the registers required for the
output parameters.
Exit: RET.
Output parameters: BC holds the small integer taken from
the stack, if it was one, rounded up or down to an integer
value, positive or negative
- HL points to what is now the last number on the stack,
and DE to the stack end, which for the moment holds the
unchanged first byte of the number just taken off the stack
- NC and Z flag a positive number less than 10000h/
65536d
- NZ flags a negative number
- carry flags an integer > FFFFh/65535d.
Called from:
19FB E LINE NO
1E99 FIND INT2
25F8 S RND
2DD5 FP TO A

FP 0/1 subroutine 350B


Writes an FP number in small integer format, one or zero
depending on the carry flag, in the address at HL and the four
following; usually on the calculator stack.
In small integer format, zero is 00 00 00 00 00 and one
is 00 00 01 00 00.
Input parameters: HL holds the address
- carry is set for one, zero for zero.
Action: zero A; not using XOR A, which would clear the
carry
- load it into the first two bytes at HL
- rotate it left, picking up the carry flag in its lo byte
- load this into the third byte
- rotate it right to zero it again

333
- load it into the last two bytes.
Exit: RET
Output parameters: changes nothing except A (zeroed)
and
the FP number.
Called from:
2D55 E SAVE (written to mem-0)
3524 no-&-no
Exit from:
3501 NOT
351B or
3524 no-&-no

fractional part of number see integer part

fractional point see "." (code 2E) after end of alphabet

FRAMES system variable 5C78


Bytes: 3
Holds a 3-byte integer which is incremented every twenty
milliseconds, ie fifty times per second, throughout the
computer operation, except during BEEP and LOAD/SAVE
operations. The first byte is the lo byte, the third is hi.
FRAMES provides an accurate clock, which can be used
in producing clock programs, but it isn't so used in the ROM;
where its only application is in providing a pseudo-random
SEED for the random number function - see 25FB S RND and
5C76 SEED.
FRAMES is zeroed on start-up, like every byte of the
RAM, in 11E2 RAM READ. The incrementing is done in 0038
MASK INT, and the three bytes go through all values from zero
to 16,777,215d about every 93.2 hours. The second byte is
incremented about every 5 seconds, the third byte about
every 20 minutes.
The third and second bytes can be used as short-term
stores in m/c programming; it is best to zero the lo byte. If
the interrupt is disabled all three FRAMES bytes are available;

334
they will be incremented by 0038 MASK INT, so in this use it
is better to call 0048 KEY INT.
Written by:
0038 MASK INT hi byte called FRAMES-3
Read by:
0038 MASK INT two lo only
1E4F RANDOMIZE two lo only
Rems:
1219 RAM SET sets clock going

'free' key sets see 02BF KEYBOARD

FREE MEM subroutine 1F1A


Not used by BASIC or ROM. Returns in BC the amount of
memory utilized so far. Can be called from m/c, or from BASIC
by
PRINT 65536 - USR 7962
which prints out the number of bytes of free memory. The
action is essentially in 1F05 TEST ROOM.

F REORDER 1D16 (1D03 FOR)


Jumps from:
1D03 FOR

frequency of beep see timing

FRST LESS 3585 (353B no-l-eql)


Jumps from:
3575 SEC PLUS (twice)

F USE 1 1D10 (1D03 FOR)


Jumps from:
1D03 FOR

FULL ADDN 303E


The exit from 3014 addition if either of the numbers to
be added, or the result of adding them, is outside the small

335
integer range. "Addition" here includes subtraction, which is
simply addition in which one of the numbers is negative.
Addition of numbers X and Y in full FP format isn't
simple:
1. The first mantissa byte of each number must be
corrected to a "true mantissa"; positive numbers have bit 7
zeroed.
2. The mantissa of the smaller number must be shifted to
the right, with corresponding increment of the exponent, till
the two exponents are the same.
3. Now the mantissas can be added like ordinary numbers.
4. If the resulting mantissa is more than one, it must be
shifted right and the exponent incremented.
5. The mantissa must be corrected back to a full FP format
mantissa, with bit 7 of the first byte showing the sign.
Further complications arise if either number is
negative, and if the result is negative: this is dealt with by
temporarily replacing the exponent byte of each number with
a leading fifth mantissa byte, 00 for a positive number or FF
for a negative one, and negating the four mantissa bytes if it is
negative.
A parallel to steps 2 to 4 can be seen in the case of
adding E-format numbers in their standard BASIC form, in
which the mantissa is always between one and ten: here the
exponent is a power of ten, not a power of two, and right
shifting is decimal shifting not binary shifting, but much else is
the same:
Eg to add X = 9.97142 E +7 and Y = 9.23076 E +6
Exp Mant
X: 7 9.97142
+ Y: 6 9.23076
Step 2. Shift Y right:
X: 7 9.97142
+ Y: 7 0.923076 (one shift right)
Step 3. Add mantissas:
----------------
X + Y: 7 10.894496 (direct sum of mantissas)

336
Step 4. Normalize result mantissa:
X + Y: 8 1.0894496 (one shift right)
and the answer is 1.0894496E+8.
Step 4 is tricky. Remember that
(a) the original four-byte mantissas were always between
half and one, so their high bit was set;
(b) but if they were negative, they have been negated, so
their high bit is now zero;
(c) they have been expanded to_five-byte mantissas by
adding a sign byte at the start of the mantissa, 00 for positive
numbers and FF for negative
(d) the mantissa of Y may have been shifted right, which
puts one in the hi bit of (the four-byte mantissa of ) negative
numbers and zero in the hi bit of positive ones.
If both numbers are positive, and there is carry from
the addition of the mantissas, the mantissa needs to be shifted
one place right.
But also if they were both negative and there is_no
carry: for this means that two numbers whose absolute values
are each more than a half have been added.
The situation would be the same with one-byte addition:
if 03 and 17h represent the negative numbers -FD and -E9,
their
sum 1A (-E6) represents the sum of -FD and -E9 (-1E6) only if a
carry is added.
And adding F9 and ED, representing addition of -07 and
-13h, gives 1E6; which represents the answer -1Ah only if you
_ignore the carry.
If one of X and Y is positive and the other negative,
since both their original mantissas were between half and one
there is no way that the sum could produce a mantissa more
than one which requires shifting_right; left perhaps, but that
will be sorted out in "normalization".
The ROM copes with these complexities quite
ingeniously:
Add together the sign bytes of X and Y, 00 for positive,
FF for negative, with the carry from the addition of the

337
mantissas. The possibilities are
both positive, no shift: 00 + 00 + no carry = 00
both positive, must shift: 00 + 00 + carry = 01
one negative, no shift: 00 + FF + carry = 00 and carry
both negative, must shift: FF + FF + no carry = FE and carry
both negative, no shift: FF + FF + carry = FF and carry.
The resulting "indicator" will be 01 with no carry or FE
with carry if a shift is required, 00 or FF with or without
carry if not.
Shift the indicator right with carry into bit 7; now
- "must shift" is 00 or FF
- "needn't shift" is 00, 7F, 80 or FF.
XOR this result with the original indicator, so that
- "must shift" becomes 01 in both cases; odd
- "needn't shift" becomes 00 or 80 ; even
The hi bit of the indicator also shows the sign of the
result: zero if both X and Y were positive, one if they were
both negative. And if only one of X and Y was negative, the hi
bit of the indicator is set, ie negative, if there was_no carry
and zero if there was carry; and this also correctly indicates
the sign of the result. The carry is the result of a five-byte
addition of numbers which are positive if the hi bit of their hi
byte is zero, negative if not; the rules are just the same as
for one-byte additions. Adding F0h/minus 10h to 16h gives
06h with carry; the result is positive. Adding F0h/minus 10h
to 06h gives F6h/minus 0Ah without carry; the result is
negative.
Input parameters: HL' holds the "return address" of the
calculator subroutine, containing the next literal address
- the numbers X and Y are on the calculator stack; one
or both may be in small integer format
- HL points to the start of the first and DE to the
start of the second, the last on the stack; in the notes, X is
called the "augend" and Y the "addend". See under addend for
these tedious terms.
Action: call 3293 RE ST TWO to put both numbers in full
FP format

338
- stack the "return address" and both pointers; SHIFT
LEN below is going to take all the registers the law allows
- call 2F9B PREP ADD for X; it gets the exponent, sets
the sign byte for a true mantissa, puts zero in byte 1 for a
positive, FF for a negative number, and subtracts the mantissa
from zero if it is a negative number
- save the exponent
- call PREP ADD again for Y
- save its exponent; this completes step 1, and gets
ready to deal with negative numbers as well
- if X has a smaller exponent than Y, or they are the
same, jump on to SHIFT LEN
- (X > Y) exchange the exponents and pointers; now HL is
pointing to the number with the smaller exponent, and from
now
on the addend Y will mean the smaller of what were X and Y,
the augend X will mean the larger.
_3055_SHIFT_LEN (step 2): subtract the exponents; the
difference is R, the number of right shifts to make in Y
- call 2FBA FETCH TWO, which puts the mantissa of the
larger X in H'B'C'CB and the mantissa of the smaller Y in
L'D'E'DE; H' and L' are the sign markers, FF for negative and
zero for positive, not as usual the exponent bytes. In effect
these are five-byte mantissas
- call 2FDD SHIFT FP to execute step 2, shifting
L'D'E'DE to the right by R binary digits; rounding up as
necessary for the bits which fall off to the right and feeding
in bits on the left to match the ones there already - so that in
the leading byte FF stays as FF and 00 as 00
- add the two pairs of lo bytes, and the two pairs of hi
bytes with any carry from the addition of the lo bytes; step 3
- perform step 4; add the exponents with the carry from
the mantissa addition
- rotate the indicator right with carry into the hi bit
- XOR the result with the indicator
- if the result is even jump on to TEST NEG; no shift
- (must shift) call 2FDD SHIFT FP with a count of one to

339
make a single right shift
- increment the exponent to compensate for the shift
- if the exponent goes to zero jump on to ADD REP 6;
arithmetic overflow.
_307C_TEST_NEG: AND the indicator with 10000000b/
80h
- put the result in the sign byte position of the result
on the calculator stack; it shows the sign of X + Y, 80h for
negative and zero for positive
- if it was zero jump on to GO NC MLT; positive result
- (negative result) negate the 4-byte result mantissa
byte by byte; from the right, with carry from each to the next
- if it doesn't produce final carry jump on to END COMPL
- [still a negative result; final carry means the result
mantissa was FF FF FF FF, now 00 00 00 00. The result is an
exact power of 2. Negative numbers -2**(X - 1) which are
powers of 2 are expressed in FP format as 8X 80 00 00 00]
rotate the carry into the hi bit of the second result byte,
making it 80
- increment the exponent.
_309F_ADD_REP_6: if the exponent adds up to zero, report
"Number too big".
_30A3_END_COMPL: put the first byte in place; now the
result mantissa is in D'E'DE, but it may require normalizing.
_30A5_GO_NC_MLT: zero the A register and exit.
Exit: into 3155 TEST NORM; it normalizes the result
mantissa, ie puts it into FP format (step 5), and stacks it.
Output parameters (as required for TEST NORM):
- the four true mantissa bytes are in DED'E'
- A is zero
- HL points to the exponent byte of the result, which
holds the normalized exponent byte
- the second position of the result holds 00 or 80h for
a positive or negative result
- the carry flag shows NC for addition.
Exit from:
3014 addition

340
303C ADDN OFLW (3014 addition)

full FP format numbers see CALCULATE

full multiplication see 30CA multiply

functions see commands, functions and operators, 1F60


DEF FN

FUNCTION SKIPOVER SUBROUTINE see 28AB FN SKPOVER

341
G

GEN ENT 1 335E (0028 FP CALC)


Entry point to FP CALC used by 3449 series-06, for no
clear reason (see under FP CALC).

GEN ENT 2 3362 (0028 FP CALC)


Entry point to FP CALC used by 3449 series-06 for
looping calls, to avoid resetting 5C67 BREG.
Called from:
3453 G LOOP

get-argt subroutine 3783


Called from 0028 FP CALC with literal 39; not otherwise
called from ROM.
Finds a_reduced_argument corresponding to X for the
calculation of COS X or SIN X. The argument X of a COS or SIN
function can have any value at all, which represents an angle.
Those who have difficulty visualizing the meaning of SIN
and COS may find it helpful to think in terms of a clock face,
without hands. The angles are represented by the numbers on
the clock, but the convention in trigonometry is to measure
them anti-clockwise from the "three-o'clock" position.
Negative angles are measured clockwise from the same start
position.
SIN X is a measure of the height of the clock number
above the middle of the clock, COS X is a measure of the
distance of the clock number to the right of the middle. Nine
o'clock to three o'clock have positive SIN, three to nine
o'clock have negative SIN: twelve to six have positive COS, six
to twelve negative COS.
For the moment, consider the angle as measured in

342
degrees. An angle of say 500d degrees represents a complete
circle, 360 degrees, plus 140d degrees: going right round the
circle leaves SIN and COS as they were, ie
SIN (X + 360d degrees) = SIN X,
and the same for COS. Analogously, 1400 hours in the twenty-
four hour clock is the same as 2 p.m. in the twelve-hour clock,
and has the same SIN and COS
So for all calculations of SIN or COS, X may be reduced
any number of times by 360d degrees without affecting the
result: eg SIN 1000d degrees = SIN 280d degrees.
In Spectrum calculations, angles are measured not in
degrees but in radians: radians are a measure of angles, like
degrees or the numbers on the clock face, but 2pi radians =
360 degrees; so a right angle is pi/2 radians, 45 degrees is pi/4
radians, etc. For trigonometry, calculus etc, radians are a
much more convenient unit than degrees, but for practical
purposes they are rather a nuisance.
If X is more than a complete circle 2pi, both its SIN
and its COS are the same as those of X - 2pi; SIN and COS are
called "cyclic" functions for this reason. There are further
equivalences within the circle itself, which make it possible to
reduce the argument further than by merely subtracting
complete circles; try visualizing them in terms of the clock
face:
SIN (-X) = -SIN X COS (-X) = COS X
SIN (pi - X) = SIN X COS (pi - X) = -COS X
SIN (X - pi) = -SIN X COS (X - pi) = -COS X
SIN (pi + X) = -SIN X COS (pi + X) = -COS X

The get-argt subroutine simplifies the calculation of


SIN and COS by finding a reduced argument V, between a
positive and a negative right angle, which has the same SIN as
X, and the same COS except for a possible change of sign.
"Between a positive and a negative right angle" means
between -pi/2 and +pi/2 radians, or between twelve and six
o'clock.
It finds this value through a series of steps:
1. Divide X by 2pi and subtract the_nearest_integer_above
343
_or_below; this is the first reduced argument Y.
Y represents the same angle as X, but with a change of
units: instead of radians, the unit is now the circle, 2pi
radians, so an angle 4 represents four complete circles, 0.25d
represents a right angle, etc.
The usual procedure for finding the nearest integer is
followed, ie add a half and then discard the fractional part; so
Y is a fraction of a circle, less than a half, positive if X is
between nine o'clock and three o'clock, otherwise negative. Y
will always be between minus a half circle and plus a half; ie it
ranges anticlockwise once over the whole circle from nine
o'clock round to nine o'clock again. The sign of SIN Y circles
will be the same as that of SIN X radians. Subtracting an
integer makes no further change in SIN or COS.
2. Multiply Y by 4: the result is the second reduced
argument Z = 4*Y. Multiplying by 4 changes the unit of angle
again: Z is in units of a 1/4 circle, ie a right angle, pi/2
radians. Z ranges from -2 to +2 right angles, still once round
the circle anticlockwise from nine o'clock to nine o'clock.
Now make a sign flag for COS by subtracting one from
ABS Z; because the units are now right angles, one represents
a right angle. If ABS Z is more than a right angle, the angle is
in the left half of the circle, six to twelve o'clock; ABS Z - 1 is
positive, signalling "COS negative". Otherwise ABS Z - 1 is
negative, signalling "COS positive".
3. The third and final reduced argument V is in the same
units as Z, right angles, but its range is reduced to minus one
to plus one right angles, the half circle from twelve to six
o'clock. There is already a flag for the sign of COS, so it is only
necessary to ensure that SIN V right angles has the same sign
as SIN Z right angles, which already has the same sign as SIN
X radians. There are three cases:
3.1. ABS Z is less than one: Z is OK, so put V = Z
3.2. ABS Z is more than one, Z is positive: put V = 2 -
Z. Now V has been put in range, and since 2 means two right
angles, ie pi, as seen above SIN (pi - X) = SIN X; in this case,
if Z was "ten o'clock" V is put at "two o'clock"

344
3.3. ABS Z is more than one, Z is negative: put V = ABS
Z - 2. Again this puts V in range, and since SIN (x - pi) = -SIN
X the sign has been corrected; if Z was "eight o'clock", ABS Z
is "ten o'clock" and V is "four o'clock".
Input parameters: none
- X is the last value on the calculator stack; it must
be the last value even for direct calls.
Action (step 1): use the calculator to divide X by 2pi
- find the nearest integer above or below; INT (X/2pi +
0.5d)
- subtract it from X/2pi; the result is Y
- calculate Z = 4*Y; step 2
- keep Z and take one from ABS Z [Z in the notes is used
for what I call ABS Z - 1]
- keep ABS Z - 1 and get its sign; one for ABS Z > 1 and
zero for ABS Z < 1
- store it in mem-0; this is the COS sign flag
- if it is one jump on to ZPLUS; more than a right angle
- (case 3.1, Z positive and less than a right angle)
return with V = Z = 4*Y on the stack.
_37A1_ZPLUS (more than a right angle): get ABS Z - 2
- check the sign of Z
- if Z is negative jump on to YNEG; negative angle
- (case 3.2, Z positive and more than a right angle)
negate ABS Z - 2 and return; with V = 2 - ABS Z
=2-Z
= 2 - 4*Y on the stack.
_37A8_YNEG (case 3.3, Z negative and more than a right
angle): return with V = ABS Z - 2
= -4*Y - 2 on the stack.
Exit: RET.
Output parameters: V has replaced X on the calculator
stack
- the flag in mem-0 is one if the COS is negative.
Called from:
37AA cos
37B5 sin

345
GET CHAR subroutine 0018
Just like 0020 NEXT CHAR, except that 5C5D CH ADD
isn't incremented at the start; it reads the_present_character,
the next printable character from the BASIC program or
editing areas; see character codes. 20h space is_not a
printable
character.
Input parameters: none
- the BASIC pointer 5C5D CH ADD is on the start address.
Action: get the character from the pointer in A.
_001C_TEST_CHAR: call 007D SKIP OVER, which moves
5C5D CH
ADD forward over the parameters of control codes and sets
the C flag for other unprintable codes
- if the flag shows NC, return; ie if any printable
character or newline is found.
Exit: RET, from 001C TEST CHAR
- or into 0020 NEXT CHAR, which moves 5C5D CH ADD
forward and loops back to TEST CHAR.
Output parameters: A holds the character code
- HL holds the address in 5C5D CH ADD.
Called from:
0652 SA DATA
06E1 SA CODE 1
12CF MAIN 3 (label omitted, misprint)
17FB LIST 1 (twice)
1B29 STMT L 1
1B55 GET PARAM
1B6F SEPARATOR
1BF4 STMT NEXT
1CBE CLASS 09
1DED READ
1E0A READ 1
1E1E READ 2
1FDF PRINT 2
1FFC PR ITEM 1

346
204E PR POSN 1
20C1 IN ITEM 1
21B9 IN ASSIGN (twice)
21E2 CO TEMP 2
2320 CIRCLE
2382 DRAW
24FB SCANNING
2522 S 2 COORD
25B3 S QUOTE
268D S DECIMAL
26B5 S STK DEC
2712 S CONT 2
27D9 SF ARGMTS
2852 SF ARG VL
2885 SF R BR 2
28B2 LOOK VARS
2934 V SYNTAX
293F V FOUND 2
29A1 SV SIMPLE$
29AE SV ARRAYS
29C3 SV COMMA
29E0 SV CH ADD
29EA SV LOOP
2A12 SV RPT C
2A2C SV ELEM$
2A52 SLICING (twice)
2A81 SL SECOND
2C2E D NO LOOP
2CDA NXT DGT 1
35DE val
Exit from:
2734 S LOOP

GET FROM MEMORY AREA SUBROUTINE see 340F get-


mem

GET HL*DE subroutine 2AF4

347
Multiplies together the contents of two registers, with
an error report "Out of memory" for overflow.
One would expect a report "Number too big"; but the
subroutine is only used in the ROM when arrays are being set
up in the variables area, and "Out of memory" explains the
error better in the circumstances.
Cf 30A9 HL=HL*DE, which merely sets a flag for overflow.
Input parameters: values in HL and DE.
Action: if syntax is being checked return at once
- otherwise call 30A9 HL=HL*DE
- if the carry flag is set report "Out of memory".
Exit: RET.
Output parameters: HL holds the result of the
multiplication
- A is corrupted, others unchanged.
Called from:
29FB SV MULT
2A22 SV NUMBER
2A2C SV ELEM$
2C2E D NO LOOP
Rems:
30A9 HL=HL*DE called by

get-mem subroutine 340F see also 342D st-mem


Retrieves a FP number from the calculator memory.
Called from 0028 FP CALC with literals E0 to E5,
depending on the memory location to be found; mem-0 is
found by get-mem-0, literal E0, and so on.
Can be called direct from m/c, though this isn't done in
the ROM. Also:
1. The number from memory is always placed on the
calculator stack in ROM calls, but this isn't necessary in
direct calls.
2. Literals E6 to FF could be used, even through FP CALC,
to address up to 20h/32d memory locations; but the memory
must

348
be relocated before doing this by putting a new address in
5C68 MEM, or the values read will overlap the last system
variables.
If the subroutine is called direct with the location number in
A, up to 33h/51d memory locations could be used; more
would confuse 3406 LOC MEM.
Input parameters: A holds the memory location number
- DE the address to which the number is to be copied; in
ROM, this is always the address in 5C65 STKEND, so that the
number is added to the calculator stack.
Action (the coding is line for line the same as st-mem,
except that it leaves out some EX DE,HLs which reverse the
direction of copying): call 3406 LOC MEM with a memory
pointer from 5C68 MEM to find the first byte of the selected
memory location
- call 33C0 MOVE FP to copy the FP number from the A'th
memory location addressed by the memory pointer to the
designated address.
Exit: RET.
Output parameters: the number in the memory location is
still there, but is now copied to the chosen destination
- HL holds the address of the first byte of this
destination
- DE that of the next address after its last byte;
return to the calculator will put this in 5C65 STKEND.
Called from:
0427 BE OCTAVE (twice)
1D16 F REORDER
1DAB NEXT (twice)
1DDA NEXT LOOP (3 times)
233B C R GRE 1
235A C ARC GE1 (3 times)
23A3 DR SIN NZ (4 times)
23C1 DR PRMS (16 times)
2425 ARC LOOP (7 times)
2439 ARC START (4 times)
247D CD PRMS1

349
2497 DRAW SAVE
2CDA NXT DGT 1
2D60 E LOOP
2D6E E FETCH
2E01 PF LOOP
2E24 PF SMALL (twice)
2ECB PF MORE
3453 G LOOP (twice)
36A0 n-mod-m (3 times)
36B7 X NEG
36C4 EXP
37AA cos
Rems:
342D st-mem much the same with pointers exchanged

GET PARAM 1B55 (18BA LINE RUN)


Stacks return addresses to 1B52 SCAN LOOP and a
command class routine; although apparently an exit routine it
is merely a mechanism for taking a turn through the
statement loop.
Exit from:
1B29 STMT L 1
1B52 SCAN LOOP

G LOOP 3453 (3449 series-06)


Jumps from:
auto

G mode (graphics mode) see 5C41 MODE

GO NC MLT 30A5 (303E FULL ADDN)


Jumps from:
307C TEST NEG

GO SUB key (ED) see also commands, functions and


operators,
KEYBOARD SCANNING

350
The H key in K mode produces the command GO SUB,
which must be followed by a numeric expression. It produces
a jump in BASIC execution to the line number given by the
expression and stores the next statement number so
execution can jump back to it when RETURN is reached.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A86 P GO SUB makes a jump via 1C82 CLASS 06 to get the line
number and 1C16 JUMP C R to the executive routine 1EED GO
SUB.

GO SUB subroutine 1EED see also GO SUB stack


Called only from the statement loop by 1A86 P GO SUB in
the syntax parameter table; executes the GO SUB command.
1. Prepares for RETURN, by putting three bytes on the GO
SUB stack: the current line number and the statement number
incremented by one. If there is no statement corresponding to
this number return will be to the following line. These
numbers are subsequently read by 1F23 RETURN.
2. Prepares to jump to the BASIC subroutine line, by
putting the line number from the GO SUB command in 5C42
NEWPPC and a zero statement number in 5C44 NSPPC; these
will be read on return to 1B76 STMT RET for the next turn of
the statement loop.
3. Makes a precautionary check of the memory available by
exiting to 1F05 TEST ROOM with a counter specifying an
allowance of 14h/20d bytes. Together with the standard 50h/
80d allowance made by TEST ROOM this makes 64h/100d
bytes, which seems rather generous considering only three
have been added to the stack.
Input parameters: none
- the GO SUB line number expression has been evaluated
and is on the calculator stack
- the machine stack has been cleared down to the error
address and the return address 1B76 STMT RET.
Action: unstack the STMT RET address

351
- get the current statement number from 5C47 SUBPPC
and increment it; RETURN will be to the statement after the
GO SUB
- take the error address off the stack and put this
statement number on the machine stack in the hi byte; ie in
the upper of the two positions just occupied by the error
address
- move the stack pointer up one place; the lo byte of
the number just stacked is immaterial and can be overwritten
- get the current line number from 5C45 PPC and stack it
- restack the error address
- put this stack pointer in 5C3D ERR SP; the error stack
pointer
- restack the STMT RET address
- call 1E67 GO TO (misprinted GO TO 1), which gets the
GO SUB line number from the calculator stack and loads it
and a zero statement number in 5C42 NEWPPC and 5C44
NSPPC
- make a counter of 14h/20d.
Exit: into 1F05 TEST ROOM, which reports an error unless
there are 64h/100d bytes of memory available.
Output parameters: BC holds the byte counter.

GO SUB stack
As all machine code programmers know, the machine
stack is an area of memory extending_upwards in address
numbers from the stack pointer address held in the SP
register; its bytes are usually taken in pairs, eg
- POP commands read the byte at SP as lo byte and the
next above it as hi byte into the chosen register, and move SP
up two places
- RET jumps to the lo-hi address in SP and SP + 1 and
moves SP up two places
- and so on.
The machine stack is_upside_down: the "number at the
top of the stack" is the one with the_lowest address, and the
size of the stack is_reduced by INC SP, increased by DEC SP.

352
For the Spectrum ROM there are effectively two machine
stacks, both upside down, one on top of the other:
- the normal machine stack for m/c CALL/RETs and PUSH/
POPs
- and the GO SUB stack for BASIC GOSUB/RETURNs
underneath it separated by the "error address", see under
5C3D ERR SP. The calculator stack, see CALCULATE, is at a
different location, and is the "right way up".
At start-up 1219 RAM SET sets the two stacks up like
this, assuming a 48K Spectrum and no Microdrive maps:
(top of memory)
P RAMT FFFF (168d bytes of UDG forms)
...
UDG FF59
RAMTOP FF58 3E
ERR SP FF57 00 <-- stack pointer on FF57
FF56 00 00
FF54 00 00
: 00 00
: 00 00
... (many bytes)
STKEND/STKBOT 5CE2 (empty calculator stack)

There is no calculator stack, and the bottom of the


machine stack is placed at the address in 5CB2 RAMTOP,
where there is an address 3E00; the reason for this address
will be explained shortly - and see 1F23 RETURN.
As the machine stack builds up, its top marked by the
stack pointer moves downwards towards the address in 5C65
STKEND; initially this is an enormous distance of empty
addresses, around 9A00h/39424d bytes, but it gets filled up
from the bottom by the BASIC program, variables, the
calculator stack, etc, and from the top by the machine stack
and GO SUB stack.
After a few nested GO SUBs have been called from BASIC,
and a few nested CALLs to ROM subroutines are in operation,
the double stack might look like this:

353
(top of memory)
RAMTOP FF58 3E 00
FF56 SS HH LL (stmt/line number for RETURN 1)
FF53 SS HH LL (stmt/line number for RETURN 2)
FF50 SS HH LL (stmt/line number for RETURN 3)
FF4D 13
ERR SP 03 (MAIN 4)
FF4B 1B 76 (STMT RET)
FF49 HH LL (PUSH 1)
FF47 HH LL (PUSH 2)
FF45 HH LL (RET 2)
FF43 HH LL (PUSH 3)
FF41 HH LL (RET 3)
FF3F HH LL (RET 4) <-- stack pointer on FF40
: 00 00
(somewhere above 5CE2, address held in 5C65
STKEND)
STKEND ???? XX XX XX XX XX (FP numbers on the
calculator
???? XX XX XX XX XX stack)
:
STKBOT ????
The addresses look the "wrong way round", because the
RAM is being displayed upside down. Everything read from it
should be read from the lowest address first.
The lower part from the address in 5C3D ERR SP to the
address in 5CB2 RAMTOP is the GO SUB stack, the upper part
from the stack pointer to the address in 5C3D ERR SP is the
machine stack.
The address in 5C3D ERR SP is the current error address,
the address to which execution jumps if an error is detected.
When it is needed, the stack pointer is given the value in 5C3D
ERR SP, usually by 0053 ERROR 2, and the next RET after
16C5 SET STK then jumps to the error address. The error
address is normally 1303 MAIN 4, where the error reports are
printed. If 5C3D ERR SP holds this stack address, resetting the
stack

354
pointer to the address in it clears the machine stack to the
bottom.
No system variable points to the start of the GO SUB
stack. Both the GO SUB and the RETURN subroutines are
arranged to operate when the machine stack has been
cleared down to the
1B76 STMT RET and the error addresses, so both these
routines
simply pick off the two addresses and read the GO SUB stack
below.
The GO SUB stack is loaded and read_three bytes at a
time: the first byte put on is the statement number within the
line to which return will be made from GO SUB, the next two
are the line number.
At the bottom of the GO SUB stack there is the_end
_marker_of_the_GO_SUB_stack, the number 3E00 put there by
1219
RAM SET; this is at the address in 5CB2 RAMTOP. It isn't a
possible RETURN address from a BASIC subroutine (it would
be line number 15872d) and if it is found by the RETURN
routine the error report "RETURN without GO SUB" gets
printed.
The total size of the two machine stacks is
indeterminate, it depends on how many PUSHes and nested
CALLs and GO SUBs are presently operating. It has to fit in
between the address in 5C65 STKEND and the address in 5CB2
RAMTOP;
pushing too many bytes on to either stack could actually
corrupt the calculator stack below the address in 5C65
STKEND, and it isn't possible to guard against this when
working in machine code. So on the GO SUB command, at
1EED GO SUB and in 1F05 TEST
ROOM, an allowance of 64h/100d bytes is made for possible
machine code stacking - perhaps a bit generous.
1EAC CLEAR stack rebuilt, clearing GO SUB stack
1EDC CLEAR 2 new 3E marker, GO SUB stack cleared
1EED GO SUB_three bytes put on GO SUB stack

355
1F23 RETURN_three bytes taken off GO SUB stack

GO TO key (EC) see also commands, functions and


operators,
KEYBOARD SCANNING
The G key in K mode produces the command GO TO,
which must be followed by a numeric expression. It produces
a jump in BASIC execution to the line number given by the
expression.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A7D P GO TO causes a jump via 1C82 CLASS 06, which gets
the line number, and 1C16 JUMP C R to the executive routine
1E67 GO TO.

GO TO subroutine 1E67
Called from the statement loop by 1A7D P GO TO in the
syntax parameter table; the executive routine of the GO TO
command. Also called from other command routines which
make jumps in BASIC execution.
Jumps to the specified BASIC line, by putting its number
in 5C42 NEWPPC and zero in 5C44 NSPPC, so that execution
will continue from there on the next turn of the statement
loop
[Note the minor error in ROM: the last line but one
before GO TO 2 should probably be CP 27h. The effect isn't
serious: if you command GO TO X, with X more than 9999d
but less than the incorrect F000h/61440d used, the command
isn't executed, but you get the "OK" report when you should
get the "Integer out of range" report.]
Input parameters: none
- the expression for the line number has been evaluated
and put on the calculator stack.
Action: call 1E99 FIND INT2 which takes the number off
the stack into the BC register
- make the statement number zero

356
- check the hi byte of the line number against F0;
should have been 27h
- if it is too big report "Integer out of range".
_1E73_GO_TO_2 (entry here from the 1DAB NEXT, 1E5F
CONTINUE and 1F23 RETURN command routines): put the
line and statement numbers in the system variables.
Exit: RET.
Output parameters: none
- 5C42 NEWPPC and 5C44 NSPPC have their new values.
Called from:
1EA1 RUN
1EED GO SUB (misprinted GO TO 1)
Rems:
1384 MAIN 8 affects the value of NSPPC
1DAB NEXT jumps into GO TO subroutine

GO TO 1 misprint for 1E67 GO TO in 1EED GO SUB

GO TO 2 1E73 (1E67 GO TO)


Exit from:
1DAB NEXT
1E5F CONTINUE
1F23 RETURN

graphic form, graphics codes/characters see graphics keys

GRAPHICS key (0F) see also KEYBOARD SCANNING, 0260


control
code key table (d)
The 9 key with caps shift in K, L or C mode works the
GRAPHICS control in editing or INPUT mode. [There is a rare
misprint in the old Spectrum handbook, page 224, where the
index says it is the zero key.]
Turns on G mode, in which
- key 9 again, with or without shift, will turn it off
- zero will DELETE
- one to eight will produce the graphic forms

357
- A to U will produce user-defined graphics
- V to Z rather surprisingly produce E-mode tokens RND,
INKEY$, PI, FN and POINT; see under 0333 K DECODE.
Later models of the Spectrum mostly have a special
GRAPH key, but 9 with caps shift still works.
0260 control code table (d)
0FA8 editing keys table - ineffective
107C ED GRAPH - ineffective (see under symbol code)

graphics keys (80 -> 8F) see also KEYBOARD SCANNING


The one -> eight keys in G mode produce the sixteen
_graphics_forms or_block_graphics, codes 80 -> 8F: without
shift, the TRUE VIDEO forms, with either shift the INVERSE
VIDEO forms.
The 8 * 8 character bits of the graphic characters take
a rather simple form: the top four bytes of a character are all
the same, and so are the bottom four, and each byte is either
00, 0F, F0 or FF. They aren't therefore stored in memory like
the other characters, which would have used 80h/128d bytes;
whenever one is to be printed it is "drawn" in eight bytes of
the calculator memory area and copied from there to the
screen or printer. See 0B38 PO GR 1 under 09F4 PRINT OUT.
0389 K GRA DGT look up through 034A K LOOK UP
0B24 PO ANY prepare to print graphics form
0B38 PO GR 1 construct form in calculator memory
0B3E PO GR 2 check code required
0B4C PO GR 3 construct form
0F38 ED LOOP accept graphic codes

graphics mode see 5C41 MODE

greater-0 subroutine 34F9 see also logical value


Called from 0028 FP CALC with literal 37; checks the
sign of the last value on the calculator stack, and replaces the
number with one if it is positive, zero if it is zero or
negative.
Also called direct as GREATER 0. In direct calls the

358
number checked may be any FP number, not necessarily on
the stack.
Input parameters: HL points to the first byte of the FP
number X; this will automatically be the last number on the
stack in calls from FP CALC.
Action: call 34E9 TEST ZERO
- if X is zero return immediately; the zero result is
already on the stack
- put FF in the A register.
Exit: immediate RET if X zero
- with X non-zero, into 3507 SIGN TO C, which in turn
exits into 350B FP 0/1. With FF, SIGN TO C flags carry, which
FP
0/1 changes to a one on the calculator stack.
Output parameters: A holds FF, others unchanged.
Called through FP CALC from:
1DE2 NEXT 1
2DE3 PRINT FP
3713 ln
371C VALID
3783 get-argt
385D XISO
Called as GREATER 0 from:
358C END TESTS

GRE.8 373D (3713 ln)


Exit from 3713 ln through
371C VALID (twice)

359
H

header area, header information, header of save/load block


see 0605 SAVE ETC

hertz see timing

HL AGAIN 30BC (30A9 HL=HL*DE)


Jumps from:
30B1 HL LOOP

HL END 30BE (30A9 HL=HL*DE)


Exit from:
30B1 HL LOOP (twice) (30A9 HL=HL*DE)

HL=HL*DE subroutine 30A9


Multiplies together the contents X and Y of two
registers, without error messages but with a flag for overflow.
Can readily be called from m/c.
It works by binary "long multiplication": make a zero
accumulator, then read the bits of X from hi to lo, doubling
the accumulator each time and adding Y to it if the bit is one.
The calculator couldn't be used, because the calculator relies
on this subroutine for small integer multiplication.
Input parameters: HL holds X and DE holds Y; the
numbers to be multiplied.
Action: make a bit counter of 10h/16d
- make an accumulator set to zero.
_30B1_HL_LOOP: double the accumulator; shifting it right
one bit
- if this makes carry jump on to HL END; overflow
- (no carry) rotate the lo byte of X left one bit with
the zero from carry into its lo bit and its hi bit into carry
- rotate the hi byte left with carry to its lo bit and

360
its hi bit to carry
- if the carry shows NC jump on to HL AGAIN; hi bit zero
- (hi bit was one) add Y to the accumulator.
_30BC_HL_AGAIN: loop back to HL LOOP counting down
the bit counter to zero.
_30BE_HL_END: recover the original BC.
Exit: RET.
Output parameters: the result is in HL
- if there has been an overflow the C flag is set;
result more than FFFFh/65535d
- BC and DE unchanged.
Called from:
2AF4 GET HL*DE
30CA multiply

HL LOOP 30B1 (30A9 HL=HL*DE)


Jumps from:
30BC HL AGAIN

housekeeping tasks
An expression used once or twice in the notes to mean
"putting everything back to normal".
0D94 CL CHAN restore chan K and system variables
0DAF CL ALL call CL CHAN

hz (= hertz) see timing

361
I

I CARRY 2AE8 (2ACC INT EXP1)


Jumps from:
2ACD INT EXP2

IF key (FA) see also commands, functions and operators,


KEYBOARD SCANNING, logical value
The U key in K mode produces the command IF. The "IF
statement" must be an expression with a logical value: a
numeric expression or comparison, not a string. There must
be a statement following the IF statement, introduced by
THEN, not by the usual colon; the statement following THEN
is only executed if the logical value of the IF statement is non-
zero, positive or negative. There needn't actually be anything
after the THEN:
"IF xxxx THEN" is good syntax by itself, though vacuous.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A81 P IF causes jumps
- to 1C82 CLASS 06 which evaluates the expression;
- back to 1AB2 to check that the "THEN" is present;
and via 1C11 CLASS 05 and 1C16 JUMP C R to the executive
routine 1CF0 IF.

IF subroutine 1CF0 see also logical value


Called only from the statement loop through 1A81 P IF in
the syntax parameter table: the executive routine for the IF
command.
Input parameters: none
- the value of the expression between IF and THEN has
been calculated and is the last value on the calculator stack;
zero means the expression is false, non-zero means it is true.

362
Action: drop the return address 1B76 STMT RET
- in syntax checking, jump on to IF 1 to check the
syntax of the THEN statement
- use the calculator to delete the last value, which
leaves DE pointing to its first byte
- call 34E9 TEST ZERO
- if there is carry exit to 1BB3 LINE END; false.
_1D00_IF_1 (true): exit to 1B29 STMT L 1.
Exit: if the value was zero 1BB3 LINE END transfers
execution to the next line
- if true 1B29 STMT L 1 executes the statement after the
THEN.
Output parameters: none.

IF 1 1D00 (1CF0 IF)


Jumps from:
1CF0 IF

IN key (BF) see also commands, functions and operators,


KEYBOARD SCANNING, 0246 extended mode table (c)
The I key in E mode with either shift produces the
function IN; it requires one numeric operand X, and the value
of the function is the present reading of port X.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code BF first to 10, then to EC,
and adds the priority 10h/16d. Code and priority 10EC are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following IN is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from EC to 2C, the calculator
offset for 34A5 in.

in subroutine 34A5
Called only from 0028 FP CALC with literal 2C to execute
the BASIC IN command. No point in calling from ROM or m/c,
the IN instructions are more effective.
Input parameters: none

363
- the port number is the last value on the calculator
stack.
Action: call 1E99 FIND INT2 to get the last value in BC
and check it for range
- read from port BC into register A.
Exit: via 34B0 IN PK STK to 2D28 STACK A, which puts A
on the stack.
Output parameters: A holds the reading from the port
- the port number has been removed from the stack.

IN ASSIGN subroutine 21B9


When you respond to an INPUT command by typing an
expression into the work space, this subroutine is called
twice:
first to check the syntax, and then again to evaluate the
expression and assign it to the variable. This doesn't apply to
INPUT ... LINE, which works differently.
Input parameters: none
- there must be an expression in the work space and a
variable of the correct type ready for assignment; ie its
address in 5C4D DEST must be in the variables area.
Action: put the BASIC pointer 5C5D CH ADD on the start
of the work space in 5C61 WORKSP
- read the first code in the work space
- if it is E2 STOP jump on to IN STOP
- (not STOP) get FLAGX; its bit 6 is the INPUT numeric/
string status flag
- call 1C59 VAL FET 2 to read the expression and assign
it; it reports an error if its status doesn't match the flag
- read the next character
- if it is 0D newline return
- report "Nonsense in BASIC".
_21D0_IN_STOP (STOP found): return in syntax checking;
anything at all can follow STOP in an INPUT, its syntax isn't
checked
- (run time) report "STOP in INPUT".
Exit: RETs in IN ASSIGN and IN STOP.

364
Output parameters: none.
Called from:
2148 IN VAR 2
2174 IN VAR 5

IN CHAN K subroutine 21D6


Checks if channel K is current channel. See channels and
streams.
In the simple Spectrum no input is possible except
through channel K; with peripherals attached this is possible,
eg through a network, and this subroutine is used in the
INPUT command routine to inhibit the copying of inputs to
the screen.
Input parameters: none.
Action: read the fifth address starting from the address
in 5C51 CURCHL
- check it with 4B "K".
Exit: RET.
Output parameters: a Z flags "this is channel K"
- A holds the channel letter
- HL its address after 5C51 CURCHL.
Called from:
2161 IN VAR 4

INDEXER subroutine 16DC see also tables


Reads tables such as the channel code look-up, CLOSE/
OPEN stream look-up, operators, etc. Each entry in the table
must be two bytes, an_index and an_offset: the index code is
compared with the code being looked up, the offset may be
any single-byte value, but in many cases is added to the
address of the index code to give a jump address for a
subroutine. The table always has an_end_marker, zero in the
byte after the last entry pair; except in the meaningless "close
stream look-up table" at 1716
Input parameters: HL holds a pointer to the table; the
address of the first index
- C holds the check code to be looked up.

365
Action: if the index is zero, return
- compare it with the check code
- increase the pointer; to put it on the offset
- if the index doesn't match the check code jump back to
INDEXER 1
- (match found) set carry and return.
_16DB_INDEXER_1 (no match yet): increase the pointer
again; to put it on the next index
- continue into INDEXER for another turn of the loop.
Exit: RET.
Output parameters: the carry flag indicates that a match
was found
- NC that zero was reached without a match
- A holds the same as C which is unchanged, or zero if
zero was reached
- HL holds the address of the offset or of the zero.
Called from:
1615 CHAN FLAG
1701 CLOSE 2
1767 OPEN 3
24FF S LOOP 1
2723 S OPERTR

INDEXER 1 16DB (16DC INDEXER)


Jumps from:
16DC INDEXER

indexing see tables, 16DC INDEXER

information area see channels and streams

IN ITEM 1 subroutine 20C1


The main executive part of the INPUT command routine.
See 2089 INPUT.
Called from:
2096 INPUT 1
Jumps from:

366
auto
21B2 IN NEXT 2

IN ITEM 2 20D8 (2089 INPUT)


Jumps from:
20C1 IN ITEM 1

IN ITEM 3 20ED (2089 INPUT)


Jumps from:
20D8 IN ITEM 2

initial channel information table 15AF see channels and


streams, tables

initial data of channels/streams see channels and streams

INITIALISATION ROUTINE see 11B7 NEW

initial key values see KEYBOARD SCANNING

initial parameters of array (first four bytes) see arrays

initial parameters (CIRCLE/DRAW), INITIAL PARAMETERS


SUBROUTINE see 247D CD PRMS1

initial stream data table 15C6 see tables

initial zero (in BASIC numbers) see 2DE3 PRINT FP

INK key (D9) see also colours, control characters,


KEYBOARD SCANNING, 0246 extended mode key table (c)
The X key in E mode with either shift produces the token
INK. INK can be used either as a BASIC command or as a print
control item within a PRINT etc statement. In either case it it
must be followed by a parameter, a numeric expression with
the value zero -> 9, and it uses this to set the "pixel on" colour
of any new printing on the screen: zero -> 7 are the screen

367
colours, INK 8 means "don't change the INK colour in this
position", INK 9 means "use black or white, whichever
contrasts best with the PAPER colour".
As a command, it is read by 1B29 STMT L 1 referring
through the syntax offset table 1A48 to the syntax parameter
table 1A7A. 1AEB P INK causes a jump to 1C96 CLASS 07
(PERMS), the executive routine for all the colour commands
INK to OVER.
As a print control item, execution is from within the
PRINT executive routine 1FC0 PRINT; each new expression
following the PRINT command is checked by a call to 1FFC PR
ITEM 1 from 1FE5 PRINT 3. If it is INK, this in turn calls 21F2
CO TEMP 3, from 2024 PR ITEM 3; here the token code D9 is
converted into the embedded control code 10h, which is then
sent through the output routine followed by its parameter.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 10h for INK into the
control character table at 0A11 produces an indirect jump to
0A7A (0A1B + 5F) PO 1 OPER. See the index description of this
subroutine for the rather tricky way in which it collects the
parameter and sends execution to 0A87 PO CONT, which
finally executes the INK command.
0BFA PO ATTR 1 handles INK 9 in screen printing
0F38 ED LOOP only two bytes required
0F6C ED CONTR only two bytes required
10FA KEY CONTR code put in A register
18C1 OUT FLASH cancel INK 9 temporarily

ink colour see colours

INK control code (10h)


The digit keys zero -> 7 in E mode without shift produce
the colour control items PAPER zero etc, with shift the items
INK zero etc. PAPER and INK 8 and 9 cannot be produced in
this way - the 8 and 9 keys in E mode control BRIGHT and
FLASH, see BRIGHT control code.
These keystrokes put the control codes 10h INK or 11h

368
PAPER, followed by the parameter, into the BASIC line. When
the line is copied to the screen, no character is printed for
either code or parameter, but the corresponding colour
command is executed for all subsequent printing either in the
lower or upper screen display. If the codes are input as part of
a string, they will remain part of the string and will be
executed whenever the string is printed out; in this case, for
all subsequent printing executed by the same PRINT
instruction.
The keyboard scanning routines at 0367 K DIGIT initially
return 10h -> 17h for the eight PAPER colours, and 18h -> 1Fh
for the INK colours, and put them in 5C08 LAST K; but these
are
split by 10A8 KEY INPUT and 10FA KEY CONTR into 10h or 11h
in the A register and the colour number in C. The channel
address is switched so that A and C are returned in series,
and both put in the BASIC.
18FF OUT LINE prints out the BASIC, either in a listing
or in the lower screen for editing; the PAPER or INK command
is implemented for the remainder of the print, but the actual
codes are skipped. They can be detected by cursor moves: the
cursor moves over the two bytes in one jump when going left,
but takes two jumps going right - possibly this is a mistake in
the ROM,
see 100C ED RIGHT. DELETE must be used twice to delete the
code and parameter.
If the cursor is placed by a right move_between the code
and parameter, and then DELETE is used - or if DELETE is
used only once from the right, which is arranged to produce
the same result - the code is deleted, leaving the parameter 00
-> 07 high and dry. All these codes except 06 print out as "?",
and are ignored by both syntax checking and execution,
because there is no interpretation for them. 06 happens to be
the PRINT comma, and is printed and executed as such; it can
be incorporated into strings, REMs etc as an economical way
of tabulating printouts.
Two such 06 codes produce a newline.

369
PAPER and INK colour codes can be used in variable
names, to give them prominence in the listing - they are
ignored when reading the variable for evaluation. [But see
under 28B2
LOOK VAR for a minor error.]
In strings, instead of using the E mode digit keys, the
colours can be put in by concatenation:
10 LET a$=CHR$ 17 + CHR$ 6 + "hello": PRINT a$
has just the same effect as
10 PRINT PAPER 6; "hello"
CHR$ 17 + CHR$ 6 alone, or even "[E mode 6]" can be
made the value of a string variable and printed in
concatenation with, or preceding, other variables. Such tricks
are occasionally useful.

INKEY$ key (A6) see also commands, functions and


operators,
KEYBOARD SCANNING, 022C extended mode table (b)
The N key in E mode without shift produces the function
INKEY$, which like PI and RND is a function without an
argument:
it needn't be followed by any parameter, though (hatch) with a
stream number is accepted. The value of the function is the
final code of whatever key is currently being pressed, or the
null string if "no key" is reported -_not the code currently in
5C08 LAST K, nor the next key pressed, INKEY$ doesn't wait.
However if a stream number is specified INKEY$ takes as its
value any input it finds from the specified stream.
On execution, 24FB SCANNING indexes into the scanning
function table at 2596 to find the executive routine 2634 S
INKEY$.
028E KEY SCAN called by executive routine
0C35 PO TRSP doesn't require trailing space
3645 read-in

INKEY$ subroutine see 2634 S INKEY$

370
INK to OVER see colours, BRIGHT control code (includes
FLASH), INK control code (includes PAPER), INVERSE control
code (includes OVER), and individual token keys

INK to TAB see colours etc as above, and control characters

IN NEXT 1 21AF (2089 INPUT)


Jumps from:
20ED IN ITEM 3

IN NEXT 2 21B2 (2089 INPUT)


Exit from:
20C1 IN ITEM 1, both direct and via the following
20FA IN PROMPT
2174 IN VAR 5
219B IN VAR 6

IN PK STK 34B0 (34AC peek)


Jumps from:
34A5 in

IN PROMPT 20FA (2089 INPUT)


Jumps from:
20D8 IN ITEM 2

IN PR 1 211A (2089 INPUT)


Jumps from:
20FA IN PROMPT

IN PR 2 211C (2089 INPUT)


Jumps from:
20FA IN PROMPT

IN PR 3 2129 (2089 INPUT)


Jumps from:
211C IN PR 2

371
INPUT key (EE) see also commands, functions and
operators,
KEYBOARD SCANNING
The I key in K mode produces the command INPUT. The
"INPUT statement" must include either a position controller or
a variable name, string or numeric; it may include both, and
in addition:
- control codes, position and colour, as for a PRINT
statement
- a_prompt in quotes; it may incorporate variable names
but if it does the prompt must be in brackets
- LINE, for string inputs only, indicating the prompt
cursor is to appear without quotes.
The prompt if any must come before the variable name,
and LINE must be after the prompt if any and immediately
before the variable name. Print controls can come anywhere
except between LINE and the variable.
The variable name can be followed by another position
controller and more variable names, with or without
prompts, LINE etc.
[Multiple INPUTs are quite acceptable, for example:
INPUT "a(1): ", a(1) ' "a(2): ", a(2) ' "a(3): ", a(3)
will print in turn, each on a new line,
a(1): (cursor)
a(2): (cursor)
a(3): (cursor)
waiting each time for the input before printing the new
prompt.
Also INPUT ' or INPUT , or INPUT ; are accepted, without
any variable name; but not INPUT alone. These merely clear
the lower screen, without waiting for any input. These options
don’t seem to be mentioned in the handbooks.]
For each variable name, the command prints out the
prompt, if any; prints a flashing L prompt cursor, which is in
quotes for a string variable except if LINE is specified; and
awaits input of an expression, which is then assigned to the
named variable.

372
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A9F P INPUT causes a jump via 1C11 CLASS 05 and 1C16 JUMP
CR
to the executive routine 2089 INPUT.
1C1F CLASS 01 identifies variable for INPUT (called by
20D8 IN ITEM 2 and 20ED IN ITEM 3)

INPUT subroutine 2089


Called only by the statement loop at 1C15 JUMP C R from
1A9F P INPUT in the syntax parameter table. Executes the
INPUT command by printing one or more prompts; waiting
for an expression from the keyboard, which is built up in the
work space and then evaluated as a BASIC expression by 24FB
SCANNING;
and assigning it to a variable. The expression from the
keyboard is often referred to in the notes as the_entry, which
may be a numeric_entry or a_string_entry.
Much of the action is in the subroutine IN ITEM 1, which
is never called or entered from elsewhere in ROM; I can see
no reason why IN ITEM 1 shouldn't have been incorporated
directly as part of INPUT 1 instead of being called as a
subroutine, and for clarity its action is included here.
All of IN ITEM 1 down to IN NEXT 2 is a loop; each turn
of the loop
- executes any print position controls ; , '
- prints any prompt from the BASIC
- waits for input
- evaluates the input and assigns it to the variable in
assignment
- and goes round the loop again till the end of the INPUT
statement is reached.
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on the byte
following the INPUT command.
Action: in syntax checking jump on to INPUT 1

373
- (run time) call 1601 CHAN OPEN with stream 01; open
channel K
- call 0D6E CLS LOWER to clear the lower screen.
_2096_INPUT_1: make flags in 5C3C TV FLAG (misprinted
DF
SZ in the notes):
"don't clear the lower screen"
"not automatic listing"
"don't copy the input"; but this will be reversed when
0E2C EDITOR calls 15D4 WAIT KEY
"print in the lower screen"
- call IN ITEM 1.
_20C1_IN_ITEM_1: call 204E PR POSN 1, which executes any
position controller; it returns with Z if the character was a
position controller. If it reaches the end of a statement, it
drops its return address, so exiting from the IN ITEM 1 loop
altogether back into INPUT 1
- if it returns with Z loop back to IN ITEM 1; in case
there are more position controllers, eg INPUT '' h
- if the next character isn't 2B ( jump on to IN ITEM 2
- (an expression in brackets is to be printed as a
prompt) move on the BASIC pointer
- call 1FDF PRINT 2 to print the prompt; it too can have
position controllers in it, and also ATs or TABs, but PRINT 2
will handle them
- (PRINT 2 will return on 29h ), but also on a statement
terminator) if the code reached isn't 29h ) report "Nonsense in
BASIC"
- move on the BASIC pointer
- jump on to IN NEXT 2.
_20D8_IN_ITEM_2 (no bracketed expression): if the next
code isn't CA LINE jump on to IN ITEM 3
- (INPUT ... LINE) move on the BASIC pointer
- call 1C1F CLASS 01 to put an address in 5C4D DEST for
the variable; it will report an error if a variable letter
doesn't immediately follow LINE
- flag "INPUT ... LINE" in FLAGX bit 7

374
- if FLAGS bit 6 shows "numeric input expected" report
"Nonsense in BASIC"; INPUT ... LINE can only be used for
string inputs
- jump on to IN PROMPT.
_20ED_IN_ITEM_3 (no LINE yet - it could still come
later): call 2C8D ALPHA
- if it returns with NC jump on to IN NEXT 1; the next
character isn't a letter, ie not the beginning of a variable
name
- (variable letter found) call 1C1F CLASS 01 to get an
address in 5C4D DEST for the variable
- flag "not INPUT ... LINE" in FLAGX bit 7.
_20FA_IN_PROMPT: in syntax checking jump on to IN
NEXT 2
- clear the work space; where the prompt cursor will be
formed
- zero bit 6 of FLAGX "string result"; provisionally
- set bit 5 of FLAGX "INPUT mode"; keyboard input will
go to the work space
- make a counter of one; one space for INPUT ... LINE or
numeric cursor
- if bit 7 of FLAGX is set jump on to IN PR 2; INPUT ...
LINE
- if bit 6 of FLAGS is set jump on to IN PR 1; numeric
entry
- (cursor will be in quotes) make the counter three.
_211A_IN_PR_1: copy bit 6 of FLAGS to bit 6 of FLAGX;
correcting to "numeric result" if a numeric entry is expected.
_211C_IN_PR_2: call 0030 BC SPACES with the counter to
make space in the work space for the cursor
- put a newline at the end of the space; the cursor
itself will be inserted by 0E2C EDITOR called in IN VAR 2 or IN
VAR 3
- if bit 1 of the counter is zero jump on to IN PR 3; it
is either 00000011b or 00000001b, so jump if it is one. Bit 1 is
misprinted bit 6 in the notes
- put 22h " in both the first two bytes of the space.

375
_2129_IN_PR_3: point 5C5B K CUR at the start of the space
- if the INPUT ... LINE flag is set jump on to IN VAR 3;
no errors are reported in INPUT ... LINE, any code at all is
accepted
- (not INPUT ... LINE) park the BASIC pointer and the
error stack pointer on the machine stack.
_213A_IN_VAR_1 (no jumps to this label, it is solely used
as an error address): put IN VAR 1 on the stack as return
address
- if channel K isn't open jump on to IN VAR 2; this can
only happen when peripherals are connected
- point 5C3D ERR SP at this stack position; return will
be to IN VAR 1 after an error in 24FB SCANNING called for
syntax checking from 1C59 VAL FET in 21B9 IN ASSIGN. Errors
detected by the call to 0E2C EDITOR from IN VAR 2 won't
return here, because EDITOR has its own error address 107F
ED ERROR.
_2148_IN_VAR_2: get a work space pointer from 5C61
WORKSP; on the start of the work space
- call 11A7 REMOVE FP; if there has been an error, 268D
S DECIMAL in SCANNING may have put number forms in the
input line on syntax checking, and REMOVE FP will take them
out again.
They aren't wanted yet, error or no error, because 21B9 IN
ASSIGN called below will put them in again
- cancel any error number; if there has been an error,
the error number will have been put in 5C3A ERR NR. Error
reports aren't made, the input is merely copied to the lower
screen with a flashing "?" and execution is returned to IN VAR
1
- call 0F2C EDITOR to make or correct the input
- call 21B9 IN ASSIGN with the syntax checking flag; it
checks that the input is a valid expression matching the
variable in string/numeric status, and inserts any number
forms required
- jump on to IN VAR 4.

376
_215E_IN_VAR_3 (INPUT ... LINE): call 0F2C EDITOR; there
can be no errors in INPUT ... LINE, so no syntax checking is
called for.
_2161_IN_VAR_4: zero 5C5C K CUR hi; only the hi byte is
zeroed, but this puts it well outside any possible address in
the work space
- call 21D6 IN CHAN K
- if it returns with NZ jump on to IN VAR 5; channel K
isn't open. It always will be open unless something like the
network is in use [it is hard to see why this isn't checked by
reference to FLAGS2 bit 4, as it was in IN VAR 1]
- call 111D ED COPY to copy the input line again to the
lower screen; in case there are multiple inputs on the same
command, eg
INPUT LINE s$ ' LINE t$
in which case the input for s$ is copied before the input for
t$ is prompted. [If there is no second input, this display is
cleared immediately; it can be seen well enough if a very long
input, at least ten lines, is made and the cursor moved back
near its beginning before pressing ENTER]
- call 0DD9 CL SET with the value in 5C82 ECHO E, so
that any further input prompts will follow those already on
screen.
_2174_IN_VAR_5 (input is finished, unless there are more
items in the INPUT command): zero bit 5 of FLAGX; "edit
mode"
- zero bit 7 of FLAGX; not INPUT ... LINE
- if it was set jump on to IN VAR 6
- (not INPUT ... LINE) drop the IN VAR 1 error address
- put the old error address back in 5C3D ERR SP
- recover the old BASIC pointer; stacked in IN PR 3
- park it temporarily in 5C5F X PTR
- call IN ASSIGN with the run-time flag; this time it
actually assigns the input to a variable in the variables area
- recover the BASIC pointer, put it in 5C5D CH ADD and
zero X PTR

377
- jump on to IN NEXT 2; go round the outer loop again if
there are more inputs in the INPUT command.
_219B_IN_VAR_6 (INPUT ... LINE finished): subtract the
value of 5C61 WORKSP from the value of 5C63 STKBOT to get
the string length of the input
- call 2AB2 STK STO $ to put the length and start
address on the calculator stack as string parameters
- call 2AFF LET which makes the assignment, ie copies
the string to the variable in the variables area
- jump on to IN NEXT 2; go round the outer loop again if
there are more inputs in the INPUT command.
_21AF_IN_NEXT_1 ( jump to here from IN ITEM 3, after any
position controller or bracketed expression): call 1FFC PR
ITEM 1 to print out ATs, TABs, colours, or prompt strings in
quotes before looping back.
_21B2_IN_NEXT_2 (an input item has been completed): call
PR POSN 1 again to see if there are any more position
controllers
- if the end of the statement hasn't been reached loop
back to IN ITEM 1
- (it has) the IN ITEM 1 subroutine now returns into
_INPUT_1 (continued): call 1BEE CHECK END, which in
syntax checking prints an error report if the statement isn't
complete or makes a double return to the statement loop if it
is
- (run time) get the current main screen print position
from 5C88 S POSN
- check its line number against the top line of the
lower screen in 5C6B DF SZ
- if the main screen print position is above the top
line of the lower screen jump on to INPUT 2
- (the screen has been scrolled) make the upper screen
line number equal to DF SZ and the column number 21h; the
next output to the main screen will go to a new bottom line.
_20AD_INPUT_2: put the new print position in 5C88 S
POSN
- correct the scroll counter in 5C8C SCR CT

378
- zero TV FLAG bit zero; "print to main screen"
- call 0DD9 CL SET to correct the system variables for
the new print position.
Exit: to 0D6E CLS LOWER, which clears the lower screen.
Output parameters: none
Rems:
0C55 PO SCR scroll may be needed
0E00 CL SCROLL [see note below]*
0F2C EDITOR called for INPUT
0F38 ED LOOP jump in INPUT ... LINE (ineffective)
1C1F CLASS 01 identifies variable for INPUT
1C56 VAL FET 1 used to evaluate variables
1FDF PRINT 2 called by INPUT routines
1FFC PR ITEM 1 called by INPUT routines
2AFF LET assignment routine for INPUT
* [The header note on 0E00 CL SCROLL, "the entry point
when scrolling for INPUT ... AT", is deeply inscrutable. In fact
CL SCROLL may be called indirectly by several places in the
INPUT routine, without AT necessarily being involved: calling
1FFC PR ITEM 1, which jumps on to 201E PR AT TAB,
which calls 0010 PRINT A 1,
which indirectly calls 09F4 PRINT OUT,
which jumps on to 0A75 PO 2 OPER
and thus on to 0A87 PO CONT
and 0AAC PO AT ERR,
which calls 0C55 PO SCR,
which jumps to 0D02 PO SCR 4,
and so through to 0D2D PO SCR 4B,
which calls CL SCROLL.
Not the most helpful note in the book.]

INPUT AD subroutine 15E6 see also channels and streams


Calls the input subroutine of the current channel.
Input parameters: none.
Action: switch to the alternate registers and save H'L'
on the stack

379
- read the address after the address in 5C51 CURCHL into
H'L'; the input routine of the current channel.
Exit: into 15F7 CALL SUB, see under 0010 PRINT A 1,
which calls the subroutine at the address found, still in the
alternate registers, and on return restores H'L' from the stack
and switches back to the main registers.
Output parameters: all main registers unchanged after
CALL SUB.
Called from:
15DE WAIT KEY
3645 read-in

input address of channel see channels and streams

input area
The first part of the work space when in input mode, up
to the newline put there in 211C IN PR 2. The part after the
newline is used by 24FB SCANNING for writing strings, etc.
OFA9 ED EDIT cleared by EDIT key
111D ED COPY to be copied to lower screen

INPUT line
The line input to the work space and printed in the
lower screen while in input mode.
0F81 ADD CHAR adds a character to
10A8 KEY INPUT copied to lower screen
117C ED C DONE exit after copying
2161 IN VAR 4 copied to display
21D0 IN STOP if INPUT line starts with STOP

INPUT ... LINE see INPUT key, 2089 INPUT

INPUT mode see FLAGX (bit 5)

input/output routines
A major section of ROM, see Introduction, from 028E KEY

380
SCAN to 11A7 REMOVE FP: keyboard, speaker, cassettes,
screen,
printer handling.

INPUT 1 2096 (2089 INPUT)


Jumps from:
2089 INPUT

INPUT 2 20AD (2089 INPUT)


Jumps from:
2096 INPUT 1

IN STOP 21D0 (2089 INPUT)


Exit from:
21B9 IN ASSIGN

INT key (BA) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The R key in E mode without shift produces the function
INT; it requires one numeric operand X, and the value of the
function is X if X is an integer, or the largest integer smaller
than X. If X is positive this is the integer part of X, but not if it
is negative.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code BA first to 0B, then to E7,
and adds the priority 10h/16d. Code and priority 10E7 are now
pushed on to the machine stack (270D S PUSH PO) while the
expression following INT is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E7 to 27, the calculator
offset for 36AF int.

int subroutine 36AF see also integer part of number


Called several times in ROM from 0028 FP CALC with
literal 27; also executes the INT function in BASIC. Not called
direct from ROM, but it could be called from m/c.
INT X returns the next integer below X - for positive

381
but not for negative X, this is the integer part of X. For
example INT -2.5 is -3.
Input parameters: none
- X must be last value on the calculator stack, even for
direct calls.
Action: use the calculator with literal 36 less-0 to
check the sign of X
- if it is negative jump on to X NEG
- (X positive) truncate X and return.
_36B7_X_NEG: truncate X
- subtract the result from X, getting the fractional
part of X
- rearrange the stack with the fractional part of X on
top and the integer part below
- call 30 not, which replaces the fractional part with
one if it is zero, with zero if it is non-zero
- if the fractional part is zero jump on to EXIT,
leaving only the integer part on the stack; X was a negative
integer
- (X negative, not an integer) subtract one from
truncated X.
_36C2_EXIT: close down the calculator.
Exit: RET.
Output parameters: none
- INT X has replaced X as last value on the stack.
Called from:
03F8 BEEP
2DA2 FP TO BC
2DC1 LOG(2**A)
2E01 PF LOOP
2E24 PF SMALL
36A0 n-mod-m
36C4 exp } The "1C46" printed after the label on
3783 get-argt } these two lines seems to be a misprint

INT CASE 3483 (346E negate)


Exit from:

382
3474 NEG TEST (346E negate)

integer form of numbers see CALCULATE

integer part of number


Any number which isn't an integer has an integer part
and a fractional part: eg pi, whose integer part is 3 and whose
fractional part is 0.C90FDAA2h/0.1415927d.
The fractional part isn't correctly called the "decimal
part", eg in the note on 2F4A PF E SBRN: there is nothing
decimal about it, it is still the same number whether
expressed in binary, octal, decimal, hex, vulgar fractions,
Chinese characters etc.
The notes seem sometimes to use the term "modulus" to
mean the integer part of a number: this is also incorrect, and
confusing, because of expressions like "X modulo 16d" or "X
mod 16d", which are short for "the remainder left when X is
divided by 16d"
2CCF DEC RPT C integer part zero for "proper fractions"
2D3B INT TO FP returns integer part of FP number
2DD5 FP TO A checks integer part not more than 255d
2E01 PF LOOP number split into parts
2E6F PF MEDIUM store integer part
2ECF PF FRACTN store fractional part
2EEF PF FR EXX integer part after multiplying by ten is
the digit to be stored
2F4A PF E SBRN fractional part handled separately
2F8B CA=10*A+C returns integer part of 10 * fraction
36AF int returns integer part as last value

INTEGER TO FLOATING POINT subroutine see 2D3B INT TO


FP

INTEGER TRUNCATION TOWARDS ZERO SUBROUTINE see


3214 truncate

383
interpreter (BASIC) see BASIC INTERPRETER, 1B8A LINE
RUN,
24FB SCANNING

interrupts
The non-maskable interrupt mode zero of the Z80 chip
isn't used in the Spectrum ROM. There is provision for its use
by m/c programmers, but an unfortunate mistake in ROM at
0066 RESET makes it impossible to do anything with it
except jump to 0000 START.
The maskable interrupt mode 1 calls 0038 MASK INT fifty
times a second to work the clock and to read the keyboard,
except during BEEPs, COPY and cassette operation.
The maskable interrupt mode 2 isn't used by ROM and is
freely available to programmers.
mode 1 disabled in:
0000 START
03B5 BEEPER
04D0 S FLAG
0556 LD BYTES
0EAC COPY
0ECD COPY BUFF
11B7 NEW
mode 1 enabled in:
0048 KEY INT
03F6 BE END
053F SA/LD RET
0EDA COPY END
0EFD COPY L 1
1219 RAM SET
Rems:
Introduction keyboard scanned 50d times per second
0038 MASK INT clock and keyboard on every interrupt
02BF KEYBOARD called by every interrupt
1303 MAIN 4 must be enabled for HALT
1F3A PAUSE counts maskable interrupts
1F3D PAUSE 1 "halts" on each interrupt

384
inter-statement marker see ":" (code 3A) at end of alphabet

INT EXP1 subroutine 2ACC


Reads a BASIC expression, which is the specification of
an array dimension or subscript, see arrays. Checks it against
the_limit_value supplied, the maximum value which can be
accepted as the result: when a dimension is specified it is set
at FF00, so effectively there is no limit except available
memory, when a subscript is being read it is set to the
dimension size.
_Out_of_range_errors aren't reported, but recorded in
an_error_register: if it remains zero, no error has occurred.
When the subroutine is called from the 2A52 SLICING loop, it
carries the error register forward from one call to another,
using 2ACD INT EXP2 as entry point so as not to reset it, and
reports an error only after both parameters have been
collected.
The purpose of this isn't at all clear.
Input parameters: HL holds the limit value
- the BASIC pointer in 5C5D CH ADD is on the first
character of an expression specifying a dimension or
subscript.
Action: make an error register set to zero.
_2ACD_INT_EXP2: call 1C82 EXPT 1NUM to evaluate the
expression and put it on the stack
- if checking syntax jump on to I RESTORE
- (run time) call 1E99 FIND INT2 to get the value of the
expression; FIND INT2 will report an error if the expression is
negative or more than FFFFh/65535d
- if it is zero jump on with carry to I CARRY
- check it against the limit value.
_2AE8_I_CARRY (the carry is set if there has been a
"zero" or "over the limit" error): if carry is set, SBC 00
decrements the error register.
_2AEB_I_RESTORE: clear the machine stack and return.
Exit: RET, from 2AEB I RESTORE.

385
Output parameters: BC holds the value of the subscript or
dimension
- A is zero if the value is within the range specified,
otherwise FF or less
- DE and HL are unchanged.
Called from:
29FB SV MULT
2C2E D NO LOOP
Rems:
2A52 SL LINE
2A81 SL SECOND save up the error register till slicing
completed
2A94 SL DEFINE test error register after slicing done
2AE8 I CARRY

INT EXP2 subroutine 2ACD (2ACC INT EXP1)


Called from:
2A52 SLICING
2A81 SL SECOND

INT FETCH subroutine 2D7F


Copies a small integer FP number X into a register, and
subtracts it from 10000h/65536d to make an absolute value if
it is marked as negative - which is indicated by a flag on
return.
X isn't necessarily on the calculator stack, though often it is.
There is no check that X is in small integer format; if
it isn't, the number returned will be rubbish.
The mechanism for producing the value of negative X
works like this:
- XOR the lo byte of X with the sign byte FF; the result
is FF minus X lo, with no carry
- subtract the sign byte; this_increments the result by
one and sets the carry flag, except only if the original lo byte
was zero. The result is now
from zero: zero without carry
from anything else: 100h - lo with carry

386
- add the sign byte FF to X hi with any carry from the lo
byte operations; this has no effect unless the lo byte was zero,
when it decrements X hi to X hi - 1
- XOR this result with the sign byte FF; it makes
if X lo was zero: 100h - X hi
if X lo was anything else: FF - X hi
The overall result is:
X zero: both bytes remain zero
X lo zero: X lo unchanged, X hi becomes 100h - X hi
otherwise: X lo becomes 100h - X lo, X hi becomes FF - X hi
which are the correct values required for the absolute value.
None of these operations have any effect if the number
is positive, since then its sign byte is zero.
Cf 2DA2 FP TO BC and 1E99 FIND INT2, which
- can only get numbers from the calculator stack, and
delete them from the stack in doing so; INT FETCH doesn't
- can read numbers which aren't integers, or not in small
integer format, and round them to integers if necessary
- signal negative numbers and out-of-range numbers; by
flags in the case of FP TO BC, by error reports in the case of
FIND INT2
- don't negate negative numbers to their absolute value.
Input parameters: HL holds the address of the first byte
of the number; the exponent byte, always zero for numbers in
small integer format.
Action: read the sign from the second byte; 00 for
positive, FF for negative
- read the value from the third byte; X lo
- XOR with the sign byte and subtract the sign byte
- read the fourth byte; X hi
- add the sign byte with carry
- XOR the result with the sign byte.
Exit: RET.
Output parameters: DE holds the value required
- C holds the sign byte, 00 or FF
- HL addresses the fourth byte of the FP number
- B unchanged

387
- always returns with NC.
Called from:
2DAD FP DELETE
2E01 PF LOOP
30CA multiply (twice)
3297 re-stack
3483 INT CASE
Rems:
2D8E INT STORE uses same mechanism to negate

INT STORE subroutine 2D8E


Puts the number X from a register holding ABS X into
"small integer" FP format, positive or negative according to a
flag. X is usually but not necessarily loaded on to the
calculator stack.
If X is flagged as negative, it is subtracted from
10000h/65536d before being loaded; the mechanism
employed is the same as that described above under 2D7F INT
FETCH.
The entry point 2D8C P INT STO can be used without the
flag if X can be assumed positive; never used from the ROM.
Cf: 2D2B STACK BC, which always puts X on the calculator
stack, and always as a positive number.
2AB6 STK STORE, which also loads a number to any
address HL, but it must already be in FP format in AEDCB, not
necessarily a small integer.
33C0 MOVE FP, which merely moves a FP number from
one location to another.
22B4 STACK NUM the same, but the destination is always
the calculator stack.
Input parameters: DE holds the absolute value of X
- HL the address of the first of the five bytes intended
to hold it
- C holds zero if X is positive, FF if negative.
Action: put zero in byte 1; small integer
- put C in byte 2; sign byte
- XOR the lo byte with the sign flag; for this and the

388
next few operations see the explanation of INT FETCH above
- subtract the sign flag from the result
- put this in byte 3
- add the sign flag to the hi byte with the carry
- XOR the result with the sign flag
- put this in byte 4
- put zero in byte 5.
Exit: RET.
Output parameters: BC, DE and HL unchanged
- X in the five bytes starting at HL.
Called from:
30EA MULT RSLT
3267 T STORE
3483 INT CASE
3492 sgn

INT TO FP subroutine 2D3B


Reads a decimal integer from BASIC and puts it on the
calculator stack in FP form.
Input parameters: A holds the character code of the first
digit, already read from the address at the BASIC pointer in
5C5D CH ADD.
Action: use the calculator to put zero on the stack for
an accumulator.
_24D0_NXT_DGT_2: call 2D22 STK DIGIT to put the value of
the digit on the calculator stack
- if it returns with carry return; no more digits
- (digit read) multiply the accumulator by ten and add
the new digit
- move on the BASIC pointer
- loop back to NXT DGT 2.
Exit: RET from NXT DGT 2 when all digits have been read.
Output parameters: the number is added to the calculator
stack as last value
- the C flag is set
- the BASIC pointer in 5C5D CH ADD is on the code after
the number.

389
Called from:
19FB E LINE NO
2CBB NOT BIN
2CFF ST E PART

IN VAR 1 213A (2089 INPUT)


There are no direct calls of or jumps to this label, but
it sets itself as error address for variable assignments in the
INPUT command routine; any errors in variable assignment
merely recopy the input BASIC to the lower screen with a
flashing error cursor. The error address is reset in 2174 IN VAR
5.
2148 IN VAR 2 return to if not OK
2174 IN VAR 5 address dropped

IN VAR 2 2148 (2089 INPUT)


Jumps from:
213A IN VAR 1

IN VAR 3 215E (2089 INPUT)


Jumps from:
2129 IN PR 3

IN VAR 4 2161 (2089 INPUT)


Jumps from:
2148 IN VAR 2

IN VAR 5 2174 (2089 INPUT)


Misprinted IN VARS 5.
Jumps from:
2161 IN VAR 4

IN VAR 6 219B (2089 INPUT)


Misprinted IN VARS 6.
Jumps from:
2174 IN VAR 5

390
INVERSE key (DD) see also colours, KEYBOARD SCANNING,
extended mode key table (c)
The M key in E mode with either shift produces the token
INVERSE. INVERSE can be used either as a BASIC command
or as a print control item within a PRINT etc statement. In
either case it must be followed by a parameter:
one for "on", which reverses the PAPER and INK colours in
any new printing position on the screen
zero for "off", which restores the position to normal.
INVERSE 8 isn't accepted.
As a command, it is read by 1B29 STMT L 1 referring
through the syntax offset table 1A48 to the syntax parameter
table 1A7A. 1AEF P INVERSE causes a jump to 1C96 CLASS 07
(PERMS), the executive routine for all the colour item
commands INK to OVER.
As a print control item, execution is from within the
PRINT executive routine 1FCD PRINT; each new expression
following the PRINT command is checked by a call to 1FFC PR
ITEM 1 from 1FE5 PRINT 3. If it is INVERSE, this in turn calls
21F2 CO TEMP 3, from 2024 PR ITEM 3; here the token code
DD is converted to the "embedded" control code 14h, which is
then sent through the output routine followed by the
parameter.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 14h for INVERSE into the
control character table at 0A11 produces an indirect jump to
0A7A (0A1F + 5B) PO 1 OPER. See the index description of this
subroutine for the rather tricky way in which it collects the
parameter and sends execution to 0A87 PO CONT, which
finally executes the INVERSE command.

inverse character (pixel), inverse status


Terms used only in notes on the SCREEN$ function: in
comparing the character on screen with the character set, it
still counts as a match even if the character is printed in
INVERSE VIDEO.
254F S SCRN LP test with mask 00 and mask FF

391
255A S SC MTCH save mask to match seven remaining
bytes

INVERSE control code (14h) and OVER control code (15h)


On the original Spectrum, the INVERSE control codes
were produced as follows:
"INVERSE on" - 4 key with caps shift
"INVERSE off" - 3 key with caps shift
Caps shift 4 puts no code on the screen,
but all following printout is in INVERSE on, and similarly the 3
key produces INVERSE off.
There are no direct keystrokes for OVER on or off.
Later models of the Spectrum mostly have special_INV
_VIDEO and_TRUE_VIDEO keys, but 3 and 4 with caps shift still
work.
The keyboard scanning routines at 039D K KLC DGT
return the initial codes 05 for INVERSE on/INV VIDEO and 04
for INVERSE off/TRUE VIDEO, and put these values in 5C08
LAST K.
However these codes are corrected by 10A8 KEY INPUT,
which puts 14h in the A register for INVERSE, and 00 or 01 for
OFF or ON in the C register. The channel address is switched
so that A and C are returned in series, and both are put in the
BASIC line.
None of this applies to OVER, but the OVER control code
and parameter can be incorporated in strings to give a similar
effect, see below.
When the BASIC line is printed out by 18FF OUT LINE,
either in a listing or in the lower screen for editing, the
INVERSE or OVER command is implemented for the
remainder of the print, but the presence of the codes isn't
otherwise indicated.
It can be detected by cursor moves: the cursor moves over the
two bytes in one jump when going left, but takes two jumps
going right - due to a mistake in the ROM, see 100C ED RIGHT.
DELETE must be used twice to delete the control code and its
parameter.

392
Such INVERSE controls can be incorporated in variable
names, eg to give them prominence in the listing: they are
ignored when reading the variable for evaluation. [But see the
minor error described under 28B2 LOOK VARS.]
The INVERSE or OVER control codes 14h/20d and 15h/21d
can be incorporated in strings:
10 LET a$="cliche" + CHR$ 8 + CHR$ 21 + CHR$ 1 + "'"
20 PRINT a$
will print "cliche" with an acute accent, not a very good one,
on the e; or you can similarly print "Noel" with a diaeresis "
on the o. Similarly with CHR$ 20 for INVERSE. This is much
more useful when printing from m/c.
Introduction INVERSE/OVER checked on every keystroke
007D SKIP OVER only one skip forward
0A3D PO RIGHT cursor right = space with OVER
0A6D PO TV 2 controls need 1 operand
0A7A PO 1 OPER changes output routine
0A87 PO CONT jump for 1-operand keys
0B93 PR ALL 1 check INVERSE and OVER
0BA4 PR ALL 2 make mask for INVERSE
0BB7 PR ALL 4 check INVERSE mask
103E ED EDGE 1 not split from parameter by cursor
10A8 KEY INPUT change INVERSE to final code
10FA KEY CONTR doesn't handle INVERSE
18C1 OUT FLASH ensures INVERSE and OVER off
2211 CO TEMP 5 sets P FLAG for INVERSE and OVER
22DC PLOT considers INVERSE and OVER
22F0 PLOT LOOP checks for OVER
22FD PL TEST IN implements INVERSE and OVER in
PLOT

inverted character see 0C0A PO MSG

INV VIDEO key see INVERSE control code

I RESTORE 2AEB (2ACC INT EXP1)


Exit from:

393
2AE8 I CARRY (2ACC INT EXP1, 2ACD INT EXP2)

IX END 3290 (3214 truncate)


Exit from:
3214 truncate, through either of:
3283 BITS ZERO
328A LESS MASK

IX register
The IX register is set at a number of places in the ROM,
all in the BEEP and SAVE/LOAD routines, see list below; but
always in an ad hoc manner. Unlike the IY register it can be
used freely in m/c programs, even those using ROM routines,
provided it is borne in mind that some of them will reset IX
for their own purposes.
IX placed by:
03B5 BEEPER (at 03D1, then incremented)
04D0 SA FLAG (decremented)
0525 SA 8 BITS (incremented)
05C2 LD NEXT (incremented)
0621 SA SPACE (from stack)
075A SA ALL (incremented)
07F4 VR CONT 2 (from stack)
084C LD DATA 1 (from stack)
08AD LD PROG 1 (from stack)
08B6 ME CONTRL (from stack)
0991 SA 1 SEC (from stack)
IX referred to by:
03D6 BE H&L LP
03F2 BE AGAIN
04FE SA LOOP
05A9 LD LOOP
05BD LD VERIFY
0629 SA BLANK
064B SA NAME
0672 SA V OLD (twice)
0685 SA V NEW

394
068F SA V TYPE
06A0 SA SCR$ (four times)
06F9 SA CODE 4 (four times)
0710 SA TYPE 3
0716 SA LINE
0723 SA LINE 1 (twice)
073A SA TYPE 0 (five times)
0767 LD LOOK H (PUSHed, POPped and read twice)
078A LD TYPE
07AD LD CH PR
07CB VR CONTRL (five times)
07E9 VR CONT 1 (twice)
0808 LD CONTRL (twice)
0819 LD CONT 1 (twice)
082E LD DATA (read, stored and retrieved)
084C LD DATA 1 (three times)
0873 LD PROG (stored, retrieved, read six times)
08B6 ME CONTRL (twice)
0970 SA CONTRL (PUSHed and POPped)
0991 SA 1 SEC (twice)

IY register see also 2D2B STACK BC, system variables


The IY register is set on start-up by 1219 RAM SET to
the address 5C3A ERR NR; thus it can be used to address any
byte from IY - 80h = 5BBAh/23482d to IY + 7Fh = 5CB9h/
23737d, including all the system variables, the "new key
status" area, stream information area, and the calculator
memory area, and this is very frequently used in all parts of
the ROM.
It is reset automatically by 2D2B STACK BC which is
always the exit routine of any USR function; see the note on
34B3 usr-no. This means you can freely use the unaltered IY
to refer to the ROM's svs in your own programming, and
you_can relocate the IY register in your m/c programs, despite
the warning on page 180 of the old Handbook, which is
amplified in the Plus 2 book, page 154. However if this is done
1. Calls to ROM routines must be limited to the simplest

395
routines, which make no reference to system variables etc -
unless of course you make your own version of the system
variables which are referred to.
2. The interrupt must be disabled, because the maskable
interrupt routine 0038 MASK INT makes many references to
the Spectrum svs.

396
J

jump subroutine 3686


Called only from 0028 FP CALC with literal 33, but both
JUMP and JUMP 2 are used as exit routines. The calculator
analogue of the Z80 assembly language command JR nn, an
unconditional relative jump to the literal from -80h/-128d
backwards to +7Fh/+127d forwards, counting from the literal
after 33 jump as zero; so 33 jump FF jumps to the jump literal
itself, making an endless loop.
The "distance" to be jumped is in the next literal: it
is the number of bytes forward or backward to jump. 80h or
more is automatically read as a backward jump. The one-byte
distance is extended to two bytes by putting 00 in its hi byte if
it is less than 80h, otherwise FF: this distance is then added
to the address of the distance literal to give the address of the
next literal to be used by the calculator.
There is no check that the address jumped to is a
literal within a sequence of calculator literals recognised by
the 335B CALCULATE routine. If it isn't the whole system is
likely to crash.
Input parameters: HL' addresses the distance literal
following 33 jump.
Action: change to the alternate registers.
_3687_JUMP_2 (the exit point from 367A dec-jr-nz if 5C67
BREG isn't yet zero): get the distance
- RLA/SBC A,A makes 00 if the distance is positive, FF
if it is negative
- make the distance a two-byte value with this sign byte
in its hi byte
- add the distance to the distance address and exchange
the registers back.
Exit: RET, from 3687 JUMP 2.
Output parameters: HL' holds the address from which the

397
next literal will be read.
Called from:
2D60 E LOOP
37AA cos
37E2 atn
Exit from:
368F jump-true (as JUMP)

JUMP C R 1C16 (1C11 CLASS 05)


The label is never used or even referred to in the ROM
or the notes, except in its cross-heading; but it is frequently
referred to in this index. It makes an indirect jump to the
address in 5C74 T ADDR, which will be a command routine
address taken from the syntax parameter table.

jump in BASIC see 5C44 NSPPC

"jump on minus", "jump on plus", "jump on zero" see jump-


true

jump-true subroutine 368F see also 3686 jump


Called only from 0028 FP CALC with literal 00. Makes a
relative jump, like that of 3686 jump, if a_test_byte isn't
zero.
The test byte is byte 3 of the last value on the calculator
stack, which is assumed to be in small integer format making
this the lo byte of a small integer. Although the name jump-
true refers to logical values, in ROM the value jumped on is
often a normal numerical value.
Very often standard sequences are used for other
conditional jumps, such as
jump on minus:
36 less-0
00 jump-true
jump on plus:
37 greater-0
00 jump-true

398
jump on zero:
30 not
30 not
00 jump-true
Input parameters: the distance of the jump is in the
distance literal following the jump-true literal 00; positive or
negative, as for 3686 jump
- HL' as usual points to the "return address", in this
case holding the distance literal
- DE addresses the last value on the calculator stack
and HL the second last; because the literal 00 makes 0028 FP
CALC treat this as a binary operation. On return from jump-
true the last value, containing the test byte, is therefore
deleted.
Action: move the last value pointer on to the test byte
- read the byte and move the pointer back again
- if the test byte is non-zero exit to 3686 jump
- (test byte zero) move on the "return address" pointer;
the next call to FP CALC will use the literal following the
distance literal.
Exit: RET if the test byte is zero, into 3686 jump if
not.
Output parameters: main registers unchanged, A
corrupted
- HL' advanced one if the test byte was zero
- the last value on the stack, containing the test byte,
has been deleted; whether or not a jump was made.
Called from:
1DDA NEXT LOOP ( jump on minus)
1DE2 NEXT 1 ( jump on plus)
238D DR 3 PRMS ( jump on zero)
2D60 E LOOP ( jump on NZ)
2DE3 PRINT FP ( jump on plus and jump on minus)
36AF int ( jump on minus
36B7 X NEG ( jump on NZ)
3713 ln ( jump on plus)
371C VALID ( jump on plus)

399
3783 get-argt ( jump on plus)
37A1 Z PLUS ( jump on minus)
37AA cos ( jump on NZ)
37E2 atn ( jump on minus)
384A sqr ( jump on NZ)
3851 to-power ( jump on NZ)
385D XISO ( jump on NZ and jump on plus)
Rems:
34F9 greater-0 used for jump on plus
3501 not - used for jump on zero
3506 less-0 used for jump on minus
3686 jump - used by

JUMP 2 3687 (3686 jump)


Exit from:
367A dec-jr-nz
3686 jump

400
K

K CH SET 02D1 (O2BF KEYBOARD)


Exit from:
02C6 K ST LOOP (twice)

K CUR system variable 5C5B


Bytes: 2
The present address within the editing area or work
space at which the_editing_cursor is to be printed when the
BASIC is output to screen. This cursor is printed as a flashing
K, L, C, E or G depending on the mode of the next keystroke
to be interpreted, so the notes sometimes also call it
the_mode _cursor.
K CUR is initialized to the start of the work space or
editing area whenever either is cleared by 1097 CLEAR SP, and
to the start of the editing area by 16B0 SET MIN.
Left and right cursor moves in the 0F2C EDITOR loop
operate simply by increasing or decreasing K CUR; this is all
that is necessary, since the edit line is recopied to the lower
screen after every edit keystroke with the appropriate mode
cursor at the place marked by K CUR.
It is one of the fourteen system pointers whose position is
adjusted by 1664 POINTERS whenever space is made or
reclaimed in the RAM.
Written by:
0F8B ADD CH 1
0FA9 ED EDIT
1011 ED CUR
1097 CLEAR SP
166B PTR NEXT
16B0 SET MIN
2129 IN PR 3
2161 IN VAR 4 (hi byte zeroed)

401
361F
Read by:
0F6C ED CONTR
0F81 ADD CHAR
0F92 ED KEYS
166B PTR NEXT
18E1 OUT CURS
361F
Rems:
1007 ED LEFT moves cursor left
100C ED RIGHT moves cursor right
1015 ED DELETE moves cursor left with delete
1031 ED EDGE moves cursor left unless at start
1051 ED EDGE 2 look for last printable char before K CUR
111D ED COPY print cursor if at end of line
1BA1 OUT LINE5 check if time to print the cursor
18C1 OUT FLASH prints all flashing cursors
18E1 OUT CURS prepares to print editing cursor
1909 OUT C 2 prints editing cursor

K DATA system variable 5C0D


Bytes: 1
Holds the parameter of an embedded INK to OVER colour
control while the control code is output through the current
channel. The parameter is calculated by 1105 KEY DATA from
the code temporarily supplied as a "final code" by keyboard
scanning, see 0333 K DECODE.
Written by:
1105 KEY DATA
Read by:
110D KEY NEXT

K DECODE subroutine 0333 see also KEYBOARD SCANNING


especially NB 3
Interprets a keystroke. The main code has already been
found by 031E K TEST: it may be
a capital letter 41h -> 5Ah or digit 30h -> 39h

402
space 20h, ENTER 0Dh
E-mode on/off 0Eh; returned if both shifts are being
pressed, see NB 2 in K MAIN under 031F K TEST.
This subroutine considers the main code in relation to
the key mode and the shift byte, and produces the final code.
In many cases the final step is to K LOOK UP with the main
code, to look up the final code from one of the tables starting
at 0205.
The base address of a table is often not its start address,
which is reached by adding to the base the smallest main code
indexed. Eg the base of letter tables is the start less 41h, the
code for "A".
Input parameters: E holds the main code
- D holds the value of FLAGS: bit 3 indicates whether K
or not-K mode is to be used
- C holds the mode value from 5C41 MODE: zero for K, L
or C modes, one for E mode, 2 for G mode
- B holds the shift byte, FFh for no shift, 27h for CAPS
SHIFT, 18h for SYMBOL SHIFT.
Action: if the main code is less than 3A jump on to K
DIGIT; jump on everything except letter keys
- (letter keys) decrement the mode value
- if it becomes negative jump on to K KLC LET; zero
signals KLC mode
- if it becomes zero jump on to K E LET; one signals E
mode
- (letter keys in G mode, user-defined graphics) add 4F
to the main code and return; this works fine for udgs A -> U,
but has the odd effect of producing the tokens RND, INKEY$,
PI,
FN, POINT for letters V -> Z
_0341_K_E_LET (letter keys in E mode): make a base
address for 022C table (b) E-mode unshifted letter keys
- if the shift byte is FF jump on to K LOOK UP; no shift
- (either shift pressed) make a base address for 0246
table (c) E-mode letter keys with either shift.
_034A_K_LOOK_UP: add the main key code to the base

403
address of the table
- get the code at the resulting address
- return.
_034F_K_KLC_LET (letter keys in KLC modes): make a base
address for 026A table (e) letter keys with symbol shift
- if bit zero of the shift byte is zero jump back to K
LOOK UP; 18h symbol shift is the only even shift byte
- if bit 3 of FLAGS is set jump on to K TOKENS; K mode
- if bit 3 of FLAGS2 is set or the shift byte isn't FF
return with the main code unchanged, making an upper-case
letter; CAPS LOCK is on or CAPS SHIFT pressed
- (L mode, no shift) add 20h; makes the lower-case
letter
- return.
_0364_K_TOKENS (letter keys in K mode): add A5 to the
main code; this finds all the tokens on letter keys
- return.
_0367_K_DIGIT (non-letters): if the code is less than 30h
return; 0D ENTER and 0E E-mode
- (digit keys) decrement the mode value; cf K DECODE
above
- if it was zero jump on to K KLC DGT; KLC modes
- if it wasn't one jump on to K GRA DGT
- (one is E mode) make a base address for 0284 table (f )
E-mode digit keys with symbol shift
- if bit 5 of the shift byte is zero jump back to K LOOK
UP; 18h symbol shift, the even shift byte
- (digit keys, E mode, not symbol shift) if the code is
more than 37h jump on to K 8 & 9
- (30h zero -> 37h seven) subtract 20h; now 10h -> 17h
- if the shift byte is FF return; no shift, these are
the interim PAPER codes. These codes and those for INK,
BRIGHT and FLASH below are converted to the standard
control code and parameter later, see 10A8 KEY INPUT
- (caps shift) add 08; 18h -> 1Fh are the interim INK
codes
- return.

404
_0382_K_8_&_9 (still E mode, 38h eight and 39h nine):
subtract 36h
- if the shift byte is FF return; correct interim codes
for 02 BRIGHT on and 03 BRIGHT off
- (caps shift on) add FE and return; correct interim
codes for 00 FLASH on and 01 FLASH off. Adding FE instead
of subtracting 02 sets the carry, but this flag never seems to
get used.
_0389_K_GRA_DGT (digit keys in G mode): make a base
address for 0260 table (d) digit keys with caps shift
- if the code is 39h nine or 30h zero jump back to K
LOOK UP; GRAPHICS and DELETE
- (31h -> 38h, the graphics forms) AND the code with
00000111b/07 and add 80h; this makes them 81h -> 88h, the
codes for the TRUE VIDEO graphics
- if the shift byte is FF return; no shift
- (either shift pressed; INVERSE VIDEO graphics forms)
XOR the code with 00001111b/0Fh and return; the hi digit is
unchanged, the lo is reversed:
0000 -> 1111
0001 -> 1110
0010 -> 1101
0011 -> 1100
0100 -> 1011
0101 -> 1010
0110 -> 1001
0111 -> 1000
which are 08 -> 0F in reverse order, the correct codes for the
INVERSE VIDEO graphics
_039D_K_KLC_DGT (digit keys in KLC mode):
- if the shift byte is FF return; the main code is the
digit
- make a base address for 0260 table (d) digit keys with
caps shift
- if bit 5 of the shift byte is set jump back to K LOOK
UP; caps shift
- (symbol shift) subtract 10h from the code; this makes

405
correct codes except for symbol shift two and symbol shift
zero
- if the result is 22h jump on to K @ CHAR; from the
symbol shift two key
- if it isn't 20h return; 20h from the symbol shift zero
key
- (symbol shift zero key) make the code 5F underline and
return.
_03B2_K_@_CHAR: make the code 40h @; symbol shift two
key.
Exit: RET, from K DECODE, K LOOK UP, K KLC LET
(twice), K
TOKENS, K DIGIT (three times), K 8 & 9 (twice), K GRA DGT
(twice), K KLC DGT (three times) and K @ CHAR.
Output parameters: A holds the final key code.
Called from:
02F1 K NEW
2634 S INKEY$

K DIGIT 0367 (0333 K DECODE)


Jumps from:
0333 K DECODE

K E LET 0341 (0333 K DECODE)


Jumps from:
0333 K DECODE

K END 0308 (02BF KEYBOARD)


Exit from:
02BF KEYBOARD, via one of:
02F1 K NEW
0310 K REPEAT

key see KEYBOARD SCANNING, 02BE KEY SCAN and


individual tokens ABS, ACS etc

key bits see KEYBOARD SCANNING

406
KEY BITS 02A1 (028E KEY SCAN)
Jumps from:
auto

KEYBOARD subroutine 02BF


The master keyboard scanning routine, called fifty times
every second by the maskable interrupt 0038 MASK INT, with
all registers saved. See KEYBOARD SCANNING. Not otherwise
called from ROM. It can be called from m/c, but this is
pointless unless the interrupt is disabled; even then it is
simpler to use RST 38h, except to avoid changing the values in
5C78 FRAMES.
Reads the keyboard to get the main code, converts it to
a final code and deposits this in 5C08 LAST K.
Also handles the key delay. A keystroke isn't accepted
until a certain number of interrupts have been counted since
the last acceptance. The count is kept in 5C00 KSTATE, which
has eight bytes in two sets, bytes 0-3 and bytes 4-7, called
KSTATE0 and KSTATE4 in the notes:
Byte zero or 4 holds the "set is free" signal FF, or the
last main code accepted if the set isn't free. All main codes
are less than 80h, so the hi bit of the code will be zero if the
code isn't free
Byte one or 5 holds the "five-call counter"
Byte 2 or 6 holds the "delay" value from REPPER/REPDEL
Byte 3 or 7 holds the "final code"
The loop K ST LOOP/K CH SET is turned twice every time
the interrupt fires, whether a key is being pressed or not,
except only if three keys or two main code keys are being
pressed at once. The first turn checks KSTATE0, the second
KSTATE4. On each turn the five-call counter is decremented,
from its start value of five; when it reaches zero the set is
marked as free.
If the key is a new one, ie the main code from the
keyboard doesn't match byte zero of either of the KSTATE
sets, and neither set is free, the subroutine returns after this

407
double loop without any further action. Although the double
loop itself takes no significant time, it takes five interrupts to
free both KSTATE sets; since one will usually become free
before the other, for a new key the delay is always five
interrupts,
one-tenth of a second, from the last key but one.
When a set is free for a new main code
- the main code is put in the first byte of the free set,
marking it as not free
- its five-call counter is reset to five
- its delay byte is set to the value in 5C09 REPDEL
- the code got by calling 0333 K DECODE with input
parameters from 5C41 MODE, FLAGS and the shift byte, is put
in its final code byte and in 5C08 LAST K.
If the keyboard code matches either of the byte zero
codes, ie if it repeats either the last input or the last but
one,
- its five-call counter is reset to five
- its delay byte is counted down, and if it doesn't reach
zero the subroutine returns without further action
- but if it does, the delay byte is set to the value in
5C0A REPPER and the code is read from the final code byte of
the key set and put straight in 5C08 LAST K without calling K
DECODE. [If you press eg the P key in K mode and hold it
down, the output is PRINT PRINT PRINT ...; if you let it up
and pause a little after the first keystroke, you get PRINT
ppp ..., as you should.]
Although in theory a match with the last key but one is
significant, "no key" also counts down the counters, and the
chances are the key set has been freed before the next-but-
one keystroke; you would have to type pretty fast for the key
before last to have any significance.
Thus the waiting time for a key matching either the last
or the last but one is the number of interrupts in 5C09
REPDEL for the first repeat and that in 5C0A REPPER for
further repeats. This makes the delay longer on the first
repetition than on subsequent ones: normally REPDEL has

408
35d, making a delay of seven-tenths of a second, and REPPER
has five, one-tenth of a second, but these can be changed by
POKing the svs.
[Experiment will show that either "pot" or "pop" can be
typed faster than "poo". This is because either the "t" or the
second "p" waits only 1/10 second or less for a free key set,
but the second "o" waits till REPDEL is counted down.]
Input parameters: none.
Action: call 028E KEY SCAN to read the keyboard
- if the flag show NZ return; three keys or two main
code keys are being pressed together
- put a pointer on KSTATE0 for the first turn of the
double loop.
_02C6_K_ST_LOOP: if byte zero shows the set to be free
jump on to K CH SET
- (set not free) decrement its five-call counter
- if this isn't now zero jump on to K CH SET
- (counter zero) mark the set free with FF in byte zero.
_02D1_K_CH_SET: put the pointer on KSTATE4 for the
second
turn of the double loop
- if the pointer has changed jump back to K ST LOOP for
the second turn
- (both sets checked) call 031E K TEST, which gets a
main code from the keyboard's key value
- if it returns with NC, return; "no key" or "shift
only"
- if the main code matches byte zero of either key set
jump on to K REPEAT
- if neither set is free return.
_02F1_K_NEW (new code, free set found): put the main
code
in byte zero of the free set; this marks it as not free
- put 5 in its five-call counter
- put the counter from 5C09 REPDEL in its delay byte
- get parameters from 5C41 MODE and FLAGS and call
0333

409
K DECODE; it returns the final code
- put the code in the final code byte.
_0308_K_END: put the code in 5C08 LAST K
- set bit 5 of FLAGS; "new key accepted"
- return.
_0310_K_REPEAT (the keystroke repeats the code of the
last keystroke): reset the five-call counter to 5
- decrement the delay byte
- if it doesn't reach zero return
- (zero delay) reload the delay byte from 5C0A REPPER
- get the code from the final code byte
- jump back to K END.
Exit: RET, from K CH SET (twice), K END or K REPEAT.
Output parameters: none
- FLAGS bit 5 set if the code was accepted.
Called from:
0048 KEY INT
Rems:
1219 RAM SET frees KSTATE0 and KSTATE4

keyboard click see 5C39 PIP

KEYBOARD DECODING SUBROUTINE see 0333 K DECODE

KEYBOARD INPUT SUBROUTINE see 10A8 KEY INPUT

keyboard interrupt see interrupts, KEYBOARD SCANNING,


0038
MASK INT

KEYBOARD SCANNING see also shift keys


The outline of these routines is as follows; the
terminology used in the notes is followed as far as possible,
but it isn't always consistent:
The master routine is 02BF KEYBOARD.
First it calls 028E KEY SCAN, which scans the keyboard

410
and gets a_key_number or_key_value, depending on the
position of the key on the keyboard, and a_shift_byte
indicating which shift is being pressed or_no-shift; see NB 1
below
Now a call to 031E K TEST converts the key number into a
_main_code, merely by looking it up in the table at 0205: the
main code is the character code for
a digit 30h to 39h
a capital letter 41h to 5Ah
ENTER 0Dh or space 20h
the Extend mode shift 0Eh; marked SYMBOL SHIFT in the
table in the notes, but its effect is always the E shift.
The table has all the main codes arranged in order of
key number.
Now KEYBOARD introduces a_delay into the key
response:
see NB 2 below, and under 02BF KEYBOARD.
Next a call to 0333 K DECODE converts the main code
into a_final_code, taking account of the shifts and the current
input mode: this can be any of the character or token codes,
see NB 3 below.
Finally KEYBOARD stores the final code in 5C08 LAST K
and sets bit 5 of FLAGS, the "new key" signal. When a code
from the keyboard is required, LAST K will be read by 10A8
KEY INPUT, set as the input routine for channel K by 15AF
initial channel information.
The key value, shift byte, main code and final code are
all various kinds of_key_codes. In the notes this term usually
means the final code.

NB 1._Scanning_the_keyboard for the key value.


Each of the eight input ports listed below gets input
from one_line_of_keys - actually a half-line, containing five
keys each. See the old Spectrum handbook, page 160, or the
Plus 2 handbook, page 139.

FEFE CAPS SHIFT -> V FE = 11111110

411
FDFE A -> G FD = 11111101
FBFE Q -> T FB = 11111011
F7FE 1 -> 5 F7 = 11110111
EFFE 0 -> 6 EF = 11101111
DFFE P -> Y * DF = 11011111
BFFE ENTER -> H BF = 10111111
7FFE SPACE -> B 7F = 01111111

* Misprinted "P to 7" in the old handbook.


Each half-line is scanned with an_initial_key_value, 2Fh
for the first, decrementing to 28h for the last. If a key is
being pressed, the port will receive a byte which is all ones
except for one of the bits zero -> 4, depending on its place in
the line: bit zero for the outer key down to bit 4 for the one
nearest the middle of the keyboard. The following table shows
the hex number received for each key, its last five_key_bits in
binary, and the initial key value IKV for each half-line. The
first three key bits may have any values.

Port IKV Key bits


FEFE: 2F 11110b/FE 11101b/FD 11011b/FB 10111b/F7 01111b/EF
C SHIFT Z X C V
FDFE: 2E 11110b/FE 11101b/FD 11011b/FB 10111b/F7 01111b/EF
A S D F G
FBFE: 2D 11110b/FE 11101b/FD 11011b/FB 10111b/F7 01111b/EF
Q W E R T
F7FE: 2C 11110b/FE 11101b/FD 11011b/FB 10111b/F7 01111b/EF
1 2 3 4 5
EFFE: 2B 01111b/EF 10111b/F7 11011b/FD 11101b/FB 11110b/FE
6 7 8 9 0
DFFE: 2A 01111b/EF 10111b/F7 11011b/FD 11101b/FB 11110b/FE
Y U I O P
BFFE: 29 01111b/EF 10111b/F7 11011b/FD 11101b/FB 11110b/FE
H J K L ENTER
7FFE: 28 01111b/EF 10111b/F7 11011b/FD 11101b/FB 11110b/FE
B N M S SHIFT SPACE

Later models of the Spectrum have extra keys, but use


the same key bits: the extra keys merely duplicate the "old"
keys with simultaneous shift operation.
Now 0281 KEY BITS reduces the initial key value by 8 for
each bit starting from the right till the zero bit is found,
resulting in the_key_values, also sometimes
called_key_numbers,

412
shown below: these numbers, from 00 = "B" to 26h/40d = "A",
are the order in which the keys appear in the main key table
(a) at 0205

"1" "2" "3" "4" "5" "6" "7" "8" "9" "0"
24 1C 14 0C 04 03 0B 13 1B 23

"Q" "W" "E" "R" "T" "Y" "U" "I" "O" "P"
25 1D 15 0D 05 02 0A 12 1A 22

"A" "S" "D" "F" "G" "H" "J" "K" "L" ENTER
26 1E 16 0E 06 01 09 11 19 21

"Z" "X" "C" "V" "B" "N" "M" E-MOD SPACE


1F 17 0F 07 00 08 10 18 20

If_no_key is being pressed, the key value is FF. A


separate_shift_byte is also returned: FF for_no_shift, 27h for
_caps_shift, 18h for_symbol_shift; see shift keys.

NB 2. Keyboard_delay_period; also discussed under 02BF


KEYBOARD
When a main code has been obtained by 031E K TEST, it
is checked for its_repeat_status by 02D1 K CH SET against the
first byte of each set in 5C00 KSTATE. If it matches either, this
is a key repeat; if not, it is a new key, and the main code is put
in the first byte of whichever set is free.
A "new key" value won't be accepted from the keyboard
by 02D1 K CH SET until one of the four-byte sets in 5C00
KSTATE is free; the_five-call_counter in each of these sets is
decremented
in 02C6 K ST LOOP on each interrupt, and it takes five
interrupts, 0.1 second, to free the set, counted from the last
keystroke but one.
A repeat key won't be accepted until the value in 5C09
REPDEL has been counted down in 0310 K REPEAT for the
first repeat, or the value in 5C0A REPPER for any further
repeats.
They are in units of 1/50th second interrupts. 5C09 REPDEL
and 5C0A REPPER are set on start-up or NEW in 1219 RAM
SET, 5C09 REPDEL at 23h/35d interrupts = 0.7 secs and 5C0A

413
REPPER at five interrupts = 0.1 sec; they can be changed by
POKing the system variables, but they aren't otherwise
changed by ROM.
The new key delay can be changed from m/c, but it is
more difficult: poke FF into 5C00 KSTATE0 and 5C04
KSTATE4, freeing both sets, to abolish the delay, or poke
some other number than 5 into 5C01 KSTATE1 and 5C05
KSTATE5 if they are free. These pokes must be done every
time KEYBOARD is called.
For a repeat keystroke, the five-call counter is reset
to five on each interrupt, so it isn't decremented.

NB 3. Conversion of main code to_final_code; also discussed


under K DECODE
0333 K DECODE uses the five_key_tables starting at 022C
- not table (a), main keys, which is used by 032C K MAIN in
031E
K TEST - to convert from the initial key value to the final code
for all characters except
21h, 23h -> 29h, 40h, 5Fh (digits with symbol shift);
80h -> 8Fh (graphics forms);
90h -> A4h (user-defined graphics);
10h -> 17h (the embedded colour controls);
E6h -> FFh (tokens, NEW -> COPY);
0Dh, 0Eh, 20h (ENTER, E mode and SPACE).
These exceptions are calculated directly from the
initial key values in various parts of the routine starting at
0333 K DECODE.
Introduction general description; only form of input
0038 MASK INT keyboard scanning on each interrupt
0048 KEY INT calls KEYBOARD to scan keyboard
028E KEY SCAN returns key number 00 -> 27h
0296 KEY LINE half-line (five keys) checked on each loop
029F KEY 3KEYS no action if three keys pressed
02A1 KEY BITS check for two or three keys
02AB KEY DONE ok for one key or two with shift
02BF KEYBOARD get key value and decode to final code

414
02D1 K CH SET key held down gives repeat
02F1 K NEW ready to decode main code
0308 K END final code into LAST K
0310 K REPEAT key repeat timing
031E K TEST decodes key number to main code
032C KEY MAIN main code found from table (a)
0333 K DECODE decodes main code to final code
0341 K E LET checks shift to choose table (b) or (c)
034A K LOOK UP indexes into table for final code
034F K KLC LET letters with sym shift, or main codes
0364 K TOKENS tokens are main code + A5h
0367 K DIGIT picks table (f ) for digits with sym shift
0389 K GRA DGT table (d) for GRAPHICS and DELETE
039D K KLC DGT digit main codes decoded to final code
0970 SA CONTRL awaits keystroke
0C88 PO SCR 2 keystroke fetched by 15D4 WAIT KEY
0D6E CLS LOWER bit 5 TV FLAG: no clear after keystroke
0F38 ED LOOP loop to handle each keystroke
0F6C ED CONTR second code fetched
10A8 KEY INPUT new code fetched and signalled
10FA KEY CODE handles control codes
1219 RAM SET initialises delay mechanisms
1303 MAIN 4 resets FLAGS/5 to signal ready for new key
15DE WAIT KEY1 flags for no-key
1F3A PAUSE checks FLAGS for keystroke
1F49 PAUSE 2 checks bit 5 of FLAGS
1F4F PAUSE END resets bit 5, no keystroke now
2089 INPUT assigns values entered from keyboard
2634 S INKEY$ gets key value and decodes it
3645 read-in gets code from input other than keyboard

KEYBOARD SUBROUTINE
In the cross-headings of the notes, "KEYBOARD
ROUTINES"
and "KEYBOARD SCANNING SUBROUTINE" are at 028E KEY
SCAN,
"KEYBOARD SUBROUTINE" is at 02BF KEYBOARD.

415
KEY CHAN 1113 (10A8 KEY INPUT)
Jumps from:
1105 KEY DATA

key codes see KEYBOARD SCANNING


In the notes it is usually the final code which is
meant.

KEY CONTR 10FA (10A8 KEY INPUT)


Jumps from:
10A8 KEY INPUT

KEY DATA 1105 (10A8 KEY INPUT)


Jumps from:
10A8 KEY INPUT
10FA KEY CONTR

KEY DONE 02AB (028E KEY SCAN)


(This heading is duplicated at 02AB and 111B)
Exit from:
0296 KEY LINE (028E KEY SCAN)

KEY DONE 111B (10A8 KEY INPUT)


Exit from:
10A8 KEY INPUT, direct and through
1113 KEY CHAN

KEY FLAG 10F4 (10A8 KEY INPUT)


Exit from:
10A8 KEY INPUT through one of:
10DB KEY=M&CL
10E6 KEY MODE

KEY INPUT subroutine 10A8

416
Not part of the KEYBOARD SCANNING system, except
that it checks if a new key has been pressed and reads its code
from 5C08 LAST K.
There is no direct call or jump in ROM, but it is the
normal input address for channel K. Channel K input is called
by 15E6 INPUT AD every fraction of a second from the 15DE
WAIT KEY1
loop, around which the execution endlessly circles while it is
awaiting a new keystroke. Apart from a few calls where any
keystroke at all is accepted, eg after "start tape and press any
key", the only calls to 15D4 WAIT KEY are from ED LOOP and
ED CONTR in the 0F2C EDITOR routine, for the input of
BASIC lines and expressions.
The final key codes as found in 5C08 LAST K and read by
this subroutine are:
00 BRIGHT off, 01 BRIGHT on, 02 FLASH off, 03 FLASH
on,
04 INVERSE off, 05 INVERSE on. It converts these to a pair of
codes, the first being 12h plus half the 5C08 LAST K value, ie
12h BRIGHT, 13h FLASH, 14h INVERSE, and the second the
final bit, ie 00 for off and 01 for on
06 CAPS LOCK; it implements this immediately. The
PRINT comma, which is also code 06, cannot be input as
such by any keystroke.
07 EDIT, 08 -> 0B the cursor moves, 0C DELETE, 0D
ENTER; it leaves these unchanged
0E E mode or 0F GRAPHICS mode; it changes the mode
flag in 5C41 MODE to E/G mode or back to L mode as
required. Again, the number marker, which is also code 0E,
cannot be input by a keystroke.
10h -> 17h PAPER colours and 18h -> 1Fh INK colours; it
outputs two codes, 10h for INK or 11h for PAPER and then the
colour parameter
20h and upwards, the characters and tokens; it makes no
change.
Input parameters: none.
Action: check bit 3 of TV FLAG; the "copy editing" flag.

417
The notes call this bit a "mode flag", which is a little
misleading, because the flag is set by 15D4 WAIT KEY
whenever the waiting loop is entered, whether there has been
a mode change or not, to signal "recopy input to lower screen"
- if it is set call 111D ED COPY to recopy the editing
or input BASIC to the lower screen
- read FLAGS bit 5; the "new key" flag
- if no new key has been pressed return
- (new key pressed) read 5C08 LAST K and reset the "new
key" flag
- if TV FLAG bit 5 is set call 0D6E CLS LOWER to clear
the lower screen; the "clear lower screen" flag. This is quite
unusual, 111D ED COPY completely overwrites the lower
screen display and doesn't require the rather slow screen
clearing
- if the key code is 20h or more jump on to KEY DONE;
all characters, including space and tokens
- (control codes) if it is 10h -> 1F jump on to KEY
CONTR; INK and PAPER colours
- if it is 06 -> 0F jump on to KEY M&CL; editing
controls
- (00/01 BRIGHT, 02/03 FLASH and 04/05 INVERSE) make
an on/off byte, zero/off for the even codes and one/on for the
odd
- halve the 5C08 LAST K value; making 00 BRIGHT, 01
FLASH, 02 INVERSE
- add 12h/18d; 12h/18d BRIGHT, 13h/19d FLASH, 14h/20d
INVERSE, which are the correct control codes
- jump on to KEY DATA.
_10DB_K_M&CL (06 -> 0F): if the code isn't 06 CAPS LOCK,
jump on to KEY MODE
- XOR FLAGS2 bit 3 with 00001000b/08; flop the CAPS
LOCK flag
- jump on to KEY FLAG.
_10E6_KEY_MODE (07 -> 0F): if the code is less than 0E
return; 07 EDIT -> 0D ENTER are left alone

418
- subtract 0D to reduce 0E E mode to 01 and 0F
GRAPHICS to 02
- compare the result with the MODE flag in 5C41 MODE;
they are the values for E and G modes
- load the result into 5C41 MODE anyway
- if the comparison showed a change in MODE jump on to
KEY FLAG
- if there was no change load zero into 5C41 MODE; E and
G modes are both on/off switches, so if the mode is already E
mode, make it L mode, and similarly for G.
_10F4_KEY_FLAG: set bit 3 of TV FLAG and return with NC;
the "copy edit" flag will make KEY INPUT recopy the input line
on the next turn of the loop to show the new cursor.
_10FA_KEY_CONTR (code 10h -> 17h for a PAPER colour or
18h -> 1Fh for an INK colour): AND the code with 00000111b/
07;
to separate off the last three bits, which are the colour
number
- make a control code byte 10h; INK
- if bit 3 of the given code is set jump on to KEY DATA;
18 -> 1Fh INK
- (10 -> 17h PAPER) make the control code byte 11h.
_1105_KEY_DATA (whether execution came from KEY
INPUT or KEY CONTR, the A register holds a colour control
code 10h INK ->
15h OVER and the C register its parameter): park the control
code in 5C0D K DATA for the moment
- make a new input address KEY NEXT
- jump on to KEY CHAN, which makes KEY NEXT
temporarily the input address of the current channel K; 15D4
WAIT KEY will
return with the control code in A. 0F38 ED LOOP, when it
finds this code, immediately calls WAIT KEY again for the
parameter:
this time 15E6 INPUT AD will jump to the temporary input
address KEY NEXT.

419
_110D_KEY_NEXT (the label is only used for the temporary
input address put in channel K by KEY CHAN) recover the
parameter code from 5C0D K DATA
- make the normal input address 10A8 KEY INPUT to be
put back in the channel.
_1113_KEY_CHAN: put a pointer on the channel input
address
- load it with either KEY NEXT supplied by KEY DATA, or
KEY INPUT supplied by KEY NEXT. [For the double
somersault performed with the input channel address in KEY
DATA and following, cf the similar acrobatics with the output
address in 0A75 PO 2 OPER and 0A7A PO 1 OPER.]
_111B_KEY_DONE: return with carry; so as to escape from
the WAIT KEY1 loop.
Exit: RET, from KEY INPUT, KEY FLAG or KEY DONE.
Output parameters: A holds the final code from 5C08
LAST
K, adjusted if necessary for control codes
- 5C41 MODE has been changed if appropriate
- bit 5 of FLAGS is zero; showing "not dealing with new
key"
- the carry flag indicates "new code found", except when
no key has been pressed or when only the cursor has
changed.
Set in channel K by:
0D94 CL CHAN
110D KEY NEXT
1219 RAM SET (from 15AF initial channel information)
Rems:
101E ED IGNORE skips two inputs - ineffective
1105 KEY DATA temporarily changes input address

KEY INT 0048 (0038 MASK INT)


Exit from:
0038 MASK INT (twice)

KEY LINE 0296 (028E KEY SCAN)

420
Jumps from:
02AB KEY DONE

KEY MODE 10E6 (10A8 KEY INPUT)


Exit from:
10DB KEY M&CL (10A8 KEY INPUT)

KEY NEXT 110D (10A8 KEY INPUT)


No calls or jumps from ROM; temporarily set as the input
address of channel K by 1105 KEY DATA; the effect is that two
character codes are input on one scan of the keyboard.

key number, key repeat period, key repeats see KEYBOARD


SCANNING

KEY SCAN subroutine 028E


Called fifty times per second by the KEYBOARD
SCANNING
system to scan the keyboard by reading the eight key input
ports; if any keys are being pressed it picks up the key bits
and converts them to a key value. See the summary under
KEYBOARD SCANNING, especially NB 1.
The byte input from the port has ones in its last five
bits indicating keys which aren't being pressed in the
corresponding half-line, and zeroes corresponding to pressed
keys. Reverse the byte - zeroes to ones and vice versa - and
AND it with 00011111b/1Fh; this makes zero if all the key bits
were ones, but if not, at least one key is being pressed and the
non-zero key bits indicate the position of pressed keys in this
half-line.
Each port is read with an initial key value, 2F for the
first port, decreasing by one for each successive port. Take 08
from the initial key value; rotate the key bits right and take
away 08 again, and repeat till a set bit is found, corresponding
to a zero bit in the original key bits. This reduces an initial key
value
2F successively to 27, 1F, 17, 0F, 07

421
2E successively to 26, 1E, 16, 0E, 06
2D successively to 25, 1D, 15, 0D, 05
... ... ... ... ... ... ... ... ...
28 successively to 20, 18, 10, 08, 00
- all the 28h/40d values from 00 to 27, one way or another.
The rotation of the key bits produces zero if only one
key was being pressed, but otherwise continue rotating the
key bits and subtracting 08 from where it left off, to find
another key value from the same line of keys.
A shift byte and a "last key" byte are both set to FF
initially; each time a pressed key is found, increment the shift
byte, and return if the result isn't zero; otherwise copy the last
key byte to the shift byte.
The first time, the shift byte has its initial FF, so
there is no return. The second time, it has the last key value,
which was also FF, so there is still no return. Only if three
keys are being pressed can the result of incrementing the last
key value be non-zero and force a return with NZ.
On exit from the loop, at most two keys being pressed:
- if no key was found pressed the last key value is FF
- if one or two were pressed it is the key value of the
last one found
- if no key or one key was pressed the shift byte is FF
- if two were pressed it is the key value of the first of
the two found
- if the two keys pressed are both shifts the last key
byte is symbol shift and the shift byte is caps shift; caps
shift is bit zero of the first key bits read, so it is read
first, and symbol shift is read last except for B, N and M.
Four tests are made:
Test 1: if the shift byte is FF return with zero; this
signals "no shift", and the last key byte is the key value or FF
for no-key.
Tests 2 and 3: if the shift byte is 27h caps shift or
18h symbol shift return with zero; again the last key byte is
the key value or FF for no-key.
Test 4: exchange the shift and last key bytes and if the

422
last key byte is symbol shift return with zero; this is either
symbol shift B, N or M or caps + symbol shift together, E
mode.
Input parameters: none.
Action: make the initial key value byte 2F
- make the key value and shift byte both FF
- make the port address FE
- make the loop counter FE; on each turn of the loop the
counter is shifted left with zero into its lo bit, so its hi bit
becomes zero when eight turns of the loop have been made.
Its successive values are
11111110b/FE
11111100b/FC
... ... ...
10000000b/80
00000000b/00
_0296_KEY_LINE: take the port input, reverse it, and AND
it with 00011111b/1Fh
- if the result is zero jump on to KEY DONE; no key
pressed on this half-line, loop again.
_029F_KEY_3KEYS (at least one key is being pressed):
increment the shift byte
- if the result isn't zero return; a return with NZ
signals "three keys" or "two non-shift keys", see above.
_02A1_KEY_BITS (valid keystroke): take 08 from the
initial key value and rotate the key bits right, with zero into
the hi bit and the lo bit into carry
- if the lo bit was zero jump back to KEY BITS
- (set bit found; there must be one, otherwise KEY LINE
would have jumped on to KEY DONE. The initial key value has
been counted down to a key value for this key) put the
previous key value in the shift byte and the new key value in
the last key byte; if this is the first key found the shift byte will
get FF
- if the rotated key bits aren't zero yet jump back to
KEY 3KEYS to find another key value.
_02AB_KEY_DONE (no more keys pressed on this port):

423
decrement the initial key value; 2F, 2E, ... 28
- shift the loop counter left with zero to its lo bit;
coming from the carry flag, which became NC either from
KEY LINE or from KEY BITS
- if its hi bit wasn't zero, loop back to KEY LINE for
the next input port
- (all ports read) if the shift byte is FF return with
Z; Test 1, the last key byte is the key value or FF for no-key
- if the shift byte is 27h return with Z; Test 2
- if the shift byte is 18h return with Z; Test 3
- exchange shift/last key bytes; E to A, D to E, A to D
- if the last key byte was 18h return with Z; Test 4.
Exit: RET, from KEY 3KEYS or KEY DONE.
Output parameters: D holds the shift byte: FF for no
shift, 27h for CAPS SHIFT, 18h for SYMBOL SHIFT
- E holds the key value: FF if no key found, otherwise
as in the table at NB 1 in KEYBOARD SCANNING.
- the Z flag indicates that a suitable key reading has
been found, or no key is being pressed; NZ that three keys, or
two non-shift keys, are being pressed.
Called from:
02BF KEYBOARD
2634 S INKEY$

key set see KEYBOARD SCANNING, 5C00 KSTATE

keystroke (key) see KEYBOARD SCANNING, 028E KEY SCAN

key tables see 0333 K DECODE, KEYBOARD SCANNING,


tables

key values see KEYBOARD SCANNING

KEY 3KEYS 029F (028E KEY SCAN)


Exit from:
028E KEY SCAN through one of:
0296 KEY LINE

424
02A1 KEY BITS

KEY=M&CL 10DB (10A8 KEY INPUT)


Jumps from:
10A8 KEY INPUT

K GRA DGT 0389 (0333 K DECODE)


Exit from:
0333 K DECODE through
0367 K DIGIT

K KLC DGT 039D (0333 K DECODE)


Exit from:
0333 K DECODE through
0367 K DIGIT

K KLC LET 034F (0333 K DECODE)


Exit from:
0333 K DECODE

K LOOK UP 034A (0333 K DECODE)


Exit from:
0333 K DECODE through one of:
0341 K E LET (twice)
034F K KLC LET
0367 K DIGIT
0389 K GRA DGT (twice)
039D K KLC DGT

K MAIN 032C (031E K TEST)


Exit from:
031E K TEST

K-mode see 5C41 MODE

K NEW 02F1 (02BF KEYBOARD)


Jumps from:

425
02D1 K CH SET

K REPEAT 0310 (02BF KEYBOARD)


Exit from:
02BF KEYBOARD through
02D1 K CH SET (twice)

KSTATE system variable 5C00


Bytes: 8
Eight bytes in two sets, KSTATE0-3 and KSTATE4-7. They
control the delays between keystroke acceptances, and are
only used in 02BF KEYBOARD, see index entry for details:
bit 0 is the main code
bit 1 is the 5-call counter
bit 2 is REPDEL/REPPER
bit 3 is the final code.
Can be freely used as spare memory when these routines
aren't called - ie when the maskable interrupt is disabled. The
first and fifth bytes should be loaded with FFh before enabling
the interrupt.
Written by:
02C6 K ST LOOP (decrements 5-call, sets free if zero)
02F1 K NEW (loads three bytes)
0310 K REPEAT (resets 5-call, decrements delay)
1219 RAM SET (sets both sets free)
Read by:
02BF KEYBOARD (picks up address of KSTATE0)
02C6 K ST LOOP (checks first bit for "free")
02D1 K CH SET (checks main code with both first bytes)
0310 K REPEAT (reads final code)

K ST LOOP 02C6 (02BF KEYBOARD)


Jumps from:
02D1 K CH SET

K TEST subroutine 031E


Finds the main code from the key value and shift byte.

426
The key value found by 028E KEY SCAN indexes the place of
the key in the table at 0205; from 00 for "B" to 26h for "A",
see the table in NB 1 of KEYBOARD SCANNING. The main
code is the value found at that place; it is the character code
of the capital letter or digit belonging to that key.
On entry,
the shift byte will be either FF no shift, 27h caps shift
or 18h symbol shift
the key value will be either zero -> 27h, which includes
both shift values, or FFh for "no key".
The two values cannot be the same, unless they are both
FF, signalling "no key, no shift".
If the key value is 27h caps shift, the shift byte must
be FF: because the key value always has the value which was
read last, and caps shift is read first of all. This is why the key
table at 0205 has no table entry for this value.
If the key value is 18h symbol shift and the shift value
27h caps shift, the subroutine returns with 0E from the main
code table. The notes refer to this sometimes as the "symbol
shift" code and sometimes as the "code for either shift"; but it
can only be returned as a main code if_both caps and symbol
shift were being pressed, and is therefore the main code for
Extend mode on/off; see K DIGIT in the entry for 0333 K
DECODE.
Input parameters (they are the same as the output
parameters of 028E K SCAN, apart from the flags which may
have changed): D holds the shift byte, E the key value.
Action: if the key value is FF no key or 27 caps shift
return; this must be "caps shift and no key", see above
- if the key value isn't 18h symbol shift jump on to K
MAIN
- (key value is 18h symbol shift) if the shift byte is
FF no-shift return; you still have NC from the "CP 18h" a
couple of lines back, so this signals "no key". Key value symbol
shift and shift byte no-shift must be "symbol shift and no key".
_032C_K_MAIN: index into the main key table at 0205 with

427
DE holding the key value; if the key value is 18h symbol shift
the shift byte must be caps shift, and the table returns 0E for
E mode
- put the byte from the indexed address in A and set the
carry.
- return.
Exit: RET, from K TEST or K MAIN.
Output parameters: A holds the main code
- B holds the shift byte
- the carry flag shows NC if no key was pressed or only
a shift key.
Called from:
02D1 K CH SET
2634 S INKEY$

K TOKENS 0364 (0333 K DECODE)


Exit from:
0333 K DECODE through
034F K KLC LET

K 8 & 9 0382 (0333 K DECODE)


Exit from:
0333 K DECODE through
0367 K DIGIT

K @ CHAR 03B2 (0333 K DECODE)


Exit from:
0333 K DECODE through
039D K KLC DGT

428
L

L ADD$ 2BAF (2AFF LET)


Exit from:
2AFF LET through
2B72 L DELETE$

LAST 386C (3851 to-power)


Appropriately named; it is the last routine in the ROM!
Exit from:
384A sqr and
3851 to-power through one of:
385D XISO
386A ONE

last entry
This term is used in 1219 RAM SET to indicate the bottom
of the machine and GO SUB stacks. See GO SUB stack.

LAST K system variable 5C08


Bytes: 1
Holds the final code of the last key read from the
keyboard, see KEYBOARD SCANNING, 028E KEY SCAN. This is
picked up by the input routine of channel K, see 10A8 KEY
INPUT. Often useful in m/c programs; since reading the
keyboard is handled automatically by the interrupt routines,
LAST K will always hold the latest keyboard reading at all
times. If the interrupt is disabled, RST 38h will put it there, or
the simplified WAIT KEY routine
LD (IY-50),0 ;LAST K
LOOP RST 38h
LD A,(IY-50)
AND A
JR Z,LOOP

429
will wait for a key input.
Written by:
0308 K END
Read by:
10A8 KEY INPUT
Rems:
02BF KEYBOARD passes value if repeat status allows
02F1 K NEW gets ready to pass value

last value on calculator stack see CALCULATE

L CHAR 2B3E (2AEF LET)


Jumps from:
auto (twice)

LD BLOCK subroutine 0802


Reports errors on LOAD/VERIFY/MERGE of a data block.
Input parameters: same as 0556 LD BYTES, see below.
Action: call LD BYTES and if this doesn't set the carry
report "Tape loading error".
Exit: RET.
Output parameters: carry set.
Called from:
08B6 ME CONTRL
Exit from:
0800 VR CONT 3
084C LD DATA 1
08AD LD PROG 1
Rems:
0556 LD BYTES called by to LOAD or VERIFY data block
LD BREAK 056B (0556 LD BYTES)
Exit from:
0556 LD BYTES through one of:
056C LD START
0574 LD WAIT
0580 LD LEADER
058F LD SYNC

430
LD BYTES subroutine 0556
Reads a string of bytes from cassette tape. Used to read
both headers and program/data blocks.
Disables the maskable interrupt; because repeated calls
to the KEYBOARD routines would interfere with the exact
timing of the tape reading routines, and also because the EAR
port used to read the tape is shared with the keyboard input.
The BREAK/SPACE key is frequently checked, but other keys
are ignored throughout the routine.
The first part of the routine, up to the middle of LD
SYNC, is concerned with identifying the leader pulses on the
tape; see 04C2 SA BYTES. There should be a series of regular
pulses with 2168d T states of "off" followed by 2168d T states of
"on", ie 2168d T states between each on/off "edge"; the series
lasting 2 seconds for a header or 5 for a data block, followed
by a shorter "sync pulse".
When a pulse is detected, the routine waits for about a
second, then checks again; if pulses are still being received,
it counts 256d pulses and then, still checking every pulse,
starts to look for the sync pulse. This begins with a shorter
"off" period of only 667d T states, and then has an "on" of 735d
T states. If the short "off" is found but the "on" is too long,
"Tape loading error" is reported. The sync pulse is the signal
for the routine to stop checking leader pulses and start
reading the bytes of the saved file.
The bytes are read one bit at a time by the LD 8 BITS
loop: a long on/off pulse, more than 2400d T states, signals
"one" and a short pulse signals "zero". As each byte is read, it
is XORed with a parity matching byte, set initially at zero. Cf
0505 SA LOOP P; every byte of the data was XORed with the
parity byte, which started at zero, and the final result put on
tape at the end of the data block. Now each byte is XORed
with the parity matching byte, including the parity byte at the
end, and if the result isn't zero something has gone wrong.
Not an infallible test, but quite a good one.
Detection of pulses is by calls to 05E7 LD EDGE 1 for a

431
half pulse or or 05E3 LD EDGE 2 for a whole on/off pulse, see
their index entries; they look for an edge, or two edges, within
the time allowed by the delay counter in the B register, which
is counted_upwards to 256d. They return with
- NC and Z if time ran out
- NC and NZ if BREAK/SPACE is pressed; NC is referred to
in the notes and this entry as the "Tape error" signal, but it
also signals a BREAK. The zero flag makes sure the "BREAK"
report is given instead of the "Tape error" report when
appropriate. Throughout this routine the SPACE key works as
a BREAK, with or without caps shift
- C if edge(s) found before time ran out.
They also change the border colour, with the last three
bits of the output going to port FE; for leader pulses it
changes the border from 101b/05 CYAN to 010b/02 RED and
back on every edge. After the sync pulse has been received
the colour is XORed with 00000011b/03, making the colours
110b/06 YELLOW and 001b/01 BLUE.
For checking pulses, the time allowance in T states is
as explained in the index entry for 05E7 LD EDGE 1.
A leader pulse should take 2 * 2168 = 4336d T states
from "off" to "off"; for leader pulses the counter is set to 9C/
156d, allowing
15440 - 59 * 156 = 6236d T states
as a maximum time for the on/off pulse, and on return from
LD EDGE 2 the counter is checked, and the double pulse
rejected if it took less than C7 - 9C = 2Bh/43d turns of the
counter, which is
59 * 43 + 880 = 3417d T states
as a minimum.
For the sync pulse, the "off" should be only 667d T
states, and "off" and "on" together should be 667 + 735 = 1402d
T states. The counter for the "off" half-pulse is set to C9h/201d,
allowing
15415 - 59 * 201 = 3556d T states
as a maximum; any longer "off" will make a "Tape error"
report.

432
On return from LD EDGE the "off" pulse is rejected if it took
more than D4 - C9 = 0Bh/11d turns of the counter, which is
404 + 59 * 11 = 1053d T states
and the search for a sync pulse is resumed.
When the "off" sync pulse has been accepted, the end of
the "on" pulse is looked for without resetting the counter, so it
must arrive within a total of 3556d T states from the start of
the "off", plus a few for the recall of the LD EDGE
subroutine, or "Tape error" will be reported.
When loading bits from the saved file, a double pulse
longer than 2400 T states counts as a one, a shorter pulse as a
zero. The delay counter is set at B0h/176d, allowing
15446 - 59 * 176 = 5062d T states
maximum, and checked against CB on return; if the length of
the pulse was less than CC - B0 = 1Ch/28d turns of the counter,
which is
830 + 28 * 59 = 2482d T states
the bit is counted as zero.
Input parameters: DE holds the length, the number of
bytes to be read; 11h/17d for a header, otherwise a number
read from the header
- A holds the header/data index, 00 for a header, FF
otherwise.
- IX holds the load pointer; the address to which the
first byte read from tape should be loaded, or in the case of
VERIFY, with which it should be compared
- the carry flag signals C for LOAD, NC for VERIFY.
Action: increment D; no way this can make zero, so it
makes an NZ flag signalling "first byte"
- save the header/data index in the AF' registers with
the LOAD/VERIFY flag and the first byte flag
- restore D
- disable the maskable interrupt
- output 00001111b/0F to port FE; this makes the border
WHITE and turns off the MIC socket, see ports
- stack the return address 053F SA/LD RET
- input from port FE; bit 6 will be zero if there is an

433
incoming pulse from EAR
- shift the bit right; now bit 5 gives the signal
- AND with 00100000b/20h and OR with 00000010b/02;
the result is 22h if there is no pulse, 02 if there is one. This is
the first output byte for LD EDGE, sent to port FE if it finds an
edge. The 010b in the last three bits turns the border RED, bit
5 is used for checking whether the edge has been changed
- set the Z flag.
_056B_LD_BREAK: if the flag shows NZ, return; this exit
point is repeatedly used when the BREAK/SPACE key might
have been pressed producing NZ.
_056C_LD_START: call 05E7 LD EDGE 1 to look for a half-
pulse; on the first call the delay counter may have any value
- if no pulse was found or BREAK was pressed jump back
to LD BREAK; the delay counter is now zero, so the timing
limit for the half-pulse is 15418d T states
- (a pulse received) make a timer byte of 0415h.
_0574_LD_WAIT: as B is zero, make 100h turns of the
DJNZ; 0Dh T states per turn
- decrement HL and repeat till HL is zero; the decrement
and zero check take 1Ah T states, so the whole waiting loop
takes
415h * (1Ah + 100h * 0Dh) = 415h * D1Ah
= 1045d * 3354d
= 3,504,930 T states
- call LD EDGE 2 with zero in the delay counter;
allowing maximum 15446d T states for the on/off pulse this
time
- if there aren't two edges, jump back to LD BREAK to
start all over again.
_0580_LD_LEADER: put 9C in the delay counter to check
for a leader pulse
- call 05E3 LD EDGE 2 to look for an on/off; if it
doesn't come, jump back to LD BREAK and start again
- if the delay counter is less than C5h jump back to LD
START; it has gone up by less than C5 - 9C, this isn't a proper
pulse, start again

434
- increment H; it was zero on exit from LD WAIT
- if it isn't yet zero loop back to LD LEADER.
_058F_LD_SYNC (it is zero again; 256d pulses have been
checked. This only takes 512 * 2168 = 1,110,016d T states, about
1/3 of a second): call LD EDGE 1 with C9 in the delay counter
to look for the "off" half-pulse of the sync pulse
- if it returns with NC jump back to LD BREAK and start
all over again; no edge was found
- (an edge was found) if the delay counter is more than
D3 jump back to LD SYNC to test the next pulse; it has gone
up by more than D3 - C9, the pulse is too long for a sync pulse
- (sync pulse "off" found) call LD EDGE 1 again; the
second half-pulse of the sync pulse must arrive before the
delay counter gets to zero. If it doesn't, return with a "Tape
error" signal
- (sync pulse received and checked) XOR the colour with
00000011b/03, making the colours 110b/06 YELLOW and
001b/01 BLUE
- zero a parity matching byte
- set the delay counter to B0h to read bits
- jump into the byte-loading loop at LD MARKER.
_05A9_LD_LOOP (the loop returns here with the L register
holding each new byte loaded from the tape): get AF', which
was saved at the start with the header/data index, a LOAD/
VERIFY flag, and a "first byte" flag
- if this is the first byte jump on to LD FLAG
- if this is verification jump on to LD VERIFY
- (this is a LOAD) put the byte just read into RAM at
the load pointer
- jump on to LD NEXT.
_05B3_LD_FLAG (the first byte, the header/data flag): if
it doesn't match the header/data index just got in A return
with the "Tape error" signal
- increment the length counter; the flag isn't recorded
and doesn't count in the length
- jump on to LD DEC.
_05BD_LD_VERIFY: (verification) compare the byte from

435
tape with the byte at the load pointer
- if they don't match, return with the "Tape error"
signal.
_05C2_LD_NEXT: move on the load pointer.
_05C4_LD_DEC: decrement the length counter
- save the flags in F'; A doesn't matter now
- reset the delay counter; why now B2h instead of B0h?
But it makes little difference
_05C8_LD_MARKER: put one in the lo bit of an "8-bit loop”
counter; see index entry after end of alphabet
_05CA_LD_8_BITS: call LD EDGE 2 to look for the on/off
bit pulse
- if two edges aren't found return with the "Tape error"
signal
- if the delay counter is more than CB set the carry
flag; long pulse
- rotate the loop counter, with the carry flag into its
lo bit and its hi bit into carry
- reset the delay counter to B0
- if the loop counter still had a zero hi bit loop back
to LD 8 BITS
- (eight bits read; if the hi bit was one, it must be
the original lo bit having worked its way to hi bit through
eight turns of the loop) XOR the byte read from the tape with
the parity matching byte
- check the length counter
- if it isn't zero jump back to LD LOOP
- (the loading is complete) if the parity matching byte
isn't zero make an NC flag; the "Tape error" signal
- return.
Exit: RETs at:
- LD BREAK; if BREAK pressed
- LD SYNC; if the sync pulse fails
- LD VERIFY; if verification fails
- LD 8 BITS; if the bit pulses fail or BREAK is pressed,
or when loading is complete, with the result of testing the
parity byte.

436
All these are indirect jumps to 053F SA/LD RET, the
address stacked at the start of the subroutine. It restores the
original border colour, checks the BREAK key, enables the
interrupt, and gives the BREAK message or returns with a
signal for "Tape error" if appropriate.
Output parameters: the C flag indicates that no error has
been detected
- IX holds the address after the last byte loaded.
Called from:
0767 LD LOOK H
0802 LD BLOCK

LD CH PR 07AD (0605 SAVE ETC)


Jumps from:
07A6 LD NAME

LD CONTRL 0808 (0605 SAVE ETC)


Jumps from:
07AD LD CH PR

LD CONT 1 0819 (0605 SAVE ETC)


Jumps from:
0808 LD CONTRL

LD CONT 2 0825 (0605 SAVE ETC)


Jumps from:
0808 LD CONTRL

LD DATA 082E (0605 SAVE ETC)


Jumps from:
0819 LD CONT 1

LD DATA 1 084C (0605 SAVE ETC)


Jumps from:
082E LD DATA

LD DEC 05C4 (0556 LD BYTES)

437
Jumps from:
05B3 LD FLAG

LD DELAY 05E9 (05E7 LD EDGE 1)


Jumps from:
aut

LD EDGE 1 subroutine 05E7 see also ports, timing, 0556 LD


BYTES
Reads the EAR port, bit 6 of port 7FFE, a prescribed
number of times, looking for an_edge, ie a change from EAR
on ->off or EAR off -> on; these are the two_edge_types. Port
7FFE also supplies a reading of the last half-row of keys on the
keyboard: its lo bit is zero if SPACE is being pressed, and if
CAPS SHIFT is also being pressed this means a BREAK. In fact
the routine doesn't check CAPS SHIFT, and either SPACE or
BREAK keys will produce a BREAK.
The routine contains two timed loops, a "waiting loop"
LD DELAY and a_sampling_loop LD SAMPLE. The waiting loop
is always the same: the first 21d turns of the loop each take
16d T states, but the last turn only takes 11d, because JR Z
takes longer to jump than not to jump. To this must be added
7 for the initial load of A and 4 for the AND A which follows,
total
21 * 16 + 11 + 7 + 4 = 358d T states.
The number of times the sampling loop is turned is set
by the timing constant in the B register when the routine is
called. This constant is counted_upwards from its initial value,
and if it reaches zero the subroutine returns signalling "no
edge found".
Each turn of the loop takes 59d T states if it doesn't
RET for time-up or BREAK, except the last which only takes
54d.
The notes count this wrong as 58d.
The "overhead" operations which follow the loop take 51d
T states, but allowing 5 for the JR Z on leaving the loop cuts
this down to an effective 46d.

438
So the time taken for a call to LD EDGE 1 if it_doesn't
find an edge is 358d waiting, plus 59d for each of 255d - B
turns of the loop, not counting the last, plus 15d for INC B and
RET Z when B reaches zero, ie
358 + 59 * (255 - B) + 15 = 15418 - B * 59d T states
If it_does find an edge, it returns with B still not
counted up to zero; call this value b2. B2 indicates the length
of time taken by the subroutine to find the edge: the time is
358d waiting, plus 59d for each of the b2 - B turns, plus the
overhead 46d, ie
358 + 59 * (b2 - B) + 46 = 59 * (b2 - B) + 404d T states.
Input parameters: B holds the timing constant to control
the number of turns made in the LD SAMPLE sampling loop;
FF makes no loops, 00 makes FFh/255d loops
- C holds the "last edge"; the present border colour in
bits 3 -> zero, and its bit 5 is 1 if the last reading of port
7FFE showed EAR on, zero if it was off.
Action: set a counter at 16h/22d for the waiting loop.
_05E9_LD_DELAY: decrement the counter
- if it isn't yet zero jump back to LD DELAY
- clear the carry flag.
_05ED_LD_SAMPLE: increment the timing constant
- if it is zero return; time is up
- read port 7FFE
- if the lo bit is zero return; the BREAK/SPACE key is
pressed
- compare the port input with the last edge
- if there has been no change loop back to LD SAMPLE
- (there is an edge) ones complement the last edge; 22h
-> DDh, or 21h -> DEh, or vice versa
- AND with 00000111b/07 to extract its last three bits
as the border colour; 02 RED or 05 CYAN or 01 BLUE or 06
YELLOW
- OR with 00001000b/08; sets bit 3 to turn MIC off
- output the colour to port FE to change the border
- set the carry and return.
Exit: RET, in three places from LD SAMPLE.

439
Output parameters: B holds zero if no edge was found, or
records how many times the port was read until an edge was
found by the number of increments from its entry value
- C still holds the border colour and pulse on/off
state, reversed from the entry value if an edge was found
- HL and DE are unchanged
- carry signals "edge found"
- NC and Z flags signal "no edge", NC and NZ signal
BREAK.
Called from:
056C LD START
058F LD SYNC (twice)
05E3 LD EDGE 2
Rems:
05E9 LD DELAY delay before entering loop
05ED LD SAMPLE gives timing of loop

LD EDGE 2 subroutine 05E3 (05E7 LD EDGE 1)


A double call of the LD EDGE routine: calls LD EDGE 1,
returns if no edge is found, otherwise exits through LD EDGE
1 again. It therefore can find both the start and the end of a
pulse from tape. The CALL and RET NC add 22d T states to
the overall time, or 28d if not even one edge is found; so using
the same calculations as above,
if LD EDGE 2 finds two edges the time is indicated by
59 * (b2 - B) + 808 + 22 = 59 * (b2 - B) + 830d T states
if it only finds one edge it takes
15418 - B * 59 + 22 = 15440 - B * 59d T states
if it finds no edge at all it takes
15446 - B * 59d T states
Called from:
0574 LD WAIT
0580 LD LEADER
05CA LD 8 BITS

L DELETE$ 2B72 (2AFF LET)


Exit from:

440
2B66 L EXISTS (2AFF LET)

LD FLAG 05B3 (0556 LD BYTES)


Exit from:
05A9 LD LOOP (0556 LD BYTES)

LD LEADER 0580 (0556 LD BYTES)


Jumps from:
auto

LD LOOK H 0767 (0605 SAVE ETC)


Jumps from:
auto
078A LD TYPE
07AD LD CH PR
Rems:
0558 LD BYTES called from 076E in LD LOOK H

LD LOOP 05A9 (0556 LD BYTES)


Jumps from:
05CA LD 8 BITS

LD MARKER 05C8 (0556 LD BYTES)


Jumps from:
058F LD SYNC

LD NAME 07A6 (0605 SAVE ETC)


Jumps from:
078A LD TYPE
07AD LD CH PR

LD NEXT 05C2 (0556 LD BYTES)


Jumps from:
05A9 LD LOOP

LD PROG 0873 (0605 SAVE ETC)


Jumps from:

441
082E LD DATA

LD PROG 1 08AD (0605 SAVE ETC)


Jumps from:
0873 LD PROG

LD SAMPLE 05ED (0556 LD BYTES)


Jumps from:
auto

LD START 056C (0556 LD BYTES)


Jumps from:
0580 LD LEADER

LD SYNC 058F (0556 LD BYTES)


Jumps from:
auto

LD TYPE 078A (0605 SAVE ETC)


Jumps from:
0767 LD LOOK H

LD VERIFY 05BD (0556 LD BYTES)


Jumps from:
05A9 LD LOOP

LD WAIT 0574 (0556 LD BYTES)


Jumps from:
auto (twice)

LD 8 BITS 05CA (0556 LD BYTES)


Jumps from:
auto

L EACH CH 2B0B (2AFF LET)


Jumps from:
2BIF L TEST CH

442
leader (save/load) see timing

leading space see 0C0A PO MSG

leading zero see 2DE3 PRINT FP

LEN key (B1) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The K key in E mode without shift produces the function
LEN; it requires one string operand X$, and the value of the
function is the length of the string X$.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B1 first to 02, then to DE.
Since LEN is a string -> numeric function its bit 6 is now reset
making it 9E; priority 10h/16d is added. Code and priority
109E are now pushed on to the machine stack (270D S PUSH
PO) while the string expression following LEN is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from 9E to 1E, the calculator
offset for 3674 len.

len subroutine 3674


Called only from 0028 FP CALC with the literal 1E;
executes the LEN command.
Input parameters: none
- a set of string parameters is last value on the
calculator stack.
Action: call 2BF1 STK FETCH which puts the 5 bytes of the
last value into AEDCB; BC is now the length required.
Exit: into 2D2B STACK BC, which puts the length on the
stack.
Output parameters: none
- the last value is now the length of the string,
replacing the parameters which were there on entry.

length of BASIC line see BASIC line

443
length of expression see strings

length of INPUT LINE see IN VAR 6 in 20C1 IN ITEM 1

length of save/load block see program/data block

length of strings see string parameters

length of timing loop see timing loop

length of variable or array see variables

L ENTER subroutine 2BA6


Copies bytes from one location to another. It differs in
three respects from a plain LDIR instruction:
- HL and DE hold the opposite from their normal inputs
- it checks BC for zero before copying the bytes
- on return, HL holds the original "destination"
unchanged.
The routine can just as well be called at 2BA7, using
the normal inputs, but on output HL will still hold the
"destination".
In ROM, used only to copy FP numbers and strings, but
could be of wide usefulness to m/c programmers. See useful
routines.
Input parameters: HL holds the first address of the
destination
- DE of the source
- BC holds the number of bytes to be copied.
Action: exchange HL and DE
- if BC is zero return
- copy the bytes.
Exit: RET.
Output parameters: if BC was zero returns with Z flag and

444
HL and DE exchanged: otherwise with NZ, HL unchanged, DE
the next address after the last one copied to. In any case BC
zero.
Exit from:
2B59 L NUMERIC
2BA3 L IN W/S

LESS MASK 328A (3214 truncate)


Jumps from:
auto

LESS THAN ZERO OPERATION see 3506 less-0

less-0 subroutine 3506


Called several times in ROM from 0028 FP CALC with
literal 36. Not otherwise called in ROM, but could well be
called direct. Checks if a FP number is negative: returns one
for negative, zero for zero or positive - the opposite of 34F9
greater-0, except both return zero for zero.
Input parameters: HL holds the first address of the FP
number; if called from FP CALC, this will be the last value on
the calculator stack, but in a direct call it could be anywhere.
Action: zero A.
_3507_SIGN_TO_C (the entry point from greater-0, but with
FF in A): XOR the sign byte with A
- rotate the hi bit into the carry; with zero A this
makes carry for negative sign, with A = FF as in greater-0 it
makes carry for positive sign.
Exit: into 350B FP 0/1, which replaces the last value on
the stack depending on the carry: zero for NC, one for C.
Output parameters: all registers unchanged except A. The
C flag shows the result.
Called from:
1DDA NEXT LOOP
2DE3 PRINT FP
36AF int
37A1 ZPLUS

445
37E2 atn
Rems:
34F9 greater-0 exits into, with opposite effect

LET key (F1) see also commands, functions and operators,


KEYBOARD SCANNING
The L key in K mode produces the command LET. The
"LET statement" must include
- a string or numeric variable name, or array name with
specified subscripts: the_variable_in_assignment.
- "="
- an expression, string or numeric; it must match in
status the variable in assignment.
The command gives the variable in assignment the value
of the expression, overwriting any value it had before.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A7A P LET causes jumps to
- 1C1F CLASS 01 to identify the variable in assignment
- back to 1A7B to check that the "=" is there
- 1C4E CLASS 02 which assigns the value to the variable
and moves on to the next statement.
The main executive routine is 2AFF LET, which is called
by CLASS 02 through 1C56 VAL FET 1.
2B72 L DELETE$ example, LET A$(4 TO 8)="abcdefg"

LET subroutine 2AFF


Implements the LET command; also called in
implementing
FOR (1D16 F REORDER), INPUT (219B IN VAR 6) and READ
(1C59 VAL
FET 2)._Assigns_a_value, numeric or string, to a variable in
assignment, whether_existing or_new.
For variable in assignment see LET key above; an

446
existing variable means one already in the variables area, a
new variable is one which only appears in the program listing
or BASIC command.
The last value on the calculator stack shows the value
to be assigned: for a numeric variable this is the actual value,
as an FP number, for a string variable it is a pair of string
parameters showing the address in RAM where the string is to
be found, its length and an indication whether it is an array
element or slice. These string parameters aren't put in the
variables area, as the FP number value of a numeric variable
is;
often they point to a string in the work space, which will be
deleted as soon as the assignment is made, so the actual string
of bytes is copied to the variables area rather than just its
parameters.
If the variable is a new variable, a space must be made
for it; this space must allow for
- the variable letter, which is flagged to show the
variable type, see under variables, but which only occupies
one byte except in the case of "long names", which are only
used for numeric variables
- and five bytes for a numeric variable, or the length of
the string value, plus two length bytes, for a string variable.
A string slice cannot be "new", and nor can a string array
element, as it must have been declared by a DIM statement.
If is an old variable, the old value can simply be
overwritten if it is a numeric variable, a string slice or array
element, since all numeric variable values are five bytes long
and a new string slice or array element must be the same
length as the old. But a_new_simple/single_string
or_complete_simple_string requires the old string to be
deleted and the new one put in the variables area.
Before a string is copied to the variables area, it is
first copied to the work space. There may well be a copy there
already, if the expression to be assigned is the result of
reading a variable or a function; but there won't be if it has
been read direct from BASIC, as in

447
LET h$ = "hello" or
10 LET h$ = "hello"
so another copy is made, to be on the safe side
The length of the space in the variables area for the
string is specified by the left-hand side of the LET statement,
the specification of the variable in assignment; the length of
the variable being assigned, the right-hand side, may not
match.
The work space is first filled with spaces, then if the string
being assigned is shorter, use its own length, if the space is
shorter use its length - so the string gets Procrusteanly
curtailed. See pages 52 and 80 of the old Spectrum handbook,
p.
62 of the Spectrum +2 book: when a string value is assigned to
a fixed length variable, the string assigned "is cut off on the
right if it is too long, or filled out with spaces if it is too
short - this is called Procrustean assignment after the
[legendary Greek] inn-keeper Procrustes who used to make
sure that his guests fitted the bed by either stretching them
out on a rack or cutting their feet off."
In L ADD$, when an old complete simple string is
replaced, the new string is added to the variables area_before
the old one is deleted; momentarily there are two variables
with the same name. There could be an "Out of memory"
error here, even though there is really room for the new
variable once the old one is deleted - tiresome, especially if
the new string is shorter than the old.
Input parameters: none
- the value to be assigned, numeric or string parameters,
is last value on the calculator stack
- 5C4D DEST holds an address, which for a new variable is
the first letter of the variable name in the program area;
for a declared numeric variable is the byte before the
five bytes of the FP value of the variable in the variables
area;
for a declared string variable is the first byte of the
value of the variable in the variables area

448
- FLAGX bit 1, misprinted FLAGS in the heading notes, is
set for a new variable, and
bit 0 is set for a "complete simple string", ie a
string of which the version in the variables area doesn't have a
record of its length
- FLAGS bit 6 is set for a numeric variable, zero for a
string variable.
Action: get a pointer to the variable from 5C4D DEST
- if FLAGX shows an old variable jump on to L EXISTS
- (new variable; the pointer is on the start of the
variable name in the BASIC command) make a byte counter
05; five bytes in a numeric value or pair of string parameters.
_2B0B_L_EACH_CH: increment the byte counter to allow
for another byte; initially for the variable letter.
_2B0C_L_NO_SP: move on the variable pointer and read
the next character
- if it is 20h space jump back to L NO SP; spaces are
ignored in variable names
- if it is more than 20h jump on to L TEST CH
- if it is less than 10h or more than 15h jump on to L
SPACES; control codes 00 -> 0F and 16 -> 1F are all taken as
terminating a variable name. The only code in these
categories which one could attempt to include in a variable
name from BASIC is a PRINT comma inserted in the variable
name by E mode 6
followed by DELETE; this passes syntax, but all of the variable
name following the PRINT comma is ignored. A variable name
"hello PRINT comma Johnny" is read as "hello"
- (this leaves 10 INK -> 15 OVER, most of which can be
included in variable names; they are ignored at this stage but
cause trouble elsewhere, see under 28B2 LOOK VARS) step
over the parameter which follows the control code
- jump back to L NO SP; ignoring both and stepping the
variable pointer on to the next letter of the variable name.
_2B1F_L_TEST_CH (a code 21h or more): call 2C88
ALPHANUM
- if the character is alphanumeric jump back to L EACH

449
CH; reading it as the next character of a long variable name
- if it is "$" jump on to L NEW$; this is a new simple
string variable.
_2B29_L_SPACES (we now have the full name of a new
numeric variable, not an array, and its length): get a pointer
on the end of the variables area from 5C59 E LINE
- call 1655 MAKE ROOM to make space there of the length
of the variable with its letter or letters
- set pointers on 5C4D DEST and the first byte of the
space in the variables area
- stack a pointer to the second byte in the space;
recovered in L SINGLE
- reduce the length by 6
- if this leaves zero jump on to L SINGLE; a short name.
_2B3E_L_CHAR (long name): skip the first variable letter
- read the next code
- if it is less than 21h jump back to L CHAR; skip
spaces and control codes
- set bit 5 of the code, converting upper to lower case
- increment the variables area pointer and copy the code
to it
- loop back to L CHAR for the next character till the
length counter reaches zero
- (counter is zero, but since the first letter was
skipped, there is still one code left) set its hi bit; marking
the end of a long name
- copy it to the variables pointer
- make a mask 11000000b/C0h for the flag bits which go
in the three hi bits of the first character of a long name.
_2B4F_L_SINGLE: get the variable pointer from 5C4D DEST
again
- XOR it with the mask; if the variable name is a single
letter, the routine jumped here from L SPACES with a zero
mask, and since letters have codes from 01000001b/41h "A" to
01111010b/7Ah "z", short names will now start with 01 and long
names with 10
- set bit 5 for lower case; now the letter flags are 011

450
for short names and 101 for long, see under variables
- recover the pointer to byte 2 of the new place in the
variables area; it was stacked in L SPACES
- call 2BEA L FIRST, which puts the flagged variable
letter in byte 1 and sets a pointer on the end of the variables
area.
_2B59_L_NUMERIC (this exit is common to both old and
new numeric variables): use 0028 FP CALC and 02 delete; the
value assigned to the variable, the last value on the calculator
stack, is deleted, ie no longer counts as part of the stack, but
its five bytes are still in the same location, and DE is now on
the stack end, which is the first of these five bytes
- make a counter of five for the bytes
- point HL at the location for the value in the
variables area
- exit into 2BA6 L ENTER, which copies the five bytes
from DE to HL.
_2B66_L_EXISTS (an old variable; in this case the
variable pointer from 5C4D DEST is on the old value in the
variables area, or one byte before it in the case of a numeric
variable): if FLAGS indicates a string variable jump on to LD
DELETE$
- (numeric variable) move the pointer on six places past
the old value in the variables area
- jump back to L NUMERIC; all numeric variables have the
same length for their value, so the old value can be simply
overwritten, without deleting it.
_2B72_L_DELETE$ (an old string variable or array): get
the start from 5C4D DEST; actually this is in HL already, as the
notes point out
- get the length from 5C72 STRLEN
- if FLAGX signals a simple string jump on to L ADD$
- (array or slice) check the length of the variable in
assignment
- if it is zero return at once; there is no assignment
to be made. This could only happen with a slice, you can't
have arrays with zero-length elements

451
- call 0030 BC SPACES to make the required number of
spaces in the work space
- save the pointers to the variables area and work
space, and the length
- fill the space in the work space with spaces
- call 2BF1 STK FETCH to get the string parameters of
the string to be assigned
- check its length with the length made in the work
space
- if there is enough room in the work space jump on to L
LENGTH
- (the string being assigned is longer than the space
made for it) use the length available; this is Procrustean
shortening.
_2B9B_L_LENGTH: check the length of the string being
assigned
- if it is a slice of zero length jump on to L IN W/S
- copy it to the work space.
_2BA3_L_IN_W/S: get back the pointers to the variables
area and work space, and the length
- exit into 2BA6 L ENTER to copy the string into the
variables area.
[2BA6 L ENTER has a separate entry in this index, since it
is useful as a free-standing subroutine even though in the
ROM it is just an exit routine.]
_2BAF_L_ADD$ (an old complete single string; HL and
5C4D DEST are pointing to the first letter of the string in the
variables area): move the pointer back three spaces; over the
2 length bytes to the variable letter
- read the letter; it already has the proper flag bits
- save its start and length; which came from 5C72 STRLEN
in L DELETE$
- call 2BC6 L STRING, which puts the new variable at the
end of the variables area
- recover the start and length of the old variable
- add three to the length for the letter and length bytes
- exit into 19E8 RECLAIM 2 to delete it.

452
_2BC0_L_NEW$ (a new complete single string; 5C4D DEST
points to the variable letter in the BASIC command): AND the
variable letter with 11011111b/DFh to make the correct flags
- exit into 2BC6 L STRING, which puts the new value of
the variable at the end of the variables area.
Exit: RET from L DELETE$ for a null string in assignment
- into 2BA6 L ENTER from L NUMERIC and from L EXISTS
through L NUMERIC to copy a FP number into the variables
area
- and from L IN W/S to copy a string to the variables
area
- into 19E8 RECLAIM 2 from L ADD$ to delete an old
variable after entering the new version.
Output parameters: none, except where input parameters
are made for the various exit routines.
Called from:
1D16 F REORDER
219B IN VAR 6
Exit from:
1C59 VAL FET 2 (1C56 VAL FET 1)
Rems:
1DEC READ 3 similar to a series of LETs
2AB1 STK ST 0 flag byte of string parameters used by

letter and digit keys see character codes

letter of variable see variables

L EXISTS 2B66 (2AFF LET)


Jumps from:
2AFF LET

L FIRST subroutine 2BEA


An "ad hoc" subroutine: somebody noticed that the same
sequence of instructions was used twice, so why not make it a
subroutine? But it makes little sense out of context.
Input parameters: A holds a byte, HL an address; in the

453
ROM, A is a variable letter and HL the bottom of a new block
just added to the variables area.
Action: move HL down one
- put A in it; overwriting what was the end marker of
the variables area
- reload HL from 5C59 E LINE and move it down one.
Exit: RET.
Output parameters: HL holds the address of the 80-byte
marking the end of the variables area; others unchanged.
Called from:
2B4F L SINGLE
Exit from:
2BC6 L STRING

LIMIT of FOR ... NEXT loops see FOR ... NEXT loops

limit value see 2ACC INT EXP1

LINE key (CA) see also KEYBOARD SCANNING, 0284 key


table (f )
The 3 key in E mode with symbol shift produces the
token LINE, which is neither command, function nor
operator; a sort of "adverb", like DATA in SAVE/LOAD
statements or STEP in FOR statements. It has two quite
unrelated uses, in INPUT commands and in SAVE/LOAD
commands; see INPUT key, 20C1 IN ITEM 1 under
2089 INPUT, and 0605 SAVE ETC.
0716 SA LINE jump forward with SAVE ... LINE
0723 SA LINE 1 find line number for SAVE ... LINE
073A SA TYPE 0 type 0 for SAVE ... LINE
0873 LD PROG checks whether line number was saved
0F38 ED LOOP gets AT/TAB values for INPUT LINE ...
1076 ED SYMBOL jump if dealing with INPUT LINE ...
20D8 IN ITEM 2 handles INPUT LINE ...
20ED IN ITEM 3 excludes INPUT LINE ...
20FA IN PROMPT jump if dealing with INPUT LINE ...
2129 IN PR 3 jump if dealing with INPUT LINE ...

454
215E IN VAR 3 collects LINE (a string)
2174 IN VAR 5 jump if dealing with INPUT LINE ...
219B IN VAR 6 stacks parameters of LINE

line see line of ...

LINE ADDR subroutine 196E


Given a BASIC line number, finds the start addresses in
the program area of the first line with that or a higher
number, and of the line before.
If the given line number is higher than any line in the
program, the "next line number" reached in the end will
either be the 80-byte, giving a line number at least 8000h/
32768d, or a variable letter, which will indicate a line number
at least 4000h/16384d. If the subroutine were called with a
line number value larger than these, it wouldn't work.
Input parameters: HL holds the number of the line in
normal lo-hi form.
Action: make two pointers to the start of the program
area from 5C53 PROG; both are on the first line number.
_1974_LINE_AD_1: call 1980 CP LINES
- if it makes NC return; the line number found at the
pointer is equal to or more than the given one
- call 19B8 NEXT ONE, which advances one pointer to the
next line number and returns with the other on the old
number
- jump back to LINE AD 1 with the advance pointer on the
new line number.
Exit: RET, in 1974 LINE AD 1.
Output parameters: HL holds the start address of the
BASIC line
- DE the address of the line before
- BC holds the input line number
- the Z flag indicates "exact line number found".
Called from:
0FA9 ED EDIT
1059 ED UP

455
155D MAIN ADD
1795 AUTO LIST (twice)
17E4 AUTO L 3
1822 LIST 5
190F LN FETCH
1B9E LINE NEW
1E45 REST RUN

LINE AD 1 1974 (196E LINE ADDR)


Exit from:
196E LINE ADDR
Jumps from:
auto

line counter see DISPLAY AREA

LINE DRAW 2477 (2420 DRW STEPS)


Jumps from:
2382 DRAW
238D DR 3 PRMS
23A3 DR SIN NZ
23C1 DR PRMS

LINE DRAWING SUBROUTINE see 24B7 DRAW LINE

LINE END 1BB3 (1B8A LINE RUN, 1BEE CHECK END)


Exit from:
1B29 STMT L 1
1BB2 REM
1BF4 STMT NEXT (1BEE CHECK END)
1CF0 IF

line execution see 1B8A LINE RUN, BASIC INTERPRETER,


running, run time

LINE NEW 1B9E (1B8A LINE RUN)


Jumps from:

456
1B7D STMT R 1

LINE NO subroutine 1695


Finds the number of a BASIC line at a given address in
the program area. Calls to this subroutine always follow
directly or almost directly a call to 196E LINE ADDR. If there
is no program, both the "line start" and the "preceding line
start" will be the program start address in 5C53 PROG, which
must hold a line number if there is any program at all; if there
is none, it is the same address as 5C4B VARS. If the line start
is on 5C4B VARS, its byte will be 40h or more, see "80-byte"
after the end of the alphabet; in this case the routine looks
for the preceding line start and reports its line number. If
this too is on 5C4B VARS, the routine uses the dummy line
start at LINE ZERO, and hence reports a zero line number.
Input parameters: HL holds a pointer to the line start;
if the last line of the program has been reached it is the
address in 5C4B VARS
- DE holds a pointer to the preceding line start, if
any.
Action: AND the byte at the line start with 11000000b/
C0h; it should be the hi byte of a line number
- if the result is non-zero jump back to LINE NO A; the
byte is 40h or more, ie HL is on 5C4B VARS
- (the byte is less than 40h) read the hi-lo line number
into DE and return.
_1691 (misprinted 1961)_LINE_NO_A: remake the pointer on
the preceding line start
- make the preceding line start pointer the address of
168F LINE ZERO, which holds a two-byte zero
- reenter LINE NO to try again; if the byte at the
pointer is still out of range, the zero line number from LINE
ZERO will be accepted next time round.
Exit: RET.
Output parameters: DE holds the line number in normal
lo-hi order
- BC unaffected.

457
Called from:
0FA9 ED EDIT
1059 ED UP
190F LN FETCH

LINE NO A 1691 (misprinted 1961; 1695 LINE NO)


Jumps from:
1695 LINE NO

line numbers
The term is used in at least five different senses in
the notes; sometimes this can be a little confusing. For 2, 3
and 4 below see DISPLAY AREA.
1. The line number of a line in the BASIC program, see
BASIC line.
2. The AT/TAB screen line number, as used in BASIC.
3. The "print position" or "position value" line number as
mostly used in ROM.
4. The pixel line number, the same as the PLOT coordinate
line number, when counting pixel lines.
5. The scroll number or scrolling line number, when
counting scroll lines. See 0E00 CL SCROLL.

line of BASIC see BASIC line

line of/on display/screen see DISPLAY AREA

line of keys see KEYBOARD SCANNING

LINE RUN subroutine 1B8A see also BASIC INTERPRETER


Executes a BASIC line which has been entered in the
editing area using 0F2C EDITOR; thus it is the main execution
subroutine for the whole of BASIC, since BASIC programs can
only be executed by commands like RUN, GO TO, in such a
line. Also called for syntax checking, see 1B17 LINE SCAN.
The main loop runs through 1BD1 NEXT LINE and 1BF4
STMT NEXT to 1BB3 LINE END and so back to 1BD1 NEXT

458
LINE, so long as there are BASIC lines to be executed; from
1BD1 NEXT LINE or 1BF4 STMT NEXT it enters the sub-loop
1B28 STMT LOOP and following, which executes each
command statement in the BASIC line, and loops back to 1B29
STMT L 1 for another statement till the line end is reached,
when it exits to 1BB3 LINE END. The statement loop from 1B28
STMT LOOP is included here as part of LINE RUN, but its
action is described out of order, following the end of the main
LINE RUN loop at 1BF4 STMT NEXT.
The same loops are turned in executing an un-numbered
BASIC command in the lower screen; but if this contains a
RUN or GO TO command, execution continues still within the
LINE RUN loop, and now 1BB3 LINE END will loop on into
following numbered BASIC lines till the end of the program is
reached.
There are fifty possible BASIC commands, see the table
under commands, functions and operators. Each command
byte in a BASIC line is converted in 1B29 STMT L 1 to an offset,
which is used to compute an address in the syntax parameter
table at 1A7A. This table contains up to eight entries for each
command address: the first entry again computes an address,
this time from the command class table at 1C01, and makes an
indirect jump to this address.
The command table calls class routines to collect from
the BASIC line whatever "operand" or "parameter" expressions
are needed to carry out the command, checks that the
required "separators" = , THEN etc are present, and finally
calls the executive subroutine.
Eg in the case of IF:
- call 1C82 CLASS 06 to evaluate the IF clause in the
BASIC as a logical expression
- check that THEN is present in BASIC
- call 1C11 CLASS 05, which prepares to exit from the
command loop
- call the executive routine 1CF0 IF.
The mechanism for stepping through this table is in 1B55
GET PARAM, which is entered with a table address and the

459
value of the byte at that address. Each byte in the table is
either
- 00 to 0B, indexes for the command class routines
- or a separator = THEN TO or comma, using their normal
token/character codes
- or one of the two bytes of a command routine address;
but they always follow an index to one of the command class
routines which don't return to 1B52 SCAN LOOP, hence these
bytes are never read by 1B55 GET PARAM.
So if the byte is 20h or more, 1B55 GET PARAM jumps on
to 1B6F SEPARATOR, which handles the separators and
returns into the loop; if it is 00 to 0B, 1B55 GET PARAM adds it
to the base address 1C01 of the command class table, adds the
offset from that table and jumps to the resulting address,
which is a command class routine.
The final jump to the executive routine is made by 1C16
JUMP C R.
Another important loop is the 24FB SCANNING loop,
which is called from the various command class routines,
usually indirectly via 1C82 EXPT 1NUM etc, to evaluate
expressions in the BASIC: these may be numbers, strings, or
variables, or functions or operators with their arguments - the
arguments themselves being expressions. Every time it
evaluates an expression, it leaves a FP number on the
calculator stack, which is either the value of a numeric
expression or the string parameters of a string expression: for
a numeric expression a decimal number in the BASIC, a
numeric variable, or a function, for string parameters the
start address and length of some place
in the RAM where the codes of the string are stored.
A resume of the svs used may be useful:
- 5C45 PPC holds the number of the line at present being
executed
- 5C49 E PPC holds the number of the "current line" for
listing on screen with the current line cursor
- 5C47 SUBPPC holds the number of the present statement
-

460
used by CONTINUE, GO SUB and FOR commands, and also for
the statement number in error reports
- 5C42 NEWPPC holds the next line number for execution
after a jump
- 5C44 NSPPC holds the statement number for execution
after a jump, or FF as a "no jump" signal, ie "next statement"
- 5C55 NXTLIN holds the start address of the next BASIC
line after the current one
- 5C5D CH ADD holds the address of the byte currently
being interpreted
- 5C74 T ADDR holds an address in the syntax parameter
table.
Input parameters: none.
Action [the description starts at 1B8A LINE RUN. The
section from 1B28 STMT LOOP to 1B7D STMT R 1 is all part of
the routine, but is taken out of order below following 1BF4
STMT NEXT]: load 5C45 PPC with -2, the line number of the
editing area
- set pointers at start and end of the editing area from
5C59 E LINE and 5C61 WORKSP
- get from 5C44 NSPPC the next statement number for
execution; set to one in 12CF MAIN 3, but probably more if
LINE RUN has been entered from 1B29 STMT L 1, looping back
after execution or syntax checking of the first statement
- jump on to 1BD1 NEXT LINE.
_1B9E_LINE_NEW (the loop return point from 1B76 STMT
RET, after a statement has been executed, if 5C44 NSPPC
indicates a jump in BASIC - GO TO, GO SUB, NEXT, etc): call
196E LINE ADDR for the address in the program area
corresponding to the line number in 5C42 NEWPPC
- if the exact line was found, jump on to 1BBF LINE USE
- (the jump is to a line number which isn't used in the
program) if the statement number isn't zero report "Statement
lost"; something vital has been deleted from the program
- if the line number is more than 4000h report "OK"; the
end of the program has been reached
- (LINE ADDR will have returned the number of the

461
following line) jump on to 1BBF LINE USE.
[1BB2 REM, the command mini-routine for the REM
command, is interpolated here: it merely drops the 1B76
STMT RET address, with the effect that the line is considered
executed already.]
_1BB3_LINE_END (see also under 1BEE CHECK END; 1B29
STMT L 1 reenters the loop here if it comes to a newline): if
syntax checking return
- (run time) get from 5C55 NXTLIN the address in the
program area of the next line start; it was loaded in 1BD1
NEXT LINE
- if the line number is more than 4000h return; the end
of the program has been reached
- zero the statement number.
_1BBF_LINE_USE (entered also from 1B9E LINE NEW, with
line address and statement number of the next line to be
executed. Statement number zero means "start from the
beginning of the line", ie the same as number one): if the
statement number is zero make it one
- read the line number, hi before lo, and put it in 5C45
PPC
- read the length of the line; in the two bytes after
the line number, see BASIC line
- compute the address of the next line start.
_1BD1_NEXT_LINE (entered also from LINE RUN executing
direct commands in the editing area, in which case the "next
line start" address is on the 80-byte at the end of the editing
area):
- put the next line start in 5C55 NXTLIN
- put the start of the present line in 5C5D CH ADD as a
BASIC pointer
- put FF in 5C44 NSPPC; "no jump"
- decrement the statement number and put it in 5C47
SUBPPC
- if this is zero jump back to 1B28 STMT LOOP; the BASIC
pointer is already correct

462
- call 198B EACH STMT to move the BASIC pointer on to
the start of the statement indicated
- if there is such a statement jump on to 1BF4 STMT NEXT
- if not report "Statement lost"; something has got
deleted .
[1BEE CHECK END is interpolated here, see its index
entry.]
_1BF4_STMT_NEXT (entered also from 1B7D STMT R 1 after
execution of each statement, if there is no jump in BASIC): if
the next character is a newline jump back to 1BB3 LINE END
- if it is a colon jump back to 1B28 STMT LOOP
- (statement complete but no terminator) report
"Nonsense in BASIC".
_1B28_STMT_LOOP (execution loops back to here either
from
1BF4 STMT NEXT at a colon or from 1BD1 NEXT LINE for the
first statement of a line): move on the BASIC pointer; this is
now on the first byte of a new statement. It must be a BASIC
command or a terminator, otherwise there will be an error
report shortly.
_1B29_STMT_L_1 (also entered from 1B17 LINE SCAN for
syntax checking and 1D00 IF 1 after a "true" IF statement): call
16BF SET WORK to clear the work space
- increment 5C47 SUBPPC
- if it goes "negative" report "Nonsense in BASIC";
there are 128d statements in the line.
- if the character at the BASIC pointer is a newline
jump on to 1BB3 LINE END
- if it is a colon jump back to 1B28 STMT LOOP
- (it should be a BASIC command) stack the loop return
address 1B76 STMT RET; any RET from here on will return
there -
unless this address has been dropped first, as is done in some
of the subroutines, or overlaid by another as happens in 1B55
GET PARAM
- put the BASIC pointer on the byte after the command
- reduce the command byte by CE

463
- if this makes carry, report "Nonsense in BASIC"; all
the command token codes start from CE DEF FN
- (the byte is 00 DEF FN -> 31h COPY) add it to 1A48;
giving an address in the syntax offset table
- add this offset to the entry address; making an
address in the syntax parameter table starting at 1A7A
- jump on to 1B55 GET PARAM.
_1B52_SCAN_LOOP (this is stacked as the return address by
1B55 GET PARAM on top of the 1B76 STMT RET address
stacked by
1B29 STMT L 1; all the command class routines return here
except 00, 02, 03, 05, 07, 0B, which jump out to command
routines with the 1B76 STMT RET address for return): recover
from 5C74 T ADDR the current place in the syntax parameter
table.
_1B55_GET_PARAM: get a byte from the syntax parameter
table
- save the next address from the table in 5C74 T ADDR
- put the 1B52 SCAN LOOP return address on the stack;
RET will now be to there till scanning of the syntax parameter
table is complete, when 1B52 SCAN LOOP will be dropped,
usually by CLASS 05. Subsequent RETs will be to 1B76 STMT
RET, the next address down on the stack
- if the syntax parameter byte is more than 20h jump on
to 1B6F SEPARATOR
- index into the command class table at 1C01 with the
parameter byte
- compute the command class address and stack it
- get the byte_after the BASIC command in A; the notes
are wrong
- set the NZ flag to stop 1C11 CLASS 05 calling 1BEE
CHECK END; B was zero
- return; an indirect jump to the command class address.
_1B6F_SEPARATOR (entered only from 1B55 GET PARAM
when a separator is found in the syntax parameter table):
compare the character in the BASIC with the separator
specified in the syntax parameter table

464
- if it doesn't match report "Nonsense in BASIC"
- move on the BASIC pointer
- return; to 1B52 SCAN LOOP.
_1B76_STMT_RET (all the BASIC command routines return
here in run time except 1CF0 IF, 1CEE STOP, 1BB2 REM, 11B7
NEW and 1793 CAT ETC, and so do command class routines
02 and 07):
call 1F54 BREAK KEY to check for BREAK.
_1B7D_STMT_R_1: if the hi bit of 5C44 NSPPC is set jump
on to 1BF4 STMT NEXT; it must be FF signalling "no jump in
BASIC"
- (jump in BASIC) if the hi bit of 5C42 NEWPPC is zero
jump on to 1B9E LINE NEW; it has a real line number, not -2
for the direct commands
- (direct command) close the loop by reentering LINE
RUN to execute any remaining statements.
Exit (apart from various report messages): RET from 1BB3
LINE END returns to 12CF MAIN 3
- other RETs are indirect jumps:
in 1B55 GET PARAM, jump to the command class table
with return address into the loop
in 1B6F SEPARATOR, jump to 1B52 SCAN LOOP.
Output parameters: none.
Called from:
12CF MAIN 3 (misprinted PROG RUN)
Exit from:
1B7D STMT R 1
Rems:
Introduction commands produce jump to command
routine
0020 NEXT CHAR called repeatedly during interpretation
0053 ERROR 2 return address to, points to error literal
1B17 LINE SCAN for syntax checking, starts here
1C16 JUMP C R indirect jump to command routine
1CD6 CL 09 1 fetches operands for PLOT etc
1E7A OUT fetches 2 operands for OUT
1EAC CLEAR fetches operand for CLEAR

465
1EAF CLEAR RUN provides default operand for CLEAR
1F3A PAUSE fetches operand for PAUSE
1F3D PAUSE 1 discriminates zero PAUSE operand
24FB SCANNING brief explanation of function evaluation

LINE SCAN subroutine 1B17


The main subroutine for checking the syntax of a line in
the editing area. See errors and 1B8A LINE RUN.
Input parameters: none.
Action: set bit 7 of FLAGS; "syntax checking"
- call 19FB E LINE NO, which reports an error if there
is a line number and it is out of range, and puts the start
address of the first statement in the BASIC pointer 5C5D CH
ADD
- zero 5C47 SUBPPC; start with the first statement
- put FF in the error index; "OK" - it will be changed
if any errors arise.
Exit: to 1B28 STMT L 1, entering the statement loop, see
under 1B8A LINE RUN; since the syntax checking flag is set,
this makes a "dry run". LINE RUN will merely check each
command and expression in the line.
Output parameters: none.
Called from:
12AC MAIN 2
Rems:
0008 ERROR 1 address reached in the BASIC put in X PTR
0053 ERROR 3 top of stack is address in checking loop

line start (BASIC) see BASIC line

LINE USE 1BBF (1B8A LINE RUN, 1BEE CHECK END)


Jumps from:
1B9E LINE NEW (twice)

LINE ZERO 168F


Two zero bytes, used at 1961 LINE NO A to load a zero
line number in case there is no line in the program area.

466
L IN W/S 2BA3 (2AFF LET)
Jumps from:
2B9B L LENGTH

LIST key (F0) see also commands, functions and operators,


KEYBOARD SCANNING
The K key in K mode produces the command LIST; it
requires a numeric parameter, but if none is specified in
BASIC zero is supplied. It may also be followed by (hatch) with
semicolon or comma and a stream number, before the line
number if any. It lists BASIC on the screen, or to the specified
stream, starting at the given line number.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AAE P LIST causes a jump via 1C11 CLASS 05 and 1C16 JUMP C
R to the executive routine 17F9 LIST.

LIST subroutine 17F9


Called only by the statement loop from the syntax
parameter table at 1AAE P LIST; executes the LIST command.
The ROM allows for commands which are "not in the
book”, eg LIST (hatch)3;1000 or LIST (hatch)3,1000, which
are equivalent to LLIST 1000. This command works correctly,
but isn't mentioned in any of the "Spectrum" handbooks.
The routine starts the listing from a line number
specified in BASIC, or from the program start if none is
specified. LIST with a line number greater than 9999d will
produce no listing, but an "OK" report, see LIST 5 below;
greater than 65535d will give "Integer out of range" from 1CDE
FETCH NUM called in LIST 4.
Input parameters: none.
Action: make a stream number 02; print to upper screen.
_17FB_LIST_1 (enter here from 17F5 LLIST): zero TV FLAG;
bit 4 signals "ordinary listing" and bit zero "upper screen"
- call 1601 CHAN OPEN to open a channel with the

467
specified stream
- read the code at the BASIC pointer
- call 2070 STR ALTER, which changes the stream if the
code is (hatch)
- if it returns with carry jump on to LIST 4; no (hatch)
- (the stream has been changed) check for comma or
semicolon
- if there is neither jump on to LIST 3.
_1814_LIST_2 (the comma or semicolon imply a line
number): move on the BASIC pointer
- call 1C82 EXPT 1NUM to read the number; [the note is
misprinted - "5.20" should be "5,20" or "5;20"]
- jump on to LIST 5.
_181A_LIST_3 (a stream change with no comma or
semicolon implies line zero): call 1CE6 USE ZERO
- jump on to LIST 5.
_181F_LIST_4 (no stream change and so no comma or
semicolon): call 1CDE FETCH NUM to get a line number from
BASIC if there is one and use zero if not.
_1822_LIST_5 (in all cases the line number is on the
calculator stack): call 1BEE CHECK END; in syntax checking it
reports "Nonsense in BASIC" if this isn't the end of the
statement, or makes a double return to the statement loop if it
is
- (run time) call 1E99 FIND INT2 to get the line number
off the stack
- AND its hi byte with 00111111b/3Fh; this reduces it to
maximum 3FFFh/16383d, to ensure that the first byte in the
variables area will act as an end marker in case no higher line
exists
- put this line number in 5C49 E PPC; making it the
"current line"
- call 196E LINE ADDR, which returns
if there is a line with that number, the start address
of the line
if there is a line after that number, its start address
if there is no such line, the address of 5C4B VARS,

468
holding an 80-byte
- put 01 in E to signal to 1865 OUT LINE that "current
line hasn't been printed"; this is really the first line of the
1835 LIST ALL exit routine, see below. OUT LINE is called in
the next line; it begins with a call to 1980 CP LINES, which
makes a Z flag if it finds an_exact match for the given line
number.
This Z flag is read in OUT LINE, with the effect that the
current line cursor is printed on the first LISTed line if its
number is exactly the one specified in the LIST command, but
not otherwise.
Exit: into 1835 LIST ALL, which makes an "ordinary"
listing of every BASIC line starting from the given number.
Output parameters: HL holds the start address in the
program area of the first line to be printed. E holds the flag
01.

LIST ALL subroutine 1835 ["ROM Disassembled" is in a


serious muddle here. The only call of LIST ALL is from 17ED
AUTO L 4,
where the call is to 1833, not 1835. However the three auto
jumps in LIST ALL are to 1835, not 1833. There should really
be another label]
Prints out BASIC lines on screen or printer, with the
current line cursor ">" on the correct line.
It is called both in "automatic" and in "ordinary"
listing, signalled by bit 4 of TV FLAG. In either case listing
terminates after the last BASIC line has been listed; taken care
of by the subroutine 1855 OUT LINE, which makes a "double
return" by dropping its own return address if the last line of
the program is reached. The difference lies in the action when
the screen is full; see 0C55 PO SCR, which is called by the
09F4 PRINT OUT routine whenever the print position line
number moves on one. In automatic listing the screen is
scrolled automatically until the current line is on screen, but
once it is, there is a return to editing. In ordinary listing the

469
"scroll?" message is printed and execution awaits an input;
this is handled within the 09F4 PRINT OUT routine.
Input parameters: HL holds a pointer to the start of the
first line to be printed.
Action (starting with line 1833): make a flag 01 to
signal "current line not yet printed"
- call 1855 OUT LINE, which prints the line at the
pointer; with the current line cursor if its line number
matches the one in 5C49 E PPC, and it updates the current
line flag if appropriate. If the line is the last in the listing it
makes a double return out of the LIST ALL routine
- call 0010 PRINT A 1 to print the carriage return
- if bit 4 of TV FLAG is zero jump back to LIST ALL and
repeat; for an ordinary listing. This endless loop is terminated
if the last line is reached, when OUT LINE makes its double
return; otherwise 09F4 PRINT OUT, called by both OUT LINE
and PRINT A 1, goes on printing lines and "scroll?" messages
indefinitely
- (automatic listing; PRINT OUT will have scrolled the
screen automatically as much as is needed to print the line,
and OUT LINE will again have made its double return if the
last line has been printed) compare the value in 5C6B DF SZ
with 5C89 S POSN hi; DF SZ holds the number of lines in the
lower screen, S POSN hi the line number of the current upper
screen print position counted from the bottom of the screen
- if they aren't equal jump back to LIST ALL; there is
still space in the upper screen
- (the upper screen is full) if the current line flag is
zero, return; the current line with its ">" cursor has already
been printed
- (the current line hasn't been printed yet) point HL at
5C6C S TOP and call 190F LN FETCH; 5C6C S TOP is the
number of the top line which PRINT OUT won't scroll off the
screen; LN FETCH replaces it with the next line number in the
BASIC
- jump back to LIST ALL to list another line; since 5C6C

470
S TOP has been moved on, automatic scrolling will be
possible.
Exit: RET, if automatic listing and the current line flag
has been zeroed
- if the last line has been printed, taken care of by
the double return in 1855 OUT LINE
- in ordinary listing, goes on with "scroll?" prompts
indefinitely.
Output parameters: none.
Called from:
17ED AUTO L 4
Exit from:
17F5 LLIST and
17F9 LIST through
1822 LIST 5
Jumps from:
auto (3 times)

LIST AND LLIST COMMAND ROUTINES see 1795 AUTO LIST

LIST ENTRY POINT see 17F9 LIST

listing see 1795 AUTO LIST

LIST SP system variable 5C3F


Bytes: 2
Return from the "automatic listing" routine, starting at
1795 AUTO LIST, is from the RET in 17ED AUTO L 4 if no
scrolling of the screen was required, but from OC55 PO SCR
after enough scrolling has been done to bring the current line
on the screen.
PO SCR can be called from several different points within the
09F4 PRINT OUT routine, and it is impossible to predict how
many RET addresses, etc will be piled up on the stack when
RET is required. So the address of the stack pointer is saved in
LIST SP on entry to 1795 AUTO LIST, and the stack pointer is

471
returned to it before returning, after the automatic listing is
complete,
from 0C55 PO SCR.
Written by:
1795 AUTO LIST
Read by:
0C55 PO SCR

LIST 1 17FB (17F9 LIST)


Jumps from:
17F5 LLIST

LIST 2 1814 (17F9 LIST)


Jumps from:
17FB LIST 1

LIST 3 181A (17F9 LIST)


Jumps from:
17FB LIST 1

LIST 4 181F (17F9 LIST)


Jumps from:
17FB LIST 1

LIST 5 1822 (17F9 LIST)


Jumps from:
1814 LIST 2
181A LIST 3

literals see CALCULATE

L LENGTH 2B9B (2AFF LET)


Jumps from:
2B72 L DELETE$

LLIST key (E1) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode key table (b)

472
The V key in E mode without shift produces the
command LLIST; it requires a numeric parameter, but zero is
supplied if none is specified by the BASIC.
It may also be followed by a stream number, see LIST
key.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1ADC P LLIST causes a jump via 1C11 CLASS 05 and 1C16 JUMP
C R to the executive routine 17F5 LLIST.

LLIST subroutine 17F5


Called only from the syntax parameter table 1A7A to
execute the LLIST command. Exactly like 17FB LIST except
that stream 03, ZX printer, is opened instead of 02. As with
LIST, there is an undocumented command option: LLIST
(hatch)2;1000 has xactly the same effect as LIST 1000.
When channel 03 is in use all scrolling routines are
bypassed.

L-mode see 5C41 MODE

LN key (B8) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The Z key in E mode without shift produces the function
LN; it requires one numeric operand X, and the value of the
function is the_natural logarithm of X, the power to which the
constant e must be raised to equal X. The number e is
2.718281828..., see index entry on 36C4 EXP.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B8 first to 09, then to E5,
and adds the priority 10h/16d. Code and priority 10E5 are now
pushed on to the machine stack (270D S PUSH PO) while the
expression following LN is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E5 to 25, the calculator
offset for 3713 ln.

473
ln subroutine 3713
Called from 0028 FP CALC with literal 25h; the executive
routine of the LN function, but also used in calculating square
roots and "to-power". It could be called direct, but its
argument X must always be last value on the calculator stack
even for direct calls.
Finds LN X, the natural logarithm of any positive number
X; that is, the logarithm to base e = 2.718281828...d, see index
entry on 36C4 EXP.
See CALCULATE for the structure of FP numbers. If E is
the true binary exponent of X and M is the true binary
mantissa of X, called X' in the notes, then
X = 2**E times M, and
LN X = LN 2**E + LN M.
LN 2**E is easily found: LN 2**E = E * LN 2. LN 2 is
just a number, 0.6931471806...d, in FP form 80 31 72 17 FB; see
373D GRE.8, near the beginning.
To find LN M, the subroutine uses the series generator,
using twelve Chebyshev polynomials with the constants:

FP form Decimal
1. 61 AC -0.0000000003
2. 64 09 0.0000000020
3. 66 DA A5 -0.0000000127
4. 69 30 C5 0.0000000823
5. 6C 90 AA -0.0000005389
6. 6E 70 6F 61 0.0000035828
7. 71 CB DA 96 -0.0000243013
8. 74 31 9F B4 0.0001693953
9. 77 A0 FE 5C FC -0.0012282837
10. 7A 1B 43 CA 36 0.0094766116
11. 7D A7 9C 7E 5E -0.0818414567
12. 80 6E 23 80 93 0.9302292213

To adjust it to the range over which the Chebyshev


polynomials can approximate the LN function, the mantissa M
is adjusted to Z by the following set of formulas. The range of
Z is -1 to +1, that of M is only 0.5d to 0.9999999999d; all
numbers are decimals:
474
if M > 0.8, Z = 2.5M - 3 (range -1 to -0.5);
if M <= 0.8, Z = 5M - 3 (range -0.5 to +1) and E is
reduced by 1.
The series produces the result
(LN M)/(M - 1) or
(LN 2M)/(2M - 1)
depending on whether M is more or less than 0.8d; in either
case LN M, and then LN 2**E + LN M, are easily calculated.
Input parameters: none
- the number X is last value on the calculator stack.
Action: restack X in full format
- if it is negative or zero report "Invalid argument";
such numbers don't have a logarithm in real numbers.
_371C_VALID: put a zero on the calculator stack and
delete it; this makes HL point to the exponent of X
- get the exponent of X
- replace it with 80h; the last value is now M
- put the exponent on the stack; because of the format
of FP numbers, this is E + 80h
- subtract 80h; the stack holds, from the top, E and M
- if M is more than 0.8d jump on to GRE.8
- (M <= 0.8d) decrement E
- double M; the short way is just to increment its
exponent. The stack holds, from the top, 2*M, E - 1.
_373D_GRE.8: calculate E*LN 2; if M was < 0.8d this is (E
- 1)*LN 2
- subtract one from M; by twice subtracting half - why?
this seems quite pointless. If M was < 0.8d this is 2M - 1
- multiply it by 2.5d and subtract half again; the
result, in decimals, is
2.5(M - 1) - 0.5 = 2.5M - 3 or
2.5(2M - 1) - 0.5 = 5M - 3;
either way it is Z as required for the series calculation
- call series-0C with the twelve Chebyshev constants
listed above; giving the result (LN M)/(M - 1), or the
equivalent with M = 2M if M was doubled
- multiply the result by M - 1 already on the stack; the

475
answer is LN M
- add this to E*LN 2 already on the stack; the answer is
E*LN 2 + LN M = LN 2**E + LN M
= LN X.
Exit: RET.
Output parameters: none
- LN X has replaced X as last value on the stack.
Called from:
3851 to-power
Rems:
Introduction approximation found by Chebyshev
polynomial
3449 series-06 LN produced by Chebyshev polynomials
384A sqr LN produces error for negative numbers

L NEW$ 2BC0 (2AFF LET)


Jumps from:
2B1F L TEST CH

LN FETCH subroutine 190F


Finds the next line number in the BASIC program
following the line number held at a given address in lo-hi
form;
this is a line number in one of the svs, 5C6C S TOP or 5C49 E
PPC, or in ROM, not at a line start, which would be in hi-lo
form.
Input parameters: HL holds the address.
Action: read the line number and save the address
- increment the line number
- call 196E LINE ADDR, which returns the start address
of the line with this number if there is one, or of the next
line
- call 1695 LINE NO to get its number.
Exit: into 191C LN STORE, which loads the number into
the given address.
Output parameters: HL is incremented by one from its
input value

476
- DE holds the required line number
- BC unchanged.
Called from:
0FF3 ED DOWN
1835 LIST ALL

L NO SP 2B0C (2AFF LET)


Jumps from:
auto (twice)

LN STORE subroutine 191C


An "ad hoc" routine, used to store line numbers in
system variables.
Input parameters: HL holds the hi byte address of a
system variable, 5C49 E PPC or 5C6C S TOP
- DE holds a BASIC line number.
Action: in input mode, return at once
- (in editing mode) load D into HL and E into HL - 1,
the lo byte of the system variable.
Exit: RET.
Output parameters: HL decremented, others unchanged.
Called from:
1059 ED UP
Exit from:
190F LN FETCH

L NUMERIC 2B59 (2AFF LET)


Jumps from:
2B66 L EXISTS

LOAD key (EF) see also commands, functions and


operators,
KEYBOARD SCANNING
The J key in K mode produces the command LOAD. The
“LOAD statement" must include a filename, though it may be
null "", and may also include modifiers such as CODE.

477
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AE0 P LOAD causes a jump via 1CDB CLASS 0B to the
executive routine for all the cassette handling commands,
0605 SAVE ETC.
The routine distinguishes between SAVE, LOAD, VERIFY and
MERGE by looking at 5C74 T ADDR, which was used to step
through the 1A7A table: its low byte reads one more than the
low byte of the "P" label, E0, E1, E2 or E3 respectively, so E1
for LOAD.
04C2 SA BYTES cassette handling routines start here
053F SA/LD RET common to SAVE and LOAD
0556 LD BYTES loads both headers and data
058F LD SYNC gets ready to load
05A9 LD LOOP loading loop
05E3 LD EDGE 2 main action when loading
0629 SA BLANK name can be null for LOAD
075A SA ALL ready to load header
0767 LD LOOK H loop to load header
07AD LD CH PR jump forward for LOAD
07CB VR CONTRL used for LOAD SCREEN$/CODE
07F4 VR CONT 2 jump forward for LOAD
0800 VR CONT 3 load data block
0802 LD BLOCK used for all loading
0808 LD CONTRL loading programs/variables/arrays
082E LD DATA loading arrays
084C LD DATA 1 loads arrays
0873 LD PROG loads programs
08AD LD PROG 1 loads program block
08B6 ME CONTRL part i is a load
1CDB CLASS 0B jumps to executive routine

LOAD A DATA BLOCK SUBROUTINE see 0802 LD BLOCK

LOAD CONTROL ROUTINE see 0808 LD CONTRL

478
LOADing routines
Ie LOAD (0808 LD CONTRL),
MERGE (08B6 ME CONTRL),
VERIFY (07CB VR CONTRL).
0802 LD BLOCK common to all

LOC MEM subroutine 3406


Adds five times the parameter supplied to a base
address. Used to find a FP number, usually in the calculator
memory.
Input parameters: HL holds the base address, usually of
the memory area
- A holds the parameter, usually derived from the
literal of get-mem-0 etc.
Action: double the parameter twice and add the original
value; ie multiply it by five
- add it to HL.
Exit: RET.
Output parameters: HL holds the address required; the
first byte of an FP number
- DE unchanged
- [the notes appear to suggest that the value of BC,
five times the parameter, is an output parameter, but it isn't
used in the ROM].
Called from:
0427 BE OCTAVE
340F get-mem
342D st-mem

logically true/false, logical value see also AND key,


greater-0, IF key, 368F jump-true, OR key
The_logical_values of expressions are:
numeric expressions of value zero: zero
all other numeric expressions (even negative) one
equalities which equate eg 7=4+3, "h"="h" one
equalities which don't equate eg 7=4+4, "h"="s" zero
inequalities which are "true" eg 1>0, "s">"h" one

479
inequalities which aren't "true" eg 0>1, "h">"s" zero
values of logic operations AND, OR, etc: depending
Zero means_false, one means_true.
The logical value is numeric; even string comparisons
are evaluated as numeric expressions.
Other kinds of expression, eg strings, cannot be used as
operands of logic commands or operators such as IF, OR; a
string may precede AND, but then isn't read for its logical
value. It may not follow AND.
The terms "true" and "not-false" are used in a slightly
different sense in the notes on 368F jump-true, where the
only possible non-zero value is one. [To be more precise, the
subroutine will make a jump on any last value with a non-zero
third byte, and won't jump on any with a zero third byte. If
the one happened to be in full FP format, 81 00 00 00 00,
jump-true would regard it as a zero and make no jump. This
comment applies to m/c programming uses of jump-true, but
not to the ROM, where it is only ever asked to distinguish
between zero and one in small integer format.]
2E24 PF SMALL wrong answer can result from STR$ error
368F jump-true any +ve value counts as true

LOG(2**A) subroutine 2DC1


Calculates log 2**A, rounded up or down to an integer; A
itself is an integer.
Log 2**A always means the logarithm of 2**A to the base
ten: the number of the power to which ten must be raised to
equal 2**A, ie the kind of logarithm one used to learn at
school. Not the same as LN 2**A, which means the logarithm
to the base e.
Used to estimate the amount of space that will be
required to print an FP number in decimal form; A is its
binary exponent. The number of digits before the decimal
point of a number X written in decimal notation is one more
than the integer part of log X; if X is less than one the number
of leading zeroes after the decimal point is the absolute value
of its log, which will then be negative.

480
The subroutine uses a well-known mathematical identity:
logarithm X to the base Y =
logarithm X to the base Z times logarithm Z to the base Y
Using 2 for the helping number Z, we have
log X = logarithm of X to the base 2 times log 2
Log 2 is just a number, which anyone can figure or look
up: 0.3010299957...d, or in FP format 7F 1A 20 9A 85.
The logarithm of X to the base 2 would take a little
figuring to get in full, but its integer part is just the true
binary exponent of X, so it is easily available when X is
written in full FP format. In this case X = 2**A, so the
exponent is just A, and the required result is A log 2, rounded
up or down to an integer.
Input parameters: A holds the true binary exponent E of
the number, if this was positive. If it was negative -E, A holds
-(ABS E - 2), two closer to zero than -E.
Action: copy A to D
- rotate the hi bit of A into carry and SBC A,A; this
makes a sign byte, zero for positive, FF for negative
- copy the sign byte to E and C
- zero A and copy it to B; all this makes a small
integer version of the exponent, positive or negative, in
registers AEDCB, 00 00 XX 00 00 for +XX and 00 FF YY FF
00 for 100h - YY = -XX
- call 2AB6 STK STORE to put this on the stack
- use the calculator to multiply it by log 2 and round
the result down to an integer.
Exit: into 2DD5 FP TO A, which returns in A the last
value on the stack; an approximation to the number of digits
before the decimal point in the decimal print of the number
whose binary exponent is A, or of the number of leading
zeroes after the decimal point; usually one too large.
Output parameters: none
- the last value on the stack is the value required.
Called from:
2E24 PF SMALL
2E56 PF LARGE

481
Rems:
2AB1 STK ST 0 used to put small integer on stack

long multiplication see 30CA multiply

long name of variable see variables

LOOK PROG subroutine 1D86


Finds the line and statement number of the next
occurrence of a specified command code in the BASIC
program;
used to find DATA, DEF FN, NEXT token bytes.
Input parameters: E holds the code of the token to be
matched
- HL holds the start address
from 5C53 PROG, the beginning of the BASIC program, if
the search is for DEF FN
of the first byte after a FOR statement, if NEXT is
being looked for
from 5C57 DATADD, if the search is for DATA.
The address may be the start address of the next line,
with the hi byte of the line number, or in the last two cases
may hold a colon. In all three cases, if there are no more lines
in the program HL holds the first variable letter or the 80-byte
at the end of the variables area.
Action: if HL holds 3A colon jump on to LOOK P 2.
_1D8B_LOOK_P_1 (the start address doesn't have a colon):
if it holds 40h or more, return with carry set; this is a
variable letter or the 80-byte, see 80-byte after the end of the
alphabet
- (on a line number) read the hi-lo line number and put
it in 5C43 NEWPPC
- read the length of the line from the two bytes
following the line number
- add it to the start address; the result is the start
address of the next line
- make a statement counter zero; it will be counted

482
downwards.
_1DA3_LOOK_P_2: call 198B EACH STMT, which decrements
the statement counter and returns with NC if the code in E is
matched, not in quotes, within that statement
- return on NC
- (no match in this statement) jump back to LOOK P 1.
Exit: RET, from LOOK P 2 if there was a "find" or LOOK P 1
if not.
Output parameters: D holds the statement number as a
negative value, FF for statement 1, FE for 2, etc
- E is unchanged
- 5C43 NEWPPC holds the line number
- the carry flag shows C if no match was found anywhere,
NC if one was found.
Called from:
1D64 F LOOP
1DED READ
2808 SF FIND DEF

LOOK P 1 1D8B (1D86 LOOK PROG)


Exit from:
1DA3 LOOK P 2 (1D86 LOOK PROG)

LOOK P 2 1DA3 (1D86 LOOK PROG)


Exit from:
1D86 LOOK PROG

look-up routines see tables

LOOK VARS subroutine 28B2


In scanning a BASIC program for execution, it is
necessary to find or create a variable in the variables area
corresponding to a variable in assignment used in the BASIC,
before a value can be assigned to it. The first byte of each
variable in the variables area is coded to show its type, in the
first 3 bits; the remaining bits indicate the letter, from 00001 =
"a" to 11010 = "z"; see the index entry variables, and pages

483
166-8 of the old Handbook, pages 144-6 of the Plus 2
handbook.
This coding isn't done in the BASIC; there the letter bits will
be the same, but they may be in upper or lower case, and the
variable name may include spaces or colour controls, which
are all cut out of the version in the variables area.
The subroutines LOOK VARS and STK VAR, together with
the exit routine VAR A 1 which sometimes intervenes,
although formally distinct, are best considered together: in
most cases calls to STK VAR directly follow calls to LOOK
VARS, or VAR A 1 if it is interpolated. Their purpose is to look
in the variables area for a variable letter matching the letter in
the BASIC and with the correct flag bits for the type of
variable, and then:
- for a numeric variable, return the address in the
variables area of the last letter of the variable name
- for a numeric array, return the address in the
variables area of the first byte of the specified element, a FP
number
- for a string or string array, put the string parameters
of the specified element on the calculator stack, and carry out
a slicing operation if this is called for;_all strings and
string arrays are treated as arrays of single characters
- check the syntax; STK VAR is called for this purpose
only by 2C05 D RPORT C.
NB 1. Finding an element in an array; the loop in SV COUNT.
If the element whose value is to be read is (x,y,z,...,r) in an
array dimensioned (a,b,c,...,i), then
- there are a*b*c*...*i elements in the string of
elements in the variables area
- in view of the way these elements are arranged, the one
looked for is serial
(x - 1)*b*c...*i + (y - 1)*c*...*i + (z - 1)*...*i + ... + r
This serial is built up in stages on each turn of the
loop as
X = zero * a + (x - 1)
Y = X * b + (y - 1)

484
= (x - 1)*b + (y - 1)
Z = Y * c + (z - 1)
= (x - 1)*b*c + (y - 1)*c + (z - 1)
and finally
R = (x - 1)*b*c...*i + (y - 1)*c*...*i + ... + r - 1
which is the serial of the element before the one sought for.
NB 2. Repeated slicing:
PRINT a$(7,3,4 TO 13)( TO 3)(2)
is perfectly good syntax, and will be executed: in this case it
will print the second of the first three of characters four to
thirteen of a$(7,3), ie the fifth character. This seems rather
pointless, and isn't mentioned as far as I know in any of the
handbooks. See SV SLICE.
NB 3. There is an oddity in V SPACES, which perhaps should
even be called an error: it doesn't skip over colour controls in
the BASIC. Colour controls are cut out of variable names when
the variable is established in the variables area, but they are
left in the BASIC. Colour controls at the_start of a variable
name are skipped in the SCANNING loop, but not colour
controls in the middle of the name. If you write
10 LET the yellow one=7
your variable is stored with the long name "theyellowone";
any colour controls you may have put in the BASIC, and the
spaces, are cut out of it. If you now write
30 PRINT the yellow one
with no colour controls, and RUN, the computer will
correctly print 7.
But if you put E-mode 6 before "yellow" and E-mode 7
after it in line 30, so that the word "yellow" prints in the
BASIC with yellow PAPER - whether or not this matches what
you put in line 10 - you will get the error report "Variable not
found, 30:1", because the colour controls within the name
don't match.
And if you make your program
10 LET the yellow one=7
20 LET the yellow one=3046
30 PRINT the yellow one

485
with colour controls in line 20 but none in 10 or 30, and RUN,
the computer will print "7", apparently ignoring line 20. It has
made a variable "theyellowone" for line 10 and given it the
value 7; it has failed to match this with the variable in line 20,
so has made a_second_variable_with_the_identical_name and
given it the value 3046; but when it looks in the variables area
for the value to print it out, it finds the old one first, and
ignores the second.
If you enjoy crashing computers, try
1 LET the yellow one = 7: GO TO 1
with colour controls in the variable name. It will add more
and more variables all called "theyellowone" until it reports
"Out of memory".
NB 4. For the error in the output flagging of LOOK VARS, see
0672 SA V OLD under 0605 SAVE ETC.
Input parameters (LOOK VARS): none
- 5C5D CH ADD holds the address of the first byte of the
variable in the BASIC
- 5C0B DEFADD will hold a DEF FN address if a user-
defined FN is being evaluated; in this case variable values for
simple variables from the corresponding DEF FN statement
are to be used in preference to variables in the variables area.
Otherwise 5C0B DEFADD holds zero. Arrays and long-name
variables aren't allowed in DEF FNs.
Action: set bit 6 of FLAGS; provisionally a numeric
variable
- read the byte at the BASIC pointer and call 2CBD ALPHA
- if it isn't a letter code report "Nonsense in BASIC"
- AND it with 00011111b/1Fh to isolate the five bits
zero -> 4 indicating the letter
- keep the result, now the "discriminator byte" of the
variable in assignment
- read the next byte
- if it is 28h (, jump on to V RUN/SYN; numeric array
- set bit 6 of the discriminator byte; this is right for
all kinds of variables except "long names"
- if the byte just read is 24h $, jump on to V STR VAR

486
- (not a string variable or array) set bit 5 of the
discriminator byte; this is right for numeric variables
including looping variables
- call 2C88 ALPHANUM and if the last byte read isn't a
letter or digit jump on to V TEST FN.
_28D4_V_CHAR (long name of a numeric variable, flag bits
101): call ALPHANUM again and jump on to V RUN/SYN if on
looping
back the byte isn't a letter or a digit
- (more codes in the long name) zero bit 6 of the
discriminator byte
- read the next byte and loop back to V CHAR.
_28DE_V_STR_VAR (byte 2 was 24h $): move on the BASIC
pointer
- zero bit 6 of FLAGS; "string" flag.
_28E3_V_TEST_FN: if 5C0B DEFADD is zero, jump on to V
RUN/SYN
- (DEF FN is in use) jump on to STK F ARG; if it doesn't
find a matching variable in the DEF FN statement it will jump
back to V RUN/SYN.
_28EF_V_RUN/SYN: in syntax checking, AND the
discriminator byte with 11000000b/E0h and set its bit 7 for a
syntax flag; now all its bits are zero except bit 7 and, if it
isn't a long name or a number array, bit 6
- jump on to V SYNTAX.
_28FD_V_RUN (run time): get the address of the start of
the variables area from 5C4B VARS
_2900_V_EACH: get the discriminator byte of the next
variable in the variables area
- AND it with 01111111b/7F
- if this comes out zero, jump on to V 80 BYTE; it is
the 80-byte marking the end of the variables area and there is
no match to the variable in assignment.
- (it isn't zero, and the only change has been in
clearing its bit 7) check it with the discriminator byte
constructed for the variable in assignment
- if they don't match, jump on to V NEXT; because bit 7

487
isn't compared, this doesn't distinguish one-letter numeric
variables (011) from looping variables (111), or simple strings
(010) from string arrays (110)
- (variable in assignment matches variable in variables
area) rotate the discriminator byte left and double it; the
original bit 6 goes into the carry and the original bit 5 goes
to hi bit
- if bit 5 was zero jump on to V FOUND 2; strings and
all arrays
- if bit 6 was set jump on to V FOUND 2; short names and
loop variables.
_2912_V_MATCHES (this leaves long names): move on the
variables area pointer
_2913_V_SPACES: read the BASIC pointer and move it on
- if the BASIC byte is a space jump back to V SPACES;
skip it
- make the BASIC byte lower case
- if it now matches the byte at the variables pointer
jump back to V MATCHES; test another character.
- try it with bit 7 set; the last character of the name
in the variables area is so marked
- if they still don't match jump on to V GET PTR; this
isn't the right variable
- (last character in the variables area matches the name
in BASIC) test the next BASIC byte with 2C88 ALPHANUM
- if it isn't alphanumeric jump on to V FOUND 1; but if
there are more characters in the BASIC name, it doesn't count
as a match.
_2929_V_GET_PTR and_292A_V_NEXT (the variable names
don’t match): put the variables pointer back on the
discriminator byte
- call 19B8 NEXT ONE; it puts the pointer on the
discriminator byte of the next variable
- jump back to V EACH to check this one with the
discriminator byte in the BASIC.

488
_2932_V_80_BYTE (nothing in the variables area matches
the discriminator byte from BASIC) set the hi bit of the
discriminator.
_2934_V_SYNTAX: read the byte at the BASIC pointer; this
was left after the last letter of the variable name in the
BASIC, or after the "$" if there was one
- if it is 28h (, jump on to V PASS; an array
- set bit 5 of the discriminator; "simple variable"
- jump on to V END.
_293E_V_FOUND_1 and_293F_V_FOUND_2: clear the stack
- restack the variables pointer
- read the byte at the BASIC pointer; this was left
after the last variable letter in BASIC, or the "$" if any.
_2943_V_PASS: step on the BASIC pointer till it is on a
byte which isn't a letter or digit [quite unnecessary, as the
notes indicate].
_294B_V_END: recover the variables pointer
- rotate the discriminator left and read what was bit 5
- return from LOOK VARS.
(See STK VAR below.)
_2951_STK_F_ARG (run time, and 5C0B DEFADD has a DEF
FN address): set a pointer on the first variable letter on the left
side of the FN statement
- if the pointer is on 29h ), jump back to V RUN SYN;
the DEF FN has no arguments.
_295A_SFA_LOOP: read the pointer and set bits 5 and 6 of
the discriminator for a simple numeric variable
- read the next byte, and if it is 0E number marker jump
on to SFA CP VR
- (the variable is a string) call 28AB FN SKPOVER, which
puts the pointer on the "$", and move on one; now it is on the
0E marker just before the string parameters
- zero bit 5 of the discriminator; "string".
_296B_SFA_CP_VAR: check the DEF FN variable letter with
the discriminator byte
- if they match jump on to SFA MATCH
- (no match yet) move the DEF FN pointer on five bytes

489
and call 28AB FN SKPOVER; now it is on comma or 29h )
- if it is 29h ) jump back to V RUN/SYN; the variable
isn't in the DEF FN statement
- (it must be a comma) call 28AB FN SKPOVER again; now
it is on the next variable letter
- loop back to SFA LOOP.
_2981_SFA_MATCH: check bit 5 of the discriminator byte
and jump on to SFA END if it is set; this marks a numeric
variable
- (a string variable) move the pointer on to the first
byte of the string parameters
- set DE on the stack end and call 33C0 MOVE FP to copy
the five string parameter bytes on to the end of the calculator
stack.
_2991_SFA_END: drop the top two values on the machine
stack; they were the variables pointers
- make flags for NC and NZ, signalling "variable found"
and "no need to call STK VAR"; because the string parameters,
if it was a string, have been stacked already
- return from LOOK VARS.
At this point
- HL holds a start address for the variable: if nothing
was found to match in the variables area, the first letter of
the variable name in the BASIC line, otherwise the last letter
of the name in the variables area
- C holds a discriminator byte "abcxxxxx":
a is one for syntax checking, zero for run time
bcxxxxx indicates what was being looked for,
bc is 00 for number arrays
01 for number variables with long names
10 for strings and string arrays
11 for number variables with short names
xxxxx indicate the variable letter in run time, all zero
in syntax checking.
- the zero flag shows Z to signal "looking for an array
or string" or NZ to signal "simple numeric variable". It was

490
made NZ in V END if bit 5 of B was set; this became bit 6 after
the rotate instruction. This B is a version of the "abcxxxxx"
byte copied from C in V RUN/SYN: its bit 5 was cleared in the
first section of LOOK VARS, then set in the same section if no
"(" followed the numeric variable letter in the BASIC, or in V
SYNTAX if no "(" followed the "$" of the string variable letter.
But V SYNTAX only operates in syntax checking, so in run
time the flag will show Z for a simple string as well as an array
- the carry flag in run time shows C if a matching
variable wasn't found, NC if it was; in syntax checking it show
carry anyway.
- M in the M/P flag signals "not a number array"; this
was set in V EACH.
The search of the variables area is therefore complete,
unless the Z flag shows zero; an array element or slice is
required, and the carry flag shows NC; the required variable
has been found.
When both these conditions exist the search is completed
by a call to STK VAR, with the parameters listed above. In
26C9 S LETTER, indexed under 24FB SCANNING, the call
follows immediately, except for a "Variable not found" report
if the carry flag was set; in other cases the following routine
comes before checking the flags and calling STK VAR:
_1C22_VAR_A_1: zero FLAGX, signalling
in bit 1, "old variable found"
in bit 0, "slice or array element"; in other words,
"don't delete the old variable copy". The other bits are
irrelevant here.
- if there is no carry jump on to VAR A 2; the variable
was found. It is an array or string if the Z/NZ flag shows zero
- (new variable) set bit 1 of FLAGX; "new variable"
- if the Z flag shows NZ jump on to VAR A 3; a simple
variable. See below, after STK VAR
- (an array) report "Variable not found"; the array
should have been set up by a DIM.
_1C30_VAR_A_2 (declared variables): if the flag shows

491
zero call 2996 STK VAR, misprinted VARS; the continuation of
VAR A 2 is described following STK VAR below.
_2996_STK_VAR subroutine (a variable is found and
identified as an array or string): zero an array/string index;
signalling an array
- zero a dimension counter
- if syntax is being checked jump on to SV COUNT; this
is a jump into the element counting loop with a loop counter
of zero - in effect 256d. The loop is turned, without concern
for anything except correct placing of commas and subscripts
being within limits, until either a 29h ) is encountered in the
BASIC or 256d turns have been completed. As only 255d
dimensions are
allowed, completing 256d turns counts as an error
- (run time) check the hi bit of the code at the
variables pointer; simple strings have zero there
- if it isn't zero jump on to SV ARRAYS; an array
variable
- (simple string) increment the array/string index; one
for a simple string.
_29A1_SV_SIMPLE$: step on the variables pointer and read
the length bytes which follow the variable letter
- put the pointer on the start of the actual string
- call 2AB2 STK STO $, misprinted STK STORE, to put the
start address, length and array/string index on the calculator
stack; they are the string parameters
- read the code in the BASIC line and jump on to SV
SLICE?.
_29AE_SV_ARRAYS (array variable, numeric or string, run
time): move the variables pointer past the length bytes
- get the dimension counter from the dimension number
of the array
- check bit 6 of the "abcxxxxx" byte; zero for numeric,
one for strings
- if it is a numeric array jump on to SV PTR
- (string array) decrement the dimension counter; the

492
last dimension, the number of characters in each element,
gets special treatment later in SV ELEM$
- if this makes zero jump back to SV SIMPLE$; the string
array had only one dimension. Handle it as a simple string,
but with the array/string index still zero
- read the code at the BASIC pointer
- if it isn't 28h ( report "Subscript wrong"; there must
be at least one subscript. This is run time, but this error
would be missed in syntax checking.
_29C0_SV_PTR: jump on into the element counting loop at
SV COUNT.
_29C3_SV_COMMA (the element counting loop returns to
here after reading each dimension until the dimension
counter steps down to zero; HL holds the element count so
far and the BASIC pointer has been moved on past the last
subscript): read the code in BASIC
- if it is a comma, jump on to SV LOOP; there is another
subscript
- (not a comma, no more subscripts) check the syntax
flag
- if this is run time report "Subscript wrong"; as the
loop counter hasn't reached zero, there_ought to be another
subscript
- (syntax checking) check the string/numeric flag
- if it isn't zero jump on to SV CLOSE; a string array,
the code may be CC TO or 29h )
- (numeric array) if the BASIC code isn't 29h ), report
"Nonsense in BASIC"
- (finished syntax checking numeric array) move on the
BASIC pointer and return; see VAR A 2 continued below.
_29DB_SV_CLOSE (syntax checking string array): if the
code is 29h ), jump on to SV DIM; the BASIC may hold
something like a$(x)(y), indicating a slice
- if the code isn't CC TO report "Nonsense in BASIC".
_29E0_SV_CH_ADD (still syntax checking string arrays; the
BASIC code just read is TO): move the BASIC pointer back one
and jump on to SV SLICE.

493
_29E7_SV_COUNT (entry point to the element counting
loop): zero the element counter; it will accumulate the serial
number of the specified element in the string of elements
following the variable name in the variables area.
_29EA_SV_LOOP: move on the BASIC pointer; now on the
subscript in BASIC
- check the syntax and string/array flags and jump on to
SV MULT unless_both flags are set
- (syntax checking of string arrays) read the code under
the BASIC pointer
- if it is 29h ) jump on to SV DIM; there are no more
subscripts
- if it is CC TO jump back to SV CH ADD; for slicing.
_29FB_SV_MULT (DE is the variables pointer, one before
the address of the dimension size in the variables area) call
2AEE DE,(DE+1) to get the dimension size corresponding to
the next subscript and move on the variables pointer to the
address of the next dimension. See NB 1. above for the
calculation. The first dimension size is the b in Y = X * b + (y -
1)
- call 2ACC INT EXP1 with the dimension size as limit;
it gets the subscript y
- if y > b report "Subscript wrong"
- decrement y to y - 1
- call 2AF4 GET HL*DE to get X * b
- add (y - 1); the result is the updated element counter
- loop back to SV COMMA, counting down the dimension
counter
- (dimension counter has gone to zero) if bit 7 of the
"abcxxxxx" byte is set flag NZ; syntax checking, and the loop
has made 256d turns.
_2A12_SV_RPT_C: if NZ is flagged report "Nonsense in
BASIC"
- if bit 6 of the "abcxxxxx" byte is set jump on to SV
ELEM$; string array
- (numeric array) if the code at the BASIC pointer isn't
29h ) report "Subscript wrong".

494
_2A22_SV_NUMBER (numeric array, the element counter
holds the number of elements up to but not including the one
specified): move on the BASIC pointer; now one past the ")"
- call 2AF4 GET HL*DE with the element counter and 05;
all the elements of a number array are FP numbers, occupying
five bytes each
- add the result to the variables pointer; it had reached
the address of the last dimension number in the
variables area, just before the first element, so this makes the
address of the first byte of the specified element
- return; see VAR A 2 continued below.
_2A2C_SV_ELEM$ (string array, the element counter holds
the number of elements up to but not including the one
specified): call 2AEE DE,(DE+1) once more; the dimension
counter was decremented in SV ARRAYS, so there is one more
dimension, the number of characters in each element
- call 2AF4 GET HL*DE to multiply the element counter by
the dimension size for the last time; getting the total number
of characters in all the elements up to but not including the
one specified
- add the result to the variables pointer; it had
reached the address of the last dimension number in the
variables area, just before the first element, so this makes the
address of the first byte of the specified string element
- call 2AB1 STK ST 0 to put the string parameters on the
stack indexed as an array; the dimension size is the length and
the final variables pointer address is the start
- read the code at the BASIC pointer
- if it is 29h ) jump on to SV DIM
- if it isn't a comma report "Subscript wrong".
_2A45_SV_SLICE (entry may be from SV CH ADD, SV
SLICE? or direct from SV ELEM$, but the BASIC pointer is in
any case on the code before a separate slicing subscript in
brackets, or before the last subscript of a string array, which is
always read as a slice): call 2A52 SLICING to change the string
parameters to those of the slice.

495
_2A48_SV_DIM (entry may be from SV CLOSE, SV LOOP, SV
DIM, or direct from SV SLICE, but the BASIC pointer is always
on a closing bracket): move on the BASIC pointer.
_2A49_SV_SLICE? (entry may be from SV SIMPLE? or
direct, but the BASIC pointer in any case has just moved past a
string array specification): if the code is 28h ( jump back to SV
SLICE
- zero bit 6 of FLAGS; "string result"
- return. In the most important case return is into:
_1C30_VAR_A_2 (continued): if bit 6 of FLAGS is set jump
on to VAR A 3; the variable is numeric
- (string variables) zero an array/slice flag; for use
if syntax checking
- in run time, call 2BF1 STK FETCH, which gets the start
address of the string into DE, its length into BC, and the
array/slice flag in A; 00 for arrays/slices, ie "overwrite old
copy", 01 for simple strings ie "delete old copy". In syntax
checking, the array/slice flag is left at zero
- transfer the array/slice flag to bit 0 of FLAGX.
_1C46_VAR_A_3: put the values from STK VAR in 5C72
STRLEN and 5C4D DEST; these are different values according
to the string/numeric status and declared/undeclared status of
the variable: for details see 5C72 STRLEN.
- return.
Exit (from STK VAR; as usual, not counting error
reports): RETs - from SV COMMA if checking syntax of
numeric arrays
- from SV NUMBER if a number element was found in run
time
- from SV SLICE? for string arrays.
Output parameters: if a number element was found, HL
holds the address of its first byte. Otherwise string parameters
on the calculator stack. FLAGS bit 6 shows the status of the
result
- for the values in 5C72 STRLEN and 5C4D DEST see 5C72
STRLEN.
LOOK VARS called from:

496
0652 SA DATA
1C1F CLASS 01
1C6C CLASS 04
26C9 S LETTER
2C02 DIM
STK VAR called from:
1C30 VAR A 2 (misprinted STK VARS)
26C9 S LETTER (misprinted STK VARS)
2C05 D RPORT C
Rems:
27BD S FN SBRN called by SCANNING (26C9 S LETTER)
288D SF VALUE sets DEFADD to make it check DEF FN
2951 STK F ARG (see STK F ARG under LOOK VARS for
comment on this baffling note)
2981 SFA MATCH vble found in DEF FN, no need
2991 SFA END drops some pointers from LOOK VARS

loop see timing

loop control variable, looping line and statement numbers,


looping variable of FOR ... NEXT loop, see variables, FOR ...
NEXT loops

loop counter see 5C67 BREG

loudspeaker, LOUDSPEAKER ROUTINES see 03B5 BEEPER

lower case letters see character codes

lower display, lower part of display/screen lower print


line, lower screen see DISPLAY AREA

LOWER SCREEN COPYING SUBROUTINE see 111D ED COPY

LPRINT key (E0) see also commands, functions and


operators,
KEYBOARD SCANNING, 022C extended mode key table (b)

497
The C key in E mode without shift produces the
command
LPRINT. The command can be followed by all the same
parameters, etc, as the PRINT command, but the colour
controls are ineffective. It produces printout through stream
3, ie to the ZX printer.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AD9 P LPRINT causes a jump via 1C11 CLASS 05 and 1C16
JUMP C R to the executive routine 1FC9 LPRINT.

LPRINT subroutine 1FC9


Called only from 1AD9 P LPRINT in the syntax parameter
table; executes the LPRINT command.
Exactly like 1FCD PRINT, see index entry, except that
channel P is opened instead of channel S, ie output is sent to
the ZX printer not to the screen.

L SINGLE 2B4F (2AFF LET)


Jumps from:
2B29 L SPACES

L SPACES 2B29 (2AFF LET)


Jumps from:
2B0C L NO SP (twice)

L STRING subroutine 2BC6


Adds a new complete simple string variable at the end of
the variables area.
Input parameters: A holds the variable letter, with the
first three flag bits already correct; 010 for a simple string
- the string parameters are the last value on the
calculator stack.
Action: call 2BF1 STK FETCH to put the string parameters
in BCDEA
- add the length to the start address; making a pointer

498
to the last byte of the string
- save this address in 5C4D DEST
- increment the length by three; allowing three extra
bytes for the variable letter and the string length
- get a pointer from 5C59 E LINE to the end of the
variables area and decrement it to below the 80-byte
- call 1655 MAKE ROOM to make room for the string at the
end of the variables area
- copy the string from its last byte whose address is in
5C4D DEST, with an extra byte in case its length is zero; the
extra byte is immediately overwritten by the length bytes
- insert the length bytes just after the position for
the variable letter.
Exit: into 2BEA L FIRST, which puts the variable letter
byte in the place of the old 80-byte.
Output parameters: A still holds the variable letter
- HL holds the address one past the required address
- BC holds the length.
Called from:
2BAF L ADD$
Exit from:
2AFF LET
2BC0 L NEW$

L TEST CH 2B1F (2AFF LET)


Jumps from:
2B0C L NO SP

499
M

machine stack see GO SUB stack

MAIN ADD 155D (see BASIC INTERPRETER)


Jumps from:
12CF MAIN 3
MAIN ADD1 157D (see BASIC INTERPRETER)
Jumps from:
155D MAIN ADD

MAIN ADD2 15AB (see BASIC INTERPRETER)


Jumps from:
157D MAIN ADD1

main code of key see KEYBOARD SCANNING

MAIN EXEC 12A2, MAIN EXECUTION LOOP, see BASIC


INTERPRETER
Jumps from:
12CF MAIN 3
15AB MAIN ADD2

MAIN G 1313 (12A2 MAIN EXEC, see BASIC INTERPRETER)


Jumps from:
1555 REPORT G

main key table 0205 see KEYBOARD SCANNING

MAIN PARSER OF THE BASIC INTERPRETER see BASIC


INTERPRETER,
1B17 LINE SCAN

main part of screen see upper screen

500
MAIN PRINTING SUBROUTINE see 15EF OUT CODE

main screen see upper screen

MAIN 1 12A9 (see BASIC INTERPRETER)


Jumps from:
1219 RAM SET

MAIN 2 12AC (see BASIC INTERPRETER)


Jumps from:
1386 MAIN 9
auto

MAIN 3 12CF (see BASIC INTERPRETER)


Jumps from:
12AC MAIN 2

MAIN 4 1303 (see BASIC INTERPRETER)


This address in the main execution loop is the error
address during BASIC execution as well as the return address
from the call to 1B8A LINE RUN in 12CF MAIN 3 on completion
of execution. Thus all inputs return here eventually.
Jumps from:
12AC MAIN 2

MAIN 5 133C (see BASIC INTERPRETER)


Jumps from:
1313 MAIN G

MAIN 6 1373 (see BASIC INTERPRETER)


Jumps from:
133C MAIN 5

MAIN 7 1376 (see BASIC INTERPRETER)


Jumps from:
133C MAIN 5

501
MAIN 8 1384 (see BASIC INTERPRETER)
Jumps from:
1376 MAIN 7

MAIN 9 1386 (see BASIC INTERPRETER)


Jumps from:
133C MAIN 5

MAKE BC SPACES RESTART see 0030 BC SPACES

MAKE EXPT 313B (30CA multiply)


Jumps from:
3125 STRT MLT

MAKE ROOM subroutine 1655


Shifts a block of memory upwards by a specified number
of bytes and adjusts the fourteen system pointers as needed,
see
1664 POINTERS. Used to open up a space for new data in any
of the areas from the microdrive maps upwards. It can be
called through 0030 BC SPACES, which always makes space
in the work
space, or 1652 ONE SPACE if only a single space is required.
Input parameters: HL points to the first byte to be moved
- BC holds the number of new spaces required.
Action: call 1F05 TEST ROOM to check for "Out of
memory"
- call 1664 POINTERS which adjusts the pointers and
loads BC with the number of bytes to be moved
- put a pointer on the new address of 5C65 STKEND
- move up the block of bytes from the pointer down to
the address set on input.
Exit: RET.
Output parameters: the new space made isn't blank, but
still contains a copy of the bytes in the area immediately
above it

502
- HL holds the address of the last byte below the lower
version
- DE that of the last below the upper one; the notes are
confusing, the "second description" as given is valid only
when the call was to 1652 ONE SPACE.
Called from:
084C LD DATA 1
0873 LD PROG
093E ME ENT 1
0955 ME ENT 2
0F6C ED CONTR
157D MAIN ADD1
169E RESERVE
1D16 F REORDER
1F94 DEF FN 5
268D S DECIMAL
2B29 L SPACES
2BC6 L STRING
2C2E D NO LOOP
Exit from:
1652 ONE SPACE
Rems:
0F81 ADD CHAR make single space in editing or input
0F8B ADD CH 1 put new input in space
255D S SC ROWS make space in work space for character
25BE S Q AGAIN make space in work space for new string
2B72 L DELETE$ make room in work space
361F str$ make space in work space for character

making room see 0030 BC SPACES, 1655 MAKE ROOM, 1652


ONE
SPACE, 1664 POINTERS

manipulatory operations see 0028 FP CALC

mantissa see CALCULATE

503
marker bit see 8-bit loop after end of alphabet

marker byte (sign) see 2F9B PREP ADD

maskable interrupt see interrupts, 0038 MASK INT

MASK INT restart subroutine 0038


Called 50d times per second by the maskable interrupt,
except during BEEPS, LOAD/SAVE operations, and COPY.
Works the clock and reads the keyboard. Can be called from
m/c, and this is sometimes useful for programs which run
with the interrupt disabled. It enables the interrupt.
Input parameters: none.
Action: increment the two lo bytes of the 3-byte system
variable 5C78 FRAMES, see index entry
- if the lo bytes are now zero, increment the hi byte.
_0048_KEY_INT: call 02BF KEYBOARD to read the
keyboard
- re-enable the interrupt; any interrupt call to the Z80
chip automatically disables it.
Exit: RET.
Output parameters: all the main registers are saved and
restored unchanged
- IX and the alternate registers aren't used.
Rems:
02BF KEYBOARD called by

MASK P system variable 5C8E


Bytes: 1
The byte following ATTR P; they are handled together as
a single system variable. Copied as required to 5C60 MASK T.
Written by:
1C96 PERMS (with ATTR P)
Read by:
0D4D TEMPS (with ATTR P)

masks

504
Strictly, a mask is a byte used to combine two other
bytes by an XOR/AND/XOR: A is XORed twice with another
“setting" byte, but ANDed in between with the mask in some
other register.
Where the mask has its bits set, the two XORs cancel out and
A keeps its original bits, but where the mask has zero bits or
"holes" the bits of A are replaced by the bits of the setting
byte: they are zeroed by the mask, and then the second XOR
sets or zeroes them as the setting byte is one or zero.
Eg:
if A is 0 0 0 0 1 1 1 1 b,
the mask is 0 1 0 1 0 1 0 1 b,
the setting byte is 0 0 1 1 1 1 0 0 b:

XOR A with the setting byte: 0 0 1 1 0 0 1 1 b


AND this with the mask: 0 0 0 1 0 0 0 1 b
XOR with the setting byte again: 0 0_1 0_1 1_0 1 b.
This has the bits of A except where "holes" in the mask
have "let through" the underlined bits from the setting byte.
See 0BDB PO ATTR, 1C96 CLASS 07, 22AA PIXEL ADD
and 226C
CO CHANGE for examples. The term is also used when the
mask byte is combined with one other byte, to zero or set
particular bits.
1C96 PERMS copy even bits of P FLAG to odd: 10101010b/
AA
2211 CO TEMP 5 mask for P FLAG - OVER/INVERSE
2228 CO TEMP 6 mask in B register
2234 CO TEMP 7 mask for P FLAG - INK/PAPER
2258 CO TEMP B mask in B for ATTR T, MASK T, P FLAG
226C CO CHANGE use of the mask
2273 CO TEMP C mask for FLASH/BRIGHT
3214 truncate used to zero bits after fraction point
3221 T GR ZERO used to zero FP 91 80 00 00 00/-65536d
3283 BITS ZERO prepares mask to truncate byte
328A LESS MASK loop to shorten mask

MASK T system variable 5C90


Bytes: 1

505
The byte following ATTR T; the two are usually handled
together as a single system variable. Used as a mask in setting
attributes: any bit which is set in the mask is left unchanged
from what is on screen already, see 0BDB PO ATTR.
ATTR T and MASK T are the attribute svs actually used in
setting screen attributes, but in every case they are copied
from other svs - either 5C8D ATTR P and MASK P for the
upper screen, or 5C48 BORDCR and zero for the lower.
Written by:
0C88 PO SCR 2 (with ATTR T)
0D2D PO SCR 4B (with ATTR T)
0D5B TEMPS 1 (with ATTR T)
18C1 OUT FLASH (with ATTR T)
Read by:
0BDB PO ATTR (with ATTR T)
0C88 PO SCR 2 (with ATTR T)
0D02 PO SCR 4 (with ATTR T)
18C1 OUT FLASH (with ATTR T)
1CBE CLASS 09
Rems:
0D4D TEMPS loaded either with 00 or with MASK P
2211 CO TEMP 5 altered by embedded colour items
2245 CO TEMP 9 prepare to alter (load base address)
2258 CO TEMP B changed by prepared mask
2287 CO TEMP E changed by prepared mask

"matching" subroutine (for strings) see 250F S QUOTE S

ME CONTRL 08B6 (0605 SAVE ETC)


Exit from:
07AD LD CH PR

ME ENTER subroutine 092C


MERGEs a single BASIC line or variable, which has been
read into the work space from tape, into the program area: ie
adds it, deleting any line with the same line number or
variable with the same discriminator byte. The old line/

506
variable is deleted from the BASIC before the new one is
copied, but the new one remains in the work space till it has
been MERGEd; so if there is pressure on memory, an "Out of
memory" report can occur.
Input parameters: HL is a work space pointer, holding the
address in the work space of the first byte of the new line or
variable to be MERGEd
- DE is a BASIC pointer, holding the address in the
program or variables area of the first byte of the old line with
the same or next highest number, or in the case of a variable,
the first byte of the matching variable or the last byte in the
variables area
- carry in the C flag indicates that a variable is being
handled
- zero in the Z/NZ flag that there is a match between
old and new and the old line or variable is to be reclaimed.
Action: if the flag shows NZ jump on to ME ENT 1; there
is no old line/variable to be reclaimed
- (reclaiming required) save the flags
- save the work space pointer in 5C5F X PTR
- call 19B8 NEXT ONE and 19E8 RECLAIM 2, which delete
the old line/variable from the program/variables area; since X
PTR is one of the "fourteen pointers", the work space pointer
will be adjusted by 1664 POINTERS to compensate for the
reclaim
- recover the flags and pointer.
_093E_ME_ENT_1: save the flags again
- call 19B8 NEXT ONE again, this time for the length of
the new line/variable
- save the start address in 5C5F X PTR; again it will be
adjusted to compensate for the "make room" to follow
- save the value of 5C53 PROG, in case room has to be
made there; cf 157D MAIN ADD 1
- save the length and recover the flags
- if carry was set jump on to ME ENT 2; a variable is
being handled
- (handling a BASIC line) make room just before the

507
BASIC pointer
- jump on to ME ENT 3.
_0955_ME_ENT_2 (handling a variable): make room_at the
BASIC pointer in or at the end of the variables area.
_0958_ME_ENT_3: recover the pointers and stack them
again
- copy the line/variable into its proper place.
- recover the pointers again and call 19E8 RECLAIM 2
again; each line/variable is deleted from the work space when
it has been copied, to avoid excessive demands on the
memory.
Exit: RET, from 0958 ME ENT 3.
Output parameters: DE holds a BASIC pointer to the start
of the next line/variable in the program or variables area.
Called from:
08EB ME NEW L2
0923 ME VAR L2

ME ENT 1 093E (092C ME ENTER)


Jumps from:
092C ME ENTER

ME ENT 2 0955 (092C ME ENTER)


Jumps from:
093E ME ENT 1

ME ENT 3 0958 (092C ME ENTER)


Exit from:
092E ME ENTER through one of:
093E ME ENT 1
0955 ME ENT 2

MEM system variable 5C68


Bytes: 2.
Holds the address of the first byte of the calculator
memory, normally the address of 5C92 MEMBOT. See 335B
CALCULATE.

508
The memory can be relocated by m/c programming to
any area in RAM; five bytes are required for each FP number
location. There are only six locations in the MEMBOT system,
but if memory is relocated 340F get-mem and 342D st-mem
can address up to 32d locations through calls by CALCULATE
literals, and up to 33h/51d locations by direct calls, see entry
on get-mem. MEM is reset to 5C92 MEMBOT each time 16C5
SET STK is called.
The memory is temporarily relocated in the ROM by 1D16
F REORDER, leading into 1D34 F L&S, see under 1D03 FOR;
here 15d places in a FOR ... NEXT loop control variable are
made into a memory area for the VALUE, LIMIT, and STEP of
the loop.
Written by:
16C5 SET STK
1D16 F REORDER
1DAB NEXT
Read by:
340F get-mem
342D st-mem

MEMBOT system variable 5C92


Bytes: 1Eh/30d
The area set aside for the calculator memory; see 335B
CALCULATE and MEM above. Usually it is written to by 342D
st- mem and read by 340F get-mem, but of course it can also
be written or read directly: either straightforwardly, for
example
5CAC the second byte of mem-5 is set at 2E56 PF LARGE, or
using the IY index. Since IY is set at 5C3A, MEMBOT is IY+58h,
and for example the first byte of mem-2 is IY+62h; see 235A C
ARC GE1.
The MEMBOT area can also be used for other purposes,
provided it doesn't matter that the calculator memory will be
corrupted. Eg in ROM:
- the first eight bytes are used to construct a

509
character form for one of the standard "graphics" at 0B24 PO
ANY
- the first five bytes are used as a spare calculator
stack for line numbers at 19FB E LINE NO
- the first byte is used as a flag at 2D55 E SAVE
- mem-3 and the 14d bytes following are used as a print
buffer for number print-outs by 2DE3 PRINT FP and following.
Written by:
0B38 PO GR 1 (with 0B4C PO GR 3)
235A C ARC GE1
2D55 E SAVE (with 350B FP 0/1)
2E24 PF SMALL
2E56 PF LARGE
2E7B PF BITS
2E8A PF BYTES
2EA9 PF INSERT
2EB8 PF ALL 9
2EEF PF FR EXX
2F18 PF RND LP
2F25 PF R BACK
2F2D PF COUNT
Read by:
03F8 BEEP
0B24 PO ANY (with 0B7F PR ALL, etc)
16C5 SET STK
19FB E LINE NO
2E24 PF SMALL
2E56 PF LARGE
2E8A PF BYTES
2EB8 PF ALL 9
2EDF PF FRN LP
2EEF PF FR EXX
2F0C PF ROUND
2F2D PF COUNT
Rems:
16C5 SET STK value of MEMBOT put in MEM
19FB E LINE NO value of MEMBOT put in STKEND

510
2DE3 PRINT FP ad hoc print buffer at MEMBOT
2E6F PF MEDIUM used to store integer part of number

memory (calculator) see 335B CALCULATE

MEMORY LOCATION SUBROUTINE see 3406 LOC MEM

memory map
Addresses below 4000h are in ROM, read-only memory,
and can be read but not written; attempts to POKE them from
BASIC or m/c have no effect at all. 4000h and above are in
RAM, random-access memory, and can be both written and
read, though POKing some addresses can have disastrous or
unpredictable effects.
----------------------------------------start up from here; ROM
0000 restarts
0095 token table and key tables
028E keyboard routines
03B5 loudspeaker routines
04C2 cassette handling
09F4 screen and printer handling
0F2C editor
11B7 initialisation
12A2 main execution loop
15AF channel input/output and miscellaneous
1A48 BASIC interpreter
24FB expression evaluation - the SCANNING loop
2D4F calculator routines, including:
32C5 constants and literals
335B calculator
386E 0592h/1426d empty bytes
3D00 character set
---------------------------------------------------start of RAM
4000 display first third
4800 middle
5000 last
5800 attributes

511
5B00 ZX printer buffer
5C00 system variables: 5C00-07 KSTATE
5C10-35 STRMS
5C3A ERR NR <- IY + 00
5C92-AF MEMBOT
-------------------------------------------5CB6 microdrive maps
The remainder of the memory is movable, depending on
the space required for each area from the microdrive maps
upwards.
When space is required, it is made upwards in areas up to the
top of the calculator stack, downwards from SP and above. A
collision between these two results in an "Out of memory"
report, see 1F65 TEST ROOM. Start addresses will be found in
the system variables shown.
-----------------------------------------------------5C4F CHANS
channel information
80-byte
------------------------------------------------------5C53 PROG
program area
------------------------------------------------------5C4B VARS
variables area
80-byte
----------------------------------------------------5C59 E LINE
editing area
newline (0D)
80-byte
----------------------------------------------------5C61 WORKSP
INPUT area
newline (0D)
workspace
----------------------------------------------------5C63 STKBOT
calculator stack; grows upwards
----------------------------------------------------5C65 STKEND
spare memory
-------------------------------------------------------------SP
machine stack; grows downwards
----------------------------------------------------5C3D ERR SP

512
GO SUB stack
----------------------------5CB2 RAM TOP (usually 7F57 or FF57)
3Eh byte
--------------------------------5C7B UDG (usually 7F58 or FF58)
user-defined graphics
----------------------------------------------------5CB4 P RAMT
top of RAM; the top of RAM is 8FFF in the 16K Spectrum,
FFFF in the 48K and later versions operating in the 48K mode.

mem-0, mem-1, &c see 335B CALCULATE, 340F get-mem,


5C92
MEMBOT, 342D st-mem

ME NEW LP 08D2 (08B6 ME CONTRL)


Jumps from:
08EB ME NEW L2

ME NEW L2 08EB (08B6 ME CONTRL)


Jumps from:
08DF ME OLD L1

ME OLD LP 08D7 (08B6 ME CONTRL)


Jumps from:
08DF ME OLD L1

ME OLD L1 08DF (08B6 ME CONTRL)


Jumps from:
08D7 ME OLD LP

ME OLD VP 08F9 (08B6 ME CONTRL)


Jumps from:
0901 ME OLD V1

ME OLD V1 0901 (08B6 ME CONTRL)


Jumps from:
091E ME OLD V4

513
ME OLD V2 0909 (08B6 ME CONTRL)
Jumps from:
08F9 ME OLD VP

ME OLD V3 0912 (08B6 ME CONTRL)


Jumps from:
auto

ME OLD V4 091E (08B6 ME CONTRL)


Jumps from:
0912 ME OLD V3

MERGE key (D5) see also commands, functions and


operators,
KEYBOARD SCANNING, 022C extended mode key table (b)
The T key in E mode without shift produces the
command MERGE. The "MERGE statement" must include a
filename but, unlike LOAD, cannot have modifiers such as
CODE.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AE2 P MERGE causes a jump via 1CDB CLASS 0B to the
executive routine for all the cassette handling commands,
0605 SAVE ETC.
The routine distinguishes between SAVE, LOAD, VERIFY and
MERGE by looking at T ADDR, which was used to step
through the 1A7A table: its low byte reads one more than the
low byte of the "P"
label, E0, E1, E2 or E3 respectively, so E3 for MERGE.
04C2 SA BYTES start of cassette handling routines
0605 SAVE ETC make code 03 from T ADDR lo
0629 SA BLANK null names allowed for MERGE
0652 SA DATA not allowed MERGE ... DATA
06A0 SA SCR$ not allowed MERGE ... SCREEN$
06C3 SA CODE not allowed MERGE ... CODE
075A SA ALL prepare to fetch header from tape

514
07AD LD CH PR jump forward for MERGE
0802 LD BLOCK called (not exit) by ME CONTRL
08B6 ME CONTRL main executive routine
08F0 ME VAR LP variables MERGEd
092C ME ENTER single line/variable MERGEd

MERGE A LINE OR A VARIABLE SUBROUTINE see 092C ME


ENTER

MERGE CONTROL ROUTINE see 08B6 ME CONTRL

message block, message printing, messages see also 0C0A


PO
MSG
Messages for printing on the screen are in ROM at:
0095 token table
09A1 cassette messages
0CF8 "scroll?"
1391 report messages
The "report messages" block includes the "comma space"
used in printing report messages, and the Sinclair copyright
message printed on start-up or NEW.
All these are printed by PO MSG, though for tokens the
entry point is 0C10 PO TOKENS.
In the notes on 20D8 IN ITEM 2 and
following,_"prompt_message" refers to the flashing cursor -
sometimes in quotes - printed in execution of an INPUT
command.
078A LD TYPE prints appropriate cassette message
0970 SA CONTRL prints "Start tape and press any key"
0C22 PO EACH loop to print each character
0C88 PO SCR 2 prints "scroll?"
1219 RAM SET prints copyright message
133C MAIN 5 prints report messages
20D8 IN ITEM 2 prepare prompt message for INPUT ...
LINE
20FA IN PROMPT print prompt message

515
MESSAGE PRINTING SUBROUTINE see 0C0A PO MSG

ME VAR LP 08F0 (08B6 ME CONTRL)


Exit from:
08B6 ME CONTRL through one of:
08D2 ME NEW LP
0923 ME VAR L2

ME VAR L1 0921 (08B6 ME CONTRL)


Jumps from:
0909 ME OLD V2
0912 ME OLD V3

ME VAR L2 0923 (08B6 ME CONTRL)


Jumps from:
08F9 ME OLD VP

MIC output see ports

minuend see addend

misprints in "ROM Disassembled"


These are really excessive, even allowing for the
complexity of the text; though many are trivial, and most of
them are in the notes, not in the listings, which for the most
part are accurately printed. Even trivial misprints can be
thoroughly confusing in a text of this kind. A few "misprints"
may actually be mistakes by the authors.
In this list, for brevity I use curly brackets {} to
indicate what I believe to be a misprint, and square brackets []
for the presumed correct reading.
0055 ERROR 3 {machine} : [machine stack]
02C6 K ST LOOP {set as free} : [set is free]
03F8 BEEP [the 12th root of 2, minus one]
05CA LD 8 BITS {carry flat set} : [carry flag set]
08B6 ME CONTRL {Now made} : [Now make]

516
0970 SA CONTRL {straighforward} : [straightforward]
0A87 PO CONT {CO TEMPS} : [CO TEMP 5]
{+21 - +22} : [+21 - +02]
0B24 PO ANY {0B7F PO ALL} : [0B7F PR ALL]
0B52 PO T&UDG {+00 - +0F} : [+00 - +14]
0B65 PO CHAR {character area} : [character set]
0BD3 {PO ALL 6} : [PR ALL 6]
0BDB PO ATTR {D holds ATTR T and E holds MASK
T} :
[E holds ATTR T and D holds MASK T]
{ATTR R} : [ATTR T]
0C3B PO SAVE {PRINT OUT} : [PRINT A 1]
0C44 PO STEP {intial} : [initial]
0C55 PO SCR {if considering INPUT ... AT} :
[if handling lower screen]
{via CL SET} : [via AUTO LIST] (see note on 1795 AUTO
LIST)
0C88 PO SCR 2 {0601 CHAN OPEN} : [1601 CHAN
OPEN]
0DD9 CL SET {a character areas} : [a character area]
{first character bit} : [first character byte]
0E19 CL SCR 3 {CL SR 1} : [CL SCR 1]
111D ED COPY {SET HL} : [SET DE]
1167 ED FULL {New} : [Now]
117E ED C END {The old value of S POSNL} :
[The new position values]
1190 SET HL {HL ... first ... DE ... 'last'} :
[DE ... first ... HL ... 'last']
1219 RAM SET (p.61 l.15) {pinter} : [printer]
12CF MAIN 3 { JR NZ,155D} : [ JP NZ,155D]
RST 0018[,GET CHAR]
{1B8A PROG RUN} : [1B8A LINE RUN]
133C MAIN 5 {OUT NUM1} : [OUT NUM 1] (twice)
{1961} LINE NO A : [1691]
1736 OPEN {+00 or +03} : [+00 to +03]
1795 AUTO LIST {SET 0,FLAGS} : [SET 0,TV FLAG]
{RES 0,FLAGS} : [RES 0,TV FLAG]

517
{editing area} : [lower screen]
17ED AUTO L 4 {1833 LIST ALL} : [1833 LIST ALL-2]; the
call is to 1833h, but this isn't the address of LIST ALL, it is
the previous line "LD E,01" which is called.
1814 LIST 2 {5.20} : [5,20] or [5;20]
1822 LIST 5 {FIND INT} : [FIND INT2]
1833 [there should be another label; perhaps LIST ALL on
line 1833 and LIST ALL 1 on line 1835, to which the auto jumps
return]
1865 {OUT LINE} : [OUT LINE 1]
18B6 NUMBER {IND HL} : [INC HL]
19DD DIFFER {address} : [addresses]
1A15 E L 1 label omitted on line JP C,1C8A,REPORT C
1B29 STMT L 1 {18CA REPORT C} : [1C8A REPORT C]
1BEE CHECK END {statment} : [statement]
{the addresses of SCAN LOOP & STMT RET} :
[the subroutine return address and STMT RET]
(see index entry on CHECK END)
1C11 CLASS 05 {AND} : [and]
1C30 VAR A 2 {STK VARS} : [STK VAR] (twice)
1C46 VAR A 3 {'destination} : ['destination']
1CD6 CL 09 1 {CO TEMP} : [CO TEMP 2]
1D34 F L&S {FP CLAC} : [FP CALC]
{after for} : [after the]
1E0A READ 1 {calue}: [value]
1EA1 RUN {1EAF CLEAR 1} : [1EAF CLEAR RUN]
1EDC {CLEAR 3} : [CLEAR 2] {(RAMTOP,HL} :
[(RAMTOP),HL]
1EED GO SUB {GO TO 1} : [GO TO]
1F3D PAUSE 1 { Jump will all} : [ Jump with all]
1F60 DEF FN {bass-by} : [pass-by]
201E PR AT TAB {PRINT OUT} : [PRINT A 1]
2024 PR ITEM 3 {a colour items} : [a colour item]
203C PR STRING {decease} : [decrease]
2061 PR POSN 2 {PR CR} : [PRINT CR]
206E PR POSN 4 {reset} : [set]
2070 STR ALTER {+FF} : [+0F]

518
2096 INPUT 1 {LD (DF SZ),+01} : [LD (TV FLAG),+01]
20FA IN PROMPT {SUNTAX} : [SYNTAX]
211C IN PR 2 {Test bit 6} : [Test bit 1]
2129 IN PR 3 {error stack} : [error stack pointer]
2174 {IN VARS 5} : [IN VAR 5]
219B {IN VARS 6} : [IN VAR 6] {STK ST $} : [STK STO $]
21E1 CO TEMP 1 {PRINT OUT} : [PRINT A 1]
21FC CO TEMP 4 {PRINT OUT} : [PRINT A 1]
(Correct in the header of CO TEMP 5)
2244 {REPORT} : [REPORT K]
2246 CO TEMP 9 {'0 to 5'7'} : ['0 to 7']
22AA PIXEL ADD {the bite} : [the bits]
22FD PL TST IN {completemented} : [complemented]
2320 CIRCLE {at 2470-24B6} : [at 247D-24B6]
233B C R GRE 1 (against 1st +38 end-calc) {SIN (PI/A)} :
[Z*SIN (PI/A)]
( " 2nd +02 delete) {machine stack} :
[calculator stack]
2382 DRAW (para 2 of heading) {cirlce} : [circle]
(para 3 of heading) {Two subroutine,} :
[Two subroutines,]
23C1 DR PRMS (halfway down p.121, after get-mem-0)
{X,Y,Y*W*SIN F,X*W} : [X,Y,Y*W*SIN F,COS F]
(after get-mem-2)
{X,Y,Y*W*SIN F,X*W,COS F} : [X,Y,Y*W*SIN F,COS F,X*W]
2420 DRAW STEPS {subseqeunt} : [subsequent]
2439 ARC START (opposite 2nd FP CALC)
{X0+X,Y0+Y,Xn+1,Xn'} : [X0+X,Y0+Y,Xn+1,Xn+1,Xn']
(next line) {X0+X,Y0+Y,Xn+1,Xn+1,Xn' - Xn' = Un'} :
[X0+X,Y0+Y,Xn+1,Xn+1 - Xn' = Un']
2497 DRAW SAVE (line after USE 252): label omitted
24CB DL LARGER {The H - L horizontal} :
[Then H - L horizontal]
24D4 D L DIAG {resisters} : [registers]
2535 S SCRN$ S {'pointed to' to} : ['pointed to' by]
{25AF} S SCRN LP : [254F]
26C9 S LETTER {STK VARS} : [STK VAR]

519
26DF S NEGATE {+D8} : [+DB] (twice)
2734 S LOOP (ii in header) {market} : [marker]
{289B} in table of operators : [279B]
27BD S FN SBRN (ii in header) {compated} : [compared]
2951 STK F ARG {ip non-zero} : [is non-zero]
29A1 SV SIMPLE$ {STK STORE} : [STK STO $]
2A20 REPORT 3 {Subscript out of range} :
[Subscript wrong]
2A2C SV ELEM$ {variable printer} : [variable pointer]
2AFF LET (last line of heading) {FLAGS} : [FLAGX]
2B3E L CHAR {Prepare the mark} : [Prepare to mark]
2B66 L EXISTS {lcoation} : [location]
2C2E D NO LOOP (11 lines from bottom of p.158)
{overal length} : [overall length]
2CCB DECIMAL {decimal} : [decimal point]
2D6D E DIVSN {'+' ot '-'} : ['+' or '-']
2DAD FP DELETE {points to iit} : [points to it]
2DF8 PF POSTVE {initalised} : [initialised]
2E7B PF BITS {where i us a small} : [where i is a small]
2EDF PF FRN LP {PR FR DGT} : [PF FR DGT]
2F2D PF COUNT {more than 9} : [more than 8]
2F46 PF NOT E label omitted: should be on AND A
2F6C PF E FRMT {jump nack} : [jump back]
2F85 PF E SIGN {OUT NUM} : [OUT NUM 1]
2FBA FETCH TWO (in heading note) {into the register} :
[into the registers]
3004 subtract {and carried on} : [and carries on]
303E FULL ADDN (opposite PUSH HL) {Sace} : [Save]
30CA multiply (p.181 l.2) {that they} : [thus they]
3146 {OFLW 1 CLR} : [OFLW1 CLR]
31FA COUNT ONE ( just above "Note:") {34the} : [34th]
(top of page 186) {intp} : [into]
32B2 RSTK LOOP {right one position} :
[left one position]
32D7 table of calculator addresses {21DF} : [31DF]
3362 GEN ENT 2 {is store} : [is stored]
33C8 STK CONST (4 from page bottom) {is fetch} :

520
[is fetched]
3453 G LOOP first "38 end-calc" should be on new line,
after "03 subtract"
34E4 USR STACK label omitted ( just before 34E7 REPORT
A)
35C9 {chrs} : [chr$] (twice)
35DC REPORT B {our of range} : [out of range]
3674 len {legnth} : [length]
367A dec-jr-nz {Decreae} : [Decrease]
36A0 n-mod-m {calculates M (mod M} :
[calculates N (mod M)]
36AF INT (para 2 l.3 of header) {is gives} : [is given]
36C4 exp (p.212, in step ii) "1C46" is printed
meaninglessly after "int"
3713 len (header) {a simply} : [a simple]
373D GRE.8 (p.214 8 from bottom) {constant} :
[constants]
(p.215 l.20,21) {LD} : [LN] (twice)
3783 get-argt (in header) {INT (X/2*PI) + 0.5)} :
[INT (X/(2*PI) + 0.5)]
(p.215, bottom) "1C46" and "174C" are printed
meaninglessly after "int" and "subtract"
(near end) {enc-calc} : [end-calc]
37AA cos {SINE, subroutine} : [SINE subroutine]
3833 asn {return a real real number} :
[returns a real number]
{SIN Y/1(1 + COS Y)} : [SIN Y/(1 + COS Y)]
Appx p.223 BASIC line {200!} : [200]
Index p.235 {3124} Integer Truncation : [3214]

mistakes in ROM
"ROM Disassembled" notes these is a number of places,
but the notes are often very cryptic and refer to subtle errors
which aren't explained. It isn't even always clear whether an
error is being identified. This index tries to give fuller
explanations of some of them; see the entries marked with
asterisks "*".

521
Introduction problems with -65536d (*wrong number)
*0066 RESET unusable; NZ shd be Z
*04AA PROGRAM NAME subroutine wrongly included
0672 SA V OLD (*0605 SAVE ETC) saving simple strings
0685 SA V NEW (*0605 SAVE ETC) syntax checking error
*0A23 PO BACK 1 character moved off screen
*0A3D PO RIGHT position not stored; perhaps not a
mistake
*0A5F PO COMMA redundant subroutine call
0C88 PO SCR 2 (*0C55 PO SCR) no attempt to save DE
0F38 ED LOOP redundant routine (*control characters)
*0FA9 ED EDIT line cursor may appear in edit line
*103E ED EDGE 1 redundant routine
1076 ED SYMBOL redundant routine (*symbol code)
*1313 MAIN G resets FLAGX twice (not spotted by notes)
*1835 LIST ALL missing label (mistake of notes)
196D OUT CH 3 (*1937 OUT CHAR) ":" in REMs
*1E67 GO TO wrong error report
257D S SCR STO (*2535 S SCRN$ S) stack loaded twice
*28B2 LOOK VARS oddity or error in colour codes
293F V FOUND 2 redundant routine
2B72 L DELETE$ (*2AFF LET) redundant command
2CDA NXT DGT 1 (*2C9B DEC TO FP) badly placed label
2E24 PF SMALL (*361F str$) value left on calc stack
3014 addition sums to -65536d give *wrong number
30CA multiply unnecessary precautions against -65536d
31FA COUNT ONE (*31AF division) loop to wrong address
3221 T GR ZERO int -65536d = -1; see *wrong number
34B3 usr-no see *alternate registers
*361F str$ value left on calculator stack

MLT LOOP 3114 (30CA multiply)


Jumps from:
3125 STRT MLT

mod see also 36A0 n-mod-m


A useful shorthand term from the Theory of Numbers,

522
short for "modulo" which is Latin for "by the measure": X mod
P or X (mod P) just means the remainder on dividing X by a
positive whole number P. The BASIC equivalent of X mod P is
X - P * INT (X/P).
This isn't at all the same thing as the modulus of the
number X, which in mathematics usually means its absolute
value, at least for real numbers; in the notes it is sometimes
wrongly used to mean its integer part.

MODE system variable 5C41


Bytes: 1
Influences the type of cursor to be printed and the
reading of a keystroke. The five key modes are
- Keyword mode: MODE zero, FLAGS bits 2 and 3 off
- Letter mode: MODE zero, FLAGS bit 2 on, FLAGS2 bit 3 off
- Capitals mode: MODE zero, FLAGS bit 2 on, FLAGS2 bit 3
on
- Extended mode: MODE 1
- Graphics mode: MODE 2
The final character code from an E mode keystroke, CAPS
and SYMBOL SHIFT together on older Spectrums, is 0E for
extended mode on/off, and from GRAPHICS, CAPS SHIFT 9 on
older Spectrums, it is 0F for graphics mode on/off. 10E6 KEY
MODE converts these
to 1 and 2 respectively, and writes them to MODE, but if the
same value was there before, it rewrites MODE as zero.
The value of MODE is passed in the C register to 0333 K
DECODE from 02F1 K NEW when the main code of a key is to
be converted to the final code. DEC C then gives an M flag for
K/L/C modes or a Z flag for extended mode; the decoding
proceeds accordingly. When the cursor is written by 18E1
OUT CURS etc, MODE is doubled; if the result is zero, a K, L,
or C is printed depending on the flags, otherwise the
character whose code is twice MOD E + 43h is printed, which
gives E or G as required.
Extended mode is cancelled in 0F81 ADD CHAR or 0F6C
ED CONTR by zeroing bit zero of MODE each time a new byte

523
is added to BASIC; but this doesn't cancel graphics mode.
MODE is zeroed by 1097 CLEAR SP whenever the editing area
or workspace is cleared.
The notes explain the flag in TV FLAG bit 3 as
signalling "the mode may have changed", but this is rather
misleading, see under TV FLAG bit 3.
Written by:
0F6C ED CONTR
0F81 ADD CHAR
1097 CLEAR SP
10E6 KEY MODE
Read by:
02F1 K NEW
10E6 KEY MODE
18E1 OUT CURS
Rems:
0205 key tables
0333 K DECODE key decoding depends on mode
0341 K E LET letter keys in E mode
034F K KLC LET letter keys KLC with/without S SHIFT
0367 K DIGIT digit key decoding depends on mode
0389 K GRA DGT digit keys in G mode
039D K KLC DGT digit keys KLC with/without shifts
0C88 PO SCR 2 letter mode signalled for "scroll?"
0F6C ED CONTR keyword mode for INK to TAB
0F81 ADD CHAR extended mode cancelled
1097 CLEAR SP extended and graphics modes cancelled
10A8 KEY INPUT jump forward for E MODE/GRAPHICS
10DB KEY M&CL jump forward for E MODE/GRAPHICS
10E6 KEY MODE adjust E MODE/GRAPHICS and write
MODE
10F4 KEY FLAG signal mode change
111D ED COPY cancel mode change signal
1386 MAIN 9 cancel letter/capitals mode
15D4 WAIT KEY flag change if lower screen was cleared
1881 OUT LINE3 flags read for K or L mode
18E1 OUT CURS sv MODE signals E or G mode

524
18F3 OUT C 1 flags signal K L or C mode
1909 OUT C 2 choice of letter determines mode
1937 OUT CHAR flag set for K mode
1968 OUT CH 2 flag set for L mode
196D OUT CH 3 present character determines mode for
next
2634 S INKEY$ always read INKEY$ in L mode

mode cursor see 5C5B K CUR

modes (key) see 5C41 MODE

modulo see mod

modulus of number see integer part, mod

MODULUS SUBROUTINE see 36A0 n-mod-m

motor (printer) see ports

MOVE key (D1) see also commands, functions and


operators,
KEYBOARD SCANNING, 0284 extended mode key table (f )
The 6 key in E mode with symbol shift produces the
command MOVE; it requires to be followed by a string
expression, comma, and string expression, but it is ineffective
on the simple Spectrum, without peripherals such as Interface
1; and such peripherals often introduce other parameter
options.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1B0A P MOVE causes a jump via
- 1C86 CLASS 0A which collects the first string
- back to 1B0B in the parameter table to check the comma
- 1C86 CLASS 0A again for the second string
- 1C10 CLASS 00 and 1C16 JUMP C R to the executive

525
routine 1793 CAT ETC.

MOVE A FLOATING POINT NUMBER SUBROUTINE, MOVE


FP see 33C0
duplicate

multiplicand see addend

multiplication see 30CA multiply, 2AF4 GET HL*DE, 30A9


HL=HL*DE

MULTIPLICATION OPERATION, multiplier, multiplier bit,


multiplier loop see multiply

multiply subroutine 30CA


Called frequently from 0028 FP CALC with literal 04;
executes the BASIC "*" operator to multiply one number by
another. Not otherwise called from ROM, but could be called
direct from m/c. Overflow, ie a result outside the FP format
range of the Spectrum, will crash any m/c program and
produce an error report.
If both numbers are in small integer format the routine
performs a_short_multiplication, using 30A9 HL=HL*DE. A
problem here is that if one was negative and the other zero,
the answer could be minus zero, 00 FF 00 00 00; see wrong
number.
Otherwise it must perform a_long or_full_multiplication.
In principle, multiplication of FP numbers is simpler than
addition or subtraction: add the exponents and multiply the
mantissas together by the binary equivalent of long
multiplication. But both mantissas and exponents have to be
handled in "true" form, see 335B CALCULATE, and after the
multiplication the result has to be "normalized" to standard FP
format; and the signs of the numbers and of the result also
have to be taken into account.
The binary long multiplication of the two mantissas is

526
handled by the_multiplier_loop, MLT LOOP to STRT MLT,
which uses no less than thirteen registers: AC'CB and D'E'DE
for the mantissas to be multiplied, H'L'HL for the result, and B'
for a loop counter. The loop turns 21h/33d times, producing a
series of rotations, the effect of which is to add the DEs into
the correct position of the HLs whenever there is a set bit, the
_multiplier_bit, in AC'CB.
The loop can be illustrated by an example in which the
mantissas are only four bits each and there are only five turns
of the loop:
X = 1101 Y = 1011 Z (result) set to zero.
Written out normally, the binary long multiplication is
as follows:
0.1101
0.1011
------
1101
0000
1101
1101
----------
0.10001111
First turn of the loop:
Shift Y right to 0101 and carry; Z = 0000 + X = 0000 +
1101 = 1101 with no carry; shift Z right to 0110 and carry
Second turn:
Shift Y right with the carry to 1010 and carry; Z = 0110
+ X = 0110 + 1101 = 0011 with carry; shift Z right with the
carry to 1001 and carry
Third turn:
Shift Y right with the carry to 1101 and no carry; skip
the addition, Z = 1001 with no carry; shift Z right with no
carry to 0100 and carry
Fourth turn:
Shift Y right with the carry to 1110 and carry; Z = 0100
+ X = 0100 + 1101 = 0001 with carry; shift Z right with carry to
1000 and carry

527
Fifth turn:
Shift Y right with the carry to 1111 and no carry; and
exit from the loop.
The four most significant bits of the result are in Z,
the rest are in Y. The hi byte of Y is used to round the last
binary digit.
The notes are more difficult to follow than they need be
because of their constant reference to "multiplicands",
"multipliers", "first numbers", "second numbers", etc; see the
index entry on addend. It may help to summarize the terms
used.
Suppose a multiplication Z = X * Y; on entry to the subroutine,
the first byte of X is in HL and of Y in DE. Then the terms used
are:
First byte in HL DE
X Y
first number second number
_multiplicand _multiplier
second value last value
In the notes on 2FBA FETCH TWO names are given to the
individual bytes of the numbers:
exponent byte M1 goes into H' N1 goes into L'
sign byte M2 goes into B' N2 goes into D'
M3 goes into C' N3 goes into E'
M4 goes into C N4 goes into D
M5 goes into B N5 goes into E
Note the reversal in BC of M4 and M5.
Input parameters: HL and DE point to the first byte of X
and Y respectively; in calls from FP CALC, X and Y are the last
two values on the calculator stack, Y last, but if the routine is
called directly they could be any RAM addresses; they don't
even need to be consecutive.
Action: if the first bytes of X and Y aren't both zero,
jump on to MULT LONG; one at least is in full FP format
- (both in small integer format) call 2D7F INT FETCH for
each, getting their absolute values off the stack into HL and
DE

528
- XOR their sign bytes together; small integer sign
bytes are 00 for positive and FF for negative, so this at once
gives a sign byte for the result
- call 30A9 HL=HL*DE
- if the result isn't a small integer jump on to MULT
OFLW
- (small integer result) if bytes 3 and 4 of the result
aren't both zero jump on to MULT RSLT
- (result is zero) put byte 4 in byte 2 to ensure it is
00 00 00 00 00; not minus zero, 00 FF 00 00 00, see wrong
number.
_30EA_MULT_RSLT: call 2D8E INT STORE, which puts the
result in the HL position, where X was on input
- return.
_30EF_MULT_OFLW (result not a small integer): recover
the Y pointer to start again.
_30F0_MULT_LONG: call 3293 RE ST TWO to restack X and
Y in full FP format
- zero the sign flag
- call 30C0 PREP M/D for X; it corrects the first
mantissa byte of a positive number by setting its hi bit
- if it returns with carry, return; X is zero, and since
it is in the result position, the result is already zero
- call 30C0 PREP M/D for Y; it also XORs the sign bits
of X and Y together, returning a sign flag for the result
- if it returns with carry jump on to ZERO RSLT
- call 2FBA FETCH TWO, which puts the sign flag in the
sign byte of the last value on the stack and loads X (M1-M5)
and
Y (N1-N5) into the ten registers BC, B'C', DE, D'E', and H'L';
H'L' holds the two exponents
- transfer B to A; B will be used for the loop counter
- stack the exponents; H'L' is needed for the result
- zero HL and H'L', "the HLs", for the result
accumulator
- make a loop counter 21h/33d
- jump into the loop at STRT MLT.

529
_3114_MLT_LOOP (the loop returns here after mantissa Y
has been rotated right, see the illustration above): if the lo bit
of mantissa Y was zero jump on to NO ADD; the addition of
mantissas X and Z is skipped in this case
- add DE to HL and DE' to HL'.
_311B_NO_ADD: shift mantissa Z right, starting from its
hi byte and carrying the lo bit from byte to byte; the carry
from the lo byte is carried on into STRT MLT.
_3125_STRT_MLT (the entry to the loop): shift mantissa Y
right, starting from its hi byte and carrying the lo bit from
byte to byte; the carry now indicates whether the lo bit was
zero or one. On the first turn of the loop, a zero is shifted
into the hi bit, but on later turns the carry from rotating
mantissa Z goes into the hi bit
- loop back to MLT LOOP until 33d turns of the loop have
been made
- (33d turns of the loop complete) move mantissa Z from
the HLs to the DEs; now the result mantissa
- recover the exponents and add them together; these are
"normalized" exponents. "True" exponents are positive if they
are 01 -> 7F, negative if they are 80 -> FF, zero if they are zero.
"Normalized" exponents are positive if they are 81 -> FF,
negative if they are 00 -> 7F, zero if they are 80. Adding
"normalized" exponents produces a "true" result exponent,
and a carry flag which is the reverse of the one required;
except when the result is zero, when the carry flag is twice
wrong, ie right
- if the result is non-zero jump on to MAKE EXPT
- (exponents add to zero) clear the carry flag; for the
double correction.
_313B_MAKE_EXPT: decrement the exponent
- reverse the carry flag.
Exit: into 313D DIVN EXPT, which checks for overflow and
forms a correct exponent byte, and 3155 TEST NORM, which
puts the product into standard FP format and stacks it
- or RET from 30EA MULT RSLT if X, Y and X * Y are small
integers

530
- RET from 30F0 MULT LONG if the product is zero.
Output parameters: the last few lines are concerned with
making all the correct input parameters for the exit routine
DIVN EXPT, ie
- the "true" mantissa of the result in the DEs
- its "true" exponent, less one, in A
- C in the carry flag if the exponent is to be
considered negative
- the pointers to X and Y are still on the machine
stack.
After the exit routines, HL and DE are restored to their
input values, but the result is now in the place addressed by
HL
- if the call was from FP CALC, this will be last value on the
calculator stack, and both X and Y have been deleted.
Called from:
03F8 BEEP
0427 BE OCTAVE (twice)
233B C R GRE 1
238D DR 3 PRMS
23C1 DR PRMS (7 times)
2425 ARC LOOP (4 times)
2497 DRAW SAVE (twice)
25F8 S RND
2CDA NXT DGT 1
2D40 NXT DGT 2
2D60 E LOOP
2D71 E TST END
2DC1 LOG(2**A)
30C0 PREP M/D
3453 G LOOP
36A0 n-mod-m
36C4 EXP
373D GRE.8 (3 times)
3783 get-argt
37B7 C ENT (twice)
37FA CASES (twice)

531
3833 asn
3851 to-power
Rems:
2D8E INT STORE -ve numbers two's complemented for
2F98 PREP ADD used by
2FBA FETCH TWO used by, sign saved in second byte
3004 ADD BACK ripple can lead to increment of
exponent
30A9 HL=HL*DE used by for small integers
313D DIVN EXPT common to multiply and division
316E SHIFT ONE "fifth byte" of result mantissa
31AF division exits into common routine

MULT LONG 30F0 (30CA multiply)


Jumps from:
30CA multiply

MULT OFLW 30EF (30CA multiply)


Jumps from:
30CA multiply

MULT RSLT 30EA (30CA multiply)


Exit from:
30CA multiply

532
N

name of DEF FN function, user-defined function see DEF FN


key

name of save/load file see 0605 SAVE ETC

name of user-defined function see DEF FN key

name of variable or array see arrays, variables

NATURAL LOGARITHM FUNCTION see 3713 ln

nature of last value see expressions

NEAR ZERO 3159 (3155 TEST NORM)


Jumps from:
316E SHIFT ONE

negate subroutine 346E


Called from 0028 FP CALC with the literal 1B; the
executive routine of the "unary minus" operation. Changes the
sign of the last value on the calculator stack, unless it is
zero. Also called direct as NEGATE from 300F subtract; and
most of the subroutine, from NEG TEST onwards, is also the
exit routine of the ABS command, which sets the sign positive
regardless of its previous value.
ABS and "negate" are both "unary operations", see binary
operations; all this means is that they concern only one value
on the calculator stack.
The sign of a FP number is indicated by bit 7 of its
second byte, which is zero for positive numbers and one for
negative numbers. This is true of both "small integer" format
and "full FP" format numbers; but a number in "small integer"

533
format must have the whole of its second byte changed, from
00 to FF or vice versa. See CALCULATE.
Input parameters: HL points to the first byte of the
number; if the routine is called from FP CALC, this is the last
value on the calculator stack, but in the direct call from
subtract it is the second last, and in calls from m/c it can be
anywhere in RAM.
Action: call 34E9 TEST ZERO
- if it returns with C, return immediately; the number
is zero
- zero the negate flag.
_3474_NEG_TEST (entry point from 346A abs, with FF in
the negate flag): check the first byte
- if it is zero jump on to INT CASE; this is a small
integer
- (full format) AND the negate flag with 10000000b/80h;
making 80h for ABS or zero for negate
- OR the sign byte with it and then reverse its hi bit;
for ABS the sign bit goes to one and then to zero/positive, for
negate the sign bit is first unchanged, then reversed
- return.
_3483_INT_CASE (small integers): call 2D7F INT FETCH,
which puts the absolute value of the integer in DE and its sign
byte in C
- OR the sign byte with the negate flag
- reverse it; for ABS the sign byte goes to FF and then
to zero/positive, for neg the sign byte is first unchanged, then
reversed.
- call 2D8E INT STORE to put the number back in the HL
address in small integer format.
Exit: RET, from NEG TEST or INT CASE.
Output parameters: HL and DE unchanged; DE will be the
stack end if the routine was called from FP CALC.
Called through FP CALC from:
2497 DRAW SAVE
37A1 ZPLUS
37AA cos

534
37E2 atn
3833 asn
3843 acs
Called direct from:
300F SUBTRACT
Exit from:
346A abs
Rems:
346A abs neg does most of the work

NEG BYTE 2FAF (2F9B PREP ADD)


Jumps from:
auto

NEG TEST 3474 (346E negate)


Exit from:
346A abs

NEW key (E6) see also commands, functions and operators,


KEYBOARD SCANNING
The A key in K mode produces the command NEW, which
accepts no parameters.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AA8 P NEW causes a jump via 1C10 CLASS 00 and 1C16 JUMP
C R to the executive routine 11B7 NEW.

NEW subroutine 11B7


Called only from the syntax parameter table at 1A7A; the
executive routine of the NEW command. On NEW, the user-
defined graphics and the values in the four svs 5CB4 P RAMT,
5CB2 RAMTOP, 5C38 RASP/PIP and 5C7B UDG are preserved
unchanged; but otherwise the effect is the same as a complete
system reset executed from 0000 START, jumping on to 11CB
START/NEW. This can be executed from the keyboard by

535
PRINT USR 0, and later models of the Spectrum have a reset
button which does the same.
Input parameters: none.
Action: disable the interrupt; the keyboard routines
cannot be called till IY and the stack pointer have been placed
and the system variables have been reloaded
- put FF in the START/NEW flag
- save the values in the four svs, RAMTOP in DE as a
reset limit and the others in the alternate registers.
_11CB_START_NEW (entry here from 0000 START, with
FFFFh as the reset limit and zero in the START/NEW flag):
output 07 to port FE to make the border white
- set the I register to 3F; it controls the TV signal
- pause for 6 NOPs; I don't know why.
_11DA_RAM_CHECK and_11DC_RAM_FILL: load every RAM
location with 02 from the reset limit down; for NEW from the
address in 5CB2 RAMTOP, for START from FFFFh. The RAM
FILL loop terminates when H gets down to 3F, the value still in
A, so 4000h, the bottom of RAM, is the lowest location
loaded.
Incidentally this turns the screen black, as every attribute is
set to 00000010b, BLACK paper and RED ink.
_11E2_RAM_READ: starting at the bottom, 3FFFh, check if
the reset limit has yet been reached
- move up the byte pointer by one
- if the limit has been reached jump on to RAM DONE
- decrement the byte; it should go from 02 to 01
- if it is zero, jump on to RAM DONE; this RAM address
is faulty, so it and all above it are useless
- decrement it again; it should go to zero
- if it does loop back to RAM READ for the next byte; if
not, again this RAM address is faulty.
_11EF_RAM_DONE: decrement the pointer; now either on
the reset limit or on the highest usable byte of memory
- restore the three svs from the alternate registers;
not 5CB2 RAMTOP yet. In the case of a complete reset from
START

536
these three get written again shortly
- increment the START/NEW flag
- if this makes zero jump on to RAM SET; FF in the flag
signalled NEW
- (START only) put the highest usable address in RAM in
5CB4 P RAMT
- copy A8h/168d bytes from the character forms
backwards into the top of RAM; from the eighth byte of "U"
down to the first byte of "A". These are the udgs
- put the first address of this lot in 5C7B UDG; for NEW
the blanking out only went up as far as the address in 5CB2
RAMTOP, so the udgs weren't disturbed
- put 00 in 5C39 PIP and 40h in 5C38 RASP.
_1219_RAM_SET (see under memory map): load 5CB2
RAMTOP;
with its old value if the flag signalled NEW, or with one below
the address in 5C7B UDG if it signalled START
- put the base address of the character set in 5C36
CHARS
- put 3E at the address in 5CB2 RAMTOP, put SP one
below it, and put the address two below that in 5C3D ERR SP;
this sets up the machine/GO SUB stack, see diagram in the
entry GO SUB stack
- put IY on 5C3A ERR NR
- start the interrupt firing
- put the base address of the channel information in
5C4F CHANS
- copy in the 15h/21d bytes of initial channel data from
the table at 15AF
- point 5C57 DATADD at the 80-byte at the end of the
channel data; just before the program area, so that READ
commands will start searching from there
- point both 5C53 PROG and 5C4B VARS at the next
address; the program area has zero length
- put another 80-byte in it
- point 5C59 E LINE at the next again
- put a newline in it, and another 80-byte in the next

537
- load the next address after the 80-byte into 5C61
WORKSP, 5C63 STKBOT and 5C65 STKEND; zero lengths for
work space and calculator stack.
- put 00111000b/38h in 5C8D ATTR P, 5C8F ATTR T and
5C48 BORDCR; WHITE paper, BLACK ink, and no FLASH or
BRIGHT
- put 23h in 5C09 REPDEL and 05 in 5C0A REPPER; see
KEYBOARD SCANNING
- free both KSTATE sets; see 5C00 KSTATE
- copy the 0Eh/14d bytes of initial stream information
from the table at 15C6 to 5C10 STRMS; see channels and
streams
- clear the printer buffer; actually it is clear
already, but this sets the ZX printer position value svs
- put 02 in 5C6E DF SZ; the lower screen is two lines
- clear both parts of the screen; to set the position
values and also the attribute bytes, so now the screen turns
white again
- print the "copyright message"; and away you go.
Exit: into the main execution loop at 12A9 MAIN 1, see
12A2 MAIN EXEC. The call to 16B0 SET MIN repeats the
zeroing of the workspace and calculator stack, but it also puts
the right address in 5C68 MEM.
Output parameters: none.
Rems:
Introduction part of executive routines
0000 START flag FF for NEW, 00 for start
11DA RAM CHECK limit is RAMTOP if NEW being executed
11EF RAM DONE only meaningful if NEW being executed
1219 RAM SET from here on common to START and NEW

new array see variables

NEW COMMAND ROUTINE see 11B7 NEW

NEW flag see 11B7 NEW

538
new key see 5C3B FLAGS bit 5, 5C00 KSTATE

newline see ENTER

newly declared string/variable see variables

NEWPPC system variable 5C42


Bytes: 2
The number of the next BASIC line to be read and
executed. Written and read in combination with 5C44 NSPPC,
the statement number within the line. Read after
interpretation of every statement at 187D STMT R 1.
Loaded by GO TO commands at 1E73 GO TO 2.
Loaded by FOR ... NEXT loops with the line containing
the appropriate NEXT command at 1D34 F L&S and 1D8B
LOOK P 1,
and similarly when searching for DATA and DEF FN
commands.
Loaded from the header at 0873 LD PROG if a program is
loaded with a specified start line.
Written by:
0873 LD PROG
1D34 F L&S
1D8B LOOK P 1
1E73 GO TO 2
Read by:
1B7D STMT R 1
Rems:
1D7C F FOUND find statement in the line with NEXT
1EA1 RUN loaded by using 1E67 GO TO
1EDC CLEAR 2 set by RUN, so RUN: stmt is ineffective
1EED GOSUB loaded by using 1E67 GO TO
1F23 RETURN loaded by using 1E73 GO TO 2

new simple/single strings see 2AFF LET

new string/variable see variables

539
NEXT key (F3) see also commands, functions and operators,
KEYBOARD SCANNING
The N key in K mode produces the command NEXT. The
“NEXT statement" must also include a looping variable letter.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A98 P NEXT causes a jump via 1C6C CLASS 04, which picks
up the loop control variable, 1C10 CLASS 00 and 1C16 JUMP C
R to the executive routine 1DAB NEXT.
1C1F CLASS 01 NEXT command uses CLASS 04
1D34 F L&S looks for line containing NEXT.
1D64 LOOP looks for 1st occurrence of NEXT with vble
1D7C FOUND finds number of statement holding NEXT
1D86 LOOK PROG looks through program for NEXT

NEXT subroutine 1DAB


Called only from the syntax parameter table at 1A7A; the
executive routine of the NEXT command. Completes a
FOR ...NEXT loop; see also 1D03 FOR and FOR ... NEXT loops
for the terminology.
A looping variable letter in the variables area is
followed by three FP numbers, the VALUE, LIMIT and STEP of
the loop, and then three more bytes holding the line number
and statement number, plus one, of the FOR statement; this
is the statement to which the loop is to return.
The routine puts the address of the first of these bytes
in 5C68 MEM, so that VALUE is in mem-0, etc. Thus it can use
the calculator to add STEP to VALUE and put the new VALUE
in the variable, etc. VALUE, LIMIT and STEP are all FP
numbers; at no stage are they assumed to be, or treated as,
integers.
Input parameters: none
- 5C4D DEST holds a pointer to the loop control variable

540
in the variables area; assuming it was found by 28B2 LOOK
VARS when called by 1C6C CLASS 04 called from the
command table
- if no loop control variable was found FLAGX bit 1 is
set.
Action: if FLAGX bit 1 is set report "Variable not found"
- if the hi bit of the variable letter is zero report
"NEXT without FOR"; an ordinary numeric variable, not a
looping variable
- step on the pointer and put it in 5C68 MEM; the bytes
following the variable letter will temporarily be treated as the
calculator memory
- use the calculator to add STEP to VALUE
- put the result in the VALUE position in the calculator
memory
- delete it; clearing the calculator stack
- call 1DDA NEXT LOOP
- if this sets the carry, return; VALUE has passed
LIMIT. No GO TO has been executed, so BASIC execution will
continue to the statement following the NEXT statement
- (VALUE hasn't passed LIMIT, so execution is to jump
back to the statement after the FOR statement) point to 0F/15d
beyond the address in 5C68 MEM; this is the lo byte of the
looping line number
- get the looping line and statement numbers
- exit into the GO TO routine.
Exit: RET if VALUE exceeds LIMIT; otherwise into 1E73 GO
TO 2, which puts the line and statement numbers in svs which
make them the next to be executed.
Output parameters: if there is a loop back, HL holds the
looping line number and D the statement number. Otherwise
none.

NEXT CHAR subroutine 0020


A simple "restart" subroutine; it increments 5C5D CH
ADD, reads the character at that address, and repeats till it
comes to a "printable" character, ie one from "!" 21h/33d to

541
COPY FFh/255d; not including the space.
In the ROM, 5C5D CH ADD is always a BASIC pointer, and
this restart is used in reading the BASIC for checking or
execution.
Input parameters: none.
Action: call 0074 CH ADD+1 to increment the address in
5C5D CH ADD and read the byte there
- jump back to 001C TEST CHAR.
Exit: 001C TEST CHAR returns if the character is
printable, otherwise leads back into a repeat of 0020 NEXT
CHAR.
Output parameters: A holds the character code, HL the
address. Others unchanged.
Called from:
0652 SA DATA
0692 SA DATA 1 (twice)
06A0 SA SCR$
06C3 SA CODE
06F5 SA CODE 3
0723 SA LINE 1
1814 LIST 2
1990 EACH S 1
19FB E LINE NO
1B28 STMT LOOP
1B29 STMT L 1
1B6F SEPARATOR
1C79 NEXT 2NUM
1C7A EXPT 2NUM
1D03 FOR
1D64 F LOOP (twice)
1D7C F FOUND
1DEC READ 3
1E2C DATA 1
1F6A DEF FN 1 (twice)
1F7D DEF FN 2
1F89 DEF FN 4 (twice)
1F94 DEF FN 5

542
1FA6 DEF FN 6 (twice)
200E PR ITEM 2
2067 PR POSN 3
2070 STR ALTER
20C1 IN ITEM 1 (twice)
20D8 IN ITEM 2
21E1 CO TEMP 1
21F2 CO TEMP 3
2320 CIRCLE
238D DR 3 PRMS
2522 S 2 COORD
25AF S U PLUS
25E8 S BRACKET (twice)
2630 S PI END
2634 S INKEY$
2668 S SCREEN$
2672 S ATTR
267B S POINT
270D S PUSH PO
2713 S CONT 3
2790 S NEXT
27BD S FN SBRN (3 times)
27D0 SF BRKT
27D9 SF ARGMTS
27E9 SF FLAG 6
27F7 SF RUN (3 times)
2802 SF ARGMT1
2852 SF ARGT VL
288D SF VALUE (3 times)
28B2 LOOK VARS
28D4 V CHAR
28DE V STR VAR
2943 V PASS
29C3 SV COMMA
29EA SV LOOP
2A22 SV NUMBER
2A48 SV DIM

543
2A52 SLICING
2A81 SL SECOND
2C2E D NO LOOP (twice)
2CA2 BIN DIGIT
2CB8 NOT BIN
2CCB DECIMAL
2CDA NEXT DGT 1
2CF2 SIGN FLAG
2CFE SIGN DONE
Rems:
2831 SF VALUES used to scan FN statement
28AB FN SKPOVR not used to scan DEF FN statement

next expression see expressions

NEXT LINE 1BD1 (1B8A LINE RUN)


Jumps from:
1B8A LINE RUN

next line, next variable see 198B NEXT ONE

NEXT LOOP subroutine 1DDA


Compares the VALUE of a FOR ... NEXT loop with the
LIMIT; the VALUE has already been incremented/
decremented by STEP, so this determines whether another
turn of the FOR loop is required or not.
See also 1D03 FOR and 1DAB NEXT, and for the
terminology see FOR ... NEXT loops.
Input parameters: none
- the calculator memory has been relocated by 1D16 F
REORDER or 1DAB NEXT in the loop control variable.
Action: use the calculator to get LIMIT, VALUE and STEP
from the temporary memory
- if STEP is negative, jump on to NEXT 1
- (STEP is positive) reverse VALUE and LIMIT on the
stack.
_1DE2_NEXT_1: take VALUE from LIMIT, or if STEP was

544
positive, take LIMIT from VALUE
- if the result is positive jump on to NEXT 2; either
way, a positive result signals "no more loops"
- clear the carry flag and return.
_1DE9_NEXT_2: set the carry and return.
Exit: RET, from NEXT 1 or NEXT 2.
Output parameters: none
- carry flag set signals "no more loops"
- the calculator memory is still placed in the loop
control variable.
Called from:
1D34 F L&S
1DAB NEXT

NEXT ONE subroutine 19B8


Given the start address of a BASIC line, or of a BASIC
variable/array, returns the start of the next line or variable
and the length in bytes of the old line or variable.
The method is to read the length of the variable or
BASIC line and add it to the base address of the line or
variable; the address after the length bytes for a line or the
variable if it has length bytes, otherwise the last address of
the variable name.
Input parameters: HL is the "old" start pointer.
Action: read the byte at the start pointer
- if it is less than 40h jump on to NEXT O 3; it must be
the hi byte of a line number. The lowest possible value for a
variable letter is 41h, for simple string a$
- (variable) if its bit 5 is zero jump on to NEXT O 4;
strings and either kind of array
- (numeric variable, not an array) double the byte
- if the result has bit 7 set jump on to NEXT O 1; a
single-letter variable. If it is a looping variable carry will
be set
- (a long-name numeric variable) clear the carry flag.
_19C7_NEXT_O_1 (numeric variables with either long or

545
short name, including looping variables; these have no length
bytes): allow 5 bytes "length allowance"; for a FP number
value
- if the carry flag shows NC jump on to NEXT O 2; not a
looping variable
- (looping variable) allow 12h/18d bytes for three
values and the line and statement numbers.
_19CE_NEXT_O_2 (the input byte is either the variable
letter, now doubled, or on looping back it is a letter of a long
name): check the hi bit of the input byte; it will be set if it is
the doubled variable letter of a short name, or the last byte of
a long name
- increment the start pointer and get the next byte
- if the hi bit was set jump on to NEXT O 5
- (long names) loop back to NEXT O 2 to check the next
byte; this continues till the last letter of the name is passed.
_19D5_NEXT_O_3 (program line): step on the pointer; to
the lo byte of the line number.
_19D6_NEXT_O_4 (strings and arrays): step the pointer on
again; now it is on the lo byte of the length, whether this is a
program line or a variable
- read the length
- step the pointer on again.
_19DB_NEXT_O_5: add the length to the pointer address;
this makes a new pointer to the start of the next line or
variable
- recover the "old" start pointer.
Exit: into 19DD DIFFER, which calculates the length by
taking the old from the new pointer and puts everything into
the desired registers.
Output parameters (from DIFFER): HL still holds the old
start pointer
- DE holds the new pointer
- BC holds the length.
Called from:
08DF ME OLD L1
0901 ME OLD V1

546
092C ME ENTER
093E ME ENT 1
155D MAIN ADD
17CE AUTO L 1
1974 LINE AD 1
292A V NEXT
2C15 D RUN

NEXT O 1 19C7 (19B8 NEXT ONE)


Jumps from:
19B8 NEXT ONE

NEXT O 2 19CE (19B8 NEXT ONE)


Jumps from:
19C7 NEXT O 1
auto

NEXT O 3 19D5 (19B8 NEXT ONE)


Jumps from:
19B8 NEXT ONE

NEXT O 4 19D6 (19B8 NEXT ONE)


Jumps from:
19B8 NEXT ONE

NEXT O 5 19DB (19B8 NEXT ONE)


Jumps from:
19CE NEXT O 2

next statement see 1BEE CHECK END

NEXT 1 1DE2 (1DDA NEXT LOOP)


Jumps from:
1DDA NEXT LOOP

NEXT 2 1DE9 (1DDA NEXT LOOP)


Jumps from:

547
1DE2 NEXT 1

NEXT 2NUM subroutine 1C79


Entry point for 1C7A CLASS 08 when the BASIC pointer is
to be moved on before collecting two number parameters
from BASIC and putting them on the calculator stack.
Called from:
1FFC PR ITEM 1
2522 S 2 COORD

NIL BYTES 3272 (3214 truncate)


Jumps from:
3214 truncate
3233 T FIRST

"ninth" line of character area see 0E88 CL ATTR

NMI (non-maskable interrupt) see interrupts

NMIADD system variable 5CB0


Bytes: 2
Cannot be used, see 0066 RESET. NMIADD makes a good
"spare" system variable for use in m/c programs. Beware! most
commercial peripheral producers are aware of this and may
well already have used it.
Read by:
0066 RESET

n-mod-m subroutine 36A0 see also mod


Called from 0028 FP CALC with literal 32; returns the
integer quotient Q and remainder R on dividing N by M, so
that
N = Q * M + R.
Then R is N mod M, the remainder on dividing N by M.
There is no corresponding BASIC operator, and the
subroutine is only called once from ROM, to calculate the

548
remainder on dividing 75d*(SEED + 1) by 65537d for a random
number; even in this case, Q is discarded. Presumably the
subroutine is included mainly for the benefit of m/c
programmers. It can also be called direct, but N and M cannot
be freely located, they must be on the calculator stack.
Q will always be an integer, and in the RND calculation
N, M and R are integers also, but this isn't necessary for the
subroutine to work.
Input parameters: HL holds the first byte of N, second
last on the calculator stack
- DE holds the first byte of M, last value.
Action: use the calculator to calculate successively
N/M
INT (N/M) = Q
M * INT (N/M)
N - M * INT (N/M) = N mod M
=R
Exit: RET.
Output parameters: HL holds the first byte of Q, second
last on the calculator stack
- DE holds the first byte of R, last value.
- mem-0 has been used and corrupted.
Called from:
25F8 S RND

N NEGTV 3705 (36C4 exp)


Jumps from:
36C4 exp

NO ADD 311B (30CA multiply)


Jumps from:
3114 MULT LOOP

no-gr-eql, no-grtr see 353B no-l-eql

no-key see KEYBOARD SCANNING

549
'no key yet' see FLAGS bit 5

no-l-eql, etc subroutine 353B


Called only from 0028 FP CALC with literals 09 -> 0Eh
and 11h -> 16h as the executive routine for all the_comparison
_operations listed below.
Not otherwise called from ROM. Can be called from m/c,
with the offset in B, but the expressions to be compared must
be on the calculator stack, not freely located in RAM.
The subroutine performs any one of twelve different
comparison operations depending on the value of the offset
on entry. In each case the comparison is between the two top
values on the calculator stack, which may be either numbers
X and Y or the parameters of strings X$ and Y$. The result of
the subroutine is always either zero for "false" or one for "true"
as the last value on the calculator stack, replacing both X/X$
and Y/Y$; see logical values. The "true" value is returned:

Offset Name Condition


09 no-l-eql if X <= Y
0A no-gr-eq if X >= Y
0B nos-neql if X <> Y
0C no-grtr if X > Y
0D no-less if X < Y
0E nos-eql if X = Y
11 str-l-eql if X$ <= Y$
12 str-gr-eq if X$ >= Y$
13 strs-neql if X$ <> Y$
14 str-grtr if X$ > Y$
15 str-less if X$ < Y$
16 strs-eql if X$ = Y$

The actual comparison of X/X$ with Y/Y$ is relatively


straightforward: the call to SUBTRACT in NU OR STR for
numbers, the byte comparison loop in BYTE COMP and
following for strings.
The routine performs much more elaborate gyrations to
produce the appropriate result by interpreting this

550
comparison in terms of the individual offset. By the time END
TESTS is reached, the AF on the machine stack and the value
on top of the calculator stack are as follows for each of the
number comparisons:
no-l-eql A even, X - Y on calc stack, no carry
no-gr-eq A even, Y - X " " " no carry
nos-neql A even, X - Y " " " carry
no-grtr A odd, X - Y " " " no carry
no-less A odd, Y - X " " " no carry
nos-eql A odd, X - Y " " " carry
In the case of number comparisons, the odd/even flag A
is zero or one.
In the case of string comparisons, A is 2 or 3, still
flagging the "odd" and "even" operations in the same way, but
there is zero on the calculator stack in all cases, and the
carry depends on X$ and Y$ as follows:
X$ > Y$ X$ = Y$ X$ < Y$
str-l-eql carry no carry no carry
str-gr-eq no carry no carry carry
strs-neql no carry carry no carry
str-grtr carry no carry no carry
str-less no carry no carry carry
strs-eql no carry carry no carry
In the first three lines carry corresponds to "wrong"
and no carry "right"; in the last three lines the reverse.
A call to 3501 NOT if carry is set makes the last value
one if it was zero and zero if it wasn't. Now
- for numbers, one corresponds to "right answer" for eql
and "wrong answer" for neql
- for strings, with asterisks marking wrong answers,
flag X$ > Y$ X$ = Y$ X$ < Y$
str-l-eql 2 one* zero* zero*
str-gr-eq 2 zero* zero* one*
strs-neql 2 zero* one* zero*
str-grtr 3 one zero zero
str-less 3 zero zero one
strs-eql 3 zero one zero
Next a call to 34F9 GREATER 0 if carry was_not set makes
the last value one if it was positive, zero if it was negative or
zero. Now
- for numbers, one means "right answer" for grtr and
less, and eql from the NOT call, and "wrong answer" for l-eql
and gr-eq, and neql from the NOT call.
551
- for strings, there has been no change from the last
table, because GREATER 0 makes one from one and zero from
zero.
Finally, another call to NOT if the odd/even flag was
even (l-eql, gr-eq and neql) exchanges zero and one; now the
result is correct in each case.
Input parameters: B holds the offset
- HL points to the first byte of X or the string
parameters of X$, second last value on the calculator stack
- DE points to the first byte of Y or the string
parameters of Y$, the last value.
Action: take 08 from the offset; making 1 -> 6 or 9 -> 0E
- if bit 2 is zero decrement the result;
0001b/01 -> 0000b/00 1001b/09 -> 1000b/08
0010b/02 -> 0001b/01 1010b/0A -> 1001b/09
0011b/03 -> 0010b/02 1011b/0B -> 1010b/0A
0100b/04 -> 0100b/04 1100b/0C -> 1100b/0C
0101b/05 -> 0101b/05 1101b/0D -> 1101b/0D
0110b/06 -> 0110b/06 1110b/0E -> 1110b/0E
_3543_EX_OR_NOT: rotate this result right, with lo bit
into the carry
- if this makes no carry jump on to NU OR STR; even
numbers
- (odd numbers) call 343C EXCHANGE to exchange X and
Y on the calculator stack; only "gr-eql" and "less" are odd
numbers, and this turns "gr-eql" into "less" and vice versa.
_354E_NU_OR_STR: if bit 2, which was bit 3 before the
rotation, is set jump on to STRINGS; the jump is made for
operations in the right-hand column in the last table, all the
string comparisons
- (numbers, the left hand column) rotate the offset into
the carry again
- save this "eql/l-gr" flag and the offset; the offset
is now the odd/even flag, zero for l-eql, gr-eq and neql, one
for grtr, less and eql. The carry will be C for eql and neql, NC
for all the others
- call 300F subtract, which gives a positive, negative
or zero result depending on X and Y

552
- jump on to END TESTS.
_3559_STRINGS (string comparisons): as before, rotate the
offset and save it with an eql/l-gr flag; the offset is now the
odd/even flag, 2 for l-eql, gr-eq and neql, 3 for grtr, less and
eql. The carry is as for the number comparisons
- call 2BF1 STK FETCH twice to get the string parameters
of both strings; this removes them from the stack.
_3564_BYTE_COMP (loop back to here from SEC PLUS,
counting down the lengths of both X$ and Y$, if the bytes of
X$ and Y$ are all equal so far): if the length of Y$ isn't zero yet
jump on to SEC PLUS
- (it has reached zero; either X$ and Y$ are equal
strings or they are equal except that Y$ is shorter than X$)
check if the length of X$ is zero as well.
_356B_SECND_LOW: if it too is zero jump on to BOTH
NULL;
the strings are equal
- (Y$ is shorter; this counts as X$ > Y$) get the eql/
gr-l flag and reverse it
- jump on to STR TEST.
_3572_BOTH_NULL (the strings are equal): get the eql/gr-l
flag and jump on to STR TEST.
_3575_SEC_PLUS (Y$ isn't counted down yet): check the
count on X$
- if it has reached zero jump on to FRST LESS; X$ is
equal to but shorter than Y$
- (both strings are equal so far, but both have more
bytes to come) compare the next bytes; DE is from X$, HL
from Y$
- if Y$ has a higher value jump on to FRST LESS
- if X$ has a higher value jump back to SECND LOW.
- (X$ and Y$ are still equal so far) move both pointers
on and decrement both length counters
- jump back to BYTE COMP.
_3585_FRST_LESS (X$ < Y$): get the eql/gr-l flag and
clear its carry.
_3588_STR_TEST: save the eql/gr-l flag

553
- put a zero on the calculator stack.
_358C_END_TESTS (see the description above): if carry is
set, call 3501 NOT
- if carry was_not set, call 34F9 GREATER 0
- rotate the odd/even flag to the right
- if it was even call NOT again.
Exit: RET.
Output parameters: HL points to the last value on the
calculator stack; zero for "false", one for "true", replacing
both the values originally placed there for comparison
- DE points to the stack end.
Rems:
2E24 PF SMALL all string comparisons can give wrong
answers due to mistake in ROM
338E ENT TABLE puts appropriate offset in B register

no-less see 353B no-l-eql

non-leading zero see 2DE3 PRINT FP

NON-MASKABLE INTERRUPT see interrupts, 0066 RESET

"Nonsense in BASIC" message see REPORT C

NO RESET 0070 (0066 RESET)


Jumps from:
0066 RESET

normal form, normalisation of FP numbers see 3155 TEST


NORM

NORMALISE 316C (3155 TEST NORM)


Jumps from:
3155 TEST NORM

NORML NOW 3186 (3155 TEST NORM)


Jumps from:

554
316E SHIFT ONE

NO RSTORE 31F9 (31AF division)


Jumps from:
31E2 DIV START

nos-eql, nos-neql see 353B no-l-eql

no-shift see KEYBOARD SCANNING

no./string flag see expressions


NOT key (C3) see also commands, functions and operators,
KEYBOARD SCANNING, 026A symbol code table (e)
The S key with symbol shift produces the function NOT;
it requires one logical or numeric operand X, and the value of
the function is one if X is zero and zero for any other value.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code C3 to 14h; NOT gets
special treatment, as its priority is only 04. It is given op code
F0.
Code and priority 04F0 are pushed on to the machine stack
(270D S PUSH PO) while the expression following NOT is
evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2373 S TIGHTER) from F0 to 30, the calculator
offset for 3501 not.

not subroutine 3501


Called from 0028 FP CALC with literal 30h; executes the
BASIC NOT function, but also used in other calculations,
sometimes by a direct call.
Given a FP number N, replaces it with one if N is zero,
otherwise with zero.
Used in calculator strings in combinations such as
30 not
30 not
00 jump-true

555
which is equivalent to a "jump on zero", see 368F jump-true.
Input parameters: HL points to N; last value on the
calculator stack if the call is from FP CALC, but it can be
anywhere in RAM for direct calls.
Action: call 34E9 TEST ZERO; it sets carry if N is zero.
Exit: into 350B FP 0/1, which changes the value on the
stack to zero or one depending on the carry flag.
Output parameters: HL points to the same address as on
input, but now it holds zero or one
- other registers, except A, unchanged.
Called by FP CALC from:
238D DE 3 PRMS (twice)
36B7 X NEG
384A sqr
3851 to-power
385D XISO
Direct calls from:
358C END TESTS (twice)

NOT BIN 2CB8 (2C9B DEC TO FP)


Jumps from:
2C9B DEC TO FP

note (beep) see timing

no-&-no subroutine 3524


Called from 0028 FP CALC with literal 08; executes the
BASIC AND operator between two numeric expressions X
AND Y. Cf 352D str-&-no, which executes X$ AND Y. Not
otherwise used in ROM, but could be called direct or through
FP CALC from m/c programs. X and Y must be in FP format;
when the subroutine is called from FP CALC, they are the last
values on the calculator stack, Y last, but for direct calls they
can be anywhere in RAM.
The BASIC AND is analogous to, but not quite the same
as, the Z80 AND instruction; X AND Y equals zero if Y is zero,
otherwise X AND Y = X. If X and Y are each one or zero, eg

556
"true" or "false", this works out the same as ANDing m/c bits,
rather than m/c bytes. Cf 351B or.
Input parameters: HL points to the first byte of X
- DE to that of Y.
Action: call 34E9 TEST ZERO for Y
- if Y wasn't zero, return; X is made last value
- (Y was zero) clear the carry
- exit to 350B FP 0/1; with NC in carry it zeroes the
value at the pointer.
Exit: RET or into 350B FP 0/1.
Output parameters: all registers except A unchanged
- Y is unchanged, X unchanged or zero
- on return to the calculator DE becomes the stack end,
deleting Y from the stack; but its bytes are still there.

NSPPC system variable 5C44 see also 5C42 NEWPPC


Bytes: 1
Normally holds FF, but if there is to be a "jump in
BASIC", ie after a GO TO, CONTINUE, GO SUB, NEXT or
RETURN,
NSPPC holds the_statement number within the BASIC line to
be executed next. The line number is in 5C42 NEWPPC.
Bit 7 of NSPPC is a flag: if bit 7 is set it signals "no
jump", simply execute the next statement in the BASIC line.
Bit 7 can only be set by NSPPC being set to FF, since 1B29
STMT L 1 only allows 7Fh/127d statements maximum in a line.
NSPPC is set to FF:
- when BASIC execution comes to an end, at 1386 MAIN 9
- during execution, as soon as the value of NSPPC has
been collected to indicate what to do next, at 1BD1 NEXT
LINE.
NSPPC is also
- set to zero on loading a new program; if the program
was saved with ... LINE there is a line number in 5C42
NEWPPC
- set to one when a direct BASIC command is to be

557
executed. Zero and one are equivalent, because 1BBF LINE
USE reads "statement zero" as "statement one".
- set with a statement number
- by 1D7C F FOUND, when the search for a NEXT
command
has been successful
- by 1E73 GO TO 2, for CONTINUE, GO SUB, GO TO,
NEXT
and RETURN commands.
Written by:
0873 LD PROG (00)
12CF MAIN 3 (01)
1386 MAIN 9 (FF)
1BD1 NEXT LINE (FF)
1D7C F FOUND (statement number)
1E73 GOTO 2 (statement number)
Read by:
1376 MAIN 7
1B7D STMT R 1
1B8A LINE RUN
1B9E LINE NEW
Rems:
1EDC CLEAR 2 as jump has been signalled, statements
following a RUN command in the same line
are ignored
1EED GOSUB set by call of GO TO 1
1F23 RETURN set by jump to GO TO 2

null name of save/load file see 0605 SAVE ETC

null string see strings

NUMBER subroutine 18B6


In displaying a BASIC line, or moving the cursor along
one already displayed, the number markers and the following
5-byte number forms are skipped; see below, number in
BASIC line.

558
Input parameters: HL holds the BASIC pointer
- A holds the byte in HL.
Action: if the byte isn't 0Eh, return
- (number marker) increment the pointer six times
- get the byte at the new position.
Exit: RET.
Output parameters: if A was 0E, HL has been incremented
and A holds the new content of HL. Otherwise no change.
Called from:
18A1 OUT LINE5
199A EACH S 3
Rems:
1894 OUT LINE4 numbers jumped over in printing line
1F94 DEF FN 5 number marker put in DEF FN

NUMBER AND NUMBER OPERATION see 3524 no-&-no

number array see arrays

'Number array: ' message see cassette messages

number forms (integer, 5-byte FP) see CALCULATE

number in BASIC line, number marker (0E)


As stored in the program area, the BASIC lines contain
6-byte insertions following every decimal, binary or E-format
number except the line numbers; the first byte contains the
_number_marker 0E, the remaining 5 the 5-byte FP equivalent
of the number in BASIC. These markers and FP forms aren't
put in after PI or other functions, only numbers written with
actual digits. They are automatically skipped over by calls to
18B6 NUMBER in printing out the line or moving the cursor.
The markers and values are inserted in the line while it
is still in the editing area, before it is copied to the
program, by 268D S BIN/DECIMAL in the SCANNING loop: the
syntax checking routine 1B17 LINE SCAN calls all the same
command class and command executive subroutines as the

559
run-time 1B8A LINE RUN, and whenever numeric expressions
are encountered S DECIMAL
computes and inserts the 5-byte forms. In run time, the same
routine looks for the number marker and reads the numeric
value from the FP form, ignoring the decimal or similar form.
If syntax checking finds errors in the line, its number
forms are removed before it is copied back to the lower
screen:
this is done by 11A7 REMOVE FP, called by 12AC MAIN 2, or by
2148 IN VAR 2 in the case of an INPUT expression.
Each DEF FN statement also has a 6-byte space after each
of its dummy variables on the left side, before the comma,
with 0E in the first byte. The spaces are made and marked by
1F94 DEF FN 5, but the FP number isn't put in the remaining 5
bytes till the function is evaluated during a run, in 2852 SF
ARG VL.
The presence of these number markers is often forgotten,
but is important if you are trying to write BASIC which is
economical of memory: an expression such as "1/7" in the
program area actually takes up 15d bytes:
"1" (number form) "/" "7" (number form)
31h 0Eh 00h 00h 01h 00h 00h 2Fh 37h 0Eh 00h 00h 07h
00h 00h
one more than 0.142857.
1894 OUT LINE4 numbers jumped over in printing line
26B6 S SD SKIP steps over characters till marker found
2843 SF ARG LP number marker sets string status flag
295A SFA LOOP finds number marker of variable

number of bytes in save/load block see program/data block

number of dimensions see arrays

"Number too big" message see REPORT 6

numeral keys see letters and digits

560
NUMERIC subroutine 2D1B see also 2C8D ALPHA, 2C88
ALPHANUM
Checks if a given character is a digit. One of the most
useful of all ROM routines for the m/c programmer.
Input parameters: character code in A.
Action: if A < 30h "zero" return with carry
- check A against 3Ah; "nine" is 39h
- reverse the carry and return.
Exit: RET.
Output parameters: NC if A was a digit, carry if not
- all registers unchanged.
Called from:
1937 OUT CHAR
2C88 ALPHANUM
2CB8 NOT BIN
2CCB DECIMAL
2CFF ST E PART
2D22 STK DIGIT

numerical comparisons see 353B no-l-eql

numeric argument, array, entry, expression, function,


marker
flag, numeric/string flag, result, status, value, variable see
expressions, variables

NU OR STR 354E (353B no-l-eql)


Jumps from:
3543 EX OR NOT

NXT DGT 1 2CDA (2C9B DEC TO FP)


Jumps from:
auto

NXT DGT 2 2D40 (2C9B DEC TO FP)


Jumps from:
auto

561
NXTLIN system variable 5C55
Bytes: 2
The address in the program area of the start of the next
line in the program. It is one of the fourteen system pointers
adjusted by 1664 POINTERS.
It is set to a new value by 1BD1 NEXT LINE each time
execution of a new BASIC line starts, and by 1D64 F LOOP
when a NEXT command has been found in performing FOR ...
NEXT loops.
It is used only by 1BB3 LINE END, to read the next line
number, if any.
Written by:
166B PTR NEXT
1BD1 NEXT LINE
1D64 F LOOP
Read by:
166B PTR NEXT
1BB3 LINE END
1D64 F LOOP

562
O

offset for calculator operations see CALCULATE

OFLOW CLR 3195 (3155 TEST NORM)


Exit from:
3155 TEST NORM through one of:
315E SKIP ZERO (twice)
3186 NORML NOW (twice)

OFLW1 CLR 3146 (3155 TEST NORM)


Misprinted OFLW 1 CLR.
Jumps from:
313D DIVN EXPT

OFLW2 CLR 3151 (3155 TEST NORM)


Jumps from:
3146 OFLW1 CLR (twice)

'OK' message see REPORT 0 [zero]

OLDPPC system variable 5C6E


Bytes: 2
The line number to which execution jumps from
CONTINUE.
See also 5C70 OSPCC, the statement number within this line.
It is written by 1376 MAIN 7 each time BASIC execution
is completed. If bit 7 of 5C44 NSPPC is zero, indicating a
“jump in BASIC", 5C42-44 NEWPPC and NSPPC are copied to
5C6E-70 OLDPPC and OSPCC; if not 5C45-47 PPC and SUBPPC.
The jump to the line and statement is made by 1E5F
CONTINUE.
Written by:
1376 MAIN 7

563
Read by:
1E5F CONTINUE

old variable see variables

ONE 386A (3851 to-power)


Jumps from:
385D XISO

one-byte binary arithmetic


Much ingenuity has been expended by the programmers
on devices to perform quite complex arithmetic with single-
byte positive integers zero -> 255d, depending usually on
rotations, AND/OR/XOR, and of course the ADD/ADC/SUB/
SBC operations. Novice m/c programmers would do well to
study and imitate these devices. It is impossible to give any
rules, but clearly a thorough familiarity with binary arithmetic
is essential.
For instructive examples see especially 0E00 CL SCREEN,
0E44 CL LINE, 0E88 CL ATTR, 0E9B CL ADDR.

ones complementing see twos complementing

ONE SHIFT 2FE5 (2FDD SHIFT FP)


Exit from:
2FDD SHIFT FP
Jumps from: auto
ONE SPACE subroutine 1652
Entry point into 1655 MAKE ROOM when only a single
byte of space is required.
Input parameters: HL is the address before which the
space is required.
Action: make a length byte one
Exit: into 1655 MAKE ROOM, which makes one space at
HL.
Output parameters: see MAKE ROOM.
Called from:

564
0F81 ADD CHAR

OPEN subroutine 1736


Called only from the syntax parameter table 1A7A;
executes the command OPEN (hatch) X,Y$. The command in
the original Spectrum only allows streams 00 -> 0F to be
opened to channels K, S or P; the routine seems incomplete
and rather pointless. It is clearly designed for expansion by
the addition of peripherals such as Interface 1.
Input parameters: none
- string parameters of a channel code Y$ are last value
on the calculator stack
- a stream number X is second last value.
Action: call 0028 FP CALC to exchange Y$ and X on the
stack; now X is last
- call 171E STR DATA to get the stream data of stream X;
it gets a pointer to the stream data from 5C10 STRMS and
reports "Invalid stream" if X isn't 00 -> 0F, see channels and
streams
- if the stream data is zero jump on to OPEN 1; closed
stream
- (open stream) save the stream data pointer
- add the stream data to the address in 5C4F CHANS and
step on three times; now the pointer is on the channel code
- if the code is 4Bh "K" or 53h "S" or 50h "P" jump on
to OPEN 1
- (only K, S or P accepted) report "Invalid stream".
_1756_OPEN_1: call 175D OPEN 2, which gets the string
parameters of Y$ from the calculator stack and returns with
the stream data for the corresponding channel
- put these at the streams pointer.
Exit: RET, in 1756 OPEN 1.
Output parameters: none
- the calculator stack has been cleared.

OPEN END 178B (175D OPEN 2)


Exit from:

565
175D OPEN 2 through one of
1781 OPEN K
1785 OPEN S
1789 OPEN P

OPEN K 1781
OPEN P 1789
OPEN S 1785
Addresses jumped to from the open stream lookup table
at 177A by the JP (HL) command in 1767 OPEN 3; see 175D
OPEN 2.

OPEN STREAM LOOK-UP TABLE see tables

OPEN 1 1756 (1736 OPEN)


Exit from:
1736 OPEN (3 times)

OPEN 2 subroutine 175D


Given a channel letter, returns the stream data to find
that channel; see channels and streams. In the standard
Spectrum without peripherals, this routine achieves very
little. However peripherals, by relocating the error stack
pointer and thus introducing their own variations on the error
routine for "Invalid file name", can allow new channels and
other variations on the use of the OPEN command.
Input parameters: none
- the string parameters of the channel letter Y$ are on
the calculator stack.
Action: call 2BF1 STK FETCH, which reads the string
parameters of Y$ into registers AEDBC; DE is the start
address, BC the length of the string
- if the length is zero report "Invalid file name".
_1767_OPEN_3: read the channel letter
- AND it with 11011111b/DFh; this makes it upper case
- call 16DC INDEXER to index with it into the open
stream look-up table at 177A; it has entries only for channels

566
K, S and P
- if the letter isn't K/S/P report "Invalid file name"
- use the offset obtained to compute a jump to the next
section; OPEN K, OPEN S or OPEN P.
_1781_OPEN_K,_1785_OPEN_S,_1789_OPEN_P: each sets the lo
byte of the stream data corresponding to the channel letter:
01 00 for channel K
06 00 for channel S
10 00 for channel P
_178B_OPEN_END: decrement the length of Y$
- if the result isn't zero report "Invalid file name";
more than one letter
- zero the hi byte of the stream data and return.
Exit: RET, from OPEN END.
Output parameters: DE holds the stream data
- HL is unchanged.
Called from:
1756 OPEN 1

OPEN 3 1767 (175D OPEN 2)


Jumps from:
175D OPEN 2

OPEN (hatch) key (D3) see also commands, functions and


operators, KEYBOARD SCANNING, 0284 extended mode key
table (f )
The 4 key in E mode with symbol shift produces the
command OPEN (hatch). The "OPEN (hatch) statement" must
also include a numeric expression for the stream and a single-
letter string expression for the channel, separated by a
comma.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AFC P OPEN jumps in sequence to:
1C82 CLASS 06 to get an expression for the stream
number

567
back to 1AFD to check that there is a comma
1C8C CLASS 0A to get the expression for the channel
1C10 CLASS 00 to check the statement is ended
and then via 1C16 JUMP C R to the executive routine 1736
OPEN.

OPEN (hatch) COMMAND ROUTINE see OPEN subroutine

operands see arguments

operands of BASIC commands see BASIC INTERPRETER

operands of control characters see control characters

operands of DEF FN statement/function see DEF FN key

operands of INK to OVER see colours

operation codes
The token code for a single-valued function command is
transformed twice to make it a calculator literal: once in 26DF
S NEGATE before it is pushed on to the machine stack, and
again in 2734 S LOOP when it is taken off. The version put on
the stack, the_operation_code, has flags in bits 6 and 7:
bit 6 is set for a numeric argument, zero for a string
bit 7 is set for a numeric result, zero for a string
Binary operators are all initially assumed to have
numeric arguments and numeric result. The operators + = > <
<=
>= <> can also be used with two string arguments, and AND
may have its first argument a string and its second a number.
These are checked in 2773 S TIGHTER after the first
argument has been read: if it was a string, not only are the
argument and result flags changed, the op code itself is
incremented by 8, which for example changes 0F addition for
numeric arguments to 17 str-add$ for string arguments.
24FB SCANNING op code and priority stacked while

568
arguments evaluated
26DF S NEGATE op codes and priorities allotted to all
single-valued operations
2707 S NO TO $ clears bit 6 of CHR$ and STR$
270D S PUSH PO stacks op code and priority
2723 S OPERTR binary operators all have both 6 & 7 set
2734 S LOOP priorities compared of "last" and
"present" operation
274C S STK LST clear both flags, producing calc literal
275B S SYNTEST check result with FLAG 6
2764 S RUNTEST set FLAG 6 to match op code result flag
2773 S TIGHTER binary operator codes changed if 1st
argument is string; all except AND now
look for string as 2nd operand also
2788 S NOT AND some ops not possible between strings

operation offset see CALCULATE

operations/operators see commands, functions, and


operators

OR key (C5) see also commands, functions and operators,


KEYBOARD SCANNING, 026A symbol code table
The U key with symbol shift produces the operator OR; it
must be both preceded and followed by an expression with a
logical value.
The effect of the operator OR is to give a value to the
expression "X OR Y": it is X if Y has logical value zero,
otherwise it is one. This isn't the same as the Z80 assembly
code OR, though it is analogous in the case where X and Y are
both one or zero. OR cannot be used between strings.
On execution, after the first expression has been
evaluated in 268D S DECIMAL, 24FB SCANNING quickly leads
to 2723 S OPERTR. Here the token code C5 is looked up in the
two tables 2795 table of operators and 27B0 table of priorities,
giving a

569
value 02C7 in BC and this is put on the machine stack in 2790
S NEXT: 02 is the priority and C7 the code of the OR operator.
When the code comes off the calculator stack in 2734 S
LOOP, C7 is converted to 07, the literal for 351B or.
2788 S NOT AND not accepted between strings

or subroutine 351B
Called from 0028 FP CALC by literal 07; executes the
BASIC OR operator. The BASIC OR is analogous to the m/c OR;
X OR
Y equals X if Y is zero, otherwise X OR Y = 1. If X and Y are
each one/"true" or zero/"false", this works out the same as
ORing m/c bits, rather than m/c bytes. Cf 3524 no-&-no.
Not otherwise called from ROM; could be called either
direct or through 0028 FP CALC from m/c programs.
Input parameters: HL points to the first byte of X
- DE to that of Y; for calls through the calculator Y
must be the last value and X the second last value on the
calculator stack, but for direct calls they may be located
anywhere in RAM.
Action: call 34E9 TEST ZERO for Y
- if Y is zero, return; X becomes last value
- (Y non-zero) set carry and exit into 350B FP 0/1; it
changes the last value to one.
Exit: RET or into 350B FP 0/1.
Output parameters (from FP 0/1): HL and DE still point to
the same addresses, but on return to the calculator DE will be
made the stack end so the result will become last value; the
bytes of Y are still in place though they have been deleted
from the stack
- A is zero, other registers unchanged.

ordinary listing see automatic listing

OSPCC system variable 5C70


Bytes: 1

570
The statement number within the line to which execution
jumps on CONTINUE. See 5C6E OLDPPC.
[I don't know why OSPCC ends in PCC whereas all the
other BASIC line and statement number svs end in PPC: PPC
itself, E PPC, NEWPPC, NSPPC, OLDPPC, SUBPPC. But it is
consistent throughout "ROM Disassembled" and all the
Spectrum handbooks I have seen. I don't even know what PPC
is supposed to stand for. There must be some arcane reason.]
Written by:
1376 MAIN 7
Read by:
1E5F CONTINUE

OTHER STR 35B7 (359C str-add$)


Jumps from:
359C strs-add

OUT key (DF) see also commands, functions and operators,


KEYBOARD SCANNING, 0246 extended mode key table (c)
The O key in E mode with either shift produces the
command OUT. The "OUT statement" must also include two
numeric expressions X,Y, separated by a comma; they needn't
be integers but X must be positive and less than 10000h/
65536d, Y may be negative but will be read in that case as
256d minus its absolute value, which must always be less than
256d.
IN is a function, OUT a command; cf PEEK and POKE.
The effect of the command is to send the byte Y to the port X.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AF1 P OUT jumps in sequence to:
1C7A CLASS 08 to get the two parameters
1C10 CLASS 00 to check the statement is ended
and then via 1C16 JUMP C R to the executive routine 1E7A
OUT.

571
OUT subroutine 1E7A
Called only from the syntax parameter table 1A7A;
executes the BASIC command OUT X,Y, when X is a port
address, Y a one-byte value. Could be called from m/c, but it is
simpler just to use OUT (C),A or similar.
Input parameters: HL points to the first byte of X,
second last value on the calculator stack
- DE to that of Y, last value.
Action: call 1E85 TWO PARAM to put Y in the A register
and X in BC
- now OUT (C),A has the desired effect.
Exit: RET.
Output parameters: A and BC as shown, X and Y have
gone from the stack.

OUT CHAR subroutine 1937


Prints any character code from the BASIC; ie outputs it
to main screen, lower screen or ZX printer depending on the
stream and channel in use.
The subroutine is mainly concerned with deciding the
mode, K or L/C, in which the next keystroke is to be
interpreted or the cursor printed, by signalling with bit 2 of
FLAGS; see the index entry. All codes signal L/C mode except
THEN and usually the colon, because all K mode keystrokes
occur at the start of a line or statement.
[Error in REMs: if you try to write a REM such as:
5000 REM subroutine: display table
the character after the ":" will print as a token:
5000 REM subroutine: DIM isplay table
There are various ways of circumventing this, eg by
putting quotes after the REM token - if you always do this, you
needn't worry about the colon problem.
Occasionally it can be made use of:
REM : PRINT THEN RUN
can be typed with only five keystrokes!
This is the meaning of the cryptic note at the end of

572
196D OUT CH 3. It isn't easy to see how to correct the ROM, as
there is no "REM" flag. Probably there should be one.]
Input parameters: A holds a character code.
Action: call 2D1B NUMERIC, and if the code is a digit
jump on to OUT CH 3; there will be no mode change
- if it is less than 21h jump on to OUT CH 3; PRINT
comma, colour codes and space also make no change
- zero bit 2 of FLAGS; K mode
- if it is CB THEN jump on to OUT CH 3; THEN is always
followed by a command, even in quotes or input mode
- if it isn't 3A colon jump on to OUT CH 1; this will
make L mode
- (colon) if bit 5 of FLAGX is set jump on to OUT CH 2;
L mode is used in input mode even after colon
- (not in input mode) if bit 2 of FLAGS2 is zero jump on
to OUT CH 3; K mode after colon not in quotes [even in REMS]
- (in quotes) jump to OUT CH 2; L mode in quotes.
_195A_OUT_CH_1 (all except digits, space, control codes,
THEN and colon):
- if it isn't code 22h " jump on to OUT CH 2
- (quotes) flop bit 2 of FLAGS2; the "quotes" flag.
_1968_OUT_CH_2 (all but digits, space, control codes,
THEN; colon in quotes or INPUT): set bit 2 of FLAGS; L mode.
_196D_OUT_CH_3: call 0010 PRINT A 1 to output the code.
Exit: RET.
Output parameters: none.
Called from:
18A1 OUT LINE5
Exit from:
1925 OUT SP 2 (192A OUT SP NO)

OUT CH 1 195A (1937 OUT CHAR)


Jumps from:
1937 OUT CHAR

OUT CH 2 1968 (1937 OUT CHAR)


Jumps from:

573
1937 OUT CHAR (twice)
195A OUT CH 1

OUT CH 3 196D (1937 OUT CHAR)


Jumps from:
1937 OUT CHAR (4 times)

OUT CODE subroutine 15EF


Alternative entry point to routines which output a data
byte by the channel/stream system: see channels and streams.
It differs from the more frequently used restart 0010
PRINT A 1 in adding 30h/48d to the value of the data byte
before outputting it. This converts a decimal digit value to the
corresponding character code, but it is also used with report
codes: the error number is incremented in 1303 MAIN 4, then
output immediately as a digit if it is less than 0Ah or
increased by 7 first in 1313 MAIN G to give the correct letter.
Input parameters: A holds the byte to be output.
Action: add 30h/48d to A.
Exit: into 15F2 PRINT A 2.
Output parameters: A holds the code to be output.
Called from:
133C MAIN 5
1A42 OUT NUM 4
2F46 PF NOT E
2F59 PF OUT DT
Exit from:
192B OUT SP 1 (192A OUT SP NO)

OUT COMMAND ROUTINE see 1E7A OUT

OUT CURS subroutine 18E1


Prints a flashing cursor, C, E, G, K or L depending on
the current mode; ie outputs it through the current channel.
Input parameters: DE is an input pointer to the address
in the input or editing area of the next character to be printed
- 5C5B K CUR holds a cursor pointer to the address in

574
the input at which the cursor is to be printed.
Action: if the input pointer isn't on the cursor pointer,
return at once
- double the value in 5C41 MODE; making zero for K/L/C
mode, 2 for E mode, 4 for G mode
- if the result is zero jump on to OUT C 1; K/L/C modes
- add 43h; making 43 + 2 = 45h, the code for "E", or 43
+ 4 = 47h, the code for "G"
- jump on to OUT C 2.
_18F3_OUT_C_1 (K/L/C modes): zero bit 3 of FLAGS; "last
character was in K mode"
- load 4Bh "K" for the cursor
- if bit 2 of FLAGS is zero jump on to OUT C 2; "next
character in K mode", see entry on FLAGS bit 2
- (L/C mode) set bit 3 of FLAGS
- increment 4Bh "K" to 4Ch "L"
- if bit 3 of FLAGS2 is zero jump on to OUT C 2; CAPS
LOCK is off
- (CAPS LOCK is on) load 43h "C" for the cursor.
_1909_OUT_C_2: call 18C1 OUT FLASH to print the selected
letter as a flashing cursor.
Exit: RET, from OUT CURS or OUT C 2.
Output parameters: DE is saved unchanged
- svs unchanged except for bit 3 of FLAGS, which is made
to correspond with bit 2.
Called from:
111D ED COPY
18A1 OUT LINE5

OUT C 1 18F3 (18E1 OUT CURS)


Jumps from:
18E1 OUT CURS

OUT C 2 1909 (18E1 OUT CURS)


Exit from:
18E1 OUT CURS through
18F3 OUT C 1 (twice)

575
OUT FLASH subroutine 18C1
Prints a flashing cursor in the BASIC line; ie outputs a
byte through the current channel. This may be either a "?" to
mark a syntax error, or the "mode cursor", letter C, E, G, K or
L signalling where input is currently being placed.
Input parameters: A holds the character code of the
cursor.
Action: use the alternate registers; apparently only to
save DE in the call from 1894 OUT LINE4.
- stack the values of 5C8F ATTR T and 5C90 MASK T
- zero bit 7 of MASK T and set bit 7 of ATTR T; this
makes FLASH
- stack the value of 5C91 P FLAG
- zero its byte; this cancels any OVER, PAPER 9 or INK 9
signals. INVERSE is irrelevant.
- call 09F4 PRINT OUT, which outputs the character code
through the current channel
- restore the system variables to their former state
- restore the main registers.
Exit: RET.
Output parameters: HL, BC and DE are preserved by EXX,
but HL', DE' and BC' are corrupted.
Called from:
1894 OUT LINE4
1909 OUT C 2

OUT LINE subroutine 1855


Prints a complete BASIC line, with line number and the
current line marker ">" if appropriate; ie outputs it byte by
byte through the current channel.
Returns with a flag showing whether this line is before/
equal to the current line or after it. The handling of this
current line flag isn't well explained in the notes:
- OUT LINE sets the flag to 00 "this is beyond the
current line" or 01 "this is before the current line"_if_the
_present_line_is_not_the_current_line, but if it is the current

576
line the flag isn't changed
- the only ROM call to LIST ALL, in 17ED AUTO L 4, is
actually a call of the address 1833, two bytes before the
heading shown in the listing; but looping back within LIST
ALL is to 1835 as shown. The command LD E,01 at 1833 is
therefore part of the subroutine, but isn't looped back to
- 5C67 BREG, in which the current line flag is saved in
OUT LINE, is read by 0C55 PO SCR in automatic listings; if the
flag is one, and the screen is full, it will be scrolled till
the current line is printed.
The entry point at 187D OUT LINE2 is also used during
input or editing of BASIC; the whole line, as it exists so far,
is copied from the input/editing area into the lower part of the
screen each time a key is pressed. This version of the BASIC
line doesn't have the inserted FP forms of the numbers, and
its line number is just a string of decimal digits: these things
are changed when the line is added to the program by 155D
MAIN ADD.
Printing the line from editing is therefore simpler than
printing a line out of the program area, as the line number
can be printed as it stands and there is no question of a
current line marker.
Input parameters: HL is a BASIC pointer to the address in
the editing or program area of the start of the line; this is
the hi byte of the hi-lo line number
- E holds the current line flag.
Action: get a value from 5C49 E PPC; this is the number
of the line which is to be marked as current
- call 1980 CP LINES; it returns with Z if this is the
current line and with C if it is before the current line
- make the "cursor byte" code 3E >
- if this is the current line jump on to OUT LINE1
- (not current line) zero the cursor byte and the
current line flag
- move the carry into bit zero of the flag; now it is
one if this line is before the current line, zero if after.
_1865_OUT_LINE1 (misprinted as OUT LINE): save the

577
current line flag in 5C67 BREG
- if the hi byte of the line number is more than 40h
make a "double return" by dropping the return address and
then returning; the line start is beyond the end of the
program
- call 1A28 OUT NUM 2 to print the line number, with
leading spaces
- advance the BASIC pointer three times; over the lo
byte of the number and the length bytes
- zero bit zero of FLAGS; "leading space allowed"
- if the cursor byte is zero jump on to OUT LINE3; no
current line cursor is to be printed
- call 0010 PRINT A 1 to print the >
_187D_OUT_LINE2 (entry point from 111D ED COPY when
input or editing is in progress): set bit zero of FLAGS; "no
leading space". The current line cursor replaces the leading
space, if it was printed, and for input this is the start of the
line.
_1881_OUT_LINE3: zero bit 2 of FLAGS2; "not in quotes"
- zero bit 2 of FLAGS; "K mode"
- if bit 5 of FLAGX is zero jump on to OUT LINE4; not in
input mode
- (input mode) set bit 2 of FLAGS; "L mode".
_1894_OUT_LINE4 (loop back to here after each character
has been output): get the syntax error pointer from 5C5F X
PTR
- if it doesn't match the BASIC pointer jump on to OUT
LINE5
- (error position) make a byte 3F ?
- call 18C1 OUT FLASH to print it as a flashing cursor.
_18A1_OUT_LINE5: call 18E1 OUT CURS, which does its own
check and prints the mode cursor if the BASIC pointer
matches 5C5D K CUR
- read the next code
- call 18B6 NUMBER; if it is a number marker it and the
FP number form which follows it are skipped
- move on the BASIC pointer; but don't read its code yet

578
- if the code read last was a newline jump on to OUT
LINE6; a newline will be printed anyway after this subroutine
returns
- call 1937 OUT CHAR to output the character
- loop back to OUT LINE4.
_18B4_OUT_LINE6: clear the machine stack and return.
Exit: RET from OUT LINE6, or double RET from OUT
LINE1;
this makes a complete return from the 17F9 LIST command
routine if the call was from 1835 LIST ALL. It cannot arise in
the calls from the EDITOR loop.
Output parameters: E holds zero for "current line has
been displayed" or one "for current line not yet displayed";
after the calls from the EDITOR loop it may hold any value.
Called from:
0FA9 ED EDIT
1835 LIST ALL

OUT LINE1 1865 (1855 OUT LINE)


Exit from:
1855 OUT LINE

OUT LINE2 subroutine 187D (1855 OUT LINE)


An alternative entry point, see above.
Called from:
111D ED COPY

OUT LINE3 1881 (1855 OUT LINE)


Jumps from:
1865 OUT LINE1

OUT LINE4 1894 (1855 OUT LINE)


Jumps from:
1881 OUT LINE3
18A1 OUT LINE5

OUT LINE5 18A1 (1855 OUT LINE)

579
Jumps from:
1894 OUT LINE4

OUT LINE6 18B4 (1855 OUT LINE)


Exit from:
1B55 OUT LINE through
18A1 OUT LINE5

OUT NUM 1 subroutine 1A1B


Prints a decimal integer of up to four digits, without
leading spaces; ie outputs it byte by byte through the current
channel. Used to print line numbers in error reports, and the
exponents of numbers in E format.
The entry point OUT NUM 2 is used in printing out BASIC
lines, and in this case leading spaces are printed.
Either entry point can also be called from m/c. They
make no check on the size of the number: numbers above
8000h/ 32768d will print as zero from OUT NUM 1, but other
numbers above 9999d will print as wrong digits, as will any
above 9999d from OUT NUM 2. You can write your own
"Chinese copy" of the OUT NUM 3 routine with an extra call to
192A OUT SP NO for a fifth digit; BC should hold
D8F0h/-10000d for the first call. See OUT SP NO for the
mechanism of digit calculation.
Input parameters: BC holds the integer.
Action: make a zero digit byte
- if the hi bit of the integer is set jump on to OUT NUM
4; BC >= 8000h/32768d, and is assumed to be -2, the line
number of a direct command, which is printed as zero
- make a signal FF for "no leading spaces"
- jump on to OUT NUM 3.
_1A28_OUT_NUM_2 (entry point for printing line numbers
with leading spaces; HL holds a pointer to the hi-lo line
number): read the hi-lo number
- make the signal 20h for "leading spaces".
_1A30_OUT_NUM_3: make three successive calls to 192A
OUT SP NO to output the three first digits, with BC

580
successively FC18h/-1000d, FF9Ch/-100d and FFF6h/-10d;
what is left of HL is brought round again with the next BC
when the subroutine is called again
- load the digit byte with L; this is now the fourth
digit.
_1A42_OUT_NUM_4: call 15EF OUT CODE to output the
fourth digit.
Exit: RET.
Output parameters: HL and DE are saved and restored
unchanged.
Called from:
133C MAIN 5 (misprinted)
Exit from:
2F85 PF E SIGN (again misprinted, differently!)

OUT NUM 2 subroutine 1A28


An alternative entry to OUT NUM 1, see above.
Called from:
1865 OUT LINE1
Rems:
1A1B OUT NUM 1 prints numbers up to 9999d

OUT NUM 3 1A30 (1A1B OUT NUM 1)


Jumps from:
1A1B OUT NUM 1

OUT NUM 4 1A42 (1A1B OUT NUM 1)


Jumps from:
1A1B OUT NUM 1

'Out of memory' message (13C6) see REPORT 4

out of range error see 2ACC INT EXP1

'Out of screen' message (13D2) see REPORT 5

output, see printing

581
output address of channel, output routine of streams see
channels and streams

output routines see printing


OUT SP NO subroutine 192A
Prints the decimal digit of an integer, and returns the
value of the integer after "decapitation" of its first digit,
ready for a further call. It will print leading spaces if
supplied with a space byte in the E register, and no leading
character if supplied with FF. Any other value in the E register
will print in the position of leading zeroes. After each call to
15EF OUT CODE from OUT SP 1, the E register holds 30h
"zero" so non-leading zeroes print correctly as zeroes.
It is called only from 1A30 OUT NUM 3, which makes
three successive calls to OUT SP NO, one for each of the three
first digits of a four-digit number, with BC indicating the digit;
BC is successively FC18h/-1000d, FF9Ch/-100d and
FFF6h/-10d, and what is left of HL is brought round again
with the next BC when the subroutine is called again.
There is no SUB HL,BC in the Z80 instruction set, but
ADD HL,BC is just as good if BC is negative. The loop in OUT
SP 1 "subtracts" BC repeatedly from the integer, keeping count
in A, till the "subtraction"_fails to set carry; then it reverses
the last "subtraction" and decrements the count, giving the
required result.
Despite the arrangement of the ROM, the two subroutines
in this section are OUT SP NO and OUT CHAR - to which OUT
SP NO also exits. OUT SP 2 would have been better placed
after OUT SP 1 where it belongs, in fact this would have saved
a jump instruction. Here the routines are taken in their logical
order, not their numeric order.
Input parameters: HL holds an integer less than 2710h/
10000d.
- BC holds FC18h/-1000d on the first call, FF9C/-100d on
the second and FFF6h/-10d on the third
- E holds 20h space if leading spaces are required, FF

582
if not; until any digit has printed and then 30h "zero", from
OUT CODE.
Action
_192A_OUT_SP_NO: make a zero digit counter.
_192B_OUT_SP_1: "subtract" BC from HL
- increase the digit counter
- if the "subtraction"_didn't make a carry jump back to
OUT SP 1
- "add" it back and decrement the digit counter; it now
holds the required digit
- if the digit is zero jump on to OUT SP 2
- (non-zero digit) exit into 15EF OUT CODE.
_1925_OUT_SP_2 (zero digit): copy the leading space flag
to the digit byte
- if it is FF return; skip leading spaces
- (it is 20h space or 30h zero) exit to 1937 OUT CHAR.
Exit: RET from OUT SP 2
- through 15EF OUT CODE, which prints the digit, from
OUT SP 1
- through 1937 OUT CHAR, which prints the space or zero,
from OUT SP 2.
Output parameters: A holds the digit
- HL holds the "remainder" from the input integer after
decapitation of its first decimal digit; eg if HL was input as
2304d on the first call, it will be 304d and 04 on the two
following calls.
Called from:
1A30 OUT NUM 3 (3 times)
Rems:
1925 OUT SP 2 line nos may require leading spaces or not

OUT SP 1 192B (192A OUT SP NO)


Jumps from:
auto

OUT SP 2 1925 (192A OUT SP NO)


Jumps from:

583
192B OUT SP 1

OVER key (DE) see also KEYBOARD SCANNING, extended


mode key
table (c)
The N key in E mode with either shift produces the token
OVER. OVER can be used either as a BASIC command or as a
print control item within a PRINT etc statement. In either
case it causes any new printing to merge with what is on
screen already, overwriting it bit by bit rather than byte by
byte.
The OVER mask is 00000000 for OVER 0 and 11111111 for
OVER 1. This mask is ANDed with the pixel already on screen,
which blanks it completely for OVER 0 and leaves it
unchanged for OVER 1; then the result is XORed with the new
pixel. See 0B93 PR ALL 1, 0BB7 PR ALL 4; for an example see
under 0B24 PO ANY.
As a command, it is read by 1B29 STMT L 1 referring
through the syntax offset table 1A48 to the syntax parameter
table 1A7A. 1AF0 P OVER causes a jump to 1C96 CLASS 07
(PERMS), the executive routine for all the colour item
commands INK to OVER.
As a print control item, execution is from within the
PRINT executive routine 1FCD PRINT; each new expression
following the PRINT command is checked by a call to 1FFC PR
ITEM 1 from 1FE5 PRINT 3. If it is OVER, this in turn calls 21F2
CO TEMP 3 from 2024 PR ITEM 3; here the token code DE is
converted to the "embedded" control code 15h, which is then
sent through the output routine followed by the parameter
one or zero.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 15h for OVER into the
control character table at 0A11 produces an indirect jump to
0A7A (0A20 + 5A) PO 1 OPER. See under PO 1 OPER for the
rather tricky way in which it collects the parameter and sends
execution to 0A87 PO CONT, which finally executes the OVER
command.

584
Introduction checked on every input
0A3D PO RIGHT cursor right equiv to PRINT OVER
1;CHR$ 32
18C1 OUT FLASH ensures OVER 0 for flashing cursors

overflow
Can mean several different things, but primarily
_overflow refers to numbers produced by calculation which
are too large to fit into the particular register(s) designated for
them, or too large for the Spectrum system as a whole to
handle; this latter is_arithmetic_overflow. Either of these
usually gets reported as an error.
_Underflow may mean either
"big underflow": a minus number has been produced
which similarly overflows the system or the registers, or
"zero underflow": a number has been produced whose
absolute value is so small as to be indistinguishable from zero.
Big underflow is an error, zero underflow merely results
in zero being used as the value instead.
The exponent of a full-format FP number cannot be zero,
because if its exponent byte is zero it must be read as a small
integer; so the smallest positive number that can be
represented is 01 00 00 00 00, which represents 2**-127 times
one-half, ie 2**-128 or one divided by 2**128. Anything less
than this must be reformed as zero, 00 00 00 00 00. It is
about 2.94 E-39,
0.0...0294 with the "..." representing 37 zeroes. The negative
of 01 00 00 00 00 is 01 80 00 00 00.
The largest possible exponent is obviously FF, true
exponent 7Fh/127d. If an operation produces a number with
exponent bigger than 127d, it must be detected and the
"Arithmetic overflow" report given.
Sometimes in the notes "overflow" means little more than
"carry", eg when FF is added to FF giving FE with carry,
occasionally called "overflow to the left", or shifting a byte to
the right produces a carry called "overflow to the right".
These are usually not errors.

585
1E9C FIND I 1 flag indicates 1-byte or 2-byte integer
too big for registers.
2AF4 GET HL*DE overflow signalled as "Out of memory"
2CA2 BIN DIGIT system cannot handle 17-digit BIN inputs!
2CFF ST E PART overflow if exponent > 127d
2F25 PF R BACK "overflow to the left"; more digits
needed
2FDD SHIFT FP "overflow to the right"; round up lo bit
2FFB ZEROS 4/5 makes zero from zero underflow
3004 ADD BACK incrementing exponent may overflow
3014 addition if small integer addn overflows, FULL
ADDN
303E FULL ADDN two quite different overflows:
1. if adding the mantissas comes out > 1,
shift them right and increment the
exponent (or vice versa for negatives);
2. the answer may be too big for the
system
3055 SHIFT LEN same as last
307C TEST NEG big underflow is possible
30A9 HL=HL*DE returns with carry if overflow
30CA multiply if small integers overflow, MULT LONG
30F0 MULT LONG has check for overflow and zero
underflow
313D DIVN EXPT overflow indicated by NC
3146 OFLW1 CLR checks "true exponent" for overflow
3151 OFLW2 CLR multiplying 4-byte mantissas has
produced
a 5-byte result; an entirely different
"overflow"
3155 TEST NORM handles zero underflow
3186 NORML NOW final carry may produce too big result
31AF division dividing by zero produces "Number too big"
37DA tan sin/cos can produce overflow
3851 to-power zero to minus power gives overflow
385D XISO divide by zero to make overflow

586
overhead see 05E7 LD EDGE 1

-----

PAPER key (DA) see also colours, commands, functions and


operators, KEYBOARD SCANNING, 0246 extended mode key
table (c)
The C key in E mode with either shift produces the token
PAPER. PAPER can be used either as a BASIC command or as
a print control item within a PRINT etc statement. In either
case it sets the "pixel off" colour of any new printing on the
screen;
INK sets the "pixel on" colour. It must be followed by a
parameter, a numeric expression with the value zero to 9.
Zero to seven are the screen colours, PAPER 8 means "don't
change the PAPER colour in this position", PAPER 9 means
"use black or white, whichever contrasts best with the INK
colour".
As a command, it is read by 1B29 STMT L 1 referring
through the syntax offset table 1A48 to the syntax parameter
table 1A7A. 1AEC P PAPER causes a jump to 1C96 CLASS 07
(PERMS), the executive routine for all the colour commands
INK to OVER.
As a print control item, execution is from within the
PRINT executive routine 1FC0 PRINT; each new expression
following the PRINT command is checked by a call to 1FFC PR
ITEM 1 from 1FE5 PRINT 3. If it is PAPER, this in turn calls 21F2
CO TEMP 3, from 2024 PR ITEM 3; here the token code DA is
converted into the embedded control code 11h, which is then
sent through the output routine followed by its parameter.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 11h for PAPER into the
control character table at 0A11 produces an indirect jump to
0A7A (0A1C + 5E) PO 1 OPER. See under PO 1 OPER for the
rather tricky way in which it collects the parameter and sends
execution to 0A87 PO CONT, which finally executes the
PAPER command.

587
OBD9 PO ATTR jump for PAPER 9
18C1 OUT FLASH eliminate PAPER 9

paper colour see colours

PAPER control code (11h) see INK control code

parameters of array see arrays

parameters of AT and TAB see control characters

parameters of BASIC command see BASIC INTERPRETER

parameters of DEF FN see DEF FN key

parameters of embedded control character see colours

parameters of INK to OVER see colours

parameters of name see strings

parameters of print position see DISPLAY AREA

parameters of save command, parameters of save/load


header
see 0605 SAVE ETC

parameters of string see strings

parenthesised expression see expressions

parity byte of save/load see 04C2 SA BYTES

PASS BY subroutine 1E39


During BASIC execution, skips over the present BASIC
statement, which is a DATA or DEF FN statement.
Input parameters: A holds E4 DATA or CE DEF FN

588
- HL holds the pointer to the BASIC program from 5C5D
CH ADD; if A holds DATA this will be in the DATA statement, if
DEF FN in the DEF FN statement.
Action: move the pointer back to the DATA or DEF FN
command; in the ROM calls this is unnecessary, since HL is
already on the address of the command byte in all cases
- put 2 in D.
Exit: to 198B EACH STMT; this decrements D, then moves
on through the program till it finds a colon or newline, when
it decrements D to zero and RETs with the BASIC pointer in
HL.
Output parameters: HL is the address of the DATA or DEF
FN, D is 2.
Exit from:
1E37 DATA 2 (1E27 DATA)
1F60 DEF FN

"pass" in FOR ... NEXT loops see FOR ... NEXT loops

passing by see 1E39 PASS BY

PAUSE key (F2) see also commands, functions and


operators,
KEYBOARD SCANNING
The M key in K mode produces the command PAUSE. The
"PAUSE statement" must include a numeric parameter X
indicating the length of the pause in 1/50th second units; not
necessarily an integer, but it will be read as an integer and it
must be less than 10000h/65536d; zero gives an indefinite
pause.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AC5 P PAUSE causes a jump via 1C82 CLASS 06, to collect the
parameter, 1C10 CLASS 00 and 1C16 JUMP C R to the executive
routine 1F3A PAUSE.

589
PAUSE subroutine 1F3A
Called only from 1AC5 P PAUSE in the statement loop to
execute the PAUSE command.
Input parameters: none
- the PAUSE parameter is on the calculator stack.
Action: call 1E99 FIND INT2 to get the parameter.
_1F3D_PAUSE_1: HALT; 1/50th second, or 1/60th in USA
- decrement the parameter
- if it is zero, jump on to PAUSE END
- if it isn't FFFFh, jump on to PAUSE 2
- (FFFFh, so it was zero) increment it back to zero.
_1F49_PAUSE_2: if bit 5 of FLAGS is zero jump back to
PAUSE 1; the "new key pressed" flag, no new key was pressed.
_1F4F_PAUSE_END (either the counter has gone to zero or
a key has been pressed): zero bit 5 of FLAGS; "waiting for key"
- return.
Exit: RET from PAUSE END.
Output parameters: none
- the parameter has come off the calculator stack.

PAUSE END 1F4F (1F3A PAUSE)


Exit from:
1F3A PAUSE through
1F3D PAUSE 1

PAUSE 1 1F3D (1F3A PAUSE)


Jumps from:
1F49 PAUSE 2

PAUSE 2 1F49 (1F3A PAUSE)


Jumps from:
1F3D PAUSE 1

P BEEP 1AE3
P BORDER 1AF5
P BRIGHT 1AEE
P CAT 1B14

590
P CIRCLE 1AE7
P CLEAR 1ABB
P CLOSE 1B02
P CLS 1ABE
P CONT 1AB8
P COPY 1AD6
P DATA 1ACC
P DEF FN 1AF9
P DIM 1AA2
P DRAW 1AD2
see 1A7A syntax offset table

PEEK key (BE) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The O key in E mode without shift produces the function
PEEK. It requires one numeric operand X; it needn't be an
integer though it is read as an integer, can be zero, and must
be < 65536d, and the value of the function is the number
stored at the ROM/RAM address INT X. PEEK is a function,
with one operand; POKE is a command, with two.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code BE first to 0F, then to EB,
and adds the priority 10h/16d. Code and priority 10EB are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following PEEK is evaluated. When the code is
taken off the stack (2734 S LOOP), it is converted (2373 S
TIGHTER) from EB to 2B, the calculator offset for 34AC peek.

peek subroutine 34AC


Called only from 0028 FP CALC with literal 2B to execute
the PEEK X function. Could be called from m/c to change the
value on the calculator stack; for ordinary purposes it is a lot
simpler to write LD A,(BC) or similar.
Input parameters: none
- X is the last value on the calculator stack.
Action: call 1F99 FIND INT 2 to get X off the calculator
stack

591
- read the byte at that address.
Exit: into 2D2B STACK A, which puts the result on the
calculator stack.
Output parameters: the byte is in A.

P ERASE 1B10 see 1A7A syntax offset table

permanent attribute values, colours see colours

PERMS subroutine 1C96 see CLASS 07

PF ALL 9 2EB8 (2DE3 PRINT FP)


Jumps from:
2EB3 PF TEST 2

PF BITS 2E7B (2DE3 PRINT FP)


Jumps from:
2E7B PF SAVE
2E8A PF BYTES

PF BYTES 2E8A (2DE3 PRINT FP)


Jumps from:
auto

PF COUNT 2F2D (2DE3 PRINT FP)


Jumps from:
2F18 PF RND LP

PF DC OUT 2F5E (2DE3 PRINT FP)


Exit from:
2F4A PF E SBRN (2DE3 PRINT FP)

PF DEC 0S 2F64 (2DE3 PRINT FP)


Jumps from:
auto

PF DIGITS 2EA1 (2DE3 PRINT FP)

592
Jumps from:
2EB8 PF ALL 9

PF E FRMT 2F6C (2DE3 PRINT FP)


Jumps from:
2F2D PF COUNT

PF E POS 2F83 (2DE3 PRINT FP)


Jumps from:
2F6C PF E PRMT

PF E SBRN 2F4A (2DE3 PRINT FP)


An exit routine of 2DE3 PRINT FP, also called
recursively from 2F6C PF E FRMT within the routine.
Called from:
2F6C PF E FRMT
Exit from:
2DE3 PRINT FP through
2F46 PF NOT E

PF E SIGN 2F85 (2DE3 PRINT FP)


Jumps from:
2F6C PF E FRMT

PF FRACTN 2ECF (2DE3 PRINT FP)


Jumps from:
2E24 PF SMALL

PF FR DGT 2EEC (2DE3 PRINT FP)


Jumps from:
2EDF PF FRN LP (misprinted)

PF FR EXX 2EEF (2DE3 PRINT FP)


Jumps from:
auto

PF FRN LP 2EDF (2DE3 PRINT FP)

593
Jumps from:
2EEF PF FR EXX

PF INSERT 2EA9 (2DE3 PRINT FP)


Jumps from:
2EA1 PF DIGITS

P FLAG system variable 5C91 see also colours


Bytes: 1
All the bits are used as separate flags. The even bits,
which are those described below, are "temporary attribute"
flags, the odd ones "permanent"; the odd bits aren't written
individually, but whenever 0D4D TEMPS is called they are
copied en bloc from the even ones. INVERSE and OVER, and
PAPER or INK 9, aren't recorded as such in the attribute
bytes; they are set by the flags of P FLAGS.
P FLAG is the byte following ATTR T and MASK T, so it is
changed by the third call to 226C CO CHANGE in 2258 CO
TEMP B.
All bits saved and restored:
0A3D PO RIGHT
0C88 PO SCR 2
0D02 PO SCR 4/0D2D PO SCR 4B
18C1 OUT FLASH
All written by:
0A3D PO RIGHT temporarily bit 0 set, others reset
0D5B TEMPS 1 odd bits temporarily copied to even bits
18C1 OUT FLASH temporarily all reset
1C96 PERMS even bits copied to odd bits
226C CO CHANGE sets INK and PAPER 9 flags
Rems:
0BDB PO ATTR used in setting PAPER/INK in attributes
0D65 TEMPS 2 odd bits copied to even bits
2211 CO TEMP 5 altered by CO CHANGE as required
2228 CO TEMP 6 altered by CO CHANGE (bits 2 and 0)
2246 CO TEMP 9 bits 6 and 4 set for PAPER/INK 9
22DC PLOT used in PLOTting pixel

594
P FLAG bit 6: PAPER 9 flag
On "PAPER colour is 9"
Written by:
2258 CO TEMP B according to parameter of command
Turned off:
1CBE CLASS 09
Read by:
0BDB PO ATTR

P FLAG bit 4: INK 9 flag


On "INK colour is 9"
Written by:
2258 CO TEMP B according to parameter of command
Read by:
0BFA PO ATTR 1

P FLAG bit 2: INVERSE flag


On: "INVERSE 1"; off "INVERSE 0"
Written by:
2228 CO TEMP 6 according to parameter of command
Read by:
0BA4 PR ALL 2
22F0 PLOT LOOP

P FLAG bit 0: OVER flag


On "OVER 1"; off "OVER 0"
Written by:
2228 CO TEMP 6 according to parameter of command
Read by:
0B93 PR ALL 1
22F0 PLOT LOOP

PF LARGE 2E56 (2DE3 PRINT FP)


Jumps from:
2E01 PF LOOP

595
P FLASH 1AED see 1A7A syntax offset table

PF LOOP 2E01 (2DE3 PRINT FP)


Jumps from:
2E56 PF LARGE

PF MEDIUM 2E6F (2DE3 PRINT FP)


Jumps from:
2E56 PF LARGE

PF MORE 2ECB (2DE3 PRINT FP)


Jumps from:
2EB8 PF ALL 9

PF NEGTVE 2DF2 (2DE3 PRINT FP)


Jumps from:
2DE3 PRINT FP

PF NOT E 2F46 (2DE3 PRINT FP)


Label omitted by a misprint. It should appear opposite
AND A, two lines above 2F4A PF E SBRN.
Jumps from:
2F2D PF COUNT

P FOR 1A90
P FORMAT 1B06
see 1A7A syntax offset table

PF OUT DT 2F59 (2DE3 PRINT FP)


Jumps from:
2F52 PF OUT LP

PF OUT LP 2F52 (2DE3 PRINT FP)


Jumps from:
2F4A PF E SBRN
2F59 PF OUT DT
2F64 PF DEC 0S

596
PF POSTVE 2DF8 (2DE3 PRINT FP)
Jumps from:
2DE3 PRINT FP

PF R BACK 2F25 (2DE3 PRINT FP)


Jumps from:
2F18 PF RND LP

PF RND LP 2F18 (2DE3 PRINT FP)


Jumps from:
2F25 PF R BACK

PF ROUND 2F0C (2DE3 PRINT FP)


Jumps from:
2EB8 PF ALL 9
2EDF PF FRN LP

PF SAVE 2E1E (2DE3 PRINT FP)


Jumps from:
2E01 PF LOOP

PF SMALL 2E24 (2DE3 PRINT FP)


Jumps from:
2E01 PF LOOP

PF TEST 2 2EB3 (2DE3 PRINT FP)


Jumps from:
2EA1 PF DIGITS

P GO SUB 1A86
P GO TO 1A7D
see 1A7A syntax offset table

PI key (A7) see also commands, functions and operators,


KEYBOARD SCANNING

597
The M key in E mode without shift produces the function
PI; a function without an argument, which always produces
the same numerical value 3.1415927d.
On execution, 24FB SCANNING indexes into the scanning
function table at 2596 to find the executive routine 2627 S PI.
0C35 PO TRSP does not require trailing space

P IF 1A81
P INK 1AEB
P INPUT 1A9F
see 1A7A syntax offset table

P INT STO 2D8C (2D8E INT STORE)


The P INT STO entry point was apparently provided for
storing a small integer_known_to_be_positive in FP format, but
it is never called from ROM.

P INVERSE 1AEF see 1A7A syntax offset table

PIP system variable 5C39


Bytes: 1
The number of cycles to be output in the_keyboard_click.
The pitch is fixed by 0F38 ED LOOP at a little more than three
octaves above middle C; the value of PIP controls the length of
the click.
Set to zero on startup by 11EF RAM DONE, at end. The
effect is that only one cycle in 03B5 BEEPER is completed,
giving a very short click of rather indeterminate pitch. The
high-pitched click can be heard by poking any number 1 ->
255d into PIP, 23609d.
Immediately follows 5C38 RASP; they are saved together
in 11B7 NEW and written together in 11EF RAM DONE, so any
change made in PIP won't be affected by NEW.
Written by:
11EF RAM DONE (twice)
Read by:
0F38 ED LOOP

598
11B7 NEW

pitch see timing

pitch of keyboard click see 5C39 PIP

pixel, pixel address, pixel byte, pixel coordinates, pixel


line see DISPLAY AREA

PIXEL ADD subroutine 22AA


Given a pair of pixel coordinates from BASIC, finds the
actual pixel in the display area. It is used by the POINT and
PLOT subroutines, and can readily be used in m/c
programming.
The BASIC y-coordinate runs from zero at the bottom of
the display area to AFh/175d at the top, the x-coordinate X
from zero on the left of the screen to FFh/255d on the right.
However the y-coordinate mainly used in the subroutine, here
called Y, runs from zero at the_top of the screen to AFh/175d
at the bottom of the upper display.
The position of the pixel in the display area is given
by three bytes, the hi and lo bytes of the display address and
the bit number at this address, each of which is calculated
separately.
The subroutine uses very compressed "one-byte binary
arithmetic", which is perhaps best explained with the help of
an example: suppose the y-coordinate is 01111011b/7Bh/123d
and the x-coordinate is 01001011b/4Bh/75d. The description
will be much easier to follow if the analysis of the display
addresses under DISPLAY AREA is continuously referred to.
First subtract the y-coordinate from AFh/175d; now in
our example, Y = 00110100b/34h/52d. X = the x-coordinate
01001011b/4Bh/75d without adjustment.
1. Hi byte: all the display addresses in any pixel line on
the screen have the same y-coordinate and the same hi byte,
which therefore depends only on Y.
- in the top third of the screen, where Y runs line by

599
line from 00 at the top to 3Fh/63d, the hi byte of the pixel
lines runs line by line from 40h to 47h for the first eight
lines, and then repeats this sequence seven more times. So
the hi byte is the remainder on dividing Y by eight, plus 40h.
- in the middle third, where Y = 40h/64d -> 7Fh/127d, the
sequence is 48h -> 4Fh, and the hi byte is the remainder on
dividing Y by eight, plus 48h
- and in the lower third, where Y = 80h/128d -> AFh/175d,
the sequence is 50h -> 55h, and the hi byte is the remainder
on dividing Y by eight, plus 50h.
Rotate three bits 010 into the hi end of Y. This divides
Y by eight and adds 40h; in the example, 01000110b/46h/72d.
Ignoring the three low bits, the result is
01000000b/40h for the top third
01001000b/48h for the second
01010000b/50h for the bottom.
XOR/AND/XOR this with Y, using 11111000b/F8h as a mask:
this replaces the last three bits with the remainder on dividing
Y by eight; see masks.
This is the correct hi byte for the pixel address.
2. Lo byte: it depends on both X and Y, but
- it is the same for all the eight character bytes of any
given character
- all the addresses in any column have the same X
coordinate, and are various multiples of 20h plus the same
number: call it R1. R1 runs from 00 to 1Fh, and is equal to X/8
ignoring any remainder
- the multiple of 20h is 0 * 20h for the top screen
character in the column, 1 * 20h for the next, then 2 *
20h, ...up to 7 * 20h = E0h for the eighth from the top; this
ends the top third, and the sequence is repeated for the
middle and bottom thirds. Call this R2 * 20h; R2 is zero -> 7
- so to get the lo byte, divide X by eight and discard
the remainder, making R1. Now divide Y by eight and discard
the remainder: this is the character line. Divide this by eight
and multiply the remainder R2 by 20h; then the lo byte is R1 +
20h * R2.

600
In our example, X is 01001011b/4Bh/75d: the first five
bits represent R1.
Rotate it left three times, making 01 011 010b; R1 is
split between the last three bits and the first two. The middle
three bits are immaterial at this stage.
XOR/AND/XOR this result with Y using 11000111b/C7h as
the mask: the example gives 01 110 010b. R1 is unaffected, but
the middle three bits are replaced by the corresponding three
bits of Y, which represent R2.
Now rotate this left two more times: the example result
is 11001001b/C9h/201d. This puts R1 in the five lo bits, and R2
in the top three, ie R2 times 20h plus R1. This is the correct lo
byte of the address.
3. The bit number at this address is R3, the remainder on
dividing X by 8. In our example X = 01001011b/4Bh/75d. AND
this with 00000111b/07, giving 00000011b/3, the bit number
of the pixel.
Input parameters: the pixel coordinates in BC: B is AFh/
175d - Y, C is X.
Action: subtract the y-coordinate from AF; giving Y
- if this makes carry, report "Integer out of range";
the y-coordinate was 176d or more
- rotate three bits 010 into the hi end of Y
- XOR/AND/XOR the result with Y using 11111000b/F8h as
mask; this is the hi byte of the address
- rotate X left three times
- XOR/AND/XOR the result with Y using 11000111b/C7h as
mask
- rotate this result left two more times; this is the lo
byte of the address
- AND X with 00000111b/07; this is the bit number of the
pixel within the display address.
Exit: RET.
Output parameters: HL holds the display address
- A holds the bit number of the pixel
- DE is unchanged.
Called from:

601
22CB POINT SUB
22E5 PLOT SUB

P LET 1A7A
P LIST 1AAE
P LLIST 1ADC
P LOAD 1AE0
see 1A7A syntax offset table

PLOT key (F6) see also commands, functions and operators,


KEYBOARD SCANNING
The Q key in K mode produces the command PLOT; it
requires two numeric parameters separated by a comma.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AC1 P PLOT causes a jump via 1CBE CLASS 09, which
executes any colour commands and gets the parameters, 1C10
CLASS 00 and 1C16
JUMP C R to the executive routine 22DC PLOT.

PLOT subroutine 22DC


Given a pair of pixel coordinates X and Y, sets the
pixel to one, making a dot in INK colour; see DISPLAY AREA.
The executive routine of the PLOT command; also used as an
exit routine from 2320 CIRCLE to draw null circles
consisting of a single point.
Input parameters: none
- X and Y (last) are last values on the calculator
stack.
Action: call 2307 STACK BC to get the coordinates in BC,
and 22E5 PLOT SUB to make the PLOT.
Exit: into 0D4D TEMPS, which restores the permanent
colours.
Output parameters: none.
Exit from:
2320 CIRCLE

602
233B C R GRE 1
Rems:
22AA PIXEL ADD finds pixel address (22E5 PLOT SUB)
24DF D L STEP relied on to check y-coordinate

PLOT END 2303 (22E5 PLOT SUB)


Jumps from:
22FD PL TST IN

PLOT LOOP 22F0 (22E5 PLOT SUB)


Jumps from:
auto

PLOT SUB subroutine 22E5


Given the PLOT coordinates X, Y of a pixel, sets that
pixel to one, the INK colour on the screen. The main
execution routine for the PLOT command; also called by
24B7 DRAW LINE from
24EC D L PLOT.
The only small complications arise from the need to
consider INVERSE and OVER. It may be of interest to note that
PLOT INVERSE 1; OVER 1; achieves nothing at all, unless there
are other colour controls.
Input parameters: Y in B, X in C.
Action: save the coordinates in 5C7D COORDS for the next
DRAW command
- call 22AA PIXEL ADD to find the display address and
bit number of the pixel
- make a loop counter of the bit number incremented by
one; now one -> 8
- make a mask 11111110b/FEh; with a "hole" at bit zero.
_22F0_PLOT_LOOP: rotate the mask right till the counter
reaches zero; now the "hole" is in the place matching the bit
number of the pixel
- get the byte from the display address
- if bit zero of P FLAG is set jump on to PL TST IN;
OVER 1

603
- (OVER 0) AND the display byte with the mask; this sets
the pixel to PAPER colour.
_22FD_PL_TEST_IN (the PLOT pixel is in PAPER colour,
unless the OVER 1 flag was set): if bit 2 of P FLAG is set jump
on to PLOT END; INVERSE 1
- (INVERSE 0) XOR the display byte with the mask;
complementing every bit_except the selected one, which stays
in PAPER colour
- complement all the display bits; the selected bit goes
to INK colour, everything else goes back to what it was at
first.
_2303_PLOT_END: put the changed display byte back in
the display address.
Exit: into 0BDB PO ATTR, which sets the attributes if
there were any colour controls.
Output parameters: none.
Called from:
22DC PLOT
24EC D L PLOT

P LPRINT 1AD9 see 1A7A syntax offset table

PL TST IN 22FD (22E5 PLOT SUB)


Jumps from:
22F0 PLOT LOOP

P MERGE 1AE2
P MOVE 1B0A
P NEW 1AA8
P NEXT 1A98
see 1A7A syntax offset table

PO ABLE 0AD9 (09F4 PRINT OUT)


Jumps from:
09F4 PRINT OUT
0A69 PO QUEST

604
PO ANY subroutine 0B24
Outputs a single character code through the current
channel; not used for control codes. Although this is in form a
free-standing subroutine, it is only called by ROM from PO
ABLE in 09F4 PRINT OUT, so its description is given under
PRINT OUT.
Called from:
0AD9 PO ABLE

PO AT ERR 0AAC (0A75 PO 2 OPER)


Jumps from:
0A87 PO CONT

PO AT SET 0ABF (0A75 PO 2 OPER)


Jumps from:
0A87 PO CONT

PO ATTR subroutine 0BDB


Colours a character area on the screen by poking the
appropriate value into the appropriate byte in the attributes
area. The colour values used are always those of the
temporary attributes.
The subroutine is given an address which is one of the
1800h/6072d addresses in the display area; see under
DISPLAY AREA. This is rather oddly called the "destination
address" in the notes. Its first task is to calculate from this the
corresponding address in the 300h/768d bytes of the
attributes area; see colours.
The lo byte needs no change: the lo bytes of display
area addresses, which are the same for each byte of the
character, run from zero to FFh in each third of the screen,
and are the same as the lo bytes of the corresponding
attribute addresses.
The hi byte in the attributes area must be either 58h,
59h or 5Ah, depending on whether the display address is in
the
top third of the screen: display addresses 4000 -> 47FFh

605
middle third of the screen: display addresses 4800 -> 4FFFh
bottom third of the screen: display addresses 5000 -> 57FFh
Convert the hi byte of the display address to 00 if it
was 40 -> 47h, 01 if it was 48 -> 4F or 02 if it was 50 -> 57h,
and add this result to 58h to get the hi byte of the attributes
area address.
Take the attribute byte from the address just found and
XOR/AND/XOR it with the attribute specification in 5C8F ATTR
T,
using 5C90 MASK T for the mask; see masks. This replaces the
bits of the attribute byte with bits from 5C8F ATTR T wherever
the mask bit is zero.
Finally adjust the new attributes for PAPER and INK 9 by
reference to P FLAG, and poke in the new attribute.
Input parameters: HL holds the address of any pixel byte
of the display area.
Action: rotate the hi byte of the address three times to
the right:
01000000b/40h -> 01000111h/47h all become 08
01001000b/48h -> 01001111b/4Fh all become 09
01010000b/50h -> 01010111b/57h all become 0A
- AND this with 00000011h/03; giving 00, 01 or 02 for
the top, middle and bottom thirds
- OR the result with 58h; this is now the hi byte of the
attribute address
- load 5C8F ATTR T and 5C90 MASK T into DE; the notes
put them in the wrong order
- XOR/AND/XOR the old attribute byte with 5C8F ATTR T
using 5C90 MASK T for the mask
- if bit 6 of P FLAG is zero jump on to PO ATTR 1; the
PAPER 9 flag
- (PAPER 9) AND the attribute byte with 11000111b/C7h;
this makes the PAPER number in the result 000b BLACK
- if bit 2 of the attribute byte isn't zero jump on to
PO ATTR 1; the INK colour is 100b/04 or more, GREEN, CYAN,
YELLOW or WHITE, and BLACK paper is correct

606
- (INK colour of 3 or less, BLACK, BLUE, RED or
MAGENTA)
XOR with 00111000b/38h; it reverses the PAPER to 111b/07
WHITE.
_0BFA_PO_ATTR_1: if bit 4 of P FLAG is zero jump on to PO
ATTR 2; the INK 9 flag
- (INK 9) AND the attribute byte with 11111000b/F8h;
this makes the INK number 000b BLACK
- if bit 5 of the attribute byte isn't zero jump on to
PO ATTR 2; the PAPER colour is 100b/04 or more, GREEN,
CYAN, YELLOW or WHITE, and BLACK ink is correct
- (PAPER colour of 3 or less, BLACK, BLUE, RED or
MAGENTA) XOR with 00000111b/07; it reverses the INK to
111b/07 WHITE.
_0C08_PO_ATTR_2: poke in the new value to the attribute
address.
Exit: RET, from PO ATTR 2.
Output parameters: HL holds the attribute address
- A holds the attribute byte.
Called from:
0BC1 PR ALL 5
Exit from:
2303 PLOT END (22DC PLOT)

PO ATTR 1 0BFA (0BDB PO ATTR)


Jumps from:
0BDB PO ATTR (twice)

PO ATTR 2 0C08 (0BDB PO ATTR)


Exit from:
0BDB PO ATTR through
0BFA PO ATTR 1 (twice)

PO BACK 1 0A23 (09F4 PRINT OUT)


Exit from:
09F4 PRINT OUT via 0A11 control character table, when
input code was 08 (cursor left).

607
PO BACK 2 0A38 (09FD PRINT OUT)
Jumps from:
0A23 PO BACK 1

PO BACK 3 0A3A (09F4 PRINT OUT)


Jumps from:
0A23 PO BACK 1 (twice)
PO CHANGE subroutine 0A80
Mainly an exit routine for 0A75 PO 2 OPER - see index
entry - but also called within that subroutine.
Called from:
0A87 PO CONT
Exit from
0A75 PO 2 OPER through one of:
0A6D PO TV 2
0A7D PO TV 1

PO CHAR 0B65 (09F4 PRINT OUT)


Part of the PO ANY routine, used as an entry point by
the call at PO RIGHT.
Called from:
0A3D PO RIGHT

PO CHAR 2 0BCA (09F4 PRINT OUT)


Exit from:
0B52 PO T&UDG

PO CHAR 3 0B76 (09F4 PRINT OUT)


Jumps from:
0B6A PO CHAR

PO COMMA 0A5F (09F4 PRINT OUT)


Exit from:
09F4 PRINT OUT via 0A11 control character table, when
input code was 06 (PRINT comma).

608
PO CONT subroutine 0A87
See OA7A PO 1 OPER, of which this subroutine is a
continuation, reached by the unorthodox method of making it
temporarily the output address for the current channel.
Made output routine and so called from 15F7 CALL SUB
by:
0A6D PO TV 2
0A7A PO 1 OPER

PO EACH 0C22 (0C0A PO MSG)


Jumps from:
0C14 PO TABLE
auto

PO ENTER 0A4F (09F4 PRINT OUT)


Exit from:
09F4 PRINT OUT via 0A11 control character table, when
input code was 0D (ENTER).

PO FETCH subroutine 0B03


See under 09F4 PRINT OUT.
Called from:
09F4 PRINT OUT
0A5F PO COMMA (unnecessary call)
0AC3 PO FILL
0B24 PO ANY
Exit from:
09F4 PRINT OUT
0B5F PO T

PO FILL OAC3 (0A7A PO 1 OPER)


Exit from:
0A5F PO COMMA
0A75 PO 2 OPER and 0A7A PO 1 OPER through
0AC2 PO TAB

PO F PR 0B1D (09F4 PRINT OUT)

609
Exit from:
0B03 PO FETCH

PO GR 1 subroutine 0B38
See under 09F4 PRINT OUT.
Called from:
0B24 PO ANY

PO GR 2 0B3E
See under 09F4 PRINT OUT.
Called from:
0B38 PO GR 1
Exit from:
0B38 PO GR 1

PO GR 3 0B4C (09F4 PRINT OUT)


Exit from:
0B38 PO GR 1/0B3E PO GR 2
Jumps from:
auto

POINT key (A9) see also commands, functions and


operators,
KEYBOARD SCANNING, 0284 extended mode table (f )
The 8 key in E mode with symbol shift produces the
function POINT; it requires two numeric operands in brackets
(X, Y), and the value of the function is one if the pixel at PLOT
coordinates X, Y is INK colour, zero if it is PAPER colour; ie
what actually shows on screen, regardless of whether it is
produced by INVERSE commands, etc.
On execution, 24FB SCANNING indexes into the scanning
function table at 25AC to find the executive routine 267B S
POINT.

POINTERS subroutine 1664


Corrects as necessary, whenever space is made or
reclaimed in memory, the fourteen_system_pointers, all 2-byte

610
RAM addresses:
5C4B VARS the start of the variables area
5C4D DEST the start of the "variable in assignment"
5C4F CHANS the start of the channel data
5C51 CURCHL the start of the channel in use
5C53 PROG the start of the BASIC program
5C55 NXTLIN the start of the next BASIC line to be executed
5C35 DATADD the end of the last DATA item used
5C59 E LINE the start of the editing area
5C5B K CUR the cursor address in the editing area
5C5D CH ADD the BASIC pointer
5C5F X PTR the next address after an error in BASIC
5C61 WORKSP the start of the temporary work space
5C63 STKBOT the bottom of the calculator stack
5C65 STKEND the end of the stack and the start of spare
space.
They are consecutive svs, and listed here in ascending
order; but the addresses aren't in sequence, they would run
CHANS, CURCHL, PROG, ... and the sequence of some of them
will vary from time to time.
As can be seen from the memory map on page 165 of the
Spectrum handbook, page 142 of the Plus 2 handbook, if bytes
are inserted in or deleted from the Microdrive maps, all
fourteen of the pointers have to be adjusted; if they are
inserted in or deleted from anywhere higher up, 5C4F CHANS
won't be changed but some of the ones higher up will be; see
also under memory map in this index.
The last part of the subroutine calculates the block
size for the LDIR or LDDR instruction which will follow the
subroutine call: the block to be moved is from the change
address up to the_old value of 5C65 STKEND, plus one
because the byte at the address in 5C65 STKEND itself is also
to be moved.
Input parameters: BC holds the "increment number"; the
number of bytes by which the pointers are to be incremented,
negative if they are to be decremented

611
- HL holds the "change address"; the one_below where the
change is to be made.
Action: make a "pointer pointer" to the first sv 5C4B
VARS
- make a counter 0Eh/14d to count the pointers.
_166B_PTR_NEXT: get the pointer at the pointer pointer
and compare it with the change address
- if it is below or on the change address jump on to PTR
DONE; this pointer needn't be changed
- (it is above the change) increment the pointer by the
increment number
- put it back in the sv at the pointer pointer
_167F_PTR_DONE: move on the pointer pointer
- decrement the counter
- if it isn't yet down to zero loop back to PTR NEXT for
the next sv
- (all the pointers have been checked) subtract the
change address from the last pointer checked, which was the
one in 5C65 STKEND; this is the size of the block to be moved
- add one to include 5C65 STKEND itself.
Exit: RET, from 167F PTR DONE.
Output parameters: HL is restored to its input value, the
change address
- DE holds the_old value from 5C65 STKEND
- BC holds the number of bytes to be moved by the LDIR
or LDDR command
- A is restored to its input value.
Called from:
1655 MAKE ROOM
19E8 RECLAIM 2

pointer (save/load block) see program/data block

pointer to system variables see 1664 POINTERS

POINT LP 22D4 (22CB POINT SUB)


Jumps from:

612
auto

POINT SUB subroutine 22CB


See 267B S POINT.
Called from:
267B S POINT

POINT SUBROUTINE see 22CB POINT SUB

POKE key (F4) see also commands, functions and


operators,
KEYBOARD SCANNING
The letter O key in K mode produces the command
POKE, which requires two numeric parameters. POKE X,Y
puts the value Y in the RAM address X. POKE is a command,
with two operands; PEEK is a function, with one.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AB1 P POKE causes a jump via 1C7A CLASS 08 - which collects
the two parameters - 1C10 CLASS OO and 1C16 JUMP C R to the
executive routine 1E80 POKE.

POKE subroutine 1E80


Called only from 1AB1 P POKE in the syntax parameter
table; executes the BASIC POKE X,Y command. Could be
called from m/c, but unlikely to be useful, as LD (BC),A etc
achieve the same object.
Input parameters: none
- X and Y (last) are the last two values on the
calculator stack.
Action: call 1E85 TWO PARAM to put X in BC and Y in A;
the execution is then just LD (BC),A.
Exit: RET.
Output parameters: BC and A as shown; the two values
have been removed from the calculator stack.

613
PO MSG subroutine 0C0A see also messages, tables
Prints the A'th message in a message table; for example
the report messages at 1391. The actual printing out is done by
the calls to 0010 PRINT A 1 in PO SAVE.
The messages may each be of any length, but there can
be at most 256d of them in any one table; the end byte of each
message is an_inverted_character, ie it has its hi bit set. The
codes of the message are therefore restricted to the values
zero -> 7Fh/127d; so messages cannot contain tokens, but
besides letters, numbers and symbols, they can consist of or
contain sequences such as 22,0,0,16,6 (decimals), equivalent
to BASIC
AT 0,0; INK 6
A useful subroutine for m/c programmers, especially for
"computed messages" or messages which are required more
than once in a m/c program.
A little care is needed in handling_leading_spaces,
since the subroutine is geared to print a single space before
some of the tokens. A leading space will be printed if bit zero
of FLAGS is zero and if the call to 0C41 PO SEARCH from PO
TABLE returns with no carry; ie if
- the message number is 20h/32d or more, and
- the first code of the message is 41h/65d or more, ie a
letter or certain symbols.
If your message table has more than 32d entries it will
be necessary to set bit zero of FLAGS to avoid a leading space
for the later ones. The tables in ROM, except for the tokens
table, are limited to 32d entries; there is no saving in bytes by
using longer or shorter tables.
In the token table at 0095, the only tokens printed with a
leading space are OR, AND, LINE, THEN, TO, STEP and the
command tokens; not <=, >= and <>, whose first code is less
than 41h/65d. At the start of an input line and after a space,
the cursor or a TAB, the flag is set to suppress the leading
space, but after a colon it will appear.
[In the notes on 1865 OUT LINE1, 1925 OUT SP 2 and 1A1B
OUT NUM 1 "leading spaces" refers a little confusingly to

614
something slightly different: that when a line has a number
less than 1000d, the line number is printed "right justified",
with one space before the numbers 100 -> 999d, two before 10
-> 99d and three before 1 -> 9.]
_Trailing_spaces, a space printed after the message, are
never printed by calls to PO MSG; PO TOKENS will print them
in some cases, but it only prints messages from the tokens
table at 0095. A call to 0C13, 3 bytes after PO TOKENS, with
your own message address in DE, will produce a trailing space
if
- the last code of the message is "$", or a letter, or
one of the symbols following "Z" and "z" in the ASCII coding
- and the message number is 3 or more.
This is done on exit to PO SAVE.
In handling the tokens, this means the subroutine prints a
trailing space after all tokens excluding the first three RND,
INKEY$ and PI, and excluding <=, >=, <>, OPEN (hatch) and
CLOSE (hatch).
Input parameters: DE holds the base address of the
message table; this address should hold a byte with bit 7 set,
otherwise PO SEARCH will step on till it finds such a byte and
count that as the base of the table
- A holds the message number; the first message in the
table being number zero.
Action: put a zero signal on the machine stack to
suppress the trailing space
- jump on to PO TABLE.
_0C10_PO_TOKENS (the entry point for printing out tokens
RND -> COPY): get the base address of the token table
- put the message number on the stack as trailing space
signal.
_0C14_PO_TABLE: call 0C41 PO SEARCH, which finds the
start of the indexed message; it returns with carry to signal
"no leading space"
- if carry is set jump on to PO EACH
- (leading space enabled) make a space byte 20h
- if bit zero of FLAGS is zero call 0C3B PO SAVE to

615
print the space; the leading space flag.
_0C22_PO_EACH: read a code from the message
- zero its hi bit
- call 0C3B PO SAVE to print it out
- read it again and double it; this sets the carry flag
if the code is 80h or more, ie if its hi bit is set to signal
"last character"
- if it was < 80h/129d loop back to PO EACH to read the
next code
- (the last code has been printed) get the trailing
space signal from the machine stack
- if the last code was 24h/36d "$", which after doubling
will be 48h/72d, jump on to PO TRSP; tokens INKEY$,
SCREEN$, VAL$, STR$, CHR$. All these except INKEY$ always
have a trailing space
- if the last code after doubling was less than 82h/130d
return; tokens <=, >=, <>, OPEN and CLOSE with hatch don't
have a trailing space.
_0C35_PO_TRSP: if the trailing space signal is two or
less, return; tokens RND, PI, INKEY$ and non-token messages
never have a trailing space
- (trailing space required) make a space byte and exit.
Exit: RET, from PO EACH or PO TRSP, if no trailing space
is printed, otherwise into 0C3B PO SAVE to print the space.
Output parameters: 20h in A if space is to be printed.
Called from:
078A LD TYPE
0970 SA CONTRL
0C88 PO SCR 2
1219 RAM SET
133C MAIN 5 (twice)
Rems:
0AC3 PO FILL no leading space after TAB command
0B6A PO CHAR 2 no leading space if space just printed
0C44 PO STEP checks inverted characters in message
table
1865 OUT LINE1 prints line number with leading spaces

616
187D OUT LINE2 zero flag: leading space allowed
192B OUT SP 1 check for leading spaces
1A1B OUT NUM 1 no leading spaces
1A2B OUT NUM 2 print with leading spaces

P OPEN 1AFC see 1A7A syntax offset table

PO QUEST 0A69 (09F4 PRINT OUT)


Jumps from:
09F4 PRINT OUT (twice)

PO RIGHT subroutine 0A3D


See 09F4 PRINT OUT.
Called from:
09F4 PRINT OUT through 0A11 control character table
with code 09.

ports
The function of the input from and output to the ports
is set by the hardware, and cannot be changed even by m/c
programs; so although any port may be input or output from
BASIC or machine code, the use of such instructions is
limited. See page 159-60 of the old Spectrum handbook, and
page 138-139 of the Plus 2 handbook. For some reason the
index of the latter book gives page 197 as the reference for
"ports": this isn't helpful. See also the ZX printer handbook
page 4.
Port addresses are 2-byte integers, but usually only
bits zero to 4 of the lo byte are significant, and only one of
these should be set to zero for a meaningful result; which
means there are only five ports for most purposes. The
keyboard uses port FE, but also uses the hi byte to multiply
the number of ports used.
More detail is given below, but the uses of the five
main ports may be summarized as follows.
_Zero_in_bit_zero: port 11111110b/FEh/254d
- input from keyboard

617
- input from EAR socket
- output to loudspeaker
- output to border
- input/output of Microdrives etc, see page 139 of the
Plus 2 handbook
_Zero_in_bit_one: port 11111101b/FDh/253d
- used by the 3-channel sound chip, RS232, keypad and
MIDI of the Plus, see pages 139 and 166 of the Plus 2 handbook
- 7FFD is used for the extra memory of the Plus, see
pages 139 and 147 of the Plus 2 handbook
_Zero_in_bit_2: port 11111011b/FBh/251d
- input from ZX printer
- output to ZX printer
_Zero_in_bit_3: port 11110111b/F7h/247d
- used by Microdrives etc, see page 139 of the Plus 2
handbook
_Zero_in_bit_4: port 11101111b/EFh/239d
- used by Microdrives etc, see page 139 of the Plus 2
handbook

Keyboard ports
In the case of the keyboard, the hi byte of the port
address is also significant; see the tables in NB 1 under
KEYBOARD SCANNING. The byte read in from port FE
indicates which key, if any, in the half-row is being pressed.
The BREAK key, SPACE with CAPS SHIFT, is considered
pressed when there is input with zero in bit zero from FEFE
(CAPS SHIFT) and also zero in bit zero from 7FFE (SPACE). See
1F54 BREAK KEY.
Inputs:
0296 KEY LINE read FEFE, FDFE, FBFE, F7FE, EFFE,
DFFE, BFFE, 7FFE in succession
053F SA/LD RET and 05ED LD SAMPLE check BREAK/
SPACE key
- bit 0 of 7FFE only
1F54 BREAK KEY check BREAK key, bit 0 of 7FFE and
FEFE

618
Rems:
028E KEY SCAN successive port addresses in BC

EAR/MIC port: EAR input, MIC, loudspeaker and border


colour output
All port FE: the same port outputs to the MIC socket,
the loudspeaker and the border, so a single OUT eg in the
SAVE routines controls all three.
Inputs:
- bit 6 is zero if there is an incoming pulse from the
EAR socket
_0556_LD_BYTES read EAR state in bit 6, move it to bit 5
and save with 010 RED in bits 2 -> zero
_05ED_LD_SAMPLE read EAR state in bit 6, move it to bit 5
and check for edge
Output:
- bit 4 set sends a pulse to the loudspeaker
- bit 3 zero turns on the MIC socket
- bits 2, 1 and zero set the border colour
03D6 BE H&L LP bit 4 on turns speaker on, zero turns it
off, border colour unchanged
04D8 SA LEADER, 04EA SA SYNC 1 and 04F2 SA SYNC 2
alternate 00000010b/00001101b turns MIC
on & border RED, MIC off & border CYAN
051C SA OUT alternate 00000001b/00001110b turns MIC
on
& border YELLOW, MIC off & border BLUE
053F SA/LD RET bits 2, 1 and 0 restore border colour
0556 LD BYTES 00001111b for MIC off and border WHITE
05ED LD/SAMPLE set bit 3 to turn off MIC and change
border colour
2294 BORDER bits 2, 1 and 0 set new border colour
Rems:
03B5 BEEPER 00001000b turns off MIC
04D0 SA FLAG 00000010b signals MIC on and RED
04F2 SA SYNC 2 00001110 signals MIC off and YELLOW
0507 SA START 00000001b signals MIC on and BLUE

619
0511 SA BIT 2 00001110 signals MIC off and YELLOW
ZX printer ports
Port FB inputs and outputs to the ZX printer: see page
160 of the old Spectrum handbook and the ZX printer's own
handbook. The printer port FB isn't mentioned in the Plus 2
handbook, nor is the ZX printer, but port FB is certainly used
for printer output in the older Spectrum Plus, see page 65
under OUT in the Spectrum Plus handbook, and the ZX
printer can be used with it in 48K mode, see the Introductory
handbook to the Spectrum Plus.
The_stylus is the "pen" which marks the paper; actually
there are two of them in the ZX printer. The_encoder is a
synchronizing device which takes 256d bits, one pixel line,
from the printer buffer and feeds them to the stylus at a
controlled rate.
Input:
- bit 7: set when the stylus is in contact with the
paper, ie ready, and stays set till an output received
- bit 6: set if printer connected, otherwise zero
- bit 0: set when the encoder is ready, and stays set
till an output received
0F0C COPY L 2 check bits 6 (on if printer connected) and
7 (on if stylus ready)
0F1E COPY L 5 check bit 0 (on if encoder ready)
Output:
- bit 7: one produces a dot from the stylus
- bit 2: zero starts the motor
one stops the motor
- bit 1: one slows the motor, unless bit 2 is also one
0EDA COPY END stop the motor with 00000100b
0EF4 COPY LINE slow motor with 00000010b
0EFD COPY L 1 stop the motor with 00000100b
0F1E COPY L 5 send bit to printer, one if bit 7 set

PO SAVE subroutine 0C3B see also scrolling


Used to print a single character_recursively, ie from

620
within the 0010 PRINT A 1 sequence, eg when expanding
token codes. The PRINT A 1 sequence operates with the
alternate register set from 15F2 PRINT A 2, switching them
back at the end of 15F7 CALL SUB, so as to save the main
registers: if it was simply called from within itself they would
be switched back and the main registers would be corrupted.
PO SAVE corrects this.
Input parameters: A holds the character code
- the alternate registers are in use.
Action: stack DE'
- exchange the main registers
- call 0010 PRINT A 1
- restore the registers and DE'.
Exit: RET.
Output parameters: main registers and DE' are preserved.
Called from:
0AD0 PO SPACE
0C14 PO TABLE
0C22 PO EACH

PO SCR subroutine 0C55


Checks whether the next print to screen will require
scrolling; if so scrolls it.
In automatic listing, which scrolls the upper screen
without prompts till the line marked as current is on screen,
the whole screen is scrolled up one line; but if the current
line indicator in 5C67 BREG is zero, indicating the current line
is already on screen, a complete return from the 1795 AUTO
LIST routine is made.
In ordinary listing of the upper screen, again the whole
screen is scrolled up one line; but if the scroll counter in
5C8C SCR CT indicates that a full screen has been scrolled, the
"scroll?" message is printed and scrolling doesn't proceed till a
key is pressed. See also TV FLAG bit 4.
In scrolling the lower screen, if there is room between
upper and lower screens only the lower screen is scrolled,
otherwise both are scrolled: scrolling is usually only by one

621
line, but in the case of an INPUT ... AT input the lower screen
may need to be scrolled several times.
Input parameters: the alternate registers are in use;
this subroutine is part of the 0010 PRINT A 1 output sequence,
all executed in the alternate registers
- BC' holds the current print position, see DISPLAY
AREA; B' is the line number, from 18h/24d at the top of either
upper or lower screen downwards. C' isn't used.
Action: if bit 1 of FLAGS is set return immediately; the
ZX printer flag. If it is set output is being made to the
printer
- stack a return address 0DD9 CL SET; all RETs will be
to there
- if bit zero of TV FLAG is set jump on to PO SCR 4; the
"lower screen flag". If it is set printing is in the lower
screen [the notes say INPUT ... AT, but the jump will be made
for any editing or input if a line end is reached]
- (scrolling the upper screen; it can be assumed that
the lower screen is blank) check the new print position line
number against the number in 5C6B DF SZ; this is the current
number of lines in the lower screen, so also the line number
of the top blank line in the lower screen
- if the line number is_less than 5C6B DF SZ report "Out
of screen"; something has gone wrong, the top display is
overlapping the bottom
- if it is_more than 5C6B DF SZ return; there are still
spare lines in the upper screen
- (scrolling is needed) if bit 4 of TV FLAG is zero jump
on to PO SCR 2; the "auto listing" flag, ordinary listing
- (auto listing) decrement the current line indicator
from 5C67 BREG
- if it was 01 jump on to PO SCR 3; the current line
isn't yet on screen, scrolling is necessary
- (5C67 BREG holds zero, the current line is on screen
already) call 1601 CHAN OPEN with stream zero to open the
keyboard channel
- put the address from 5C3F LIST SP in the stack pointer

622
- zero bit 4 of TV FLAG; signalling ordinary listing
- return; the return address at 5C3F LIST SP to which
the stack pointer now points was set as the return address for
the whole routine in 1795 AUTO LIST, so the LIST command
routine is now terminated.
_0C88_PO_SCR_2 (ordinary listing): decrement the scroll
counter in 5C8C SCR CT
- if it doesn't reach zero jump on to PO SCR 3; more
lines can be scrolled before prompting "scroll?"
- (a whole screen has been scrolled) set 5C8C SCR CT
back to 18h/24d less the current line; this is the number of
lines in the upper screen
- stack the attributes from 5C8F ATTR T/MASK T and P
FLAG; the permanent ones will be copied to the temporary
ones for the time being
- call 0601 CHAN OPEN with stream minus 3 to open the
keyboard channel; output to the lower screen
- call 0C0A PO MSG to print message number zero from
0CF8, the "scroll?" message
- set bit 5 of TV FLAG; "clear the lower screen after
this keystroke"
- set bit 3 of FLAGS; "L mode". K mode would interpret
eg "N" as NEXT
- zero bit 5 of FLAGS; "waiting for a key"
- switch back into the main registers, to avoid a double
EXX into the main registers [however there seems no need to
stack DE' as suggested by the notes: this was done in 0C3B PO
SAVE where DE' carries an important pointer, but here DE' is
only pointing to the last byte of "scroll?"]
- call 15D4 WAIT KEY; it waits till a key is pressed,
then clears the lower screen and returns with the final key
code in A
- switch the registers back to the alternates
- if the key code is 20h BREAK/SPACE report "BREAK -
CONT repeats"
- if it is E2 STOP the same

623
- OR the code with 00100000b/20h, setting bit 5 to make
it lower-case
- if it was upper or lower case "N" make the report
- (no BREAK; continue scrolling) call 1601 CHAN OPEN
again with stream minus 2 to open the upper screen output
channel
- recover ATTR T/MASK T and P FLAG from the stack and
restore the temporary attributes.
_0CD2_PO_SCR_3 (scroll the screen by one line, automatic
or ordinary listing): call 0DFE CL SC ALL to scroll the whole
screen by one line; a blank line is added at the bottom of the
lower screen
- make a new print position at the left margin, always
column 21h, of the line one above 5C6B DF SZ; DF SZ marked
the top of the lower screen before the scroll, so this position
is on the blank line at the top of the lower screen
- stack this print position
- call 0E9B CL ADDR to find the address in the display
area of the print position; this blank line used to belong to
the lower screen, and its attributes have been scrolled up, so
they must be changed to upper screen attributes
- convert the display address to the corresponding
attribute address; the seven instructions after the subroutine
call, which do this conversion, duplicate exactly the first
seven of 0BDB PO ATTR - a very unusual duplication in the
ROM - and are explained under PO ATTR
- make the address 5AE0; this is the address of the
attributes of the first position in the bottom screen line,
which is in the lower screen but was given upper-screen
attributes when it was cleared, by the last part of 0E4D CL
LINE 2, because TV FLAG bit zero was zero for "upper screen"
- get the upper screen attributes from the bottom line
and the lower screen attributes from the print position
address just found
- set a counter to 20h for the number of characters in
the line.
_0CF0_PO_SCR_3A: step along the lines putting the

624
attributes from the bottom line in the new upper screen line
and vice versa
- recover the position values and return.
_0D02_PO_SCR_4 (scrolling the lower screen): if the print
position line number is zero or one report "Out of screen";
remember it is counted from 18h/24d at the top of the lower
screen. The lower screen is never allowed to grow beyond
17h/23d
lines, leaving one for the upper screen
- add the line number to the number in 5C6B DF SZ and
subtract 19h
- if there is no carry, return; 5C6B DF SZ records the
number of lines available in the lower screen, and the number
of lines being used down to the present line number (L, say) is
19h/25d - L. If this is no bigger than 5C6B DF SZ (D, say),
there is no need to scroll. So add the line number to 5C6B DF
SZ and subtract 19h:
D + L - 19h = D - (19h - L)
and if there is no carry, D >= (19h - L) and there is no need
to scroll
- (the lower screen must be scrolled) negate the result;
giving (19h - L) - D, the number of scrolls needed
- save the position values and the temporary colour svs
from 5C8F ATTR T/MASK T and P FLAG
- call OD4D TEMPS so as to use the permanent colours;
always used in the lower screen
- make a counter of the number of scrolls needed; this
is the scroll counter, not to be confused with the line counter,
which is still to be calculated. The routine scrolls X lines up
Y times, where X is the line counter and Y the scroll counter.
_0D1C_PO_SCR_4A: increment 5C6B DF SZ, but keep the
old value
- check the new value with the print position line
number in the upper screen from 5C89 S POSN hi
- if 5C89 S POSN hi is larger jump on to PO SCR 4B;

625
there is still room between the end of the upper screen and
the new top of the lower, so no need to scroll the upper
screen yet
- (the whole screen must be scrolled) increment 5C89 S
POSN hi; the upper screen print position will move up one
- make a line counter 18h/24d; all the lines will be
scrolled, right down to the last line of the lower screen.
_0D2D_PO_SCR_4B: call 0E00 CL SCROLL to scroll up one
as many lines as indicated by the line counter, counted from
the bottom of the screen; this will either be the old value of
5C6B DF SZ, or 18h for the whole screen
- decrement the scroll counter
- if it isn't zero yet loop back to PO SCR 4A
- (scrolling is finished) recover the temporary
attribute svs from the stack and put them back
- zero bit zero of TV FLAG; "upper screen"
- call 0DD9 CL SET with the new upper screen print
position to set the print position svs
- set bit zero of TV FLAG; "lower screen"
- recover the lower screen print position and return.
Exit: RET from PO SCR; output is to printer
- indirect jumps to 0DD9 CL SET from
PO SCR; no need for scroll
PO SCR 3A; upper screen scroll completed
PO SCR 4; no need for scroll
PO SCR 4B; lower screen scrolls completed
- multiple return from PO SCR; end of LISTing routine.
Output parameters: BC' holds the corrected print
position.
Called from:
0A4F PO ENTER
0B93 PR ALL 1
Exit from:
0AAC PO AT ERR
Rems:
1795 AUTO LIST loads LIST SP for ret after auto listing

626
PO SCR 2 0C88 (0C55 PO SCR)
Jumps from:
0C55 PO SCR

PO SCR 3 0CD2 (0C55 PO SCR)


Jumps from:
0C55 PO SCR
0C88 PO SCR 2

PO SCR 3A 0CF0 (0C55 PO SCR)


Exit from:
0CD2 PO SCR 3 (0C55 PO SCR)
Jumps from:
auto

PO SCR 4 0D02 (0C55 PO SCR)


Exit from:
0C55 PO SCR

PO SCR 4A 0D1C (0C55 PO SCR)


Jumps from:
0D2D PO SCR 4B

PO SCR 4B 0D2D (0C55 PO SCR)


Exit from:
0D1C PO SCR 4A (0C55 PO SCR)

PO SEARCH subroutine 0C41


Finds an indexed entry in a message table. Called only
from 0C14 PO TABLE in 0C0A PO MSG. Can be useful in m/c
programs.
The start of the table is marked by a byte with the hi
bit set, and the last character of each message in it is
similarly marked. Returns with the carry flag set to signal "no
leading space".
Input parameters: A holds the message number of the

627
required message in the table; the first message is number
zero
- DE holds the pointer to the table to be searched; an
earlier address would work, provided message number zero
starts after the first byte with hi bit set to be encountered.
Action: increment the index; now it counts from one.
_0C44_PO_STEP: check the hi bit of the byte at the
pointer
- increment the pointer
- if the hi bit is zero loop back to PO STEP
- (hi bit set) decrement the message number
- if it isn't zero yet loop back to PO STEP
- (the pointer is on the first byte of the required
message) if the index was less than 20h/32d return with carry;
no leading space
- if the first code of the message is below 41h "A",
return with carry.
Exit: two RETs, from 0C44 PO STEP.
Output parameters: DE holds the address of the first byte
of the message
- "no carry" signifies "leading space may be required";
see 0C0A PO MSG.
Called from:
0C14 PO TABLE

position see DISPLAY AREA

position control characters see control characters

POSITION FETCH SUBROUTINE see 0B03 PO FETCH

POSITION STORE SUBROUTINE see 0ADC PO STORE

position value see DISPLAY AREA

PO SPACE 0AD0 (09F4 PRINT OUT)


Exit from:

628
0AC2 PO TAB (0A7A PO 1 OPER)
Jumps from:
auto

PO ST E 0AF0 (09F4 PRINT OUT)


Exit from:
0ADC PO STORE

PO STEP 0C44 (0C41 PO SEARCH)


Exit from:
0C41 PO SEARCH
Jumps from:
auto (twice)

PO STORE 0ADC (09F4 PRINT OUT)


Exit from:
0AD9 PO ABLE
0DD9 CL SET/0DF4 CL SET 2

PO ST PR 0AFC (0ADC PO STORE)


Exit from:
0ADC PO STORE

PO T 0B5F (09F4 PRINT OUT)


Jumps from:
0B52 PO T&UDG

PO TAB 0AC2 (09F4 PRINT OUT)


Jumps from:
0AB7 PO CONT

PO TABLE 0C14 (0C0A PO MSG)


Jumps from:
0C0A PO MSG

PO TOKENS subroutine 0C10 (0C0A PO MSG)


Entry point to 0C0A PO MSG used when printing tokens.

629
Called from:
0B5F PO T

PO TRSP 0C35 (0C0A PO MSG)


Exit from:
0C22 PO EACH (0C0A PO MSG)

PO TV 1 0A7D (09F4 PRINT OUT)


Jumps from:
0A75 PO 2 OPER

PO TV 2 0A6D (09F4 PRINT OUT)


Made output routine by 0A75 PO 2 OPER in PO 1 OPER.

PO T&UDG 0B52 (09F4 PRINT OUT)


Exit from:
0B24 PO ANY

P OUT 1AF1
P OVER 1AF0
see 1A7A syntax offset table

PO 1 OPER subroutine 0A7A


See 09F4 PRINT OUT.

PO 2 OPER subroutine 0A75


See 09F4 PRINT OUT.

P PAPER 1AEC
P PAUSE 1AC5
see 1A7A syntax offset table

PPC system variable 5C45


Bytes: 2
The BASIC line number of the statement currently being
executed. When execution reaches a new BASIC line, or
jumps to one, the line number is read into PPC by 1BBF LINE

630
USE. If the line has no number - a direct command - it is given
the number FFFEh/65534d (-2) by 1B8A LINE RUN.
PPC is read by 133C MAIN 5 after BASIC execution is
completed, so that the line number can be printed by 1A1B
OUT NUM 1 in the report message; if the number is -2, it gets
printed as zero.
5C47 SUBPPC and PPC are copied together into 5C70
OSPCC and OLDPPC by 1376 MAIN 7 to mark the line and
statement from which execution is to start after CONTINUE,
unless bit 7 of 5C44 NSPPC indicates that a jump is due.
PPC is also read and included as part of the control
variable of a FOR ... NEXT loop (1D34 F L&S) and to be put on
the GO SUB stack in executing a GO SUB command (1EED GO
SUB).
Written by:
1B8A LINE RUN
1BBF LINE USE
Read by:
1376 MAIN 7
133C MAIN 5
1D34 F L&S (twice)
1EED GO SUB

P PLOT 1AC1
P POKE 1AB1
see 1A7A syntax offset table

P POSN system variable 5C7F


Bytes: 1.
The "column number", ie the byte reached in the print
buffer, when printing out on the ZX printer. It is written by
0ADC PO STORE, just like the print position system variables
when printing is done on screen, and similarly fetched by
0B1D PO F PR.
Written by:
0AFC PO ST PR
Read by:

631
0B1D PO F PR

P PRINT 1A9C see 1A7A syntax offset table

PR ALL 0B7F (09F4 PRINT OUT)


Exit from:
0B24 PO ANY, direct and through
0B76 PO CHAR 3

PR ALL 1 0B93 (09F4 PRINT OUT)


Jumps from:
0B7F PR ALL

PR ALL 2 0BA4 (09F4 PRINT OUT)


Jumps from:
0B93 PR ALL 1

PR ALL 3 0BB6 (09F4 PRINT OUT)


Jumps from:
0BA4 PR ALL 2

PR ALL 4 0BB7 (09F4 PRINT OUT)


Jumps from:
0BC1 PR ALL 5

PR ALL 5 0BC1 (09F4 PRINT OUT)


Exit from:
0BD3 PR ALL 6 (0B24 PO ANY)

PR ALL 6 0BD3 (09F4 PRINT OUT)


(misprinted PO ALL 6 in the listing)
Jumps from:
0BB7 PR ALL 4

P RAMT system variable 5CB4


Bytes: 2.

632
Holds FFFFh/65535d in the 48K Spectrum, 7FFFh/32767d
in the 16K model - ie the address of the last available byte in
RAM. POKing it with 7FFFh will cause a 48K to simulate the
16K.
Loaded on start-up by 11EF RAM DONE with the last
usable RAM address; the second write, about 1200. It is not
changed by the NEW command - it is saved in 11B7 NEW and
restored in 11EF RAM DONE. Otherwise read by ROM only to
check that RAM TOP is not being set too high, in 1EB7 CLEAR
1.
Written by:
11EF RAM DONE (twice)
Read by:
11B7 NEW
1EB7 CLEAR 1
Rems:
0000 START loads FFFFh for P RAMT

P RANDOM 1AB5 see 1A7A syntax offset table

PR AT TAB 201E (1FC9 PRINT)


Exit from:
1FC9 PRINT through
1FFC PR ITEM 1
200E PR ITEM 2

PRB BYTES OEE7 (0EDF CLEAR PRB)


Jumps from:
auto

PR CC system variable 5C80


Bytes: 2. [Actually only the lo byte is used, and in the
Spectrum handbooks 5C81 is marked "not used". However it is
written to by 0AFC PO ST PR in both bytes; rather pointlessly,
since the hi byte is irrelevant, LD (IY+70),L would be no
longer, and indeed this is used at 0EDF CLEAR PRB. So it is

633
safe to use the spare system variable 5CB1 provided it is
protected from channel 3 outputs.]
The print position, column number only, for the ZX
printer. PR CC is written by 0AFC PO ST PR each time a
character is printed or the print position is changed, and set
to zero by 0EDF CLEAR PRB whenever the print buffer is
cleared. It is read by 0B1D PO F PR whenever a character is to
be printed.
Written by:
0AFC PO ST PR
0EDF CLEAR PRB
Read by:
0B1D PO F PR

P READ 1AC8 see 1A7A syntax offset table

precision
Means, roughly, "the number of correct digits obtained
as the result of a calculation".
There are many ways of representing numbers in print,
but most of them involve a string of digits, eg binary, hex or
decimal. The first digit is most significant and the last,
besides being least significant, is usually slightly inaccurate.
Pi, for example, is somewhere between 3.1415 and 3.1416
decimal, closer to 3.1416, but no finite string of decimal digits
can represent it exactly, nor of binary or hex digits; only
integers and exact fractions can be written in digits without
rounding, and_exact representations of "real numbers" like pi
can only be given by infinite series.
The 5-byte FP number form represents numbers with a
maximum precision of 32d binary digits, which is equivalent
to between nine and ten decimal digits, but the Spectrum
only prints out numbers with eight decimal digits - the eighth
is rounded to give as good a representation as possible of the
missing digits. This is not a limitation of the computer: it is not
very difficult to rewrite the calculator's main arithmetic
routines in a m/c program so as to use eg a 6-byte or 8-byte

634
mantissa, with precision of 48d or 64d binary digits, 14d plus
and 19d plus decimal digits.
32d-digit precision is maintained in multiplication and
division by giving the mantissa a temporary fifth byte, and
using its hi bit, the_33rd_binary_digit, to round the 32nd
digit. Even the_34th_binary_digit has some significance in
additions.
All rounding from calculations is binary rounding, which
is very simple: round up if the last digit is one, down if it is
zero. This is sometimes referred to as_adding_in_the_carry
_from_the_calculation.
If the mileage indicator in your car only shows 5
figures, and you have done 99,999d miles, when you
complete another mile_the_carry_will_ripple right back to the
left-hand digit and your indicator will show 00000. This
illustrates the use of the term in the notes, except that there it
is always binary digits which are in question.
2DE3 PRINT FP rounds X to eight digits
2EB8 PF ALL 9 round up if ninth digit set
2EDF PF FRN LP round up 8-digit integer if fraction part
2F0C PF ROUND round print buffer to maximum 8 digits
2F18 PF RND LP add carry to round up or down
2F25 PF R BACK jump back to round again
2FDD SHIFT FP add back carry and set to zero if it
ripples back
2FE5 ONE SHIFT check for ripple back of carry
3004 ADD BACK add back carry and check for ripple
316E SHIFT ONE mantissa has temporary fifth byte to
ensure 32d-digit precision
3186 NORML NOW add back carry and check for ripple
31AF division 32 binary digit precision obtained
31FA COUNT ONE the 33rd binary digit is needed for
precision in the 32nd

P REM 1AA5 see 1A7A syntax offset table

PR END Z subroutine 2045

635
See 1FCD PRINT.
Called from:
1FDF PRINT 2
2067 PR POSN 3

PREP ADD subroutine 2F9B


See 303E FULL ADDN; this subroutine is really part of
FULL ADDN. Performs step 1. as explained in the introduction
to FULL ADDN, and also:
- negates the mantissa if it is a negative number
- returns the exponent byte and a five-byte mantissa
separately: the first byte of the mantissa is now a sign byte,
00 for a positive number, FF for a negative number.
Thus
pi, 82 49 0F DA A2, becomes 82 and 00 C9 0F DA A2,
minus pi, 82 C9 0F DA A2, becomes 82 and FF 36 F0 25 5E.
Input parameters: HL is the first address of the five
bytes of the number, which must be in full format, not a small
integer.
Action: get the exponent
- zero the first byte; this is now the sign byte
- if the exponent was zero, return; the number must be
zero
- move the pointer on to the second byte; its hi bit is
the sign marker, zero for positive and one for negative
- check the sign marker and set it anyway; making the
mantissa a true mantissa
- if it was zero, return; the number is positive, and
all necessary action has been taken
- (negative number) make a counter of five for the bytes
of the number
- move the pointer past the last byte
- set the carry flag.
_2FAF_NEG_BYTE: decrement the pointer
- get the byte at the pointer
- CPL it and add in the carry; the CPL instruction
doesn't affect the carry flag, so the last byte is subtracted

636
from 100h, the others from FFh, unless the fifth byte was 00
to start with, in which case it is restored to 00 and the carry
goes on to the next byte
- loop back to NEG BYTE till all five bytes have been
changed; the sign byte becomes FFh.
Exit: RET, from PREP ADD for a positive or NEG BYTE for
a negative number.
Output parameters: A holds the exponent byte
- others unchanged.
Called from:
303E FULL ADDN (twice)

PREP M/D subroutine 30C0


Called for each operand of a long multiplication or
division, after the operands have been put in full FP format;
makes the only adjustment required for multiplication or
division, ie sets the hi bit of the mantissa. But it also
figures the sign of the result, which will be positive for like
signs of the two operands, negative if they are different.
Input parameters: HL holds the first address of the FP
number
- A holds a sign flag, which is zero on the first call
to PREP M/D.
Action: call 34E9 TEST ZERO
- if the number is zero return with carry
- move the pointer on to the sign byte of the number
- XOR the sign byte with the sign flag; only bit 7 is
significant - it will be set if the signs are unlike, zeroed if
they are alike
- set the hi bit of the mantissa; this makes a "true
mantissa".
Exit: RET.
Output parameters: all registers unchanged except A;
after both sign bytes have been XORed with its starting zero,
its hi bit will be one for unlike signs, zero for like, ie
signalling the sign bit of the result
- the carry flag is set if the number was zero.

637
Called from:
30F0 MULT LONG (twice)
31AF division (twice)

present character see 0018 GET CHAR

P RESTORE 1ACF
P RETURN 1A8D
see 1A7A syntax parameter table

PRINT key (F5) see also commands, functions and


operators,
KEYBOARD SCANNING
The P key in K mode shift produces the command PRINT.
The "PRINT statement" may, but need not, include position
and colour controls and one or more operands, string or
numeric expressions separated by position controllers. It may
also include (hatch) with a stream number. It outputs the
operands, or a newline if none are given, to the chosen
stream, or to stream 2 channel S if none is specified.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A9C P PRINT causes a jump via 1C11 CLASS 05 and 1C16 JUMP
C R to the executive routine 1FCD PRINT.
0364 K TOKENS converts letter P to command PRINT

PRINT subroutine 1FCD


Called only from 1A9C P PRINT in the syntax parameter
table; the executive routine for the PRINT command. It
outputs its operands to channel S, main screen, unless
another stream is specified, using the permanent colours
unless others are specified.
All of the PRINT command routine is common to the
LPRINT command, the only difference being in the stream
number set at the start. Parts of it are also called as
subroutines by the

638
2089 INPUT command routine, and one minor part by the
0605 SAVE ETC command routine. These subroutines are
described here as part of this routine, for the sake of clarity.
Input parameters: none.
Action: make the stream number 2.
_1FCF_PRINT_1: call 1601 CHAN OPEN to open the specified
channel; stream 3 channel P, ZX printer, in the case of
LPRINT, stream 2 channel S main screen in the case of PRINT
- call 0D4D TEMPS to use the permanent colours [a
redundant call: 1601 CHAN OPEN has just exited through
TEMPS
from 1646 CHAN S 1, for either of the screen outputs]
- call PRINT 2; see immediately below
- call 1BEE CHECK END, which in syntax checking reports
an error if the end of the statement has not been reached and
makes a double return to the statement loop if it has
- (run time) return; to the statement loop.
_1FDF_PRINT_2 (formally an independent subroutine, but
the only call outside the PRINT command routine is from
2089 INPUT, to handle the INK to TAB controls): get the first
code of the PRINT or INPUT statement
- call PR END Z; see immediately below
- if the code is newline, colon or 29h ) jump on to
PRINT 4.
_1FE5_PRINT_3 (the code is not a terminator): call PR
POSN 1 to check for and execute the semicolon, comma and
newline position controls; see immediately below
- if it returns with zero loop back to PRINT 3; a
position control was executed, so look for more
- call PR ITEM 1 to check for and execute AT, TAB, and
the colour controls or to output string or numeric
expressions;
see immediately below
- call PR POSN 1 again, and again loop back to PRINT 3
if any position controls are executed.

639
_1FF2_PRINT_4 (either a terminator has been encountered,
or there are no more position controls): if the code is 29h ),
return; this must be the close of an INPUT prompt.
_1FF5_PRINT_CR (also called as a subroutine by PR POSN
2): output a newline and return; the end of the PRINT
statement has been reached. This is bypassed if the PRINT
statement ends with a position control, eg semicolon, see PR
POSN 3 below.
_1FFC_PR_ITEM_1 (another formally independent
subroutine, but outside the PRINT routine called only by 21AF
IN NEXT 1 in
the INPUT command routine): read the first code
- if it's not "AT" jump on to PR ITEM 2
- ("AT") call 1C79 NEXT 2NUM and 2307 STK TO BC to
collect the two parameters
- jump on to PR AT TAB with them and the AT control
code 16h.
_200E_PR_ITEM_2: if the code isn't TAB jump to PR ITEM 3
- ("TAB") call 1C82 EXPT 1NUM and 1E99 FIND INT2 to
read the parameter from BASIC into BC; there is only one
parameter,
but it may be a 2-byte number
- enter PR AT TAB with the TAB control code 17h.
_201E_PR_AT_TAB: call the output routine 0010 PRINT A 1
three times, once with the control code and once with each
parameter; this triple call is used to execute indirect jumps
within the 0A75 PO 2 OPER routine, so as to handle the
control code and its operands together. See under 09F4
PRINT OUT
- return, into PRINT 3.
_2024_PR_ITEM_3 (not AT or TAB): call 21F2 CO TEMP 3,
which checks for and executes any colour controls
- if it returns with no carry, return, into PRINT 3; a
colour control was executed
- call 2070 STR ALTER to check for and execute any
instructions such as "PRINT (hatch) 0"
- if it returns with no carry, return, into PRINT 3; the

640
stream has been altered
- (the next code isn't a position controller, a colour
control, an AT or TAB, or a hatch, so it must be interpreted as
an expression to be output) call 24FB SCANNING, which puts
its numeric value or its string parameters on the calculator
stack
- if bit 6 of FLAGS is zero call 2BF1 STK FETCH, to get
the start pointer and length from the stack; it doesn't affect
the Z flag. Bit 6 is the string/numeric flag, zero for a string,
one for a numeric result
- if it is one exit into 2DE3 PRINT FP; the complicated
routine to print a numeric result which is described separately
in this index. Its return will be into PRINT 3.
_203C_PR_STRING (output a string): check the length
- decrement it
- if it was zero return, to PRINT 3; either it was a
null string, or the loop has now output all its bytes
- read the byte at the pointer and move the pointer on
- call 0010 PRINT A 1 to output the byte
- loop back to PR STRING.
_2045_PR_END_Z (again formally a subroutine, but called
only from PRINT 2 and PR POSN 3): if the code is 29h ),
return;
this must be the end of an INPUT prompt.
_2048_PR_ST_END (another formal subroutine, this one
called also from 06C3 SA CODE to check if the statement ends
after SAVE/LOAD ... CODE): if the code is a newline or a colon,
return.
_204E_PR_POSN_1 (yet another formal subroutine, called
from 20C1 IN ITEM 1 and 21B2 IN NEXT 2 in the INPUT
command
routine apart from the two calls in PRINT 3): get the code
- if it is 3B semicolon jump on to PR POSN 3; the
semicolon is a position controller, but no action is taken on it
- if it isn't 2C comma jump on to PR POSN 2
- (it is a comma) output a PRINT comma, code 06
- jump on to PR POSN 3.

641
_2061_PR_POSN_2 (not semicolon or comma): if it isn't 27h
single quote, return with NZ to PRINT 3; not a position
controller
- (it is a quote, calling for a newline) call 1FF5 PRINT
CR (misprinted) to execute the newline.
_2067_PR_POSN_3 (a position controller has been
executed): get the next character
- call PR END Z
- if it returns with NZ jump on to PR POSN 4; not the
end of the PRINT statement
- (end of PRINT statement) drop a return address, making
a double return to the statement loop; bypassing the final
newline if the PRINT statement ends with a position controller
_206E_PR_POSN_4: set the Z flag [ie make it Z; "reset" in
the notes is a misprint]
- return.
Exit: the RETs in PRINT 1, PRINT 4 and PRINT CR, except
of course when it is a subroutine called from PR POSN 2, all go
back to 1B76 STMT RET
- the RETs in PR AT TAB, PR ITEM 3, PR END Z, PR ST
END, PR POSN 2 and PR POSN 4 are all "internal" returns,
staying within the command routine, except when the return
address is
dropped in PR POSN 4 causing a return to STMT RET.
- PR ITEM 3 exits to 2DE3 PRINT FP to output a number,
but the return from there will again be into PRINT 3.
Output parameters: none.

printable characters see character codes, 0B24 PO ABLE

PRINT A CARRIAGE RETURN SUBROUTINE see 1EF5 PRINT


CR under
1FCD PRINT

PRINT A CHARACTER RESTART see 0010 PRINT A 1

print address used to mean "pixel address in the display

642
area" in 0B6A PO CHAR 2; see DISPLAY AREA

PRINT A FLASHING CHARACTER SUBROUTINE see 1BC1


OUT FLASH

PRINT A FLOATING POINT NUMBER SUBROUTINE see


2DE3 PRINT FP

PRINT ALL CHARACTERS SUBROUTINE see 0B7F PR ALL


(0B24 PO
ANY)

PRINT ANY CHARACTERS SUBROUTINE see 0B24 PO ANY

PRINT A QUESTION MARK SUBROUTINE see 0A69 PO


QUEST

PRINT A WHOLE BASIC LINE SUBROUTINE see 1855 OUT


LINE

PRINT A 1 subroutine (restart) 0010


Sends a single byte to output.
The output routine of the Spectrum system; although we
usually call it "printing", what actually happens to the bytes
output depends on the channel selected at the moment.
Channel S will put them on the main screen, channel K into
the lower screen, channel P will send them to the ZX printer,
channel R will copy them to the work space; other channels
selected through Interface 1 etc will send them to a network,
to a DATA file on Microdrive, etc. Even when printing to the
screen, there is an important distinction between this output
routine and the PRINT command routine, see under_printing.
The routine makes in effect a "computed GO SUB" call by
going into CALL SUB with a pointer to the current output
address from the channel data, which will be
09F4 PRINT OUT for channels 'K', 'S' and 'P'
150F ADD CHAR for channel 'R'; only available to ROM

643
See 15AF INITIAL CHANNEL INFORMATION. The output
subroutine address is pointed to by 5C51 CURCHL; from m/c,
though not from BASIC, you can put any address you like in
this system variable, and this is no doubt how Interface 1 and
other peripherals manipulate the output of the Spectrum.
PRINT A 1 operates with the alternate registers, and as
a consequence cannot be called "recursively", ie from within
its own routines, without getting the main registers back and
probably corrupting them: this is handled by calling 0C3B PO
SAVE instead of PRINT A 1 whenever recursive printing is
required, see the index entry.
Input parameters: A holds the byte to be output.
Action: jump on to PRINT A 2; only eight bytes are
available for a restart. [The five bytes of PRINT A 2 could well
have been put here, followed by a jump to CALL SUB.]
_15F2_PRINT_A_2: save all the main registers by EXX
- put HL' on the stack; very often the vital return
address from calculator operations
- get a pointer to the current channel output routine
address in 5C51 CURCHL
_15F7_CALL_SUB: read the address from the channel
information
- call 162C CALL JUMP, which is just a JP (HL); thus in
effect it calls the subroutine addressed in 5C51 CURCHL
- restore the registers.
Exit: RET, from 15F7 CALL SUB.
Output parameters: none
- the main registers, provided the output routine
doesn't use EXX, and HL' are saved, but not the A register.
Called from:
07AD LD CH PR (twice)
0C3B PO SAVE
133C MAIN 5 (twice)
1835 LIST ALL
1865 OUT LINE1
196D OUT CH 3
1FF5 PRINT CR

644
201E PR AT TAB (3 times)
203C PR STRING
204E PR POSN 1
21FC CO TEMP 4 (twice)
2DE3 PRINT FP
2DF2 PF NEGTVE
2F64 PF DEC 0$
2F6C PF E FRMT
2F85 PF E SGN

PRINT A 2 15F2 (0010 PRINT A 1)


Jumps from:
0010 PRINT A 1

print buffer (ad hoc) see 2DE3 PRINT FP

PRINT comma (06) see control characters

PRINT COMMA SUBROUTINE see 0A5F PO COMMA

PRINT CR subroutine 1FF5


See 1FCD PRINT.
Called from:
2061 PR POSN 2
Exit from:
1FF2 PRINT 4

printer (ZX printer), printer buffer, printer 'column',


printer motor, printer position
Output to the ZX printer is executed by opening channel
P and using 0010 PRINT A 1, which calls 09F4 PRINT OUT.
The pixels picked up by 0B65 PO CHAR and following are
output by 0BD3 PR ALL 6 to the print column position kept in
5C80 PR CC, in the print buffer, instead of to the screen. For
the OUT commands of 0EF4 COPY LINE etc used to operate
the motor and stylus, see ports.
The ZX printer is much more a toy printer than the

645
Spectrum is a toy computer. Many more sophisticated, but
more expensive, printers can be bought, with interfaces
which divert the output of channel P to their own routines, so
that LPRINT, LLIST and even COPY are effective with them.
The print buffer is a blank space of 100h/256d bytes at
5B00h/23296d in RAM; enough for 20h/32d characters each
of 8 pixels. It is undisturbed by all operations of the Spectrum
except those concerned with the ZX printer, which leaves it
available for m/c programs not using the printer; a good place
to keep variables, flags etc. If IX is placed at 5B7Fh, all its
bytes are accessible to the IX indexing system.
Every time the buffer is filled, or a newline is output,
its bits are output to the printer by 0ECD COPY BUFF, and it is
blanked to zeroes by 0EDF CLEAR PRB; this is also done by
1303 MAIN 4 on completion of any command, even if the
buffer isn't full, providing bit 2 of FLAGS2 signals it has been
used.
09F4 PRINT OUT handles output to printer or screen
0A23 PO BACK 1 cannot backspace to last line on printer
0A4F PO ENTER carriage return clears print buffer
0A87 PO CONT line numbers ignored in output to printer
0ADC PO STORE position values read differently
0AFC PO ST PR stores position values for printer
0B03 PO FETCH position values read differently
0B1D PO F PR fetches position values for printer
0B7F PR ALL print buffer cleared after 20h characters
0BA4 PR ALL 2 flags signalling use of printer
0BB7 PR ALL 4 pixel prepared for in print buffer
0BD3 PR ALL 6 pixel put in print buffer
0C55 PO SCR skipped when outputting to printer
0DD9 CL SET prepares position value for PO STORE
0ECD COPY BUFF copies eight pixel lines to printer
0EDA COPY END stops motor
0EDF CLEAR PRB prepares to blank printer buffer
0EE7 PRB BYTES blanks printer buffer
0EF4 COPY LINE slows motor for last two lines
0EFD COPY L 1 handles BREAK during line printing

646
0F0C COPY L 2 checks that printer present and ready
0F1E COPY L 5 outputs bits to printer
1219 RAM SET clears printer buffer
1303 MAIN 4 empties printer buffer after execution
1646 CHAN S 1 signals "not using printer"
164D CHAN P signals "using printer"

print format for numbers see 2DE3 PRINT FP below

PRINT FP subroutine 2DE3


Part of the PRINT command routine: converts the last
value on the calculator stack, which can be any kind of
number handled by the floating-point calculator, to the
standard_14-_character_number_format, and outputs this
version to screen, printer etc, through the current output
channel.
For the format, see page 46 of the old Spectrum
handbook, page 59-60 of the Plus 2 book; or try this demo
program:
100 LET x=EXP 1: REM the number e, 2.718281828
200 PRINT 1/x/100, x: LET x = -10*x: GO TO 200
Another demonstration program is given on page 167 of
"ROM Disassembled".
You will observe that when numbers are shown on
screen,
- only 8 significant figures are shown
- numbers greater than 100,000,000d or less than
0.00001d
are shown in E format
- numbers greater than 10**38 or less than 10**-39 cause
over- or underflow.
Thus the maximum number of characters required to
output any number is 8 digits + 1 decimal point + 1 minus sign
+ 4 (eg "E-38"), 14 characters in all.
Most of the subroutine deals only with positive values;
if the number is negative, a minus sign is printed and the
subroutine continues with the absolute value of the number.

647
Nine significant digits of the number to be output are
first compiled in the calculator memory's mem-3 and mem-4,
the ten bytes starting at 5CA1h/23713d, and two counters are
set up in mem-5; mem-3 to mem-5 are called
the_ad_hoc_print_buffer, nothing to do with the main print
buffer, see_printer. The counters are
- the significant figures counter: the overall number of
digits to be output excluding leading or trailing zeroes
- the magnitude counter: a positive number shows the
number of digits before the decimal point, a negative number
the number of_leading_zeroes, ie zeroes after the decimal
point and before the significant figures of the number.
The magnitude counter is calculated from the logarithm
of X to the base ten, using its integer part only: ie INT log X, or
strictly speaking INT ABS log X. If X has N digits before the
decimal point, N must be between 10**(N - 1) and 10**N; for
example 69.7 is between 10**1 = 10 and 10**2 = 100. So log X is
between N - 1 and N; log 69.7 is 1.8432328.
If X has N leading zeroes after the decimal point, N
must be between 10**-(N + 1) and 10**-N; for example 0.00697
is
between 10**-3 = 0.001 and 10**-2 = 0.01. Now ABS log X is
between N and N + 1; log 0.00697 is -3 + 0.8432328, so ABS log
0.00697 is 2.1567672.
The log calculations are performed by calls to LOG
(2**A), which works from the binary exponent B of X. For a
number X greater than one, B is positive or zero, and the
logarithm of X to the base 2 is B plus a fraction; then log X to
the base ten is B * log 2 to base 10 = 0.3010d * B plus a
fraction; see under the subroutine LOG(2**A). For X less than
one, B is negative, but the essential calculation is the same.
Actually the routine sometimes works with the normalized
version of B, but the effect is as described here.
LOG (2**A) gives results which are often one off,
because they take no account of the fractions; the adjustment
required is usually plus one for positive B, minus one for

648
negative, but this is simplified by using B + 2 for negative B, so
the later correction is always in the same direction.
The nine digits are assembled by PF LOOP to PF FR EXX,
rounded to eight by PF ROUND to PF R BACK and output from
the buffer by PF E SBRN and following.
If the number is less than one, its representation
should have an_initial_zero before the decimal point; the
routine fails to output this in some cases, see PF NOT E below.
The subroutine can readily be used by m/c programmers;
its length and complexity make this extremely worth while!
However, much simpler and faster routines can be devised for
outputting restricted classes of numbers, eg integers or
positive numbers only; there are some in the ROM. Much of
the complexity is the consequence of the restriction to 14d
output characters.
Input parameters: none
- the number X to be output is on the calculator stack.
Action: use the less-0 and greater-0 routines of the
calculator to check the sign of X
- if X is negative, jump on to PF NEGTVE
- if positive to PF POSTVE
- (that leaves zero) output 30h "zero" and return.
_2DF2_PF_NEGTVE: use the calculator to replace X by ABS
X
- output 2D -.
_2DF8_PF_POSTVE: put zeroes in mem-3, mem-4 and
mem-5;
this creates an ad hoc print buffer of 15d bytes in the
calculator memory area
- save HL'; this routine is called by the calculator,
from 361F str$, and using it "recursively" could corrupt the
saved return address in HL'.
_2E01_PF_LOOP: use the calculator to split X into an
integer part iX and a fractional part fX
- if the exponent byte of iX isn't zero jump on to PF

649
LARGE; iX is more than 10000h/65536d, otherwise 36AF int
would return it in small integer form, with zero exponent
byte
- (iX is a small integer) make a bit counter 10h/16d for
the binary digits of iX
- call 2D7F INT FETCH to get iX into DE
- if its hi byte isn't zero jump on to PF SAVE
- (hi byte is zero) if both its bytes are zero jump on
to PF SMALL; iX is zero, X is a pure fraction
- (iX is a one-byte integer) move the lo byte to the hi
byte of the register
- reduce the bit counter to 08.
_2E1E_PF_SAVE: save iX in the alternate registers
- jump on to PF BITS.
_2E24_PF_SMALL (iX is zero, ie X is a pure fraction) read
the exponent of fX [an error here: the zero iX ought to have
been cleared from the stack before proceeding. For its
unfortunate effects see under 361F str$]
- subtract 7Eh/126d; this makes B + 2, two more than the
normalized binary exponent of fX. B is negative, ie a byte 01
->7F, because X is a pure fraction, so B + 2 is 03 -> 81h
- call 2DC1 LOG(2**A) which returns the number N of
leading zeroes required after the decimal point
- subtract N from the twelfth byte of the ad hoc print
buffer, mem-5 byte 2; it was zeroed at the start of the routine,
but if this isn't the first turn round from PF LOOP there may
be a number in it. This is the magnitude counter, now zero or
negative, indicating zeroes after the decimal point
- call 2D4F E TO FP to get fX * 10**N from the last
value on the stack fX and N in the A register; this ought to be
fX decimal shifted left until it has no leading zeroes, but N
may be one too large. The notes call it y
- use the calculator to separate y into its integer and
fractional parts; the notes call them i2 and f2. If N was too
large i2 won't be zero, but it can only be a single decimal
digit
- put i2 in the first byte of the print buffer; it will

650
be the first to be output after the leading zeroes, unless it is
zero
- decrement it, shift its hi bit into the carry, then
subtract it from itself with carry and increment it again. This
produces zero from zero:
00 -> FF -> FE with carry -> FF -> 00
and one from anything else:
X -> (X - 1) -> 2(X - 1) without carry -> 00 -> 01
Doubling X - 1 can't produce carry when X is just a
decimal digit
- put this zero or one in byte 11d of the buffer, the
significant figures counter, and add it to the number in the
magnitude counter, which presumably is negative
- jump on to PF FRACTN with a pointer to f2, the last
value on the stack.
_2E56_PF_LARGE (X is in full FP format, and so more than
65535d): take 80h/128d from the exponent of iX; now a true
exponent
- if this leaves less than 1Ch/28d jump on to PF MEDIUM
- (iX is more than 2**27 = 134,217,728d, nine or more
decimal digits) call 2DC1 LOG(2**A) to get the number N of
decimal digits; 9 -> 38d
- subtract seven; N - 7
- add the result to the magnitude counter
- negate it; now -(N - 7)
- call 2D4F E TO FP to get iX/10**(N - 7) on the stack
from iX on the stack and -(N - 7) in A; this is a number,
probably not an integer, of eight or possibly seven decimal
digits
- jump back to PF LOOP to go round again; the reduced
number will be split again into integer and fractional parts.
The old fractional part gets lost, but it couldn't be output in
the 14-character format anyway.
_2E8F_PF_MEDIUM (X isn't a small integer or a pure
fraction; X is more than one but less than 2**27. It is
therefore necessary to count how many of the binary digits

651
should be considered as preceding the fractional point. N in
the magnitude counter indicates by how many powers of ten,
if any, X has been reduced; iX is the last value on the stack):
- call 2FBA FETCH TWO with the pointers switched, to get
iX into L'D'E'DE
- set the hi bit of D'; this corrects the mantissa to a
true mantissa
- get the exponent from L'
- subtract 80h/128d; this corrects it to the true
exponent, which is the number of binary digits in iX
- make this the bit counter for PF BITS
_2E7B_PF_BITS (also entered from PF SAVE: there is either
a one-byte iX in D' with a bit counter 08, or
a two-byte iX in D'E' with a bit counter 16d, or
a four-byte iX in D'E'DE with a bit counter showing
the actual number of significant digits): shift D'E'DE left with
zero into the lo bit and the hi bit to carry; this carry
represents the hi binary digit of iX
- get a pointer to the tenth byte of the buffer, mem-4
byte 5; the lo digit of the print buffer
- make a byte counter 05 for PF BYTES.
_2E8A_PF_BYTES: get the byte at the pointer; it
represents two decimal digits, see below, initially both zero
- add it to itself with the carry; this doubles each of
the decimal digits and adds the new binary digit to the lo
decimal digit
- use the DAA "decimal adjust" instruction, with which
my readers are no doubt thoroughly familiar! It is used after
single-byte additions, subtractions, increments or decrements
when the bytes are meant to represent a pair of decimal
digits.
Eg if A is 37h representing 37d, and B is 84h representing 84d,
ADD A,B will produce BBh in A without carry; but if the ADD
A,B is followed by DAA, without disturbing the carry and half-
carry flags, BBh is corrected to 21h with carry, representing
37d + 84d = 121d.
So now the byte in the print buffer represents two

652
decimal digits; originally zero, and corrected each time a new
binary digit is added from iX
- put this byte back at the pointer
- move the pointer back to the next highest byte
- decrement the byte counter and loop back to PF BYTES
until it reaches zero; this adds any carry from a lower byte
into the decimal digits in the higher byte
- (all decimal digits adjusted for the new binary digit)
loop back to PF BITS for the next binary digit
- (up to nine decimal digits are now held in the five lo
bytes of the buffer, two to a byte) zero a byte buffer
- put a source pointer on the sixth byte of the buffer,
the first pair of digits, and a destination pointer on the first
byte of the buffer
- make a digit counter of nine
- use RLD to drop the hi digit; which is zero, there are
never more than nine digits. The RLD instruction rotates
_clockwise:
byte buffer in A
/ \
hi digit of (HL) <- lo digit of (HL)

and signals Z or NZ depending on what goes into the byte


buffer
- make a leading zero flag FF.
_2EA1_PF_DIGITS: RLD again, getting the next digit in the
byte buffer
- if it isn't zero jump on to PF INSERT
- (zero digit) test the leading zero flag
- if it is still FF jump on to PF TEST 2; leading zeroes
aren't put in the buffer.
_2EA9_PF_INSERT: load the single digit to the destination
pointer
- move on the pointer
- increment the significant figures counter and the
magnitude counter
- flag 00 "no leading zeroes now".

653
_2EB3_PF_TEST_2: check the lo bit of the digit counter
- if it is zero jump on to PF ALL 9; the counter is even
- (the counter is odd) move on the source pointer; so on
odd turns RLD puts what was the lo digit in the byte buffer; on
even turns it puts the hi digit in the byte buffer and the lo
digit in the hi position.
_2EB8_PF_ALL_9: loop back to PF DIGITS till nine turns
are complete
- (iX is now loaded as a decimal in the first nine bytes
of the buffer) take nine from the significant figures counter
- if this makes carry jump on to PF MORE; there are less
than nine decimal digits so far, and there may be a fractional
part
- (nine digits already loaded) decrement the significant
figures counter to eight; only eight will actually be output
- check the ninth digit against 04; it makes carry if
the ninth digit is five or more, so carry means "round up
eighth digit"
- jump on to PF ROUND.
_2ECB_PF_MORE (iX has gone into the print buffer, and
there are digits to spare for fX): use the calculator to delete
iX and put fX as last value; it has been in mem-2 since PF
LOOP.
_2ECF_PF_FRACTN (entry also from PF SMALL, when
there was no integer part): switch the pointers and call 2FBA
FETCH TWO to get fX in L'D'E'DE
- get the exponent and subtract it from 80h; this is the
number of binary places the mantissa must be shifted right to
make zero its true exponent, never negative since fX being
less than one has an exponent of 80h or less
- make the exponent zero
- set the hi bit of the mantissa; now the true mantissa
- call 2FDD SHIFT FP to make the right shift in the
mantissa; now the binary representation of fX
_2EDF_PF_FRN_LP (fX is in D'E'DE): get the significant
figures count from the print buffer
- if it is less than eight jump on to PF FR DGT

654
- (eight digits already) rotate the hi bit of fX into
carry
- jump on to PF ROUND; carry means "round up eighth
digit"
_2EEC_PF_FR_DGT (fX in binary form is in D'E'DE, and
there is room for it in digital form in the print buffer): zero
a carry buffer
- make a loop counter two for the main and alternate
registers.
_2EEF_PF_FR_EXX: call 2F8B CA=10*A+C twice, first with
the lo digit and then with the hi digit of the present two
digits of fX, each time putting the lo byte of the result in fX
replacing the old digit; CA=10*A+C multiplies the digit by ten,
adds the carry buffer, and puts the lo byte of the result in A
- this is put in fX replacing the old digit - and the hi in the
carry buffer
- switch the registers and loop back to PF FR EXX
- (after two turns of the loop, fX has been multiplied
by ten; the fractional part replaces fX in D'E'DE, the integer
part is in the carry buffer, and is the next digit to be put in
the print buffer) get a pointer to the start of the print buffer
- add the significant figures counter; now the pointer
is on the first empty digit
- put the new digit in the print buffer at this place
- increment the significant figures counter.
- loop back to PF FRN LP for another decimal digit.
_2F0C_PF_ROUND (entry here from PF ALL 9 and PF FRN
LP, in either case with eight or less digits in the buffer and the
carry flag indicating whether to round up or down): save the
flag
- put a pointer on the first digit in the buffer
- add the significant figures counter; now the pointer
is just after the last significant digit
- make a digit counter equal to the significant figure
counter
- recover the carry.

655
_2F18_PF_RND_LP: move the pointer back one and get the
digit
- add in the carry and put the digit back at the pointer
- if it is zero jump on to PF R BACK; this must be a
trailing zero, so the significant figures count is to be reduced
- compare it with ten, and reverse the carry flag; now
carry means "ten", it can't be more than ten
- if it isn't ten jump on to PF COUNT; this exits from
the loop, there are no more carries.
_2F25_PF_R_BACK (if there is no carry, the last digit
with the carry was a trailing zero; if there is carry the last
digit with the carry was ten, which is also a trailing zero, but
the carry must be added into the preceding digit): count down
the loop counter and loop back to PF RND LP
- (there is_still a carry after all the digits have been
checked; so all the digits were nines): put one in the digit
byte reached
- increment the digit count
- increment the magnitude count in the print buffer.
_2F2D_PF_COUNT: put the digit count in the print buffer
as the significant figures counter; it has been reduced if there
were any trailing zeroes, increased if all the digits were nines
- (now the print buffer is ready for outputting; it
holds digits in bytes one to nine, with the eighth rounded, the
significant figures counter in byte eleven and the magnitude
counter in byte twelve) clear the calculator stack
- recover the saved HL'; the registers aren't exchanged
again
- make a printing counter equal to the significant
figures counter
- make a pointer to the first digit in the buffer
- if the magnitude counter is less than 08 jump on to PF
NOT E
- if it is less than FB/minus 4 jump on to PF E FRMT;
the number is either more than 99,999,999 or less than
0.00001, and must be output in E format.
_2F46_PF_NOT_E (the label is omitted by a misprint: it

656
should be on the "AND A" line): if the magnitude counter is
zero call 15EF OUT CODE to output a zero; no integer part and
no leading zeroes [a minor error, of style rather than
execution:
the initial zero isn't output if there are four or less leading
zeroes, and it really should be. "PRINT 1/7" produces
0.14285714
with an initial zero, "PRINT 1/70" produces .014285714 without
one.]
2F4A_PF_E_SBRN (this section down to the return in PF
DC OUT is also called as a subroutine from PF E FRMT, with 01
as magnitude counter and the original significant figures
counter):
subtract the magnitude counter from zero
- if the result is negative jump on to PF OUT LP; the
counter was positive
- (the magnitude counter is negative) make the positive
result a loop counter and jump on to PF DC OUT.
_2F52_PF_OUT_LP (there are digits to output before the
decimal point): if the significant figures counter has been
counted down to zero jump on to PF OUT DT; the magnitude
counter is still non-zero, so there are trailing zeroes in the
integer part
- (still significant digits to output) read the next
digit from the buffer and move on the pointer
- decrement the significant figures counter.
_2F59_PF_OUT_DT: call 15EF OUT CODE to output the
digit;
it will be a zero if the significant figures counter was zero
- loop back to PF OUT LP counting down the magnitude
counter.
_2F5E_PF_DC_OUT (either the integer part has been output
and the magnitude counter counted down to zero,
or on entry from PF E SBRN there was no integer part
and the magnitude counter indicates leading zeroes to be
output,
or on the second time round, after the jump back to PF

657
OUT LP from PF DEC 0S below, both integer and fractional
parts have been output and both the magnitude counter and
the
significant figures counter have been counted down)
- if the significant figures counter has been counted
down to zero, return; there are no more digits to output
- (more digits to output) increment the loop counter;
there will be one extra turn of the PF DEC 0S loop to output
the decimal point
- load 2E decimal point.
_2F64_PF_DEC_0S: call 0010 PRINT A 1 to output the
decimal point or zero
- load 30h "zero"
- loop back to PF DEC 0S till the magnitude counter
counts down to zero
- (decimal point and leading zeroes output) take the
significant figures counter for a loop counter
- jump back to PF OUT LP to output the digits of the
fractional part.
_2F6C_PF_E_FRMT (entered from PF COUNT with the
magnitude counter showing either more than eight significant
figures or more than four leading zeroes):
- decrement the magnitude to get the decimal exponent,
the number after E; eg 100,000,000, nine digits, is 1E+8, while
0.000001, five leading zeroes, is 1E-6
- call 2F4A PF E SBRN with magnitude count one; it
prints the number before E, always with one digit before its
decimal point
- load 45h "E"
- call 0010 PRINT A 1 to output it
- check the exponent
- if it is positive jump on to PF E POS
- (negative exponent) negate it
- load 2D "minus sign"
- jump on to PF E SIGN.
_2F83_PF_E_POS: load 2B +
_2F85_PF_E_SIGN: call 0010 PRINT A 1 to output the + or -

658
- exit into 1A1B OUT NUM 1 (misprinted OUT NUM); it can
output any integer up to 9999d.
Exit: RETs at PF NEGTVE if the number is zero and at PF
DC OUT
- to 1A1B OUT NUM 1 from PF E SIGN if E format is used.
Output parameters: none
- the calculator stack has been cleared; except,
unfortunately, if PF SMALL is used - see 361F str$.
Called from:
361F str$
Exit from:
2024 PR ITEM 3
Rems:
2DC1 LOG(2**A) calculates no of digits in integer part
2F8B CA=10*A+C converts FP fractions to digits

PRINTING CHARACTER IN A BASIC LINE SUBROUTINE see


1925 OUT SP 2

printing on screen or ZX printer


Although we usually call it "printing", what actually
happens to the bytes "printed" depends on which channel is
currently open: they may go to the main or lower screen, to
the ZX printer, to the work space, to the network, etc. See
upper screen, printer, channel R.
There are two more or less distinct kinds of routine in
the ROM concerned with printing or_output:
- the general-purpose output restart 0010 PRINT A 1, and
the normal channel output routine 09F4 PRINT OUT, which
handle listings of BASIC as well as the output of computed
expressions
- the 1FCD PRINT command routine, which is concerned
with transforming_print_items in or computed by the BASIC
program -
colour and position controls, string and numeric expressions -
into forms suitable for output, and uses the general-purpose
routines for actual output.

659
The distinction is important eg in the handling of
position and colour controls: in PRINT OUT, an expression
such
as "AT X,Y; INK Z" will be output as a string, with the tokens
expanded but without any attempt to execute the
instructions; in PRINT, the tokens are converted to control
codes before being output through PRINT OUT, which
therefore executes them in this case.
In the following REM list the command and control
routines, as opposed to the general-purpose output routines,
are marked with an asterisk.*
*078A LD TYPE message printed on screen
*07A6 LD NAME loop to print filename
*07AD LD CH PR print one character of filename
09F4 PRINT OUT start of main output routines
0A3D PO RIGHT equivalent to PRINT OVER 1; CHR$ 32
0A4F PO ENTER carriage return works differently on
screen and ZX printer
0A69 PO QUEST prints '?' for non-printable characters
0AAC PO AT ERR checks for scroll in lower screen
0AD0 PO SPACE print D spaces for TAB
0AD9 PO ABLE calls PO ANY to print characters
0B24 PO ANY calls appropriate routines for different
character types
0B5F PO T prints tokens
0B7F PR ALL prints character from character form
0BB7 PR ALL 4 prints eight pixels of character
0BC1 PR ALL 5 set attribute byte after printing
0C0A PO MSG prints messages/tokens from lists
0C14 PO TABLE finds message/token in table
0C22 PO EACH prints each character of message/token
0C3B PO SAVE prints character while saving registers
0C88 PO SCR 2 prints "scroll?" message
0EF4 COPY LINE printing to ZX printer
0EFD COPY L 1 if BREAK pressed, empty buffer and stop
0F1E COPY L 5 single bits sent to ZX printer
0FA9 ED EDIT copies BASIC line to editing area

660
111D ED COPY copies line in editing area to lower screen
1150 ED BLANK lines filled out with blanks
115E ED SPACES prints each blank
*1219 RAM SET prints copyright message
17F5 LLIST opens channel for ZX printer
1835 LIST ALL loop to print whole lines of BASIC
1855 OUT LINE prints BASIC line with line number
1865 OUT LINE1 prints line number and line cursor
1881 OUT LINE3 set signals to print in K or L mode
1894 OUT LINE4 loop to print each char in BASIC line,
and error marker
18A1 OUT LINE5 print cursor if required
18B4 OUT LINE6 line print of BASIC complete
18C1 OUT FLASH prints cursor or error cursor
18E1 OUT CURS checks if C E G K L cursor to be printed
18F3 OUT C 1 selects C K or L for cursor print
1909 OUT C 2 prints cursor with flash
1925 OUT SP 2 start of routine to print individual
characters in BASIC line
192B OUT SP 1 prints line number digit or leading space
1937 OUT CHAR prints each character except line
numbers
195A OUT CH 1 signals quote mode on/off
196D OUT CH 2 print character and loop
1A1B OUT NUM 1 prints numbers 0-9999 from BC register
1A2B OUT NUM 2 prints numbers 0-9999 from (HL)
1A30 OUT NUM 3 prints integer in HL
1A42 OUT NUM 4 prints last digit
*1FC3 UNSTACK Z skips printing in syntax checks
*1FC9 LPRINT signals channel for ZX printer
*1FCD PRINT signals channel for screen output
*1FCF PRINT 1 opens channel, sets temp colours
*1FDF PRINT 2 checks for last print item
*1FE5 PRINT 3 checks for position and colour items
*1FFC PR ITEM 1 handles AT and parameters
*201E PR AT TAB outputs AT or TAB and parameters
*2024 PR ITEM 3 sorts out colour items, numeric and

661
string
expressions
*2045 PR END 2 checks end of printing
*21AF IN NEXT 1 handles position/colour controls in
INPUTs
*2DC1 LOG(2**A) calculates printing length of FP number
*2DE3 PRINT FP start of routine to print numbers from
calculator stack
*2DF2 PF NEGTVE prints minus sign
*2E24 PF SMALL digits for printing stored in buffer
*2E56 PF LARGE large numbers reduced to printable size
*2EA9 PF INSERT digits put in buffer for printing
*2F0C PF ROUND maximum of 8 digits for printing,
rounded
*2F18 PF RND LP final zero or tenth digit not printed
*2F2D PF COUNT B counts digits to be printed
*2F46 PF NOT E prints initial zero if required
*2F4A PF E SBRN prints digits before and after dec point
*2F52 PF OUT LP picks digits from print buffer
*2F59 PF OUT DT print digit from buffer
*2F5E PF DC OUT is dec point necessary?
*2F64 PF DC 0S print dec point and/or zeroes
*2F6C PF E FRMT print 'E' and prepare for exponent
*2F85 PF SIGN print '+' or '-' and exponent
*361F str$ uses PRINT FP to output number to work space

print items see colours, control characters, printing

PRINT ITEMS SUBROUTINE see 1FFC PR ITEM 1

PRINT OUT subroutine 09F4 see also printing


The output routine for all channels except the R
channel; output will be to the upper or lower screen, or to the
ZX printer, depending on the state of the flags set by 1615
CHAN FLAG. It is therefore the routine usually called through
restart 0010 PRINT A 1, but there are also two direct calls for
specific purposes.

662
The manoeuvres of PO 2 OPER, PO 1 OPER, PO CHANGE
and PO CONT require some explanation: they handle the
output of AT, TAB and the colour control codes.
AT and TAB codes are each followed by two parameters,
eg
16h 03 11h; AT 3,17 or
17h 19h 00; TAB 25.
Similarly colour control codes are each followed by one
parameter, eg
10h 07; INK 7 or
14h 01; INVERSE 1.
Output of an AT or TAB control is handled by 201E PR AT
TAB in the 1FCD PRINT command routine; it calls the output
routine 0010 PRINT A 1 three times, once with the control
code and the second and third times with the parameters.
Similarly output of colour controls is handled by 21FC CO
TEMP 4, which calls the output routine 0010 PRINT A 1 twice,
once with the control code and the second time with the
parameter.
The three codes of AT or TAB, or the two of a colour
control, are collected before any execution is performed and
executed all at once by PO CONT. PO CONT is entered with
- the control code in 5C0E TVDATA lo: 10h for INK, 11h
for PAPER, ... etc, 16h for AT or 17h for TAB
- the first of two parameters, if there are two, in 5C0F
TVDATA hi: the line number of AT, the lo byte of TAB
- the second of the two AT/TAB parameters, or the sole
parameter of the colour controls, in the A register: the column
number of AT, the hi byte of TAB, the colour number for INK/
PAPER or the on/off signal for the other colour controls.
The collection of these input parameters is achieved by
manipulating the channel output address. 0010 PRINT A 1
doesn't itself execute output, it merely calls the output
address; if another address is substituted, the next call to
PRINT A 1 will execute the routine at that address. So for
example in executing output of AT
- the first call to PRINT A 1 enters PO 2 OPER, through

663
the control character table in PRINT OUT, with code 16h/22d
AT.
PO 2 OPER, PO TV 1 and PO CHANGE put this code in 5C0E
TVDATA lo and put the address of PO TV 2 in the output
channel
- thus the second call to PRINT A 1 enters PO TV 2
directly, not through the table, and this time with the code of
the first AT parameter. PO TV 2 and PO CHANGE put this code
in 5C0F TVDATA hi and change the output address again, this
time to PO CONT
- so the third call to PRINT A 1 enters PO CONT directly,
now with the code of the second AT parameter and with the
other two codes in TVDATA. PO CONT changes the output
address back to normal, 09F4 PRINT OUT, before proceeding
to execute the AT with a full set of parameters.
Execution of TAB is just the same; execution of the one-
parameter colour controls is the same except that initially
execution is directed to PO 1 OPER instead of PO 2 OPER, with
the effect that the middle call to PO TV 2 is left out.
The section from 2211 CO TEMP 5 to 2287 CO TEMP E is
described here, following 0AD0 PO SPACE: although located
in a quite different part of the ROM, it is actually part of the
PRINT OUT routine. This is the routine which executes the
colour controls INK to OVER, by making whatever changes are
called for in 5C8F ATTR T/MASK T/P FLAG. ATTR T holds the
attributes to be used for the next screen output; MASK T has
zero bits in the positions which are to be changed in the
screen attributes; P FLAG signals INVERSE, OVER, PAPER 9
and INK 9. These svs are changed by three calls to CO
CHANGE, which performs an XOR/AND/XOR on the value in
each of the svs in turn, using a setting byte in A and a mask in
B register; the bulk of the routine is concerned with
preparing the setting bytes and masks. See under
colours, masks.
[There are two errors in the PRINT OUT routine, though
fortunately their effects are seldom encountered:

664
1. 0A23 PO BACK 1 contains what is perhaps the most
serious error in the Spectrum ROM: it doesn't just give wrong
responses, it can actually crash programs and damage the
user's variable records. The description given under PO BACK
1 below explains what_ought to happen, but in fact for
checking moves "off screen" the ROM compares the line
number with 18h instead of
19h: the ninth instruction in the section is LD A,18 when it
ought to be LD A,19.
The effect can be seen by the following test program.
Don't try this if you have anything valuable in the computer!
10 CLS: PRINT AT X,0; CHR$ 8 + "Miles"
If you run this with X = 1, the backspace code CHR$ 8 is
ignored, and "Miles" is printed at the start of the second
screen line. This is what ought to happen at the top screen
line, X = zero.
If you run it with X = zero, the backspace isn't ignored
as it should be: "iles" is printed at the start of line zero.
What happens to the "M" is that it is output eventually
to PR ALL with a "screen address" in HL which was calculated
from the "impossible" position value 1902h by 0E9B CL ADDR
called from 0DD9 CL SET. None of these subroutines make
any check on impossible position values. The "screen address"
from CL ADDR comes out as 58E0h, which is read as the "first
pixel byte" on screen by PR ALL, and so it pokes the eight
character pixel rows of "M" into
58E0 - an attribute
59E0 - " "
5AE0 - " "
5BE0 - the printer buffer
5CE0 - spare RAM
5DE0 - " "
5EE0 - " "
5FE0 - " "
Next it calls 0BDB PO ATTR which finds the "attribute

665
address" corresponding to this "screen address": it will be
5BE0 again - in the fourth third of the screen! - so no further
harm is done when the attribute byte is poked into it.
The first four of these addresses are harmless enough,
but the other four can do incalculable damage, since they are
all too likely to be in your BASIC program and/or variables
record: a fairly common effect, with a short BASIC program, is
to produce the "Variable not found" error whenever you try to
use any variable, even if you give them new values.
2. As the notes indicate, 0A3D PO RIGHT ought to exit
through PO STORE, to update the position value svs. As it is, if
the routine is ever used - which seems unlikely! - it will be
ineffective, as the next print will overwrite the code just
stepped over.]
Input parameters: a character code in A.
Action: call 0B03 PO FETCH to get the print position
- if the character code is 20h or more jump on to PO
ABLE; a "printable" code, ie space, a symbol, a digit or letter, a
graphic or a token
- if it is less than 06 or more than 17h jump on to PO
QUEST; these codes are meaningless in output
- (that leaves 06 PRINT comma, 08 cursor left, 09 cursor
right, 0D newline, and the colour and AT/TAB controls 10h ->
17h; the rest, 07 and 0A -> 0C, are meaningless and are
handled by PO QUEST through the table) add the code to the
base address 0A0B of the control character table at 0A11
- add the offset at that address; this computes the
address of the executive routine
- stack the address
- jump to PO FETCH, to get the position values again, as
HL has been used; it will return to the computed address.
_0A23_PO_BACK_1 (entered from the control character
table to execute the backspace, code 08; cf 1007 ED LEFT,
used in editing to move the cursor back): increment the print
position column number
- if it isn't now 22h/34d, jump on to PO BACK 3; the
position isn't at a line start

666
- (the last print was at a line start, column 21h/33d)
if bit 1 of FLAGS is set jump on to PO BACK 2; output is to the
ZX printer, which cannot handle the backspace at line start
- (not ZX printer) increment the line number; ie go up
one line
- make the column number 02; as if the last print was on
the last but one column
- if the new line number isn't 18h/24d jump on to PO
BACK 3; moving up a line goes off screen [this should have
been 19h/25d, see above]
- (the print position has gone off screen) decrement the
line number; ie refuse to accept the backspace.
_0A38_PO_BACK_2 (the backspace isn't accepted): restore
the column number to line start.
_0A3A_PO_BACK_3: exit into 0DD9 CL SET and 0ADC PO
STORE, which record the new position values in svs.
_0A3D_PO_RIGHT (entered from the control character
table to execute code 09 "print position right"; it is impossible
to get this code into BASIC with any kind of keystroke, but it
can be included in print strings from m/c and could be input
from peripherals, for what it is worth): save the current P
FLAG value
- set P FLAG to 01; OVER 1
- load a space and call 0B65 PO CHAR to output it;
instead of manipulating the column position directly, as in PO
BACK 1 for left movement, the ROM takes the simpler course
of printing an "invisible" space by using OVER 1, which has
exactly the desired effect [except for the error noted above]
- restore the original P FLAG value and return.
_0A4F_PO_ENTER (entered from the control character
table to execute code 0D, newline. Quite different from 1024
ED ENTER, which handles the ENTER key during editing): if
bit 1 of FLAGS is set jump on to 0ECD COPY BUFF; output is
to the ZX printer.
This empties the printer buffer
- (not ZX printer) load the column number 21h/33d;
signalling that the last print position was at line start

667
- call 0C55 PO SCR to check for screen scrolling and
execute it if necessary
- decrement the print position line number; down one
line
- exit into 0DD9 CL SET, which finds the display address
of the new print position, and 0ADC PO STORE which sets the
position value svs.
_0A5F_PO_COMMA (entered from the control character
table to execute code 06, PRINT comma; cf 204E PR POSN 1
which changes a comma code 2C in a PRINT command to a
PRINT comma 06 before outputting it): call 0B03 PO FETCH
to get the current print column number; as the notes point
out, this is unnecessary, because this subroutine is actually
entered from PO FETCH, by the indirect jump from PRINT
OUT. However, this call means that PO COMMA could be
called as a subroutine from m/c
- decrement the column number twice
- AND it with 00010000b/10h; if it was 11h or less this
comes out as 00, if 12h or more as 10h
0000xxxxb/0xh AND 10h = 00
0001xxxxb/1xh AND 10h = 10h
- exit to PO FILL, which outputs the correct number of
spaces to TAB on to a half-line start.
_0A69_PO_QUEST (entered from PRINT OUT or the
control character table to execute the meaningless codes 00
-> 05, 07,
0A -> 0C, 0E, 0F or 18h -> 1Fh): load 3F ?
- exit to PO ABLE to output the "?".
_0A6D_PO_TV_2 (entered only by an indirect jump from
PO 2 OPER through PO CHANGE, see above): load the address
of PO CONT
- store the character code in 5C0F TVDATA hi; the first
parameter of an AT or TAB control
- exit to PO CHANGE, which continues output of AT/TAB
and their parameters.

668
_0A75_PO_2_OPER (entered from the control character
table to execute codes 16h and 17h, the AT and TAB control
codes):
load the address of PO TV 2; the next call to PRINT A 1 will go
there, see above
- jump on to PO TV 1.
_0A7A_PO_1_OPER: (entered from the control character
table to execute the colour control codes): load the address of
PO CONT; the next call to PRINT A 1 will go there, see above.
_0A7D_PO_TV_1: save the control code in 5C0E TVDATA lo;
this is either AT/TAB or a colour control.
_0A80_PO_CHANGE (entered here and from PO TV 2 with
the address of PO CONT, but from PO 2 OPER with the
address of PO TV 2, and also called as a subroutine from PO
CONT with the address of PRINT OUT): get the address of 5C51
CURCHL; the first two bytes are the output address
- load the two bytes of the next output address there
- return.
_0A87_PO_CONT (entered with a full set of parameters,
three codes for an AT/TAB or two for a colour control): load
the address of PRINT OUT
- call PO CHANGE to restore it to the output channel
- get the parameter(s) from 5C0E TVDATA
- if the control code in 5C0E TVDATA lo is less than
16h/22d jump on to 2211 CO TEMP 5; colour controls. CO
TEMP 5, misprinted CO TEMPS in "ROM Disassembled", is
described below, after PO SPACE
- if the code is 17h/23d TAB jump on to PO TAB
- (AT) make a two-byte print position, hi byte line
number and lo byte column number, from the AT parameters
- subtract the column number, which is that given in the
BASIC command, from 1Fh/31d; so instead of running from
zero to 31d it now runs from 1Fh/31d to zero
- if this makes a carry jump on to PO AT ERR
- add 02; see DISPLAY AREA, where it is explained how

669
the print position column number takes values from 21h on
the left margin to 02 on the right. There is a misprint in the
notes, +21 - +22 should read +21 - +2
- if bit 1 of FLAGS is set jump on to PO AT SET; the ZX
printer takes no account of line numbers
- (screen output) subtract the line number from 16h/22d;
so instead of running from zero to 21d as in the AT command
it runs from 16h/22d to one.
_0AAC_PO_AT_ERR: if carry is set report "Integer out of
range"; AT called for a tab 32d or more, or a line 22d or more
- add 02 in two moves; the second gives the normal
position value line number, see DISPLAY AREA, but the first
value, one less, is used for a check in a moment
- if bit zero of TV FLAG is set exit to 0C55 PO SCR;
output is to the lower screen. An AT control in the lower
screen may require scrolling; it could appear in an INPUT ...
AT, or in a PRINT (hatch)
- (upper screen) if the line number less one is less
than the number in 5C6B DF SZ report "Out of screen"; DF SZ
holds the position value line number of the top of the lower
screen, and if the new line number isn't more than this the
upper screen is going to overlap the lower.
_0ABF_PO_AT_SET (AT execution completed) exit to 0DD9
CL SET to fix the position value svs.
_0AC2_PO_TAB (TAB; the hi byte or second byte of the
parameter input by BASIC is ignored, and the lo byte is
reduced "modulo 32d", ie its remainder on dividing by 32d is
used. Eg
TAB 73 is interpreted as TAB 9
TAB 567 = 55 + 2 * 256d is interpreted as TAB 23):
- get the first parameter; the lo byte. It came from
5C0F TVDATA hi in PO CONT
_OAC3_PO_FILL (this is also the exit routine from PO
COMMA; in either case the A register holds a tab position):
call 0B03 PO FETCH to get the last column number; say C
- add the tab; say T + C
- decrement the result; T + C - 1

670
- AND it with 00011111b/1Fh to get the result modulo
20h/32d; the present tab position is 21h minus the print
position column number C, so the number of spaces required
to execute the TAB command is T - (21h - C) = T + C - 1 (- 20h).
The -20h disappears in the modulo operation
- if the result is zero return; no spaces need to be
printed
- (spaces needed) make a counter for the number of
spaces; the D register is used so as not to disturb the position
values in
- set bit zero of FLAGS; "no leading space before a
following token".
_0AD0_PO_SPACE: make a space byte and call 0C3B PO
SAVE
to output it; the alternate registers are in use throughout the
PRINT OUT routine
- decrement the counter and loop back to PO SPACE till
it reaches zero
- return.
_2211_CO_TEMP_5 (although in a quite different part of
the ROM, this routine logically belongs here; it is entered only
from PO CONT, to handle colour controls): subtract 11h/17d
from the control code; they ran from 10h/16d INK to 15h/21d
OVER
- add back the carry; now both INK and PAPER are zero,
but with carry for INK, and FLASH to OVER are 01 -> 04
- if the result is zero jump on to CO TEMP 7; INK and
PAPER
- subtract 02 and add back the carry; now
FLASH is zero with carry
BRIGHT is zero without carry
INVERSE is 01, OVER is 02
- if the result is zero jump on to CO TEMP C; FLASH and
BRIGHT
- (INVERSE and OVER) check whether the result is 01
- get the on/off parameter; it came from BASIC, zero for
off or 01 for on

671
- make a mask 00000001b; this will control bit zero in P
FLAG, which controls OVER, using the on/off parameter
which is also in bit zero as a setting byte
- if the control code came to 02 jump on to CO TEMP 6;
OVER
- (INVERSE) rotate the on/off parameter twice left; now
it is in bit 2, making a setting byte
- make the mask 00000100b/04; to control bit 2 in P
FLAG, which controls INVERSE
_2228_CO_TEMP_6: if the parameter as it came from BASIC
was more than one report "Invalid colour"
- (zero or one are OK) put a pointer on P FLAG
- jump on to CO CHANGE.
_2234_CO_TEMP_7 (INK and PAPER, with carry set for
INK):
get the colour parameter; it is already a setting byte for INK
- make a mask 00000111b/07 for INK
- if carry was set jump on to CO TEMP 8
- (PAPER) rotate the colour parameter 3 places left; to
the PAPER position, making a setting byte for PAPER
- make the mask 00111000b/38h for PAPER.
_223E_CO_TEMP_8: if the parameter as it came from BASIC
was more than nine report "Invalid colour".
_2240_CO_TEMP_9: put a pointer on 5C8F ATTR T
- if the colour parameter is zero -> 7 jump on to CO
TEMP B
- (colours 8 and 9) put the value of ATTR T in the
setting byte
- if the colour is 8 jump on to CO TEMP A
- (colour 9) OR the setting byte with the mask; making
colour 7 WHITE
- reverse the setting byte and AND the result with
00100100b/24h; this will give zero if the hi bit of the colour
is one, ie GREEN, CYAN, YELLOW or WHITE
- if it gives zero jump on to CO TEMP A; the setting
byte is now zero, colour BLACK
- (BLACK, BLUE, RED, MAGENTA) copy the mask to the

672
setting byte; colour WHITE
Perhaps some examples will help:
INK 9 (mask 00000111b)
ATTR T is blue INK (001) on cyan PAPER (101b/5)
XX101001 -> XX101111 -> YY010000 -> 00000000, so use
00000000b
to get black INK XX101000b
ATTR T is cyan INK (101b/5) on blue PAPER (001)
XX001101 -> XX001111 -> YY110000 -> 00100000, so use
00000111b
and get white INK XX001111b.
PAPER 9 (mask 00111000b)
ATTR T is blue INK (001) on cyan PAPER (101b/5)
XX101001 -> XX111001 -> YY001110 -> 00000100, so use
00111000b
and get white PAPER XX111001b
ATTR T is cyan INK (101b/5) on blue PAPER (001)
XX001101 -> XX111101 -> YY000010 -> 00000000, so use
00000000b
to get black PAPER XX000101b.
_2257_CO_TEMP_A and_2258_CO_TEMP_B: call 226C CO
CHANGE to correct 5C8F ATTR T
- make a mask 00000111b/07
- subtract from it the colour parameter from BASIC;
making carry for colours 8 and 9
- subtract it from itself with the carry; this makes a
setting byte for 5C90 MASK T which is all zeroes for colours
zero to 7, all ones for 8 or 9. The PAPER or INK mask for MASK
T is the same as for 5C8F ATTR T
- call CO CHANGE again to correct 5C90 MASK T; it is the
next byte after 5C8F ATTR T
- shift the mask last used left twice; it was 00111000b
or 00000111b, so this makes 11100000b or 00011100b
- AND the result with 01010000b/50h; making
01000000b for PAPER or 00010000b for INK. These are the
correct masks for PAPER 9 and INK 9 in P FLAG
- subtract the colour parameter from BASIC from 08;

673
making carry for 9 only
- subtract the result from itself with the carry; as
before, this makes a setting byte for P FLAG 00000000b if
the colour is not 9 or 11111111b if it is
- exit into CO CHANGE; to correct P FLAG.
_[226C_CO_CHANGE, which comes next, is a viable m/c
subroutine so is given a description under its own name in
this index, though in ROM it is only called or entered from this
section.]
_2273_CO_TEMP_C (FLASH and BRIGHT, with carry for
FLASH):
SBC A copies the carry flag to the Z/NZ flag; now NZ flags
FLASH
- rotate the parameter from BASIC; the on/off parameter
goes to the hi bit, making a correct setting byte for FLASH
- make a mask 10000000b/80h; correct for FLASH
- if the flag shows NZ jump on to CO TEMP D
- (BRIGHT) rotate the parameter again; now the on/off
parameter is in bit 6, correct for BRIGHT
- remake the mask as 01000000b/40h; for BRIGHT.
_227D_CO_TEMP_D: if the parameter from BASIC is 08
jump on to CO TEMP E; FLASH 8 and BRIGHT 8 are good
syntax, but the set bit will have no effect on 5C8F ATTR T,
because the rotations have not moved it to the "hole" in the
mask
- if it is not zero or one, report "Invalid colour".
_2287_CO_TEMP_E: put a pointer on 5C8F ATTR T and call
CO CHANGE to correct it
- rotate the setting byte three times right and exit
into CO CHANGE to correct 5C90 MASK T; if the parameter
was 8, this will set the bit in MASK T, otherwise not.
_0AD9_PO_ABLE (entered either direct from PRINT OUT
or from PO QUEST, with any printable code from space
upwards): call PO ANY to output the code; PO ANY is
described below.
_0ADC_PO_STORE (exit here after any change in the print
position due to cursor moves, position controllers, scrolling

674
or printing of characters, to put the new values in the system
variables relating to the screen or printer: BC holds line and
column numbers of the_last print position, HL the pixel
address of the first byte of the next character position) if bit 1
of FLAGS is set jump on to PO ST PR; output to the ZX printer
- if bit zero of TV FLAG is set jump on to PO ST E;
output to the lower screen
- (upper screen) put the position values in 5C88 S POSN
- put the pixel address in 5C84 DF CC
- return.
_0AF0_PO_ST_E (lower screen): put the position values in
5C8A S POSNL and 5C82 ECHO E
- put the pixel address in 5C86 DF CCL
- return.
_0AFC_PO_ST_PR (ZX printer): put the column number in
5C7F P POSN; there is no line number
- put the pixel address in 5C80 PR CC
- return.
_0B03_PO_FETCH (a free-standing subroutine, but only
called in the PRINT OUT block: from PRINT OUT, PO COMMA -
an unnecessary call - PO FILL, PO ANY; also the apparent exit
from PRINT OUT, but its return is to another PRINT OUT
routine): if bit 1 of FLAGS is set jump on to PO F PR; output to
the ZX printer
- get the upper screen print position from 5C88 S POSN
and the display address from 5C84 DF CC
- if bit zero of TV FLAG is not set, return; output to
the upper screen
- (lower screen) get the print position from 5C8A S
POSNL and the display address from 5C86 DF CCL
- return.
_0B1D_PO_F_PR (ZX printer): get the column number from
5C7F P POSN and the print buffer address from 5C80 PR CC
- return.
_0B24_PO_ANY (formally a free-standing subroutine, but
only called from PO ABLE; A holds a code to be output): if the
code is less than 80h/128d jump on to PO CHAR; a single

675
character
- if the code is more than 8Fh/143d jump on to PO
T&UDG; a token or user-defined graphic
- (the code is one of the keyboard graphics 80h/128d to
8Fh/143d) call PO GR 1 to construct the graphic form in the
calculator memory area
- call PO FETCH to recover the print position parameters
- put a pointer on the graphic form just constructed
- jump on to PR ALL.
_0B38_PO_GR_1 (formally a free-standing subroutine, but
only called from PO ANY; it constructs any of the "graphic
forms", character codes 80h/128d to 8Fh/143d. These are not
stored as "character forms", like the alphanumeric characters,
since it is relatively easy to "draw" them whenever they are
required.
All the forms consist of a byte repeated four times to
make the upper half of the form and then another byte
repeated four times to form the lower half: each of these bytes
is always either 00h, 0Fh, F0h or FFh.
The 10h/16d forms can be tabulated as follows: the upper
line shows the last four bits of the character code, the lower
shows the byte used in the_lower half of the character
followed by the byte used in the upper half:
0000 0001 0010 0011 0100 0101 0110 0111
0000 000F 00F0 00FF 0F00 0F0F 0FF0 0FFF

1000 1001 1010 1011 1100 1101 1110 1111


F000 F00F F0F0 F0FF FF00 FF0F FFF0 FFFF
The correspondence is obvious):
- put a pointer on 5C92 MEMBOT; the calculator memory
area of 30d bytes
- call PO GR 2 and immediately exit into it; thus going
through it twice and then returning into PO ANY.
_0B3E_PO_GR_2 (formally a subroutine, but never called
except at this point): rotate the character code right into the
carry flag; on the first run this sets carry for odd character
codes, which have F as last digit of their lo byte, see the table
above. On the second run the character code has already been

676
shifted twice, so carry is set by what started out as bit 2, and
codes with bit 2 set have F as last digit of their hi byte
- take the code from itself with the carry; making 00
for even codes or FF for odd, depending on the carry
- AND the result with 0F; making 00 for even, 0F for
odd, the lo nibble of the byte
- rotate and subtract the character code again
- AND this time with F0; making 00 for even, F0 for odd,
the hi nibble of the byte
- OR the two nibbles together to make the whole byte.
- make a counter 04; the byte is used four times.
_0B4C_PO_GR_3: put the byte just formed into the next
location of the memory area
- move on the pointer
- reduce the counter
- if it is not zero loop back to PO GR 3
- (four bytes loaded) return; the first time the routine
is called, PO GR 2 is immediately entered again with the lo
byte of the character code already shifted four bytes right,
producing the byte for the lower half of the graphics
character.
The second time, this is a return into PO ANY.
_0B52_PO_T&UDG (entered from PO ANY if the code is a
token or user-defined graphic): subtract A5h/165d from the
code
- if this does not give carry jump on to PO T; the code
is 165d or more, a token
- (a user-defined graphic) add back 15h/21d; now the
code is zero -> 14h/20d; 0Fh in the notes is incorrect
- put a pointer on the address in 5C7B UDG; this is the
base address for the UD graphics
- jump on to PO CHAR 2 to print the character.
_0B5F_PO_T (tokens): call 0C10 PO TOKENS to print out
the token
- jump back to PO FETCH to exit; the display address in
HL has been overwritten.
_0B65_PO_CHAR (output a single character code from the

677
standard set; this entry point is also called as a subroutine
from PO RIGHT, to move the cursor right by printing an
"invisible space"): get the "base address of the character set"
from 5C36 CHARS; this base is 100h/256d bytes before the
start of the character set, so code 20h/32d lands on the first
byte of the set, which is also the first byte of the form for 20h
"space", and other codes correspondingly
_0B6A_PO_CHAR_2: zero bit zero of FLAGS; "print the next
token with a leading space"
- if the present code is not space jump on to PO CHAR 3
- (it is space) set bit zero of FLAGS; no leading space
after a space.
Perhaps some examples will help:
INK 9 (mask 00000111b)
ATTR T is blue INK (001) on cyan PAPER (101b/5)
XX101001 -> XX101111 -> YY010000 -> 00000000, so use
00000000b
to get black INK XX101000b
ATTR T is cyan INK (101b/5) on blue PAPER (001)
XX001101 -> XX001111 -> YY110000 -> 00100000, so use
00000111b
and get white INK XX001111b.
PAPER 9 (mask 00111000b)
ATTR T is blue INK (001) on cyan PAPER (101b/5)
XX101001 -> XX111001 -> YY001110 -> 00000100, so use
00111000b
and get white PAPER XX111001b
ATTR T is cyan INK (101b/5) on blue PAPER (001)
XX001101 -> XX111101 -> YY000010 -> 00000000, so use
00000000b
to get black PAPER XX000101b.
_2257_CO_TEMP_A and_2258_CO_TEMP_B: call 226C CO
CHANGE to correct 5C8F ATTR T
- make a mask 00000111b/07
- subtract from it the colour parameter from BASIC;
making carry for colours 8 and 9
- subtract it from itself with the carry; this makes a

678
setting byte for 5C90 MASK T which is all zeroes for colours
zero to 7, all ones for 8 or 9. The PAPER or INK mask for MASK
T is the same as for 5C8F ATTR T
- call CO CHANGE again to correct 5C90 MASK T; it is the
next byte after 5C8F ATTR T
- shift the mask last used left twice; it was 00111000b
or 00000111b, so this makes 11100000b or 00011100b
- AND the result with 01010000b/50h; making
01000000b for PAPER or 00010000b for INK. These are the
correct masks for PAPER 9 and INK 9 in P FLAG
- subtract the colour parameter from BASIC from 08;
making carry for 9 only
- subtract the result from itself with the carry; as
before, this makes a setting byte for P FLAG 00000000b if
the colour is not 9 or 11111111b if it is
- exit into CO CHANGE; to correct P FLAG.
_[226C_CO_CHANGE, which comes next, is a viable m/c
subroutine so is given a description under its own name in
this index, though in ROM it is only called or entered from this
section.]
_2273_CO_TEMP_C (FLASH and BRIGHT, with carry for
FLASH):
SBC A copies the carry flag to the Z/NZ flag; now NZ flags
FLASH
- rotate the parameter from BASIC; the on/off parameter
goes to the hi bit, making a correct setting byte for FLASH
- make a mask 10000000b/80h; correct for FLASH
- if the flag shows NZ jump on to CO TEMP D
- (BRIGHT) rotate the parameter again; now the on/off
parameter is in bit 6, correct for BRIGHT
- remake the mask as 01000000b/40h; for BRIGHT.
_227D_CO_TEMP_D: if the parameter from BASIC is 08
jump on to CO TEMP E; FLASH 8 and BRIGHT 8 are good
syntax, but the set bit will have no effect on 5C8F ATTR T,
because the rotations have not moved it to the "hole" in the
mask
- if it is not zero or one, report "Invalid colour".

679
_2287_CO_TEMP_E: put a pointer on 5C8F ATTR T and call
CO CHANGE to correct it
- rotate the setting byte three times right and exit
into CO CHANGE to correct 5C90 MASK T; if the parameter
was 8, this will set the bit in MASK T, otherwise not.
_0AD9_PO_ABLE (entered either direct from PRINT OUT
or from PO QUEST, with any printable code from space
upwards): call PO ANY to output the code; PO ANY is
described below.
_0ADC_PO_STORE (exit here after any change in the print
position due to cursor moves, position controllers, scrolling
or printing of characters, to put the new values in the system
variables relating to the screen or printer: BC holds line and
column numbers of the_last print position, HL the pixel
address of the first byte of the next character position) if bit 1
of FLAGS is set jump on to PO ST PR; output to the ZX printer
- if bit zero of TV FLAG is set jump on to PO ST E;
output to the lower screen
- (upper screen) put the position values in 5C88 S POSN
- put the pixel address in 5C84 DF CC
- return.
_0AF0_PO_ST_E (lower screen): put the position values in
5C8A S POSNL and 5C82 ECHO E
- put the pixel address in 5C86 DF CCL
- return.
_0AFC_PO_ST_PR (ZX printer): put the column number in
5C7F P POSN; there is no line number
- put the pixel address in 5C80 PR CC
- return.
_0B03_PO_FETCH (a free-standing subroutine, but only
called in the PRINT OUT block: from PRINT OUT, PO COMMA -
an unnecessary call - PO FILL, PO ANY; also the apparent exit
from PRINT OUT, but its return is to another PRINT OUT
routine): if bit 1 of FLAGS is set jump on to PO F PR; output to
the ZX printer
- get the upper screen print position from 5C88 S POSN
and the display address from 5C84 DF CC

680
- if bit zero of TV FLAG is not set, return; output to
the upper screen
- (lower screen) get the print position from 5C8A S
POSNL and the display address from 5C86 DF CCL
- return.
_0B1D_PO_F_PR (ZX printer): get the column number from
5C7F P POSN and the print buffer address from 5C80 PR CC
- return.
_0B24_PO_ANY (formally a free-standing subroutine, but
only called from PO ABLE; A holds a code to be output): if the
code is less than 80h/128d jump on to PO CHAR; a single
character
- if the code is more than 8Fh/143d jump on to PO
T&UDG; a token or user-defined graphic
- (the code is one of the keyboard graphics 80h/128d to
8Fh/143d) call PO GR 1 to construct the graphic form in the
calculator memory area
- call PO FETCH to recover the print position parameters
- put a pointer on the graphic form just constructed
- jump on to PR ALL.
_0B38_PO_GR_1 (formally a free-standing subroutine, but
only called from PO ANY; it constructs any of the "graphic
forms", character codes 80h/128d to 8Fh/143d. These are not
stored as "character forms", like the alphanumeric characters,
since it is relatively easy to "draw" them whenever they are
required.
All the forms consist of a byte repeated four times to
make the upper half of the form and then another byte
repeated four times to form the lower half: each of these
bytes is always either 00h, 0Fh, F0h or FFh.
The 10h/16d forms can be tabulated as follows: the upper
line shows the last four bits of the character code, the lower
shows the byte used in the_lower half of the character
followed by the byte used in the upper half:
0000 0001 0010 0011 0100 0101 0110 0111
0000 000F 00F0 00FF 0F00 0F0F 0FF0 0FFF

1000 1001 1010 1011 1100 1101 1110 1111


F000 F00F F0F0 F0FF FF00 FF0F FFF0 FFFF

681
The correspondence is obvious):
- put a pointer on 5C92 MEMBOT; the calculator memory
area of 30d bytes
- call PO GR 2 and immediately exit into it; thus going
through it twice and then returning into PO ANY.
_0B3E_PO_GR_2 (formally a subroutine, but never called
except at this point): rotate the character code right into the
carry flag; on the first run this sets carry for odd character
codes, which have F as last digit of their lo byte, see the
table above. On the second run the character code has already
been shifted twice, so carry is set by what started out as bit
2, and codes with bit 2 set have F as last digit of their hi
byte
- take the code from itself with the carry; making 00
for even codes or FF for odd, depending on the carry
- AND the result with 0F; making 00 for even, 0F for
odd, the lo nibble of the byte
- rotate and subtract the character code again
- AND this time with F0; making 00 for even, F0 for odd,
the hi nibble of the byte
- OR the two nibbles together to make the whole byte.
- make a counter 04; the byte is used four times.
_0B4C_PO_GR_3: put the byte just formed into the next
location of the memory area
- move on the pointer
- reduce the counter
- if it is not zero loop back to PO GR 3
- (four bytes loaded) return; the first time the routine
is called, PO GR 2 is immediately entered again with the lo
byte of the character code already shifted four bytes right,
producing the byte for the lower half of the graphics
character.
The second time, this is a return into PO ANY.
_0B52_PO_T&UDG (entered from PO ANY if the code is a
token or user-defined graphic): subtract A5h/165d from the
code
- if this does not give carry jump on to PO T; the code

682
is 165d or more, a token
- (a user-defined graphic) add back 15h/21d; now the
code is zero -> 14h/20d; 0Fh in the notes is incorrect
- put a pointer on the address in 5C7B UDG; this is the
base address for the UD graphics
- jump on to PO CHAR 2 to print the character.
_0B5F_PO_T (tokens): call 0C10 PO TOKENS to print out
the token
- jump back to PO FETCH to exit; the display address in
HL has been overwritten.
_0B65_PO_CHAR (output a single character code from the
standard set; this entry point is also called as a subroutine
from PO RIGHT, to move the cursor right by printing an
"invisible space"): get the "base address of the character set"
from 5C36 CHARS; this base is 100h/256d bytes before the
start of the character set, so code 20h/32d lands on the first
byte of the set, which is also the first byte of the form for 20h
"space", and other codes correspondingly
_0B6A_PO_CHAR_2: zero bit zero of FLAGS; "print the next
token with a leading space"
- if the present code is not space jump on to PO CHAR 3
- (it is space) set bit zero of FLAGS; no leading space
after a space.
_0B76_PO_CHAR_3: add eight times the character code to
the base address; this finds the character form either in the
standard set, if entry was from PO CHAR, or in the UDGs if it
was from PO T&UDG.
_0B7F_PR_ALL (DE holds a pointer to the first of the
eight bytes of a character form, whether normal or a user-
defined graphic or the ad hoc keyboard graphic constructed
in the memory area; BC holds the print position, HL the
display area address for the first byte): decrement the print
position column number
- prepare a new column number 21h/33d
- if it didn't reach zero, jump on to PR ALL 1
- (end of the output line) decrement the line number
- put 21h/33d as the column number

683
- if bit 1 of FLAGS is zero jump on to PR ALL 1; output
to the screen
- (ZX printing) call 0ECD COPY BUFF to output the whole
print buffer.
_0B93_PR_ALL_1: check if the column number has been
changed to 21h/33d
- if it has call 0C55 PO SCR to check whether scrolling
is required; line end
- get the byte from 5C91 P FLAG
- prepare an OVER mask 11111111b/FFh for OVER on
- if bit zero of P FLAG is set jump on to PR ALL 2; it
signals OVER on
- change the OVER mask to 00000000b; OVER off.
_0BA4_PR_ALL_2: rotate the P FLAG byte right twice
- subtract it from itself with the carry; this makes an
INVERSE mask, also 00000000b for "off" and 11111111b for
"on", since bit 2 of P FLAG went into the carry
- make a counter 08 for the eight bytes of the character
form
- if bit 1 of FLAGS is zero jump on to PR ALL 3 with NC;
output to the screen
- (ZX printing) set bit 1 of FLAGS2; "print buffer not
empty"
- set the carry; it signals output to the ZX printer.
_0BB6_PR_ALL_3 and_0BB7_PR_ALL_4: save the carry flag
- get a pixel byte from the screen pointer
- AND it with the OVER mask; this zeroes it for OVER off
and leaves it unchanged for OVER on
- XOR the result with the byte from the character form;
this reverses each bit from the screen pixel which
corresponds to a one in the character form, so for OVER off it
just copies the character form
- XOR the result with the INVERSE mask; this leaves it
unchanged for INVERSE off and flops each bit for INVERSE
on.
For a complex example, suppose both INVERSE and
OVER are on, the character on screen is upper-case letter O:

684
........
..****..
.*....*.
.*....*.
.*....*.
.*....*.
..****..
........
and the character to be printed is X:
........
.*....*.
..*..*..
...**...
...**...
..*..*..
.*....*.
........
The O is unchanged by the OVER mask, so when it is
XORed
with the X we get:
........
.******.
.**..**.
.* **.*.
.*.**.*.
.**..**.
.******.
........
The INVERSE mask XORs this to
********
*......*
*..**..*
*.*..*.*
*.*..*.*
*..**..*
*......*
********

685
Try
CLS: PRINT "OOOOO"; AT 0,0; INVERSE 1; OVER 1;"XXXXX"
to see the effect of this on screen
- if the saved flag signals ZX printing jump on to PR
ALL 6
- (not ZX printing) increment the screen pointer by
100h/256d to find the next display position; see DISPLAY
AREA.
_0BC1_PR_ALL_5 (PR ALL 6 jumps back to here in ZX
printing): increment the character form pointer
- decrement the byte counter
- if it is not down to zero yet loop back to PR ALL 4
for the next pixel byte
- (all eight bytes output) put a pointer back on the
last display area address loaded
- read bit 1 of FLAGS; "ZX printer" flag [unnecessarily;
INC and DEC do not affect carry, so the carry flag still signals
"ZX printer" or not from the last time it was checked]
- if it is zero for output to screen, call 0BDB PO ATTR
to find the corresponding attribute address and put in the
attribute from 5C8F ATTR T
- decrement the print position column number; this is
why it was put at 21h/33d for a new line start, rather than one
- increment the display area address
- return.
_0BD3_PR_ALL_6 (ZX printing; in this case the destination
pointer is in the print buffer, not the display area): move on
the pointer by 20h; to the next pixel byte in the print buffer
- jump back into PR ALL 5.
Exit:
- RETs from PO RIGHT (incorrect), PO SPACE, PO STORE,
PO ST E, PO ST PR
- into 0DD9 CL SET, which exits back to PO STORE and
RETs from there, from PO BACK 3, PO ENTER, PO AT SET
- into 226C CO CHANGE, which makes a final adjustment
to the attribute svs, from CO TEMP 6, CO TEMP B and CO
TEMP E

686
- into 0C55 PO SCR, to check if scrolling is required,
from PO AT ERR
- the RETs from PO FETCH, except when it is made the
exit routine by PO T, PO F PR, PO GR 3 and PR ALL 5 are
"internal", as these routines are only called in the PRINT OUT
routine
- the RET from PO CHANGE, which is also an exit from PO
TV 2 and PO TV 1, is "internal" in the call from PO CONT, and
really also elsewhere, since the output routine is immediately
called again with the changed address.
Output parameters: none.
Called from:
115E ED SPACES
18C1 OUT FLASH
Rems:
Introduction rather slow output routine
0D94 CL CHAN address put in output of channel
0DAF CL ALL address put in output of channel
15AF initial channel information
21E1 CO TEMP 1 misprint for 0010 PRINT A 1
21FC CO TEMP 4 misprint for 0010 PRINT A 1
2211 CO TEMP 5 (correct) called by PRINT OUT

print position see DISPLAY AREA

PRINT POSITION SUBROUTINE, print statement see 1FCD


PRINT

PRINT THE CURSOR SUBROUTINE see 18E1 OUT CURS

PRINT 1 1FCF (1FCD PRINT)


Exit from:
1FC9 LPRINT
1FCD PRINT

PRINT 2 subroutine 1FDF


See 1FCD PRINT.

687
Called from:
1FCF PRINT 1
20C1 IN ITEM 1

PRINT 3 1FE5 (1FCD PRINT)


Jumps from:
auto (twice)

PRINT 4 1FF2 (1FCD PRINT)


Exit from:
1FDF PRINT 2

priorities see 24FB SCANNING

PR ITEM 1 subroutine 1FFC


See 1FCD PRINT.
Called from:
1FE5 PRINT 3
21AF IN NEXT 1

PR ITEM 2 200E (1FCD PRINT)


Jumps from:
1FFC PR ITEM 1

PR ITEM 3 2024 (1FCD PRINT)


Exit from:
200E PR ITEM 2

Procrustean lengthening/shortening see 2B72 L DELETE$


(2AFF
LET)

PROG system variable 5C53


Bytes: 2
Holds the address of the_start_of_the_BASIC_program.
Loaded on start-up with the address just above the channels
area. Although it is one of the fourteen system pointers set by

688
1664 POINTERS, the only thing that can change it is expansion
or contraction of the Microdrive maps. ME ENT 1 -> ME ENT 3
and MAIN ADD 1 save and restore it when adding lines to the
program, in case the line is at the start, when they would
move PROG.
Written by:
0958 ME ENT 3
1219 RAM SET
157D MAIN ADD1
166B PTR NEXT
Read by:
073A SA TYPE 0
093E ME ENT 1
157D MAIN ADD1
196E LINE ADDR
2802 SF ARGMT 1

program area, program (BASIC), program block, program/


data block, program/data flag
A BASIC program input from the keyboard or loaded
from tape etc is put in memory as a string of token and
character codes in the_program_area, starting at
the_base_address stored in the sv 5C53 PROG, which is just
above the Microdrive maps if any and the channel
information, and ending just before the address in 5C4B VARS
which marks the start of the variables area. This area expands
or contracts with the program.
When the program is saved on tape etc, it is the string
of bytes in the program area and the variables area which is
the _save/load_block to be recorded; the header recorded on
tape holds the_parameters_of_the_save/load_block, signalling:
- this is a program, and a line number if one is saved
- the_start_address or_destination_address to which the
program is to be loaded, ie the address in 5C53 PROG; as
SAVing or LOADing proceeds, the destination address is
stepped on through the program, and the notes sometimes

689
speak of the_base _address being so stepped on, which is
confusing and wrong
- the_size_of_the_save/load_block, or_number_of_bytes
_in_the_save/load_block, indicating the space that must be
opened up to accept it on loading back, between the
addresses in svs 5C53 PROG and 5C59 E LINE, the end of the
variables area;
the_length_counter is counted down during SAVing and
LOADing and when it reaches zero the parity byte is saved or
checked
- the_program_length, ie not including the variables,
indicating where 5C4B VARS must be set
At several points the notes use the expression_data
_block, as opposed to_header_block, to mean a block of bytes
which is a program or an array of data or a CODE or SCREEN$
block. This is logical enough in context, but can be confusing.
Introduction on saving, header describes saved block
04C2 SA BYTES saves the block
04D0 SA FLAG adjust length and base address to allow
for flag
04F2 SA SYNC 2 1st byte saved indicates header or block
04FE SA LOOP check length counter for zero
0525 SA 8 BITS count down and move on base address
0556 LD BYTES load a block
058F LD SYNC check flag before loading block
05B3 LD FLAG adjust counter to allow for flag
05BD LD VERIFY verifies a block
05C2 LD NEXT step on destination address
05C4 LD DEC count down
05CA LD 8 BITS loop till counter zero
0621 SA SPACE put start address in header
064B SA NAME get start address
06C3 SA CODE zero length signifies unspecified
06E1 SA CODE 1 must specify start/length for CODE
06F0 SA CODE 2 zero length signifies unspecified
06F5 SA CODE 3 get length as specified
06F9 SA CODE 4 all parameters in header

690
073A SA TYPE 0 puts parameters in header string
075A SA ALL save pointer to start
07CB VR CONTRL on verification, check recorded bytes
against those in memory
07F4 VR CONT 2 transfer destination pointer
0800 VR CONT 3 signals data not header
0802 LD BLOCK loads or verifies a block
0808 LD CONTRL loads program and variables or array
0819 LD CONT 1 checks space available for new block
082E LD DATA skipped when loading program
084C LD DATA 1 signals "data block" when loading array
0873 LD PROG loading program and variables
08AD LD PROG 1 signals "load" & "data" and loads
08B6 ME CONTRL loads block for merging
08D2 ME NEW LP loop through lines of new program
08D7 ME OLD LP inner loop through lines of old program
08DF ME OLD L1 place found for line of new program
08F0 ME VAR LP merge old and new variables
0970 SA CONTRL saving a block
0991 SA 1 SEC get length & start, signal "data" & save
155D MAIN ADD adding line to BASIC
1974 LINE AD 1 find place for new line number in program
19B8 NEXT ONE find start of next line in BASIC
1B17 LINE SCAN reads every code of BASIC program
1B9E LINE NEW scan program for address to jump to
1BB3 LINE END check for end of program
1BD1 NEXT LINE sets signals for reading program line
1D64 F LOOP looks through program area for NEXT
1D86 LOOK PROG looks for DATA, DEF FN, NEXT
1D8B LOOK P 1 loops through each line of program
26C9 S LETTER variables if DEF FN are in program area
27BD S FN SBRN to scan FNs, find DEF FN in program
area
2802 SF ARGMT1 start search at PROG less one
2808 SF FND DF search program for DEF FN
28B2 LOOK VARS seeks variable named in program area

691
'Program: ' message see 09A1 cassette messages

PROGRAM NAME 04AA see unused addresses

PROG RUN misprint for LINE RUN at 12CF MAIN 3

prompts see message printing

proper character code


A mysterious expression used only in the note on 15EF
OUT CODE; probably a misprint for [report] character code.

PR POSN 1 subroutine 204E


See 1FCD PRINT.
Called from:
1FE5 PRINT 3 (twice)
20C1 IN ITEM 1
21B2 IN NEXT 2

PR POSN 2 2061 (1FCD PRINT)


Exit from:
204E PR POSN 1

PR POSN 3 2067 (1FCD PRINT)


Jumps from:
204E PR POSN 1 (3 times)

PR POSN 4 206E (1FCD PRINT)


Jumps from:
2067 PR POSN 3

PR ST END subroutine 2048


See 1FCD PRINT.
Called from:
06C3 SA CODE
Exit from:
2045 PR END Z

692
PR STRING 203C (1FCD PRINT)
Exit from:
2024 PR ITEM 3
auto

P RUN 1AAB
P SAVE 1ADF
P STOP 1A8A
see 1A7A syntax parameter table

PTR DONE 1675 (1664 POINTERS)


Exit from:
166B PTR NEXT

PTR NEXT 166B (1664 POINTERS)


Jumps from:
1675 PTR DONE

pulses, pulsing signal see timing

P VERIFY 1AE1 see 1A7A syntax parameter table

693
Q

question mark see ? key (3F) after end of alphabet

quotation mark, quotes see " (22) after end of alphabet

'quotes' flag, quote mode see 5C6A FLAGS2 bit 2

694
R

radians see 3783 get-argt

RAM CHECK 11DA (1187 NEW)


There seems to be no use of this label anywhere in ROM.

RAM DONE 11EF (1187 NEW)


Jumps from:
11E2 RAM READ (twice)

RAM FILL 11DC (1187 NEW)


Jumps from:
auto

RAM READ 11E2 (1187 NEW)


Jumps from:
auto

RAM SET 1219 (1187 NEW)


Jumps from:
11EF RAM DONE

RAMTOP system variable 5CB2


Bytes: 2
Loaded on start-up by 1219 RAM SET with the address of
the highest byte below the user-defined graphics; this will be
FF57h/65367d for the 48K Spectrum or 7F57h/32599 for the
16K machine. This address is loaded with a marker byte 3Eh/
62d.
RAMTOP isn't affected by a NEW command, and poking it
from BASIC or m/c with a lower number will create a safe
block of memory for m/c programs etc: this can also be done
from BASIC by the command CLEAR xx, using 1EDC CLEAR 2.

695
CLEAR_always sets RAMTOP, but if no value is supplied the
existing value is used.
RAMTOP cannot be set higher than 5CB4 P RAMT, or lower
than 50d bytes above the top of the calculator stack pointed
to by 5C55 STKEND. This lower limit will vary with the size of
the program and variables areas, etc.
Written by:
1219 RAM SET
1EDC CLEAR 2
Read by:
11B7 NEW
1219 RAM SET
1EAF CLEAR RUN
Rems:
11DA RAM CHECK memory rewrite (NEW) from RAMTOP
down
1EAC CLEAR can move RAMTOP
1EB7 CLEAR 1 new RAMTOP checked
1EDA REPORT M if RAMTOP too high or low

RANDOMIZE key (F9) see also commands, functions and


operators, KEYBOARD SCANNING
The T key in K mode produces the command
RANDOMIZE, which requires a numeric parameter; zero is
supplied if there is none in the BASIC. It is interpreted by 1B29
STMT L 1 using the syntax offset table 1A48 and the syntax
parameter table 1A7A;
1AB5 P RANDOM jumps through 1C0D CLASS 03, which
collects the parameter or supplies zero, and 1C16 JUMP C R to
the executive routine 1E4F RANDOMIZE.

RANDOMIZE subroutine 1E4F


Called only from 1AB5 P RANDOM in the syntax
parameter table; the executive routine of the RANDOMIZE
command, which loads 5C76 SEED with a given value S, or if
none is given from BASIC with the two lo bytes from 5C78
FRAMES, which run from zero to 65535d every 20 minutes or

696
so. This number is random, ie unpredictable, in practice if not
in theory; see the comments under 25F8 S RND.
Input parameters: none
- S is on the calculator stack; if S wasn't specified in
the BASIC command, it is zero.
Action: call 1E99 FIND INT2 to get S
- if it isn't zero jump on to RAND 1
- (zero S) make S equal to the lo bytes of 5C78 FRAMES.
_1E5A_RAND_1: load S into 5C76 SEED.
Exit: RET, from 1E5A RAND 1.
Output parameters: none, and the stack is cleared.

random numbers see 25F8 S RND

RAND 1 1E5A (1E4F RANDOMIZE)


Exit from:
1E4F RANDOMIZE

range of steps (line drawing) see 24B7 DRAW LINE

RASP system variable 5C38


Bytes: 1
The_length of the low-pitched "groan" emitted by the
Spectrum when it finds it impossible to perform an editing
command, because either the screen or the memory is full;
see 107F ED ERROR and 1167 ED FULL. The "pulses" of the
rasp are always the same length, 6800 T-states or 7.8
thousandths of a second, 129 hz; RASP just controls
the_number of pulses. It is set at 40h/64d on initialization,
half a second, and not changed thereafter unless a new value
is deliberately POKEd into it; any such change of value will be
undisturbed by a NEW command.
The immediately following system variable is the one-
byte PIP, and both are written together in 11EF RAM DONE.
Written by:
11EF RAM DONE (twice)
Read by:

697
107F ED ERROR
1167 ED FULL
11B7 NEW

READ key (E3) see also commands, functions and


operators,
KEYBOARD SCANNING, 022C extended mode table (b)
The A key in E mode without shift produces the
command READ. The "READ statement" must also include
one or more variable names, separated by commas.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AC8 P READ causes a jump via 1C11 CLASS 05 and 1C16 JUMP
C R to the executive routine 1DED READ.

READ subroutine 1DED


Called only from 1AC8 P READ in the syntax parameter
table 1A7A; the executive routine of the READ command.
Assigns values to all the variables in the READ statement,
from the next DATA statements in the BASIC.
Input parameters: none.
Action: call 1C1F CLASS 01, which either finds the
variable named in the READ statement or makes a new one
- call 0018 GET CHAR to get the current BASIC pointer
and store it temporarily in 5C5F X PTR; the usual store, 5C5D
CH ADD, is needed for the DATA search
- put a data pointer on the current address in 5C57
DATADD; this is just after the last DATA item read, if any
- if the code at that address is 2C comma, jump on to
READ 1; the next DATA item is just past the comma
- (not a comma, ie the last DATA statement has been read
right through) load the token code E4 DATA
- call 1D86 LOOK PROG to find the next DATA statement
- if none is found report "Out of DATA".

698
_1E0A_READ_1: call 0077 TEMP PTR1 to move the pointer
on past the DATA token or comma and store it in 5C5D CH
ADD
- call 1C56 VAL FET 1 to evaluate the DATA expression
and assign it to the variable
- store the pointer in 5C57 DATADD
- zero 5C5F X PTR; "no error yet"
- call 0078 TEMP PTR2 to put the BASIC pointer to the
READ command back in 5C5D CH ADD; LD (CH ADD),HL
would have done just as well.
_1E1E_READ_2 (also entered from READ in syntax
checking, which skips the variable assignment): if the next
code in READ is 2C comma jump back to 1DEC READ 3 for
another variable
- (not a comma) call 1BEE CHECK END; in syntax checking
it reports "Nonsense in BASIC" if this isn't the end of the
statement and makes a double return to 1B76 STMT RET if it is
- (run time) return.
_1DEC_READ_3 (there is another variable to be assigned):
read the next variable letter in the READ statement
- re-enter READ.
Exit: RET, from 1E1E READ 2; this will go back to 1B76
STMT RET.
Output parameters: none.
Rems:
1C1F CLASS 01 called by 1DED READ
2AFF LET called by READ 1 via 1C59 VAL FET 2 for
assignment to the variable

read-in subroutine 3645


Called only from 0028 FP CALC with literal 1A to execute
the function INKEY$ (hatch) as in PRINT INKEY$ (hatch) 4
when channel 4 has been opened to read data from
Microdrives etc; it reads a single character from the input
channel and puts it in the work space labelled as a string
expression. The routine assumes that whatever input system
is being used, it will either return a character code in the A

699
register and set the carry flag, or return with no carry if
nothing was read. Not used in the standard Spectrum,
without peripherals.
Input parameters: none
- the stream number on the calculator stack.
Action: call 1E94 FIND INT1 to get the stream number
- if it is more than 0Fh/15d report "Integer out of
range"
- save the current channel address
- call 1601 CHAN OPEN to open the specified channel
- call 15E6 INPUT AD to read one character.
- make a string length zero
- if carry isn't set jump on to R I STORE; no byte was
received
- (byte received) make the length one
- call 0030 BC SPACES to make a space in the work space
- put the byte in it.
_365F_R_I_STORE: call 2AB2 STK STO $ to put the address
and the unit or zero length of the code on the calculator stack
as string parameters
- call 1615 CHAN FLAG with the old channel address to
set the channel information back as it was.
Exit: into 35BF STK PNTRS; HL and DE have been changed
by CHAN FLAG, and must be set back as stack pointers to the
string parameters on the calculator stack.
Output parameters: HL and DE point to the last value on
the stack and to the stack end
- the last value is a pair of string parameters defining
the code obtained, which is in the work space.
Rems:
2634 S INKEY$ puts op code 5A (later changed to 1A) on
the stack for INKEY$ followed by (hatch)

READ statement see READ key

READ 1 1E0A (1DED READ)


Jumps from:

700
1DED READ (twice)

READ 2 1E1E (1DED READ)


Exit from:
1DED READ

READ 3 1DEC (1DED READ)


Jumps from:
1E1E READ 2

real numbers see also precision


A real number, in mathematical jargon, means roughly a
number which can be used to express a physical quantity or
ratio: zero and all whole numbers and fractions, positive or
negative, are real numbers, but so are non-terminating
decimals like square roots, pi, the constant e, and so on.
These last are real numbers but not rational numbers, ie they
can't be expressed as a/b with a and b both integers.
Imaginary numbers like i, the square root of minus one, and
complex numbers Ai + B, aren't real numbers, and
mathematicians have invented many other kinds of numbers:
vectors and tensors are other examples.
37E2 atn the angle in radians whose tan is X
3833 asn the angle in radians whose sin is X
3843 acs the angle in radians whose cos is X

real time see 5C78 FRAMES

REC EDIT subroutine 16D4


This is a subroutine to reduce the edit line to a single
carriage return, as in 1219 RAM SET; it would do this if called
with HL holding one less than the value in 5C61 WORKSP.
However, it isn't used by the ROM; the same operation is
performed at 0FA9 ED EDIT by a call to 1097 CLEAR SP, twice.

reclaiming see 1664 POINTERS, 19E5 RECLAIM 1

701
RECLAIM 1 subroutine 19E5
Whenever anything from the top of the system variables
to the top of the calculator stack is_removed or_deleted or
reduced in size, the ROM immediately moves down all the
bytes above the reduction, and adjusts the fourteen system
pointers as necessary, those which mark the boundaries
between various sections of the working memory. See 1664
POINTERS. Examples are the deletion of a line in BASIC, or a
DIM command reducing the size of an array.
This_reclaiming or scavenging operation is done by the
routines starting at 19E5 RECLAIM 1; 19B8 NEXT ONE and
RECLAIM 2 together delete a BASIC line or variable starting at
HL.
Input parameters: DE points to the first byte to be
deleted
- HL to the first to be left alone above the block
deleted.
Action: call 19DD DIFFER to get the length and reverse
the start and end pointers to the RECLAIM 2 values.
_19E8_RECLAIM_2 (an alternative entry point; HL points to
the first byte to be deleted and BC holds the number of bytes
to be deleted): save the length
- reverse the bits in the hi byte
- reverse the bits in the lo byte
- increment it as a whole; these operations negate the
two-byte length, ie subtract it from 10000h/65536d
- call 1664 POINTERS; it "adds" this negative length to
any of the fourteen pointers which are above the reclaim
location, and returns with pointers to the start and end
addresses and the length of the block from the top of the
deletion to the top of the calculator stack, ready for the "move
block up" LDDR instruction used in 1655 MAKE ROOM
- exchange the end pointer and the start pointer
- recover the length of the block for deletion
- add it to the old start pointer; the parameters are
now correct for the "move block down" LDIR instruction
- save the start pointer, make the move and recover it.

702
Exit: RET, from RECLAIM 2.
Output parameters: HL holds the address at which the
deletion was made.
Called from:
0873 LD PROG
1097 CLEAR SP
1EB7 CLEAR 1
Exit from:
16D4 REC EDIT
Rems:
Introduction redundant strings are reclaimed
0819 LD CONT 1 assumes program reclaimed on LOAD
082E LD DATA array reclaimed when new one LOADed
092C ME ENTER line/variable reclaimed if overwritten
0958 ME ENT 3 line/variable removed from work space
1015 ED DELETE reclaims deleted character
155D MAIN ADD line of BASIC reclaimed if overwritten
1664 POINTERS corrects 14 pointers after reclaim
2AB1 STK STO 0 flag from 2AFF LET signals to reclaim
old string
2B72 L DELETE$ no deletion unless assigning to complete
string
2BAF L ADD$ new string entered before old reclaimed
2C02 DIM old array reclaimed before new with same
name
entered
2C15 D RUN existing array reclaimed

RECLAIM 2 subroutine 19E8


Alternative entry point to RECLAIM 1, see above.
Called from:
082E LD DATA
092C ME ENTER
0958 ME ENT 3
11A7 REMOVE FP
155D MAIN ADD
2C15 D RUN

703
Exit from:
1015 ED DELETE
2BAF L ADD$
Rems:
19E5 RECLAIM 1 merely different input parameters

recursive printing see 0C3B PO SAVE

REDUCE ARGUMENT SUBROUTINE, reduced argument see


3783 get-argt

RE ENTRY 3365 (0028 FP CALC)


Jumps from:
338E ENT TABLE (indirect through all subroutines)
Rems:
33A2 fp-calc-2 drops return address to RE ENTRY; it is
almost immediately put back again
369B end-calc drops return address to RE ENTRY

REM key (EA) see also commands, functions and operators,


KEYBOARD SCANNING
The E key in K mode produces the command REM. The
"REM statement" may include anything at all. Keystrokes after
a REM command won't be interpreted in K mode, except
after THEN or a colon; the latter is a mistake, see 196D OUT
CHAR 3.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AA5 P REM causes a jump via 1C11 CLASS 05 and 1C16 JUMP C
R to the executive routine 1BB2 REM.
196D OUT CH 3 K mode left on after colon

REM subroutine 1BB2


Called only from 1AA5 P REM in the syntax parameter

704
table; the "executive routine" of the REM command - not that
much is actually executed! The effect of the command is to
make the computer ignore the rest of the current line.
Action: drop the return address to 1B76 STMT RET.
Exit: into 1BB3 LINE END, so that execution treats the
REM line as already completed. The 1B52 SCAN LOOP address
was dropped in 1C11 CLASS O5, so the next return address on
the stack is the one set up by the call to 1B8A LINE RUN
(misprinted PROG RUN) in 12CF MAIN 3, and return will be to
there, unless 1BBF LINE USE and 1BD1 NEXT LINE loop on to
the next program line.

REMOVE FP subroutine 11A7 see also number in BASIC line.


Removes the markers and FP number forms from the
BASIC whenever it is copied back to the editing or input
areas.
Input parameters: HL is a BASIC pointer to the start of
the line being edited from 5C59 E LINE, or 5C61 WORKSP
when the subroutine is called from 2148 IN VAR 2.
Action: if the pointer holds a number marker 0E, delete 6
bytes using 19E8 RECLAIM 2
- read the byte at the pointer again
- step on the pointer
- if it isn't a newline loop back to REMOVE FP.
Exit: RET.
Output parameters: none.
Called from:
12AC MAIN 2
2148 IN VAR 2
Jumps from:
auto

removing variable/line from workspace see reclaiming

REPDEL system variable 5C09


Bytes: 1.
The time in 50ths of a second before a pressed key will

705
repeat the first time; 60ths of a second in USA. It is set at
35d on start-up and not changed thereafter. If a different value
is POKEd, it will be reset to 35d by a NEW command.
For the use of REPDEL, see NB 2 under KEYBOARD
SCANNING.
Written by:
1219 RAM SET
Read by:
02F1 K NEW
Rems:
0310 K REPEAT decrement copy of REPDEL/REPPER

repeated key see KEYBOARD SCANNING

REPEATING KEY SUBROUTINE see 0310 K REPEAT

repeat status see KEYBOARD SCANNING

replacement of line or variable see 08B6 ME CONTROL in


0605
SAVE ETC

REPORT A 34E7, 371A


"Invalid argument" (141A)
This error message is printed if the BASIC calls for an
impossible evaluation, eg SQR -1.
Exit from:
34BC usr$ (3 times)
34D3 USR RANGE
3713 ln
384A sqr

REPORT AND LINE NUMBER PRINTING SUBROUTINE see


1A1B
OUT NUM 1

REPORT B 046C, 1E9F, 24F9, 35DC

706
"Integer out of range" (142A)
This error message is printed if an integer value is too
big or too small for the calculation specified, eg PRINT AT 30,7
or CHR$ -1.
Exit from:
03FB BEEP (4 times)
0427 BE OCTAVE
0AAC PO AT ERR
1E67 GOTO
1E85 TWO PARAM
1E9C FIND I 1 (twice)
22AA PIXEL ADD
2314 STK TO A
24DF D L STEP
35C9 chrs (twice)
3645 read-in

REPORT C 1C8A, 21CE


"Nonsense in BASIC" (143E)
This catch-all error message is printed if an error
occurs which doesn't fall into any of the categories covered by
the other error messages: eg MERGE "" DATA. Unhelpful!
Exit from:
0652 SA DATA
0672 SA V OLD
0692 SA DATA 1 (indirect, through SA V OLD)
06A0 SA SCR$
06C3 SA CODE (twice)
06E1 SA CODE 1
0723 SA LINE 1
1A15 E L 1 (label omitted)
1B29 STMT L 1 (twice)
1B6F SEPARATOR
1BF4 STMT NEXT
1C59 VAL FET 2
1C6C CLASS 04
1C7A EXPT 2NUM

707
1C82 EXPT 1NUM
1C8C EXPT EXP
1F89 DEF FN 4
1FBD DEF FN 7
20C1 IN ITEM 1
20D8 IN ITEM 2
21B9 IN ASSIGN
21E2 CO TEMP 2
2320 CIRCLE
250F S QUOTE 8
252D S RPORT C
25E8 S BRACKET
26DF S NEGATE (twice)
2761 S RPORT C
27BD F FN SBRN
27E6 SF RPORT C
28B2 LOOK VARS
2A7A SL RPT C
2C05 D RPORT C
2CCF DEC RPT C
360C V RPORT C

report code/number/value see errors

REPORT D 0552, 0D00


"BREAK - CONT repeats" (144F)
This report is printed:
- if tape operations are stopped by BREAK; in some
circumstances, eg while a header is being saved
- if scrolling is discontinued.
An unlabelled "REPORT D" is incorporated in 0EFD COPY L 1.
Exit from:
053F SA/LD RET
0C88 PO SCR 2 (3 times)

REPORT E 1E08
"Out of DATA" (1463)

708
This error message is printed if a READ statement is
still being implemented but there is no data left in the BASIC.
Label not used in ROM.
Exit from:
1DED READ

REPORT F 0642, 1765


"Invalid file name" (146E)
This error message is printed if you try to SAVE without
any filename or with one longer than 10d characters; LOADing
longer names is accepted, though any characters after the
tenth will be ignored.
It is also used, rather inappropriately, to reject
OPENing of any channel labelled with more than one
character, or without any characters, or with any letter other
than K, S or P.
Exit from:
0629 SA BLANK
175D OPEN 2
1767 OPEN 3
178B OPEN END

REPORT G 1555
"No room for line" (147F)
Unlike most of the others, this error message isn't
detected during the scanning of the line in 12AC MAIN 2, but
after scanning is complete, when 12CF MAIN 3 exits to 155D
MAIN ADD.
There is no direct jump or exit to the routine: its
address is put on the empty machine stack in MAIN ADD.
Since the bottom of the stack is the location pointed to by
5C3D ERR SP, if any error occurs while the line is being
transferred to the program area RST 0008 ERROR 1 will jump
to REPORT G. The only possible one is in the call to 1655
MAKE ROOM in 157D MAIN ADD1, which might produce a
memory overflow.
It is taken off the stack again in 15AB MAIN ADD2 and

709
when execution goes round the MAIN EXEC loop again, in
12CF MAIN 3, is replaced by the address 1303 MAIN 4, which
prints nearly all the other error messages.
Rems:
155D MAIN ADD address put on stack
15AB MAIN ADD2 address dropped

REPORT H 21D4
"STOP in INPUT" (148F)
This report is printed if STOP is given as the response
to an INPUT prompt; its report number 10h is put in 5C3A
ERR NR by 1001 ED STOP, see under 0FF3 ED DOWN.
STOP in a program or as a direct command produces
report 9, "STOP statement". It seems a bit superfluous to have
the two messages. Label not used in ROM.
Exit from:
21D0 IN STOP
Rems:
1001 ED STOP 10h put in ERR NR

REPORT I 1D84
"FOR without NEXT" (149C)
This error message is printed if no NEXT command can
be found with a variable name that matches the current FOR.
Exit from:
1D64 F LOOP

REPORT J 15C4
"Invalid I/O device" (14AC)
This error message is printed if input is attempted on
the standard Spectrum from any source except the keyboard.
There are no jumps or exits to it in the ROM, but it is
made the channel input address by the initial channel
information 15AF for all channels except K, the only one
which will accept input.

REPORT K 2244 (misprint, "-K" omitted)

710
"Invalid colour" (14BE)
This error message is printed:
- if any number except zero -> 9 is put with PAPER and
INK commands
- if any but zero -> 7 is put with BORDER commands
- if any but zero or one is put with INVERSE or OVER
commands
- if any but zero, one or 8 is put with BRIGHT or FLASH
commands.
Exit from:
2228 CO TEMP 6
223E CO TEMP 8
227D CO TEMP D
2294 BORDER

REPORT L 1B7B
"BREAK into program" (14CC)
This report is printed if the BREAK key is found to be
pressed at the end of execution of any BASIC statement;
CONTINUE has no effect in this case. Label not used in ROM.
Exit from:
1B76 STMT RET

REPORT M 1EDA
"RAMTOP no good" (14DE)
This error message is printed if an attempt is made by a
CLEAR command to set 5CB2 RAMTOP either above the
address in 5CB4 P RAMT or less than 50d bytes above that in
5C65 STKEND.
Exit from:
1EB7 CLEAR 1 (twice)

report messages 1391 see errors

REPORT N 1BEC
"Statement lost" (14EC)
This slightly mystifying error message is printed if

711
execution is somehow made to jump to a statement which
doesn't exist; eg if the statement got deleted from the
program after a BREAK had stopped execution with a GO TO
jump pending, and then CONTINUE was pressed.
Exit from:
1B9E LINE NEW
1BD1 NEXT LINE

report number see errors

REPORT O 160E, 1725


"Invalid stream" (14FA)
This error message is printed:
- if you try to open a stream with a number outside the
range zero -> 15d
- if you try to open a channel with any label other than
K, S or P
- if you try to use any of the commands CAT, ERASE,
FORMAT or MOVE on the standard Spectrum without
Interface 1 or equivalent.
The difference between this and Report J is a subtle
one, and one is tempted to suggest they could have been
amalgamated.
Exit from:
1601 CHAN OPEN
171E STR DATA
1736 OPEN
1793 CAT ETC
2070 STR ALTER

REPORT P 2812
"FN without DEF" (1508)
This error message is printed if execution cannot find a
DEF FN with a name matching the FN employed. Label not
used in ROM.
Exit from:
2808 SF FND DEF

712
report printing see errors

REPORT Q 288B
"Parameter error" (1516)
This error message is printed
- if a FN producing a string is used where a number is
required, or vice versa
- if the last character of a DEF FN isn't a ")".
"DEF FN error" would have been shorter and more lucid.
Exit from:
2852 SF ARG VL (twice)
2885 SF R BR 2

REPORT R 0806
"Tape loading error" (1525)
This error message is printed
- if verification fails
- if LOAD (0556 LD BYTES) returns with NC; this means
BREAK was pressed during the load, or
the leader pulse or sync pulse was defective, or
the tape record being verified is the wrong type, or
there is a break in the string of byte pulses on the
tape eg by the tape breaking or stopping, or
the parity matching has failed.
Exit from:
07CB VR CONTRL (twice)
0802 LD BLOCK

report value see errors

REPORT 0 1BB0
"OK" (1392)
This report is printed if no errors have been found.
Label not used in ROM.
Exit from:
1B9E LINE NEW

713
REPORT 1 1DD8
"NEXT without FOR" (1394)
This error message is printed if a FOR statement
couldn't be found to match the NEXT statement. Why didn't
they
combine this with Report I using some such message as "FOR/
NEXT
mismatch"?
Exit from:
1DAB NEXT

REPORT 2 0670, 1C2E


"Variable not found" (13A4)
This error message is printed
- if you try to SAVE or VERIFY or put values in an array
which hasn't already been named by a DIM statement
- if a NEXT refers to a variable which doesn't exist at
all
- if a LET statement or similar tries to take values from
a variable which hasn't been given any.
Exit from:
0652 SA DATA
1C22 VAR A 1
1DAB NEXT
26C9 S LETTER

REPORT 3 2A20
"Subscript wrong" (13B6), misprinted "Subscript out of
range" at 2A20.
This error message is printed
- if an array is referred to without specifying enough
subscripts
- if the subscript is too big or too small
- if there is no ")" after the subscripts
- if a comma is missing between them
- if a slicing command is incompatible with the

714
dimensions
- if a DIM command tries to set a dimension less than one
or more than 255d.
Exit from:
29AE SV ARRAYS
29C3 SV COMMA
29FB SV MULT
2A12 SV RPT C
2A2C SV ELEM$
2A94 SV DEFINE
2C2E D NO LOOP

REPORT 4 1F15
"Out of memory" (13C6)
This error message is printed
- if any command would result in a memory overflow, eg a
DIM command asking for a huge array, or you try to load a big
BASIC program when 5CB2 RAMTOP has been set low down
- rather inappropriately, if there is overflow in the
2AFD GET HL*DE routine. This is logical enough when as in
the ROM the routine is only used to calculate the amount of
room needed for dimensioned arrays.
The implementation of this routine is a little different
from the others: 0008 ERROR 1 and 0053 ERROR 2 are by-
passed.
The note on 1F15 is cryptic to the point of bafflement: if
instead of saying "the error marker [a seriously ambiguous
expression] isn't to be used" the authors had got really
garrulous and said "the error cursor pointer isn't needed",
they would have saved this author an hour or two of study.
The point is simply that memory overflow is never
detected by syntax checking, which systematically skips over
all operations which might produce it; it is an error produced
only by actual execution. If it arises from input or editing, the
result is a "groan" and a refusal to accept the ENTER
keystroke;

715
if from execution of BASIC in_run-time, the result is an error
message. What is never required is a reprint of the input with
an error cursor, so the error cursor pointer 5C5F X PTR is
irrelevant; always to be distinguished - except in the notes! -
from the error number in 5C3A ERR NR and the error stack
pointer 5C3D ERR SP.
The authors of the notes have missed the point that it
would have done no harm to use the full error restart anyway,
and RST 8h followed by byte 03 is three bytes shorter than the
coding actually used in the ROM.
Exit from:
1F05 TEST ROOM (3 times)
2AF4 GET HL*DE
2C2E D NO LOOP

REPORT 5 OC86
"Out of screen" (13D2)
This error message is printed if you try to print below
the bottom of the available screen, eg with an AT command.
Exit from:
0AAC PO AT ERR
0C55 PO SCR
0D02 PO SCR 4

REPORT 6 31AD, 3703


"Number too big" (13DF)
This error message is printed if you specify a number
too big to handle, eg BIN 10000000000000000, more than
65535d,
or 2/0, or 10**40; the Spectrum can print 1e38 but not 1e39.
Labelled "Arithmetic overflow" at 31AD; a misprint, but
it comes to the same thing.
Exit from:
2CA2 BIN DIGIT
2CFF ST E PART (twice)
309F ADD REP 6
313D DIVN EXPT

716
3146 OFLW1 CLR
3186 NORML NOW
31AF division
36C4 EXP (twice)

REPORT 7 1F36 see also GO SUB stack


"RETURN without GO SUB" (13ED)
This error message is printed if a RETURN command
produces the return address 3E00h/15872d; this is the end
marker of the GO SUB stack, so it means there are no more
return addresses available.
Exit from:
1F23 RETURN

REPORT 8 15E4
"End of file" (1401)
This error message isn't used by the standard Spectrum,
on which the only input subroutine is 10A8 KEY INPUT, the
keyboard routine. But peripherals such as Interface 1 can
open other channels including channels with other input
commands, such as "PRINT INKEY$ (hatch)4;". These must be
so arranged that if the peripheral has no data available for
input, the input routine returns with NC and NZ, which will
produce the error message. Label not used in ROM.
Exit from:
15DE WAIT KEY1

REPORT 9 1CEE
"STOP statement" (140C)
Called only from 1A8A P STOP in the syntax parameter
table; in fact the executive routine of the STOP command. It
operates just like any other report routine, so that if STOP is
used in a BASIC line or command the effect is to print this
"error message" and await a new input.
See also Report H above, "STOP in INPUT".

REPPER system variable 5C0A

717
Bytes: 1.
See also 5C09 REPDEL. The time in 50ths of a second
before a pressed key will repeat_after the first time; 60ths of a
second in the USA. It is set at 5 on start-up and not changed
thereafter: if a different value is POKEd, it will be reset to 5 by
a NEW command. For the use of REPPER see NB 2 under
KEYBOARD
SCANNING.
5C09 REPDEL and REPPER are handled as a single 2-byte
variable in 1219 RAM SET.
Written by:
1219 RAM SET
Read by:
0310 K REPEAT

RESERVE 169E (0030 BC SPACES)


Exit from:
0030 BC SPACES

RESET subroutine 0066


What could have been an occasionally useful subroutine
is vitiated by a mistake in the ROM: the sixth line reads JR NZ,
NO RESET, but it was obviously intended to read JR Z,NO
RESET.
The result is that if 5CB0 NMIADD is given any other value
than zero, the subroutine returns without action; if it is zero,
the computer resets itself and blanks its memory completely!
Fortunately there is no call from ROM.
If the mistake were corrected, it would be possible to
write m/c programs giving a subroutine address to 5CB0
NMIADD, which would then be called by some peripheral
activating the non-maskable interrupt.

re-stack subroutine 3297


Called from 0028 FP CALC with the literal 3D; also
called direct in one case. See also 3293 RE ST TWO, which is a
double call of re-stack.

718
It converts a 5-byte FP number X to full 5-byte FP
format; see CALCULATE. If X is already in full FP format, it is
unchanged: the routine is only useful if X may be in small
integer format, marked by the 1st of the 5 bytes being zero. In
the ROM calls, X is nearly always the last value on the
calculator stack, but this isn't essential for direct calls.
In many cases it is used just before "end-calc"; the
point is to be able to read the exponent of X directly from the
calculator stack. In two cases, 36C4 EXP and 3783 get-argt, re-
stack is followed by a multiplication, and seems pointless, as
the 21DF multiply subroutine would restack X in any case.
If the small integer is in the DE register, the required
full format version is XX DD EE 00 00, where
- XX is the exponent, which must be determined
- DD is the byte in D, with its hi bit marked for
positive or negative
- EE is the byte in E.
The conversion is executed as follows:
- put zeroes in the last two bytes
- put 80+11h in the exponent byte for starters: this is
the maximum possible for a small integer
- test the hi byte DD for zero; if it isn't zero the
exponent will be more than 80+9h
- test the lo byte EE for zero; if both bytes are zero,
all five bytes of the FP number are left at zero
- if the hi byte is zero but the lo byte isn't, put 80+9h
in the exponent, now the maximum possible, move the lo
byte to the hi byte and zero the lo byte.
The five bytes are now a correct true exponent and true
mantissa for X, and only require to be normalized:
- if the mantissa has any leading binary zeroes, they are
left-shifted out of it, with a decrement of the exponent for
each shift
- the hi bit of the mantissa is zeroed for positive X,
set for negative.
Input parameters: HL points to the first byte of X.
Action: if the first byte isn't zero, return immediately;

719
X is already in full format
- save DE; usually a pointer to the calculator stack
- call 2D7F INT FETCH to get ABS X and the sign flag
into the registers; it leaves a pointer on the fourth byte
- put zeroes in the last two bytes
- make a provisional exponent 80+11h
- if the hi byte of X isn't zero jump on to RS NRMLSE
- (hi byte is zero) if the lo byte is zero jump on to RS
STORE; X is zero
- (hi byte is zero but lo byte isn't) move the lo byte
to the hi byte and zero the lo byte
- put 80+9h in the exponent.
_32B1_RS_NRMLSE: switch the pointers; X is now in HL, or
maybe in H only.
_32B2_RSTK_LOOP: decrement the exponent
- double X; this shifts it one bit left (a misprint in
the notes)
- if there is no carry jump back to RSTK LOOP; the shift
got rid of a leading zero
- (there is carry) rotate the hi bit of the sign byte
into the carry
- rotate both bytes of X right, with the carry going
into the hi bit; the hi bit will be zero if the flag was 00/
positive, one if it was FF/negative.
_32BD_RS_STORE (the normalized FP format bytes for the
exponent and the first two mantissa bytes of X are in B, D and
E
registers): put BDE in the first three bytes of the FP number;
the last two are already zero
- recover DE.
Exit: RET
- from RE STACK if X was already in full format
- from RS STORE if X was zero or a small integer.
Output parameters: HL and DE unchanged.
Called with literal 3D from:
2320 CIRCLE
23A3 DR SIN NZ

720
36C4 EXP
3713 ln
3783 get-argt
Called direct from:
37E2 atn
Exit from:
3296 RESTK SUB (3293 RE ST TWO)

RESTACK TWO SUBROUTINE see 3293 RE ST TWO

restart routines
RST 00H START resets the computer.
RST 08H ERROR 1 notes the point reached in execution,
reads the report value, clears the machine stack except for the
error return address, clears the calculator stack, and returns
to the preset error return address - usually 1303 MAIN 4,
which prints an error report.
RST 10H PRINT A 1 outputs a byte through the current
channel.
RST 18H GET CHAR makes HL a BASIC pointer to the
address
currently in 5C5D CH ADD, or to the next printable character
if
the one at 5C5D CH ADD is unprintable, and loads the code.
RST 20H NEXT CHAR same as GET CHAR, except that
5C5D CH
ADD is first incremented.
RST 28H FP CALC the floating point calculator.
RST 30H BC SPACES makes extra room in the work space.
RST 38H MASK INT works the clock and reads the
keyboard.
Introduction all possible restarts are used
0000 START heading RESTART ROUTINES

RESTK SUB subroutine 3296


Not a real subroutine, just a dodge to be able to call

721
3297 re-stack twice in succession with a switch of pointers.
See
3293 RE ST TWO.

RESTORE key (E5) see also commands, functions and


operators,
KEYBOARD SCANNING, 022C extended mode table (b)
The S key in E mode without shift produces the command
RESTORE. The command requires a line number, but zero is
used if none is supplied by BASIC.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1ACF P RESTORE calls 1C0D CLASS 03 to fetch the line
number before jumping via 1C16 JUMP C R to the executive
routine 1E42 RESTORE.

RESTORE subroutine 1E42


Called only from 1ACF P RESTORE in the syntax
parameter table to execute the RESTORE X command. The
entry point 1E45 REST RUN is called from the 1EA1 RUN
command routine; in this case X is supplied in the BC register
instead of on the calculator stack, which incidentally makes it
much simpler to use from m/c.
5C57 DATADD marks the last address in the program from
which DATA was READ. The subroutine resets 5C57 DATADD
to mark the start of the line whose number is X from the
RESTORE X command, or the first existing line after it.
Input parameters: none
- X is on the calculator stack.
Action: call 1E99 FIND INT2 to get X off the stack.
_1E45_REST_RUN (called by 1EA1 RUN with zero already in
BC): call 196E LINE ADDR to find the start address of line X, or
the next after it if it doesn't exist
- decrement the address; so it points to the end of the
line before
- put this address in 5C57 DATADD.

722
Exit: RET, from 1E45 REST RUN.
Output parameters: none
- X is off the stack.
Rems:
1EA1 RUN always does RESTORE 0

restoring division see 31AF division

REST RUN subroutine 1E45


See 1E42 RESTORE.
Called from:
1EA1 RUN
Rems:
1E42 RESTORE used by RUN routine

RE ST TWO subroutine 3293


See 3297 re-stack. This merely calls it twice so as to
restack two numbers X and Y. In the ROM calls they are
always the last two on the calculator stack, but this isn't
essential.
This is done for all four basic arithmetic operations unless
both operands and the result come out as "small integers".
Input parameters: HL and DE hold the start addresses of X
and Y respectively; Y would be last on the stack.
Action: call RESTK SUB
- immediately exit into it.
_3296_RESTK_SUB: switch HL and DE.
Exit: into 3297 RE STACK, which restacks the number
addressed by HL and returns with HL and DE unchanged.
Output parameters: the pointers have been restored to
their starting values, after being exchanged twice.
Called from:
303E FULL ADDN
30F0 MULT LONG
31AF division

RESULT OK 370C (36C4 EXP)

723
Exit from:
36C4 EXP (twice)

RETURN key (FE) see also commands, functions and


operators,
KEYBOARD SCANNING
The Y key in K mode produces the command RETURN,
which accepts no operands. The command is read by 1B29
STMT L 1
referring through the syntax offset table 1A48 to the syntax
parameter table 1A7A. 1A8D P RETURN causes a jump via 1C10
CLASS
00 and 1C16 JUMP C R to the executive routine 1F23 RETURN.

RETURN subroutine 1F23 see also GO SUB stack, 1B9E LINE


NEW
under 1BBA LINE RUN
Called only from 1A8D P RETURN in the syntax parameter
table; executes the RETURN command by taking a line
number and statement number, three bytes or one-and-a-half
POPs in all, from the GO SUB stack and transferring BASIC
execution to them.
Input parameters: none.
Action: POP the return address of the statement loop,
1B76 STMT RET; this is held and put back on the stack later
- POP the error address marked by the error stack
pointer 5C3D ERR SP; also put back later
- POP two bytes from the GO SUB stack; the RETURN line
number of the last GO SUB command, or the end marker
3E00 of the GO SUB stack if there is none.
- if the hi byte of the POP is 3E report "RETURN without
GO SUB"; it can't be a line number, 3E00h would be line
15872.
- move the stack pointer up one; DEC SP, this stack is
upside down

724
- exchange the top two bytes with the error address; the
hi byte of the POP holds the statement number, the lo byte
holds the hi byte of the line number again
- move the statement number into the D register; for GO
TO 2
- make the error stack pointer 5C3D ERR SP point to the
new stack location of the error address
- put the return address 1B76 STMT RET back on the
stack.
Exit: into 1E73 GO TO 2, which puts the line and
statement numbers in svs determining the next statement to
be executed and returns to 1B76 STMT RET.
Output parameters: HL holds the RETURN line number
- D holds the statement number.

ripple see precision

R I STORE 365F (3645 read-in)


Jumps from:
3645 read-in

RND key (A5) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The T key in E mode without shift produces the function
RND; like PI and INKEY$, it is a function without an argument
- from the point of view of BASIC. In fact it uses the 2-byte
value in 5C76 SEED as argument to produce a random
number.
On execution, 24FB SCANNING indexes into the scanning
function table at 2596 to find the executive routine 25F8 S
RND.
0C35 PO TRSP it doesn't require trailing space

rounding see precision

RSLT ZERO 370E (36C4 exp)


Exit from:

725
36C4 exp through
3705 N NEGTV (twice)

RS NRMLSE 32B1 (3297 re-stack)


Jumps from:
3297 re-stack

RS STORE 32BD (3297 re-stack)


Exit from:
3297 re-stack

RSTK LOOP 32B2 (3297 re-stack)


Jumps from:
auto

RUN key (F7) see also commands, functions and operators,


KEYBOARD SCANNING
The R key in K mode produces the command RUN. The
command requires a line number as operand, but if none is
given in the BASIC it assumes zero.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AAB P RUN calls 1C0D CLASS 03 to collect the line number
and then jumps to the executive routine 1EA1 RUN.
Introduction - exemplar of execution considering
multiple statements

RUN subroutine 1EA1


Called only from 1AAB P RUN in the syntax parameter
table; executes the RUN X command, equivalent to CLEAR:
RESTORE 0: GO TO X. X is zero if no line number is supplied;
RESTORE operates with zero whether one was supplied or
not.
Input parameters: none
- X is last value on the calculator stack.
Action: call 1E67 GO TO, which sets the svs so that the

726
return to the statement loop from 1EDC CLEAR will execute
from the start of line X
- call 1E45 REST RUN with zero, to set the DATA pointer
in 5C57 DATADD to the start of the program.
Exit: to 1EAF CLEAR RUN (misprinted), which deletes all
variables, clears the screen, and goes on to 1B76 STMT RET
which handles the jump to line X.
Output parameters: none
- X has gone from the calculator stack.
Rems:
1E42 RESTORE uses REST RUN entry point
1EAF CLEAR RUN no change to RAMTOP
1EDC CLEAR 2 always counts as last statement in BASIC
line - any following cannot be executed

running, run-time see FLAGS bit 7

run-time errors see 1F15 REPORT 4

727
S

SA ALL 075A (0605 SAVE ETC)


Jumps from:
0692 SA DATA 1
0710 SA TYPE 3

SA BIT 1 0514 (04C2 SA BYTES)


Jumps from:
0525 SA 8 BITS
auto
Rems:
0511 SA BIT 2 timing loop for "off" or "on" bytes

SA BIT 2 0511 (04C2 SA BYTES)


Jumps from:
051C SA OUT

SA BLANK 0629 (0605 SAVE ETC)


Jumps from:
auto

SA BYTES subroutine 04C2 see also 0556 LD BYTES, ports


(EAR input etc), program/data block, timing
Records a string of bytes on cassette tape, either a
header block or a program/data block. The actual recording
consists of:
- a leader, 5 seconds for a header block or 2 seconds for
a data block, pulsing at about 800 hz and signalled by RED/
CYAN stripes in the border
- a single sync pulse
- a single flag byte, zero for a header and FF for a data
block
- the bytes of the header or data block

728
- the parity byte.
Each of the bytes is recorded bit by bit, long pulses
for bit one of 2 * 1710d T states, about 1024d hz, and short
pulses for bit zero of 2 * 855d T states, about 512d hz, and
signalled by YELLOW/BLUE stripes in the border.
The_parity_byte is a device to check that no bytes are
missed or incorrect when a file is loaded from tape. It isn't
infallible, but its failure rate is low. Before SAVing starts,
the parity byte is made to match the first byte to be saved, the
header/data flag (SA START). Each byte SAVed is XORed
cumulatively with this parity byte (SA LOOP P); when all the
bytes of the block have been saved the parity byte is also
output to the tape (SA PARITY). On loading, the parity byte is
first zeroed (058F LD SYNC), then XORed with each new byte
as it is loaded (05CA LD 8 BITS). When everything has loaded,
the parity byte is finally XORed with the last byte on the tape,
the SAVed parity byte, and this should give zero.
Input parameters: DE holds the length; the number of
bytes, excluding the flag and parity byte
- IX holds the address of the first byte to be recorded
- A holds a header/data flag; zero for a header, FF for
a data block.
Action: stack the return address 053F SA/LD RET; all RETs
will go to there
- make a pulse counter 1F80h; this will actually produce
2080h/8320d half pulses taking 8320 * 2168 = 18,037,760d T
states, a little over 5 seconds, for the header
- if the hi bit of the header/data flag is zero jump on
to SA FLAG; header
- (data block) reset the pulse counter to 0C98h; it will
produce 0D98h/3480d half pulses taking 3480 * 2168 =
7,544,640d T states, a little over 2 seconds, for a data block
_04D0_SA_FLAG: increment the length and decrement the
start; to allow for the flag
- turn off the interrupt; the keyboard scan would upset
the timing
- prepare a port signal 00000010b/02; zero in bit 3

729
turns MIC on, 010 in the lo bits makes the border RED
- make the delay counter a low number; the 2 in the port
signal is convenient, but any low number would have done.
_04D8_SA_LEADER: a one-line delay loop; 13d T states for
each turn with the delay counter, but only 8 for the exit. With
delay counter C the delay is 13 * C - 5d T states; so on entry,
with only two turns, 21d
- output the port signal
- XOR it with 00001111b/0Fh; changing 00000010b/02
MIC on and RED to 00001101b/0Dh MIC off and CYAN and
back for each half pulse.
- make the delay counter A4h/164d
- decrement the pulse counter
- if it isn't zero yet jump back to SA LEADER; this time
the delay loop will take 13 * 164 - 5 = 2127d T states, to which
must be added 41d for the rest of the half-pulse loop, so each
half pulse takes 2168d T states
- (the lo byte of the pulse counter has reached zero)
decrement the hi byte and the delay counter
- if the hi byte isn't_negative yet jump back to the SA
LEADER delay loop; using JP P instead of JR NZ gives the loop
an extra turn. The reason for using it is to make the timing
exact,
because JP is slightly faster than JR. This time the delay will
be 13d T states less, which exactly compensates for the
additional m/c, and the half pulse again takes 2168d T states
- (the pulse counter is negative, all the pulses have
been output) make a new delay counter 2Fh/47d.
_04EA_SA_SYNC_1: an exactly similar delay loop; on entry
it will take 13 * 47 - 5 = 606d T states, which with the 61d
inclusive from the last OUT makes 667d
- output the port signal; it will be on/RED again after
an even number of loops
- make the signal 00001101b/0Dh for off/CYAN
- make a new delay counter 37h/55d.
_04F2_SA_SYNC_2: another identical delay loop; 55d turns
of this one plus the 25d T states inclusive from the last OUT

730
makes 13 * 55 - 5 + 25 = 735d T states from on to off
- output the signal for off/CYAN
- make a new delay counter 3Bh/59d and port signal
00001110b/0Eh; bit 4 signalling MIC off and the lo bits
signalling 110b/06 YELLOW
- get the header/data flag; the first code byte to be
output, and also used as the starter for the parity bit
- jump on to SA START; the entry point for the loop
extending from SA LOOP down to the end of SA 8 BITS, each
turn of which will save one code byte in the L register. The
loop will be taken out of order, for clarity's sake.
_0507_SA_START: save the parity; at the start it is 00
for a header, FFh for a program/data block
- make a port signal 00000001b/01 MIC on/BLUE
- set carry as the marker for an 8-bit loop (see index
entry after the end of the alphabet)
- jump on to SA 8 BITS.
_0525_SA_8_BITS: rotate the code byte left; the carry
goes into its lo bit, its hi bit goes into the carry
- if the resulting byte isn't zero jump back to SA BIT
1, the inner bit-saving loop, see below; it still has bits to be
saved. It can't be zero till eight turns are complete, because
of the set carry on entering the loop, but a zero bit comes in
from the right after each bit pulse loop, because of the XOR in
SA OUT
- (it is zero, that code byte is finished with) count
down the block length counter
- step on the code byte pointer
- set the delay counter to 31h/49d
- if port 7FFE shows BREAK or SPACE is being pressed,
return; to SA LD/ RET
- if the hi byte of the counter isn't yet_negative jump
back to SA LOOP to save another code byte; it reaches zero
when the block has all been saved, but there is still the parity
byte to save
- (everything has been saved) make a final delay counter
3Bh/59d.

731
_053C_SA_DELAY: another delay loop; the delay is 59 * 13
- 5 = 762d T states, recording a little silence on the tape just
to make sure the last bit's "off" is recognizable
- return; to SA LD/RET.
_04FE_SA_LOOP: test the length counter
- if it is zero, jump on to SA PARITY; this must be the
parity byte
- get the new code byte at the pointer.
_0505_SA_LOOP_P: XOR the new code byte with the parity
byte; continues the loop into SA START to save the new code
byte, see above.
_050E_SA_PARITY (entered from SA LOOP after all the
code bytes of the header or data block have been saved and
counted down): put the parity in the code byte
- jump back to SA LOOP P; the parity byte as code byte
is there XORed with itself as parity byte, which must give zero,
but the result is put in H which up to now has held the parity
byte, and this H is never used again.
_0514_SA_BIT_1 (the inner loop, outputting individual
bits, is entered here from SA 8 BITS and is turned twice, once
for the "on" half-pulse and again via SA BIT 2 for the "off". On
entry carry is set for a long pulse representing a one, NC for a
short pulse representing zero, and the carry flag isn't
disturbed throughout both turns of the loop, so both the on
and off halves are long or short accordingly): start with a delay
loop; using the counter set at
3Bh/59d in SA SYNC 2 for the very first entry, making
a delay of 59 * 13 - 5 = 762 T states, plus 82d inclusive since
the last OUT in SA SYNC 2 makes 844d
31h/49d in SA 8 BITS for the first entry of each
subsequent code byte, making a delay of 49 * 13 - 5 = 632 T
states, plus 208d inclusive since the last OUT in SA OUT makes
840d
3Eh/62d in SA OUT for the "off" loop of each bit,
making a delay of 62 * 13 - 5 = 801 T states, plus 42d inclusive
since the last OUT in SA OUT makes 843d
3Dh/61d in SA OUT (reduced after the JR) for the "on"

732
loop of each bit after the first in the byte, making a delay of
61 * 13 - 5 = 788 T states, plus 55d inclusive since the last
OUT in SA OUT makes 843d
- if carry isn't set (zero bit) jump on to SA OUT; the
jump takes 12d T states, a total of 855d - give or take a few!
- ("one" bit) make a delay counter 42h/66d.
_051A_SA_SET: another delay loop; this one is 66 * 13 - 5
= 853d, plus 7 for not jumping and another 7 to load the
counter, a total of 1710d, making the longer pulse for the
"one".
_051C_SA_OUT: output the port signal
- make a delay counter 3Eh/62d
- if the flag shows NZ jump back to SA BIT 2; the Z flag
remains at NZ from SA 8 BITS to here on the "on" turn of the
bit pulse loop
- (the flag was zeroed in SA BIT 2, so if it is now Z
this must be the "off" turn) decrement the delay counter to
equalize the half-pulse lengths
- clear the carry; this NC will be rotated into the code
byte as a zero
- make the port signal 01; MIC on and BLUE
- continue into SA 8 BITS to collect the next bit.
_0511_SA_BIT_2 (the start of the "off" half-pulse): make
a port signal 0Eh; MIC off and YELLOW, a value carried in the
C register from SA SYNC 2 to the end of the routine
- make a Z flag from the hi bit of the delay counter; it
is never as much as 80h, so reading it makes the flag without
disturbing the carry, etc
- re-enter SA BIT 1.
Exit: RET, from SA 8 BITS on a BREAK or from SA DELAY;
it will be to 053F SA/LD RET which was preloaded on the
machine stack. This restores the original border colour and
enables the interrupt.
Output parameters: none.
Called from:
0970 SA CONTRL
Exit from:

733
0991 SA 1 SEC
Rems:
058F LD SYNC parity zeroed initially
05A9 LD LOOP parity is last byte loaded
05CA LD 8 BITS new byte XORed with parity byte
0970 SA CONTRL header saved with parity byte

SA CODE 06C3 (0605 SAVE ETC)


Jumps from:
06A0 SA SCR$

SA CODE 1 06E1 (0605 SAVE ETC)


Jumps from:
06C3 SA CODE
SA CODE 2 06F0 (0605 SAVE ETC)
Jumps from:
06C3 SA CODE

SA CODE 3 06F5 (0605 SAVE ETC)


Jumps from:
06E1 SA CODE 1

SA CODE 4 06F9 (0605 SAVE ETC)


Jumps from:
06F0 SA CODE 2

SA CONTRL 0970 (0605 SAVE ETC)


Jumps from:
075A SA ALL
Rems:
04C2 SA BYTES uses SA BYTES at 098A (3rd last line)

SA DATA 0652 (0605 SAVE ETC)


Jumps from:
0605 SAVE ETC
0644 SA NULL

734
SA DATA 1 0692 (0605 SAVE ETC)
Jumps from:
0672 SA V OLD

SA DELAY 053C (04C2 SA BYTES)


Jumps from:
auto

SA FLAG 04D0 (04C2 SA BYTES)


Jumps from:
04C2 SA BYTES

SA/LD END 0554 (053F SA LD/RET)


Jumps from:
053F SA/LD RET

SA/LD RET 053F


The common exit from the routines for SAVing and
LOADing bytes; a few tidying-up tasks.
Input parameters: none
- if the carry flag is NC LOADing has failed or BREAK
has been pressed.
Action: save the flag
- get the old border colour from 5C48 BORDCR
- AND it with 00111000b/38h to isolate the PAPER colour
- shift it right 3 times to make it the colour number
- output it to port FE for the border
- read port 7FFE for the BREAK signal; either BREAK or
space will produce the signal. It may have been read already,
this is to ensure that a BREAK won't be misinterpreted as a
LOAD error
- rotate the lo bit of the port input into carry
- enable the interrupt
- if carry is set jump on to SA LD/END; no BREAK
- report "BREAK - CONT repeats".
_0554_SA/LD_END: recover the carry flag.
Exit: RET.

735
Output parameters: none
- a set carry flag still signals a successful LOAD.
Exit from (indirect jumps: the address is stacked at the
start of each routine):
04C2 SA BYTES
0556 LD BYTES
Rems:
0525 SA 8 BITS return on BREAK

SA LEADER 04D8 (04C2 SA BYTES)


Jumps from:
auto (3 times)

SA LINE 0716 (0605 SAVE ETC)


Jumps from:
06C3 SA CODE

SA LINE 1 0723 (0605 SAVE ETC)


Jumps from:
0716 SA LINE

SA LOOP 04FE (04C2 SA BYTES)


Jumps from:
0525 SA 8 BITS

SA LOOP P 0505 (04C2 SA BYTES)


Jumps from:
050E SA PARITY

S ALPHNUM 2684 (24FB SCANNING) see also BASIC


INTERPRETER
Jumps from:
24FF S LOOP 1

sampling loop see 05E7 LD EDGE 1

SA NAME 064B (0605 SAVE ETC)

736
Jumps from:
0629 SA BLANK

SA NULL 0644 (0605 SAVE ETC)


Jumps from:
0629 SA BLANK

SA OUT 051C (04C2 SA BYTES)


Jumps from:
0514 SA BIT 1

SA PARITY 050E (04C2 SA BYTES)


Jumps from:
04FE SA LOOP

SA SCR$ 06A0 (0605 SAVE ETC)


Jumps from:
0652 SA DATA

SA SET 051A (04C2 SA BYTES)


Jumps from:
auto

SA SPACE 0621 (0605 SAVE ETC)


Jumps from:
0605 SAVE ETC

SA START 0507 (04C2 SA BYTES)


Jumps from:
04F2 SA SYNC 2

SA SYNC 1 04EA (04C2 SA BYTES)


Jumps from:
auto

SA SYNC 2 04F2 (04C2 SA BYTES)


Jumps from:

737
auto

S ATTR subroutine 2672


Called only from the scanning function table at 2596;
the executive routine of the ATTR (X,Y) function. Finds the
value of the attribute code at the X,Y position on the screen; X
and Y are coordinates in the BASIC AT system, see DISPLAY
AREA.
For the attribute lines, see colours. They comprise
8 starts with hi byte 58h corresponding to X = zero -> 7
8 starts with hi byte 59h corresponding to X = 8 -> 0Fh/15d
8 starts with hi byte 5Ah corresponding to X = 10h -> 17h/23d
Thus the hi byte of the attribute line start can be
found by dividing X by 8, discarding the remainder, and
adding the result to 58h.
Within each third, the lo bytes of the line starts are
00h, 20h, ... E0h, ie 20h times the remainder on dividing X
by 8; and the lo bytes in each line run from this value to 1Fh
more, given by Y, so the lo byte is 20h * (X modulo 8) + Y.
Cf rather similar calculations performed in 0BDB PO
ATTR and 0E88 CL ATTR.
Most of the action is in S ATTR S, which is formally a
free-standing subroutine, but only called from here; this is a
device to abbreviate the S ATTR routine and enable all the
routines indexed in the scanning function table to be fitted
into 100h/256d bytes, the range of a table offset. So the
description of S ATTR S is included here.
Input parameters: none.
Action: call 2522 S 2 COORD to read X and Y from the
BASIC and put them on the calculator stack
- call S ATTR S; see below
- move the BASIC pointer on to the next character; ie
after the ")"
- exit to S NUMERIC.
_2580_S_ATTR_S: call 2307 STK TO BC to take X and Y off
the stack into registers
- rotate X right three times: if X in binaries is

738
000abcde, this produces cde000ab; ab is the result and cde
the remainder on dividing X by 8, X modulo 8
- save cde000ab
- AND it with 11100000b/E0h, producing cde00000;
cde00000 is 20h times the remainder
- XOR cde00000 with Y; the result is the value for the
lo byte of the attribute address
- AND cde000ab with 00000011b/03 to get 000000ab;
this is zero, one or 2 for original line numbers zero -> 7, 8 ->
15d and 16 -> 23d respectively
- add this to 58h for the hi byte
- read the code at this attribute address
- exit into 2D28 STACK A to put the code on the
calculator stack before returning to 2672 S ATTR.
Exit from S ATTR: into 26C3 S NUMERIC, which signals
"numeric result" and returns into the expression scanning
loop.
Output parameters: none
- the attribute code is on the calculator stack.
Rems:
2522 S 2 COORD called by S ATTR

S ATTR S subroutine 2580


See 2672 S ATTR above.
Called from:
2672 S ATTR

SA TYPE 0 073A (0605 SAVE ETC)


Jumps from:
0716 SA LINE

SA TYPE 3 0710 (0605 SAVE ETC)


Jumps from:
06A0 SA SCR$

SAVE key (F8) see also commands, functions and operators,


KEYBOARD SCANNING

739
The S key in K mode produces the command SAVE. The
command is read by 1B29 STMT L 1 referring through the
syntax offset table 1A48 to the syntax parameter table 1A7A.
1ADF P SAVE causes an immediate jump via 1CDB CLASS 0B to
the executive routine 0605 SAVE ETC; T ADDR has been
incremented and holds 1AE0, which marks the execution as a
SAVE.

SAVE CONTROL ROUTINE see 0970 SA CONTRL

SAVE ETC subroutine 0605


The exit routine from 1CDB CLASS 0B, which is called
only from the 1A7A syntax parameter table as the executive
routine for all four cassette operations SAVE, LOAD, VERIFY
and MERGE.
A SAVE command produces an 11h/17d byte_header,
which is saved ahead of the data file and contains information
about it.
There is a one-second delay between the header and the file.
[This delay is actually quite unnecessary; in my own m/c
programs I have rewritten parts of the routine to cut out this
delay, which I found tiresome when I was using cassette tape
extensively, with no ill effect at all.]
Headers are handled using the IX indexing register:
Byte IX+0 is the_type flag:
00 for a header, or for a program
01 for a number array
02 for a string array
03 for CODE, including SCREEN$
Bytes IX+1 to IX+A are the_file_name. The name is a
string of up to 10d characters, filled out with spaces,
originated by being input within quotes or read as the value of
a string variable, as part of the BASIC command. Null names
aren't accepted for SAVE commands.
Bytes IX+B (lo) and IX+C (hi) are the length of the file
in bytes.
For programs, the length is read from the svs; in this

740
case the length includes the variables area as well as the
program.
For arrays, the length is read from the variables area.
For SCREEN$, the length is always 1B00h/6912d bytes;
display area plus attributes area.
For CODE, the length must be specified in the SAVE
command.
Bytes IX+D (lo) and IX+E (hi):
For program files, they are the LINE number from which
the program is to run on loading, or 8000h if none is given
For CODE files, they are the start address of the file,
ie the address from which it is to be loaded into memory; this
must be specified in the SAVE command
For SCREEN$ files, they are always 4000h/16384d, the
start of the display area
For array files, IX+E is the array letter, and IX+D
isn't used.
Bytes IX+F (lo) and IX+10 (hi) are only used for
programs: they are the length of the program itself, not
counting the variables which were saved with it.
LOAD/MERGE/VERIFY commands make 22h/34d spaces
in the work space: the_header_area. The header being looked
for is constructed in the first 11h/17d bytes of this area, and the
header found on tape is loaded into the second 11h/17d bytes.
I will call these
the_first_and_second_WS_(work_space)_headers;
the notes usually call them the "old" and the "new" headers.
When a LOAD, MERGE or VERIFY command is executed
the header is read from tape into the second WS header, and
printed on screen; but loading is only executed if the type
flags and the names in the two headers match. If no name is
given in the command, FFh followed by nine spaces is put in
the first header, and this is accepted as a match for any name
on tape.
The SAVE ETC routine therefore:
- prepares an operation flag to mark which of the four
operations is being performed

741
- creates a header area of 11h/17d bytes in the work
space, immediately followed by a duplicate 11h/17d byte
space, except in the case of SAVE
- prepares a "start pointer": in the case of SAVE
commands this is the address of the first byte to be SAVEd, in
the case of LOAD commands the address where the first byte
from tape is to be put.
- fills the first WS header with data from the BASIC
command.
These are the only preliminaries required for the SAVE
operation, but for the other three operations:
- a header is loaded from the tape and its bytes copied
into the second WS header
- the second WS header is compared with the first and its
file name and type are displayed on screen
- if the types or file names in the two WS headers don't
match, the program jumps back and waits for another header
from the tape.
Here (LD CH PR) the execution jumps three ways
depending on the operation and type flags; all variants exit
into LD BLOCK for the actual loading, except the MERGE
variant which calls LD BLOCK as a subroutine before merging
the new program lines and
variables into the old ones.
If the operation is VERIFY jump to VR CONTRL.
If the type flag is 3 (SCREEN$ and CODE) jump to VR
CONTRL.
If the operation is LOAD jump to LD CONTRL.
If the operation is MERGE jump to ME CONTRL.
[There is an error in 0652 SA DATA/0672 SA V OLD,
which can be tiresome for some kinds of operation. To see the
effect of the error
- make a simple string, eg LET h$ = "hello"
- save it as if it was an array with SAVE "h" DATA h$();
this ought to produce an error report, but doesn't
- LOAD this pseudo-array off tape with LOAD "h" DATA
h$(); it loads without difficulty, but you can't get at it in

742
any way. PRINT h$, PRINT h$(1), PRINT h$( TO ), PRINT h$
( TO LEN h$), LET g$ = h$, and so on all produce "Subscript
wrong" or "Nonsense in BASIC" reports.
There is an error trap, JP NZ,1C8A REPORT C at the start
of SA V OLD, depending on the very complicated output
parameters of 28B2 LOOK VARS, but it doesn't work. LOOK
VARS distinguishes simple strings from arrays with the Z/NZ
flag only in syntax checking time, and doesn't actually look in
the variables area at all: it reports an array if the variable
letter and "$" are followed by "(". Of course in a SAVE or
LOAD ... DATA command they always are, so the JP NZ line
might have been left out for all the good it does.
It looks almost as if the programmers thought they were
making it possible to SAVE a simple string as an array,
overlooking the difficulties implied by the structure of the
variables.]
Input parameters: none
- 5C74 T ADDR holds one more than the address in the
1A7A parameter table of the P routine selected by the BASIC; it
was incremented in 1B55 GET PARAM. This is an indicator of
the type of operation to be performed. It will be 1AEO, 1AE1,
1AE2 or 1AE3 for SAVE, LOAD, VERIFY and MERGE
respectively.
Action: drop the 1B52 SCAN LOOP return address; return
will now be to 1B76 STMT LOOP
- take E0h from the value of 5C74 T ADDR lo to make an
"op flag" and replace it in 5C74 T ADDR lo; now 00 for SAVE,
01 for LOAD, 02 for VERIFY, 03 for MERGE
- call 1C8C EXPT EXP to get the string parameters of the
file name on the calculator stack
- make the length of the header area 11h/17d bytes; for
SAVE
- if 5C74 T ADDR lo is zero jump on to SA SPACE
- (not SAVE) make the length 22h/34d bytes.
_0621_SA_SPACE: call 0030 BC SPACES to make space in
the work space for the headers
- set the pointer IX on the start of the first WS header

743
- make a counter for 0Bh/11d spaces
- make a space byte 20h.
_0629_SA_BLANK: load eleven spaces into IX+00 -> IX+0A;
for the type flag and file name
- put FFh in IX+1; for a null name if none is supplied
- call 2BF1 STK FETCH to get the string parameters of
the file name; start address in DE and length in BC
- make a test byte FFF6h/minus ten
- add the test byte to the length less one
- if this makes NC jump on to SA NAME; this ingenious
test gives carry if BC is zero or more than ten
- (no carry, the length is zero or more than ten) if the
op flag is zero report "Invalid file name"; you can't SAVE
without a valid file name.
_0644_SA_NULL: if the length is zero jump on to SA DATA;
leaving the FFh marking a null name
- (length more than ten) make the length 0Ah/10d.
_064B_SA_NAME: copy the name to the first WS header; as
many bytes as its length indicates
_0652_SA_DATA: get the code after the filename
- if it isn't E4 DATA jump on to SA SCR$
- (it is DATA) if the op flag is 03 report "Nonsense in
BASIC"; MERGE ... DATA isn't allowed
- (SAVE, LOAD or VERIFY ... DATA, handling arrays) get
the code after DATA; a variable letter
- call 28B2 LOOK VARS to find the array in the variables
area; see the index entry for its complicated output
parameters.
STK VAR isn't called, so the parameters are those shown
under 28B2 LOOK VARS_before the description of 1C22 VAR A
1.
- set bit 7 of the variable letter to indicate an array;
only arrays are supposed to be SAVEd as DATA, but see the
error noted above
- if the flag shows NC jump on to SA V OLD; a variable
matching the given letter was found
- (new variable) set the start pointer to zero; this

744
signal is finally read in LD CONTRL
- if the op flag is one jump on to SA V NEW; LOAD
- (SAVE or VERIFY ... DATA) report "Variable not found";
you can't SAVE or VERIFY an array not already declared.
_0672_SA_V_OLD (SAVE, LOAD or VERIFY ... DATA,
handling
arrays; an "old" variable has been found): if the flag doesn't
show Z report "Nonsense in BASIC"; this is ineffective, see
above for the error
- copy the length of the array into the header from the
variables pointer returned by 28B2 LOOK VARS
- move on the variables pointer; now on the number of
dimensions.
_0685_SA_V_NEW [as the notes point out, this section
shouldn't be entered in syntax checking, because the
workspace header hasn't been prepared and IX hasn't been
made a pointer to it; however the error isn't detectable]: put
the variable letter from 28B2 LOOK VARS in the header
- set the type flag to one; for a number array
- if the status flag is zero jump on to SA V TYPE; a
number array
- make the type flag 2; for a string array.
_068F_SA_V_TYPE: put the type flag in the header.
_0692_SA_DATA_1: move on the BASIC pointer; it should
now be on ")" after the array dimensions
- if it isn't on a 29h ) jump back to SA V OLD, which
reports "Nonsense in BASIC"
- move on the BASIC pointer past the ")"
- call 1BEE CHECK END; in syntax checking it reports
"Nonsense in BASIC" if the statement hasn't ended and makes
a double return if it has
- (run time) jump on to SA ALL.
_06A0_SA_SCR$ (the code after the filename isn't DATA):
if it isn't AA SCREEN$ jump on to SA CODE
- (SCREEN$) if the op flag is 03 MERGE report "Nonsense
in BASIC"; you can't MERGE ... SCREEN$
- move on the BASIC pointer

745
- call 1BEE CHECK END; no parameters may follow
SCREEN$
- (run time) put the length 1B00h bytes and the start
4000h in the header and the start pointer; this covers the
display and attributes areas
- jump on to SA TYPE 3.
_06C3_SA_CODE (the code after the filename isn't DATA or
SCREEN$): if it isn't AF CODE jump on to SA LINE
- (CODE) if the op flag is 03 MERGE report "Nonsense in
BASIC"; you can't MERGE ... CODE
- move on the BASIC pointer
- call 2048 PR ST END; it returns with Z if the
statement has ended
- if it hasn't jump on to SA CODE 1
- (CODE with no parameters) if the op flag is zero
report "Nonsense in BASIC"; SAVE ... CODE commands must
specify a start address
- call 1CE6 USE ZERO to put a zero on the calculator
stack for "start unspecified"
- jump on to SA CODE 2.
_06E1_SA_CODE_1: call 1C82 EXPT 1NUM to read the start
number from BASIC on to the calculator stack
- if the next code is 2C comma jump on to SA CODE 3
- (no length specified) if the op flag is zero report
"Nonsense in BASIC"; SAVE ... CODE commands must specify
length as well as start.
_06F0_SA_CODE_2 (CODE, no length specified): call 1CE0
USE ZERO to put a zero on the stack
- jump on to SA CODE 4.
_O6F5_SA_CODE_3 (length specified in BASIC): move on
the BASIC pointer
- call 1C82 EXPT 1NUM to get the length after the comma
and put it on the stack.
_06F9_SA_CODE_4: call 1BEE CHECK END; this must be the
end of the statement
- (run time) call 1E99 FIND INT2 to get the length off
the stack

746
- put it in the header
- repeat for the start address
- make a start pointer by copying the start address.
_0710_SA_TYPE_3 (SCREEN$ or CODE, both type 3): put
the type flag 03 in the header
- jump on to SA ALL.
_0716_SA_LINE (the code after the filename isn't DATA,
SCREEN$ or CODE): if it is CA LINE jump on to SA LINE 1
- (none of DATA, SCREEN$, CODE or LINE after the
filename; this must be a plain program SAVE/LOAD) call 1BEE
CHECK END; it must be the end of the statement
- (run time) put an 80-byte in the line number position
of the header
- jump on to SA TYPE 0.
_0723_SA_LINE_1 (LINE): if the op code isn't zero report
"Nonsense in BASIC"; the command must be SAVE
- move on the BASIC pointer
- call 1C82 EXPT 1NUM to put the line number on the
calculator stack
- call 1BEE CHECK END; this must be the end of the
statement
- (run time) call 1E09 FIND INT2 to get the number into
a register
- put it in the header.
_073A_SA_TYPE_0 (a program, with or without LINE): put
zero in the header for the type flag
- get a value from 5C59 E LINE; the address one beyond
the end of the variables area
- get a value from 5C53 PROG; the start address of the
BASIC
- take the PROG address from the E LINE address with
carry; the length of the data block, omitting the 80-byte at the
end of the variables area
- put it in the header
- get a value from 5C4B VARS and take the PROG address
from this; the length of the program alone, without variables
- put this in the header

747
- put the PROG address in the start pointer.
_075A_SA_ALL (the first WS header and start pointer have
now been prepared for all variants of the four commands) if
the op flag is zero jump on to SA CONTRL; SAVE.
- (LOAD/VERIFY/MERGE) move IX on 11h/17d bytes; to
point to the second WS header.
_0767_LD_LOOK_H: make a byte counter of seventeen; to
count the header bytes
- make a zero flag; the header/data flag, "awaiting
header"
- call 0556 LD BYTES to read the tape; the tape may well
have been running since the routine was entered, but the
action so far has taken no more than a split second
- if LD BYTES returns with NC loop back to LD LOOK H
- (carry returned from LD BYTES; the header has been
loaded) call 1601 CHAN OPEN with stream minus 2; channel S
to print on screen
- set the scroll counter to 03; the "scroll?" prompt
will never be printed, however many file names get displayed
on screen
- make a match flag 80h/128d; if the hi bit remains set
it will flag "no match" even if the names match
- compare the type flags in the first and second WS
headers
- if they don't match jump on to LD TYPE
- (the type flags match) make the match flag F6h/minus
ten for character code matching.
_078A_LD_TYPE: if the type flag is 04 or more jump back
to LD LOOK H to wait for another header; out of range
- (header in range) get the base address 09C0 of the
cassette messages; 09C0 is the "inverted" code at the end of
"... any key."
- call 0C0A PO MSG to print the message appropriate to
the type, starting with a newline; the type flag acts as an
index byte into the message table
- put a pointer on the type flag in the second WS header

748
- put another pointer 10h/16d bytes back; on the name in
the first header
- make a loop counter 0A; to read the bytes
- if the code in the first header isn't FF jump on to LD
NAME; not a null name,
- (null name) add the loop counter to the byte counter
making zero if the type flags matched or 8A if they didn't; hi
bit zero signals a match, so any name at all will match the null
name.
_07A6_LD_NAME: move on the second header pointer
- read its code
- compare it with the code in the first header
- move on the first header pointer
- if they don't match jump on to LD CH PR
- (they match) increment the match flag; after ten
matches it will be zero if the type flags matched, 8A if they
didn't
_07AD_LD_CH_PR: call 0010 PRINT A 1 to print the
character from the second WS header on the screen; whether
there is a match or not
- loop back to LD NAME for the next
- (all ten have been checked) read the hi bit of the
match flag
- if it is set jump back to LD LOOK H; no match, wait
for the next header on the tape
- (the names match) make a newline byte 0D
- call 0010 PRINT A 1 to print it; the call to PO MSG in
LD TYPE prints a newline_before each new file name on
screen, but one is also required after the last
- read the type flag from the second header
- if it shows type 3 jump on to VR CONTRL; LOADing of
SCREEN$/CODE is handled by the VERIFY routine, they can't
be MERGEd
- (not SCREEN$/CODE) read the op flag
- decrement it
- if this makes zero jump on to LD CONTRL; other LOADs
- if it was 03 jump on to ME CONTRL; MERGE.

749
_07CB_VR_CONTRL (VERIFY or LOAD ... CODE/SCREEN$):
get the length of the data block from both the first and second
WS headers
- if the first header shows zero length jump on to VR
CONT 1; it wasn't specified
- (length specified in BASIC) if the length in the
second WS header is longer report "Tape loading error"; you
can't LOAD or VERIFY_part of the code on tape
- if they are the same jump on to VR CONT 1
- (more commanded than there is on tape) if the op flag
is VERIFY report "Tape loading error"; however if the
command was LOAD the tape will load OK, as far as it goes.
_07E9_VR_CONT_1: if the start pointer isn't zero jump on
to VR CONT 2
- (no start address was specified) use the start address
from the second WS header
_07F4_VR_CONT_2 (checking completed): copy the start
pointer to IX
- if the op flag is 02 jump on to VR CONT 3 with carry
set; for VERIFY
- clear carry; for LOAD.
_0800_VR_CONT_3: flag FF in the header/data index;
expecting a data block.
_0802_LD_BLOCK: call 0565 LD BYTES to execute the
LOAD/VERIFY
- if it makes a carry, return; the routine is finished
- (no carry from LD BYTES) report "Tape loading error".
_0808_LD_CONTRL (loading programs and arrays): get the
length from the second WS header
- if the start pointer is non-zero jump on to LD CONT 1
- (zero start, ie unspecified; it must be a "new array",
since the start pointer for programs is always from 5C53
PROG) add three to the length; only the last of the four initial
parameter bytes was saved on tape.
_0810_LD_CONT_1: read the length in the first WS header;
this is the length of the existing program or of the array
already in the variables area, if any

750
- check it against the length in the second; if the
existing length is longer, jump on to LD DATA.
_0825_LD_CONT_2 (more room needed): add five to the
difference in lengths; to allow for various PUSHes which will
be made
- call 1F05 TEST ROOM to check for "Out of memory"; this
will be done again in the calls to 1655 MAKE ROOM at LD
DATA 1 or LD PROG, but by then any old array or program
will have been deleted.
_082E_LD_DATA: if the type flag is zero jump on to LD
PROG; program load
- (loading an array) if the start pointer is zero jump
on to LD DATA 1; new array
- (old array) get the length of the old array from the
variables area; in the two bytes before the start pointer
- add three for the initial parameters
- call 19E8 RECLAIM 2 to delete the old array; the
header pointer is parked in 5C5F X PTR while this is done.
_084C_LD_DATA_1: put a destination pointer on the 80-
byte at the end of the variables area
- get the length of the array on tape from the second WS
header
- add the three bytes for the initial parameters
- get the variable letter from the first WS header; thisw
as specified in the BASIC
- call 1655 MAKE ROOM to make space at the end of the
variables area; it leaves a variables pointer just below the new
space
- move on this variables pointer
- put in the variable letter
- put the length in the next two bytes
- move the pointer on again; this is the destination for
the bytes from tape
- put it in IX as the start pointer
- set the carry flag for LOAD
- make a header/data index FF for data
- jump back to LD BLOCK.

751
_0873_LD_PROG (loading a program): put a pointer on the
80-byte at the end of the variables area
- get the length of the new program plus variables from
the second WS header
- stack it; it will be used more than once
- call 19E5 RECLAIM 1 to delete from the address in 5C53
PROG to the 80-byte; the address from PROG is now in DE
- call 1655 MAKE ROOM to make a space for the new
length. The header pointer was parked in 5C5F X PTR for the
RECLAIM 1 and MAKE ROOM calls
- move on the pointer left by MAKE ROOM just before the
space just made
- get the "program only" length from the second WS
header
- add it to the MAKE ROOM pointer
- put this address in 5C4B VARS
- get the hi byte of the LINE number from the second WS
header
- AND it with 11000000b/C0h
- if the result isn't zero jump on to LD PROG 1; it must
have been 80h signalling "no LINE specified". Any LINE X with
the hi byte of X more than 00111111b, ie X more than 3FFFh/
16383d, will have this effect
- get the lo byte
- put the LINE number in 5C42 NEWPPC and zero in 5C44
NSPPC; signalling GO TO the start of that line
_08AD_LD_PROG_1: recover the program/variables length
and the start pointer
- set the carry flag; for LOAD
- make a header/data index FF; for data
- jump back to LD BLOCK.
_08B6_ME_CONTRL: get the program/variables length
from the second header
- add one
- call 0030 BC SPACES to make room for the data block,
plus an end marker, in the work space; everything from tape

752
will be loaded into the work space, and MERGEd into the
existing program from there
- put an 80-byte in the last space to mark the end
- put the start and length pointers in the correct
registers for LD BLOCK
- set the carry flag for LOAD
- make a header/data index FF for data
- call LD BLOCK as a subroutine; instead of exiting to
it as in the other CONTRL routines
- (the program/variables from tape are now in the work
space) recover the pointer to the start of the work space
- get from 5C53 PROG a BASIC pointer to the start of the
program area.
_08D2_ME_NEW_LP: read the hi byte of the line number of
the BASIC line at the work space pointer; in hi-lo format
- AND it with 11000000b/C0h
- if the result isn't zero jump on to ME VAR LP; the
"line number" is more than 3FFFh/16383d so it must be either
the 80-byte or the first variable letter.
_08D7_ME_OLD_LP (there are still BASIC lines to MERGE):
if the hi byte of the line in the work space doesn't match the
one in the program area jump on to ME OLD L1
- compare the lo bytes.
_08DF_ME_OLD_L1: put the pointers back on the hi bytes
- if the work space line number (HL) is equal or lower
jump on to ME NEW L2; the work space line is to be MERGEd
in the program
- (the existing line number is lower than the one in the
work space) call 19B8 NEXT ONE to move the BASIC pointer
on to the start of the next existing line; leaving the work
space pointer where it is
- loop back to ME OLD LP.
_08EB_ME_NEW_L2 (a line is to be MERGEd from the work
space): call ME ENTER; see below, after 0923 ME VAR L2
- jump back to ME NEW LP.
_08F0_ME_VAR_LP (ME NEW LP jumps on to here when it
finds the first variable letter or the 80-byte in the work space):

753
- read the variable letter in the work space
- if it is the 80-byte, return; all the lines and
variables have been MERGEd
- (a variable letter found in the work space) save the
work space pointer
- get the address from 5C4B VARS as a BASIC pointer.
_08F9_ME_OLD_VP: read the variable letter in the BASIC
- if it is the 80-byte jump on to ME VAR L2; this is the
end of the variables area and nothing matched
- if it matches the variable letter in the work space
jump on to ME OLD V2.
_0901_ME_OLD_V1 (no match yet): call 19B8 NEXT ONE to
get the next BASIC variable letter
- loop back to ME OLD VP.
_0909_ME_OLD_V2 (variable letter in the variables area
matching the one in the work space): AND the variable letter
in the BASIC with 11100000b/E0h
- check the result against 01100000b/A0; the marker for
long names
- if it isn't a long name jump on to ME VAR L1; the
match is complete
- (long names) get the work space pointer and save the
BASIC pointer.
_0912_ME_OLD_V3: move on both pointers
- check the next code of the long name
- if they don't match jump on to ME OLD V4
- (everything matches so far) if the hi bit of the code
is zero loop back to ME OLD V3
- (hi bit set, the last characters of the name match)
jump on to ME VAR L1; it is the work space code which is
checked, but they must be the same, eg "hell" and "hello"
aren’t counted as matching.
_091E_ME_OLD_V4 (mismatch of long names): recover the
BASIC pointer
- jump back to ME OLD V1.
_0921_ME_VAR_L1 (the variable in BASIC is to be deleted):
make a delete flag FF for ME ENTER.

754
_0923_ME_VAR_L2 (also entered from ME OLD VP when no
match of variable letters was found, with the 80-byte in the
delete flag): get the work space pointer and BASIC pointer
- increment the delete flag; making 00 for "delete", 81h
for "don't", but any non-zero value counts as "don't"
- set the carry; for a variable MERGE
- call ME ENTER; see just below
- loop back to ME VAR LP; find another variable in the
work space.
_092C_ME_ENTER (formally a free-standing subroutine,
but called only from ME VAR L2 with carry and from ME NEW
L2 above with NC; the carry is a line/variable "l/v" flag. In
either case Z is a delete flag, signalling that line numbers or
variable letters match, so the old l/v is to be deleted) if the
delete flag shows NZ jump straight on to ME ENT 1; no
deletion
- save the pointers and flags; 5C5F X PTR is again used
as a parking lot
- call 19B8 NEXT ONE to get the length of the l/v in the
existing program or variables area
- call 19E8 RECLAIM 2 to delete this length from the
existing BASIC
- recover the pointers and flags.
_093E_ME_ENT_1: save the BASIC pointer and flags again
- call 19B8 NEXT ONE to find the next l/v start in the
work space
- save the work space pointer in 5C5F X PTR
- save 5C53 PROG; if a line is inserted at the very
start of the program it will move 5C53 PROG on
- if the l/v flag shows carry jump on to ME ENT 2;
variable
- (program line) call 1655 MAKE ROOM to make a space
for the line_before the BASIC pointer; it is on the following line
in the program area
- move on the MAKE ROOM pointer to the start of the
space just made
- jump on to ME ENT 3.

755
_0955_ME_ENT_2 (variable): call 1655 MAKE ROOM to
make space for the variable_at the BASIC pointer; it is on the
end of the variables area.
_0958_ME_ENT_3: recover the work space pointer, the
length of the l/v in the work space, and 5C53 PROG
- copy the l/v from the work space to the BASIC pointer;
leaving the BASIC pointer just after the end of the new l/v
- get back the work space pointer and length
- save the BASIC pointer
- call 19E8 RECLAIM 2 to delete the l/v from the work
space; the routine clears the work space as it goes, keeping
the danger of memory overflow to a minimum, but the 80-
byte end marker remains, to be cleared by the call to 16BF
SET WORK at 1B29 STMT L 1 in the statement loop
- recover the BASIC pointer and return; to ME NEW L2 or
ME VAR L2.
_0970_SA_CONTRL (the jump to here was from 075A SA
ALL):
call 1601 CHAN OPEN with stream minus 3 to open channel K
- call 0C0A PO MSG to print the "Start tape ..." message
in the lower screen
- set bit 5 of TV FLAG; "clear the lower screen"
- call 15D4 WAIT KEY to wait for any key to be pressed;
there is no check for BREAK
- save the header pointer
- make a length counter; 11h bytes for a header
- make a header/data index 00 for a header
- call 04C2 SA BYTES to save the header
- recover the header pointer
- make a delay counter for 32h/50d HALTs.
_0991_SA_1_SEC: HALT
- loop back to SA 1 SEC till the counter goes to zero;
one second
- (pause finished) get the length of the data block from
the WS header
- make a header/data index FF for a data block
- recover the start pointer for the data block

756
- exit into SA BYTES.
Exit: RETs from LD BLOCK and ME VAR LP will be to 1B76
STMT LOOP
- the RET from ME ENT 3 is an internal RET, staying
within the routine
- into 04C2 SA BYTES from SA CONTRL, which again will
RET to 1B76 STMT LOOP.
Output parameters: none.
Rems:
Introduction 17d byte header describes following block
04C2 SA BYTES signalled by 00 flag in A register
04F2 SA SYNC 2 flag is first byte saved
0556 LD BYTES loads header, later file
058F LD SYNC loads or verifies header
05B3 LD FLAG type must match first byte on tape
0605 SAVE ETC constructing the header
068F SA V TYPE puts type flag in header area
06A0 SA SCR$ length and start for SCREEN$ in header
area
06F9 SA CODE 4 length and start for CODE in header area
0710 SA TYPE 3 enters type for CODE/SCREEN$
073A SA TYPE 0 length and start for PROG in header area
075A SA ALL ready to copy header from tape
0767 LD LOOK H finds a header on tape and loads it
078A LD TYPE only types 00, 01, 02, 03 accepted
07AD LD CH PR name checks with name required
07CB VR CONTRL checks length from header
07E5 VR CONT 1 start - use header if zero specified
0808 LD CONTRL length from header for PROG or array
084C LD DATA 1 array name from header area
0970 SA CONTRL saving header
294B V END mistake: SAVE ... DATA can save simple
string

SAVE/LOAD blocks/header/pointer, SAVE, LOAD, VERIFY


AND MERGE COMMAND ROUTINES see 0605 SAVE ETC

757
SA V NEW 0685 (0605 SAVE ETC)
Jumps from:
0652 SA DATA

SA V OLD 0672 (0605 SAVE ETC)


Jumps from:
0652 SV DATA
0692 SA DATA 1

SA V TYPE 068F (0605 SAVE ETC)


Jumps from:
0685 SA V NEW

SA 1 SEC 0991 (0605 SAVE ETC)


Jumps from:
auto
Rems:
04C2 SA BYTES called from 099E (actually it is the exit
routine)

SA 8 BITS 0525 (04C2 SA BYTES)


Jumps from:
0507 SA START

S BIN (S DECIMAL) subroutine 268D


The notes call this routine by the two different names,
S BIN and S DECIMAL, in different parts of the listing. There
seems to be no good reason for this.
Entered twice from the scanning function table at 2596
and also from 2684 S ALPHNUM if an expression is found
beginning with a digit. It is used to read an actual number
from the BASIC line, during syntax checking or run time.
The number may start
- with a digit, and perhaps include a decimal point or an
E-format exponent
- with BIN

758
- with a decimal point; see under 2F46 PF NOT E in 2DE3
PRINT FP.
Numeric expressions such as variables are not read by S
DECIMAL.
There are two quite separate parts to the routine, one
for run time and one for syntax checking; syntax checking
inserts a number marker and the 5-byte FP form of the
number after its representation in the BASIC line, run time
ignores the BASIC and just reads the FP format version.
Input parameters: none.
Action: if the syntax/run flag is set jump on to S STK
DEC; run time
- (syntax checking) call 2C9B DEC TO FP to put the
number in FP form on the calculator stack
- make a pointer to the BASIC after the number
- make a length byte 06
- call 1655 MAKE ROOM to put six spaces in the BASIC
line
- put the number marker 0E in the first space
- copy the FP form off the stack into the remaining five
- call 0077 TEMP PTR1 to put the address just after the
FP form in 5C5D CH ADD
- exit to 26C3 S NUMERIC.
_26B5_S_STK_DEC (run time) make a BASIC pointer to the
first code of the number.
_26B6_S_SD_SKIP: move on the pointer
- if the code at the pointer is not 0D number marker
loop back to S SD SKIP
- (number marker found) move the pointer on to the first
byte of the FP form
- call 33B4 STACK NUM to read the FP form on to the
calculator stack
- put the BASIC pointer in 5C5D CH ADD; STACK NUM
leaves it on the byte following the FP form.
Exit: into 26C3 S NUMERIC either from S DECIMAL or S
SD SKIP; this signals "numeric result" and exits back into the
SCANNING loop.

759
Output parameters: none.
Rems:
2684 S ALPHNUM exits into

S BRACKET subroutine 25E8


Entered only from the scanning function table at 2596
when a bracket "(" is encountered in reading the BASIC line.
The bracket is read as the start of a sub-expression, string or
numeric; the characters following it are evaluated as such
until a ")" is encountered.
Input parameters: none.
Action: read the next code in BASIC
- call 24FB SCANNING, which puts the value or string
parameters of the sub-expression on the calculator stack and
returns with the code following the sub-expression
- if this code is not 29h ) report "Nonsense in BASIC"
- get the next code.
Exit: back into the SCANNING loop at 2712 S CONT 2.
Output parameters: HL is a BASIC pointer and A the code.

SCAN ENT 336C (0028 FP CALC)


Jumps from:
33A2 fp-calc-2

SCAN LOOP 1B52 (1B8A LINE RUN)


No direct calls or jumps.
Return address stacked at:
1B55 GET PARAM
Returns from:
1B6F SEPARATOR
1C46 VAR A 3
1C82 CLASS 06 (EXPT 1NUM)
1C8C CLASS 0A (EXPT EXP)
Rems:
0605 SAVE ETC address dropped
1BEE CHECK END address dropped
1C11 CLASS 05 address dropped

760
1C4E CLASS 02 address dropped
1C96 PERMS address dropped

SCANNING subroutine 24FB see also BASIC INTERPRETER


Any string of codes in the BASIC which is intended to
have a numeric value or a string value is an "expression"; from
the simplest such as 1.0 or "hello" to complicated ones such as
the example in the notes, CHR$ (T+A-26*INT((T+a)/26)+65).
SCANNING evaluates all such expressions, from the
simplest to the most complex, and puts the result of the
evaluation on the calculator stack; if it is a numeric
expression the result will be an FP number and the string/
numeric flag, bit 6 of FLAGS, will be set to indicate a numeric
result, otherwise it will be the string parameters of the string
result - essentially a start address and a length - and the flag
bit will be zero.
Complex expressions are evaluated by building up two
stacks simultaneously:
- an evaluation stack on the calculator stack, consisting
of numeric values and pairs of string parameters obtained by
evaluating the various component sub-expressions of the
complex expression; these are taken from the stack as they
are required to evaluate subsequent components
- an op stack on the machine stack; each two-byte value
on this stack consists of a_priority in the hi byte and an op
code in the lo byte.
As each value is ready for stacking, the previous one is
taken off and its priority checked. This happens in S LOOP:
- if it is the zero stacked on entry to SCANNING, the
present operation is executed and the subroutine terminates;
zero is lower than any priority
- if the last operation has higher_binding_priority than
the present operation, the last one is executed. Its result goes
on the evaluation stack replacing any values used in executing
it; then the op code and priority of the present one is stacked
in its place. For the priorities see the table under commands,
functions and operators

761
- if the present has higher priority than the last, its
op code and priority are simply stacked on the op stack.
Input parameters: none.
Action: read the first code of the expression
- put a double zero on the op stack.
_24FF_S_LOOP_1 (loop back to here after each code has
been evaluated, provided the next code is not a binary
operator, one which comes between its operands:
from S PUSH PO after a function op code and priority
have gone on the op stack
from S U PLUS after a unary plus has been read
from S NEXT in some other cases):
- use the code to index into the scanning function table
at 2596; the codes in the table are
22 " 28 ( 2E . 2B + A8 FN
A5 RND A7 PI A6 INKEY$ C4 BIN AA SCREEN$
AB ATTR A9 POINT
- if it is not one of the codes in the table jump on to
S ALPHNUM
- add the offset from the table to the code address in
the table
- jump to this address; it is the routine to handle the
code.
As the offsets are all less than FFh, the routines are
packed in after the table, and for the sake of abbreviation
several of them merely call secondary subroutines. These
routines and secondary routines are indicated here in square
brackets, but they each have a separate entry in this index.
They all exit back into the scanning loop at one point or
another, as indicated.
_[250F_S_QUOTE_S: secondary routine to handle quotes.]
_[2522_S_2_COORD: subroutine to collect coordinates (X,Y)
for SCREEN$, ATTR, POINT.]
_[2530_SYNTAX_Z: checks the syntax/run flag.]
_[2535_S_SCRN$_S: secondary routine to handle SCREEN$.]
_[2580_S_ATTR_A: secondary routine to handle ATTR.]
_2596_here the scanning function table itself is inserted

762
_[25AF_S_U_PLUS: handles unary + with exit to S LOOP 1.]
_[25B3_S_QUOTE: handles quotes with exit to S CONT 2.]
_[25E8_S_BRACKET: handles ( with exit to S CONT 2.]
_[25F5_S_FN: handles FN with exit through 27BD S FN
SBRN
to S CONT 2.]
_[25F8_S_RND: handles RND with exit to S NUMERIC.]
_[2627_S_PI: handles PI with exit to S NUMERIC.]
_[2634_S_INKEY$: handles INKEY$ with exit to S CONT 2.]
_[2668_S_SCREEN$: handles SCREEN$ with exit through
25DB
S STRING to S CONT 2.]
_[2672_S_ATTR: handles ATTR with exit to S NUMERIC.]
_[267B_S_POINT: handles POINT with exit to S NUMERIC.]
_2684_S_ALPHNUM ( jump to here if the code isn't in the
scanning function table): call 2C88 ALPHANUM; it returns
with NC if the code isn't alphanumeric
- if the code isn't alphanumeric jump on to S NEGATE
- (alphanumeric code) if it is more than 41h jump on to
S LETTER; a letter
- (a digit):
_[268D_S_BIN/S_DECIMAL: handles numbers, ie any
expression beginning with a digit, decimal point, or BIN, with
exit to S NUMERIC.]
_26C3_S_NUMERIC (all the table operations that produce a
numeric result exit to here with the numeric value on the
calculator stack): set bit 6 of FLAGS to signal "numeric result"
- jump on to S CONT 1.
_26C9_S_LETTER (the code is a letter of the alphabet,
which can only be a variable letter): call 28B2 LOOK VARS
- if it returns with carry report "Variable not found";
no matching variable in the variables area
- if it returns with Z call 2996 STK VAR (misprinted STK
VARS) to put its value or string parameters on the calculator
stack; a string or array
- if 5C3B FLAGS is less than 11000000b/C0h jump on to S
CONT 1; one of the hi bits is zero, bit 7 zero for syntax

763
checking or bit 6 for a string result
- (run time, and the variable is a numeric one; if it is
an array, STK VAR placed a pointer before its FP number
value in the variables area, if it is simple LOOK VARS did)
move the pointer on to the FP number
- call 33B4 STACK NUM to copy the value to the
calculator stack.
_26DD_S_CONT_1: jump on to S CONT 2; merely a "stepping
stone".
_26DF_S_NEGATE (the first code of the expression to be
evaluated isn't in the scanning function table and isn't a
letter or a digit): load priority 09 and op code DB; twice
misprinted in the notes, but correct in the listing. This is
correct for unary minus, literal 1B negate
- if the code is 2D "-" jump on to S PUSH PO
- (not unary minus) load priority 10h and op code 18h;
this is correct for literal 18 val$
- if the code is AE VAL$ jump on to S PUSH PO
- (not unary minus or VAL$) subtract AF from the token
code; all the symbols or tokens which can begin a numeric or
string expression in BASIC have now been dealt with except
those running consecutively from AF CODE to C3 NOT, so
subtracting AF leaves 00 to 14h
- if the subtraction made a carry report "Nonsense in
BASIC"; code less than AF CODE
- load priority 04 and op code F0h; this is correct for
literal 30 not
- if the code is now 14h jump on to S PUSH PO; it was C3
NOT
- if the comparison didn't make a carry report “Nonsense
in BASIC"; code more than C3 NOT
- (not NOT) load priority 10h, the highest; all the rest
of these tokens are functions with priority 10h
- add DC to what is left of the token code; the result
is an op code matching the table at 32D7, except for the two hi
bits which are temporarily used as flags, and they will be

764
zeroed in S STK LST before the table is used. Op codes DC
CODE to EF CHR$ match literals 1C code to 2F chr$ in the
table; bit 7 is set for numeric argument, zero for string
argument; bit 6 is set for numeric result, zero for string result
- if the code is less than DF zero bit 6 for a numeric
result; DC CODE, DD VAL and DE LEN.
_2707_S_NO_TO_$: if the code is more than ED zero bit 7
for a string argument; EE STR$ and EF CHR$.
_270D_S_PUSH_PO: push the priority and flagged op code
on the op stack
- move on the BASIC pointer
- loop back to S LOOP 1.
_2712_S_CONT_2 (a numeric or string expression or sub-
expression has been evaluated and its value or string
parameters put on the evaluation stack: the next code could
be a binary operator, one which comes between its operands):
read the code.
_2713_S_CONT_3: if it isn't 28h ( jump on to S OPERTR
- (the "(" can only be correct if it indicates slicing
of a string expression) if bit 6 of FLAGS is NZ jump on to S
LOOP; the string/numeric status flag indicates that the
expression just evaluated was numeric. S LOOP will evaluate
the whole expression up to the "(" and then return to the
calling routine, which will surely report an error, as "(" cannot
be syntactically correct in this position
- (the expression was a string, the "(" indicates
slicing) call 2A52 SLICING to slice it
- move on the BASIC pointer
- loop back to S CONT 3.
_2723_S_OPERTR (a numeric or string expression or sub-
expression has been evaluated and its value or string
parameters put on the evaluation stack; the next code isn't "(",
it may or may not be a binary operator): load the base address
of the table of operators at 2795
- call 16DC INDEXER with the next code as index
- if INDEXER returns with NC jump on to S LOOP to
evaluate the whole expression; the code isn't in the table, ie

765
not a binary operator
- read the op code from the table; a code from C3 - to
CF + which matches the literal in the table at 32D7 but with
both hi bits set:
C3 - to C7 OR match 03 subtract to 07 or,
C8 AND to CF + match 08 no-&-no to 0F addition
and then again, adding 08 to the op codes, match the
string operations 10h str-&-no to 17h strs-add
- add the op code to the base address 26ED of the
priority table at 27B0; eg 26ED + C3 = 27B0, the first entry
- get the priority from the table.
_2734_S_LOOP (entered with the "present" or "new"
operation and priority "o/p" in BC and any previous o/ps
stacked up on the machine stack, the "op stack"; the bottom of
the op stack is marked with a double zero which was put on it
on entry to SCANNING. The corresponding values of
expressions and sub-expressions are stacked on the calculator
stack, the “evaluation stack"): POP the last o/p and compare
the new priority with the one before it
- if the new priority is higher, jump on to S TIGHTER;
the new o/p will be added to the op stack
- (new priority equal or lower) if the last priority was
zero, exit into 0018 GET CHAR; the new priority must be zero
too, so there is no new operation to perform and the bottom
of the op stack has been reached
- stack the new o/p; the priority of the last op is
higher, so it will be executed now. Note that each operation on
the stack, except possibly the new o/p, must have a higher
priority than the one below it. If the last code to be read from
BASIC wasn't a binary operator, the new o/p will have zero
priority; this will have the effect of executing the stack right
down to the bottom
- put a pointer on FLAGS
- if the last op code isn't ED USR jump on to S STK LST
- (USR) if bit 6 of FLAGS is zero change the op code to

766
10011001b/99h, corresponding to literal 19h usr-$; the string/
numeric status flag in the hi bit shows that the argument of
USR is a string.
_274C_S_STK_LST: stack the last o/p
- if syntax is being checked jump on to S SYNTEST
- AND the op code with 00111111b/3Fh; this zeroes the
flags in bits 6 and 7 and makes it the calculator literal
- use the calculator with literal 3B fp-calc-2 and with
the operation literal in the B register, which executes the
operation
- jump on to S RUNTEST.
_275B_S_SYNTEST (syntax checking) XOR the op code with
5C3B FLAGS; bit 6 should match, making zero, since in both it
is a flag signalling string or numeric argument
- AND the result with 01000000b/40h; zero if the flags
match.
_2761_S_RPORT_C: if the result isn't zero report
"Nonsense in BASIC".
_2764_S_RUNTEST: recover the last o/p from the op stack
- set bit 6 of FLAGS; numeric argument
- if bit 7 of the op code is set jump on to S LOOPEND;
the result status flag. The argument status matches the result
of the last op, as it should, since the result of each op is the
argument of the next
- (string result) zero bit 6 of FLAGS; the argument
status flag, to match the result flag.
_2770_S_LOOPEND: recover the top o/p from the stack;
this is now the "new" one, and any remaining on the stack are
in order of priority with the highest priority at the top of the
stack. The new one could still be lower than the top one on
the stack
- loop back to S LOOP.
_2773_S_TIGHTER (the new o/p has higher priority than
anything on the op stack, and must go on the stack) put the
last o/p back on the stack
- if bit 6 of FLAGS is set jump on to S NEXT; the

767
argument status flag. The result of the last op was numeric,
and numeric arguments have been assumed in flagging the op
code
- (the last op had a string result, so the new op has a
string argument. It must be one of the binary operators, AND,
"+" or a comparison operation; the only_functions which have
string arguments, CODE, VAL and LEN, all have top priority,
and are PUSHed by S PUSH PO before S LOOP is entered.
Whatever the new o/p may be on entry to S LOOP, S TIGHTER
can never be reached if any of these are on the op stack) AND
the new op code with 00111111b/3Fh; zero both its argument
and its result flags
- add 08 to the op code; this changes
08 no-&-no to 10 str-&-no (AND)
0F addition to 17 strs-add (+)
and all the number comparisons 09 -> 0E to the
corresponding string comparisons 11 -> 16
- if the op code isn't 10h str-&-no jump on to S NOT
AND; all the other operators have string arguments after as
well as before them
- (str-&-no) set the argument flag in the op code, now
01010000b/50h; its second argument will be numeric
- jump on to S NEXT.
_2788_S_NOT_AND: if the op code was less than 10h report
"Nonsense in BASIC"; operators before 10h in the literal list
can't have a string argument
- if the op code was "+" jump on to S NEXT; this is the
only one that produces strings from strings
- set bit 7 of the op code, the result flag; numeric
result.
_2790_S_NEXT: push the new o/p on the op stack
- move on the BASIC pointer
- loop back to S LOOP 1; not S LOOP, don't confuse them!
_[27BD_S_FN_SBRN: secondary routine to handle FN.]
Exit: the_only exit, apart from error reports, is into
0018 GET CHAR from S LOOP, when the op stack has been
executed down to the bottom.

768
Output parameters: none
- the result of evaluating the expression is on the
calculator stack
- 0018 GET CHAR on exit will read the code following the
expression.
Called from:
1C59 VAL FET 2
1C82 EXPT 1 NUM
1C8C EXPT EXP
1E2C DATA 1
1FA6 DEF FN 6
2024 PR ITEM 1
25E8 S BRACKET
27D9 SF ARGMTS
2852 SF ARG VL
288D SF VALUE
35DE val
360C V RPORT C
Rems:
Introduction rather slow since many variants checked
028E KEY SCAN called by S INKEY$ routine
22CB POINT SUB called by S POINT routine
27BD S FN SBRN evaluates DEF FN by calls to SCANNING
2831 SF VALUES evaluates DEF FN by calls to SCANNING
2951 STK F ARG used to calculate value of variable
2981 SFA MATCH leaves SCANNING to calculate numerics
2CA2 BIN DIGIT leaves SCANNING to check syntax
32D7 address table literals come from SCANNING
33A2 fp-calc-2 sole ROM use is in SCANNING
33B4 STACK NUM used twice by SCANNING
34B3 usr-no omits to ensure return into SCANNING
3645 READ IN called by S INKEY$ in SCANNING

scanning attributes subroutine see 2580 S ATTR S

scanning BRACKET routine see 25E8 S BRACKET

769
scanning DECIMAL routine see 268D S BIN

SCANNING FUNCTION ROUTINES see 25AF S U PLUS

SCANNING FUNCTION SUBROUTINE see 27BD S FN SBRN

scanning loop
In the notes, this may mean either the statement
checking/execution loop 1B17 LINE SCAN/1B8A LINE RUN, or
the expression evaluation loop 24FB SCANNING.

scanning PI routine see 2627 S PI

scanning QUOTE routine see 25B3 S QUOTE, 250F S QUOTE


S

scanning SCREEN$ subroutine see 2535 S SCRN$ S

scanning the keyboard see KEYBOARD SCANNING

SCANNING VARIABLE ROUTINE see 26C9 S LETTER

scanning 2 coordinates subroutine see 2522 S 2 COORD

S CONT 1 26DD (24FB SCANNING)


Jumps from:
26C3 S NUMERIC
26C9 S LETTER

S CONT 2 2712 (24FB SCANNING)


Jumps from:
25DB S STRING
25E8 S BRACKET
2665 S INK$ EN
26DD S CONT 1
27F4 SF SYN EN
288D SF VALUE

770
S CONT 3 2713 (24FB SCANNING)
Jumps from:
auto

SCR CT system variable 5C8C


Bytes: 1.
The_scroll_counter: decremented each time a line is
scrolled or a screen line filled. When it reduces to zero, the
"scroll?" message is printed, and the counter reset to 18h/24d,
in 0C88 PO SCR 2.
POKing any number more than one into SCR CT inhibits
printing of the message: eg at 0767 LD LOOK H. This may also
be done from m/c programs or from BASIC.
When the screen is cleared, SCR CT is set to one by 0DAF
CL ALL; this is changed to the correct value for the number of
lines currently on screen by 12CF MAIN 3 or 20AD INPUT 2.
The scroll counter isn't the same as the_scroll_numbers:
there are two of these, one being the number of lines to be
scrolled counted from the top of the screen, and the other the
number of times the scrolling subroutine is to be called to
scroll the screen up by one line. These aren't kept in svs, but
calculated each time scrolling is called for. The notes don't
always succeed in distinguishing these three numbers clearly.
Written by:
0767 LD LOOK H
0C88 PO SCR 2 (twice)
0DAF CL ALL
12CF MAIN 3
20AD INPUT 2
Read by:
0C88 PO SCR 2 (tested for zero)
Rems:
0D02 PO SCR 4 when AT used in lower screen, scroll
number is 19h less the AT line number
less the value of DF SZ
0D1C PO SCR 4A number is 18h to scroll whole screen

771
0D2D PO SCR 4B scroll B lines

screen, screen address see DISPLAY AREA

SCREEN AND PRINTER HANDLING ROUTINES see 09F4


PRINT OUT

screen colours see colours

SCREEN$ key (AA) see also commands, functions and


operators,
KEYBOARD SCANNING, 0246 extended mod table (c)
The K key in E mode with either shift produces the token
SCREEN$; it can be used either as a function or as an "adverb"
after LOAD/SAVE commands.
As a function, it requires two numeric operands (X,Y),
which must be in brackets, and the value of the function is the
single-character string consisting of the character at the BASIC
AT position (X,Y) on the screen. On execution, 24FB
SCANNING indexes into the scanning function table at 2596 to
find the executive routine 2668 S SCREEN$, and its secondary
subroutine 2525 S SCRN$ S.
After a LOAD/SAVE command it isn't read as a function
but as an abbreviation for CODE 16384,6912, ie the bytes of
the display area. This can also be used with VERIFY, though
not with MERGE.
06A0 SA SCR$ executes LOAD/SAVE SCREEN$
0710 SA TYPE 3 codes LOAD/SAVE SCREEN$ as type 3
07AD LD CH PR loading handled as verifying
07CB VR CONTRL loading handled as verifying

scroll counter see 5C8C SCR CT

scrolling
The display operation in which all the lines or some
lines on the screen are reprinted one line higher than before.
Because the order of the display bytes in the display area is

772
far from straightforward, this is a complex operation. See the
index entry on 0C55 PO SCR, which I hope clarifies the rather
confusing notes.
The screen is scrolled:
a) when input to the lower screen calls for printing on a
line outside the present range of the lower screen, either
because the last line of the lower screen is full and characters
are still being input, or because of an AT or TAB control or
position controls such as comma or ' in an INPUT command.
b) in listings, when the last position on the screen has
been printed and there are more to come, or when a newline
is to be printed on the last available line. This may be in an
ordinary listing, after the "scroll?" prompt has appeared and
scrolling has been accepted; or in an automatic listing if the
screen is full and the current line hasn't yet appeared on
screen. TV FLAG bit 4 is cleared for ordinary listing, set for
automatic.
c) in executing a PRINT command in the upper screen;
this is treated like an ordinary listing, because TV FLAG bit 4
will be zero.
All these cases arise in execution of the 09F4 PRINT OUT
screen output routine when for one reason or another the
print position line number has been decremented, bringing it
nearer to the bottom of the screen. Wherever in ROM this is
done, a call is made to 0C55 PO SCR to check the need for
scrolling; if scrolling is required, it calls
either 0DFE CL SC ALL, from 0CD2 PO SCR 3, to scroll
17h/23d lines, ie the whole screen_except the bottom line; to
avoid scrolling the "screen?" prompt
or 0E00 CL SCROLL, from 0D2D PO SCR 4B, to expand
the lower screen; it scrolls
either 18h/24d lines, ie the whole screen_including
the bottom line, which probably has input on it - this is done
when the upper screen is full
or the number of lines in 5C6B DF SZ, ie the lower screen
only, including its bottom line - this is done when the upper
screen isn't full.

773
For the scroll counter and the other scroll numbers, see
under 5C8C SCR CT.
0A4F PO ENTER checks whether scrolling required
0AAC PO AT ERR may be needed in lower screen on "AT"
0B93 PR ALL 1 may be needed when line end reached
0C55 PO SCR sr to scroll if required
0C88 PO SCR 2 "scroll" prompt printed and response
interpreted
0CD2 PO SCR 3 prepares to execute scroll
0D02 PO SCR 4 prepares to scroll lower screen
0D1C PO SCR 4A prepares scroll counter
0D2D PO SCR 4B counts scrolls
0DFE CL SC ALL entry to sr after "scroll?" prompt
0E00 CL SCROLL executes single line scroll
0E05 CL SCR 1 start of 8-pixel scrolling loop
0E19 CL SCR 3 checks for boundary of "thirds"
17ED AUTO L 4 ends auto list loop if no scrolling
1835 LIST ALL scroll till "current line" appears

SCROLLING SUBROUTINE see 0DFE CL SC ALL

'scroll?' message 0CF8


This message isn't included with the main message list
at 1391, mainly though not exclusively report messages, but is
inserted as a one-item list on its own. This is to avoid having
more than 32d messages in the main list, see the remarks on
leading spaces under 0C0A PO MSG.
0A4F PO ENTER checks for, before transferring line to
main display
0C88 PO SCR 2 prints if required
0DFE CL SC ALL entry to scrolling sr after
10A8 KEY INPUT clears "scroll?" from lower screen

scroll number see 5C8C SCR CT

S DECIMAL see 268D S BIN

774
SECND LOW 356B (353B no-l-eql)
Jumps from:
3575 SEC PLUS

second byte of FP number, second number, second


operand,
second value on calculator stack see CALCULATE

SEC PLUS 3575 (353B no-l-eql)


Jumps from:
3564 BYTE COMP

security
"One disadvantage of the [cassette handling] system is
that it is not possible to produce programs with any 'security'
whatever" (Introduction). However, commercial producers of
programs have managed to overcome this imagined
limitation.

SEED system variable 5C76 see also 1E4F RANDOMIZE,


random numbers and 25F8 S RND
Bytes: 2.
The argument of the RND function, which is the
remainder on dividing (SEED + 1) * 75 by 65537, less one,
divided by 65536.
RND always uses SEED, it doesn't accept a value from
BASIC. SEED can be set to X by RANDOMIZE X, or to the
present value of the two lo bytes of 5C78 FRAMES by
RANDOMIZE zero.
For most purposes it doesn't matter at all to the
working of the Spectrum what number is in SEED, and it is
only disturbed by RANDOMIZE and RND, so it is a suitable
address for flags etc to be used in m/c programming; it is
IY+50.
Written by:
1E5A RAND 1
25F8 S RND

775
Read by:
25F8 S RND

semicolon see control characters, ";" (3B) after end of


alphabet

SEPARATOR subroutine 1B6F


So far as this subroutine is concerned, the_separators
are those items in the parameter table at 1A7A which aren't
command class indexes, so they have codes greater than 20h,
and not subroutine addresses:
2C , in 1AFC P OPEN
1B0A P MOVE
3D = in 1A7A P LET
1A90 P FOR
CB THEN in 1A81 P IF
CC TO in 1A90 P FOR
This subroutine checks that the command being read
does in fact contain a code corresponding to the required
separator.
However in other parts of the notes,_separators means
the codes checked for in 1BF4 STMT NEXT, the statement
separators newline and colon. In 2712 S CONT 2 it isn't clear
what is meant: "a carriage return character, a colon, a
separator or a THEN" seems to imply duplications with either
of the above interpretations.
It would also make sense to use the term for the
position controllers comma, semicolon and single quote,
which are used as "separators" between items in a PRINT
statement; but so far as I can see it never is so used in the
notes.
Input parameters: none
Action: read the code in BASIC
- compare it with the separator in the parameter table
- if it doesn't match report "Nonsense in BASIC"
- move on the BASIC pointer.
Exit: RET.

776
Output parameters: none.
Exit from:
1B55 GET PARAM
Rems:
1C16 JUMP C R all separators have been considered

separators see 1B6F SEPARATOR above

SERIES GENERATOR SUBROUTINE see series-06

series 06 etc subroutine 3449


The notes use three different names for the same
subroutine, series-06, series-08 and series-0C. Whichever
name is used, series-N calculates the sum of N Chebyshev
polynomials, each multiplied by the appropriate constant.
This is approximately the value of the function required. See
under Chebyshev polynomials in this index, and the
illustrated
examples of LN X and ATN X on page 227 of the notes.
Called in the ROM only from 0028 FP CALC with literals
86, 88 and 8C, but it could called with any literal 80h+N from
80h to 9Fh, ie with N up to 1F. N dictates the number of
Chebyshev constants to be read. The literal must be followed
by N constants, each one in stk-data format. In 336C SCAN
ENT the literal is first converted to 7C, which is used to find
the series subroutine in the table; then the original literal less
80h provides the counter N for the constants.
The subroutine could be called directly from m/c, ie
without going through 0028 FP CALC: the counter N must be
in the A register, the argument of the function on the
calculator stack, and the N Chebyshev constants in stk-data
format must immediately follow the call command. The result
would be returned on the calculator stack. However there is
no ROM example of such a call.
As explained, very inadequately in the header note, and I
hope less confusingly in the index under Chebyshev

777
polynomials, if you already have two Chebyshev polynomials,
"this one" and "the last one", it is easy to calculate a third, "the
next one":
the next one = 2Z times this one minus the last one.
Use P(i) and A(i) to mean "this polynomial" and "this
constant"; i - 1 indicates "the last" and i + 1 "the next".
The three simplest polynomials are
P(1) = 1
P(2) = 2Z
P(3) = 4Z**2 - 2
and the rest are found by the formula
P(i + 1) = 2Z * P(i) - P(i - 1).
However the constants are given in an order which
requires the first constant to be multiplied by the most
complicated polynomial, and the last constant by the
simplest:
so each term of the approximation to the function is A(N + 1 -
i) * P(i), and the final approximation is
A(N) * P(1) + A(N-1) * P(2) + ... A(1) * P(N)
The ROM ingeniously builds up the polynomials and
multiplies in the constants at the same time, in a series of
stages. Each turn of the loop calculates a value B(i) by the
formula
B(i) = 2Z*B(i-1) - B(i-2) + A(i),
starting with zeroes for B(i - 1) and B(i - 2).
So the first four calculations come out as
B(1) = A(1)
B(2) = 2Z*A(1) + A(2)
B(3) = 2Z*B(2) - B(1) + A(3)
= 2Z[2Z*A(1) + A(2)] - A(1) + A(3)
= (4Z**2 - 1)*A(1) + 2Z*A(2) + A(3)
= (4Z**2 - 2)*A(1)
+ 2Z*A(2)
+ A(3)
+ B(1)
B(4) = 2Z*B(3) - B(2) + A(4)
= 2Z[(4Z**2 - 1)*A(1) + 2Z*A(2) + A(3)]

778
- [2Z*A(1) + A(2)] + A(4)
= (8Z**3 - 6Z)*A(1)
+ (4Z**2 - 2)*A(2)
+ 2Z*A(3)
+ A(4)
+ B(2)
In every case it will be found that
B(i) = A(1)*P(i) + A(2)*P(i-1) + ... + A(i)*P(1)
+ B(i-2)
so after N turns of the loop the required result is
B(N) - B(N-2).
Input parameters: A holds the number of constants N
- the argument Z of the function is on the calculator
stack.
Action: put N in the B register and call 335E GEN ENT 1
to store it in 5C67 BREG as a loop counter for later; this
enters the calculator
- double Z and store 2Z
- also store a zero.
_3453_G_LOOP (entered on the i'th turn of the loop with
2Z in mem-0
B(i-2) "the last but one" in mem-1; not used initially
B(i-1) "the last one" in mem-2; zero initially
B(i) "this one" on the stack; zero initially
the loop counter N in 5C67 BREG):
- use the calculator in a quite straightforward way to
calculate 2Z*B(i) - B(i-1); put B(i-1) in mem-1
- call 33C6 STK DATA direct to get the constant A(i+1);
it will get the FP form for the constant from bytes following
the calling literal or routine
- call 3362 GEN ENT 2 to reenter the calculator without
resetting 5C67 BREG
- add the constant, making
B(i+1) = 2Z*B(i) - B(i-1) + A(i+1)
- put B(i) in mem-2 and delete it from the calculator
stack; all parameters are now in place for the next turn of the
loop

779
- if the 5C67 BREG counter isn't yet zero loop back to G
LOOP
- (the loop has been turned N times) subtract B(N-2) in
mem-1 from B(N) on the stack; this is the result.
Exit: RET from G LOOP.
Output parameters: the result is on the stack
- HL and DE are pointers to the last value and stack end
as usual.
Called from:
36C4 exp (N=8)
373D GRE 8 (N=C)
37B7 C ENT (N=6)
37FA CASES (N=C)
Rems:
33C6 STK DATA literals supplied from calling subroutine
367A dec-jr-nz only used in series generator
3713 ln used to calculate (ln M)/(M-1) or
(ln 2M)/(2M-1), where M is mantissa of argument
37B5 sin used to calculate sin X using reduced argument
37E2 atn used to calculate atn X using reduced argument

SET ATTRIBUTE BYTE SUBROUTINE see 0BDB PO ATTR

SET DE subroutine 1195


DE is "set" to hold the_first address, not the last as
in the heading of the notes, of the working area currently in
use, either the editing area or the work space. If entered with
NC in the carry flag, it also sets HL to the last address of the
work space. Both calls from ROM are made with the carry flag
set, but the exit from 1190 SET HL has it cleared.
Input parameters: none.
Action: load DE with the address from 5C59 E LINE, the
start of the editing area
- if bit 5 of FLAGX is zero, return; editing mode
- reload DE with the address from 5C61 WORKSP, the start
of the work space
- if the carry is set, return

780
- load HL with the address from 5C53 STKBOT, the end of
the work space.
Exit: RET, three alternatives.
Output parameters: DE set, also HL if appropriate.
Called from:
1031 ED EDGE
111D ED COPY (misprinted SET HL)
Exit from:
1190 SET HL

SET HL subroutine 1190


HL is "set" to hold the_last address and DE the_first of
the working area currently in use, either the editing area or
the work space; the notes get it wrong.
Input parameters: none.
Action: set HL to the address in 5C61 WORKSP
- move HL back one; to the end of the editing area
- clear the carry.
Exit: into 1195 SET DE, which does the rest.
Output parameters: no change except in HL and the carry.
Called from:
1097 CLEAR SP

SET MIN subroutine 16B0


Reduces the editing area to a single newline, the work
space and calculator stack to zero, and ensures that 5C68
MEM points to its normal address, the base of the calculator
memory.
There is no call to the RECLAIM routines: these are the
highest areas in working RAM, and there is nothing to reclaim
above them.
Input parameters: none.
Action: get the address from 5C59 E LINE, the start of
the editing area
- put a newline in it
- put it in 5C5B K CUR, the current mode cursor address
- move on one

781
- put an 80-byte to mark the end of the editing area
- move on one
- load this address in 5C61 WORKSP, the start of the
work space.
_16BF_SET_WORK (this entry point is used when the
editing area is to be left as it was): get the address in 5C61
WORKSP
- put it in 5C63 STKBOT, the end of the work space.
_16C5_SET_STK (this entry point is used when both editing
area and work space are to be left; 5C68 MEM is reset to 5C92
MEMBOT, which is the only point of the jump from 1A15 E L 1):
get the address from 5C63 STKBOT
- put it in 5C65 STKEND, the end of the calculator stack
- put the normal address 5C92 MEMBOT in 5C68 MEM,
marking the start of the calculator memory area.
Exit: RET, from 16C5 SET STK.
Output parameters: HL holds STKEND=STKBOT
- new values have been given to the top three of the
fourteen system pointers; but not to 5C5D CH ADD and 5C5E
X PTR.
12A9 MAIN 1
1313 MAIN G

SET PERMANENT COLOURS SUBROUTINE see 1C96 CLASS


07

SET STK 16C5 (16B0 SET MIN)


Exit from:
0055 ERROR 3
16B0 SET MIN
16BF SET WORK
1A15 E L 1

SET WORK subroutine 16BF


See 16B0 SET MIN.
Called from:
1B29 STMT L 1

782
20FA IN PROMPT

SFA CP VR 296B (28B2 LOOK VARS)


Jumps from:
295A SFA LOOP

SFA END 2991 (28B2 LOOK VARS)


Exit from:
2981 SFA MATCH (28B2 LOOK VARS)

SFA LOOP 295A (2951 STK F ARG)


Jumps from:
296B SFA CP VR

SFA MATCH 2981 (28B2 LOOK VARS)


Jumps from:
296B SFA CP VR

SF ARG LP 2843 (25F5 S FN)


Jumps from:
2852 SF ARG VL

SF ARGMTS 27D9 (25F5 S FN)


Jumps from:
auto

SF ARGMT 1 2802 (25F5 S FN)


Jumps from:
27F7 SF RUN

SF ARG VL 2852 (25F5 S FN)


Jumps from:
2843 SF ARG LP

SF BRKT 1 27D0 (25F5 S FN)


Jumps from:
27BD S FN SBRN

783
SF BRKT 2 27E4 (25F5 S FN)
Jumps from:
27D9 SF ARGMTS

SF CP DEF 2814 (25F5 S FN)


Jumps from:
2808 SF FND DEF

SF FLAG 6 27E9 (25F5 S FN)


Jumps from:
27D0 SF BRKT 1

SF FND DEF 2808 (25F5 S FN)


Jumps from:
2825 SF NOT FD

S FN subroutine 25F5
Entered only from the scanning function table 2696; the
executive routine of the FN function, which must have a
corresponding DEF FN statement giving the user’s definition
of the function.
It jumps straight on to S FN SBRN, whose description is
included here; there is no room for the whole of this lengthy
subroutine in the 100h/256d-byte range of the table offsets of
the scanning function table.
The left side of a DEF FN statement usually contains a
set of arguments (X, Y, Z, ... X$, Y$, Z$, ...) corresponding to
some of the variables in the FN statement; in the DEF FN
statement each of the arguments on the left side, even the
string arguments, is followed by a number marker and a 5-
byte space for the numeric value or string parameters. In the
FN expression some of the arguments may be actual values,
others expressions or variables in the variables area or
arguments of the DEF FN, but they must all have a value -
new variables will produce an error.
However there may be no arguments at all: functions like

784
DEF FN p()=PI*X or
DEF FN r$()= "The result is "+STR$ X
are accepted.
The subroutine is in two independent parts, one for
syntax checking and the other for run time. In syntax
checking, only the FN statement is checked. Apart from
reporting any syntax errors, the routine puts 5-byte FP
number forms after any plain numbers in the FN arguments;
not after variables, eg in FN x(7,3/a) after the 7 and the 3 but
not after the a.
The run time section
- scans the program till a DEF FN statement with matching
name and numeric/string type is found
- puts a numeric value or string parameters in each of
the 5-byte spaces in the left side of the DEF FN statement,
using the value given by the FN expression
- evaluates the right side of the DEF FN statement, using
24FB SCANNING, which picks up preferentially the values
from the "dummy" variables on the left side, but looks in the
variables area for any variable names which don't appear on
the left side.
As always with SCANNING, the result of the evaluation is left
as last value on the calculator stack.
FNs can be nested, ie one DEF FN can use as arguments
expressions involving another FN, or even the same FN. When
the routine is evaluating the right-hand side of the DEF FN
statement in SF VALUE, it stacks the address from 5C0B
DEFADD and replaces it with the pointer to the first argument
in the new DEF FN; thus nested FN expressions will make a
stack of DEFADD addresses, with zero at the bottom. As
evaluation of each DEF FN is completed, an old DEFADD
address is removed from the stack and restored to 5C08
DEFADD, which thus holds a zero when the whole nest has
been cleared. Any non-zero address from 5C08 DEFADD is
used by 2951 STK F ARG, called by V TEST FN in 28B2 LOOK
VARS, called by 24FB SCANNING, to find variable values when
evaluating FN expressions, in preference to those from the

785
variables area.
Since 5C0B DEFADD is always in the program area and is
used only in run time, it doesn't require adjustment for any
MAKE ROOM or RECLAIM; hence it isn't included in the
"fourteen pointers" as one might expect.
Input parameters: none
- 5C5D CH ADD holds a BASIC pointer to the FN token.
Action: jump straight on to S FN SBRN.
_27BD_S_FN_SBRN: in run time, jump on to SF RUN
- (syntax checking) move on the BASIC pointer; now it
should be on the function letter
- call 2C8D ALPHA; if the code is a letter it returns
with carry
- if not a letter report "Nonsense in BASIC"
- move on the BASIC pointer
- check if the code is 24h $ and stack the Z/NZ flag
- if not "$" jump on to SF BRKT 1
- (it is "$") move the pointer on again.
_27D0_SF_BRKT_1: if the code isn't 28h ( report “Nonsense
in BASIC"
- move on the pointer
- if the code is 29h ) already jump on to S FLAG 6;
there are no arguments in the FN statement.
_27D9_SF_ARGMTS (still checking syntax; there are
arguments): call 24FB SCANNING, which checks the syntax of
the next argument expression and puts an FP number form
after any plain number mentioned in it; it leaves the BASIC
pointer on the last code of the expression
- if the code at the pointer isn't 2C comma jump on to
SF BRKT 2; there are no more arguments
- (more arguments in syntax checking) move the pointer
on again
- loop back to SF ARGMTS to read the next argument.
_27E4_SF_BRKT_2 (still syntax checking, no more
arguments): check if the code is 29h ).
_27E6_SF_RPRT_C: if there is no match report "Nonsense in
BASIC".

786
_27E9_SF_FLAG_6: move the pointer on again
- zero bit 6 of FLAGS; string result
- recover the flag stacked in S FN SBRN; Z indicates a
"$" after the argument letter
- if there was a "$" jump on to SF SYN EN
- (numeric argument) set bit 6 of FLAGS; numeric result.
_27F4_SF_SYN_EN: exit to 2712 S CONT 2.
_27F7_SF_RUN (run time): move on the BASIC pointer; to
the FN letter
- AND it with 11011111b/DFh to make it upper case
- move on the BASIC pointer
- subtract 24h for "$" from the code after the letter;
this makes a string/numeric flag, zero for a string
- if the result isn't zero jump on to SF ARGMT1;
"numeric" flag
- (zero flag, string argument) move on the pointer past
the "$".
_2802_SF_ARGMT1: move on the pointer; to the first code
of the arguments
- save this address; it will be used in SF VALUES below
for 5C08 DEFADD
- make a DEF FN pointer just before the address in 5C53
PROG, the start of the program area.
_2808_SF_FND_DF: make a statement counter 00 and
comparison byte CE DEF FN
- call 1D86 LOOK PROG to find a DEF FN command from
statement zero
- if none is found report "FN without DEF".
_2814_SF_CP_DEF: stack the pointer to the DEF FN
command
- call 2B2B FN SKPOVR to move the DEF FN pointer to the
function letter; not 0020 NEXT CHAR, which moves the FN
pointer
- AND it with 11011111b/DFh to make it upper case
- if it doesn't match the function letter of the FN jump
on to SF NOT FD
- (the letters match) call 2B2B FN SKPOVR to move the

787
DEF FN pointer on again
- subtract 24h $ from the code following the DEF FN
letter
- if it matches the string/numeric flag from the FN
letter jump on to SF VALUES.
_2825_SF_NOT_FD (mismatch): recover the pointer to the
DEF FN command
- move it back one
- make a statement counter 02
- call 198B EACH STMT again to look for the next
statement
- loop back to SF FND DF to look for another DEF FN.
_2831_SF_VALUES (matching DEF FN found): if the string/
numeric signal from the FN letter is zero move on to the next
code; "$" was found after the variable letter, but either way
the DEF FN pointer is now on "("
- drop the pointer to the DEF FN token; there is still a
pointer to DEF FN, on the "(" before the first argument
- get the FN pointer and put it in 5C5D CH ADD; now
calls to 0020 NEXT CHAR move the FN pointer, calls to 28AB
FN SKPOVR move the DEF FN pointer, and both subroutines
skip colour controls etc
- move the DEF FN pointer on to the first argument
- if it is on ")" jump on to SF R BR 2; there are no
arguments in the DEF FN statement.
_2843_SF_ARG_LP: increment the DEF FN pointer, not
skipping colour controls etc; one after the argument letter
- if the code is a number marker jump on to SF ARG VL
with the signal 01000000b/40h; a numeric argument. There
cannot be any colour controls between a numeric argument
letter and its number marker
- (number marker not found yet) move the DEF FN
pointer back on to the argument letter
- move it on one skipping control codes; there could be
some between a string argument letter and its "$"
- increment the pointer; now past the "$", this must be
the number marker with the string parameters

788
- make the numeric/string flag zero for a string.
_2852_SF_ARG_VL: increment the DEF FN pointer; now on
the first byte of the FP number form
- save this pointer and the numeric/string flag
- call 24FB SCANNING to evaluate the expression at the
FN pointer in 5C5D CH ADD; it returns with the evaluation on
the calculator stack and a numeric/string flag in bit 6 of FLAGS
- XOR the numeric/string flag from SF ARG LP with the
flag from SCANNING
- AND the result with 01000000b/40h; both flags are in
bit 6, so the XOR will zero bit 6 if they are alike, and the AND
will make zero if they are alike, 40h if not
- if the result isn't zero report "Parameter error"; one
is a string, the other numeric
- (the evaluation is OK) move 5C65 STKEND back 5 places
so as to clear the stack
- copy what was the last value on the stack into the 5-
byte space in DEF FN; the value of that argument
- move the DEF FN pointer back one and then on again,
skipping any colour controls which might follow the FP
number form
- if the next code in DEF FN is 29h ) jump on to SF R BR
2; there are no more arguments
- (no ")" in DEF FN yet) look at the code in FN
- if it isn't 2C comma report "Parameter error"; there
are no more arguments in the FN statement
- move on both DEF FN and FN pointers
- loop back to SF ARG LP to evaluate the next argument.
_2885_SF_R_BR_2 (closing bracket found in DEF FN): look
at the code in FN
- if it isn't 29h ) report "Parameter error".
_288D_SF_VALUE (all the arguments on the left-hand side
of the DEF FN statement now have their evaluations recorded
in FP number forms following the argument letter): move the
DEF FN pointer into 5C5D CH ADD to make it the BASIC
pointer; on the closing bracket of the left side of the DEF FN
statement

789
- get the address in 5C0B DEFADD and exchange it with
the one on top of the stack; the one from DEFADD is a pointer
to the first argument of any nested DEF FN statement, or zero
if there is none, the one from the stack is a pointer to the first
argument of the present DEF FN statement, which was
stacked way back in SF ARGMT1
- put the current pointer in 5C08 DEFADD
- move the BASIC pointer on twice, to the start of the
right side of DEF FN
- call 24FB SCANNING to evaluate the right side of the
DEF FN statement; the result on the calculator stack is the
value of the FN expression
- put the BASIC pointer into 5C5D CH ADD; on the ")" of
the FN statement, marking the point reached by 24FB
SCANNING so far
- put the stacked DEF FN pointer into 5C08 DEFADD; this
points to the first argument of any nested DEF FN statement,
or zero if there is none
- move on the BASIC pointer again.
Exit: from SF VALUES or SF SYN EN, back into the
scanning loop at 2712 S CONT 2; the entry point if the next
code might be a binary operator.
Output parameters: HL holds the address from 5C5D CH
ADD
- A holds the code at that address, the statement
terminator after the FN statement
- 5C08 DEFADD has been restored unchanged.

SF NOT FD 2825 (25F5 S FN)


Jumps from:
2814 SF CP DEF

S FN SBRN 27BD (25F5 S FN)


Jumps from:
25F5 S FN

SF R BR 2 2885 (25F5 S FN)

790
Jumps from:
2831 SF VALUES
2852 SF ARG VL

SF RPRT C 27E6 (25F5 S FN)


Jumps from:
27D0 SF BRKT 1

SF RUN 27F7 (25F5 S FN)


Jumps from:
27BD S FN SBRN

SF SYN EN 27F4 (25F5 S FN)


Jumps from:
27E9 SF FLAG 6

SF VALUE 288D (25F5 S FN)


Jumps from:
2885 SF R BR 2

SF VALUES 2831 (25F5 S FN)


Jumps from:
2814 SF CP DEF

SGN key (BC) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The F key in E mode without shift produces the function
SGN; it requires one numeric operand X, and the value of the
function is -1 for negative X, zero for zero, and one for positive
X.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code BC first to 0D, then to E9,
and adds the priority 10h/16d. Code and priority 10E9 are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following SGN is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E9 to 29, the calculator

791
offset for 3492 sgn.

sgn subroutine 3492


Called from 0028 FP CALC with literal 29; the executive
subroutine of the SGN X function. Not used in ROM otherwise.
Could be called direct from m/c, with X located anywhere in
RAM.
Given X, a 5-byte FP number, it replaces X with one if X
is positive, zero if zero, and -1 if negative. X can be in
either full or "small integer" format, but the result is always
a "small integer".
Input parameters: HL points to the first byte of X.
Action: call 34E9 TEST ZERO
- if it returns with carry, return without further
action; X is zero
- make a byte 01; the absolute value of the result
- shift the sign byte left into carry
- use SBC A,A to make a sign flag; zero for a positive,
FF for a negative X
- call 2D8E INT STORE; this puts the absolute value on
the stack with the appropriate sign, as a small integer.
Exit: RET.
Output parameters: HL and DE are preserved unchanged.

shift byte see KEYBOARD SCANNING

SHIFT FP subroutine 2FDD see also 335B CALCULATE


Shifts the true mantissa of a FP number to the right by
a specified number of binary places, corresponding to an
adjustment of the exponent made by the calling routine; the
result still expresses the same number 2**E times M, but it
isn't in standard FP format, since M is a true mantissa and
may not be between a half and one.
This operation is required
- in addition, including subtraction, of FP numbers; see
303E FULL ADDN. SHIFT FP performs step 2

792
- in printing out FP numbers; see 2ECF PF FRACTN under
the index entry 2DE3 PRINT FP.
It is an essentially simple operation, but there are two
complications:
1. Negative numbers. This isn't a problem for PRINT FP,
which is only concerned with absolute values. For addition/
subtraction the mantissa has been prepared by 2F9B PREP
ADD before SHIFT FP is called. PREP ADD
sets the hi bit so that the mantissa is a true
representation of the number
negates the mantissa if it is a negative number, ie
subtracts it from 1 00 00 00 00h
adds a fifth byte at the start of the mantissa, zero
for a positive number or 11111111b/FFh for a negative number;
thus if the mantissa was C9 0F DA A2, representing -pi/4, it
now becomes FF 36 F0 25 5E. When the mantissa is shifted
right, this sign byte remains unchanged but its lo bit,
the_sign_marker_bit, is shifted into the hi bit of the mantissa
on each shift.
2. The Spectrum handles numbers with a precision of not
more than 32d binary digits, a bit more than 9 decimal digits.
If right shifting of the binary digits pushes a one into
the 33rd digit, the 32nd digit is rounded up in normal binary
rounding. This could, with a small negative number, and with
the sign marker bits coming in from the left, round the whole
number up to zero.
If the right shift called for is more than 32d places,
the result will be indistinguishable from zero, within the
margin of accuracy of the Spectrum; although if the number is
negative, the result may actually be 1111111 ... 11b/FFFFh.
In either case the subroutine sets the result to zero.
Input parameters: A holds the number of shifts to be
made
- a_five-byte mantissa, as adjusted by 2F9B PREP ADD, is
in the five registers L'D'E'DE; L' is the sign byte.
Action: if the shift number is zero, return at once; no
shifts to be made

793
- if the shift number is more than 20h/32d, jump on to
ADDEND 0; more than 32d shifts will make the mantissa zero
- make a shift counter for the loop.
_2FE5_ONE_SHIFT: shift the sign byte right; copying its
hi bit and shifting its lo bit into carry
- shift the mantissa bytes right; carry to hi bit, lo
bit to carry each time
- loop back to ONE SHIFT till the shift counter goes to
zero
- (shifting completed) if the carry flag shows NC,
return; no rounding
- (the last shift left a carry) call 3004 ADD BACK,
which adds the carry back into the four lo mantissa bytes,
returning with Z if it "rippled back" to make zero
- return on NZ; no "ripple".
_2FF9_ADDEND_0 (result zero) zero A
- get the alternate registers.
_2FFB_ZEROS_4/5 (this is also called as a subroutine from
315E SKIP ZERO, where A isn't necessarily zero): zero the hi
byte of L'D'E'DE
- put the value of A in the second byte
- zero the remaining three bytes.
Exit: RET, from ONE SHIFT or ZEROS 4/5.
Output parameters: the adjusted mantissa is in L'D'E'DE
- HL, BC unchanged.
Called from:
2ECF PF FRACTN
3055 SHIFT LEN (twice)
Rems:
303E FULL ADDN called to line up the mantissas

shift keys, shifts see also KEYBOARD SCANNING


When the keyboard is scanned on each interrupt, by
028E KEY SCAN, the shift byte records the state of the shift
keys:
FF means "no shift"
18h means "symbol shift"; the only even value

794
27h means "caps shift", and if the_key_byte holds 18h,
the combination means "E mode".
If one of the shift byte and the key byte holds 18h or
27h and the other holds FFh, 028E KEY SCAN returns from
KEY DONE with NZ, causing an immediate return from 02BF
KEYBOARD; this is _shift_only or "shift and no key".
Caps shift 27h is read first from the keyboard scan, so
if no other key is read it will be in the key byte, which is
loaded first, but if any other key is read it will be in the
shift byte; so when the key code is transformed to a main
code in 031E K TEST, no main code is needed for caps shift.
The shift byte is used in 0333 K DECODE to implement caps
shift,
for letter keys by zeroing bit 5,
for digit keys in various ways:
in KL modes to make an edit control from 0260 table (d)
in E mode to make a colour control code, 02/03 for
BRIGHT, 18h -> 1Fh for the seven INK colours.
10h -> 17h for the PAPER colours are similarly produced
without caps shift, and are rather cryptically referred to as
_unshifted_codes in the notes on 10FA KEY CONTR. See the list
under colours.
KEY SCAN can collect symbol shift 18h in either byte,
with or without another code. However it is exchanged with
the shift byte at the end of 02AB KEY DONE, unless the shift
byte is FFh or 27h; so 18h can only remain in the key byte for
"shift and no key" or "caps and symbol shift together". When
the key byte holds 18h, K TEST returns main code 0Eh, called
"symbol shift" in the notes and tables; but this is highly
misleading, because by the time K TEST is called "shift and no
key" has been rejected and the only possible combination has
the shift byte showing "caps shift", ie both shifts were being
pressed, which counts as E mode.
Thus the main code 0Eh should properly be called "E
mode", and it is always treated as such.
0205 KEY TABLES separate tables depending on shift
028E KEY SCAN value of D indicates which shift pressed

795
0296 KEY LINE caps shift is on first five-key line
02AB KEY DONE double key accepted if one is caps shift;
if both shifts, symbol shift in E reg
02D1 K CH SET ignore shift key only
02F1 K NEW affects decoding of main code
031E K TEST shift byte transferred to B register
0333 K DECODE final code affected by
0341 K E LET letter keys in E mode
034F K KLC LET letter keys with caps or symbol shift
0367 K DIGIT digit keys in E mode with/without shift
0382 K 8 & 9 keys 8&9 in E mode with/without shift
0389 K GRA DGT digit keys in G mode with/without shift
039D K KLC DGT digit keys with/without shift
10FA KEY CONTR "unshifted" codes are PAPER colours
1F54 BREAK KEY only effective with caps shift

SHIFT LEN 3055 (3014 addition)


Jumps from:
303E FULL ADDN

SHIFT ONE 316E (30CA multiply)


Jumps from:
auto

shift only see shift keys

shifts see shift keys

short (integer) format of numbers see CALCULATE

short multiplication see 2FDD SHIFT FP

short name of variable see variables

sign bit/byte of FP number see CALCULATE

SIGN DONE 2CFE (2C9B DEC TO FP)

796
Jumps from:
2CF2 SIGN FLAG

SIGN FLAG 2CF2 (2C9B DEC TO FP)


Jumps from:
2CEB E FORMAT

sign marker bits see 2FDD SHIFT FP

SIGN TO C 3507 (3506 less-0)


Jumps from:
34F9 greater-0

SIGNUM FUNCTION see 3492 sgn

S IK$ STK 2660 (2634 S INKEY$)


Jumps from:
2684 S INKEY$ (twice)

simple strings/variables see strings, variables, 2AFF LET

SIN key (B2) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The Q key in E mode without shift produces the function
SIN; it requires one numeric operand X, and the value of the
function is sin X. X is in radians, see index entry on 3783 get-
argt.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B2 first to 03, then to DF
and adds the priority 10h/16d. Code and priority 10DF are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following SIN is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from DF to 1F, the calculator
offset for 37B5 sin.

sin subroutine 37B5

797
Called only from 0028 FP CALC with literal 1F; the
executive routine of the SIN function, ie given X in radians,
returns sin X. Could be called direct from m/c. If you are not
sure about radians, see under 3783 get-argt.
Input parameters: none
- X must be the last value on the calculator stack, even
for direct calls.
Action: use the calculator with literal 39 get-argt,
which transforms X into a "reduced argument" W; this is a
number between -1 and +1, the fraction of a right angle which
will have the same sin as X, ie sin [W * (pi/2)] = sin X.
_37B7_C_ENT (the entry point from 37AA cos): transform
the argument again, this time to Z = 2W**2 - 1; the range is
now from zero to one, and no doubt this simplifies the
Chebyshev calculation.
- use literal 86h series-06 with the six Chebyshev
coefficients:
FP format Decimal
1. 64 E6 00 00 00 -0.000000003
2. 6C 1F 0B 00 00 0.000000592
3. 73 8F 38 EE 00 -0.000068294
4. 79 15 63 BB 23 0.004559008
5. 7E 92 0D CD ED -0.142630785
6. 81 23 5D 1B EA 1.276278962
See the BASIC demonstration program on page 223 of the
notes;
the result of the calculation is sin [W * (pi/2)] divided by W,
ie (sin X)/W
- multiply this by W for the result sin X.
Exit: RET, from C ENT.
Output parameters: none, but the last value on the stack is
sin X.
Called from:
238D DR 3 PRMS
23C1 DR PRMS
2497 DRAW SAVE (twice)
37DA tan
Rems:

798
Introduction uses Chebyshev polynomials
335B CALCULATE example of unary operation
3449 series-06 calculates approximation for
3783 get-argt transforms X to "reduced argument" for
37AA cos calculates complementary angle, then sin

SINE FUNCTION see 37B5 sin above

SINGLE CALCULATION SUBROUTINE, SINGLE OPERATION


SUBROUTINE
see 33A2 fp-calc-2

single strings see strings, 2AFF LET

S INKEY$ subroutine 2634


Called only from 24FB SCANNING through the scanning
function table at 2596. The executive routine of the INKEY$
function.
The value of INKEY$ is a string from the keyboard,
unless the INKEY$ token is followed by (hatch) and
parameters for reading in from Microdrive etc, in which case
other inputs may be used
- of length zero, null string, if no key is now being
pressed
- or of length one, a single character.
INKEY$ does not wait: it looks at the keyboard and
accepts the character of any key which is pressed, but if there
is none or no valid one, it takes a null value, ie a string of
length zero. If you want it to wait you must write the classic
BASIC line
1000 IF INKEY$="" THEN GO TO 1000
There would be little point in using 2634 S INKEY$ from
m/c. To read the keyboard simply get a value from 5C08 LAST
KEY at IY - 50; for the effect of the BASIC waiting line write
something like
LD (IY-50),0
LOOP LD A,(IY-50)

799
AND A
JR Z,LOOP
This loop will repeat indefinitely till LAST KEY is
given some non-zero value by the interrupt operating the
keyboard scan.
Input parameters: none.
Action: make an op code 5A and priority 10h; see 24FB
SCANNING. This will call 3645 read-in, which handles reading
in from Microdrive etc
- move on the BASIC pointer
- if the code in BASIC is 23h (hatch) exit to 270D S
PUSH PO to stack the op code and priority
- (no hatch in BASIC, ie normal INKEY$) zero bit 6 of
FLAGS for "string result"
- if bit 7 of FLAGS shows syntax checking exit through S
INK$ EN.
- (run time; the keyboard is scanned, tested and decoded
by direct calls to the appropriate subroutines, without waiting
for the interrupt) call 028E KEY SCAN; it returns with NZ "no
key" if two keys are being pressed or similar
- make a zero length byte
- if there is NZ jump on to S IK$ STK; no valid key
- (key value received) call 031E K TEST; it returns with
NC for no-key including "shift only"
- if there is NC jump on to S IK$ STK; no-key
- decrement D; which is returned zero by K TEST, so now
FFh
- call 0333 K DECODE; D is read as if it was 5C3B FLAGS,
but the only relevant bit is 3 signalling "not K mode"
- save the final code; it cannot be anything in K, E or
G mode, but can be anything from L/C mode, including
symbol shift tokens such as THEN, TO
- call 0030 BC SPACES to make one space in the work
space
- load the code into it
- make a length byte 01.
_2660_S_IK$_STK: call 2AB2 STK STO $ to put the string

800
parameters of the string on the calculator stack; its length is
zero or one from the length byte, its start if any is the place
in the work space.
_2665_S_INK$_EN: exit.
Exit: loops back into the scanning loop
- through 270D S PUSH PO to 24FF S LOOP 1 for INKEY$
(hatch); the next code must be the stream number
- to 2712 S CONT 2 when the keyboard input has been
stacked; the next code might be a binary operator.
Output parameters: none
- for keyboard input the string parameters are now on
the calculator stack.
Rems:
O28E KEY SCAN called by S INKEY$
3645 read-in called by S INKEY$

S INK$ EN 2665 (2634 S INKEY$)


Jumps from:
2634 S INKEY$

size of program/data block see program/data block

SKIP CONS subroutine 33F7


Finds a particular constant in the calculator's constant
table at 32C5, which lists five constants, zero, one, half, pi/2
and ten, in the compressed "stk-data" form.
Could well be used in m/c programs of a mathematical
type, to find constants in a list of up to 256d constants
located anywhere in RAM.
Input parameters: HL holds the address of the start of
the table
- A holds the serial number of the required constant
starting at zero.
Action: test the counter for zero.
_33F8_SKIP_NEXT: if the counter is zero, return; the
pointer is on the required constant

801
- (non-zero counter) make a destination address 0000; as
this is in ROM, the output from STK CONST will not be
accepted
- call 33C8 STK CONST to expand the constant pointed to;
the resulting 5-byte number just disappears, but it moves the
pointer on through the table to the next entry
- decrement the counter
- loop back to SKIP NEXT.
Exit: RET, from 33F8 SKIP NEXT.
Output parameters: HL now holds the first address of the
required constant
- A is zero
- DE is saved unchanged.
Called from:
341B stk-zero
Rems:
33C6 STK DATA called by to get constant literals

SKIP NEXT 33F8 (33F7 SKIP CONS)


Exit from:
33F7 SKIP CONS
auto

SKIP OVER subroutine 007D see also printable characters


under character codes

Checks the code at the BASIC pointer and sets flags to


mark its syntax; if it is INK to OVER, moves the pointer on to
skip the argument, if AT or TAB moves on twice to skip both
arguments. If the pointer is moved, puts the new value into
5C5D CH ADD. Sets the carry unless the code is the newline or
a code from 21h "!" upwards; the space also sets carry.
Input parameters: A holds the code
- HL holds the pointer.
Action: if the code is more than 20h/32d return with no
carry; alphanumerics, symbols, tokens
- if it is a newline 0D return with no carry

802
- if it is zero -> 0C, 0E or 0F, or 18h -> 20h, return
with carry
- (this leaves 10h -> 17h, INK to TAB) move the pointer
on one
- if the code was 16h AT or 17h TAB move it on again.
_0090_SKIPS: set the carry and load the new pointer into
5C5D CH ADD.
Exit: RETs, four from SKIP OVER and one from SKIPS.
Output parameters: nothing changed except the flags and
possibly HL
- NC flag for newline 0D or anything above 20h
- C flag for zero to 0C, 0E -> 20h inclusive.
Called from:
001C TEST CHAR

SKIPS 0090 (007D SKIP OVER)


Exit from:
0070 SKIP OVER

SKIP ZERO 315E (3155 TEST NORM)


Jumps from:
3159 NEAR ZERO

SL DEFINE 2A94 (2A52 SLICING)


Jumps from:
2A7A SL RPT C
2A81 SL SECOND

S LETTER 26C9 (24FB SCANNING)


Jumps from:
2684 S ALPHANUM

sliced strings see strings

SLICING subroutine 2A52


Slicing is really a function: as SIN X finds the
numerical value of a variable X and converts it into a new

803
numerical value SIN X, so a$(3 TO 8) finds the string value of a
variable a$ and converts it into a new string; new because it
has a new start point and length.
There is however no key token SLICE. The executive
routine SLICING is called during 24FB SCANNING
(a) at 2A45 SV SLICE, when the last subscript of a string
array is reached and it is not replaced by a ")".
(b) at 2713 S CONT 3, when a string expression has just
been evaluated and the next character is a "(".
Case (a) takes care of expressions such as a$(3,2) if a$
only has two dimensions and a$(3,2 TO 7), case (b) of such as
a$(2 TO 7), a$(3)(2 TO 7).
In either case the string parameters of the string or
string array element are already the last value on the
calculator stack. The subroutine replaces them with the
parameters of the slice.
Input parameters: none
- the BASIC pointer in 5C5D CH ADD is on either the
comma before the slicing parameters or a "(" following a string
expression.
Action: call 2BF1 STK FETCH, which removes the string
parameters of the whole string from the stack and puts them
in
A: the array flag, zero for a slice or array element, one
for a complete simple string
BC: the length of the string
DE: the start address of the string
- move on the BASIC pointer
- if the next code is 29h ), as in a$(), jump on to SL
STORE; [a curiosity here: if the dimensions are a$(1,5), say,
PRINT a$(1,)
will pass syntax and print all five characters, just like PRINT
a$(1)]
- make an error register with a start value of zero;
signalling "no errors yet"
- make a slice start with a start value of one;
signalling "slice starts at first character"

804
- read the code again
- get the length as a limit for the slice value
- if it is CC TO jump on to SL SECOND with the slice
start one
- (slice start is given by BASIC) call 2ACD INT EXP2 to
read the slice start from BASIC; if it is negative or more than
65535d then 1E99 FIND INT2 called from INT EXP2 will report
"Integer out of range", but if it is over the limit value
specified, the string length, it merely decrements the error
register to a negative number
- read the code after the slice start
- if it is CC TO jump on to SL SECOND
- check if it is 29h ).
_2A7A_SL_RPT_C: if there is no match, report "Nonsense in
BASIC"
- (the code is ")") make a slice end byte equal to the
slice start; this is a single character slice, eg a$(4) or
a$(3,2)
- jump on to SL DEFINE.
_2A81_SL_SECOND: move the BASIC pointer on after "TO"
- make a limit byte; again the length of the string
- if the code found is 29h ) jump on to SL DEFINE; the
slice end is equal to the length of the string
- (slice end specified in BASIC) call 2ACD INT EXP2 to
read the slice end from the BASIC; again it decrements the
error register to a negative number if the BASIC value is over
the limit value, the string length
- if the code at the BASIC pointer is now not 29h ) jump
back to SL RPT C to report a "Nonsense".
_2A94_SL_DEFINE: get back the string start pointer from
the stack
- add the string start byte
- take away one; this is now the slice start address
- take the slice start from the slice end
- make a zero byte
- if the subtraction made a carry jump on to SL OVER
with zero as the length; slices of negative length are null

805
strings, but are allowed as correct syntax
- (zero or positive difference) increment the
difference; this is the length of the slice, eg if start and end
are equal the length should be one, not zero
- check the error register
- if it is negative report "Subscript wrong"
- transfer the slice length to the length byte.
_2AA8_SL_OVER: zero bit 6 of FLAGS; "string result".
Exit: into 2AB1 STK ST 0, which puts the string
parameters on the calculator stack, with the index zeroed to
show "array/slice".
Output parameters (into STK ST 0): the start and length
of the slice are in DE and BC.
Called from:
2713 S CONT 3
2A45 SV SLICE
Rems:
2996 STK VAR check for slicing before stacking string
parameters
2AB1 STK ST 0 array/slice flag is used in LET routines

S LOOP 2734 (24FB SCANNING)


Jumps from:
2713 S CONT 3
2723 S OPERTR
2770 S LOOPEND

S LOOPEND 2770 (24FB SCANNING)


Jumps from:
2764 S RUNTEST
S LOOP 1 24FF (24FB SCANNING)
Jumps from:
25AF S U PLUS
270D S PUSH PO
2790 S NEXT

SL OVER 2AA8 (2A52 SLICING)

806
Jumps from:
2A94 SL DEFINE

SL RPT C 2A7A (2A52 SLICING)


Jumps from:
2A12 SV RPT C
2A81 SL SECOND

SL SECOND 2A81 (2A52 SLICING)


Jumps from:
2A52 SLICING (twice)

SL STORE 2AAD (2A52 SLICING)


Jumps from:
2A52 SLICING

SMALL 37F8 (37E2 atn)


Jumps from:
37E2 atn

small integer format of FP numbers see CALCULATE

S NEGATE 26DF (24FB SCANNING) see also BASIC


INTERPRETER
Jumps from:
2684 S ALPHNUM

S NEXT 2790 (24FB SCANNING)


Jumps from:
2773 S TIGHTER
2788 S NOT AND

S NOT AND 2788 (24FB SCANNING)


Jumps from:
2773 S TIGHTER

S NO TO $ 2707 (24FB SCANNING)

807
Jumps from:
26DF S NEGAT

S NUMERIC 26C3 (24FB SCANNING)


Jumps from:
2630 S PI END
2672 S ATTR
267B S POINT
268D S BIN

S OPERTR 2723 (24FB SCANNING)


Jumps from:
2713 S CONT 3

sound generation loop see 03B5 BEEPER.

source variable see variables

space, space character, SPACE key (20h) see also space in


RAM below
Without shift, or with symbol shift, read as space
whether in C, E, G, K or L mode. With caps shift, read as
BREAK in contexts where a BREAK is looked for, otherwise as
space.
For many purposes, space behaves as a letter code. But
not in 007D SKIP OVER; hence not in 0018 GET CHAR and
0020 NEXT
CHAR, so spaces are ignored in reading variable names and
generally in executing BASIC lines, except within quotes.
0205 main key table (a)
0333 K DECODE jumps for space as if digit
0367 K DIGIT return with space
0629 SA BLANK "save" filename filled out with spaces
0A3D PO RIGHT "cursor right" implemented by printing a
space with OVER 1
0AC3 PO FILL calculate no of spaces to execute TAB
0AD0 PO SPACE print calculated no to execute TAB

808
0B6A PO CHAR 2 space printed as leading space
0C14 PO TABLE print leading space before message
1150 ED BLANK line of BASIC filled out with spaces
115E ED SPACES prints spaces to fill line
133C MAIN 5 report code followed with space, message
with comma and space
1925 OUT SP 2 prints leading space if required
192B OUT SP 1 digit with leading space if required
1937 OUT CHAR space after INK to TAB
28AB FN SKPOVR skip over spaces
2913 V SPACES ignored in variable long name
295A SFA LOOP skip over spaces after variable letter
2B0B L EACH CH ignore spaces in variable name
2B0C L NO SP skip over spaces
2B72 L DELETE$ new room in work space filled with
blanks
2C2E D NO LOOP space put in last element of string array

space (in RAM)


In 111D ED COPY, rather confusingly used to mean "work
space or editing area, whichever is being used". Otherwise
usually refers to the operation of moving up a block of code to
leave room for insertion of new code, see 1655 MAKE ROOM.

spaces see space above

S PI subroutine 2627
Called only by the scanning loop from the table at 2696;
the executive routine of the PI function, which has no
operand but merely returns the number pi, 3.1415927d/
3.243FA68h, or in FP format 82 49 0F DA A2. Pi is a "real
number", so all these representations are approximate.
Input parameters: none.
Action: use the calculator; pi isn't in the list of
constants at 32C5, but pi/2 is
- get pi/2 on the calculator stack
- increment its exponent byte; ie double it.

809
_2630_S_PI_END (also used as exit by 26F8 S RND): move
on the BASIC pointer.
Exit: into 26C3 S NUMERIC, which goes back into the
scanning loop, with the string/numeric flag appropriately set,
at 2712 S CONT 2; the next code may be a binary operator.
Output parameters: none
- pi is now last value on the calculator stack.
Rems:
2625 S RND END skipped over

S PI END 2630 (2627 S PI)


Jumps from:
2625 S RND END
2627 S PI

S POINT subroutine 267B


Called only by the scanning loop from the function table
2696; the executive routine of the POINT function, which has
two operands; POINT (X,Y) is one if the pixel at X,Y on the
screen is ink colour, zero if it is paper colour.
Most of the action is in POINT SUB, which is only called
from here, and therefore not described separately: the only
point of this is to abbreviate S POINT to fit in the 256d-byte
range of the table offsets.
Input parameters: none.
Action: call 2522 S 2 COORD to check that the coordinates
are in place in the BASIC
- call POINT SUB.
_22CB_POINT_SUB: call 2307 STK TO BC to get the
coordinates
- call 22AA PIXEL ADD to convert them to a pixel address
in the display area and the bit number of the pixel
- make a pixel counter by incrementing the bit number
- read the pixel byte from the display area.
_22D4_POINT_LP: rotate the byte left
- loop back to POINT LP counting down to zero; this
leaves the pixel in the lo bit

810
- AND the rotated byte with 00000001b
- exit to 2D28 STACK A, which puts one or zero on the
calculator stack.
- (return to S POINT) move on the BASIC pointer.
Exit: into 26C3 S NUMERIC, which goes back into the
scanning loop with the string/numeric flag set to numeric, at
2712 S CONT 2; the next code may be a binary operator.
Output parameters: none
- one for ink pixel or zero for paper pixel on the
calculator stack.
Rems:
2522 S 2 COORDS called by S POINT

S POSN system variable 5C88 see also DISPLAY AREA


Bytes: 2
The print position in the upper screen, ie the position
at which the last character was printed, updated after every
output to the upper screen or cursor move by 0ADC PO
STORE.
- in the hi byte the line number goes from 18h/24d at the
_top to one at the bottom
- in the lo byte the column number goes from 20h/32d on
the_left of the screen to one on the right; however S POSN lo
never holds the value one. If printing reaches the end of a line
or skips to the start of a new line, the hi byte is decremented,
usually with a test for scrolling, and the lo byte set to 21h/33d,
which is notionally the last position printed.
When the screen is cleared, S POSN is set to lo 21h/33d,
hi 18h/24d, by 0DAF CL ALL followed by PO STORE.
Written by:
0ADC PO STORE
20AD INPUT 2
0D1C PO SCR 4A (hi byte)
Read by:
0B03 PO FETCH
0D1C PO SCR 4A (hi byte)
0D2D PO SCR 4B

811
12CF MAIN 3 (hi byte)
1835 LIST ALL (hi byte)
2096 INPUT 1

S POSNL system variable 5C8A


Bytes: 2
Similar to S POSN, but gives the current print position
in the_lower part of the screen; and the line number is
counted from the top of the lower part of the screen. This is
line 18h/24d of the lower screen, and always left blank: so
initially, and each time the lower screen is cleared, the hi byte
is set to 17h/23d and the lo byte to 21h/33d by 0DA0 CL CHAN
A followed by 0ADC PO STORE.
Written by:
0AF0 PO ST E
Read by:
0B03 PO FETCH
111D ED COPY (twice)
1150 ED BLANK (once each byte)
1167 ED FULL
117E ED C END

S PUSH PO 270D (24FB SCANNING)


Jumps from:
2634 S INKEY$
26DF S NEGATE (three times)
2707 S NO TO $

S Q AGAIN 25BE (25B3 S QUOTE)


Jumps from:
auto

SQUARE ROOT FUNCTION see 384A sqr

S Q COPY 25CB (25B3 S QUOTE)


Jumps from:
auto (twice)

812
S Q PRMS 25D9 (25B3 S QUOTE)
Jumps from:
2583 S QUOTE
25BE S Q AGAIN

SQR key (BB) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The H key in E mode without shift produces the function
SQR; it requires one numeric operand X, and the value of the
function is the square root of X.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code BB first to 0C, then to E8,
and adds the priority 10h/16d. Code and priority 10E8 are
now pushed on to the machine stack (270D S PUSH PO) while
the expression following SQR is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from E8 to 28, the calculator
offset for 384A sqr.

sqr subroutine 384A


Called only from 0028 FP CALC by offset 28; the
executive routine of the SQR function, but also called a couple
of times in performing other calculations - both times through
the calculator, but it could be called direct. Given any positive
number X, returns the square root of X.
Most square roots aren't rational numbers, ie not a
ratio of two integers a/b, so only an approximation can be
found in decimals, hex, binary etc: for example the square
root of two is 1.414..., but as Pythagoras discovered this isn't a
rational number; he kept this terrifying fact secret. See under
real numbers.
Zero X returns a square root zero, negative X gives an
error: though the square roots of minus numbers are
extensively used in some types of physics, they aren't real
numbers and can't be expressed in decimals, hex, etc in the
ordinary way.

813
Input parameters: X must be last value on the calculator
stack, even for direct calls.
Action: use the calculator with literals for duplicate/
not/jump-true, equivalent to "jump on zero", to LAST
- (non-zero X) put 0.5d on the stack.
Exit: to 386C LAST if X is zero; this is merely end-calc
and RET, but zero X on the stack is already the result
- otherwise into 3851 to-power, which finds the 0.5'th
power of X, ie its sauare root, or reports error if X is
negative.
Output parameters: none
- the calculator stack holds, from the top, 0.5d, X.
Called from:
247D CD PRMS1
3833 asn
Rems:
3449 series-06 indirectly produced by Chebyshev
polynomials (through offset 25 ln)

S QUOTE subroutine 25B3


Called by SCANNING through the scanning function table
2696; the executive routine for handling string quotes. See
also 22h " after the end of the alphabet.
Quotation marks signal the beginning of a string, of
length up to the next quotes; but repeated double quotes may
be included as part of the string, to count as a single "
character. This subroutine collects the start address and
length of the string and puts them on the calculator stack as
string parameters. If the string includes quotes, a copy is
made to the work space with the doubled quotes reduced to
single, and the string parameters point to this copy.
Input parameters: none.
Action: get the BASIC pointer and increment it; now on
the start of the string
- make a zero byte; "length so far"
- call 250F S QUOTE S which steps along the string
counting bytes till it comes to another "; it returns with Z if

814
this " is immediately followed by another. On each call it
returns one more than the actual length so far
- if it returns with NZ jump on to S Q PRMS; "end of
string".
_25BE_S_Q_AGAIN (S QUOTE S returned with zero): call S
QUOTE S again
- (end of string reached; there are included quotes, but
S QUOTE S has returned one more than the length of the
string without its opening and closing quotes and counting
only single embedded quotes) make a space in the work space
of this length
- set pointers on the start of the string and the start
of the work space.
_25CB_S_Q_COPY: read a code from the string pointer
- move on the string pointer
- copy the code to the work space pointer
- move on the work space pointer
- if the last code wasn't 22h " loop back to S Q COPY
- (it was) read the next code and move on the string
pointer only
- if the next code is again 22h ", loop back to S Q
COPY; without copying the second quote of the pair to the
work space
_25D9_S_Q_PRMS (copying is finished, or if entry is from
S QUOTE there was no copying; the string start pointer is on
the stack, and the length including the final quote has been
counted) decrement the length to exclude the final quote
- recover the start pointer; these are the string
parameters.
Exit: into 25DB S STRING, which puts the parameters of
the copy in the work space, if one was made, otherwise of the
string in the BASIC on the calculator stack and returns into
the scanning loop at 2712 S CONT 2; the next code may be a
binary operator.
Output parameters: none
- the string parameters are on the calculator stack.
Rems:

815
250F S QUOTE S used by

S QUOTE S subroutine 250F


Finds the length of a string between quotes; see S QUOTE
above, which is the only routine to call it.
Input parameters: BC holds the length of the string as
counted so far, perhaps up to a pair of embedded quotes.
Action: call 0074 CH ADD+1 to move on the BASIC pointer
in 5C5D CH ADD
- increment the string length
- if the code at the pointer is a newline report
"Nonsense in BASIC"; the string lacks a closing "
- if it isn't 22h " loop back to S QUOTE S
- (" found) call 0074 CH ADD+1 to move on the BASIC
pointer
- if the next character is a consecutive " return with
Z; the end of the string hasn't been reached.
Exit: RET.
Output parameters: BC holds the length of the string,
inclusive of the last but not the first quotation mark, and
counting embedded pairs as single characters
- the Z/NZ flag shows Z if return was made on the first
of two ""s.
Called from:
25B3 S QUOTE
25BE S Q AGAIN
Jumps from:
auto

S RND subroutine 25F8


Called only by SCANNING through the scanning function
table 2696; the executive routine for handling the RND
function.
Puts a "pseudorandom" number between zero and one on the
stack;
like PI, it is a function without an argument. Only half true in
this case: RND X can't be written in BASIC, but the subroutine

816
really uses the value in 5C76 SEED as an argument. See page
73 of the old handbook, page 77 of the Plus 2 handbook.
Strictly speaking, a random number is a number that
can't be predicted, or one whose method of calculation isn't
known. Computers therefore can't produce random numbers!
The "pseudorandom" numbers of the Spectrum are produced
from a "seed" S by the formula
R = remainder on dividing (S+1)*75 by 65537, less 1,
divided by 65536; all decimals.
These are perfectly predictable, but the computer can
produce them faster than you can predict them!
Input parameters: none.
Action: get the value from 5C76 SEED and call 2D2B
STACK
BC to put it on the stack
- figure the formula above, not including the division
by 65536d, on the calculator
- duplicate this result
- call 2DA2 FP TO BC to take one copy of the stack;
leaving only one copy on the stack. FP TO BC makes no error
reports, but truncates the number on the stack to an integer,
and if it is more than 65536d merely returns its remainder on
dividing by 65536d
- put it in 5C76 SEED
- get the exponent of the copy from the stack pointer
- if it is zero jump on to S RND END; the result is zero
- (non-zero) take away 10h from the exponent, equivalent
to dividing the number by 2**10h = 65536d.
_2625_S_RND_END: jump on to exit.
Exit: into 26C3 S NUMERIC via 2630 PI END, going back
into the scanning loop with the flag set for "numeric result" at
2712 S CONT 2; the next code may be a binary operator.
Output parameters: none
- the value of RND is on the calculator stack.
Rems:
26C3 S NUMERIC numeric result identified
36A0 n-mod-m only used to calculate random number

817
S RND END 2625 (25F8 S RND)
Jumps from:
25F8 S RND (twice)

S RPORT C 252D (24FB SCANNING; the heading is


duplicated at
2761)
Exit from:
2522 S 2 COORD (twice)

S RPORT C 2761 (2734 S LOOP; the heading is duplicated at


252D)
Exit from:
275B S SYNTEST
2788 S NOT AND

S RUNTEST 2764 (24FB SCANNING)


Jumps from:
274C S STK LIST

S SC MATCH 255A (2668 S SCREEN$)


Jumps from:
254F S SCRN LP

S SCREEN$ subroutine 2668


Called only from the scanning function table 2596; the
executive routine of the SCREEN$ function. Finds SCREEN$
(X,Y), the character on screen at the print position X,Y. The
result is a string of length one.
If the character at X,Y isn't in the range 20h "space"
to 7Fh "", the function returns the null string, length zero.
This won't happen with tokens, which are put on screen as
letters, but it will happen with user-designed graphics, unless
sv 5C3O CHARS is reset for a scan of the UDGs.
There are two complications:
1. The pixel bytes of the characters aren't consecutive on

818
the screen, although they are in the character set. See
DISPLAY AREA.
2. The character on screen may be displayed INVERSE, ie
with PAPER and INK reversed; this must still count as a match.
The subroutine cannot recognise one character printed OVER
another.
Most of the action is in the pseudo-subroutine 2525 S
SCRN$ S, which is called only from here, but this is merely a
device to abbreviate S SCREEN$ so that it will fit in the 256d-
byte range of the table offsets, so its description is included
here.
[As the notes on S SCRN$ S point out, the programmer
lost his place here: S SCRN$ S should return directly, not
through STK STO$. You can see the effect by running the
following:
10 CLS: PRINT "hello"
20 PRINT SCREEN$ (0,0)+SCREEN$(0,1)
This ought to print "hello" followed on the next line by
"he", but in fact it is followed by "ee". This is because the
PRINT statement in line 20 is a single expression; in
evaluating this expression, the stack gets wrongly loaded
with_two "h"s and _two "e"s and only the last two sets of string
parameters get printed. If the "+" in line 20 is replaced with a
";", so that the two SCREEN$ functions are evaluated as
separate expressions, the program prints out correctly.
A nit-picker could also criticize the ROM programming in
S SCRN$ S on the ground that as a large number of the
character forms have 00000000 as their first pixel byte, it
wastes a good deal of time; quicker to sample the forms in
their fourth or fifth bytes, which are much more distinctive.
However "a good deal of time" doesn't actually mean much
when time is measured in T states; there is no perceptible
delay in response to the function.]
Input parameters: none.
Action: call 2522 S 2 COORD to read the X and Y
coordinates from BASIC and put them on the calculator stack
- call S SCRN$ S to evaluate the function.

819
_2535_S_SCRN$_S: call 2307 STK TO BC to get the
coordinates back; X is in C register, Y in B
- get a pointer to the address in 5C36 CHARS; this is
100h less than on the first pixel byte of the character set
- add 100h; now on the first pixel byte
- calculate a screen pointer to the address in the
display area of the first pixel on screen belonging to the print
position: for tab Y on line X this is, all in hex numbers:
4000 + 20 * (remainder on dividing X by 8) + Y
The "one-byte binary arithmetic" used is almost identical with
that of 0E9B CL ADDR; see the index entry
- make a character counter 60h/96d.
_254F_S_SCRN_LP: get the pixel byte from the screen
pointer
- XOR it with the one in the character set; a direct
match gives 00000000b and an "inverse match" gives 11111111b
- if the result is zero jump on to SC MATCH
- (not a direct match) increment the result; 11111111b
becomes 00000000b
- if it isn't now zero jump on to S SCR NEXT
- (inverse match) decrement it back to 11111111b.
_255A_S_SC_MATCH (first pixels match): copy the result of
matching as an inverse/direct flag; 00 for direct match, FF for
inverse match
- make another counter 07; to loop through the remaining
seven pixel bytes of the screen character and the character
form. The bytes are consecutive in the character form but
100h/256d bytes apart in the screen display.
_255D_S_SC_ROWS: move the screen pointer on 100h; to
the next pixel byte on screen
- move the character form pointer on one
- XOR the screen pixel byte with the form pixel byte
- now XOR the result with the inverse/direct flag; in
either case a match will produce zero
- if it isn't zero jump on to S SCR NEXT; mismatch
- (matching so far) loop back to S SC ROWS counting
down the pixel bytes to zero

820
- (all eight bytes match) drop the pointers
- recover the character counter; it was counting down
characters from 96d
- subtract it from 80h/128d to get the character code;
eg the first code in the set is 128 - 96 = 32d, the space
- make a space byte one; this one is also used on exit
from S SCR STO as the length of the string to be stacked
- call 0030 BC SPACES to make one space in the work
space
- put the code in it
- jump on to S SCR STO.
_2573_S_SCR_NXT: (mismatch found) move on the
character
set pointer by 8 bytes
- put the screen pointer back on the first pixel byte
- loop back to S SCRN LP, counting down the character
counter to zero
- (no match found anywhere) make the string length zero.
_257D_S_SCR_STO: exit from S SCRN$ S through 257D S
SCR STO$ which puts the string parameters on the calculator
stack.
- (return to S SCREEN$) move on the BASIC pointer and
exit.
Exit: into 25DB S STRING, which stacks the string
parameters again - unnecessarily, see the note above - and
jumps back into the scanning loop at 2712 S CONT 2; the next
code could be a binary operator.
Output parameters: none
- doubled string parameters on the calculator stack.
Rems:
2522 S 2 COORD used by S SCREEN$

S SCRN LP 254F (2668 S SCREEN$)


Jumps from:
2573 S SCR NXT

S SCR NXT 2573 (2668 S SCREEN$)

821
Jumps from:
254F S SCRN LP
255D S SC ROWS

S SCRN$ S subroutine 2535


See 2668 S SCREEN$.
Called from:
2668 S SCREEN$

S SCR ROWS 255D (2668 S SCREEN$)


Jumps from:
auto

S SCR STO 257D (2668 S SCREEN$)


Jumps from:
255D S SC ROWS

S SD SKIP 26B6 (268D S BIN)


Jumps from:
auto

S STK DEC 26B5 (268D S BIN)


Jumps from:
268D S BIN

S STK LIST 274C (24FB SCANNING) see also BASIC


INTERPRETER
Jumps from:
2734 S LOOP (twice)
Rems:
34B3 usr-no NB that on return to BASIC from m/c,
alternate HL' must hold 2758h, the address
of 38 end-calc in this section

S STRING 25DB (24FB SCANNING)


Exit from:
25B3 S QUOTE

822
25D9 S Q PRMS
2668 S SCREEN$
Rems:
257D S SCR STO mistake; parameters stacked twice

S SYNTEST 275B (24FB SCANNING)


Jumps from:
274C S STK LST

stack see CALCULATE, GO SUB stack

STACK A subroutine 2D28


Given a single-byte integer, puts its value on the
calculator stack in small integer FP form. The value is always
read as positive, eg FFh is read as 255d, not -1.
Input parameters: value in the A register.
Action: transfer the value to the BC register.
Exit: into 2D2B STACK BC, which puts the value on the
stack.
Output parameters: the value in BC, with B zero
- everything else unchanged.
Called from:
23C1 DR PRMS (twice)
2439 ARC START (twice)
245F ARC END (twice)
2497 DRAW SAVE
371C VALID
STKEND is not on, but one beyond, the last byte of the
last number on the stack; therefore also the first byte to be
filled by any new number put on the stack, or if a number has
just been deleted from the stack it points to the first byte of
that number, which isn't overwritten until there has been a
call to 16C5 SET STK or similar.
Written by:
1219 RAM SET
166B PTR NEXT
16C5 SET STK

823
19FB E LINE NO
268D S DECIMAL
2852 SF ARG VL
2981 SFA MATCH
2AB6 STK STORE
2BF1 STK FETCH
3365 RE ENTRY
33B4 STACK NUM
Read by:
1655 MAKE ROOM
166B PTR NEXT
1EB7 CLEAR 1
1F05 TEST ROOM
268D S DECIMAL
2852 SF ARG VL
2981 SFA MATCH
2AB6 STK STORE
2BF1 STK FETCH
338E ENT TABLE (really a read of BREG; should be
written LD BC,(BREG-1))
33B4 STACK NUM
35BF STK PNTRS
Rems:
167F PTR DONE left in DE after PTR NEXT; top of block
to be moved up or down
2B59 L NUMERIC delete moves STKEND down five bytes
2D2B STACK BC end-calc makes HL point to STKEND-5
2DA2 FP TO BC end-calc makes HL point to STKEND-5
2DAD FP DELETE after end-calc DE points to STKEND
3014 addition exits with DE=STKEND, HL=STKEND-5
30EA MULT RSLT exits with DE=STKEND
323F T SMALL save STKEND on machine stack*
3267 T STORE exit with DE=STKEND*
3272 NIL BYTES save STKEND on machine stack*
3290 IX END exit with DE=STKEND*
335B CALCULATE set DE=STKEND
342D st-mem exit with DE=STKEND*

824
3483 INT CASE exit with DE=STKEND*
3492 sgn exit with DE=STKEND*
*These notes aren't quite correct, because DE isn't
necessarily STKEND; the subroutines are always used
in the ROM for numbers on the stack, but they could
be used otherwise.

STK F ARG 2951 (28B2 LOOK VARS)


Exit from:
28E3 V TEST FN (28B2 LOOK VARS)

STK FETCH subroutine 2BF1


Copies the last value from the calculator stack into the
registers AEDCB, and moves 5C65 STKEND down so that the
value is notionally removed from the stack; but in fact it is still
there and can be read by appropriate m/c.
Input parameters: none.
Action: set a pointer on 5C65 STKEND
- step it back five times, copying the byte into the
appropriate register at each step
- put the final pointer address in 5C65 STKEND.
Exit: RET.
Output parameters: A, BC, DE have all been given values
as indicated
- HL holds the new 5C65 STKEND
- if the last value is a pair of string parameters, as
often
- BC holds the length of the string
- DE its start address
An alternative to using the reset button is the BASIC
command PRINT [or RANDOMIZE] USR 0. The effect is similar
to NEW, but in fact more complete, in that 5CB2 RAMTOP, the
user-defined graphics, the click and the rasp are all reset to
their standard values.
Input parameters: none.
Action: disable the interrupt; the keyboard scanning
interrupt routine would be chaotic until IY gets its value in

825
1219 RAM SET
- make a START/NEW flag of zero; when 11EF RAM DONE
is reached, 5CB4 P RAMT, 5C38 RASP, 5C39 PIP and later 5C7B
UDG will be given their standard values, and 168d bytes from
the character set, A -> U, will be copied into the top end of
RAM
- signal FFFFh for "maximum possible RAM".
Exit: into 11CB START/NEW; see under 11B7 NEW.
Output parameters: A holds the START/NEW flag
- DE holds the RAM limit.
Exit from:
0066 RESET
Rems:
11B7 NEW at 11CB START/NEW A holds zero from START
11DA RAM CHECK check continues up to FFFF
11EF RAM DONE restoration of svs is meaningless
1219 RAM SET from here on common to START and NEW

start address of BASIC line, etc see BASIC line, etc

START/NEW 11CB (11B7 NEW)


Jumps from:
0000 START
Rems:
11B7 NEW entry point from 0000 START

start of data block see program/data block, 0605 SAVE ETC

start (pointer) of array data, etc see arrays, etc

start of program see 5C53 PROG

start of slice see 2A52 SLICING

start of string see strings

'starts' in memory

826
An expression only used in 19DD DIFFER; it seems to
mean the start addresses of the various sections of RAM such
as those kept in 5C53 PROG, 5C4B VARS, 5C59 E LINE, 5C61
WORKSP.
As the subroutine merely returns the difference between
HL and DE, whatever numbers they happen to hold, the
expression is unnecessary anyway.

'Start tape, then press any key' message see cassette


messages

statement (BASIC) see BASIC line; for eg "DEF FN


statement",
see DEF FN key

statement counter, statement number see BASIC line

STATEMENT LOOP see STMT L 1 in 1B8A LINE RUN

status, numeric/string see expressions

STEP key (CD) see also KEYBOARD SCANNING, 026A


symbol code
table (e)
The D key with symbol shift in C, K or L mode produces
the "adverb" token STEP, which is neither a command, a
function nor an operator; not in the command table at 1A48
and never part of an expression, unless you include it in a
string within quotes, which is quite acceptable; so isn't read
by 24FB SCANNING either.
The only context in which this token can be used is
within a FOR statement, see 1D03 FOR.

ST E PART 2CFF (2C9B DEC TO FP)


Jumps from:
2CF2 SIGN FLAG

827
steps (line drawing) see 24B7 DRAW LINE

S TIGHTER 2773 (24FB SCANNING)


Jumps from:
2734 S LOOP

STKBOT system variable 5C63


Bytes: 2
Holds the address of the bottom of the calculator stack,
which also marks the end of the work space. It is one of the
fourteen system pointers which are adjusted by 1664
POINTERS whenever space is made or reclaimed in the RAM.
Written by:
1219 RAM SET
166B PTR NEXT
16BF SET WORK
Read by:
1195 SET DE
166B PTR NEXT
169E RESERVE
16CF SET STK
219B IN VAR 6

STK CODE 3671 (3669 code)


Jumps from:
3669 code

STK CONST subroutine 33C8


See 33C6 stk-data below.
Called from:
33F8 SKIP NEXT
341B stk-zero

stk-data subroutine 33C6


Called from 0028 FP CALC with literal 34; once also
called direct. It puts on the calculator stack a number X
defined by 2, 3, 4 or 5 bytes which it finds at the address in

828
the alternate HL register: when called from FP CALC these are
the bytes following the 34 literal, when it is called direct
from one of the calculator routines they are the bytes
following the literal of the calling routine. Such a call is made
from 3453 G LOOP in the series-06 routine. To call it from m/c
it is necessary to load HL' with a pointer to the first byte.
The bytes of the constant are supplied not in normal FP
format but in a special "stk-data format"; see the header notes.
The algorithm is:
Delete zero bytes from the end of the FP number.
Reduce the first byte by 50h, and then add 40h for each
byte remaining after the_second.
If this produces a multiple of 40h, put this multiple of
40h as an additional first byte, and put the first byte of the
FP number, less 50h, as the_second byte. This additional byte
_doesn't count when adding 40h in the second step.
Using the same examples as those used in the index entry
CALCULATE for FP numbers:
X = 73d = 49h
FP(73) = 87 12 00 00 00
Stk-data: (34) 37 12
X = 500d = 1F4h
FP(500) = 89 7A 00 00 00
Stk-data: (34) 39 7A
X = pi = 3.1415927d
FP(pi) = 82 49 0F DA A2
Stk-data: (34) F2 49 0F DA A2
X = e**pi = 23.140693d
FP(e**pi) = 85 39 20 23 A8
Stk-data: (34) F5 39 20 23 A8
X = -41764d = -A324
FP(X) = 90 A3 24 00 00
Stk-data: (34) 80 40 A3 24
Input parameters: HL holds the address of the first byte
of the last value
- DE holds the stack end pointer from 5C65 STKEND
- alternate HL' holds the "return address", which in

829
fact holds the next literal; all these are the usual for a unary
operation.
Action: put a result pointer on the stack end; it will be
the address of the first byte of the new last value.
_33C8_STK_CONST (the entry point when X is one of the
five constants in the table at 32C5; in this case the result
pointer already points to the address where the number is to
be put): call 33A9 TEST 5 SP to check that there is room to
extend the calculator stack
- get the "return" address and read the byte there,
saving the result pointer
- AND the byte with 11000000b/C0h
- rotate the two hi bits into the two lo bits; the
effect is to divide it by 40h, discarding the remainder
- increment the result; now a byte counter for the other
bytes of X
- AND the original "return" byte with 00111111b/3Fh;
this finds the remainder on dividing the byte by 40h
- if the remainder isn't zero jump on to FORM EXP; this
remainder is the exponent byte of X, less 50h
- (the remainder was zero) read the next byte.
_33DE_FORM_EXP: add 50h; making the exponent byte of
X
- copy the exponent to the result pointer
- subtract the counter from 5; this is the number of
trailing zero bytes that will be needed
- copy bytes from the "return" address into the result
using the counter
- put the "return" address, now moved on past the bytes
of the number, back in HL'
- recover the result pointer
- set the counter for the trailing zeroes
- make a zero byte.
_33F1_STK_ZEROS: decrement the zero counter
- if it is now zero, return
- put a zero byte in the result on the calculator stack
- loop back to STK ZEROS.

830
Exit: RET, from STK ZEROS. When called by literal 34 this
will be to 3365 RE ENTRY, the address put on the stack in
338E ENT TABLE.
Output parameters: HL' holds the address following the
data bytes; it was incremented each time a byte was read from
the "return" address
- DE holds the new stack end pointer
- HL points to the first byte of the last value.
Called by literal 34 from:
03F8 BEEP
0427 BE OCTAVE (twice)
247D CD PRMS1
25F8 S RND (twice)
2DC1 LOG(2**A)
36C4 EXP
371C VALID (twice)
373D GRE.8 (twice)
3783 get-argt
Called as STK DATA by:
3453 G LOOP
Rems:
341B stk-zero uses STK CONST entry point

STK DIGIT subroutine 2D22


Given the character code for a decimal digit, 30h/48d
"zero" -> 39h/57d "nine", puts the digit value on the calculator
stack as a small integer. Used in calculating the FP forms of
decimal numbers in BASIC.
Input parameters: A holds the character code.
Action: call 2D1B NUMERIC
- if the code isn't a digit, return at once
- (a digit) subtract 30h/48d for the value of the digit.
Exit: immediate RET if not a digit, with carry set
- into 2D28 STACK A to put it on the stack.
Output parameters: A holds the result, nothing else
changed.
Called from:

831
2CDA NXT DGT 1
2D40 NXT DGT 2

STKEND system variable 5C65


Bytes: 2.
Holds the address of the top of the calculator stack. It
is the last of the fourteen system pointers adjusted by 1664
POINTERS each time space is made or reclaimed in RAM:
STKEND moves upwards into the "spare space" in RAM,
initially a very large space, until it collides with the top of the
machine stack expanding downwards, producing an "Out of
memory" error.
STKEND is not on, but one beyond, the last byte of the
last number on the stack; therefore also the first byte to be
filled by any new number put on the stack, or if a number has
just been deleted from the stack it points to the first byte of
that number, which isn't overwritten until there has been a
call to 16C5 SET STK or similar.
Written by:
1219 RAM SET
166B PTR NEXT
16C5 SET STK
19FB E LINE NO
268D S DECIMAL
2852 SF ARG VL
2981 SFA MATCH
2AB6 STK STORE
2BF1 STK FETCH
3365 RE ENTRY
33B4 STACK NUM
Read by:
1655 MAKE ROOM
166B PTR NEXT
1EB7 CLEAR 1
1F05 TEST ROOM
268D S DECIMAL
2852 SF ARG VL

832
2981 SFA MATCH
2AB6 STK STORE
2BF1 STK FETCH
338E ENT TABLE (really a read of BREG; should be
written LD BC,(BREG-1))
33B4 STACK NUM
35BF STK PNTRS
Rems:
167F PTR DONE left in DE after PTR NEXT; top of block
to be moved up or down
2B59 L NUMERIC delete moves STKEND down five bytes
2D2B STACK BC end-calc makes HL point to STKEND-5
2DA2 FP TO BC end-calc makes HL point to STKEND-5
2DAD FP DELETE after end-calc DE points to STKEND
3014 addition exits with DE=STKEND, HL=STKEND-5
30EA MULT RSLT exits with DE=STKEND
323F T SMALL save STKEND on machine stack*
3267 T STORE exit with DE=STKEND*
3272 NIL BYTES save STKEND on machine stack*
3290 IX END exit with DE=STKEND*
335B CALCULATE set DE=STKEND
342D st-mem exit with DE=STKEND*
3483 INT CASE exit with DE=STKEND*
3492 sgn exit with DE=STKEND*
*These notes aren't quite correct, because DE isn't
necessarily STKEND; the subroutines are always used
in the ROM for numbers on the stack, but they could
be used otherwise.

STK F ARG 2951 (28B2 LOOK VARS)


Exit from:
28E3 V TEST FN (28B2 LOOK VARS)

STK FETCH subroutine 2BF1


Copies the last value from the calculator stack into the

833
registers AEDCB, and moves 5C65 STKEND down so that the
value is notionally removed from the stack; but in fact it is still
there and can be read by appropriate m/c.
Input parameters: none.
Action: set a pointer on 5C65 STKEND
- step it back five times, copying the byte into the
appropriate register at each step
- put the final pointer address in 5C65 STKEND.
Exit: RET.
Output parameters: A, BC, DE have all been given values
as indicated
- HL holds the new 5C65 STKEND
- if the last value is a pair of string parameters, as
often
- BC holds the length of the string
- DE its start address
- A its array/slice index.
Called from:
0629 SA BLANK
175D OPEN 2
1C30 VAR A 2
2024 PR ITEM 3
2A52 SLICING
2B72 L DELETE$
2BC6 L STRING
34BC usr-$
3559 STRINGS (twice)
359C strs-add (twice)
35DE val
3669 code
3674 len

stk-half, stk-one, stk-pi/2 see 341B stk-zero

STK PNTRS subroutine 35BF


Points HL at the last value on the calculator stack and

834
DE at the calculator stack end address in 5C65 STKEND; their
standard settings for a "unary operation". Called at the start of
the CALCULATE subroutine, but since this is bypassed when
fp-calc-2 is used it is also made the exit routine for some of the
calculation routines.
Input parameters: none.
Action: load HL from 5C65 STKEND
- add minus five; result in HL
- recover the 5C65 STKEND address in DE.
Exit: RET.
Output parameters: no other registers affected.
Called from:
335B CALCULATE
Exit from:
35B7 OTHER STR (359C strs-add)
360C V RPORT C (35DE val)
365F R I STORE (3645 read-in)

STK STORE subroutine 2AB6


See 2AB1 STK ST 0 below.
Called from:
2D2B STACK BC
2DC1 LOG(2>A)
359C strs-add
35C9 CHRS
Exit from:
2AB2 STK STO $

STK STO $ subroutine 2AB2


See 2AB1 STK ST 0 below.
Called from:
219B IN VAR 6 (misprinted IN VARS 6 and STK ST $)
25DB S STRING
2660 S IK$ STK
29A1 SV SIMPLE$ (misprinted STK STORE)
361F str$
365F R I STORE

835
Exit from:
257D S SCR STO
2AB1 STK ST 0

STK ST 0 subroutine 2AB1


The first of three entry points to STK STORE; STK STORE
adds a FP number to the calculator stack, taking its bytes from
AEDCB, the reverse of 2BF1 STK FETCH.
In ROM, these subroutines are only used for string
parameters and small integers. However STK STORE itself
could well be used for a full format FP number. The first two
entry points flag the result as a string, the STK ST 0 entry
point flags the string parameters as belonging to an array or
slice.
Input parameters: BC, DE hold the last four bytes of the
number, in the AEDCB order; if the number is a pair of string
parameters, BC holds the length of the string and DE its start
address
- for the two later entry points, A holds the first
byte; if the number is a pair of string parameters, the array/
slice index, one for simple strings or zero for an array or
slice
Action: zero the first byte; signalling "array or slice".
_2AB2_STK_STO_$ (the entry point for string parameters in
general): zero the string/numeric status flag, bit 6 of FLAGS;
"string result".
_2AB6_STK_STORE (a still more generalized entry point,
used for numeric as well as string values): call 33A9 TEST 5 SP
to check that there is room in memory for an extension of the
stack
- get a pointer to the end of the calculator stack from
5C65 STKEND
- step through 5 bytes loading AEDCB into each in turn
- put the new pointer in 5C65 STKEND.
Exit: RET, from STK STORE.
Output parameters: HL holds the new 5C65 STKEND,
others unchanged.

836
Called from:
2A2C SV ELEM$
Exit from:
2AAD SL STORE

stk-ten see 341B stk-zero

STK TO A subroutine 2314


Gets the one-byte last value off the calculator stack,
rounded down to an integer, as an absolute value but with a
marker of the sign; reports an error if the absolute value is
more than FFh/255d.
Cf: 1E94 FIND INT1 - takes a one-byte number from stack
into A, with error messages if negative or out of range
2D7F INT FETCH - copies a small integer into DE,
without error reports or flags
2DD5 FP TO A - takes a one-byte number from stack
into A, with flags but no error messages.
Input parameters: none.
Action: call 2DD5 FP TO A to get the number; without any
error reports, but carry will be flagged if the absolute value of
the number was more than 255d and NZ flagged if the number
is negative
- if carry is set report "Integer out of range"
- make a sign marker 01 for positive
- return on Z flag
- (negative number) make the sign marker FFh/minus one
for negative.
Exit: RET, provided the number is in range.
Output parameters: A holds the number
- C holds the sign flag
- HL and DE point to the last value on the calculator
stack and the stack end respectively.
Called from:
2307 STK TO BC (twice)
Rems:
171E STR DATA (STK TO A is a misprint for FIND INT1)

837
STK TO BC subroutine 2307 see also FIND INT 2, FP TO BC
Gets the last_two values off the calculator stack,
rounded down to integers, as absolute values but with
markers of their signs; if the absolute value of either integer is
more than FFh/255d, reports an error.
Usually called to get coordinates X,Y, eg for a PLOT
command; Y is assumed to be the last on the stack.
Cf: 1E99 FIND INT2 - gets a 2-byte integer from the
calculator stack with error reports if negative or out of range
2DA2 FP TO BC - gets a 2-byte integer from the
calculator stack with flags but no error reports and the other
routines mentioned under 2314 STK TO A
above.
Input parameters: none
- X and Y on the calculator stack.
Action: call 2314 STK TO A twice, putting the results in
the appropriate registers; see the output parameters below.
Exit: RET, provided both X and Y are in range.
Output parameters: B holds ABS Y, with SGN Y in D
- C holds ABS X, with SGN X in E
- HL still points to the last value on the calculator
stack.
Called from:
1FFC PR ITEM 1
22DC PLOT
24B7 DRAW LINE
2535 S SCRN$ S
2580 S ATTR S

STK VAR subroutine 2996


See under 28B2 LOOK VARS.
Called from:
1C30 VAR A 2 (misprinted STACK VARS)
26C9 S LETTER (misprinted STACK VARS)
2C05 D RPORT C

838
stk-zero subroutine 341B
Takes a constant from the table at 32C5 and puts it on
the calculator stack in normal FP form.
The same subroutine works for any of the five constants
in the table, and it would have been better named "get-const"
or something similar.
Called from 0028 FP CALC with any of the literals
A0: stk-zero
A1: stk-one
A2: stk-half
A3: stk-pi/2
A4: stk-ten
Can be called direct from m/c, though this isn't done by
the ROM; note
a) in calls from the calculator the constant will always
be loaded on to the calculator stack, but in direct calls from
m/c it could be loaded to any address supplied in the DE
register; on output DE will have been moved on five places,
but this new pointer won't have been loaded into 5C65
STKEND as in the case of a call through the calculator
b) the routine as it stands can be used only for the
given table: it can't be used for a home-made table of
constants. A "Chinese copy", with the address of the home-
made table in the fifth line, should work OK; or copy only the
first four lines, then LD HL with your address, then "JP 3422".
Such routines could address up to 256d different constants in
one table; they must all be in "stk-data format", see under stk-
data and the BASIC programs Appendix to this index.
Input parameters: A holds the table serial of the
required constant, starting at zero for the first
- DE holds a destination address, usually the one in
5C65 STKEND
- HL' holds the "return address" from the calculator
routine; the address of the next literal to be read by FP CALC.
Action: save HL' on the machine stack
- put the table address in HL'
- call 33F7 SKIP CONS which steps through the table to

839
the specified serial
- call 33C8 STK CONST which converts the constant to
normal FP format and stacks it.
Exit: RET.
Output parameters: DE holds the new value for 5C65
STKEND
- HL holds the address of the first byte of the constant
on the stack
- HL' has been restored.
Called from:
stk-zero:
1CE6 USE ZERO
235A C ARC GE 1
2CCF DEC RPT C
2D3B INT TO FP
2DA3 FP TO BC
2DF8 PF POSTVE
3449 series-06
3588 STR TEST
370E RESLT ZERO
371C VALID
3783 GET ARGT
37FB SMALL
385D XISO
stk-one:
2497 DRAW SAVE
25F8 S RND (twice)
2CD5 DEC STO 1
36B7 X NEG
36C4 EXP
371C VALID
37A1 ZPLUS
37AA cos
37B7 C ENT
37E2 atn
37FA CASES
3833 asn (twice)

840
385D XISO
386A ONE
stk-half:
238D DR 3 PRMS
23C1 DR PRMS
2497 DRAW SAVE
373D GRE.8 (3 times)
3783 GET ARGT
384A sqr
stk-pi/2:
233B C R GRE 1
2627 S PI
37E2 atn
3843 acs
stk-ten:
2CDA NXT DGT 1
2D40 NXT DGT 2
2D55 E SAVE
Rems:
33C6 stk-data gets literals from constant table

STK ZEROS 33F1 (33C6 STK DATA)


Jumps from:
auto

st-mem subroutine 342D see also 340F get-mem


Called from 0028 FP CALC with literals C0 to C5,
depending on the memory location to be filled; puts a FP
number in a calculator memory location. Mem-0 is filled by st-
mem-0, literal C0, and so on to C5 st-mem-5 which fills
mem-5.
Can be called direct from m/c, though this isn't done in
the ROM:
- in ROM calls the number put in memory is always
fetched from the calculator stack; not necessarily in direct
calls

841
- literals C6 to DF could be used, even through FP CALC,
to address an additional twenty-six memory locations; but the
memory must be relocated before doing this, by putting a new
address in 5C68 MEM, or 56B2 RAMTOP etc will be
overwritten. If the subroutine is called direct with the location
number in A, up to 33h/51d memory locations can be used -
to attempt to use more would confuse 3406 LOC MEM.
Input parameters: A holds the memory location number
- HL holds the address of the first byte of the number
to be remembered; the last value on the calculator stack in all
ROM uses, but not necessarily.
Action (the coding is line for line the same as 340F get-
mem-0 except for some EX DE,HLs which reverse the
direction of copying): get a memory pointer from 5C68 MEM
- call 3406 LOC MEM to find the first byte of the
selected memory location
- call 33C0 MOVE FP to copy the FP number from its
present address to memory.
Exit: RET.
Output parameters: HL unchanged
- DE now points to the stack end, or the first byte
after the number just copied.
Called from:
st-mem-0:
03F8 BEEP
0427 BE OCTAVE
1D16 F REORDER
1DAB NEXT (nb memory relocated)
235A C ARC GE1 (twice)
23A3 DR SIN NZ
23C1 DR PRMS (twice)
2439 ARC START (twice)
2497 DRAW SAVE
2CD5 DEC STO 1
2CDA NXT DGT 1
3449 series-06
36A0 n-mod-m (twice)

842
36B7 X NEG
3783 get-argt
st-mem-1:
235A C ARC GE1
23A3 DR SIN NZ
23C1 DR PRMS (3 times)
2425 ARC LOOP
2497 DRAW SAVE
2D60 E LOOP
2E24 PF SMALL
3453 G LOOP
st-mem-2:
235A C ARC GE1
23C1 DR PRMS (3 times)
2425 ARC LOOP
2E01 PF LOOP (twice)
3449 series-06
3453 G LOOP
st-mem-3
2497 DRAW SAVE
2DF8 PF POSTVE
36C4 EXP
st-mem-4:
2497 DRAW SAVE
2DF8 PF POSTVE
st-mem-5:
233B C R GRE 1
238D DR 3 PRMS
23C1 DR PRMS (twice)
2DF8 PF POSTVE
Rems:
335B CALCULATE example of manipulatory operation

STMT LOOP 1B28 (1B8A LINE RUN) see also BASIC


INTERPRETER
Jumps from:
1B29 STMT L 1

843
1BD1 NEXT LINE
1BF4 STMT NEXT

STMT L 1 1B29 (1B8A LINE RUN)


Jumps from:
1B17 LINE SCAN
1D00 IF 1

STMT NEXT 1BF4 (1B8A LINE RUN)


Jumps from:
1B7D STMT R 1
1BD1 NEXT LINE

STMT RET 1B76 (1B8A LINE RUN)


Jumps from:
1EDC CLEAR 2
Indirect jumps from:
1C4E CLASS 02
1D7C F FOUND
1E1E READ 2
1E73 GO TO 2
1E7A OUT
1E80 POKE
1F4F PAUSE END
Rems:
1B29 STMT L 1 made return address for statement loop
1BB2 REM address dropped
1BEE CHECK END address dropped
1CF0 IF address dropped
1EED GO SUB return address saved
1F23 RETURN return address saved

STMT R 1 1B7D (1B8A LINE RUN)


Jumps from:
1B76 STMT RET

STOP key (E2) see also commands, functions and operators,

844
KEYBOARD SCANNING, 026A symbol code key table (e)
The A key in C, K or L mode with symbol shift produces
the command STOP, which accepts no parameters.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1A8A P STOP causes a jump via 1C10 CLASS 00 and 1C16 JUMP
C R to the executive routine 1CEE REPORT 9 (STOP).
0C88 PO SCR 2 makes break after "scroll?"
133C MAIN 5 increments SUBPPC in case of CONTINUE
21B9 IN ASSIGN aborts INPUT routine
21D0 IN STOP error report only in run-time

S TOP system variable 5C6C see also 1795 AUTO LIST


Bytes: 2
The line number of the BASIC line which is to appear at
the top of the screen when a listing is displayed.
Written by:
17CE AUTO L 1
17E1 AUTO L 2
1835 LIST ALL (using 190F LN FETCH and 191C LN STORE)
Read by:
1795 AUTO LIST
17E4 AUTO L 3
1835 LIST ALL

STOP, STOP COMMAND ROUTINE see 1CEE REPORT 9

'STOP in INPUT' message see 21D4 REPORT H

'STOP statement' message see 140C REPORT 9

STORE IN MEMORY AREA SUBROUTINE see 342D st-mem

STR ALTER subroutine 2070 see also channels and streams

845
Either LIST, LLIST, PRINT or LPRINT may be followed by
the (hatch) symbol and a number: the effect is to LIST or
PRINT to the stream indicated.
For some reason this option isn't mentioned in any
edition of the Handbooks. Its only useful application on the
unextended Spectrum is in commands such as
PRINT (hatch)0 [or (hatch)1]; "hello": PAUSE 0
which prints the message in the input area. If the PAUSE is
omitted, the message prints but is immediately obliterated by
the "OK" message.
This subroutine changes the stream if it finds (hatch).
Input parameters: A holds the character immediately
following the BASIC command.
Action: return with carry unless A is 23h (hatch)
- move on the BASIC pointer
- call 1CB2 EXPT 1NUM to read the number following
(hatch) on to the calculator stack
- call 1E94 FIND INT1 to put it in a register
- if it is more than 0Fh/15d report "Invalid stream"; 0F
misprinted FF in the notes
- call 1601 CHAN OPEN to open the stream.
[But CHAN OPEN won't open any stream unless it has
already a channel allotted to it; and the only streams with
channels in the unextended Spectrum are zero -> 3; and
although you can open others, for example with the
command
OPEN (hatch)4,"s"
you cannot use any other channels than the upper or lower
screens and the ZX printer channel. Not much of this amounts
to anything useful in practice.]
- clear the carry flag.
Exit: RET.
Output parameters: none
- the carry flag is set if there has been no change,
cleared if the stream has changed.
Called from:
17FB LIST 1

846
2024 PR ITEM 3

STR DATA subroutine 171E see also channels and streams


Converts a stream number into the corresponding stream
data, which is a 2-byte offset for finding the channel data.
Input parameters: none
- the stream number N is on the calculator stack.
Action: call 1E94 FIND INT1, misprinted STK TO A, to get
N; this excludes the minus numbers
- if N is more than 0Fh/15d report "Invalid stream".
_1727_STR_DATA_1: add 3 to N
- rotate it left to double it
- add it to 5C10 STRMS; each item of stream data,
starting with the data for stream -3, takes up 2 bytes, so this
now addresses the N'th item of stream data [two bytes could
have been saved here by omitting ADD A,03 and writing LD
HL,5C16]
- read the data.
Exit: RET, from STR DATA 1.
Output parameters: BC holds the stream data
- HL holds the stream address.
Called from:
16E5 CLOSE
1736 OPEN

STR DATA 1 1727


Jumps from:
171E STR DATA

stream code, stream data, stream information, stream


numbers, streams see channels and streams

str-gr-eql, str-grtr see 353B no-l-eql

STRING AND NUMBER OPERATION see 352D str-&-no

string argument, string arrays see arguments, arrays,

847
strings

string comparisons see 353B no-l-eql, strings

STRING CONCATENATION OPERATION see 359C strs-add

string entry see 2089 INPUT

string expression see expressions

string flag see strings

string function (DEF FN)


One whose result is a string; as opposed to a numeric
function. Its arguments aren't necessarily strings, eg
DEF FN c$(X,Y) = CHR$ X + CHR$ Y
is a string function with numeric arguments.
27E9 SF FLAG 6 bit 6 of FLAGS cleared for
27F7 SF RUN zero flag for

string length, string parameters see strings

string quotes see " (22) after end of alphabet

string result see expressions

STRINGS 3559 (353B no-l-eql etc)


Jumps from:
354E NU OR STR

strings see also expressions, string functions, variables


The original of a string will be in the editing area for
a direct command
PRINT "hello"
in the program area if the command is a BASIC line
10 PRINT "hello"
or in the variables area if it is made the value of a string

848
variable
LET h$="hello"
In the last case the effect of 2BAF L ADD$, 2BC0 L NEW$ and
following is
- to create a new string variable h$ in the variables
area, deleting any string variable of the same name which may
already be there
- to copy the characters of the string, and 2 bytes
giving its length, to the variables area as the value of the
variable.
Sometimes the string is copied to the work space in
executing a command; this is done when it contains
embedded quotes and in executing a concatenation function,
but not otherwise. Strings in the editing area or work space
aren't preserved for long: for example, only until a PRINT
command has been executed by 203C PR STRING, which
takes the string parameters off the stack and thus finds the
copy of the string.
When the command has been executed, execution returns to
the statement loop, and any copy in the editing area or the
work space is deleted in 1B29 STMT 1.
In scanning BASIC lines or commands the quote sign 22h
“ signals that what follows is to be evaluated as a string.
Whenever 24FB SCANNING encounters this code, what
follows the quotation marks up to the closing quotation marks
is treated as a string (25B3 S QUOTE), and
its_string_parameters are put on the calculator stack (25DB S
STRING, calling 2A82 STK STO $): a 5-byte code giving the
location, length and type of the string.
Also bit 6 of FLAGS is zeroed to show that the last value on the
calculator stack is a pair of string parameters, not a FP
number (25DB S STRING).
In the case of variables, whenever an expression
containing the string variable is evaluated (26C9 S LETTER)
the variable is found in the variables area by a call to 28B2
LOOK VARS, and its string parameters stacked by a call to
2996 STK VAR. The copy stored in the variables area doesn't

849
have quotation marks at start or end, and any embedded
quotes are single, not double.
No string parameters are made or stored for any of these
strings until the expression is scanned for execution of a
BASIC command. String parameters are never stored in the
variables area, and the only time they are put into the BASIC
line is in the special case of a DEF FN statement:_each of the
arguments on the left side of the statement, whether string or
numeric, has a 6-byte "invisible space" after it with a 0Eh
number marker in the first byte. These are filled with the
current "value" of the argument whenever the function is
used, and in the case of string variables the "value" is the pair
of string parameters.
String variables may be either simple strings or the
elements of string arrays. Simple string variables don't have a
fixed length; if the command
LET h$="hello"
is followed by
LET h$="hooray"
the length of the variable h$ is automatically increased from 5
to 6. String array elements do have a fixed length, set by the
last dimension of the DIM command by which the array was
defined, and unalterable except by another DIM command
which will delete all the former string elements.
Either simple string variables or string array elements
may be_sliced; expressions such as
a$(X TO Y)
a$(V,W,X TO Y)
a$(V,W, TO Y)
are all sliced strings. Either X or Y may be omitted, but TO
may not. Another form of slice is the_single-character_slice:
a$(X) when a$() has only one dimension,
a$(X,Y) when it has only two, etc
In a$(X TO Y) or a$(X), a$ may be a simple string instead of a
one-dimensioned array.
Slicing may also be specified by expressions such as
a$(X,Y)(P TO Q) or

850
a$(X,Y)(P TO Q)(R TO S) or even
"hello"(P TO Q)
In both the first two a$() must have_three dimensions.
The handbooks don't mention these variants.
The effect of slicing is that when the expression is
read by the 24FB SCANNING loop, the string parameters of
the whole string are first stacked, then replaced by the
parameters of the slice selected (2A52 SLICING). Slices may be
copied_from as in
PRINT a$(X TO Y)
LET b$=a$(X TO Y)
or copied_to as in
LET b$(X TO Y)=a$
In the latter case there is no need to reform the string
variable or array, the new letters are just copied into the old
variable.
Some functions require strings as arguments, and some
have strings as their result: the operation codes carry flags to
signal this (26DF S NEGATE):
- bit 7 of the code is cleared if the_result is a string
- bit 6 of the code is cleared if the_argument is a string.
Operators (2773 S TIGHTER and following) are more
complex. Some only accept numeric operands and have
numeric results. Others accept string operands, and in these
cases the operation code is incremented by 08 to give the
corresponding string operation; + changed to strs-add; no-&-
no changed to str-&-no; etc. Of these, only AND refuses
a_second string operand, and only + operates on strings to
produce a string; the other "comparison operations" all
operate on two strings to produce a
number.
_Array_strings: the elements of string array variables.
_Character_string: any sequence of character codes, not
necessarily a "string" as discussed here.
_Complete_simple_strings: not sliced and not array
elements.
_Length_of_string or_of_string_expression: the number of

851
characters in it, not counting opening and closing quotes and
counting any doubled quotes within the string as only one
character. It is recorded as bytes 4 and 5 in the string
parameters.
_Null_string: a string of zero length, as in LET s$="".
Its length parameter is zero, its start parameter is
disregarded. You cannot make an array of null strings.
_Parameters_of_string: see string parameters below.
_Simple_strings: variables not elements of an array.
_Single_strings: sometimes used for "simple strings".
_Start_of_string: the address of the first code of the
string in the variables area, work space etc. If it is in the
variables area, the start is the first of the string codes,
after the variable name and length bytes. Kept in bytes 2-3 of
the string parameters.
_String_expression: any combination of functions,
operators, variables, etc, which has a string value.
_String_parameters: there are 5 bytes.
Byte 1: one for a complete simple string, zero for a
slice_or array element; one indicates that the length isn't
fixed, and therefore when the string is rewritten the old copy
must be deleted, not just overwritten with new codes
Bytes 2-3: the start address of the copy in the
variables area, work space etc
Bytes 4-5: the number of characters in the string, not
counting opening and closing quotes and counting any
doubled quotes within the string as only one character.
_String_status: as opposed to numeric status, the string
nature of an expression, or of the last entry on the calculator
stack. The status of the last value is signalled by bit 6 of
FLAGS, zero for string parameters.
Introduction strings well managed
0605 SAVE ETC filename string parameters put on stack
0629 SA BLANK and fetched back
0672 SA V OLD simple strings can be SAVEd but not
LOADed
175D OPEN 2 gets string parameters for channel code

852
1767 OPEN 3 gets length of channel code
178B OPEN END length must be one
1C30 VAR A 2 call STK VAR to get parameters
1C46 VAR A 3 vble letter_or length in STRLEN
1C8C EXPT EXP evaluate string expression
1F6A DEF FN 1 clear FLAGS 6 for string variable
1FA6 DEF FN 6 check FN variable with DEF FN for status
2024 PR ITEM 3 fetch string parameters for printout
203C PR STRING loop to print characters of string
24FB SCANNING explains start and length parameters
2573 S SCR NXT value of SCREEN$ (X,Y) may be null
257D S SCR STO stack parameters of SCREEN$ (X,Y)
25B3 S QUOTE string parameters after quotation marks
25BE S Q AGAIN open work space for string
25CB S Q COPY copy string to work space
25D9 S Q PRMS get length not counting double quotes
25DB S STRING stack string parameters of expression
2634 S INKEY$ handle 0 or 1 length string from INKEY$
2660 S IK$ STK stack parameters of INKEY$
26C9 S LETTER stack parameters of string value of vble
26DF S NEGATE flags of operation codes explained
2713 S CONT 3 check if slicing required after expression
already read, eg a$(2,3)(4 TO 5)
2734 S LOOP usr-no and usr-str distinguished by FLAGS 6
275B SYNTEST check status flag (in syntax checking)
2764 S RUNTEST make status flag (in run-time)
2773 S TIGHTER change op code for string operands
2788 S NOT AND exceptional ops with string result etc
27F7 SF RUN status flag for FN expressions
2808 SF FND DF save status flag
2814 SF CP DEF status of FN and DEF FN must match
2825 SF NOT FD save status flag
2831 SF VALUES string parameters inserted after argu-
ments on left side of DEF FN statement
2852 SF ARG VL parameters of string DEF FN arguments
put on stack
28B2 LOOK VARS string vbles in BASIC marked by "$"

853
28DE V STR VAR clears FLAGS 6 for string vble
294B V END signals for string vbles/arrays
2951 STK F ARG stacks parameters of string DEF FN vble
295A SFA LOOP special status flag for matching DEF FN
vbles
296B SFA CP VR skip string parameters
2981 SFA MATCH stack string parameters without STK
VAR
2996 STK VAR determine and stack params of string vble
29A1 SV SIMPLE$ stacks params for simple string
29AE SV ARRAYS find start of string in array
29C0 SV PTR save variable pointer
29C3 SV COMMA dimension counter decremented for
strings
29D8 SV CLOSE last subscript is slice
29E0 SV CH ADD evaluate slice
29EA SV LOOP detects slice with TO
29FB SV MULT separate string arrays from numeric
2A12 SV RPT C jump ahead for string array
2A2C SV ELEM$ params for array element found and
stacked
2A45 SV SLICE parameters changed for slice
2A49 SV SLICE? check for slice and clear FLAGS 6
2A52 SLICING executes slicing
2A81 SL SECOND length of slice saved
2A94 SL DEFINE string parameters of slice stacked
2AA8 SL OVER clear FLAGS 6 for parameters of slice
2AB1 STK ST 0 entry point for slice/element parameters
2AB2 STK STO $ entry point for simple strings
2AB6 STK STORE puts string params on stack from
ADECB
2ACD INT EXP2 check value against string length
2AFF LET string vble start is address of 1st of string
2B72 L DELETE$ rewriting old vble: simple strings, add
new then delete old; slices/elements,
copy in work space first, then to
variables area

854
2B9B L LENGTH copy new string to work space
2BA3 L IN W/S recover length and start
2BA6 L ENTER copy from work space to vbles area
2BAF L ADD$ copy new to vbles area and delete old
2BC0 L NEW prepare vble for new string
2BC6 L STRING make space in vbles area and copy length
2BF1 STK FETCH gets string params from stack into
AEDCB
2DE3 PRINT FP turns decimal number into string
2E24 PF SMALL mistake; gives wrong answers in some
string comparisons
352D str-&-no make null string if X zero
354E NU OR STR sorts numeric and string comparisons
3559 STRINGS get parameters of two strings
3564 BYTE COMP jump for null second string
356B SECND LOW second less than first
3575 SEC PLUS compare byte by byte
3588 STR TEST zero put on stack for string comparisons
359C strs-add two strings copied to work space and
parameters of concatenation stacked
35B7 OTHER STR second string copied to work space
35C9 chr$ one-char string made in work space and
params
stacked
35DE val evaluate string as numeric or string expression
360C V RPRT C string scanned as expression
361F str$ evaluate expression as string
3645 read-in one-char string made in work space and
365F R I STORE params stacked
3669 code uses string parameters
3674 len value of function is in string parameters

string status of DEF FN & DEF FN arguments see strings

string-valued function see string function

string value (on calculator stack) see strings

855
string variable see string, variables

STRLEN system variable 5C72 see also 1C46 VAR A 3 in the


long entry on 18B2 LOOK VARS
Bytes: 2
STRLEN and 5C4D DEST hold data for identifying a
"variable in assignment", eg the X in FOR X=Y TO Z or the X$
in LET X$="hello". They are loaded in 1C46 VAR A 3, part of
the exit routines from
- 1C1F CLASS 01, after the variable has been identified
for a LET command
- 1C6C CLASS 04, after the looping variable for a FOR
command has been constructed.
STRLEN may hold either
- for string variables which already have a value,
including array elements and slices: the length parameter of
the variable, with DEST holding the start parameter - "string
parameter"
- for numeric variables which already have a value: the
letter of the variable in its lo byte, as it appears in the
variables area, with DEST a pointer just before the FP form of
the numeric value in the variables area - "variables identifier"
- for new variables, string or numeric, which don't yet
have a value: the letter of the variable in its lo byte, with
bits 5 and 6 flagging the type of variable as it will be put in
the variables area but with bit 7 zero, with DEST as a pointer
to the first letter of the variable name in the BASIC line "BASIC
identifier".
The "string parameter" value is only used for simple
strings; it is read in 2B72 L DELETE$ and immediately checked
for zero, then held over all the way to the call to 19E8
RECLAIM 2 in 2BAF L ADD$, where after being incremented
to cover the letter and length bytes of the variable it measures
the deletion of the old version of the variable.
The "BASIC and variables identifiers" are only used in
the case of a looping variable in FOR ... NEXT loops, to find

856
the first NEXT command with a matching variable letter. The
identifiers are never used at all for ordinary numeric or string
variables.
Written by:
1C46 VAR A 3
Read by:
1D34 F L&S
2B72 L DELETE$
Rems:
1C22 VAR A 1 subroutine gets values for DEST and
STRLEN

str-l-eql, str-less see 353B no-l-eql

STRMS system variable 5C10 see channels and streams


Bytes: 38

strs-add subroutine 359C


Called only from 0028 FP CALC with literal 17; executes
the "+" operation when the arguments are strings, ie string
concatenation. For example: LET C$ = A$ + B$.
Could be called direct; this isn't done in the ROM.
Input parameters: none
- the string parameters of A$ and B$ are on the stack,
B$ as last value; they must be on the stack even for a direct
call.
Action: call 2BF1 STK FETCH to get the string parameters
of B$
- save the start and length
- call 2BF1 STK FETCH to get the string parameters of
A$; the parameters of A$ and B$ have now been cleared from
the calculator stack
- save the start and length
- add the lengths
- call 0030 BC SPACES to make a space of this length in
the work space
- call 2AB2 STK STORE to stack the string parameters of

857
C$; the start is the start of the work space, the length the sum
of the lengths of A$ and B$
- recover the parameters of A$
- if its length is zero jump on to OTHER STR
- copy its codes into the start of the work space.
_35B7_OTHER_STR: recover the parameters of B$
- if its length is zero exit
- copy its codes into the work space following the codes
of A$.
Exit: into 35BF STK PNTRS to restore the stack pointers
to positions suitable for unary operations.
Output parameters: none
- the last value is now the string parameters of C$.
Rems:
2788 S NOT AND leave flag cleared, string result

strs-eql, strs-neql see 353B no-l-eql

STR TEST 3588 (353B no-l-eql)


Jumps from:
356B SECND LOW
3572 BOTH NULL

STRT MULT 3125 (30CA multiply)


Jumps from:
30F0 MULT LONG

STR$ key (C1) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The Y key in E mode without shift produces the function
STR$; it requires one operand, a numeric expression, which
must be in brackets unless it is just a number or a variable,
and the value of the function is a character string showing
what would be printed on screen if that expression were
evaluated. STR$ (x-x) prints the_character "zero", not the_digit
zero, if x has been given a value, otherwise gets an error
message.

858
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code C1 first to 12, then to EE,
then, because it has a string result, to 6E, and adds the
priority 10h/16d. Code and priority 106E are now pushed on
to the machine stack (270D S PUSH PO) while the expression
following STR$ is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from 6E to 2E, the calculator
offset for 361F str$.
2707 S NO TO $ only STR$ and CHR$ give strings from nos

str$ subroutine 361F


Called from 0028 FP CALC with literal 2E; the executive
routine of the STR$ function. Outputs the evaluation Y of an
expression, EXP say, into the work space, then gives this
evaluation parameters as a string and puts them on the stack.
Eg if EXP is 7*9 then X is 63; the string in the work
space is "63".
Not otherwise called from ROM; could be called from m/
c, but it is difficult to imagine what useful purpose this might
serve.
[A mistake in ROM at 2E24 PF SMALL; an unwanted zero
is left on the calculator stack below the result of STR$ X if X
has an absolute value less than one. This will be taken as the
value of any function or operator of which STR$ X is an
argument or second operand - since it is a string, this only
applies to CODE, FN, LEN, USR, VAL, VAL$, "+", and the
comparison operations - and means that all the values on the
stack below the unwanted zero will also be read one out of
order in the evaluation of more complex expressions.
For example, the BASIC command
PRINT "Result = " + STR$ X
will fail if X is less than one; if X = 7, it will print correctly
Result = 7
but if X = 1/7 it will print only
0.14285714

859
because the unwanted zero is used as the first argument of
the concatenation operation instead of the parameters of
"Result =".
The command
PRINT "Result = "; STR$ X
should be used instead.]
Input parameters: none
- the result Y of evaluating the expression EXP is last
value on the calculator stack.
Action: call 0030 BC SPACES to make a single space in the
work space
- put its address in 5C5B K CUR; channel R uses K CUR as
its destination pointer
- call 1601 CHAN OPEN with stream number minus one;
open channel R to output to the work space
- call 2DE3 PRINT FP to convert the last value Y to the
BASIC decimal 14-character format and output it to the work
space; 0F81 ADD CHAR, the output routine of channel R,
makes room for the characters one by one before 5C5B K CUR
and copies them in
- call 1615 CHAN FLAG with the old channel address; this
is a short cut to restoration of the old channel
- take away the start address in the work space from the
final 5C5B K CUR value; K CUR is just past the end of the
expression, so this gives the string length
- call 2AB2 STK STO $ with the start and length; it puts
the string parameters on the calculator stack.
Exit: RET.
Output parameters: HL and DE point to the last value and
the stack end respectively
- the string parameters of the string version of Y are
on the stack.
2DE3 PRINT FP called by str$
2DF8 PF POSTVE must save alternate HL' for str$
2E24 PF SMALL mistake in ROM

str-&-no subroutine 352D

860
Called only from 0028 FP CALC with literal 10 as the
executive routine of the AND function when the first operand
is a string, X$ AND Y; the literal 08 no-&-no returned by 2723 S
OPERTR for AND is changed to 10 str-&-no when the string
operand X$ is detected in 2773 S TIGHTER. Outputs a string
equivalent to its first operand X$ unless the second operand Y
is zero, when it outputs the null string.
Can be called from m/c, given the correct input
parameters.
Input parameters: HL and DE point to the string
parameters of X$ and the FP number Y; for direct calls they
could be anywhere in memory, but for calls through the
calculator they are always on the stack, with Y as last value.
Action: call 34E9 TEST ZERO; it doesn't take Y off the
stack
- if Y isn't zero return at once; X$ is the correct
result
- (Y is zero) step back through the last two (length)
bytes of the string parameters putting in zeroes; the start
parameter is left unchanged, it is ignored in the case of a null
string.
Exit: RET.
Output parameters: A may be corrupted, others
unchanged
- if the subroutine was called through the calculator
3365 RE ENTRY in effect deletes Y from the stack, so X$ is
now last value.

stylus (printer)
"Wait for the stylus" at 0F0C COPY L 2 is just a fancy
way of saying "wait for the printer". The ZX printer cannot
receive data while it is operating its stylus to make the dots
for the line stored in its print buffer, and it sends zeroes to
port FB until it is ready for more data.

sub-expression see expressions

861
SUBN ONLY 31F2 (31AF division)
Jumps from:
31DB DIV 34TH

SUBPPC system variable 5C47 see also BASIC line


Bytes: 1
The statement number of the statement currently being
executed in the BASIC line:
- set to zero at the beginning of the syntax checking
scan of each line (1B17 LINE SCAN), and incremented at the
beginning of each statement scan (1B29 STMT L 1).
- given a value in 1BD1 NEXT LINE, which will be zero if
a line has just been completed, but may not be when coming
from 1B9E LINE NEW after a jump.
- copied into a FOR ... NEXT control variable as the
looping statement, and also read for copying into 5C44
NSPCC, in 1D34 F L&S.
- saved as a single byte on the machine stack, with the
two bytes of the line number, by a GO SUB command; see
1EED GO SUB for this tricky manoeuvre. This is POPped in
1F23 RETURN, and the statement number put in 5C44 NSPCC
in 1E73 GO TO 2.
- printed out as part of any report message by 133C MAIN
5; and if the report comes from a STOP or a BREAK, it is
incremented so that CONTINUE will start from the next
statement.
Usually it is the value taken to copy into 5C70 OSPCC when
three bytes are put in it and 5C6E OLDPPC at 1384 MAIN 8.
Written by:
1373 MAIN 6
1B17 LINE SCAN
1B29 STMT L 1
1BD1 NEXT LINE
Read by:
133C MAIN 5
1376 MAIN 7
1D34 F L&S (twice)

862
1EED GO SUB

subscripts see arrays

subtract subroutine 300F


Called from 0028 FP CALC with literal 03; the executive
routine of the "-" operator, but also used in many other
calculations, in one case directly without using FP CALC.
Subtracts the last value Y, the "subtrahend", on the calculator
stack from the second last value X, the "minuend"; see under
addend for these tiresome expressions.
Can be called from m/c directly or through FP CALC.
Input parameters: HL and DE point to X and Y; in all the
ROM calls, even the direct one, they are the second last and
last values on the calculator stack, but this isn't necessary for
direct calls. However the bytes of X and Y must be stored
consecutively in memory, with Y following X, since this is how
they are read in 3014 addition.
Action: just call 346E NEGATE to change the sign of Y;
the exit routine will add -Y to X.
Exit: into 3014 addition.
Output parameters: HL and DE unchanged.
Called by literal 03 from:
03F8 BEEP
0427 BE OCTAVE
1DE2 NEXT 1
235A C ARC GE1
23C1 DR PRMS (twice)
2425 ARC LOOP
2439 ARC START (twice)
245F ARC END (twice)
2497 DRAW SAVE
25F8 S RND
2E01 PF LOOP
2E24 PF SMALL
3453 G LOOP (twice)
36A0 n-mod-m

863
36B7 X NEG (twice)
36C4 EXP (twice)
371C VALID (3 times)
373D GRE.8 (3 times)
3783 get-argt (twice)
37A1 ZPLUS
37AA cos
37B7 C ENT
37FA CASES
3833 asn
3843 acs
Called as SUBTRACT by:
354E NU OR STR
Rems:
2F9B PREP ADD exponent and mantissa of X and Y
prepared

subtrahend see addend

S U PLUS subroutine 25AF


The "unary plus" routine, entered from 24FB SCANNING
via the scanning function table at 2596 when a "+" is
encountered as the first code of a new expression, eg
LET X = +17
As "+" only means "not minus", the routine merely moves
on the BASIC pointer and reenters the scanning loop at 24FF S
LOOP 1 to read the expression.

suppress leading space see messages

sv in this index = system variable

SV ARRAYS 29AE (28B2 LOOK VARS)


Jumps from:
2996 STK VAR

SV CH ADD 29E0 (28B2 LOOK VARS)

864
Jumps from:
29EA SV LOOP

SV CLOSE 29D8 (28B2 LOOK VARS)


Jumps from:
29C3 SV COMMA

SV COMMA 29C3 (28B2 LOOK VARS)


Jumps from:
29FB SV MULT

SV COUNT 29E7 (28B2 LOOK VARS)


Jumps from:
2996 STK VAR
29C0 SV PTR
Rems:
2AC3 SV COMMA entry point to loop evaluating element

SV DIM 2A48 (28B2 LOOK VARS)


Jumps from:
29D8 SV CLOSE
29EA SV LOOP
2A2C SV ELEM$

SV ELEM$ 2A2C (28B2 LOOK VARS)


Jumps from:
2A12 SV RPT C

SV LOOP 29EA (28B2 LOOK VARS)


Jumps from:
29C3 SV COMMA

SV MULT 29FB (28B2 LOOK VARS)


Jumps from:
29EA SV LOOP

SV NUMBER 2A22 (28B2 LOOK VARS)

865
Jumps from:
2A12 SV RPT C

SV PTR 29C0 (28B2 LOOK VARS)


Jumps from:
29AE SV ARRAYS

SV RPT C 2A12 (28B2 LOOK VARS)


Jumps from:
29C3 SV COMMA
29D8 SV CLOSE

SV SIMPLE$ 29A1 (28B2 LOOK VARS)


Jumps from:
29AE SV ARRAYS

SV SLICE 2A45 (28B2 LOOK VARS)


Jumps from:
29E0 SV CH ADD
2A49 SV SLICE?

SV SLICE? 2A49 (28B2 LOOK VARS)


Exit from:
29A1 SV SIMPLE$
2A48 SV DIM

SWAP BYTE 343E (343C exchange)


Exit from:
343C exchange
Jumps from:
auto

symbol code see shift keys


The editing keys table at 0FA0 ends with two slightly
mysterious items, one which jumps to 1076 ED SYMBOL if the
table is entered with a code 0E, and another which jumps to
107C ED GRAPH for a code 0F. In fact no key or combination

866
of keys can produce these codes at this point; although 0E can
be returned as a final code by 0333 K DECODE, the channel K
input routine 10A8 K INPUT merely uses this to set E mode,
and returns without carry signalling "no new key value
received".
So in the unexpanded Spectrum the routines are never
entered at all; but they might be input from other channel
inputs such as a DATA stream from Microdrive. The effect of
code 0E is (1076 ED SYMBOL) to insert a number marker 0E in
the editing line, but only if the INPUT ... LINE flag is set; and
of code 0F to insert the GRAPHICS code 0F unconditionally.

SYMBOL SHIFT key see KEYBOARD SCANNING, shift keys

sync pulse see timing

syntax check, syntax errors see errors

syntax/run flag see 5C3B FLAGS bit 7

SYNTAX Z subroutine 2530


Checks the syntax checking/run time flag.
BASIC lines are scanned by 1B8A LINE RUN for execution
and by 1B17 LINE SCAN for syntax checking; these two
scanning loops have most of their routines in common.
Expressions are scanned by 24FB SCANNING in both cases.
The action required is often different for checking and for
execution; FLAGS bit 7 signals the present mode.
The Z80 instruction BIT 7,(FLAGS), or strictly speaking,
BIT 7,(IY+1) is only four bytes FD CB 01 7E, the 01 being the
index; but CALL 2530h is only three bytes. As the flag is
referred to thirty-two times in ROM, it was worth making this
tiny subroutine out of it.
Output parameters: Z for syntax checking, NZ for
execution.
Called from:
0605 SAVE ETC

867
0672 SA V OLD
17FB LIST 1
1BB3 LINE END
1BEE CHECK END
1C30 VAR A 2
1CBE CLASS 09
1CE6 USE ZERO
1CF0 IF
1DED READ
1E27 DATA
1F60 DEF FN
1FC3 UNSTACK Z
1FCF PRINT 1
204E PR POSN 1
2089 INPUT
20FA IN PROMPT
21D0 IN STOP
25BE S Q AGAIN
25F8 S RND
2627 S PI
268D S BIN
274C S STK LIST
27BD S FN SBRN
28E3 V TEST FN
28EF V RUN/SYN
2A52 SLICING
2AAD SL STORE
2ACD INT EXP2
2AF4 GET HL*DE
2C05 D RPORT C
Exit from:
2522 S 2 COORD
system pointers see 1664 POINTERS

system reset see also 11B7 NEW


The system is reset by a jump to 0000, equivalent to

868
pressing the reset button on the later models, not fitted on the
old Spectrum, or switching the computer off and on again. All
memory of existing programs and sv settings is wiped out.
The same effect can be achieved by PRINT [or RANDOMIZE]
USR 0 from BASIC.
The subroutine 0066 RESET achieves the same effect,
due to an unfortunate mistake in ROM. See its index entry.

system variables
There are full lists of these in Chapter 25 of the
original Handbook, Part 25 of the Plus 2 handbook; the
addresses are only given in decimals but in this index they are
all given in hex.
Some notes:
- CAPS LOCK is set by setting bit 3 of 5C6A FLAGS2: POKE
23658,8 will set it and POKE 23658,0 will clear it
- scrolling can be inhibited by repeatedly POKing 5C8C
SCR CT with any value greater than one
- the normal value of 5C36 CHARS is 3C00h, zero in
23606d and 60d in 23607d
- IY must be set at 5C3Ah/23610 ERR NR for all ROM
operations except a few very simple ones, including of course
calls to ROM subroutines from m/c; all the svs can be
addressed by IY indexes
- the fourteen system pointers, see 1664 POINTERS, are
those from 5C4B VARS to 5C65 STKEND inclusive
- safe locations for user flags and system variables,
apart from the eleven "spare flags", see Introduction to this
index, are as follows.
Never disturbed (well, hardly ever):
- 5CB0h/23728d/IY+118d NMIADD (two bytes)
- 5C7Ah/23672d/IY+62d, the hi byte of FRAMES; if you
zero all three bytes, the hi byte won't be disturbed for about
20 minutes!
Never disturbed, and read only to print UDGs:
- 537Bh/23675d/IY+65d UDG (two bytes)
Disturbed only if channel 3 (ZX printer) is used:

869
- 5C75h/23679d/IY+69d P POSN (two bytes)
Disturbed only by the RANDOMIZE command and RND
function:
- 5C76h/23670d/IY+60d SEED (two bytes)
Disturbed only by plotting or drawing on the screen:
- 5C7Dh/23677d/IY+67d COORDS (two bytes)
Disturbed only by automatic listings:
- 5C3Fh/23615d/IY+5d LIST SP (two bytes) and
- 5C6Ch/23660d/IY+50d S TOP (two bytes)
Safe if the interrupt is disabled and the keyboard isn't
used, but FFh should be poked into 5C00h and 5C04h and
suitable values should be poked back into REPDEL, REPPER,
RASP/ PIP before reenabling it:
- 5C00h/23552d/IY-58d KSTATE (8 bytes)
- 5C08h/23560d/IY-50d LAST K (one byte)
- 5C09h/23561d/IY-49d REPDEL (one byte)
- 5C0Ah/23562d/IY-48d REPPER (one byte)
- 5C0Dh/23565d/IY-45d K DATA (one byte)
- 5C38h/23608d/IY- 2d RASP/PIP (two bytes)
Safe so long as BASIC isn't used:
- 5C0Bh/23563d/IY-47d DEFADD (two bytes)
- 5C42h/23618d/IY+8d NEWPPC (two bytes)
- 5C44h/23620d/IY+10d NSPPC (one byte)
- 5C45h/23621d/IY+11d PPC (two bytes)
- 5C47h/23623d/IY+13d SUBPPC (one byte)
- 5C49h/23625d/IY+15d E PPC (two bytes)
- 5C4Dh/23629d/IY+19d DEST (two bytes)
- 5C5Fh/23647d/IY+37d X PTR (two bytes)
- 5C6Eh/23662d/IY+52d OLDPPC (two bytes)
- 5C70h/23664d/IY+54d OSPCC (one byte)
- 5C72h/23666d/IY+56d STRLEN (two bytes)
- 5C82h/23682d/IY+72d ECHO E (two bytes)
Safe so long as neither BASIC nor certain ROM routines
are used, see the index entry:
- 5C92h/23698d/IY+88d MEMBOT (thirty bytes)
Of course, anything above the address currently in 5CB2
RAMTOP is also safe.

870
But be warned: the designers of peripherals sometimes
use some of these. For example, the DISCiPLE interface uses
NMIADD, and if you try to use it too you will crash the system.

S 2 COORD subroutine 2522


Checks the BASIC to make sure that the expression
currently pointed to by the pointer in 5C5D CH ADD takes the
form required by ATTR, POINT and SCREEN$ "(X,Y)"; where X
and Y may be expressions, but must be numeric. The values
of X and Y are read on to the calculator stack.
The coordinates for ATTR and SCREEN$ are character
coordinates as in an AT command, but for POINT they are
pixel coordinates as in a PLOT command.
Input parameters: none.
Action: if the next character isn't 28h ( jump on to S
RPORT C to report "Nonsense in BASIC"
- call 1C79 NEXT 2NUM to get the values on the stack; it
reports "Nonsense" if either expression isn't numeric or if the
comma isn't there, but it will accept any numeric expressions,
even very large, negative or fractional ones; the function
subroutines therefore have to check the numbers again
- check that the next character is 29h ).
_252D_S_RPORT_C: report "Nonsense in BASIC" if the
character didn't match.
Exit: into 2530 SYNTAX Z which sets the syntax/run flag.
Output parameters: only the syntax/run flag and the two
coordinate values on the calculator stack.
Called from:
2668 S SCREEN$
2672 S ATTR
267B S POINT

871
T

TAB key (AD) see also control characters, KEYBOARD


SCANNING,
022C extended mode table (b)
The P key in E mode without shift produces the print
control item code TAB. TAB can only be used within a PRINT
etc statement, and must be followed by one numeric
parameter. It causes a jump forward to the specified print
column reduced by any multiple of 32d, ie on the next line if
necessary, but never any further.
Execution is from within the PRINT executive routine
1FCD PRINT; each new expression following the PRINT etc
command is checked by a call to 1FFC PR ITEM 1 from 1FE5
PRINT 3. If it is a TAB, the TAB control character 17h/23d
and_two parameters, the lo and hi bytes of the number
supplied from BASIC, are sent through the output routine.
The way this works when printing on screen can be seen
at 09F4 PRINT OUT; indexing with 17h for TAB into the
control character table at 0A11 produces an indirect jump to
0A75 (0A22 + 53) PO 2 OPER. See the index description of this
subroutine for the rather tricky way in which it collects the
two parameters and sends execution to 0A87 PO CONT, which
finally resets the print position to comply with the TAB
command.
The TAB control character 23d can be used from BASIC,
but it requires_two parameters, the second of which is really
rather pointless: it is read as the hi byte of the tab value, but
whatever value is specified it is invariably read as zero:
10 LET a$=CHR$ 23 + CHR$ 7 + CHR$ 97 + "hello!"
20 PRINT a$
has just the same effect as
10 PRINT TAB 7;"hello"
This can also be done from m/c, but is much less useful

872
than the AT code 22d.
0A5F PO COMMA tabs set by comma
0A75 PO 2 OPER different entry for AT/TAB
0A87 PO CONT partly skipped for TAB
0AC2 PO TAB executes TAB command
200E PR ITEM 2 outputs TAB and parameter

tables
There are several different kinds of table, and several
different routines for reading them:
- 0C41 PO SEARCH for lists of messages: this type of
table is a continuous unindexed string of up to 256d messages
preceded by an 80h, not separated from each other except
that the last byte of each message has its bit 7 set. See 0C0A
PO MSG
- 16DC INDEXER for any table with a one-byte index
followed by a single byte, with zero marking the end of the
table; finds a one-byte "offset" in the 2nd, 4th, 6th ... place
in the table by comparing its input index in the C register with
the 1st, 3rd, 5th ... place
- 33BE ENT TABLE for the calculator "literals"
- 3406 LOC MEM finds an FP format number in a list of
such numbers
- 22F7 SKIP CONS finds a "stk-data" format number in a
list of constants.
There are also "single-byte tables": each entry is a
single byte, usually an "offset" which when added to the
address where it is found gives the address of a subroutine to
be jumped to. There is no general subroutine to read these
tables, though it wouldn't have been difficult to write one;
each is read by a different part of the ROM.
[A convenient way of writing such tables in assembly
language is as follows:
TAB.ADD DEFB SR0 - TAB.ADD - 0
DEFB SR1 - TAB.ADD - 1
DEFB SR2 - TAB.ADD - 2
etc, followed by

873
SR0 (first subroutine)
SR1 etc.
Of course there mustn't be more than 255d bytes between
the end of the table and the start of the last of the
subroutines.]

Table of addresses (calculator functions) 32D7


The calculator literals, see CALCULATE, FP CALC. Each
entry is a 2-byte subroutine address, unindexed. Read by 338E
ENT TABLE
335B CALCULATOR "literal" used to find entry
338C DOUBLE A index double to find 2-byte entry

Table of cassette messages see cassette messages

Table of channel code look-ups 162D


Leads to the subroutines setting the appropriate flags
for channels K, S and P. See channels and streams. Read in
1615 CHAN FLAG by 16DC INDEXER.

Table of close stream look-ups 1716


A dummy table - all three entries lead to the same
address. Read in 1701 CLOSE 2 by 16DC INDEXER.

Table of command classes 1C01 see also syntax offset table


below
A single-byte jump table: each entry forms the address
of a "command class" routine to be jumped to.
1B55 GET PARAM computes its own return address. An
index derived from the "parameter table" at 1A7A and
following fetches an "offset" from the command class table
and adds it to the table address; then this is stacked so that
RET returns to it.

Table of constants 32C5


The five calculator constants, zero, one, half, pi/2 and
ten; not indexed, and each a different number of bytes. The

874
correct entry is found by 3357 SKIP CONS from the calculator
literals A0 -> A4, after they has been converted to zero -> 04
by 336C SCAN ENT.
33C6 stk-data called by SKIP CONS
3418 stk-zero uses 33C8 STK CONST entry point

Table of control characters 0A11


Another single-byte jump table. The table is indexed
into in 09F4 PRINT OUT by a character code from 06, the
PRINT comma, to 17h/23d, the TAB control. Its "base address"
is therefore six bytes before the start of the table. The jump to
the indexed subroutine isn't actually made till 0B03 PO
FETCH.

Table of control codes (d) 0260 see KEYBOARD SCANNING


NB 3.

Table of editing keys 0FA0


Very similar to the table of control characters at 0A11,
but only handles character codes 07 EDIT to 0Fh GRAPHICS.
The jump is computed and executed in 0F92 ED KEYS.

Table of extended mode keys (b) 022C see KEYBOARD


SCANNING
NB 3.

Table of extended mode keys (c) 0246 see KEYBOARD


SCANNING
NB 3.

Table of extended mode keys (f ) 0284 see KEYBOARD


SCANNING
NB 3.

Table of initial channel information 15AF


Not so much a table as a string of bytes ready to be

875
copied by 1219 RAM SET from ROM into the channel
information area in RAM on start-up.

Table of initial stream data 15C6


Like the preceding, this isn't so much a table as a
string of bytes ready to be copied by 1219 RAM SET from ROM
into the system variable 5C10 STRMS on start-up. But it is also
indexed into from 16E5 CLOSE to reopen streams one -> 3 if an
attempt is made to close them.

Table of main keys 0205 see KEYBOARD SCANNING NB 3.

Table of open stream look-ups 177A


This is like the channel code look-up table at 162D, and
is similarly looked up by the 16DC INDEXER subroutine from
1767 OPEN 3.

Table of operators 2795


2723 S OPERTR uses 16DC INDEXER to convert a
character code such as 2B "+" into an operator code - later in
274C S STK LIST to be transformed into a literal for the
calculator. But its first use is to index into the priorities table
at 27B0, getting the priority appropriate to that operation.

Table of operator priorities 27B0 see table of operators


above

Table of report messages 1391


The error reports, read by 0C41 PO SEARCH in 0C0A PO
MSG call from 133C MAIN C. All the Spectrum messages are in
this table except the cassette messages, the token extensions,
the "scroll?", comma space and copyright messages; these are
excluded to keep the table down to thirty-two entries,
simplifying the control of leading spaces, see under PO MSG.

Table of scanning functions 2596 see also BASIC


INTERPRETER

876
Read by 16DC INDEXER in 24FF S LOOP 1 to give a jump
address for each of the functions taken over from the old ZX81
computer: the characters " ( . "unary plus", and the tokens FN,
RND, PI, INKEY$, BIN, SCREEN$, ATTR and POINT. The other
functions and operators are handled differently, see entry
under the individual token key.

Table of semitones 046E


A table, in five-byte FP format numbers, of the
frequencies in hz of each semitone in the scale of middle C.
0427 BE OCTAVE uses 3406 LOC MEM, which just counts
every fifth address in a table of FP format numbers, to find the
required frequency from the table.

Table of symbol keys (e) 026A see KEYBOARD SCANNING


NB 3.

Table of syntax offsets 1A48


A single-byte jump table of a slightly different form.
1B29 STMT L 1 computes the address of the right item in the
table by adding an index derived from the token code, zero ->
31h for the fifty BASIC commands, to the base address. See
under commands, functions and operators. This address finds
an offset, which however doesn't lead to a subroutine address
but to an address in the syntax parameter table immediately
following.
This finds one or more new indexes which check that the
command contains the appropriate separators, comma, TO,
THEN etc, and finds subroutine addresses in yet another
table, the command class table.
A return address is preset before jumping to these
subroutines, 1B52 SCAN LOOP, where the next index is read
from the syntax parameter table and a jump made to the next
subroutine. The loop is only broken when one of the CLASS
subroutines is reached which drops the SCAN LOOP return
address
- eg 1C10 CLASS 00 which leads into 1C16 JUMP CR.

877
1B6F SEPARATOR checks for presence of separator

Table of syntax parameters 1A7A see syntax offset table


above

Table of tokens 0095


A message table, similar to the report messages table,
see above, and similarly read by 0C41 PO SEARCH from 0C14
PO TABLE. There are 5Bh/91d entries. In this case the first
message "?" is only one character; so the table starts with a
byte with one bit set, and the introductory 80-byte isn't
needed.
[Actually this first message never gets used.]
Introduction - token expansion through table

TABLE SEARCH SUBROUTINE see 0C41 PO SEARCH

T ADDR system variable 5C74


Bytes: 2.
Holds the address where the next item in the syntax
parameter table at 1A7A and following is to be found. See
under table of syntax offsets above. Loaded from this table by
1B52 SCAN LOOP and incremented after each subroutine is
executed by 1B55 GET PARAM. The actual jump to the address
is made in 1C16 JUMP CR.
Also used in 0605 SAVE ETC as a temporary store for an
operation flag indicating which of the four commands SAVE,
LOAD, VERIFY or MERGE is at present being executed; the
value on entering the subroutine will have been 1AE0, 1AE1,
1AE2 or 1AE3, one more than the addresses of P SAVE etc in
the syntax parameter table, since T ADDR got incremented in
1B52 SCAN LOOP.
So reducing its lo byte by E0 (0605 SAVE ETC) leaves a flag
zero, one, 2 or 3 as appropriate.
This flag is used whenever it is necessary to exclude

878
certain operations from the general routine: eg you can't SAVE
with a null name (0629 SA BLANK), you can't MERGE ... DATA
(0652 SA DATA) etc.
Used in a rather similar way by 1C96 PERMS: the lo byte
of the addresses for the INK TO OVER commands, after having
been incremented in 1B65 GET PARAMS, is EC for INK to F1
for OVER.
Take away 13h makes D9 to DE, the token code, which is fed
into 21FC CO TEMP 4 to send it to print output.
Written by:
0605 SAVE ETC
1B55 GET PARAM
Read by:
0605 SAVE ETC
0629 SA BLANK
0652 SA DATA (twice)
06A0 SA SCR$
06C3 SA CODE (twice)
06E1 SA CODE 1
0723 SA LINE 1
075A SA ALL
07AD LD CH PR
07F4 VR CONT 2
1B52 SCAN LOOP
1C16 JUMP C R
1C96 PERMS

TAN key (B4) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The E key in E mode without shift produces the function
TAN: it requires one numeric operand X, and the value of the
function is tan X. X is in radians, see under 3783 get-argt.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B4 first to 05, then to E1,
and adds the priority 10h/16d. Code and priority 10E1 are now
pushed on to the machine stack (270D S PUSH PO) while the
expression following TAN is evaluated.

879
When the code is taken off the stack (2734 S LOOP) it is
converted 2773 S TIGHTER) from E1 to 21, the calculator offset
for 37DA tan.

tan subroutine 37DA


Called from 0028 FP CALC with the literal 21h; the
executive routine of the TAN function. Not otherwise called in
ROM, but can be called direct from m/c.
Given X in radians returns tan X. If you aren't sure
about radians, see under 3783 get-argt.
Input parameters: HL points to the first byte of X
- DE holds the address of the stack end
- X must be the last value on the calculator stack, even
for direct calls.
Action: use the calculator to compute SIN X/COS X, which
is always equal to TAN X; the division subroutine will report
"Number too big" if COS X is zero. This happens for TAN pi/2,
which equals "infinity".
Exit: RET.
Output parameters: HL and DE unchanged
- TAN X on the stack, replacing X.
Rems:
3449 series-06 indirectly used in calculation

"Tape loading error" message see 0806 REPORT R

temporary colour items


Rather confusingly used to mean "colour control code" at
21E2 CO TEMP 2. See colours.

TEMPORARY COLOUR ITEMS SUBROUTINE see 0D4D


TEMPS

temporary colours see colours

temporary memory area see 5C68 MEM

880
TEMP PTR 1 subroutine 0077
See 0074 CH ADD+1.
Called from:
1E0A READ 1
26BD S BIN
Rems:
0074 CH ADD+1 used to set CH ADD temporarily

TEMP PTR 2 subroutine 0078


See 0074 CH ADD+1.
Called from:
1E0A READ 1
Rems:
0074 CH ADD+1 used to set CH ADD temporarily

TEMPS subroutine 0D4D see also colours


Copies the permanent attribute values from 5C8D ATTR P
to 5C8F ATTR T. This is done each time a PRINT etc command
is executed, eg at 1FCF PRINT 1; if the ATTR T values aren't
changed by an embedded colour item, those which came
from ATTR P are implemented, but the values in ATTR T are
always used in any actual print.
The subroutine is also used elsewhere, eg to make sure
the permanent colours are used in clearing the screen or in
displays to the lower screen, etc.
Input parameters: none.
Action: make a zero byte
- get a value from 5C8D ATTR P/MASK P; one byte each
- if bit zero of TV FLAG is zero jump on to TEMPS 1;
upper screen
- (lower screen) replace MASK P with a zero byte and
ATTR P with the value from 5C4B BORDCR.
_0D5B_TEMPS_1: put the values in 5C8F ATTR T/MASK T
- get 5C91 P FLAG; its even bits flag "temporary" PAPER
9, INK 9, INVERSE and OVER, its odd bits flag the same things
"permanently"
- if the NZ flag is showing from the read of TV FLAG bit

881
zero jump on to TEMPS 2; lower screen
- (upper screen) rotate the flags one place to the right
for a setting byte; putting the "permanent" bits in the
"temporary" positions.
_0D65_TEMPS_2: XOR/AND/XOR the original flag byte with
the rotated byte using 1010101b/55h as a mask; for the lower
screen the flag byte hasn't been changed, so this changes
nothing. For the upper screen it replaces the even bits with
what were the odd bits. See masks. The hi bit is bit 7, the lo bit
is bit zero, so the mask has "holes" in the even bits; the rotated
flag byte is used as a setting byte, and its even bits, copied
from the odd bits of the original flag byte, go through the
"holes" to replace the even bits of the original flag byte
- put the new byte in P FLAG.
Exit: RET, from 0D65 TEMPS 2.
Output parameters: HL holds P FLAG
- A corrupted, no others changed.
Called from:
0D02 PO SCR 4
0D6E CLS LOWER
0DAF CL ALL
111D ED COPY (twice)
1C96 PERMS
1CDE CLASS 09
1FCF PRINT 1
Exit from:
1646 CHAN S 1
22DC PLOT
2477 LINE DRAW

TEMPS 1 0D5B (0D4D TEMPS)


Jumps from:
0D4D TEMPS

TEMPS 2 0D65 (0D4D TEMPS)


Jumps from:
0D5B TEMPS 1

882
terminator see BASIC line

TEST CHAR 001C (0018 GET CHAR)


Exit from:
0018 GET CHAR
0020 NEXT CHAR

TEST FOR SCROLL SUBROUTINE see 0C55 PO SCR

TEST NEG 307C (3014 addition)


Jumps from:
3055 SHIFT LEN

TEST NORM subroutine 3155 see also CALCULATE


The common exit routine of the four arithmetic
operations, addition, subtract, multiply, divide. Entered with
a result mantissa and exponent which may
require_normalization;
also with two pointer addresses on the machine stack.
The_normal_form of an FP number has a mantissa less
than one and more than or equal to a half; but the hi bit,
which would always be set for this range of values, is used as a
sign bit, zero for positive and one for negative. When one FP
number is added to, subtracted from, multiplied or divided by
another, the result produced by the routines often has a
mantissa less than a half or more than one, but if it is more
than one it is corrected in the main routines. Normalization
consists of repeatedly doubling the mantissa and
decrementing the exponent, till the mantissa is within range.
After this has been done a correct sign bit is inserted.
Normalizing is performed with an exponent already in
standard form, ie ranging from 01 for a true exponent -7F, to
FF for a true exponent +7F. If one number is subtracted from
an almost equal number, or two small numbers are
multiplied together, or a small number is divided by a large

883
one, the normalized exponent may well come out less than
01, corresponding to a true exponent less than -7F.
An FP number less than 01 00 00 00 00 is considered
equivalent to zero; see under overflow. The result must be
returned as zero if the normalized exponent is less than zero,
or it is zero and the mantissa is less than half; but is rounded
up to 01 00 00 00 00 if it is zero and the mantissa is more
than a half. 01 00 00 00 00 is equal to 2**-127d * 0.5d or
2**-128d, a little less than 3E-39; its true exponent is -7Fh/-127d
and its true mantissa one half. Its negative is 01 80 00 00 00,
and the true mantissa is 80 00 00 00 in either case.
If the exponent in the input parameters is already
small, decrementing it in normalization may again bring it
down to zero, in which case the result is again returned as
zero or 01 00 00 00 00.
Input parameters: HL points to the position for the
result, usually the last value on the calculator stack
- a normalised exponent byte is already in position in
the first place of the result
- the second place holds a sign flag: its hi bit is one
for negative, zero for positive, as it will be in the normalized
result, its other bits are immaterial
- 32d bits of the mantissa are in D'E'DE; this is a true
mantissa, without indication of sign, and it may have leading
zeroes, which a normalized mantissa can't have
- a 33rd and 34th bit are in the hi bits of A after
multiplication or division; after addition/subtraction A is zero
- the carry flag is NC after addition/subtraction; after
multiplication/division it will be C if the standard FP form
exponent came out at less than 01, ie less than -7F in true
form. This exponent will be 00 or FF/minus one, never less
- there are two addresses on the machine stack above the
return address, the two calculator stack pointers, which were
stacked in 30F0 MULT LONG, 31AF division or 303E FULL
ADDN.
Action: if the carry is NC jump on to NORMALISE

884
- (exponent is zero or minus one) test the exponent byte
for zero.
_3159_NEAR_ZERO (the normalizing loop jumps back to
here if the exponent gets decremented to zero): make a signal
byte 80h
- if the exponent is zero jump on to SKIP ZERO; with 80h
in the signal byte.
_315D_ZERO_RSLT (exponent is minus one; or loop back to
here if the normalizing loop finds 32d zeroes in the mantissa):
zero the signal byte.
_315E_SKIP_ZERO: AND the hi byte of the mantissa with
the signal byte; the resulting signal is zero if the exponent is
zero or the mantissa has a leading zero, but 80h if the
mantissa is one half or more_and the exponent is minus one
- call 2FFB ZEROS 4/5, which copies the signal byte in L
and zeroes the mantissa D'E'DE; it doesn't change the signal
byte
- rotate the signal byte left; its hi bit goes to its lo
bit and also to carry, making 00 or 01-and-carry from 00 or
80h
- put this in the exponent place
- if there is carry jump on to OFLOW CLR; the signal was
80h. The signal byte will be put in the hi byte of the mantissa,
so the number has been rounded up to 01 80 00 00 00, which
will be corrected to 01 00 00 00 00 if it is positive
- (zero signal) zero the second result byte; the sign
byte, negative zero isn't allowed. The number will now be 00
00 00 00 00 zero
- jump on to OFLOW CLR.
_316C_NORMALISE (the exponent isn't zero or less, but
there may be leading zeroes in the mantissa): make a digit
counter 20h/32d to count the 32d digits of the mantissa.
_316E_SHIFT_ONE: check the hi bit of the mantissa
- if it is one jump on to NORML NOW; the mantissa is now
one-half or more
- (a leading zero) shift D'E'DEA one place left; note

885
the fifth byte A, which is zero after additions and subtractions
but has two more or less meaningful hi bits after
multiplications or divisions. In multiplications or divisions the
result mantissa must be between a quarter and one, because
the mantissas of X and Y were both between half and one; so
the result mantissa can't need shifting more than one place,
and the lower bits of A never get used. [The note "A is rotated
circularly ... " etc is mystifying to say the least, and I suspect
meaningless.]
- decrement the exponent
- if it reaches zero jump back to NEAR ZERO
- loop back to SHIFT ONE, counting down the digits
- (the loop has counted down to zero) jump back to ZERO
RSLT; there were no bits set in the mantissa at all. This will
happen eg as a result of "X - X".
_3186_NORML_NOW: rotate one more bit left out of the
fifth byte; it will only be set if it is the 34th bit from a
multiplication or division
- if it was zero jump on to OFLW CLR
- (carry from bit 34) call 3004 ADD BACK, which adds the
carry into the mantissa to round it up
- if this doesn't produce the zero flag jump on to OFLW
CLR
- (zero flag means the mantissa was FF FF FF FF, now
rounded up to OO OO OO OO) make the mantissa 80 00 00
00 and increment the exponent
- if this makes zero report "Number too big"; the
exponent was FF.
_3195_OFLOW_CLR (we now have a correct normalized
exponent in its proper place in the result, and the hi bit of
the sign byte is also correct in its place; the mantissa is
between half and one, but it is still a true mantissa and still
in D'E'DE): set a pointer on the sign byte
- move the true mantissa into ACDE
- shift the hi byte left, losing the hi bit; a one in
all cases
- shift the sign bit left into carry from its temporary

886
store in the result sign byte
- shift back the hi byte of the mantissa taking the hi
bit from carry.
- step through the four result mantissa bytes loading
ACDE into them
- get back the two calculator stack pointers.
Exit: RET, from OFLOW CLR.
Output parameters: HL points to the first byte of the
result
- DE to the byte beyond the result
- HL' to the "return address" holding the next literal.
Exit from:
30A5 GO NC MULT (3014 addition)
3151 OFLW2 CLR (30CA multiply, 313D DIVN EXPT)
Rems:
303E FULL ADDN normalizes result with correct sign bit
30F0 MULT LONG performed in TEST NORM as part of
this
3146 OFLW1 CLR check for overflow if in normal form
3151 OFLW2 CLR any overflow needed in normalization
3155 TEST NORM if no carry, no further adjustment reqd
316C NORMALISE executes normalization
3186 NORML NOW handles carry

TEST ROOM subroutine 1F05 see also GO SUB stack,


memory
The room available for expansion of the RAM part of the
Spectrum operation is the "spare" space between the top of
the calculator stack which expands upwards, and the "top" of
the machine/GO SUB stack which expands downwards.
If this space ever contracted to nothing, the calculator
stack and machine stack would collide, overwriting each
other, with unpredictable but certainly disastrous results.
No check is really possible on expansion of the machine
stack, but every other expansion of memory uses the TEST
ROOM subroutine to check that there is enough space for the
command called for, and to abort it if not: eg "making room"

887
in the various system areas (1655 MAKE ROOM), adding a
value to the calculator stack (33A9 TEST 5 SP) or putting
another address on the GO SUB stack (1EED GO SUB). RUN the
following simple program:
10 GO SUB 10
It goes on piling up RETURN addresses on the GO SUB stack
until the whole of the spare memory is full of RETURN
addresses, when it breaks off with the report "4 Out of
memory, 10:1".
The defence adopted against over-expansion of the
machine stack is to make an allowance in TEST ROOM of
eighty bytes for it; assuming that no more than forty 2-byte
values will ever be on the machine stack at once. This
assumption is presumably valid for all the ROM routines, but
it is perhaps worth bearing in mind as a limitation on m/c
programs.
The subroutine is also used to return the amount of
memory used so far by the subroutine 1F1A FREE MEM, which
can be called from BASIC only by USR 7962, or by m/c
equivalents.
10 PRINT 65536-USR 7962: GO SUB 10
makes an interesting demonstration.
Input parameters: BC holds the room count, the amount
of space which it is proposed to take up; usually with a bit of
spare "for contingencies", eg 20d spare bytes when it is
proposed to add to the GO SUB stack.
Action: get the address of the top of the calculator
stack from 5C65 STKEND
- add the room count
- if this is already more than FFFFh report "Out of
memory"; apparently a duplication, but a carry here would
invalidate the carry signal of later operations
- add eighty bytes; to allow for PUSHes and CALLs
extending the machine stack
- if this is now more than FFFFh report "Out of memory"
- check the pointer against SP, the machine stack

888
pointer; making carry if the room count comes out below the
stack pointer, ie there is enough spare memory
- if there is no carry report "Out of memory".
Exit: RET.
Output parameters: HL holds the number of bytes in use -
including all the ROM etc. The "free memory" is 10000h/
65536d- HL.
Called from:
0825 LD CONT 2 (BC=length of load block plus 5)
0FA9 ED EDIT (BC=length of edit line plus 10)
1655 MAKE ROOM (BC=length of space to be made)
1F1A FREE MEM (BC=zero)
33A9 TEST 5 SP (BC=5)
Exit from:
1EED GO SUB (BC=20)

TEST ZERO subroutine 34E9


Checks a FP number to see if it is zero. Zero can only
be represented as a "small integer"; in this format the fifth
byte is immaterial, so only the first four need to be looked at.
All must be zero for the number to be zero.
Input parameters: HL holds the first address of the
number.
Action: get the first byte
- OR the second with it, then the third with that result
and the fourth with the result again; any non-zero byte will
flag NZ
- if the result flags Z set the carry.
Exit: RET.
Output parameters: all registers are saved unchanged
- returns with carry as well as Z if the number was zero.
Called from:
1CF0 IF
30C0 PREP M/D
346E NEGATE
3492 sgn
34F9 greater-0

889
3501 not
351B or
3524 no-&-no
352D str-&-no

TEST 5 SP subroutine 33A9 see also TEST ROOM


Tests whether 5 spaces can be made without memory
overflow - enough to put a number on the calculator stack.
Input parameters: none.
Action: call 1F05 TEST ROOM with five in BC.
Exit: RET.
Output parameters: HL and DE are saved unchanged
- BC holds five
- otherwise none; there must be room, or there would be
an "Out of memory" report from TEST ROOM.
Called from:
2AB6 STK STORE
33C0 MOVE FP
33C8 STK CONST

T EXPNENT 326C (3214 truncate)


Jumps from:
3233 T FIRST

T FIRST 3233 (3214 truncate)


Jumps from:
3221 T GR ZERO

T GR ZERO 3221 (3214 truncate)


Jumps from:
3214 truncate

THEN key (CB) see also KEYBOARD_SCANNING, 026A


symbol code
table (a)

890
The G key with symbol shift produces the token THEN; its
only use is in IF commands, to introduce the command which
is to be executed if the IF expression is "true".
When the IF command is scanned by the statement loop
in 1B6F SEPARATOR, reading the syntax parameters at 1A81,
the command is checked to ensure that THEN is there. By this
time the value of the IF expression, "true" or "false", is already
last value on the calculator stack; whether "true" or "false",
the THEN token is simply skipped. But like the colon it marks
the end of a statement, and at 2713 S CONT 3 leading to 2723 S
OPERTR and 2734 S LOOP, it triggers the evaluation of the IF
expression.
The keystroke following THEN is always read in K mode,
even in REMs or INPUT ... LINE. This is sometimes useful.
1937 OUT CHAR leaves K mode in FLAGS
19A5 EACH S 4 jump for THEN
19AD EACH S 5 counts as end of statement
2712 S CONT 2 marks end of expression

'third' of display see DISPLAY AREA

tighter binding see 24FB SCANNING

time period, time-up, timing constant, timing loop, timing


period
Executing the various instructions of the Z80 chip,
which is the "central processing unit" of the Spectrum, takes
various lengths of time which are measured in a unit called a
_T_state, or sometimes a_clock_cycle. The details can be found
in various reference books, eg "Z80 Instruction Handbook" by
Nat Wadsworth, publ. Hayden Book Company, Rochelle Park
NJ.
The fastest instructions, such as LD A,B or EX DE,HL or
NOP, "no operation", take only four T states, the slowest, eg
INC (IX+index), as many as 23d T states. Most conditional
instructions, including DJNZ, take longer if their condition is
met than if it isn't; DJNZ takes 13d T states to loop back, but

891
only eight to exit if B is zero. Given the reference books, it
is a fairly simple matter to compute the duration in T states of
any string of instructions or loop of your m/c.
The actual duration of a T state depends on the computer
being used: the Spectrum "system clock" is set to drive
through 3.5 million T states per second, which means it
performs 875,000d of the four-T-state instructions per second.
This is so fast that there is no appreciable delay in carrying
out any ordinary BASIC instruction, even a quite complicated
one, and the programmers have in several places been able to
sacrifice speed for compression of the coding; see under the
subroutine 2535 S SCRN$ S. It takes a little ingenuity to write a
m/c program that takes more than a fraction of a second to
produce a useful result, unless delays are deliberately put in
the code.
A simple_delay_loop or_timing_loop is the instruction
0514 SA BIT 1 DJNZ 0514 SA BIT 1
which merely repeats itself as many times as allowed by the B
counter before continuing. The duration of the timing loop
will be thirteen times B minus five T states. B is called the
_timing_constant.
A long delay loop is sometimes called a_waiting_period;
eg in 0574 LD WAIT, see under 0556 LD BYTES, where the
length of the delay is 0415 * (1A + 100 * 0D)h = 35,8B22h =
3,504,930d T states.
Much longer delays can conveniently be introduced by
the HALT command: a single HALT stops execution of the m/c
for 70,000d T states, 1/50th second. However HALT cannot be
used with the interrupt disabled, and is little used in the ROM.
There are also indefinite waiting loops such as 15DE
WAIT KEY1 which continue to loop, for ever if necessary, until
some input from outside, in this case a keystroke, allows them
to break out.
Timing is important in two sections of the ROM routines:
1. Save/load routines, see 0556 LD BYTES, 04C2 SA BYTES,
ports

892
_edges,_edge_types: an "edge" in general means the change
from zero/off to one/on or vice versa. In this context it means
the change from MIC ON to MIC OFF or vice versa. The_edge-
type means the MIC setting after the change.
_leader_signal: both header and main block are preceded
by a "leader signal", lasting five seconds for the header and
two seconds for the block. The leader signal consists of a
regular series of pulses at a frequency of about 800d hz; each
pulse both BEEPs to the loudspeaker and changes the border
colour from CYAN to RED and back, so that the leader pulse is
audible as a whistling note and visible as stripes in the border.
_pulses,_leader_pulses,_sync_pulse: a pulse is defined as
a period of MIC on followed by a period of MIC off, ie two
edges. The leader pulses constituting the leader signal are
followed by a much shorter sync pulse, which signals the end
of the leader, and then by the bit pulses signalling the SAVEd
code, long pulses for one and short for zero; these too are
signalled to the speaker and the border, making YELLOW/
BLUE stripes.
2._BEEP_routines, see 03F8 BEEP and 03B5 BEEPER
_note: the note in musical terminology: C, C sharp, etc.
_frequency: the number of pulses per second, measured
in hertz: middle C is 261.63d hz. Doubling the frequency raises
the note by one octave: as there are twelve semitones in an
octave, the multiple to raise the note by one semitone is the
number which when multiplied twelve times by itself makes
two, ie the twelfth root of two, see 03F8 BEEP
_pitch: used in the notes to mean the number given by
BASIC specifying the note required: zero for middle C, plus
one for each semitone above.
Introduction stable pitch
0310 K REPEAT key repeat delays
03B5 BEEPER pulse on/off timing loop is 4 times L
T states, with fine tuning in IX
03D1 BE IX+3 loop entered one NOP earlier for each fine
tuning adjustment
03D6 BE H&L LP start of timing loop - calculated delay

893
between OUT signals
03F2 BE AGAIN four short instructions quicker than
previous paragraph
03F8 BEEP pitch is last value, frequency and loop time
calculated
0425 BE i OK frequency
0427 BE OCTAVE finds octave, frequency, loop time
046E semitone table
04D8 SA LEADER leader pulses have cycle 4336dT =
807dhz
04EA SA SYNC 1 off 667dT=0.2 thousandths/sec; sync
pulse 735dT=0.4 thousandths/sec
04F2 SA SYNC 2 timing constant set to 3Bh/59d
0511 SA BIT 2 bits: "zero" 855dT shorter than "one"
0514 SA BIT 1 timing loop; now timer set to 42h/66d
051A SA SET timing loop
051C SA OUT timer set to 3Eh/62d
0525 SA 8 BITS timer set to 31h/49d
053C SA DELAY note on length of data bit pulses
056B LD BREAK wait till pulse received
056C LD START waits about 4 thousandths/sec
0574 LD WAIT B was zero, so the DJNZ loop cycles 256d
times; the next four instructions take 26d T
states; HL set to 415h/1045d counts repeats of
the whole cycle, making
(13 * 256 + 8 + 26) * 1045 = 3,513,290 T states
0580 LD LEADER looks for leader pulses, half-cycle
2168 T; timer for LD EDGE 9Ch/156d
058F LD SYNC look for sync pulse
05C4 LD DEC timer for LD EDGE B2h/178d
05CA LD 8 BITS checks bit; one bit (including "off"
time) will be 2 * 801 + 855 = 2457dT,
zero bit will be 1602dT. Set timer again
to B0h/176d
05E3 LD EDGE 2 looks for 1 or 2 edges in given time
05E7 LD EDGE 1 4 T for DEC A, 12 T for JR NZ; times
22d = 352d, plus 7 for JR NZ again and 4

894
for AND A
05ED LD SAMPLE loop takes 60 T each pass
0970 SA CONTRL set delay of fifty HALTS, one second
11CB START/NEW six NOPs, 4 T each

T NUMERIC 3252 (3214 truncate)


Jumps from:
323F T SMALL

TO key (CC) see also KEYBOARD SCANNING, 026A symbol


code
table (a)
The F key with symbol shift produces the token TO; it
has two quite different uses, as a separator in FOR ... NEXT
loops and as a signal for slicing of strings.
When the FOR command is scanned by the statement
loop in 1B6F SEPARATOR, reading the syntax parameters at
1A90, the command is checked to ensure that TO is there. By
this time the value of the first parameter of the FOR
expression (the VALUE) is already last value on the calculator
stack; the TO token is simply skipped. It serves a purpose at
2713 S CONT 3 leading to 2723 S OPERTR and 2734 S LOOP, by
triggering the evaluation of
the expression.
In 2A52 SLICING, the need for a slicing operation has
already been recognised before the TO is scanned: the last
subscript of an array, or any "(" following a simple string or
the completed expression of an array (2713 S CONT 3),
produces a call to SLICING. Any slice of more than one
character must have a TO in it; if TO is the first character of
the slicing it is read as "1 TO", if the last it is read as "TO the
dimension limit". You can command PRINT a$( TO ), though it
does nothing very useful, just prints out the whole of a$.
29D8 SV CLOSE last subscript must be followed by TO
or ")"
29EA SV LOOP slicing indicated by TO

895
token codes, tokens see also character codes and individual
token keys
The 91d character codes from A5 RND to FF COPY are
tokens, that is to say that in printouts of the BASIC line a
single character code, produced by a single keystroke, is
_expanded to a string of two or more characters.
This has nothing to do with the implementation of the
code: <> etc are tokens, + - * / aren't. Nor has it much to do
with the key "mode": all the letter keys in K mode produce
tokens, eleven of them are produced by symbol shift in any
mode, and the rest by E mode with or without shift, but other
keys in E mode produce symbols.
All the tokens are "printable" characters, see 001C SKIP
OVER.
The tokens are only expanded, ie transformed into
strings of letters or symbols, when they are output to screen
or ZX printer: each token is represented by its single token
code in the program area, in the variables area if it is
incorporated in strings, in the editing area or in the input
area/work space.
For actual printing out of the codes, they are treated
much like "messages", eg the report messages. See under
0C0A PO MSG and Table of tokens. At 0B52 PO T&UDG in the
09F4 PRINT OUT routine, the code is reduced by A5h so that
it is now from 00 RND to 5A COPY. This is used by 0C14 PO
TABLE and 0C41 PO SEARCH to index into the token table at
0095 and print the token. The "?" at the beginning of the table
isn't counted - it doesn't seem to be used by the ROM.
The use of tokens, which simplifies input by the user
and also simplifies compilation of the BASIC, is a Sinclair
invention and isn't found on other computers, in which all
commands etc must be typed out in full, as in the Spectrum in
128K mode.
Introduction token table
0364 K TOKENS all tokens in K/L mode found by "main
code" (ie capital letter) plus A5
06A0 SA SCR$ SCREEN$ token checked

896
06C3 SA CODE CODE token checked
0716 SA LINE LINE token checked
09F4 PRINT OUT entered with code in A
0B24 PO ANY jump to print tokens
0B52 PO T&UDG jump again to print tokens
0B5F PO T call and exit for token printing
0C0A PO MSG prints messages or tokens
0C10 PO TOKENS entry point for token printing
0C14 PO TABLE leading space printed for most tokens
0C22 PO EACH prints characters of token
0F38 ED LOOP all tokens accepted by editing
10A8 KEY INPUT all tokens accepted by key input
1795 AUTO LIST tokens expanded in listing of BASIC
1937 OUT CHAR checks tokens before printing edit line
198B EACH STMT can find selected token in BASIC
1990 EACH S 1 check character with given token
1C96 PERMS checks for INK to OVER tokens
1D86 LOOK PROG looks for DATA, DEF FN, NEXT tokens
1E39 PASS BY finds statement following DATA or DEF FN

top line see automatic line number, print position, 5C6C S


TOP

top of calculator stack see CALCULATE

top of (possible) RAM see 5CB2 RAMTOP, 5CB4 P RAMT


The Spectrum chips allow RAM to be read and written up
to the address FFFFh set in 0000 START; 7FFFh in the 16K
Spectrum. But if there is a defect in the chip preventing
certain addresses being written, it will be detected on start-up
by 11E2 RAM READ; 02 has been written into every address by
11DC RAM FILL, and this is decremented twice in each
address. If any of them fails to reach zero, it is defective, and P
RAMT is set at the one below. Thus the computer will still
work, though with a reduced RAM.
On a NEW command the check is also made, but only up
to the limit set in 5CB2 RAMTOP.

897
top of screen see DISPLAY AREA

to-power subroutine 3851


Called from 0028 FP CALC with literal 06; the executive
routine of the "up arrow" operator, printed in this index as
"**", as in many versions of BASIC. X**Y returns the number X
to the power Y. Fractional and negative powers are explained
in the old Spectrum handbook on p. 65, pp. 70-71 in the Plus 2
book.
Not called from ROM otherwise, but it is the exit from
384A sqr, used to raise X to the power one-half. Can be called
from ROM direct or through the calculator.
The subroutine makes use of the EXP and LN functions to
simplify the calculation. It is important to understand that
these two functions reverse each other: EXP X means e**X, X
= LN Y if Y = EXP X = e**X. So EXP LN X = X.
If Z = X**Y, then
LN Z = Y * LN X
unless X is negative, which is an error, or zero.
Use the ln routine to get LN X, then calculate LN Z as Y
* LN X; exit to the exp routine which returns
EXP LN Z = Z = X**Y
as required.
Input parameters: HL and DE point to X and Y, the second
last and last values on the stack respectively; they must be on
the stack even for direct calls.
Action: use the calculator to check X
- if it is zero jump on to XISO; the sequence not/jump
true is equivalent to "jump on zero"
- (non-zero X) calculate LN Z = Y * LN X; 3713 ln will
report "Invalid argument" if X is negative
- exit to 36C4 exp, which gives a correct result for
non-zero X whatever the value of Y.
_385D_XISO (X = zero): if Y is also zero, jump on to ONE
- put a zero on the calculator stack
- if Y is positive jump on to LAST with zero as the

898
result; any positive power of zero is zero
- (Y is negative) calculate one divided by zero, which
reports "Number too big"; correct for zero X and negative Y.
_386A_ONE (zero X; Y**0 is considered to give the result
one, whether Y is positive or negative or even zero): clear the
calculator stack and put one on it as the result.
_386C_LAST: exit.
Exit: to 36C4 exp, which calculates EXP (Y * LN X), unless
X is zero.
Output parameters: the result on the calculator stack,
X**Y, zero or one
- HL pointing to it
- DE pointing to the stack end.
Exit from:
384A sqr

trailing space see 0C0A PO MSG

true exponent of FP number, true FP form of numbers, true


numerical bit of FP number see CALCULATE

"true" expression see logical value

TRUE VIDEO key see colours, INVERSE control code

truncate subroutine 3214


Called from 0028 FP CALC with literal 3A; not called
directly from ROM, but it could so be called from m/c.
It returns the integer part of X, positive or negative;
ie X ignoring any of its decimal/binary/hex digits after the
fractional point.
This isn't precisely the executive routine of any BASIC
function; for positive numbers it is the same as INT, but for
negative numbers INT returns the next integer_below, ie
further from zero, and truncate returns the next_above, ie
closer to zero.
The subroutine is more complicated than one might

899
expect. In a FP number the four mantissa bytes corrected for
positive/negative sign give a 32d-binary-digit representation of
the number, and to truncate it all you need to do is zero the
digits representing the fractional part; but the only way to tell
which digits are the integer part and which the fractional part
is by counting from the left with the true exponent.
Input parameters: HL points to the first byte of the
number X to be truncated; in all the ROM calls this is the last
value on the calculator stack, but for direct calls it could be
anywhere in memory.
Action: if the first byte of X is zero, return at once;
zero exponent byte means X is in small integer format and
there is nothing for the subroutine to do
- (full FP format) if it is more than 80h jump on to T
GR ZERO
- (exponent is less than 80h, so X is less than one)
zero the exponent
- make a bit counter 20h/32d to zero all 32d bits of the
mantissa
- jump on to NIL BYTES.
_3221_T_GR_ZERO and_3233_T_FIRST (exponent is 81h or
more, so X is more than one): if the exponent isn't 91h jump
on to T SMALL
- (the exponent is 91h, ie the number is 2 * 65536d
times a mantissa between half and one, or 65536d times a
number between one and two) this includes the "wrong
number" -65536d.
See under wrong number for a discussion of this part of the
ROM, which is misconceived and erroneous.
_323F_T SMALL: if the exponent of X is more than 91h
jump on to X LARGE
- (the true exponent is 10h or less, so the result will
be less than 10000h, and can be expressed as a small integer)
reverse the exponent, ie subtract it from FFh; transforming it
from the range 81h -> 90h to 7Eh -> 6Fh
- add 91h, transforming it to the range 0Fh -> 00; this
is a zero counter, the number of binary zero digits to be

900
inserted at the start of the sixteen-bit representation of X
- load the two hi bytes of the mantissa into DE;
shifting these right with the zero counter will give the
absolute value of truncated X in DE, after correcting the hi bit
which indicates the +/- sign of X
- make a sign flag set to 00 for positive
- if the hi bit of D is zero jump on to T NUMERIC; X is
positive
- (X is negative) make the sign flag FF; negative.
_3252_T_NUMERIC: set the hi bit; now DE holds two bytes
of the true mantissa
- if the zero counter is less than eight jump on to T
TEST
- (zero counter eight or more; all of the E byte is
fractional part) move D to E
- zero D
- take away eight from the zero counter.
_325E_T_TEST: if the zero counter is zero jump on to T
STORE; no shifting will be needed
- (some shifts needed) copy the zero counter into a loop
counter for the shifting loop.
_3261_T_SHIFT: shift DE right with zero into the hi bit
- loop back to T SHIFT, counting down the zero counter.
_3267_T_STORE (DE now holds the small integer
truncation of X): call 2D8E INT STORE, which makes a small
integer at the pointer address; with zero in the exponent
byte, the sign flag from the C register in the second, E and D
in the third and fourth
- return.
_326C_T_EXPNENT (entered only from T FIRST, see under
wrong number): get the exponent again.
_326D_X_LARGE (the exponent is more than 90h, so X
after truncation will still be in full FP format): if the exponent
is A0h or more, return; the true exponent is 20h or more, X is
larger than FFFF,FFFFh. All 32d of the binary digits of the
mantissa are used up representing the integer part and the

901
fractional part isn't represented in FP format at all, so
truncation will have no effect
- (X has a fractional part) negate the result of taking
A0h from the exponent; the subtraction transformed the
exponent from 91h -> 9Fh to F1h -> FFh, so negating,
subtracting from 100h, makes 09 -> 01. This is the fractional
bit counter, the number of digits at the lo end of the mantissa
which represent the fractional part of X and therefore are to
be zeroed.
_3272_NIL_BYTES: set a pointer on the last mantissa byte
- divide the fractional bit counter by eight; this is
the number of complete mantissa bytes to be zeroed
- if this byte counter is zero jump on to BITS ZERO.
_327E_BYTE_ZERO: zero the byte at the byte pointer
- move back the byte pointer
- loop back to BYTE ZERO counting down the byte
counter to zero.
_3283_BITS_ZERO: AND the fractional bit counter with
00000111b/07; this gives the remainder on dividing it by eight,
the number of fractional bits to be zeroed in the next byte
back
- if the result is zero jump on to IX END
- make the remainder a loop counter
- make a mask byte 11111111b/FFh.
_328A_LESS_MASK: shift the mask left, with zero into its
lo bit
- loop back to LESS MASK counting the loop down to
zero;
on exit from this loop the mask will be eg 11100000b if the
count was 05
- AND this mask with the last non-zeroed byte; thus
zeroing just the right bits for the correct result.
_2290_IX_END: restore the pointers as they were input.
Exit: RETs
- from truncate if X was a small integer already
- from T STORE if X was small enough to be made a small
integer

902
- from X LARGE if X was too big to have a fractional
part
- from IX END otherwise.
Output parameters: truncated X has replaced X
- HL points to its first byte
- DE points one past its last byte; if the subroutine
was called through the calculator, truncated X is the last value
on the stack and DE is the stack end.
Called from:
36AF int
36B7 X NEG
Rems:
3014 addition problems with "wrong number" -65536

T SHIFT 3261 (3214 truncate)


Jumps from:
auto

T SMALL 323F (3214 truncate)


Jumps from:
3221 T GR ZERO

T states see time period

T STORE 3267 (3214 truncate)


Jumps from:
325E T TEST

T TEST 325E (3214 truncate)


Jumps from:
3252 T NUMERIC

TVDATA system variable 5C0E see 0A75 PO 2 OPER under


09F4 PRINT OUT
Bytes: 2.
When the expression following a PRINT command is
being

903
output to screen, INK to TAB instructions have already been
converted to control codes 10h/16d -> 17h/23d followed by
parameters; but the parameters will be decoded wrongly
unless they are read merely as numbers following a control
code. So the control code is stored in the lo byte of this sv
(0A7D PO TV 1) and the first parameter, if there are two, in its
hi byte (0A6D
PO TV 2) while the rest of the instruction is read.
Implementation is then at 0A87 PO CONT.
Written by:
0A6D PO TV 2 (hi byte)
0A7D PO TV 1 (lo byte)
Read by:
0A87 PO CONT (both bytes)
Rems:
0A75 PO 2 OPER saves code while output address
changed

TV display see DISPLAY AREA


Only the Introduction calls it the "TV display".

TV FLAG system variable 5C3C


Bytes: 1.
The eight bits are all separate flags; four aren't used
in ROM and are available for m/c programmers, but they are
cleared whenever BASIC is listed or an INPUT command
executed.
All eight flags written by:
1795 AUTO LIST (bit 4 set, others zeroed)
17FB LIST 1 (all zeroed)
2096 INPUT 1 (misprinted DF SZ; bit zero set, others
zeroed)

TV flag bit 7: not used

TV flag bit 6: not used

904
TV flag bit 5: clear screen flag
On "clear lower screen after keystroke"; off "don't".
The lower screen is cleared by any keystroke after:
- "Start tape and press any key" when SAVing.
- the "scroll?" message
- the copyright message
- any report message
Turned on by:
0970 SA CONTRL
0C88 PO SCR 2
1219 RAM SET
1313 MAIN G
Turned off by:
0D6E CLS LOWER
111D ED COPY
Read by:
10A8 KEY INPUT
15D4 WAIT KEY

TV flag bit 4: Auto list flag


On "dealing with auto list"; off "not". The difference
between_automatic and_ordinary_listing is that in automatic
listing a check is made to ensure that the_current_line, the one
marked with the > cursor, appears somewhere on the screen;
if it doesn't, the listing is scrolled up without any "scroll?"
prompt until it appears at the bottom of the screen. See 1795
AUTO LIST.
The_current_line_number is always in 5C49 E PPC; the_top
_line number from which printing to the screen begins is in
5C6C S TOP. The top number used last time, from S TOP, is
first choice for the "automatic line number", but the listing
routine makes sure the current line won't be left off the
screen.
Turned on by:
1795 AUTO LIST
Turned off by:
0C55 PO SCR

905
17ED AUTO L 4
Read by:
0C55 PO SCR
1835 LIST ALL
Rems:
0C55 PO SCR partly skipped for ordinary listing
0DAF CL ALL called in automatic listing
0FF3 ED DOWN new listing produced
106E ED LIST new listing produced
12A2 MAIN EXEC produces listing
15AB MAIN ADD2 produces listing
1795 AUTO LIST signal is in TV FLAG
17CE AUTO L 1 moves on to find suitable S TOP
17E1 AUTO L 2 gets line number of top line
17E4 AUTO L 3 makes listing
17FB LIST 1 signals ordinary listing
1822 LIST 5 signal "before current line"
1835 LIST ALL handles scroll down to current line
1855 OUT LINE signals before or after current line

TV flag bit 3: Copy edit flag


On "copy the edit or input line to lower screen"; off
"don't". While waiting for key inputs, execution circles many
many times round the 15DE WAIT KEY1 loop, calling 15E6
INPUT AD and through it 10A8 KEY INPUT on every turn of
the loop. KEY INPUT first checks this flag and if it is set,
indicating that some change has been made to the input since
last time, recopies the editing or input to the screen. The
notes call it a "mode flag", set if "the cursor mode may have
changed"; this isn't exactly wrong, but doesn't tell the whole
story.
Turned on by:
10F4 KEY FLAG
15D4 WAIT KEY
Turned off by:
111D ED COPY
1795 AUTO LIST

906
17FB LIST 1
Read by:
10A8 KEY INPUT

TV flag bit 2: not used

TV flag bit 1: not used

TV flag bit 0: Lower screen flag


On "printing in lower part of screen"; off "upper
screen". See upper screen.
Turned on by:
0D2D PO SCR 4B
0D6E CLS LOWER
1634 CHAN K
1795 AUTO LIST (misprinted FLAGS)
2096 INPUT 1 (misprinted DF SZ)
Turned off by:
0D2D PO SCR 4B
1642 CHAN S
1795 AUTO LIST (twice: off when all off, then off
again, again misprinted FLAGS)
17FB LIST 1
1C96 PERMS
1CBE CLASS 09
20AD INPUT 2
Read by:
0AAC PO AT ERR
0ADC PO STORE
0B03 PO FETCH
0C55 PO SCR
0D4D TEMPS
0DD9 CL SET
0E4D CL LINE 2

TWO PARAM subroutine 1E85


Gets two small integers from the calculator stack into A

907
and BC. Used for the OUT and POKE commands, which
require a two-byte address and a one-byte integer to put to it.
Input parameters: none
- the two numbers on the stack, the smaller one as last
value.
Action: call 2DD5 FP TO A to get the first parameter
- if it is more than 255d report "Integer out of range"
- if FP TO A returned with Z jump on to TWO P 1;
positive
- (it is negative) negate it; so that eg -1 will be read
as FFh.
_1E8E_TWO_P_1: call 1E99 FIND INT2 to get the second
parameter; this will produce error reports for a negative
number or one bigger than 65535d.
Exit: RET, from TWO P 1.
Output parameters: A and BC hold the two numbers; they
have been cleared from the stack.
Called from:
1E74 OUT
1E80 POKE

TWO P 1 1E8E (1E85 TWO PARAM)


Exit from:
1E85 TWO PARAM

twos complementing
Normal terminology, but it may be helpful to remark that
a number is twos complemented or negated (NEG) by
subtracting it from 1000...h and ones complemented (CPL) by
subtracting it from FFFF...h; as many 0s or Fs as hex digits in
the number.

type of save/load blocks see 0605 SAVE ETC

type of variable see variables

908
U

UDG system variable 5C7B,


Bytes: 2.
The address of the first user-defined graphic. The udgs
consist of twenty-one character forms, each of eight bytes,
arranged like those in the character set; see page 93 in the old
Handbook, page 89 in the Plus 2 handbook, and page 221 of
"ROM Disassembled". On start-up the 168d bytes are copied
directly from the character forms for capital A to U in the
character set.
The_udg_character_codes are 90h to A4h; but when the
code has been identified as a udg for printing out (0B52 PO
T&UDG), they are reduced by 90h so that they run from 00 to
14h.
This makes the first code code zero, so the "base address" of
the set is the same as its first address, unlike the character
codes addressed by 5C36 CHARS, whose first code is 20h/32d.
There is no provision in the ROM for changing the udg
character forms or for relocating UDG itself; the bytes must be
POKEd individually from BASIC, or of course from m/c. The
USR function with a string argument, see usr$ below, merely
finds the first byte in the udg area of the character form
associated with that letter. However UDG itself and any forms
inserted in the udg area are preserved from being rewritten
by the NEW command.
There are several quite practical commercial programs to
assist users in composing udg forms, and even supplying such
things as whole alphabets of script forms or similar.
Written by:
11EF RAM DONE (twice)
Read by:
0B52 PO T&UDG
11B7 NEW

909
Rems:
0B24 PO ANY separates UDGs from other codes

UNARY MINUS OPERATION see 346E negate

unary operation see binary operation

"underline" character (5F)


Key zero with caps shift; handled as a normal character.
Its "final key code" is allotted as a one-off in 039D K KLC DGT.

unprintable codes see character codes

unshifted codes see shift keys

UNSTACK Z subroutine 1FC3


Uses the syntax/run flag to choose between two return
addresses, one used in syntax checking and the other in
execution: to cut out actual execution during syntax checking.
It is always called from another subroutine, so the
machine stack has two return addresses on it. The top one,
which is an address in the calling subroutine, will continue
into execution; if the flag calls for execution it is jumped to. If
the flag is for syntax checking, UNSTACK Z makes a double
return to the return address of the calling subroutine,
skipping its executive part.
Could have applications in m/c programs, see useful
routines.
Input parameters: none.
Action and Exit: call 2530 SYNTAX Z to read the flag
- POP the upper return address
- if the flag is set for syntax checking, return
- (run time) jump to the POPped address.
Output parameters: HL is corrupted.
Called from:
1FF5 PRINT CR
1FFC PR ITEM 1

910
200E PR ITEM 2
2024 PR ITEM 3
2070 STR ALTER
21FC CO TEMP 4

unused addresses
All these are filled with FFh bytes except as indicated.
0010 PRINT A 1 (5 bytes)
0020 NEXT CHAR (3 bytes)
0028 FP CALC (5 bytes)
0055 ERROR 3 (7 bytes)
04AA PROGRAM NAME: 24 bytes, coded as follows:
CALL 24FB;(SCANNING)
LD A,(5C3B);(FLAGS)
ADD A,A
JP M,1C8A;(REPORT C) this is a jump if
;bit 6 of FLAGS was set
POP HL
RET NC
PUSH HL ;puts return address back
CALL 2BF1;(STK FETCH)
LD H,D
LD L,E
DEC C
RET M ;if bit 7 of C is set
ADD HL,BC
SET 7,(HL)
RET
This makes sense, but I wouldn't recommend calling it as
a USR routine without much thought.
1988 EACH STMT (3 bytes, each 23h INC HL)
386C LAST (491h/1169d bytes; in 128K Spectrums these
contain several new routines)

"up arrow" character (5E) see also commands, functions


and operators, KEYBOARD SCANNING, 026A symbol code
table

911
In this index, replaced by "**"; the word processor
cannot print the "up arrow"! But it is often so represented in
other BASICs.
The H key with symbol shift produces the operator "up
arrow"; it must be both preceded and followed by a numeric
expression.
The effect of the operator X**Y is to return X to the
power Y; X must be positive, even for eg X**2.
Special cases:
X**0 = 1 provided X isn't negative, even if X = 0
0**Y = 0 provided Y is positive
X**(1/Y) = the Y'th root of X
X**(-Y) = 1/(X**Y) provided X isn't zero
These are all standard mathematical identities; (-X)**0
is usually accepted as giving the value plus one, but it gives
an error in Spectrum BASIC.
On execution, after X has been evaluated in 268D S
DECIMAL or 26C9 S LETTER, 24FB SCANNING quickly leads
to 2723 S OPERTR. Here the token code 5E is looked up in the
two tables 2795 table of operators and 27B0 table of priorities,
giving a value 0AC6 in BC, 0A being the priority and C6 the
code of the ** operator. This is put on the machine stack in
2790 S NEXT.
When the code comes off the calculator stack in 2734 S
LOOP, C6 is converted (2773 S TIGHTER) to 06, the 3851 to-
power executive routine.

upper and lower case letters see character codes


upper display, upper print line, upper screen see DISPLAY
AREA

useful routines for m/c programs


These are subroutines which can be used in a variety of
different kinds of program. I have tried not to include
anything too fancy: experienced programmers will think of all
sorts of clever tricks.
General purpose:

912
_162C_CALL_JUMP: call it with any address in HL, it will
call that address as a subroutine. A "computed GO SUB"
command.
_16DC_INDEXER: very useful for indexed tables of all
kinds.
_19DD_DIFFER: returns with BC = HL - DE, and with HL
and DE exchanged but not otherwise affected.
_1BB0_REPORT_0 [zero]: a jump to here clears all the
"work areas", resets BASIC, and prints the "OK" message. Not a
bad way to exit from some types of m/c program. But it won't
reset the alternate HL' register.
_1E94_FIND_INT1 see 2DD5 FP TO A.
_1E99_FIND_INT2 see 2DA2 FP TO BC.
_1E9C_FIND_I_1: reports "Integer out of range" if called
with either carry or NZ.
_1F1A_FREE_MEM: from BASIC, PRINT 65536-USR 7962
shows the amount of memory still unused.
_1F54_BREAK_KEY: returns with NC if BREAK is pressed.
Invaluable for m/c programs, including debugging.
_1FC3_UNSTACK_Z: by using the flag in bit 7 of FLAGS, you
can have a choice of return addresses from any subroutine.
_226C_CO_CHANGE: can be used to change individual bits
in the byte at the address HL, using A as the setting byte and B
as a "mask". See the index entry, and under masks.
_2AEE_DE,(DE+1): call it with an address in DE, it
returns with DE holding the values in the two following
addresses. Could be used in reading a table of addresses.
_2AFD_GET_HL*DE: see 30A9 HL=HL*DE.
_2BA6_L_ENTER: call it at 2BA7 to carry out a normal LDIR
instruction. The advantage is that it checks BC for zero,
returning with zero if BC was zero, thus avoiding many a m/c
crash! On return, HL will have the old DE value unless BC was
zero.
_2C88_ALPHANUM call it with a code in A, it will return
with carry if the code is a digit, A-Z or a-z.
_2C8D_ALPHA: call it with a code in A, it will return
with carry if the code is A-Z or a-z.

913
_2D1B_NUMERIC call it with a code in A, it will return
_without carry if the code is a digit; the opposite of the last
two.
_2D7F_INT_FETCH: call it with HL holding the address of
the first byte of a small integer format, it returns with the
value of the number in DE.
_2D8E_INT_STORE: the reverse of INT FETCH: call it with a
number in DE and an address in HL, it puts DE in small
integer format in the five addresses starting at HL.
_2DA2_FP_TO_BC: takes a 2-byte number off the calculator
stack into BC. 1E99 FIND INT2 does the same, but crashes
with error reports if the number isn't an integer or is out of
range.
_2DD5_FP_TO_A: takes a small number off the calculator
stack into A. 1E94 FIND INT1 does the same, but crashes with
error reports if the number isn't an integer or is out of range.
_30A9_HL=HL*DE: returns HL = HL * DE, or with carry set
if the result is too big. 2AF4 GET HL*DE does the same, but
crashes with the "Out of memory" report if the result is too
big.
_33C0_MOVE_FP (duplicate): despite the description in the
notes, this subroutine can be used to copy any five bytes from
any address HL to any other DE.
_343E_SWAP_BYTE: call it with a number in B, it will
exchange B bytes from the address starting in HL with B bytes
from the address starting in DE.
Printing on screen:
_0010_PRINT_A_1: the simplest way to print on screen.
Prints a single character on screen at the current print
position. You must open a channel first, but then it can be
used repeatedly. It saves all the registers.
_0C0A_PO_MSG: the most useful subroutine for printing
messages on screen.
_0D6B_CLS: a call to it is just like the BASIC CLS
command. But the subroutine opens channel K; if you want to
print out after using it, you must call 1601 CHAN OPEN after it.
Cf 0DAF CL ALL.

914
_0DAF_CL_ALL: clears the screen, like 0D6B CLS, but
doesn't reform the lower screen area, which can be a
nuisance. However it leaves channel S open.
_0DD9_CL_SET: call it with a print position in BC, line
from 24d -> one in B, column from 32d -> one in C, it will
return with HL holding the address of the first pixel of that
character in the display area. See also 0E9B CL ADDR.
_0E00_CL_SCROLL: call it with a number in B, it will
scroll the whole screen up by B lines.
_0E44_CL_LINE: call it with a number one -> 18h/24d in B;
it clears the bottom B lines on the screen. A simple way to
clear the input area, among other things.
_0E88_CL_ATTR: call it with HL holding 800h plus the
display address of the first pixel of any character on the
screen, see 0E9B CL ADDR below; it returns with DE holding
the attribute address of that character. Useful for setting
attributes when non-standard methods of printing on screen
are used.
_0E9B_CL_ADDR: call it with a line number in B, from one
at the bottom of the screen to 18h/24d at the top; it will
return with HL holding the address in the display area of the
first pixel of the first character in that line. Useful for
putting marks on the screen. But see also 0DD9 CL SET.
_1601_CHAN_OPEN: essential for any printing on screen
from m/c. Call it with zero or one in A to print in the lower
screen, two in the upper, or three to print out on the ZX
printer. An output routine such as RST 10h is also necessary.
Beware! CHAN OPEN corrupts all the registers.
_1A1B_OUT_NUM_1: call it with a number in BC, it will
print BC on the screen, provided you have opened the correct
channel. But BC must be less than 10000d. 1A28 OUT NUM 2
is similar, but prints leading spaces so that the number always
takes four spaces.
_203C_PR_STRING: prints out a string starting at DE, of
length BC.
_2294_BORDER: rather than calling 2294, call 229B with
the required colour in A - but make sure it is 7 or less! If

915
there is any doubt about it, call 2297, which will give an error
if the colour is impossible.
_22E5_PLOT_SUB: call it with pixel coordinates in BC, it
plots a pixel at that position.
_238D_DR_3_PRMS: can be called at 2394 to draw arcs on
the screen. All three parameters must be on the calculator
stack: the X and Y DRAW parameters, ie the_increments
required, and Z the turning angle of the arc, Z last. The old
PLOT coordinates must be in 5C7D COORDS.
_24B7 DRAW_LINE: can similarly be used to draw straight
lines, with two increments on the stack and two coordinates
in 5C7D COORDS.
_2DC1_LOG(2**A): given the exponent byte of a FP number,
will tell you how many decimal digits are needed to print it.
Useful eg in printing tables with the decimal points aligned.
_2DE3_PRINT_FP: prints on screen a number from the
calculator stack, at the current print position. A channel must
be opened. Will print any real number, in 14-character
standard format: for integers, can well be preceded by 2D28
STACK A or 2D2B STACK BC.
Miscellaneous:
_0038_MASK_INT see 02BF KEYBOARD.
_02BF_KEYBOARD: usually unnecessary to call this, as it
is called repeatedly by the interrupt, and the final code of the
last key pressed can always be found in 5C0B LAST K, IY-50.
However, if your program runs with the interrupt disabled it
can be called direct or by RST 38h.
_03B5_BEEPER: call it with DE holding the number of
pulses required, duration times frequency, HL holding the
length of each pulse in units of four T states:
3,500,000d/(4 times frequency)
These values are usually best found by trial and error!
_196E_LINE_ADDR: call it with a BASIC line number in HL,
it returns with HL holding the start address of that line in the
program area, or the nearest above it.
_1974_LINE_AD_1: call 1975, not 1974, with a line start

916
address in HL and BASIC line number in BC for a similar result
to LINE ADDR.
_1980_CP_LINES: call it with the address of a BASIC line
number in HL and a number for comparison in BC; it returns
with zero and carry flags signalling the result of the
comparison.
Actually any hi-lo number will work.

user defined graphic codes see 5C7B UDG

USE ZERO subroutine 1CE6


Puts a zero on the calculator stack, usually because no
specific parameter has been found in the BASIC.
Input parameters: none.
Action: use the calculator to stack a zero.
Exit: RET.
Output parameters: HL, DE point to the first byte of the
last value and to the stack end.
Called from:
06C3 SA CODE
06F0 SA CODE 2
181A LIST 3
Exit from:
1CDE FETCH NUM (twice)

USE 252 2495 (247D CD PRMS1)


A label is missing on the following line due to a
misprint: it should be marked 2497 DRAW SAVE.
Jumps from:
247D CD PRMS1

USR key (C0) see also commands, functions and operators,


KEYBOARD SCANNING, 022C extended mode table (b)
The L key in E mode without shift produces the function
USR. There are two quite different functions using the same
code.
USR X$: if the function has a string argument, it must

917
be a single letter from A to U inclusive, either upper or lower
case, or an already defined user character. The function
returns the address of the first byte of the corresponding
character form in the user-defined graphics area.
USR X: if it has a numeric argument, it must be a
positive number less than 10000h/66536d. To evaluate the
function, the program jumps to the address X, executes
whatever m/c it finds there as a m/c subroutine, and when it
finds a RET resumes execution of the BASIC program with the
exit value of the BC register as the value of the function. X
may be a subroutine address in ROM, or the start of a m/c
routine prepared by the user and stored in RAM.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code C0 first to 11h, then to
ED, and adds the priority 10h/16d. Code and priority 10ED
are now pushed on to the machine stack (270D S PUSH PO)
while the expression following USR is evaluated.
When the code is taken off the stack (2734 S LOOP), bit
6 of FLAGS indicates whether the argument is string or
numeric.
For a numeric argument (2773 S TIGHTER), the operation
code is converted from ED to 2D, the calculator offset for 34B3
usr-no.
For a string argument, ED is replaced by 99h, which is
immediately (in 274C S STK LIST) converted to 19h, the offset
for 34BC usr-$.

usr-no subroutine 34B3


Called from 0028 FP CALC with the literal 2D; executes
the function USR X with numeric argument. Not called
otherwise from ROM. Fairly pointless to call it from m/c,
although it would work given the right inputs.
The actual value returned by USR X may or may not be
important: to give simple examples,
RANDOMIZE USR 0. The point of this is to reset the
computer, and in the course of doing so the computer will
"forget" all about returning a value.

918
PRINT 65536-USR 7962. Now the point is to get the
number of RAM bytes used up so far; see 1F1A FREE MEM.
Input parameters: HL points to the first byte of X, which
must be the last value on the calculator stack; it must also be
positive and less than 10000h/65536d, but needn't be an
integer
- DE points to the stack end.
Action: call 1E99 FIND INT2, which rounds X to an integer
and gets it off the stack into BC
- PUSH the return address 2D2B STACK BC
- make an indirect jump to the address X.
Exit: into the subroutine selected by the user. This will
return to STACK BC, which stacks the BC value and resets IY
in case the user-defined routine relocated it - but doesn't reset
HL' - and then returns into the calculator loop.
Output parameters: none.

USR RANGE 34D3 (34BC usr-$)


Jumps from:
34BC usr-$

USR STACK 34E4 (34BC usr-$)


This label is omitted by a misprint; it should be on the
JP 2D2B STACK BC line just before 34E7 REPORT A.
Jumps from:
34D3 USR RANGE

usr-$ subroutine 34BC


Called from 0028 FP CALC with the literal 19h; executes
the function USR X$ with string argument, by returning the
address of the first byte of the udg character form
corresponding to X$. Not called otherwise from ROM. Can be
called from m/c, direct or through the calculator.
There are only 15h/21d udgs, and each has eight bytes.
The required address is eight times the serial number of the
named udg, starting from zero, plus the address in 5C7B UDG.
A user-defined graphic (udg) will have a code in the

919
range 90h -> A4h, so subtracting 90h gives the required serial
00 -> 14h, which is now multiplied by eight.
A letter code will be either 41h -> 55h (A-U) or 61h ->
75h (a-u). These are decremented by one and multiplied by 8
in 16-bit arithmetic, ie the result is decremented by any
multiple of 100h/256d:
less one times eight mod 100h
41h->55h --> 40h->54h --> 200h->2A0h --> 00h->A0h
61h->75h --> 60h->74h --> 300h->3A0h --> 00h->A0h

90h->A4h --> 00h->14h --> 00h->A0h


Input parameters: none
- the string parameters of X$ must be the last value on
the calculator stack; X$ must be a single letter A to U or a to
u, or a udg character.
Action: call 2BF1 STK FETCH, which puts the start address
of the string in DE and its length in BC
- if the length isn't one report "Invalid argument"
- get the character code from the start address
- call 2C8D ALPHA, which checks for an alphabetic letter
- jump on to USR RANGE if the character is a letter
- (it isn't a letter) take away 90h
- if this makes a carry report "Invalid argument"; the
code is before the start of the udgs
- if the result is more than 14h/20d report "Invalid
argument"; the code is after the end of the udgs
- increment the result; to cancel the decrement which
follows.
_34D3_USR_RANGE: decrement the letter code
- double it three times; multiply by eight
- if the result is more than A0h report "Invalid
argument"; letters VWXYZ aren't accepted
- add the resulting offset to the lo byte of the address
in 5C7B UDG
- if there is no carry jump on to USR STACK
- (carry from the lo byte) increment the hi byte; the
result is the start address of the udg character form.

920
_34E4_USR_STACK (the label is omitted by a misprint; it
should be on the last line before 34E7 REPORT A): exit.
Exit: into 2D2B STACK BC, which puts the address on the
calculator stack.
Output parameters: BC holds the address required.

921
V

VAL key (B0) see also VAL$ key, commands, functions and
operators, KEYBOARD SCANNING, 022C extended code table
(b)
The J key in E mode without shift produces the function
VAL; it requires one string operand X$, which must be, or be
equivalent to, a numeric expression enclosed in quotes. The
value of the function is the value of the numeric expression.
On execution, 24FB SCANNING quickly leads to 26DF S
NEGATE. This converts the key code B0 first to 01, then to DD,
then to 5D, and adds the priority 10h/16d. Code and priority
105D are now pushed on to the machine stack (270D S PUSH
PO) while the string following VAL is evaluated.
When the code is taken off the stack (2734 S LOOP), it
is converted (2773 S TIGHTER) from 5D to 1D, the calculator
offset for 35DE val.
VAL is a rather sophisticated function, which many
Spectrum programmers will never use in their lives. Two
examples of its use:
a) in a program to plot curves: the function to be
plotted is input as a string s$, eg "SIN X": VAL s$ is evaluated
for a range of values X and the curve drawn, with s$ used as a
label.
b) a string can be used as a compact number array: d$ =
"312831303130313130313031", the numbers of days in each of
the
12 months, takes up much less space in memory than d(12)
with the elements 31, 28, etc. VAL d$(2*m-1 TO 2*m) is then
the same as d(m).

val subroutine 35DE


Called from 0028 FP CALC; the executive routine of the

922
VAL X$ and VAL$ X$ functions. Not otherwise called from
ROM, and unlikely to be useful in m/c programs.
Takes the string X$ and evaluates it as an expression;
returns the value, as a number for VAL X$ and as a string for
VAL$ X$.
Input parameters: the string parameters of X$ on the
calculator stack
- HL and DE pointing to its first byte and to the stack
end respectively
- B holds the literal, 1Dh for VAL and 18h for VAL$.
Action: save the current BASIC pointer in 5C5D CH ADD
- add E3h to the literal; this sets carry for VAL but
not for VAL$
- make a "$" flag by SBC A; FFh for VAL or 00 for VAL$
- call 2BF1 STK FETCH to get the string parameters of X$
off the calculator stack
- call 0030 BC SPACES to make a space of one more than
the string length in the work space
- make a pointer to the start of X$
- save the pointer to the start of the work space in
5C5D CH ADD
- copy X$ into the work space, with an extra byte at the
end
- put a newline in this extra byte
- call 24FB SCANNING with the flag for syntax checking; it
will report an error if X$ doesn't evaluate as an expression
- check that the character reached by syntax checking is
the newline
- if not jump on to V RPORT C to report "Nonsense in
BASIC"
- XOR FLAGS with the $ flag
- AND the result with 01000000b/40h; SCANNING will
leave bit 6 of FLAGS zero for a numeric result, one for a string
result, and this must match the $ flag. If it does, XOR produces
a zero bit 6 and the AND produces a zero byte.
_360C_V_RPORT_C: report "Nonsense in BASIC" on any
non-zero result

923
- point 5C5D CH ADD to the start of X$ again
- call 24FB SCANNING with the flag set for run time; it
evaluates X$ as an expression and puts the result on the
calculator stack
- recover the old value of 5C5D CH ADD and restore it.
Exit: into 35BF STK PNTRS, which sets HL and DE to the
first byte of the last value and to the stack end respectively.
Output parameters: none
- the numeric or string value obtained by evaluating the
expression in X$ is now on the calculator stack.

VAL FET 1 subroutine 1C56


The executive routines for LET, READ and INPUT don't
call 24FB SCANNING directly to evaluate the expression
offered; this subroutine is called instead to make sure the
string/numeric status of the result matches what is required,
eg to reject
LET number = "string".
There are two entry points, VAL FET 1 for inputs in the
editing area and VAL FET 2 for inputs in the work space:
because the "status required" flag is in FLAGS for editing area
inputs and in FLAGX for work space inputs, in bit 6 in either
case.
Input parameters: none.
Action: read FLAGS.
_1C59_VAL_FET_2 (entry here with FLAGX already read):
- stack the flag byte; it shows the "status required"
- call 24FB SCANNING to evaluate the expression
- XOR the result flag now in FLAGS with the "status
required" flag from the stack; this zeroes bit 6 if the bit 6s
match
- AND the result with 01000000b/40h, which zeroes the
whole byte if the bit 6s match
- if there is a mismatch report "Nonsense in BASIC".
Exit: RET for syntax checking
- into 2AFF LET in run time, to put the value from the
calculator stack in the variable it is assigned to.

924
Output parameters: none
- the evaluation result is on the calculator stack.
Called from:
1C4E CLASS 02
1E0A READ 1

VAL FET 2 subroutine 1C59


See VAL FET 1 above.
Called from:
21B9 IN ASSIGN

VALID 371C (3713 ln)


Jumps from:
3713 ln

valid expression see 24FB SCANNING

valid keystroke see 031E K TEST

value of BASIC variable see variables

VALUE of FOR ... NEXT loop see FOR ... NEXT loops

VAL$ key (AE) see also commands, functions and operators,


KEYBOARD SCANNING, 0246 extended mode table (c)
The J key in E mode with either shift produces the
function VAL$; it requires one string operand X$, which must
be a string expression enclosed in an extra pair of quotes,
such as
VAL$ """hello"" + CHR$ 32 + ""there""" or
VAL$ """Result = "" + STR$ SIN X".
The value of the function is the value of the string
expression, eg in these cases "hello there" and "Result =
0.7183402" or whatever.
On execution, 24FB SCANNING quickly leads to 26DF S

925
NEGATE. This puts the priority and code 1018 on the machine
stack (270D S PUSH PO) while the expression following VAL$
is evaluated.
When the code is taken off the stack (274C S STK LST),
it is used as the calculator offset for 35DE val$, the same as
35DE val, see above.
The function VAL$ is even more recondite than the
function VAL: I don't think I personally have ever used it, or
seen in used, in a BASIC program. One could envisage a rather
fancy program handling functions, in which all functions are
input as strings and eg a function h(x) might be constructed
by concatenating f(x) and g(x):
h$ = VAL$ "f$ + ""* SIN "" + g$"
These are deep waters, Watson.

VAR A 1 1C22 (28B2 LOOK VARS)


Exit from:
1C1F CLASS 01
1C6C CLASS 04

VAR A 2 1C30 (28B2 LOOK VARS)


Exit from:
1C22 VAR A 1 (28B2 LOOK VARS)

VAR A 3 1C46 (28B2 LOOK VARS)


Jumps from:
1C22 VAR A 1
1C30 VAR A 2

variable in assignment see variables, LET key

VARIABLE IN ASSIGNMENT SUBROUTINE see 28B2 LOOK


VARS

variables (BASIC) see also 28B2 LOOK VARS, variables (DEF


FN)
When the scanning of BASIC expressions encounters a

926
letter of the alphabet, not enclosed in quotation marks, at the
start of a new sub-expression, it is read by 26C9 S LETTER as
the first byte of a_variable_name.
A variable name must begin with a letter, and in the
BASIC program area the letter is represented by its normal
character code. If this is followed by "(" (28B2 LOOK VARS) the
variable is a_numeric_array; if by "$(", a_string_array; if by "$"
without "(", a simple_string_variable; otherwise it is a
_numeric_variable. If the letter is followed by another
alphanumeric character it is a_long_name; otherwise it is a
_short_name, which can also be a loop control variable like X
in
FOR X = Y TO Z. Variables which aren't arrays are often called
_simple_variables.
A variable is an expression to which a_value can be
_assigned, by LET, INPUT or READ, or in the case of arrays,
also by DIM and LOAD ... DATA; when the assignment is
made, the name of the variable together with the value
assigned to it is retained in the variables area. Whenever that
name is encountered in executing the BASIC, the variable
name is found by 28B2 LOOK VARS and following, and its
assigned value used in evaluating the expression containing
the variable. If no value has been assigned yet, the name
won't be found in the variables area, and the executive
routine prints the message "Variable not
found".
The value of the variable may be a number or a string,
or an array of numbers or strings; but arrays, see separate
entry in this index, differ from simple variables in that values
cannot be assigned to them till they have been established in
the variables area by a DIM statement. The values in the
variables area are always either actual numbers in FP form or
actual character codes, never string parameters or any cross-
reference to other variables etc.
In the variables area the "$" and "(" aren't reproduced;
the initial_variable_letter is reproduced, always in lower case,
but with its first three bits carrying various flags to show

927
what_type of variable it is. You will find a clear exposition of
this on pages 166-168 of the old Spectrum handbook, pages
144-146 of the PLus 2 handbook. Summarizing:
+-----+---------------+----------------------------------
|1st 3|Type |Initial letter followed by
|bits | |
+=====+===============+==================================
|010 |simple string |length (2 bytes), string text
+-----+---------------+----------------------------------
|011 |simple numeric,|5-byte FP number
| |1-letter name |
+-----+---------------+----------------------------------
|100 |numeric array |length (excluding name and 2-byte
| | |length), no of dimensions (1
| | |byte); size (2 bytes) of each;
| | |elements (5 bytes each)
+-----+---------------+----------------------------------
|101 |simple numeric,|other characters of name, last
| |long name |with bit 7 set; 5-byte FP number
+-----+---------------+----------------------------------
|110 |string array |length, no of dimensions (1 byte);
| | |size (2 bytes) of each dimension;
| | |elements (1 byte each)
+-----+---------------+----------------------------------
|111 |loop control |VALUE, LIMIT, STEP (5 bytes each;
| | |line no (2 bytes) and statement
| | |no (1 byte) of looping line
+-----+---------------+----------------------------------
Only arrays and string variables contain a record of
their_length, in the two bytes after the variable letter; it is
the length exclusive of the first three bytes. Loop control
variables are always 12h/18d bytes, other numeric variables 5
bytes, plus any letters of the name after the first, the last
being marked with bit 7 set. These figures are exclusive of the
initial variable letter.
String arrays are essentially arrays of single letters.
In some respects, eg for slicing purposes, simple strings are
handled like one-dimension arrays; but one-dimension arrays
have a fixed length, simple strings (zero-dimension arrays?)
don't.
Another way of looking at this is to say that string
arrays, including one-dimensional string arrays, are arrays of
strings of fixed length, but simple strings have a length that
can be changed; the last dimension of a string array element
represents a slicing subscript, and slicing subscripts can also
be used with simple strings. Both descriptions are equally

928
valid, and it is a matter of convenience which one is used in
any particular context.
Only simple numeric variables which aren't loop control
variables may have a long name; in long names, spaces and
the upper/lower case distinction are ignored:
LET long name = PI: PRINT LONGNAME
will print 3.14159...
An oddity is that colour controls may be included in
long variable names: LET followed by E-mode 6 and then
typing "yo", without the quotes, will display the variable
name "yo" on yellow PAPER in the BASIC listing; better to
follow with E-mode 7, or everything else goes yellow as well.
Like spaces, this is ignored in matching variable names: you
can colour-code your BASIC to your heart's content without
confusing the execution, provided you confine the colour
codes to the beginning and end
of the variable name; see the minor error described under
28B2 LOOK VARS.
A_new_variable is one to which no value has been
assigned yet.
A_declared_variable or_old_variable is one which is
already in the variables area with a value; but if a new value
is assigned the old value gets overwritten, or in the case of a
simple string, the old value is deleted after the new one has
been written.
The_destination_address is the address in the variables
area to which the new variable or the new value has to be
written.
A_variable_in_assignment is the one mentioned on the
left side of a LET or FOR statement, or in a READ or INPUT
statement, to be given a value from the right side or from a
DATA statement or the keyboard input. If this is also a variable
it is called a_source_variable.
_FOR_..._NEXT_control_variables, as can be seen from the
summary above, are a special category, not handled by 26C9 S
LETTER like the other variable names but by the 1D93 FOR
and 1DAB NEXT subroutines; see the index entries.

929
The_variables_area is the part of RAM just above the
BASIC program area, and is saved with the program by a SAVE
command; its start address is kept in 5C4B VARS, and it
extends up to the editing area starting at the address kept in
5C59 E LINE. It always ends with an 80-byte, which is used to
signal the end of the area, eg at 2900 V EACH in 28B2 LOOK
VARS. On start-up or NEW (1219 RAM SET) or CLEAR (1EB7
CLEAR 1) it is reduced to the 80-byte alone, but every time a
value is given to a new variable or an array is declared by DIM,
the new variable
is inserted before the 80-byte, with everything from 5C59 E
LINE
upwards being moved up.
Introduction wide range of types can be handled
0652 SA DATA array found in vbles area
0670 REPORT 2 "Vble not found"
0672 SA V OLD finds length in vbles area
073A SA TYPE 0 header stores program length and
program + vbles length
0808 LD CONTRL loads program and vbles
0819 LD CONT 1 checks length of program + vbles
082E LD DATA clears old array from vbles area
084C LD DATA 1 new array put at 80-byte
0873 LD PROG old program and vbles reclaimed, new
loaded
08B6 ME CONTRL new vbles must be merged with old
08F0 ME VAR LP new vbles put in at end, or overwrite
old
08F9 ME OLD VP checks each old against each new vble
0901 ME OLD V1 searches old vble names for match
0909 ME OLD V2 long names matched in each character
0912 ME OLD V3 loop to check long names
0921 ME VAR L1 replace old vble
0923 ME VAR L2 add new vble
092C ME ENTER reclaims old vble
093E ME ENT 1 prepare to add new vble
0955 ME ENT 2 makes room for new

930
0958 ME ENT 3 moves new vble from work space
1C1F CLASS 01 used by vble handling commands
1C22 VAR A 1 checks for new vble
1C2E REPORT 2 "Variable not found"
1C30 VAR A 2 deals with old vbles
1C4C VAR A 3 sets STRLEN/DEST for all types of vble
1C4E CLASS 02 assigns value to vble
1C56 VAL FET 1 gets value to be assigned
1C59 VAL FET 2 checks for mismatch of string/numeric
1C6C CLASS 04 looks in vbles area for loop control vble
1D16 F REORDER sets up or uses loop control vble
1D34 F L&S puts LIMIT, STEP, loop line no in vble
1D64 F LOOP looks for matching NEXT vble
1DAB NEXT checks VALUE + STEP against LIMIT and
adjusts
loop control vble
1EAC CLEAR also clears vbles area
1EB7 CLEAR 1 reduce vbles area to 1 byte
2089 INPUT gives value to vble from keyboard
20D8 IN ITEM 2 finds destination for INPUT...LINE value
20ED IN ITEM 3 finds destination for ordinary value
2161 IN VAR 4 resets svs in preparation for assignment
2174 IN VAR 5 makes assignment
219B IN VAR 6 makes assignment for INPUT ... LINE
21B9 IN ASSIGN assigns value to vble
26C9 S LETTER gets vble value on to calculator stack
28B2 LOOK VARS to find vble in vbles area
28D4 V CHAR to find long names
28EF V RUN/SYN prepare to search
28FD V RUN searching in run-time
2900 V EACH check each 1st letter
2912 V MATCHES check each letter of long name
2913 V SPACES skip spaces
2929 V GET PTR no match
292A V NEXT next vble
2932 V 80 BYTE end reached, match not found
293E V FOUND 1 match was found

931
2943 V PASS skip over long name
2948 V END summarizes outcome of vble search
2AFF LET assigns value to vble
2B0B L EACH CH reads long name
2B0C L NO SP ignores spaces & colour codes
2B1F L TEST CH reads string vble names
2B29 L SPACES makes room for new vble in vbles area
2B3E L CHAR copies chars of long name
2B4F L SINGLE puts vble letter in vbles area
2B59 L NUMERIC add number to vbles area
2B66 L EXISTS old vbles: check for string/numeric
2B72 L DELETE$ make space for new string vble in
work space
2BA3 L IN W/S ready to copy new string to vbles area
2BA6 L ENTER copy number or string to vbles area
2BAF L ADD$ add complete simple string to vbles area
2BC0 L NEW$ prepare to add new simple string
2BC6 L STRING make room and copy string from work
space
2BEA L FIRST insertg new vble letter
2C02 DIM puts zero/blank array in vbles area
2C15 D RUN reclaim old array after DIM command
2C1F D LETTER get parameters of new array
2C2E D NO LOOP makes room in vbles area for new array
3405 LOC MEM operates on loop control vble as memory

variables (DEF FN)


A DEF FN statement usually contains_dummy_variables,
eg
x, y, z$ in
DEF FN q(x,y,z$)=x*PI+y**CODE z$
The variables x, y and z$ may or may not duplicate
others in the variables area, they aren't looked for there:
when the FN expressions are evaluated their values are sought
for within the DEF FN statement, and only if they aren't found
there is any search made in the variables area.
In the notes on 1F6A DEF FN 1 and following, however,

932
"the variable of the function" means something quite
different, which would be better called "the letter [or name] of
the function: "q" in the example above.
1F6A DEF FN 1 name checked for string/numeric status
1F7D DEF FN 2 name must be followed by "$" or "("
1FA6 DEF FN 6 function name must match in status the
expression as evaluated

VARS system variable 5C4B


Bytes: 2.
Holds the address of the start of the variables area,
see variables above.
One of the fourteen system pointers checked and reset by
1664 POINTERS each time space is made or reclaimed in RAM.
Written by:
0873 LD PROG
1219 RAM SET
166B PTR NEXT
Read by:
073A SA TYPE 0
08F0 ME VAR LP
166B PTR NEXT
1EB7 CLEAR 1
28FD V RUN
Rem:
1664 POINTERS the first of the system pointers

V CHAR 28D4 (28B2 LOOK VARS)


Jumps from:
auto

V EACH 2900 (28B2 LOOK VARS)


Jumps from:
292A V NEXT

V END 294B (28B2 LOOK VARS)


Exit from 28B2 LOOK VARS through one of:

933
2934 V SYNTAX
2943 V PASS

VERIFY key (D6) see also commands, functions and


operators,
KEYBOARD SCANNING, 0246 extended mode key table (c)
The R key in E mode with either shift produces the
command VERIFY. The "VERIFY statement", exactly as for
LOAD, must include a filename and may also include
modifiers such as CODE.
The command is read by 1B29 STMT L 1 referring through
the syntax offset table 1A48 to the syntax parameter table
1A7A.
1AE1 P VERIFY causes a jump via 1CDB CLASS 0B to the
executive routine for all the cassette handling commands,
0605 SAVE ETC.
The routine distinguishes between SAVE, LOAD, VERIFY and
MERGE by looking at 5C74 T ADDR, which was used to step
through the 1A7A table: its low byte reads one more than the
low byte of the
"P" label, E2 in the case of VERIFY.
04C2 SA BYTES outline of SAVE, LOAD, VERIFY
0556 LD BYTES flag is NC for verifying
058F LD SYNC header bytes verified
05A9 LD LOOP jump forward to verify
05BD LD VERIFY tests tape byte against memory byte
05E3 LD EDGE 2 important part of verifying operation
0605 SAVE ETC flag 02 signals verifying
0629 SA BLANK null name allowed in verifying
0652 SA DATA can't verify "new array"
075A SA ALL checks header from tape against work space
07AD LD CH PR same routine for VERIFY, LOAD CODE/
SCREEN$
07CB VR CONTRL check length of data block
07F4 VR CONT 2 set flag to signal verification
0802 LD BLOCK handles verification error

934
VERIFY CONTROL ROUTINE see 07CB VR CONTRL (in 0605
SAVE ETC)

V FOUND 1 293E (28B2 LOOK VARS)


Jumps from:
2913 V SPACES

V FOUND 2 293F (28B2 LOOK VARS)


Jumps from:
2900 V EACH (twice)
Rems:
2943 V PASS duplicates skip of long names

V GET PTR 2929 (28B2 LOOK VARS)


Jumps from:
2913 V SPACES

V MATCHES 2912 (28B2 LOOK VARS)


Jumps from:
2913 V SPACES

V NEXT 292A (28B2 LOOK VARS)


Jumps from:
2900 V EACH

V PASS 2943 (28B2 LOOK VARS)


Jumps from:
2934 V SYNTAX
auto

VR CONTRL 07CB (0605 SAVE ETC)


Jumps from:
07AD LD CH PR

VR CONT 1 07E9 (0605 SAVE ETC)


Jumps from:
07CB VR CONTRL (twice)

935
VR CONT 2 07F4 (0605 SAVE ETC)
Jumps from:
07E9 VR CONT 1

VR CONT 3 0800 (0605 SAVE ETC)


Jumps from:
07F4 VR CONT 2

V RPORT C 360C (35DE val)


Jumps from:
35DE val

V RUN 28FD (28B2 LOOK VARS)


Jumps from:
28EF V RUN/SYN

V RUN/SYN 28EF (28B2 LOOK VARS)


Jumps from:
28B2 LOOK VARS
28D4 V CHAR
28E3 V TEST FN
2951 STK F ARG
296B SFA CP/VR

V SPACES 2913 (28B2 LOOK VARS)


Jumps from:
auto

V STR VAR 28DE (28B2 LOOK VARS)


Jumps from:
28B2 LOOK VARS

V SYNTAX 2934 (28B2 LOOK VARS)


Jumps from:
28EF R RUN/SYN

936
V TEST FN 28E3 (28B2 LOOK VARS)
Jumps from:
28B2 LOOK VARS

V 80 BYTE 2932 (28B2 LOOK VARS)


Jumps from:
2900 V EACH

937
W

waiting period see time period

WAIT KEY subroutine 15D4


Much simpler than it looks. Apart from setting a flag if
required, merely makes repeated calls to the general-purpose
input routine 15E6 INPUT AD till it finds the carry flag set.
INPUT AD jumps into whatever subroutine is addressed
on the input side of the current channel: in normal working,
this is always 10A8 KEY INPUT, the input routine of channel K,
the only one which has an operating input routine. KEY
INPUT will return with carry only if a new key has been
pressed, so the ROM execution remains stuck in WAIT KEY
until this happens.
The Spectrum in fact spends at least 99% of its working
life circling endlessly round the loop in 15DE WAIT KEY 1!
Input parameters: none.
Action: if TV FLAG bit 5 is set jump on to WAIT KEY 1;
the flag signals "clear lower screen after the next keystroke"
- (don't clear) set TV FLAG bit 3 to signal "copy edit/
input line to lower screen"; it is always copied on first entry
to WAIT KEY except just after one of the "Press any key"
prompts, see under TV FLAG bit 3, when there is nothing to
copy
_15DE_WAIT_KEY_1: call the channel input routine via 15E6
INPUT AD
- if it returns with carry, return; a meaningful new key
has been pressed, or an acceptable input from some other
channel
- (it returned with no carry) if it also returned with
zero, loop back to WAIT KEY 1; no-key or no input
- (it returned with no carry and with NZ) report "End of

938
file"; impossible for keyboard inputs, but assumed possible for
inputs handled by other channels set up by peripherals.
Exit: RET, from WAIT KEY1.
Output parameters: none of its own, see 10A8 KEY INPUT
for output from the keyboard.
Called from:
0970 SA CONTRL
0C88 PO SCR 2
0F38 ED LOOP (twice)
0F6C ED CONTRL
101E ED IGNORE (twice)

WAIT KEY1 15DE (15D4 WAIT KEY)


Exit from:
15D4 WAIT KEY
auto

work areas
As 16B0 SET MIN affects the editing area, the work space
and the calculator stack, this is presumably what the notes
mean at the two references below.
12A9 MAIN 1 gives them minimum configurations
1313 MAIN G clears them

WORKSP system variable 5C61


Bytes: 2.
Holds the address of the first byte of the work space,
see below. One of the fourteen system pointers reset by 1664
POINTERS each time space is made or reclaimed in RAM.
Written by:
1219 RAM SET
166B PTR NEXT
169E RESERVE
16B0 SET MIN
Read by:
0030 BC SPACES
1190 SET HL

939
1195 SET DE
155D MAIN ADD
157D MAIN ADD 1
166B PTR NEXT
16BF SET WORK
1B8A LINE RUN
2148 IN VAR 2
219B IN VAR 6
21B9 IN ASSIGN
Rems:
1031 ED EDGE start address for INPUT is WORKSP

work space
The work space is an area of RAM defined by a bottom
address held in 5C61 WORKSP, up to the bottom of the
calculator stack held in 5C63 STKBOT, used for various
purposes; really it is where things are put that aren't meant to
be preserved very long and have no other special place to go.
Nothing can be kept there very long: the work space is cleared
- each time ENTER is hit in the editing routines, by the
call to 1650 SET MIN from 12A9 MAIN 1
- each time a report message is printed by the call to
1650 SET MIN from 1313 MAIN G
- each time a statement is executed by the call to 16BF
SET WORK from 1B29 STMT L 1
- and on a few other occasions.
The main uses of the work space are:
- compilation of the header for SAVE/LOAD commands
- on a MERGE command, the whole program and variables
block is first loaded into the work space, and then fitted into
the existing program and variables as appropriate
- INPUTs are compiled in the work space. FLAGX bit 5
being on signals "input mode", which requires inputs to be
sent to the work space
- some strings/slices are compiled in the work space, and
copied to the variables area if they are to be preserved
- for the VAL function, an expression is copied to the

940
work space and evaluated from there; similarly, for the STR$
function, the value of an expression is output to the work
space and can be copied from there.
0030 BC SPACES makes room in work space
0605 SAVE ETC header is constructed in work space
0621 SA SPACE makes space for header
064B SA NAME name transferred to work space
0672 SA V OLD length of array block to work space
06F9 SA CODE 4 start/ length of code block to work space
073A SA TYPE 6 program/variables params to work space
075A SA ALL tape header will also go into work space
08B6 ME CONTRL whole data block goes into work space
0958 ME ENT 3 new items moved one by one from work
space
1097 CLEAR SP clears work space (or editing area)
1190 SET HL marks first/last address if FLAGX bit 5 set
169E RESERVE makes room in work space
16B0 SET MIN clears work space etc
16BF SET WORK clears work space etc
1B29 STMT L 1 clears work space
20FA IN PROMPT prompt for INPUT prepared in work
space
219B IN VAR 6 input measured in work space
21B9 IN ASSIGN expression read from work space
255D S SC ROWS one character put in work space
25BE S Q AGAIN work space prepared for string
25CB S Q COPY copy string to work space
2634 S INKEY$ one character put in work space
28B2 LOOK VARS variable name in work space or
program
2B72 L DELETE$ string slices compiled in work space
2B9B L LENGTH new string copied to work space
2BA6 L ENTER string moved from work space to vbles
area
359C str-add two strings put end to end in work space
35C9 chr$ single character put in work space
35DE val expression evaluated from work space

941
361F str$ expression output to work space
3645 read-in one character put in work space

wrong number
In "ROM Disassembled" there are several notes on the
difficult number -65536d, pointing out among other things at
least two mistakes in the ROM over handling it: see
Introduction, 3014 addition, 3214 truncate and the note on
page
229. A few additional comments may be helpful:
65536d = 1,0000h
= 1,0000,0000,0000,0000b
= 2**10h/2**16d
The full FP form of +65536d is 91 00 00 00 00, with an
exponent and mantissa, corrected to their "true" forms, of 11h
and 80 00 00 00 respectively: 65536d = 2**11h times one half.
So far so good: 65536d is just like any other power of two.
65536d can't be put in small integer form: the largest
number which can be so expressed is 65535d, 00 00 FF FF 00
in small integer form. One more than this, in small integer
form, would come out as 00 00 00 00 00, which means zero
_Minus 65536d also produces no problems so long as it is
in full FP form: 91 80 00 00 00 is again just like any other
negative power of two.
So what is the problem? If the programmers had been
content to exclude minus 65536d from small integer format
like plus 65536d, there probably wouldn't be any. But this
leaves one five-byte number form without an interpretation:
00 FF 00 00 00.
The 00 in its first byte shows it is a small integer, the FF in
its sign byte shows it is negative, the third and fourth bytes
show it is zero; minus zero is the same as plus zero, and
indeed the ROM corrects minus zero to plus zero after any
arithmetic
operation in 315E SKIP ZERO, see under 3155 TEST NORM.
At some point in the development of the Spectrum ROM

942
someone seems to have had the bright idea that -65536d
needn't be excluded from small integer format, it could be
represented by 00 FF 00 00 00: -0000h being made to stand
for -10000h. But this idea wasn't properly implemented, and
00 FF 00 00 00 is the real "wrong number".
The wrong number 00 FF 00 00 00 can't be produced
directly by reading decimal numbers from the BASIC, because
such numbers are always read as absolute values, in 2C9B
DEC TO FP, called from 268D S DECIMAL: 65536d couldn't be
put on the calculator stack as a small integer and 346E negate
can't change a full FP number to a small integer.
In addition, which includes subtraction, the wrong
number can be produced by adding two small integers, but
only if both are negative. Take the example in the notes:
compare the process in 3014 addition of adding
-65000 + -536 = -65536d ie 00 FF 18 02 + 00 FF E8 FD
with
-65000 + -535 = -65535d ie 00 FF 18 02 + 00 FF E9 FD
and
+65000 + +536 = +65536d ie 00 00 E8 FD + 00 00 18 02

First case, -65000 + -536 = -65536d:


ADD HL,BC: 0218 + FDE8 = 0000 and carry
ADC A, (HL) (the two sign bytes):
FF + FF + 1 = FF and carry
RRCA: FF and carry
ADC A,00: 00 and carry
SBC A,A: FF.

Second case, -65000 + -535 = -65535d:


ADD HL,BC: 0218 + FDE9 = 0001 and carry
ADC A, (HL) (the two sign bytes):
FF + FF + 1 = FF and carry
RRCA: FF and carry
ADC A,00: 00 and carry
SBC A,A: FF.

943
Third case, +65000 + +536 = +65536d:
ADD HL,BC: FDE8 + 0218 = 0000 and carry
ADC A, (HL) (the two sign bytes):
00 + 00 + 1 = 01, no carry
RRCA: 00 and carry
ADC A,00: 01, no carry
As the result isn't zero, this third case jumps on to
ADDN OFLW.
Thus the wrong number can be produced by addition/
subtraction: if it is to be excluded, it seems to me the
simplest way would be to jump on to ADDN OFLW if ADD
HL,BC produces zero with carry - a lot simpler than the
alternative coding suggested in the Appendix on page 230 of
the notes.
30CA multiply and 31AF division never produce the
wrong number as a result, because such a result of either
would always be in full FP form.
The only other function which can actually produce the
wrong number seems to be INT, as in PRINT INT (-65535.5).
This is the result of the misconceived manoeuvres of 3221 T
GR ZERO in 3214 truncate; a self-inflicted wound, because as
the notes point out this section of the ROM achieves nothing
except to rewrite the full FP form of -65536d as the wrong
number:
_3221_T_GR_ZERO (see index entry on 3214 truncate; X has
an exponent more than 80h, which is in the A register, and its
address is in HL): if the exponent isn't 91h precisely jump on
to T SMALL; now X is between 65536d (91 00 00 00 00) and
131,071.999d (91 0F FF FF FF), or if X is negative, -X is
between these values. All bits of the last two mantissa bytes
except the hi bit are in the fractional part of X
- move the pointer on the the third mantissa byte
- make a mask 10000000b/80h to separate the hi bit of
this byte; the only one in the integer part of X
- AND the mask with the third byte
- OR this hi bit with the second mantissa byte
- if this makes NZ jump on to T FIRST; they aren't all

944
zero bits
- (the second mantissa byte and the hi bit of the third
are all zero) XOR the first mantissa byte with 80h; making NZ
if it isn't 80h precisely.
_3233_T_FIRST: move the pointer back to the exponent
byte
-if X isn't 91 80 00 XX XX, with the hi bit of the XXs
being zero, jump on to T EXPNENT, see 3214 truncate; ie with
any number which doesn't truncate to -65536d precisely
- (the result is to be 00 FF 00 00 00) set the first two
bytes to 00 FF
- jump on to NIL BYTES with 24d in the counter to zero
the last three bytes.
The wrong number could be excluded by just omitting
this part of the routine, and if the change suggested above
were made in 3014 addition the wrong number could never
arise at all.
Much more would need to be done to save the wrong
number as a representation of -65536d:
- the wrong number isn't printed correctly: 2DE3 PRINT
FP recognises it as a negative number, and when 2DF2 PF
NEGTVE calls 346A abs it is recognised as a negative small
integer, but 3483 INT CASE changes it to zero. As the test for
zero in PRINT FP has been bypassed, it isn't printed as zero
but as the smallest possible number -1E-38
- if the wrong number is in the_input of an arithmetic
operation it is evaluated as zero. In addition/subtraction and i
n division, eg
PRINT -65000 - 536 + X
PRINT (-65000-536)/X
3293 RE ST TWO restacks it as zero; in 30CA multiply, eg
PRINT (-65000-536)*X
2D7F INT FETCH in 30CA multiply makes it minus zero, but
the sign is corrected in the exit routine, so it becomes zero
again.
In summary:

945
- the use of the wrong number 00 FF 00 00 00 for
-65536d was a bad idea
- it can only be produced by 3014 addition and 3214
truncate, and it would be a simple matter to trap and exclude
it from both
- to make sense of it would require rewriting in 2D7F INT
FETCH, 3483 INT CASE, 3297 RE STACK, and probably other
routines as well.

946
X

xEm format of numbers see E-format numbers

XISO 385D (3851 to-power)


Jumps from:
3851 to-power

X LARGE 326D (3214 truncate)


Exit from:
323F T SMALL

X NEG 36B7 (36AF int)


Jumps from:
36AF int

XOR/AND/XOR see masks

X PTR system variable 5C5F see also errors


Bytes: 2.
The error address pointer; holds the address in the
editing area or the work space at which the error cursor, a
flashing "?", is to be printed.
Its hi byte is zeroed whenever an error report is
printed out (1313 MAIN G) and on completion of any edit line
or input, whether or not there is an error (117E ED C END);
this makes it impossible for a match to be found with any
address in the editing area or workspace.
One of the fourteen system pointers reset by 1664
POINTERS each time space is made or reclaimed in RAM. Also
used as a temporary "free parking" place: for saving IX while
space in RAM is reclaimed at 082E LD DATA and following;
and for saving 5C5D CH ADD while a READ is being executed
at 1DED READ.

947
Not to be confused with 5C3D ERR SP, the error stack
pointer. Both are called in the notes "the error pointer", which
is not helpful.
Written by:
0008 ERROR 1
082E LD DATA
0873 LD PROG
092C ME ENTER
093E ME ENT 1
117E ED C END
1313 MAIN G
166B PTR NEXT
1DED READ
1E0A READ 1
2174 IN VAR 5 (twice)
Read by:
082E LD DATA
0873 LD PROG
092C ME ENTER
0958 ME ENT 3
166B PTR NEXT
1894 OUT LINE 4
1E0A READ 1
2174 IN VAR 5
Rems:
1DEC READ 3 used as free parking

948
Y

YNEG 37A8 (3783 get-argt)


Jumps from:
37A1 ZPLUS

949
Z

zero key see character codes

ZERO OR ONE SUBROUTINE see 350B FP 0/1

ZERO RSLT 315D (30CA multiply)


Jumps from:
30F0 MULT LONG
316E SHIFT ONE

ZEROS 4/5 subroutine 2FFB


See 2FDD SHIFT FP.
Called by:
315E SKIP ZERO
Exit from:
2FF9 ADDEND 0 (2FDD SHIFT FP)

ZPLUS 37A1 (3783 get-argt)


Jumps from:
3783 get-argt

ZX printer see printer

ZX80
The first Sinclair computer. Don't confuse it with the
Z80, the central processing chip on which these and many
other small computers are based.
Preface Spectrum ROM developed from the 4K ROM of
ZX80
24B7 DRAW LINE line drawing first used in

ZX81
The second Sinclair computer.

950
Preface Spectrum is successor to ZX81
Introduction not very good cassette handling
04AA PROGRAM NAME a fossil surviving from
24B7 DRAW LINE described in ZX81 handbook

Z80 machine code


What in this book is called "m/c". Strictly speaking,
machine code consists of hex numbers, as understood by the
Z80 chip in conveying each of the 255d different instructions.
Codings such as "LD A,L" and "JR Z,LOOP" are in "Z80
assembly language", but as this translates very directly into
real machine code it is often loosely referred to as machine
code also.

Preface the authors have learned from the ROM


Introduction the ROM is complex machine code

951

The non-alphabetic entries which follow are first the


digits and then other symbols in order of their ASCII codes.

"0" [zero] see character codes

33rd bit, 34th bit see precision

5-byte values see CALCULATE

5-call counter see KEYBOARD SCANNING, 02BF KEYBOARD

8-bit loop
When a byte is to be rotated eight times in a loop
operation, more compact coding can often be achieved by
arranging for the eighth loop to result in a set bit being
rotated into the carry for the first time, or similar, rather
than using a DJNZ loop. This is the_marker_bit.
0507 SA START loop finished when byte zero
051C SA OUT set bit put in bit zero
0525 SA 8 BITS marker moves left
05C8 LD MARKER loop finished when C flag set

80-byte
An 80-byte, an address containing 10000000b/80h, is
placed on start-up by 1219 RAM SET
- at the end of the channels area; this one is part of
the "initial channel information" at 15AF
- at the end of the variables area
- after the newline at the end of the editing area.
These are used several times as a signal that a search
has reached the end of the area. For example in 196E LINE
ADDR, which steps on from the start of each line number in
the program area to the next, looking for a line number bigger
than a given number: if the given number is bigger than any in
952
the program, the "line start" will eventually be the first
variable letter in the variables area, if there is one. No variable
letter can be less than 40h, which as the hi byte of a line
number would make an impossible line number 16384d or
more, so the routine terminates. But if there are no variables
in the variables area, the "line start" will be the 80-byte,
indicating an even more impossible line number.
An 80-byte in the SAVE/LOAD header of a program block
indicates that no LINE start is specified.
0716 SA LINE put in header if no LINE
084C LD DATA 1 end of vbles area, E LINE minus one
0873 LD PROGS end of vbles area, E LINE minus one
08B6 ME CONTRL put at end of MERGing vbles area in
work space
08F9 ME OLD VP if found in old variables, add new
1219 RAM SET put in required locations
16B0 SET MIN editing area reduced to newline and 80-
byte
1B8A LINE RUN set pointer on end marker
2900 V EACH search for vble name terminates on 80-byte
2B29 L SPACES vble put just before 80-byte
2B4F L SINGLE new vble overwrites old 80-byte, new
added
2BC6 L STRING new string put just before 80-byte
2BEA L FIRST new vble overwrites old 80-byte, new
added
2C2E D NO LOOP make room for new array before 80-
byte

! key (21h) see character codes

" key (22h) see also character codes


When printing out a BASIC line, 195A OUT CH 1 flops the
"quotes flag", bit 2 of FLAGS2, every time a " is encountered;
this ensures that the colon isn't read as a statement separator
if it is in quotes (1937 OUT CHAR).

953
When searching for the end of the line or statement, the "
sets an ad hoc flag, and separators such as colon or THEN are
ignored until another " turns the flag off.
When scanning to evaluate expressions (24FB
SCANNING), a
" signals the start of a string, ending at the first undoubled "
(25B3 S QUOTE).
195A OUT CH 1 quote mode changed
199A EACH S 3 quotes flag set in C register
211C IN PR 2 quotes printed twice in INPUT prompt
250F S QUOTE S counts length between quotes
25B3 S QUOTE finds quote to match first quote
25BE S Q AGAIN handles doubled quotes
25CB S Q COPY copies string between quotes to work
space
25D9 S Q PRMS gets length and start of string in quotes
35DE val quotes have been dropped from string

(hatch) key (23h) see also character codes


All commands for changing the stream must end with
(hatch) and a number. Also (hatch) distinguishes commands
calling for keyboard input, eg plain INKEY$, from those
calling for input from some other stream, eg INKEY$ (hatch).
2070 STR ALTER reads (hatch) and number
2634 S INKEY$ (hatch) diverts to "read-in" subroutine

$ key (24h) see also character codes


A variable name or FN name must have a string value if
and only if the name is letter + "$".
0C22 PO EACH tokens ending "$" handled differently
1F6A DEF FN 1 DEF FN with "$" has string value
1F89 DEF FN 4 skips "$" in function name
27BD S FN SBRN 2nd char of DEF FN must be "$" or "("
27F7 SF RUN jumps for non-string function
2814 SF CP DEF both FN and DEF FN must match for "$"
2831 SF VALUES skip over "$"
2843 SF ARG LP skip over all but "$"

954
28B2 LOOK VARS strings signalled by "$"
28DE V STR VAR skip over "$"
295A SFA LOOP skip over all but "$"
2B1F L TEST CH string/numeric vbles separated by "$"

% key (25h), & key (26h), ' key (27h) see character codes

( key (28h) see also character codes


The ( character
- is required at the start of a FN and of a DEF FN
statement
- signals a PRINT requirement in an INPUT statement
- is required to introduce the coordinates of ATTR,
SCREEN$, POINT
- following a complete numeric expression, will always
produce an error report; see 2712 S CONT 2 under 24FB
SCANNING
- following a variable name, signals an array
- following a string expression, calls for slicing.
1F7D DEF FN 2 checks for "(" in DEF FN
20C1 IN ITEM 1 checks for "(" in INPUT statement
2522 S 2 COORD checks for "(" before ATTR &c
coordinates
259B scanning function table - evaluate
2712 S CONT 2 expression may be followed by "("
2713 S CONT 3 for strings, introduces slicing
27D0 SF BRKT 1 error if no "(" introducing FN
27F7 SF RUN skip over "(" of DEF FN
2831 SF VALUES skip over "(" of FN
28B2 LOOK VARS signals array
2934 V SYNTAX signals array
294B V END simple string vble followed by "("
29AE SV ARRAYS most arrays must have "("
2A49 SV SLICE? "(" may signal a slice

) key (29h) see also character codes


The ) character

955
- is required after a SAVE/LOAD DATA command
- if it immediately follows "(", signals "no arguments" -
but it must be there after the arguments
- terminates the call to PRINTing in an INPUT command
- is required after the coordinates of ATTR, SCREEN$,
POINT, or an expression opened by "("
- is required to close both FN and DEF FN (left side)
statements, and signals the last argument
- signals "no more subscripts" in evaluating an array.
0692 SA DATA 1 check for ")" after SAVE/LOAD DATA
1F7D DEF FN 2 "()" signals no arguments in DEF FN
1FA6 DEF FN 6 check for ")" at end of DEF FN
1FF2 PRINT 4 ends PRINT call in INPUT commands
2045 PR END Z sets Z flag after printing characters
20C1 IN ITEM 1 check for ")" in INPUT command
2522 S 2 COORD check for ")" after ATTR &c coordinates
25E8 S BRACKET checks for ")" at end of expression
27D0 SF BRKT 1 "()" signals no arguments
27E4 SF BRKT 2 check for ")" at end of FN
2831 SF VALUES "()" signals no arguments in DEF FN
2852 SF ARG VL signals end of arguments in DEF FN
2885 SF R BR 2 check for ")" at end of FN
288D SF VALUE marks ends of DEF FN (left side) and FN
2951 STK F ARG "()" signals no arguments in DEF FN
296B SFA CP VR signals end of arguments in DEF FN
29C3 SV COMMA signals no more subscripts in array
29D8 SV CLOSE signals no more subscripts in array
29EA SV LOOP signals no more subscripts in array
2A12 SV RPT C ")" must close array name
2A2C SV ELEM$ signals no more subscripts in array
2A52 SLICING (twice) ")" must close slice; "()"
signals trivial slice
2A81 SL SECOND marks "no second parameter"
2C2E D NO LOOP marks error if no of subscripts doesn't
match no of dimensions

956
* key (2A) see also character codes, commands, functions
and operators
The * (2A) key B with symbol shift, and the
/ (2F) key V with symbol shift
can conveniently be described together; each of them must
be both preceded and followed by a numeric expression.
On execution, after the first expression has been
evaluated in 268D S DECIMAL or 26C9 S LETTER, the
scanning loop quickly leads to 2723 S OPERTR. Here the token
code 2A/2F is looked up in the two tables 2795 table of
operators and 27B0 table of priorities, giving a value
08C4/08C5 in BC, 08 being the priority and C4/C5 the code of
the operator. This is put on the machine stack in 2790 S NEXT.
When the code comes off the calculator stack in 274C S
STK LAST, C4/C5 are converted to 04/05, the literals for the
30CA multiply or 31AF division executive routines.
2788 S NOT AND not accepted with string arguments

** see "up arrow" under U in the alphabet

+ key (2B) see also commands, functions and operators


The + (2B) key K with symbol shift, and the
- (2D) key J with symbol shift
can conveniently be described together; they may be either
"binary" plus/minus, both preceded and followed by a
numeric expression, or "unary" plus/minus, followed but not
preceded by a numeric expression; "+" but not "-" can also
operate between two string expressions as a "concatenation",
which is always binary.
On execution, if an expression has been evaluated
already by 24FB SCANNING, they are taken as binary
operators at 2712 S CONT 2, which quickly leads to 2723 S
OPERTR. Here the token code 2B/2D is looked up in the two
tables 2795 table of operators and 27B0 table of priorities,
giving a value 06CF/06C3 in BC; 06 being the priority and CF/
C3 the code of the operator.
This is put on the machine stack in 2790 S NEXT.

957
When the code comes off the calculator stack in 274C S
STK LST, CF/C3 is converted to 0F/03, the literals for the 3014
addition or 300F subtract executive routine; however, if the
operand before the operator is a string, literal 0F for addition
is changed (2773 S TIGHTER) to 17 for the executive routine
359C str-add.
If "+" or "-" are found by 24F8 SCANNING before an
expression has been evaluated, they are handled as unary
operators: 2B "+" is found by 24FF S LOOP 1 in the function
table at 2596, and is just ignored by 25AF S U PLUS. The
vacuous "unary plus" function only means "not minus".
Unary 2D "-" is handled as a special case at 26DF S
NEGATE, which puts priority 09 and op code DB (D8 is a
misprint in the notes) on the stack directly; when it comes off
in 274C S STK LST it is converted to 1B, the literal for 346E
negate.
039D K KLC DGT "-" read as special case
26DF S NEGATE unary minus executed as special case
2788 S NOT AND "+" but not "-" accepted between strings
2CF2 SIGN FLAG handles xE+m and xE-m
2DF2 PF NEGTVE printout of -X
2F6C PF E FRMT printout of "-" in xE-m
2F83 PF E POS printout of "+" in xE+m
2F85 PF E SIGN printout of "+/-" in xE+/-m

, key (2C - also 06, PRINT comma) see also character codes
The comma has a special key on most later models, but
can still be got by key N with symbol shift as on the older
Spectrum. It has several special functions:
- after LOAD ... CODE, it signals that a length is being
specified
- as a print control code, the PRINT comma code 06, it is
equivalent to TAB 0 or TAB 16d - the character code is
changed at 204E PR POSN 1. See control codes.
- in a LIST (hatch) command, it signals that a line
number follows

958
- it is required as a separator in OPEN and MOVE
commands and other commands with two numeric
parameters, or in a few cases three
- it is required between each expression in a DATA
statement, each argument in a FN statement, and each
subscript in an array or DIM expression, but mustn't follow
the last one:
the_absence of a comma signals "no more expressions"
- the control items in a PRINT statement must be
separated by comma, semicolon or "'".
06E1 SA CODE signals start or length
0A5F PO COMMA executes print comma
0F38 ED LOOP print comma accepted as special case
17FB LIST 1 handles LIST (hatch) command
1AFC P OPEN required as separator
1B0A P MOVE required as separator
1C7A EXPT 2NUM check for "," between numeric
parameters
1DED READ absence of "," marks end of DATA statement
1E1E READ 2 absence of "," marks end of DATA statement
1E27 DATA expressions must be separated by commas
1E2C DATA 1 check each expression for comma
1F94 DEF FN 5 if no comma, no more parameters
204E PR POSN 1 changes code 2C to PRINT comma 06
21E2 CO TEMPS 2 print control items must be separated
2320 CIRCLE check for "," after X,Y coordinates
2382 DRAW signals third parameter
27D9 SF ARGMTS absence signals end of FN arguments
2852 SF ARG VL absence signals mismatch of DEF FN &
FN
29C3 SV COMMA absence signals missing subscript
2A2C SV ELEM$ "," or ")" must follow subscript
2C2E D NO LOOP if no comma, no more dimensions

", space" message 1536


Used only in printing out "error reports", eg
0/ OK/, /9999:2

959
The "/"s here indicate the sections in which the report is
printed.
133C MAIN 5 printed after report message

- key (2D) see + key (2B)

-2 line number
The line number given to a "direct command" typed into
the editing area.
1A1B OUT NUM 1 zero, not -2, printed in error reports
1B8A LINE RUN line in edit area given line number -2

. key (2E) see also character codes


The stop/point has a special key on later models, but
can still be got by the M key with symbol shift as in the older
Spectrum. It produces the decimal "function": not a real
function, it indicates that what follows is to be read as a
decimal fraction, just as BIN signals that what follows is to be
read as a binary integer.
On execution, 24FB SCANNING indexes into the scanning
function table at 2596 to find the executive routine 268D S
DECIMAL (S BIN).
Although "." is commonly called, in the notes and
elsewhere, a_decimal_point, it would be better called a
"fractional point"; it marks the break between the integer and
fractional part of a number, whether that number is expressed
as a decimal, or in hex or binary notation. The
expression_binary_point is occasionally used in the notes,
though I think not "hex point", which is just as logical or
illogical.
Most of the references below relate to printing the
fractional point in printouts of FP numbers; a tricky business,
see 2DE3 PRINT FP.
2CB8 NOT BIN checks for fractional part of number
2DC1 LOG(2**A) figures digits before or zeroes after
the point
2E24 PF SMALL in printouts, cuts out leading zeroes

960
after the fractional point
2E56 PF LARGE in printouts, reduces digits before the
fractional point to eight maximum
2EA9 PF INSERT counts digits to print before the point
2F25 PF R BACK rounding may add a digit before the
point
2F2D PF COUNT count of digits before the point
2F46 PF NOT F one digit, zero perhaps, before the point
2F4B PF E SBRN check for digits before the point (xEm)
2F5E PF DC OUT prepares to print point
2F64 PF DEC 0S prints point
2F6C PF E FRMT always one digit before the point in xEm
326D X LARGE if binary point after mantissa, no
fractional part

/ key (2F) see * key (2A)

: key (3A) see also character codes


The Z key with symbol shift produces the colon. No
Spectrum model that I know of has a separate key for this
character, which causes a certain amount of grumbling among
users; but some add-on keyboards have it. Its main use is to
mark the end of BASIC statements, see the references below.
One of the more tiresome mistakes in the Spectrum ROM
is the one in 1937 OUT CHAR, which makes the keystroke
print keywords after a colon even in REMs: see under OUT
CHAR.
133C MAIN 5 used in reports between line and stment no
1937 OUT CHAR puts input into L mode unless in quotes
196D OUT CH 3 notes ROM mistake
19A5 EACH S 4 marks the end of a statement
1B29 STMT L 1 marks the end of a statement
1BF4 STMT NEXT marks the end of a statement
1CDE FETCH NUM marks the end of a statement
1D86 LOOK PROG signals "more statements in this line"
2048 PR ST END sets flag to show the end of a statement
2712 S CONT 2 marks end of expression

961
; key (3B)
The O key with symbol shift produces the semicolon.
Later Spectrum models have a separate key, but symbol shift
O still works.
Its main function in BASIC is to separate print control
items, with the signal "no tab wanted", see control characters.
17FB LIST 1 after (hatch), signals that a line number
follows
204E PR POSN 1 in print control items, no tab
21E2 CO TEMP 2 ";" or "," must separate control item

< key (3C)


It is convenient to describe together all the
"comparison operation" keys:
3C < key R with symbol shift; op code CD
3D = key L with symbol shift; op code CE
3E > key T with symbol shift; op code CC
C7 <= key Q with symbol shift; op code C9
C8 >= key E with symbol shift; op code CA
C9 <> key W with symbol shift; op code CB
Each of them must have two operands, one preceding and
one following; they may be either both strings or both
numeric, but both must be the same.
The result of any of these operators is a logical value
zero if the comparison is "false", or one if it is "true". See
logical values.
On execution, after the first expression has been
evaluated in 268D S DECIMAL, 25B3 S QUOTE or 26C9 S
LETTER, the
scanning loop quickly leads to 2723 S OPERTR. Here the token
code 3C -> 3E or C7 -> C9 is looked up in the two tables 2795
table of operators and 27B0 table of priorities, giving a
priority 05 in the B register and the operator code C9 -> CE in
the C register. However, if the first argument already
evaluated is a string the code is changed by 2773 S TIGHTER,
to 11h -> 16h. The priority plus op code is put on the machine
stack in 2790 S NEXT.
962
When the code comes off the calculator stack in 274C S
STK LAST, C9 -> CE are converted to 09 -> 0E; all the literals
09 -> 0E and 11h -> 16h call the same executive routine 353B
no-l-eql, but its action differs according to the literal value.

= key (3D) see also < key above


This key has another function besides being a binary
operator, that of separator in the command table at 1A7A P
LET and 1A90 P FOR, and in DEF FN statements as noted
below.
1FA6 DEF FN 6 must follow ")" on left of DEF FN
288D SF VALUE skip over ")=" in evaluating DEF FN

> key (3E) see also < key above


This symbol has another function besides being a binary
operator; it is printed in BASIC listings just after the line
number of the current line as a_current_line_cursor/marker.
See
TV FLAG bit 4 and 1795 AUTO LIST.
1795 AUTO LIST current line must appear on screen
1855 OUT LINE prepare to print cursor
1865 OUT LINE1 print cursor

? key (3F) see also character codes, errors


This character has two special uses:
- it is printed out if an "unprintable" code has somehow
got into the listing
- a flashing "?" in the editing area indicates a syntax
error.
09F4 PRINT OUT printed for meaningless codes
0A69 PO QUEST prints "?" for meaningless codes
1894 OUT LINE4 finds place for error cursor
18C1 OUT FLASH prints error cursor
1B6F SEPARATOR error marker shown if separator
missing

@ key (40h) see also character codes

963
039D K KLC DGT special case in key decoding
03B2 K @ CHR special case in key decoding

[ key (5BH), \ key (5Ch), ] key (5Dh) see character codes

key (5Eh) see "up arrow"; the symbol can't be printed with
this word processor

key (5Fh) see "underline"; the symbol can't be printed


with this word processor

'@ Sinclair Research' message 1539


Printed only after NEW or on start-up, by 1219 RAM SET.

<= key (C7), >= key (C8), <> key (C9) see < key

964
APPENDIX - BASIC programs

1. Program for hex/decimal conversions:


A simple program to print out the hex equivalent of any
decimal number up to 65535, and vice versa.

10 REM "hex

999 REM "Hex to decimal:


1000 CLS:
PRINT AT 3,0; "Input four-figure hex number;
program will return decimal
equivalent."
AT 8,0; "For reverse conversion, press
ENTER"''
1100 DIM h$(4): POKE 23658,8:
INPUT "Input hex number: "; LINE h$:
POKE 23658,0:
IF h$ = " " THEN GO TO 2000:
REM The POKES set and unset CAPS LOCK: REM four
spaces
between the quotes in the last line.
1200 PRINT h$; " hex = "; FN d(h$); " dec":
GO TO 1100:
REM "FN d(h$) gives decimal value of four hex digits.

1999 REM "Decimal to hex:


2000 CLS:
PRINT AT 3,0; "Input integer up to 65535;
program will return hex
equivalent.";
AT 8,0; "To terminate, press ENTER"''
2100 DIM n$(5): DIM h$(4): POKE 23658,0:
INPUT "Input number: "; n$:
IF n$=" " THEN STOP:

965
REM "Five spaces between the quotes in last line.
2200 LET n=VAL n$: LET p=4
2300 LET r= n - 16*INT (n/16):
LET h$(p) = CHR$ (r+48):
IF r > 9 THEN LET h$(p) = CHR$ (r+55)
2400 LET p = p - 1:
IF p=0 THEN PRINT n$; " dec = "; h$; " hex": GO TO 2100
2500 LET n = INT (n/16): GO TO 2300

8000 REM "Functions:


d(h$) gives decimal value of four hex digits
e(h$) gives decimal value of one hex digit
8010 DEF FN d(h$) = FN e(h$(4)) +
16*FN e(h$(3)) +
16**2*FN e(h$(2)) +
16**3*FN e(h$(1))
8020 DEF FN e(h$) = CODE (h$) - 48*(CODE h$<64)
- 55*(CODE h$>64)

9999 SAVE d1 "hex" LINE 1

2. Programs to calculate numbers in FP formats:


2.1 Put any positive number into full FP format; a
relatively simple program which first displays the exponent as
a decimal number, then each digit of the mantissa also as
decimal numbers, ie 10 must be read as Ah, &c. These are the
true exponent and mantissa; they must be corrected by
adding 80h/128d to the exponent and making a sign bit for the
mantissa.

5 REM "fpdec

10 REM "FP format in decimals.

99 REM "Input X: calculate 2**e * n = X such that n is


between 0.5 and one.
100 INPUT n: LET e=0:

966
IF NOT n THEN GO TO 600
200 IF n<.5 THEN GO TO 500
300 LET n=n/2: LET e=e+1:
IF n<1 THEN GO TO 600
400 GO TO 300
500 LET n=n*2: LET e=e-1:
IF n<.5 THEN GO TO 500

599 REM "Print out.


600 CLS:
PRINT "Exponent: ";e+128''
"Mantissa bytes:"
700 FOR f=1 TO 4: LET n=n*16:
PRINT INT n:
LET n=n-INT n:
NEXT f

9000 STOP
9999 SAVE d1"fpdec" LINE 9000

2.2 Print decimal numbers or expressions ,positive or


negative, in the hex digits of full FP format, and convert FP
numbers in either full or small integer format in hex digits
back to decimal.
A more elaborate program.

5 REM "fpnum

10 CLS: PRINT "To convert decimal numbers to FP


format, type
RUN 100 and ENTER."
20 PRINT "To convert FP format numbers
(full or small integer) to
decimal, type
RUN 1000 and ENTER."
30 STOP

967
99 REM "Decimal to FP: input X, and handle zero X.
100 CLS: INPUT "Input numeric expression:"'n:
LET m=n:
IF NOT n THEN LET hi=0: LET lo=0: GO TO 800

199 REM "Get the sign and absolute value of X: if X has


negative or zero true exponent jump on.
200 LET s=SGN n: LET n=ABS n: LET e=0:
IF n<.5 THEN GO TO 600
300 IF n<1 THEN GO TO 700

399 REM "Figure true mantissa and exponent for X > 1


400 LET n=n/2: LET e=e+1:
IF n<1 THEN GO TO 700
500 GO TO 400

599 REM "X < half


600 LET n=n*2: LET e=e-1:
IF n<.5 THEN GO TO 600

699 REM "Convert exponent to FP format in hex digits and


put sign bit in mantissa.
700 LET e=e+128: LET hi=INT (e/16): LET lo=e-16*hi:
IF s=1 THEN LET n=n-.5000000001:
REM "the .5000000001 (eight zeros) is needed to correct
a slight inaccuracy in multiplication: PRINT INT (2*.5) will
print zero, not one. This is not a Spectrum peculiarity, similar
adjustments are sometimes required with much larger
computers to correct the INT function. I know of no rule for
applying the corrections, I just use trial and error.

799 REM "Print out.


800 CLS: PRINT "Decimal", "FP format",
FN d$(hi); FN d$(lo);" ";:
FOR f=1 TO 11: IF f/3=INT (f/3) THEN PRINT " ";: NEXT f
900 LET n=n*16:
PRINT FN d$(INT n);:

968
LET n=n-INT n:
NEXT f:
STOP

1000 REM "FP to decimal:


1050 POKE 23658,8:
REM "this sets CAPS LOCK

1099 REM "Input X and set character counters. Read


exponent:
subr at 2000 converts hex digit at char to decimal in hdig.
1100 INPUT "Input five-byte FP number (spaces
will be ignored):"'f$:
LET char=0: LET mcount=11:
GO SUB 2000: LET exp=16*(hdig-8):
GO SUB 2000: LET exp=exp+hdig:
IF exp<>-128 THEN GO TO 1500

1200 REM "Small integers.


1300 GO SUB 2000: LET sign=hdig: LET char=char+1:
GO SUB 2000: LET lo=hdig:
GO SUB 2000: LET lo=lo*16+hdig:
GO SUB 2000: LET hi=hdig:
GO SUB 2000: LET hi=hi*16+hdig:
LET mant=lo+256*hi: LET exp=32:
IF sign THEN LET mant=65535-mant:
LET sign=-1: GO TO 1900
1400 LET sign=1: GO TO 1900

1500 REM "Full format:


1600 GO SUB 2000: LET mant=hdig:
IF mant>8 THEN LET sign= -1: GO TO 1800
1700 LET sign=1: LET mant=mant+8
1800 GO SUB 2500:
REM "Subr 2500 accumulates mantissa value in mant.

1899 REM "Print out.

969
1900 CLS:
PRINT "FP format", f$'
"Decimal",
2**(exp-32)*mant*sign:
POKE 23658,0: REM CAPS LOCK off
1950 STOP

2000 REM "Subroutine to read hex digit at char into hdig.


2100 LET char=char+1:
IF char>LEN f$ THEN LET hdig=0: GO TO 2400
2200 IF f$(char)=" " THEN GO TO 2100
2300 LET hdig=FN h(char)
2400 LET mcount=mcount-1: RETURN

2500 REM "Subroutine to calculate mantissa.


2600 GO SUB 2000:
IF mcount THEN LET mant=16*mant+hdig: GO TO 2600
2700 RETURN

2800 REM "FN h(x): converts hex to decimal.


2900 DEF FN h(x) =
(CODE f$(x)-48) * (CODE f$(x)<60) +
(CODE f$(x)-55) * (CODE f$(x)>60)

3000 REM "FN d$(x): converts decimal to hex.


3100 DEF FN d$(x) = CHR$ ((x+48)*(x<10) +
(x+55)*(x>9))

9999 SAVE d1"fpnum" LINE 9000

3. A program to put constants into "stk-data" format, eg for


inclusion in a user list of constants.

5 REM "fpconst

10 CLS: PRINT "Shows decimal numbers (positive


or negative) in stk-data format."

970
20 PRINT "Press any key and enter decimal
number or expression."
30 PAUSE 0

100 INPUT "Input decimal number or numeric


expression:"'n

1000 REM "Construct "decimal FP number" in n() array.


1010 DIM n(5): LET m=n:
IF NOT n THEN GO TO 2000
1020 LET s=SGN n: LET n=ABS n: LET e=0:
IF n<.5 THEN GO TO 1060
1030 IF n<1 THEN GO TO 1070
1040 LET n=n/2: LET e=e+1:
IF n<1 THEN GO TO 1070
1050 GO TO 1040
1060 LET n=n*2: LET e=e-1:
IF n<.5 THEN GO TO 1060

1069 REM "Now e is true exponent, N is true mantissa.


1070 LET n(1)=e+128:
FOR f=2 TO 5: LET n=(n+.0000000001) *256:
LET n(f )=INT n:
LET n=n-n(f ):
NEXT f:
IF s=1 THEN LET n(2)=n(2)-128:
REM "The .0000000001 (nine zeros) corrects the INT
function.

2000 REM "Convert N to stk-data format.


2010 CLS:
PRINT "Decimal",m'
"Stk-data 34h",:
FOR f=5 TO 1 STEP -1:
IF NOT n(f ) THEN NEXT f
2020 IF NOT f THEN LET n(2)=176: LET f=3:
GO TO 2070

971
2030 IF f=1 THEN LET f=2: GO TO 2070
2040 LET g=f-2: LET n(1)=n(1)-80:
IF n(1)<>64*INT (n(1)/64) THEN GO TO 2060

2049 REM "Print out.


2050 PRINT FN h$(n(1)+64*g);:
GO TO 2040
2060 LET n(1)=n(1)+64*g
2070 FOR g=1 TO f: PRINT FN h$(n(g));:
NEXT g
2080 STOP

3000 REM "Functions:


FN h$(x): makes hex byte ready to print from decimal.
FN d$(x): converts decimal digit to hex.
3100 DEF FN h$(x) = FN d$(INT x/16)) +
FN d$(x - 16*INT (x/16)) +
""
3200 DEF FN d$(x) = CHR$ (48*(x<10) +
55*(x>9) +
x)
9999 SAVE d1"fpconst" LINE 9000

This program gives different representations for the


constants "one" and "ten" from those in the constant table at
32C5:
- "one" comes out as 31 00 instead of 40 80 00 01
- "ten" comes out as 34 20 instead of 40 B0 00 0A
The reason is that the ROM uses the small integer forms
as the basis for figuring the "stk-data" forms. I cannot see any
useful purpose served by this, and the forms come out two
bytes longer.

4. Programs for reading ROM and RAM:


4.1 A very simple general-purpose reader, which can be
added to existing programs ad hoc for debugging etc: it can
even be used as a direct command:

972
100 INPUT x: CLS: FOR f=x to 70000: PRINT f, PEEK f:
NEXT f

4.2 An obvious expansion of this would make it print out in


hex, using FN h$(f ) from one of the above programs.
4.3 Another expansion, useful for reading messages, prints
out the character represented by the code:

100 INPUT x:
CLS:
FOR f=x to 70000: PRINT f;
TAB 7; PEEK f,:
IF PEEK f > 32
THEN PRINT CHR$ PEEK f;:
REM "Some character codes less than 32 would give error
reports.
200 PRINT:
NEXT f

5. A program to print out 640d hex addresses with their


bytes on a single 66-line page, in ten columns of 64d entries
each. It is useful for studying machine code programs,
whether in the ROM, one's own, or obtained from elsewhere.
It requires an eighty-column printer.

5 REM "hexpt

10 REM "Machine code display.

20 REM "Functions:
h$() converts decimal to hex,
a$() and b$() form the hi and lo hex digits of a
byte given in decimals,
z$() forms a 2-byte number given in decimals into
four hex digits.
30 DEF FN h$(x) = CHR$ (48*(x<10) +

973
55*(x>9) + x)
40 DEF FN a$(x) = FN h$(x - 16*INT (x/16))
50 DEF FN b$(x) = FN h$(INT (x/16))
60 DEF FN z$(x) = FN a$(INT (x/256)) +
FN b$(INT (x/256)) +
FN a$(x - 256*INT (x/256)) +
FN b$(x - 256*INT (x/256))

1000 REM "Initial prompt:


1100 CLS: PRINT AT 7,3; "Prints out machine code in";
AT 8,6; "hexadecimal numbers.";
AT 10,6; "640 bytes per page.":
INPUT "Input start no (decimal): ";g:
INPUT "How many pages? ";p:
LET z = g + 640*p - 1

1199 REM "Page printing loop: subr 2100 prints entry a.


1200 FOR f=g TO g+63:
FOR c=0 TO 9:
REM "Prints entry in line f, column c+1: ta, tb are tab
settings.
1300 LET a= f + 64*c:
LET ta= 8*c:
LET tb= 8*c + 5:
GO SUB 2100:
NEXT c:
NEXT f:
LPRINT ''':
LET g= g + 640:
IF g < z THEN GO TO 1200

1400 STOP

2000 REM "Subr to print address and contents of entry a.


2100 LPRINT TAB ta; FN z$(a);
TAB tb; FN z$(PEEK a)(3 TO);:
RETURN

974
9999 SAVE d1 "hexpt" LINE 1000

975
ROM LISTING

There are several commercially available


"disassembler" programs, which read machine code and print
out assembly language; such programs are usually mostly in
m/c and quite complex.

All numbers are hex unless otherwise stated

0000 START DI
XOR A
LD DE,FFFF
JP 11CB;START/NEW
0008 ERROR.1 LD HL,(5C5D);CH.ADD
LD (5C5F),HL;X.PTR
JR 0053;ERROR.2
0010 PRINT.A.1 JP 15F2;PRINT.A.2
DEFB FF,FF,FF,FF,FF
0018 GET.CHAR LD HL,(5C5D);CH.ADD
LD A,(HL)
001C TEST.CHAR CALL 007D;SKIP.OVER
RET NC
0020 NEXT.CHAR CALL 0074;CH.ADD+1
JR 001C;TEST;CHAR
DEFB FF,FF,FF
0028 FP.CALC JP 335B;CALCULATE
DEFB FF,FF,FF,FF,FF
0030 BC.SPACES PUSH BC
LD HL,(5C61);WORKSP
PUSH HL
JP 169E;RESERVE
0038 MASK.INT PUSH AF
PUSH HL
LD HL,(5C78);FRAMES
INC HL

976
LD (5C78),HL;FRAMES
LD A,H
OR L
JR NZ,0048;KEY.INT
INC (IY+40);FRAMES+2
0048 KEY.INT PUSH BC
PUSH DE
CALL 02BF;KEYBOARD [+2: CALL 386E]
POP DE
POP BC
POP HL
POP AF
EI
RET

0053 ERROR.2 POP HL


LD L,(HL)
0055 ERROR.3 LD (IY+00),L;ERR.NR
LD SP,(5C3D);ERR.SP
JP 16C5;SET.STK
DEFB FF,FF,FF,FF,FF,FF,FF

0066 RESET PUSH AF


PUSH HL
LD HL,(5CB0);NMIADD
LD A,H
OR L
JR NZ,0070;NO.RESET
JP (HL)
0070 NO.RESET POP HL
POP AF
RETN

0074 CH.ADD+1 LD HL,(5C5D);CH.ADD


0077 TEMP.PTR1 INC HL
0078 TEMP.PTR2 LD (5C5D),HL;CH.ADD
LD A,(HL)

977
RET

007D SKIP.OVER CP 21
RET NC
CP 0D
RET Z
CP 10
RET C
CP 18
CCF
RET C
INC HL
CP 16
JR C,0090;SKIPS
INC HL
0090 SKIPS SCF
LD (5C5D),HL;CH.ADD
RET

TOKEN TABLE
Letters followed by space in this listing have bit 7 set -
spaces in the m/c are marked with "."
0095 DMES '? RND INKEY$ PI FN POINT SCREEN$ '
00AF DMES 'ATTR AT TAB VAL$ CODE VAL LEN SIN '
00C9 DMES 'COS TAN ASN ACS ATN LN EXP INT SQR '
00E3 DMES 'SGN ABS PEEK IN USR STR$ CHR$ NOT '
00FD DMES 'BIN OR AND <= >= <> LINE THEN TO '
0115 DMES 'STEP DEF.FN CAT FORMAT MOVE ERASE '
0121 DMES 'OPEN.(hatch) CLOSE.(hatch) MERGE '
0143 DMES 'VERIFY BEEP CIRCLE INK PAPER FLASH '
0160 DMES 'BRIGHT INVERSE OVER OUT LPRINT '
017A DMES 'LLIST STOP READ DATA RESTORE NEW '
0195 DMES 'BORDER CONTINUE DIM REM FOR GO.TO '
01B1 DMES 'GO.SUB INPUT LOAD LIST LET PAUSE
NEXT '
01D0 DMES 'POKE PRINT PLOT RUN SAVE RANDOMIZE
'

978
01ED DMES 'IF CLS DRAW CLEAR RETURN COPY '

KEY TABLES
Here spaces are used merely to separate symbols/tokens
0205 DMES 'B H Y 6 5 T G V N J U 7 4 R F C'
0215 DMES 'M K I 8 3 E D X SYMSH L O 9 2 W S Z'
0225 DMES 'SPACE ENTER P 0 1 Q A'

022C DMES 'READ BIN LPRINT DATA TAN SGN ABS


SQR'
0234 DMES 'CODE VAL LEN USR PI INKEY$ PEEK TAB'
023C DMES 'SIN INT RESTORE RND CHR$ LLIST COS
EXP'
0244 DMES 'STR$ LN'

0246 DMES 'tilde BRIGHT PAPER \ ATN ( ) CIRCLE'


024E DMES 'IN VAL$ SCREEN$ ATTR INVERSE OVER
OUT '
0256 DMES 'ASN VERIFY | MERGE ] FLASH ACS INK'
025E DMES '[ BEEP'

0260 DMES 'DELETE EDIT C LOCK TV INVV <- down up'


0268 DMES '-> GRAPHICS'

026A DMES 'STOP * ? STEP >= TO THEN uparrow'


0272 DMES 'AT - + = . , ; " '
027A DMES '<= < NOT > OR / <> `'
0282 DMES 'AND :'

0284 DMES 'FORMAT DEF.FN FN LINE'


0288 DMES 'OPEN CLOSE MOVE ERASE'
028C DMES 'POINT CAT'

028E KEY.SCAN LD L,2F


LD DE,FFFF
LD BC,FEFE
0296 KEY.LINE IN A,(C)

979
CPL
AND 1F
JR Z,02AB;KEY.DONE
LD H,A
LD A,L
029F KEY.3KEYS INC D
RET NZ
02A1 KEY.BITS SUB 08
SRL H
JR NC,02A1;KEY.BITS
LD D,E
LD E,A
JR NZ,029F;KEY.3KEYS
02AB KEY.DONE DEC L
RLC B
JR C,0296;KEY.LINE
LD A,D
INC A
RET Z
CP 28
RET Z
CP 19
RET Z
LD A,E
LD E,D
LD D,A
CP 18
RET

02BF KEYBOARD CALL 028E;KEY.SCAN


RET NZ
LD HL,5C00;KSTATE0
026C K.ST.LOOP BIT 7,(HL)
JR NZ,02D1;K.CH.SET
INC HL
DEC (HL)
DEC HL

980
JR NZ,02D1;K.CH.SET
LD (HL),FF
02D1 K.CH.SET LD A,L
LD HL,5C04;KSTATE4
CP L
JR NZ,02C6;K.ST.LOOP
CALL 031E;K.TEST
RET NC
LD HL,5C00;KSTATE0
CP (HL)
JR Z,0310;K.REPEAT
EX DE,HL
LD HL,5C04;KSTATE4
CP (HL)
JR Z,0310;K.REPEAT
BIT 7,(HL)
JR NZ,02F1;K.NEW
EX DE,HL
BIT 7,(HL)
RET Z
02F1 K.NEW LD E,A
LD (HL),A
INC HL
LD (HL),05
INC HL
LD A,(5C09);REPDEL
LD (HL),A
INC HL
LD C,(IY+07);MODE
LD D,(IY+01);FLAGS
PUSH HL
CALL 0333;K.DECODE
POP HL
LD (HL),A
0308 K.END LD (5C08),A;LAST.K
SET 5,(IY+01);FLAGS
RET

981
0310 K.REPEAT INC HL
LD (HL),05
INC HL
DEC (HL)
RET NZ
LD A,(5C0A);REPPER
LD (HL),A
INC HL
LD A,(HL)
JR 0308;K.END

031E K.TEST LD B,D


LD D,00
LD A,E
CP 27
RET NC
CP 18
JR NZ,032C;K.MAIN
BIT 7,B
RET NZ
032C K.MAIN LD HL,0205;Main key table
ADD HL,DE
LD A,(HL)
SCF
RET

0333 K.DECODE LD A,E


CP 3A
JR C,0367;K.DIGIT
DEC C
JP M,034F;K.KLC.LET
JR Z,0341;K.E.LET
ADD A,4F
RET
0341 K.E.LET LD HL,01EB;E-mode letters unshifted table
INC B
JR Z,034A;K.LOOK.UP

982
LD HL,0205;Main key table
034A K.LOOK.UP LD D,00
ADD HL,DE
LD A,(HL)
RET
034F K.KLC.LET LD HL,0229;Symbol code letters table
BIT 0,B
JR Z,034A;K.LOOK.UP
BIT 3,D
JR Z,0364;K.TOKENS
BIT 3,(IY+30);FLAGS2
RET NZ
INC B
RET NZ
ADD A,20
RET
0364 K.TOKENS ADD A,A5
RET
0367 K.DIGIT CP 30
RET C
DEC C
JP M,039D;K.KLC.DGT
JR NZ,0389;K.GRA.DGT
LD HL,0254;E-mode digits with sym shift table
BIT 5,B
JR Z,034A;K.LOOK.UP
CP 38
JR NC,0382;K.8.&.9
SUB 20
INC B
RET Z
ADD A,08
RET
0382 K.8.&.9 SUB 36
INC B
RET Z
ADD A,FE

983
RET
0389 K.GRA.DGT LD HL,0230;Digits with c shift table
CP 39
JR Z,034A;K.LOOK.UP
CP 30
JR Z,034A;K.LOOK.UP
AND 07
ADD A,80
INC B
RET Z
XOR 0F
RET
039D K.KLC.DGT INC B
RET Z
BIT 5,B
LD HL,0230
JR NZ,034A;K.LOOK.UP
SUB 10
CP 22
JR Z,03B2;K.@.CHAR
CP 20
RET NZ
LD A,5F
RET
03B2 K.@.CHAR LD A,40
RET

03B5 BEEPER DI
LD A,L
SRL L
SRL L
CPL
AND 03
LD C,A
LD B,00
LD IX,03D1
ADD IX,BC

984
LD A,(5C48);BORDCR
AND 38
RRCA
RRCA
RRCA
OR 08
03D1 BE.IX+3 NOP
03D2 BE.IX+2 NOP
03D3 BE.IX+1 NOP
03D4 BE.IX+0 INC B
INC C
03D6 BE.H&L.LP DEC C
JR NZ,03D6;BE.H&L.LP
LD C,3F
DEC B
JP NZ,03D6;BE.H&L.LP
XOR 10
OUT (FE),A
LD B,H
LD C,A
BIT 4,A
JR NZ,03F2;BE.AGAIN
LD A,D
OR E
JR Z,03F6;BE.END
LD A,C
LD C,L
DEC DE
JP (IX)
03F2 BE.AGAIN LD C,L
INC C
JP (IX)
03F6 BE.END EI
RET

03F8 BEEP RST 0028;FP.CALC


DEFB 31;duplicate

985
DEFB 27;int
DEFB C0;st-mem-0
DEFB 03;subtract
DEFB 34;stk-data
DEFB EC;exponent 7C
DEFB 6C,98,1F,F5
DEFB 04;multiply
DEFB A1;stk-one
DEFB 0F;addition
DEFB 38;end-calc
LD HL,5C92;mem-0
LD A,(HL)
AND A
JR NZ,046C;REPORT.B
INC HL
LD C,(HL)
INC HL
LD B,(HL)
LD A,B
RLA
SBC A,A
CP C
JR NZ,046C;REPORT.B
INC HL
CP (HL)
JR NZ,046C;REPORT.B
LD A,B
ADD A,3C
JP P,0425;BE.i.OK
JP PO 046C;REPORT.B
0425 BE.i.OK LD B,FA
0427 BE.OCTAVE INC B
SUB 0C
JR NC,0427;BE.OCTAVE
ADD A,0C
PUSH BC
LD HL,046E

986
CALL 3406;LOC.MEM
CALL 33B4;STACK.NUM
RST 0028;FP.CALC
DEFB 04;multiply
DEFB 38;end-calc
POP AF
ADD A,(HL)
LD (HL),A
RST 0028;FP.CALC
DEFB C0;st-mem-0
DEFB 02;delete
DEFB 31;duplicate
DEFB 38;end-calc
CALL 1E94;FIND.INT1
CP 0B
JR NC,046C;REPORT.B
RST 0028;FP.CALC
DEFB E0;get-mem-0
DEFB 04;multiply
DEFB E0;get-mem-0
DEFB 34;stk-data
DEFB 80;four bytes
DEFB 43;exponent 93
DEFB 55,9F,80
DEFB 01;exchange
DEFB 05;division
DEFB 34;stk-data
DEFB 35;exponent 85
DEFB 71
DEFB 03;subtract
DEFB 38;end-calc
CALL 1E99;FIND.INT2
PUSH BC
CALL 1E99;FIND.INT2
POP HL
LD D,B
LD E,C

987
LD A,D
OR E
RET Z
DEC DE
JP 03B5;BEEPER
046C REPORT.B RST 0008;ERROR.1
DEFB 0A;Out of range

046E DEFB 89,02,D0,12,86;261.63 hz C


0473 DEFB 89,0A,97,60,75;277.18 hz C#
0478 DEFB 89,12,D5,17,1F;293.66 hz D
047D DEFB 89,1B,90,41,02;311.13 hz D#
0482 DEFB 89,24,D0,53,CA;329.63 hz E
0487 DEFB 89,2E,9D,36,B1;349.23 hz F
048C DEFB 89,38,FF,49,3E;369.99 hz F#
0491 DEFB 89,43,FF,6A,73;392.00 hz G
0496 DEFB 89,4F,A7,00,54;415.30 hz G#
049B DEFB 89,5C,00,00,00;440.00 hz A
04A0 DEFB 89,69,14,F6,24;466.16 hz A#
04A5 DEFB 89,76,F1,10,05;493.88 hz B

04AA PROG.NAME CALL 24FB;SCANNING


LD A,(5C3B);FLAGS
ADD A,A
JP M,1C8A;REPORT.C
POP HL
RET NC
PUSH HL
CALL 2BF1;STK.FETCH
LD H,D
LD L,E
DEC C
RET M
ADD HL,BC
SET 7,(HL)
RET

988
04C2 SA.BYTES LD HL,053F;SA.LD.RET
PUSH HL
LD HL,1F80
BIT 7,A
JR Z,04D0;SA.FLAG
LD HL,0C98
04D0 SA.FLAG EX AF,AF'
INC DE
DEC IX
DI
LD A,02
LD B,A
04D8 SA.LEADER DJNZ 04D8;SA.LEADER
OUT (FE),A
XOR 0F
LD B,A4
DEC L
JR NZ,04D8;SA.LEADER
DEC B
DEC H
JP P,04D8;SA.LEADER
LD B,2F
04EA SA.SYNC.1 DJNZ 04EA;SA.SYNC.1
OUT (FE),A
LD A,0D
LD B,37
04F2 SA.SYNC.2 DJNZ 04F2;SA.SYNC.2
OUT (FE),A
LD BC,3B0E
EX AF,AF'
LD L,A
JP 0507;SA.START
04FE SA.LOOP LD A,D
OR E
JR Z,050E;SA.PARITY
LD L,(IX+00)
0505 SA.LOOP.P LD A,H

989
XOR L
0507 SA.START LD H,A
LD A,01
SCF
JP 0525;SA.8.BITS
050E SA.PARITY LD L,H
JR 0505;SA.LOOP.P
0511 SA.BIT.2 LD A,C
BIT 7,B
0514 SA.BIT.1 DJNZ 0514;SA.BIT.1
JR NC,051C;SA.OUT
LD B,42
051A SA.SET DJNZ 051A;SA.ST
051C SA.OUT OUT (FE),A
LD B,3E
JR NZ,0511;SA.SET
DEC B
XOR A
INC A
0525 SA.8.BITS RL L
JP NZ,0514;SA.BIT.1
DEC DE
INC IX
LD B,31
LD A,7F
IN A,(FE)
RRA
RET NC
LD A,D
INC A
JP NZ,04FE;SA.LOOP
LD B,3B
053C SA.DELAY DJNZ 053C;SA.DELAY
RET

053F SA/LD.RET PUSH AF


LD A,(5C48);BORDCR

990
AND 38
RRCA
RRCA
RRCA
OUT (FE),A
LD A,7F
IN A,(FE)
RRA
EI
JR C,0554;SA/LD.END
0552 REPORT.D RST 0008;ERROR.1
DEFB 0C;"BREAK/CONT repeats"
0554 SA/LD.END POP AF
RET

O556 LD.BYTES INC D


EX AF,AF'
DEC D
DI
LD A,0F
OUT (FE),A
LD HL,053F;SA/LD.RET
PUSH HL
IN A,(FE)
RRA
AND 20
OR 02
LD C,A
CP A
056B LD.BREAK RET NZ
056C LD.START CALL 05E7;LD.EDGE.1
JR NC,056B;LD.BREAK
LD HL,0415
0574 LD.WAIT DJNZ 0574;LD.WAIT
DEC HL
LD A,H
OR L

991
JR NZ,0574;LD.WAIT
CALL 05E3;LD.EDGE.2
JR NC,056B;LD.BREAK
0580 LD.LEADER LD B,9C
CALL 05E3;LD.EDGE.2
JR NC,056B;LD.BREAK
LD A,C6
CP B
JR NC,056C;LD.START
INC H
JR NZ,0580;LD.LEADER
058F LD.SYNC LD B,C9
CALL 05E7;LD.EDGE.1
JR NC,056B;LD.BREAK
LD A,B
CP D4
JR NC,058F;LD.SYNC
CALL 05E7;LD.EDGE.1
RET NC
LD A,C
XOR 03
LD C,A
LD H,00
LD B,B0
JR 05C8;LD.MARKER
05A9 LD.LOOP EX AF,AF'
JR NZ,05B3;LD.FLAG
JR NC,05BD;LD.VERIFY
LD (IX+00),L
JR 05C2;LD.NEXT
05B3 LD.FLAG RL C
XOR L
RET NZ
LD A,C
RRA
LD C,A
INC DE

992
JR 05C4;LD.DEC
05BD LD.VERIFY LD A,(IX+00)
XOR L
RET NZ
05C2 LD.NEXT INC IX
05C4 LD.DEC DEC DE
EX AF,AF'
LD B,B2
05C8 LD.MARKER LD L,01
05CA LD.8.BITS CALL 05E3;LD.EDGE.2
RET NC
LD A,CB
CP B
RL L
LD B,B0
JP NC,05CA;LD.8.BITS
LD A,H
XOR L
LD H,A
LD A,D
OR E
JR NZ,05A9;LD.LOOP
LD A,H
CP 01
RET

05E3 LD.EDGE 2 CALL 05E7;LD.EDGE.1


RET NC
05E7 LD.EDGE.1 LD A,16
05E9 LD.DELAY DEC A
JR NZ,05E9;LD.DELAY
AND A
05ED LD.SAMPLE INC B
RET Z
LD A,7F
IN A,(FE)
RRA

993
RET NC
XOR C
AND 20
JR Z,05ED;LD.SAMPLE
LD A,C
CPL
LD C,A
AND 07
OR 08
OUT (FE),A
SCF
RET

0605 SAVE.ETC POP AF


LD A,(5C74);T.ADDR
SUB E0
LD (5C74),A;T.ADDR
CALL 1C8C;EXPT.EXP
CALL 2530;SYNTAX.Z
JR Z,0652;SA.DATA
LD BC,0011
LD A,(5C74);T.ADDR
AND A
JR Z,0621;SA.SPACE
LD C,22
0621 SA.SPACE RST 0030;BC.SPACES
PUSH DE
POP IX
LD B,0B
LD A,20
0629 SA.BLANK LD (DE),A
INC DE
DJNZ 0629;SA.BLANK
LD (IX+01),FF
CALL 2BF1;STK.FETCH
LD HL,FFF6
DEC BC

994
ADD HL,BC
INC BC
JR NC,064B;SA.NAME
LD A,(5C74);T.ADDR
AND A
JR NZ,0644;SA.NULL
0642 REPORT.F RST 0008;ERROR.1
DEFB 0E;"Invalid file name"
0644 SA.NULL LD A,B
OR C
JR Z,0652;SA.DATA
LD BC,000A
064B SA.NAME PUSH IX
POP HL
INC HL
EX DE,HL
LDIR
0652 SA.DATA RST 0018;GET.CHAR
CP E4
JR NZ,06A0;SA.SCR$
LD A,(5C74);T.ADDR
CP 03
JP Z,1C8A;REPORT.C
RST 0020;NEXT.CHAR
CALL 28B2;LOOK.VARS
SET 7,C
JR NC,0672;SA.V.OLD
LD HL,0000
LD A,(5C74);T.ADDR
DEC A
JR Z,0865;SA.V.NEW
0670 REPORT.2 RST 0008;ERROR.1
DEFB 01;"Variable not found"2
0672 SA.V.OLD JP NZ,1C8A;REPORT.C
CALL 2530;SYNTAX.Z
JR Z,0692;SA.DATA.1
INC HL

995
LD A,(HL)
LD (IX+0B),A
INC HL
LD A,(HL)
LD (IX+0C),A
INC HL
0685 SA.V.NEW LD (IX+0E),C
LD A,01
BIT 6,C
JR Z,068F;SA.V.TYPE
INC A
06B5 SA.V.TYPE LD (IX+00),A
0692 SA.DATA.1 EX DE,HL
RST 0020;NEXT.CHAR
CP 29
JR NZ,0672;SA.V.OLD
RST 0020;NEXT.CHAR
CALL 1BEE;CHECK.END
EX DE,HL
JP 075A;SA.ALL
06AD SA.SCR$ CP AA
JR NZ,06C3;SA.CODE
LD A,(5C74);T.ADDR
CP 03
JP Z,1C8A;REPORT.C
RST 0020;NEXT.CHAR
CALL 1BEE;CHECK.END
LD (IX+0B),00
LD (IX+0C),1B
LD HL,4000
LD (IX+0D),L
LD (IX+0E),H
JR 0710;SA.TYPE.3
06C3 SA.CODE CP AF
JR NZ,0716;SA.LINE
LD A,(5C74);T.ADDR
CP 03

996
JP Z,1C8A;REPORT.C
RST 0020;NEXT.CHAR
CALL 2048;PR.ST.END
JR NZ,06E1;SA.CODE.1
LD A,(5C74);T.ADDR
AND A
JP Z,1C8A;REPORT.C
CALL 1CE6;USE.ZERO
JR 06F0;SA.CODE.2
06E1 SA.CODE.1 CALL 1C82;EXPT.1NUM
RST 0018;GET.CHAR
CP 2C
JR Z,065F;SA.CODE.3
LD A,(5C74);T.ADDR
AND A
JP Z,1C8A;REPORT.C
06F0 SA.CODE.2 CALL 1CE6;USE.ZERO
JR 06F9;SA.CODE.4
06F5 SA.CODE.3 RST 0020;NEXT.CHAR
CALL 1C82;EXPT.1NUM
06F9 SA.CODE.4 CALL 1BEE;CHECK.END
CALL 1E99;FIND.INT2
LD (IX+0B),C
LD (IX+0C),B
CALL 1E99;FIND.INT2
LD (IX+0D),C
LD (IX+0E),B
LD H,B
LD L,C
0710 SA.TYPE.3 LD (IX+00,)03
JR 075A;SA.ALL
0716 SA.LINE CP CA
JR Z,0723;SA.LINE.1
CALL 1BEE;CHECK.END
LD (IX+0E),80
JR 073A;SA.TYPE.0
0723 SA.LINE.1 LD A,(5C74);T.ADDR

997
AND A
JP NZ,1C8A;REPORT.C
RST 0020;NEXT.CHAR
CALL 1C82;EXPT.1NUM
CALL 1BEE;CHECK.END
CALL 1E99;FIND.INT2
LD (IX+0D),C
LD (IX+0E),B
073A SA.TYPE.0 LD (IX+00),00
LD HL,(5C59);E.LINE
LD DE,(5C53);PROG
SCF
SBC HL,DE
LD (IX+0B),L
LD (IX+0C),H
LD HL,(5C4B);VARS
SBC
LD (IX+0F),L
LD (IX+10),H
EX DE,HL
075A SA.ALL LD A,(5C74);T.ADDR
AND A
JP Z,0970;SA.CONTRL
PUSH HL
LD BC,0011
ADD IX,BC
0767 LD.LOOK.H PUSH IX
LD DE,0011
XOR A
SCF
CALL 0556;LD.BYTES
POP IX
JR NC,0767;LD.LOOK.H
LD A,FE
CALL 1601;CHAN.OPEN
LD (IY+52),03;SCR.CT
LD C,80

998
LD A,(IX+00)
CP (IX-11)
JR NZ,078A;LD.TYPE
LD C,F6
078A LD.TYPE CP 04
JR NC,0767;LD.LOOK.H
LD DE,09C0;cassette messages
PUSH BC
CALL 0C0A;PO.MSG
POP BC
PUSH IX
POP DE
LD HL,FFF0
ADD HL,DE
LD B,0A
LD A,(HL)
INC A
JR NZ,07A6;LD.NAME
LD A,C
ADD A,B
LD C,A
07A6 LD.NAME INC DE
LD A,(DE)
CP (HL)
INC HL
JR NZ,07AD;LD,CH,PR
INC C
07AD LD.CH.PR RST 0010;PRINT.A.1
DJNZ 07A6;LD.NAME
BIT 7,C
JR NZ,0767;LD.LOOK.H
LD A,0D
RST 0010;PRINT.A.1
POP HL
LD A,(IX+00)
CP 03
JR Z,07CB;VR.CONTRL

999
LD A,(5C74);T.ADDR
DEC A
JP Z,0808;LD.CONTRL
CP 02
JP Z,08B6;ME.CONTRL
07CB VR.CONTRL PUSH HL
LD L,(IX-06)
LD H,(IX-05)
LD E,(IX+0B)
LD D,(IX+0C)
LD A,H
OR L
JR Z,07E9;VR.CONT.1
SBC HL,DE
JR C,0806;REPORT.R
JR Z,07E9;VR.CONT.1
LD A,(IX+00)
CP 03
JR NZ,0806;REPORT.R
07E9 VR.CONT.1 POP HL
LD A,H
OR L
JR NZ,07F4;VR.CONT.2
LD L,(IX+0D)
LD H,(IX+0E)
07F4 VR.CONT.2 PUSH HL
POP IX
LD A,(5C74);T.ADDR
CP 02
SCF
JR NZ,0800;VR.CONT.3
AND A
0800 VR.CONT.3 LD A,FF

0802 LD.BLOCK CALL 0556;LD.BUYES


RET C
0806 REPORT.R RST 0008;ERROR.1

1000
DEFB 1A;"Tape loading error"

0808 LD.CONTRL LD E,(IX+0B)


LD D,(IX+0C)
PUSH HL
LD A,H
OR L
JR NZ,0819;LD.CONT.1
INC DE
INC DE
INC DE
EX DE,HL
JR 0825;LD.CONT.2
0819 LD.CONT.1 LD L,(IX-06)
LD H,(IX-05)
EX DE,HL
SCF
SBC HL,DE
JR C,082E;LD.DATA
0825 LD.CONT.2 LD DE,0005
ADD HL,DE
LD B,H
LD C,L
CALL 1F05;TEST.ROOM
082E LD.DATA POP HL
LD A,(IX+00)
AND A
JR Z,0873;LD.PROG
LD A,H
OR L
JR Z,084C;LD.DATA.1
DEC HL
LD B,(HL)
DEC HL
LD C,(HL)
DEC HL
INC BC

1001
INC BC
INC BC
LD (5C5F),IX;X.PTR
CALL 19E8;RECLAIM.2
LD IX,(5C5F);X.PTR
084C LD.DATA.1 LD HL,(5C59);E.LINE
DEC HL
LD C,(IX+0B)
LD B,(IX+0C)
PUSH BC
INC BC
INC BC
INC BC
LD A,(IX-03)
PUSH AF
CALL 1655;MAKE.ROOM
INC HL
POP AF
LD (HL),A
POP DE
INC HL
LD (HL),E
INC HL
LD (HL),D
INC HL
PUSH HL
POP IX
SCF
LD A,FF
JP 0802;LD.BLOCK
0873 LD.PROG EX DE,HL
LD HL,(5C59);E.LINE
DEC HL
LD (5C5F),IX;X.PTR
LD C,(IX+0B)
LD B,(IX+0C)
PUSH BC

1002
CALL 19E5;RECLAIM.1
POP BC
PUSH HL
PUSH BC
CALL 1655;MAKE.ROOM
LD IX,(5C5F);X.PTR
INC HL
LD C,(IX+0F)
LD B,(IX+10)
ADD HL,BC
LD (5C4B),HL;VARS
LD H,(IX+0E)
LD A,H
AND C0
JR NZ,08AD;LD.PROG.1
LD L,(IX+0D)
LD (5C42),HL;NEWPPC
LD (IY+0A),00;NSPPC
08AD LD.PROG.1 POP DE
POP IX
SCF
LD A,FF
JP 0802;LD.BLOCK
08B6 ME.CONTRL LD C,(IX+0B)
LD B,(IX+0C)
PUSH BC
INC BC
RST 0030;BC.SPACES
LD (HL),80
EX DE,HL
POP DE
PUSH HL
PUSH HL
POP IX
SCF
LD A,FF
CALL 0802;LD.BLOCK

1003
POP HL
LD DE,(5C53);PROG
08D2 ME.NEW.LP LD A,(HL)
AND C0
JR NZ,08F0;ME.VAR.LP
08D7 ME.OLD.LP LD A,(DE)
INC DE
CP (HL)
INC HL
JR NZ,08DF;ME.OLD.L1
LD A,(DE)
CP (HL)
08DF ME.OLD.L1 DEC DE
DEC HL
JR NC,08EB;ME.NEW.L2
PUSH HL
EX DE,HL
CALL 19B8;NEXT.ONE
POP HL
JR 08D7;ME.OLD.LP
08EB ME.NEW.L2 CALL 092C;ME.ENTER
JR 08D2;ME.NEW.LP
08F0 ME.VAR.LP LD A,(HL)
LD C,A
CP 80
RET Z
PUSH HL
LD HL,(5C4B);VARS
08F9 ME.OLD.VP LD A,(HL)
CP 80
JR Z,0923;ME.VAR.L2
CP C
JR Z,0909;ME.OLD.V2
0901 ME.OLD.V1 PUSH BC
CALL 19B8;NEXT.ONE
POP BC
EX DE,HL

1004
JR 08F9;ME.OLD.VP
0909 ME.OLD.V2 AND E0
CP A0
JR NZ,0921;ME.VAR.L1
POP DE
PUSH DE
PUSH HL
0912 ME.OLD.V3 INC HL
INC DE
LD A,(DE)
CP (HL)
JR NZ,091E;ME.OLD.V4
RLA
JR NC,0912;ME.OLD.V3
POP HL
JR 0921;ME.VAR.L1
091E ME.OLD.V4 POP HL
JR 0901;ME.OLD.V1
0921 ME.VAR.L1 LD A,FF
0923 ME.VAR.L2 POP DE
EX DE,HL
INC A
SCF
CALL 092C;ME.ENTER
JR 08F0;ME.VAR.LP
092C ME.ENTER JR NZ,093E;ME.ENT.1
EX AF,AF'
LD (5C5F),HL;X.PTR
EX DE,HL
CALL 19B8;NEXT.ONE
CALL 19E8;RECLAIM.2
EX DE,HL
LD HL,(5C5F);X.PTR
EX AF,AF'
093E ME.ENT.1 EX AF,AF'
PUSH DE
CALL 19B8;NEXT.ONE

1005
LD (5C5F),HL;X.PTR
LD HL,(5C53);PROG
EX (SP),HL
PUSH BC
EX AF,AF'
JR C,0955;ME.ENT.2
DEC HL
CALL 1655;MAKE.ROOM
INC HL
JR 0958;ME.ENT.3
0955 ME.ENT.2 CALL 1655;MAKE.ROOM
0958 ME.ENT.3 INC HL
POP BC
POP DE
LD (5C53),DE;PROG
LD DE,(5C5F);X.PTR
PUSH BC
PUSH DE
EX DE,HL
LDIR
POP HL
POP BC
PUSH DE
CALL 19E8;RECLAIM.2
POP DE
RET
0970 SA.CONTRL PUSH HL
LD A,FD
CALL 1601;CHAN.OPEN
XOR A
LD DE,09A1;"Start tape" etc
CALL 0C0A;PO.MSG
SET 5,(IY+02);TV.FLAG
CALL 15D4;WAIT.KEY
PUSH IX
LD DE,0011
XOR A

1006
CALL 04C2 SA.BYTES
POP IX
LD B,32
0991 SA.1.SEC HALT
DJNZ 0991;SA,1,SEC
LD E,(IX+0B)
LD D,(IX+0C)
LD A,FF
POP IX
JP 04C2;SA.BYTES

09A1 DEFB 80
09A2 DEFM 'Start tape, then press any key'
09C0 DEFB '.'+128,13
09C2 DEFM 'Program:'
09CA DEFB ' '+128,13
09CC DEFM 'Number array:'
09D9 DEFB ' '+128,13
09DB DEFM 'Character array:'
09EB DEFB ' '+128,13
09ED DEFM 'Bytes:'
09F3 DEFB ' '+128

09F4 PRINT.OUT CALL 0B03;PO.FETCH


CP 20
JP NC,0AD9;PO.ABLE
CP 06
JR C,0A69;PO.QUEST
CP 18
JR NC,0A69;PO.QUEST
LD HL,0A0B;control table
LD E,A
LD D,00
ADD HL,DE
LD E,(HL)
ADD HL,DE
PUSH HL

1007
JP 0B03;PO.FETCH

0A11 DEFB 0A5F-0A11;PO.COMMA


DEFB 0A69-0A12;PO.QUEST
DEFB 0A23-0A13;PO.BACK.1
DEFB 0A3D-0A14;PO.RIGHT
DEFB 0A69-0A15;PO.QUEST
DEFB 0A69-0A16;PO.QUEST
DEFB 0A69-0A17;PO.QUEST
DEFB 0A4F-0A18;PO.ENTER
DEFB 0A69-0A19;PO.QUEST
DEFB 0A69-0A1A;PO.QUEST
DEFB 0A7A-0A1B;PO.1.OPER
DEFB 0A7A-0A1C;PO.1.OPER
DEFB 0A7A-0A1D;PO.1.OPER
DEFB 0A7A-0A1E;PO.1.OPER
DEFB 0A7A-0A1F;PO.1.OPER
DEFB 0A7A-0A20;PO.1.OPER
DEFB 0A75-0A21;PO.2.OPER
DEFB 0A75-0A22;PO.2.OPER

0A23 PO.BACK 1 INC C


LD A,22
CP C
JR NZ,0A3A;PO.BACK.3
BIT 1,(IY+01);FLAGS
JR NZ,0A38;PO.BACK.2
INC B
LD C,02
LD A,18
CP B
JR NZ,0A3A;PO.BACK.3
DEC B
0A38 PO.BACK.2 LD C,21
0A3A PO.BACK.3 JP 0DD9;CL.SET

0A3D PO.RIGHT LD A,(5C91);P.FLAG

1008
PUSH AF
LD (IY+57),01;P.FLAG
LD A,20
CALL 0B65;PO.CHAR
POP AF
LD (5C91),A;P.FLAG
RET

0A4F PO.ENTER BIT 1,(IY+01);FLAGS


JP NZ,0ECD;COPY.BUFF
LD C,21
CALL 0C55;PO.SCR
DEC B
JP 0DD9;CL.SET

0A5F PO.COMMA CALL 0B03;PO.FETCH


LD A,C
DEC A
DEC A
AND 10
JR 0AC3;PO.FILL

0A69 PO.QUEST LD A,3F


JR 0AD9;PO.ABLE

0A6D PO.TV.2 LD DE,0A87;PO.CONT


LD (5C0F),A;TVDATA hi
JR 0A80;PO.CHANGE
0A75 PO.2.OPER LD DE,0A6D;PO.TV.2
JR 0A7D;PO.TV.1
0A7A PO.1.OPER LD DE,0A87;PO.CONT
0A7D PO.TV.1 LD (5C0E),A;TVDATA
0A80 PO.CHANGE LD HL,(5C51);CURCHL
LD (HL),E
INC HL
LD (HL),D
RET

1009
0A87 PO.CONT LD DE,09F4;PRINT.OUT
CALL 0A80;PO.CHANGE
LD HL,(5C0E);TVDATA
LD D,A
LD A,L
CP 16
JP C,2211;CO.TEMPS
JR NZ,0AC2;PO.TAB
LD B,H
LD C,D
LD A,1F
SUB C
JR C,0AAC;PO.AT.ERR
ADD A,02
LD C,A
BIT 1,(IY+01);FLAGS
JR NZ,0ABF;PO.AT.SET
LD A,16
SUB B
0AAC PO.AT.ERR JP C,1E9F;REPORT.B
INC A
LD B,A
INC B
BIT 0,(IY+02);TVFLAG
JP NZ,0C55;PO.SCR
CP (IY+31);DF.SZ
JP C,0C86;REPORT.5
0ABF PO.AT.SET JP 0DD9;CL.SET
0AC2 PO.TAB LD A,H
0AC3 PO.FILL CALL 0B03;PO.FETCH
ADD A,C
DEC A
AND 1F
RET Z
LD D,A
SET 0,(IY+01);FLAGS
0AD0 PO.SPACE LD A,20

1010
CALL 0C3B;PO.SAVE
DEC D
JR NZ,0AD0;PO.SPACE
RET

0AD9 PO.ABLE CALL 0B24;PO.ANY


0ADC PO.STORE BIT 1,(IY+01);FLAGS
JR NZ,0AFC;PO.ST.PR
BIT 0,(IY+02);TVFLAG
JR NZ,0AF0;PO.ST.E
LD (5C88),BC;S.POSN
LD (5C84),HL;DF.CC
RET
0AF0 PO.ST.E LD (5C8A),BC;SPOSNL
LD (5C82),BC;ECHO.E
LD (5C86),HL;DFCCL
RET
0AFC PO.ST.PR LD (IY+45),C;P.POSN
LD (5C80),HL;PR.CC
RET

0B03 PO.FETCH BIT 1,(IY+01);FLAGS


JR NZ,0B1D;PO.F.PR
LD BC,(5C88);S.POSN
LD HL,(5C84);DF.CC
BIT 0,(IY+02);TVFLAG
RET Z
LD BC,(5C8A);SPOSNL
LD HL,(5C86);DFCCL
RET
0A1D PO.F.PR LD C,(IY+45);P.POSN
LD HL,(5C80);PR.CC
RET

0B24 PO.ANY CP 80
JR C,0B65;PO.CHAR
CP 90

1011
JR NC,0B52;PO.T&UDG
LD B,A
CALL 0B3B;PO.GR.1
CALL 0B03;PO.FETCH
LD DE,5C92;MEMBOT
JR 0B7F;PR.ALL
0B38 PO.GR.1 LD HL,5C92;MEMBOT
CALL 0B3E;PO.GR.2
0B3E PO.GR.2 RR B
SBC A,A
AND 0F
LD C,A
RR B
SBC A,A
AND F0
OR C
LD C,04
0B4C PO.GR.3 LD (HL),A
INC HL
DEC C
JR NZ,0B4C;PO.GR.3
RET
0B52 PO.T&UDG SUB A5 [Plus 2: JP 3B9F
JR NC,0B5F;PO.T NOP]
ADD A,15
PUSH BC
LD BC,(5C7B);UDG
JR 0B6A;PO.CHAR.2
0B5F PO.T CALL 0C10;PO.TOKENS
JP 0B03;PO.FETCH
0B65 PO.CHAR PUSH BC
LD BC,(5C36);CHARS
0B6A PO.CHAR.2 EX DE,HL
LD HL,5C3B;FLAGS
RES 0,(HL)
CP 20
JR NZ,0B76;PO.CHAR.3

1012
SET 0,(HL)
0B76 PO.CHAR.3 LD H,00
LD L,A
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,BC
POP BC
EX DE,HL
0B7F PR.ALL LD A,C
DEC A
LD A,21
JR NZ,0B93;PR.ALL.1
DEC B
LD C,A
BIT 1,(IY+01);FLAGS
JR Z,0B93;PR.ALL.1
PUSH DE
CALL 0ECD;COPY.BUFF
POP DE
LD A,C
0B93 PR.ALL.1 CP C
PUSH DE
CALL Z,0C55;PO.SCR
POP DE
PUSH BC
PUSH HL
LD A,(5C91);P.FLAG
LD B,FF
RRA
JR C,0BA4;PR.ALL.2
INC B
0BA4 PR.ALL.2 RRA
RRA
SBC A,A
LD C,A
LD A,08

1013
AND A
BIT 1,(IY+01);FLAGS
JR Z,0BB6;PR.ALL.3
SET 1,(IY+30);FLAGS2
SCF
0BB6 PR.ALL.3 EX DE,HL
0BB7 PR.ALL.4 EX AF,AF'
LD A,(DE)
AND B
XOR (HL)
XOR C
LD (DE),A
EX AF,AF'
JR C,0BD3;PR.ALL.6
INC D
0BC1 PR.ALL.5 INC HL
DEC A
JR NZ,0BB7;PR.ALL.4
EX DE,HL
DEC H
BIT 1,(IY+01);FLAGS
CALL Z,0BDB;PO.ATTR
POP HL
POP BC
DEC C
INC HL
RET
0BD3 PR.ALL.6 EX AF,AF'
LD A,20
ADD A,E
LD E,A
EX AF,AF'
JR 0BC1;PR.ALL.5
0BDB PO.ATTR LD A,H
RRCA
RRCA
RRCA

1014
AND 03
OR 58
LD H,A
LD DE,(5C8F);ATTR.T
LD A,(HL)
XOR E
AND D
XOR E
BIT 6,(IY+57);P.FLAG
JR Z,0BFA;PO.ATTR.1
AND C7
BIT 2,A
JR NZ,0BFA;PO.ATTR.1
XOR 38
0BFA PO.ATTR.1 BIT 4,(IY+57);P.FLAG
JR Z,0C08;PO.ATTR.2
AND F8
BIT 5,A
JR NZ,0C08;PO.ATTR.2
XOR 07
0C08 PO.ATTR.2 LD (HL),A
RET

0C0A PO.MSG PUSH HL


LD H,00
EX (SP),HL
JR 0C14;PO.TABLE
0C10 PO.TOKENS LD DE,0095;token table
PUSH AF
0C14 PO.TABLE CALL 0C41;PO.SEARCH
JR C,0C22;PO.EACH
LD A,20
BIT 0,(IY+01);FLAGS
CALL Z,0C3B;PO.SAVE
0C22 PO.EACH LD A,(DE)
AND 7F
CALL 0C3B;PO.SAVE

1015
LD A,(DE)
INC DE
ADD A,A
JR NZ,0C22;PO.EACH
POP DE
CP 48
JR Z,0C35;PO.TRSP
CP 82
RET C
0C35 PO.TRSP LD A,D
CP 03
RET C
LD A,20
0C3B PO.SAVE PUSH DE
EXX
RST 0010;PRINT.A.1
EXX
POP DE
RET
0C41 PO.SEARCH PUSH AF
EX DE,HL
INC A
0C44 PO.STEP BIT 7,(HL)
INC HL
JR Z,0C44;PO.STEP
DEC A
JR NZ,0C44;PO.STEP
EX DE,HL
POP AF
CP 20
RET C
LD A,(DE)
SUB 41
RET
0C55 PO.SCR BIT 1,(IY+01);FLAGS
RET NZ
LD D,0DD9;CL.SET

1016
PUSH DE
LD A,B
BIT 0,(IY+02);TVFLAG
JP NZ,0D02;PO.SCR.4
CP (IY+31);DF.SZ
JR C,0C86;REPORT.5
RET NZ
BIT 4,(IY+02);TVFLAG
JR Z,0C88;PO.SCR.2
LD E,(IY+2D);BREG
DEC E
JR Z,0CD2;PO.SCR.3
LD A,00
CALL 1601;CHAN.OPEN
LD SP,(5C3F);LIST.SP
RES 4,(IY+02);TVFLAG
RET
0C86 REPORT.5 RST 0008;ERROR.1
DEFB 04;"Out of screen"
0C88 PO.SCR.2 DEC (IY+52);SCR.CT
JR NZ,0CD2;PO.SCR.3
LD A,18
SUB B
LD (5C8C),A;SCR.CT
LD HL,(5C8F);ATTR.T
PUSH HL
LD A,(5C91);P.FLAG
PUSH AF
LD A,FD
CALL 1601;CHAN.OPEN
XOR A
LD DE,0CF8;"scroll?"
CALL 0C0A;PO.MSG
SET 5,(IY+02);TVFLAG
LD HL,5C3B;FLAGS
SET 3,(HL)
RES 5,(HL)

1017
EXX
CALL 15D4;WAIT.KEY
EXX
CP 20
JR Z,0D00;REPORT.D
CP E2
JR Z,0D00;REPORT.D
OR 20
CP 6E
JR Z,0D00;REPORT.D
LD A,FE
CALL 1601;CHAN.OPEN
POP AF
LD (5C91),A;P.FLAG
POP HL
LD (5C8F),HL;ATTR.T
0CD2 PO.SCR.3 CALL 0DFE;CL.SC.ALL
LD B,(IY+31);DF.SZ
INC B
LD C,21
PUSH BC
CALL 0E9B;CL.ADDR
LD A,H
RRCA
RRCA
RRCA
AND 03
OR 58
LD H,A
LD DE,5AE0;1st attribute of last line
LD A,(DE)
LD C,(HL)
LD B,20
EX DE,HL
0CF0 PO.SCR.3A LD (DE),A
LD (HL),C
INC DE

1018
INC HL
DJNZ 0CF0;PO.SCR.3A
POP BC
RET

0CF8 DEFB 80
DEFM 'scroll'
DEFB '?'+128

0D00 REPORT.D RST 0008;ERROR.1


DEFB 0C;"BREAK - CONT repeats"
0D02 PO.SCR.4 CP 02
JR C,0C86;REPORT.5
ADD A,(IY+31);DF.SZ
SUB 19
RET NC
NEG
PUSH BC
LD B,A
LD HL,(5C8F);ATTR.T
PUSH HL
LD HL,(5C91);P.FLAG
PUSH HL
CALL 0D4D;TEMPS
LD A,B
0D1C PO.SCR.4A PUSH AF
LD HL,5C6B;DF.SZ
LD B,(HL)
LD A,B
INC A
LD (HL),A
LD HL,5C89;S.POSN hi
CP (HL)
JR C,0D2D;PO.SCR.4B
INC (HL)
LD B,18
0D2D PO.SCR.4B CALL 0E00;CL.SCROLL

1019
POP AF
DEC A
JR NZ,0D1C;PO.SCR.4A
POP HL
LD (IY+57),L;P.FLAG
POP HL
LD (5C8F);ATTR.T
LD BC,(5C88);S.POSN
RES 0,(IY+02);TVFLAG
CALL 0DD9;CL.SET
SET 0,(IY+02);TVFLAG
POP BC
RET

0D4D TEMPS XOR A


LD HL,(5C8D);ATTR.P
BIT 0,(IY+02);TVFLAG
JR Z,0D5B;TEMPS.1
LD H,A
LD L,(IY+0E);BORDCR
0D5B TEMPS.1 LD (5C8F),HL;ATTR.T
LD HL,5C91;P.FLAG
JR NZ,0D65;TEMPS.2
LD A,(HL)
RRCA
0D65 TEMPS.2 XOR (HL)
AND 55
XOR (HL)
LD (HL),A
RET

0D6B CLS CALL 0DAF;CL.ALL


0D6E CLS.LOWER LD HL,5C3C;TV.FLAG
RES 5,(HL)
SET 0,(HL)
CALL 0D4D;TEMPS
LD B,(IY+31);DF.SZ

1020
CALL 0E44;CL.LINE
LD HL,5AC0;1st attribute of last line
LD A,(5C8D);ATTR.P
DEC B
JR 0D8E;CLS.3
0D87 CLS.1 LD C,20
0D89 CLS.2 DEC HL
LD (HL),A
DEC C
JR NZ,0D89;CLS.2
0D8E CLS.3 DJNZ 0D87;CLS.1
LD (IY+31),02;DF.SZ
0D94 CL.CHAN LD A,FD
CALL 1601;CHAN.OPEN
LD HL,(5C51);CURCHL
LD DE,09F4;PRINT.OUT
AND A
0DA0 CL.CHAN.A LD (L),E
INC HL
LD (HL),D
INC HL
LD DE,10A8;KEY.INPUT
CCF
JR C,0DA0;CL.CHAN.A
LD BC,1721
JR 0DD9;CL.SET

0DAF CL.ALL LD HL,0000


LD (5C7D),HL;COORDS
RES 0,(IY+30);FLAGS2
CALL 0D94;CL.CHAN
LD A,FE
CALL 1601;CHAN.OPEN
CALL 0D4D;TEMPS
LD B,18
CALL 0E44;CL.LINE
LD HL,(5C51);CURCHL

1021
LD DE,09F4;PRINT.OUY
LD (HL),E
INC HL
LD (HL),D
LD (IY+52),01;SCR.CT
LD BC,1821
0DD9 CL.SET LD HL,5B00;print buffer
BIT 1,(IY+01);FLAGS
JR NZ,0DF4;CL.SET.2
LD A,B
BIT 0,(IY+02);TVFLAG
JR Z,0DEE;CL.SET.1
ADD A,(IY+31);DF.SZ
SUB 18
0DEE CL.SET.1 PUSH BC
LD B,A
CALL 0E9B;CL.ADDR
POP BC
0DF4 CL.SET.2 LD A,21
SUB C
LD E,A
LD D,00
ADD HL,DE
JP 0ADC;PO.STORE

0DFE CL.SC.ALL LD B,17


0E00 CL.SCROLL CALL 0E9B;CL.ADDR
LD C,08
0E05 CL.SCR.1 PUSH BC
PUSH HL
LD A,B
AND 07
LD A,B
JR NZ,0E19;CL.SCR.3
0E0D CL.SCR.2 EX DE,HL
LD HL,F8E0;-720
ADD HL,DE

1022
EX DE,HL
LD BC,0020
DEC A
LDIR
0E19 CL.SCR.3 EX DE,HL
LD HL,FFE0;-20
ADD HL,DE
EX DE,HL
LD B,A
AND 07
RRCA
RRCA
RRCA
LD C,A
LD A,B
LD B,00
LDIR
LD B,07
ADD HL,BC
AND F8
JR NZ,0E0D;CL.SCR.2
POP HL
INC H
POP BC
DEC C
JR NZ,0E05;CL.SCR.1
CALL 0E88;CL.ATTR
LD HL,FFE0;-20
ADD HL,DE
EX DE,HL
LDIR
LD B,01

0E44 CL.LINE PUSH BC


CALL 0E9B;CL.ADDR
LD C,08
0E4A CL.LINE.1 PUSH BC

1023
PUSH HL
LD A,B
0E4D CL.LINE.2 AND 07
RRCA
RRCA
RRCA
LD C,A
LD A,B
LD B,00
DEC C
LD D,H
LD E,L
LD (HL),00
INC DE
LDIR
LD DE,0701
ADD HL,DE
DEC A
AND F8
LD B,A
JR NZ,0E4D;CL.LINE.2
POP HL
INC H
POP BC
DEC C
JR NZ,0E4A;CL.LINE.1
CALL 0E88;CL.ATTR
LD H,D
LD L,E
INC DE
LD A,(5C8D);ATTR.P
BIT 0,(IY+02);TVFLAG
JR Z,0E80;CL.LINE.3
LD A,(5C48);BORDCR
0E80 CL.LINE.3 LD (HL),A
DEC BC
LDIR

1024
POP BC
LD C,21
RET

0E88 CL.ATTR LD A,H


RRCA
RRCA
RRCA
DEC A
OR 50
LD H,A
EX DE,HL
LD H,C
LD L,B
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL
LD B,H
LD C,L
RET

0E9B CL.ADDR LD A,18


SUB B
LD D,A
RRCA
RRCA
RRCA
AND E0
LD L,A
LD A,D
AND 18
OR 40
LD H,A
RET

1025
0EAC COPY DI
LD B,B0
LD HL,4000
0EB2 COPY.1 PUSH HL
PUSH BC
CALL 0EF4;COPY.LINE
POP BC
POP HL
INC H
LD A,H
AND 07
JR NZ,0EC9;COPY.2
LD A,L
ADD A,20
LD L,A
CCF
SBC A,A
AND F8
ADD A,H
LD H,A
0EC9 COPY.2 DJNZ 0EB2;COPY.1
JR 0EDA;COPY.END

0ECD COPY.BUFF DI
LD HL,5B00;print buffer
LD B,08
0ED3 COPY.3 PUSH BC
CALL 0EF4;COPY.LINE
POP BC
DJNZ 0ED3;COPY.3
0EDA COPY.END LD A,04
OUT (FB),A
EI
0EDF CLEAR.PRB LD HL,5B00;print buffer
LD (IY+46),L;PR.CC
XOR A
LD B,A

1026
0EE7 PRB.BYTES LD (HL),A
INC HL
DJNZ 0EE7;PRB.BYTES
RES 1,(IY+30);FLAGS2
LD C,21
JP 0DD9;CL.SET

0EF4 COPY.LINE LD A,B


CP 03
SBC A,A
AND 02
OUT (FB),A
LD D,A
0EFD COPY.L.1 CALL 1F54;BREAK.KEY
JR C,0F0C;COPY.L.2
LD A,04
OUT (FB),A
EI
CALL 0EDF;CLEAR.PRB
RST 0008;ERROR.1
DEFB 0C;"BREAK/CONT repeats"
0F0C COPY.L.2 IN A,(FB)
ADD A,A
RET M
JR NC,0EFD;COPY.L.1
LD C,20
0F14 COPY.L.3 LD E,(HL)
INC HL
LD B,08
0F18 COPY.L.4 RL D
RL E
RR D
0F1E COPY.L.5 IN A,(FB)
RRA
JR NC,0F1E;COPY.L.5
LD A,D
OUT (FB),A

1027
DJNZ 0F18;COPY.L.4
DEC C
JR NZ,0F14;COPY.L.3
RET

0F2C EDITOR LD HL,(5C3D);ERR.SP


PUSH HL
0F30 ED AGAIN LD HL,107F;ED.ERROR
PUSH HL
LD (5C3D),SP;ERR.SP
0F38 ED.LOOP CALL 15D4;WAIT.KEY
PUSH AF
LD D,00
LD E,(IY-FF);PIP
LD HL,00C8
CALL 03B5;BEEPER
POP AF
LD HL,0F38;ED.LOOP
PUSH HL
CP 18
JR NC,0F81;ADD.CHAR
CP 07
JR C,0F81;ADD.CHAR
CP 10
JR C,0F92;ED.KEYS
LD BC,0002
LD D,A
CP 16
JR C,0F6C;ED.CONTR
INC BC
BIT 7,(IY+37);FLAGX
JP Z,101E;ED.IGNORE
CALL 15D4;WAIT.KEY
LD E,A
0F6C ED.CONTR CALL 15D4;WAIT.KEY
PUSH DE
LD HL,(5C5B);K.CUR

1028
RES 0,(IY+07);MODE
CALL 1655;MAKE.ROOM
POP BC
INC HL
LD (HL),B
INC HL
LD (HL),C
JR 0F8B;ADD.CH.1

0F81 ADD.CHAR RES 0,(IY+07);MODE


LD HL,(5C5B);K.CUR
CALL 1652;ONE.SPACE
0F8B ADD.CH.1 LD (DE),A
INC DE
LD (5C5B),DE;K.CUR
RET

0F92 ED.KEYS LD E,A


LD D,00
LD HL,0F99;edit key table
ADD HL,DE
LD E,(HL)
ADD HL,DE
PUSH HL
LD HL,(5C5B);K.CUR
RET

0FA0 DEFB 0FA9-0FA0;ED.EDIT


DEFB 1007-0FA1;ED.LEFT
DEFB 100C-0FA2;ED.RIGHT
DEFB 0FF3-0FA3;ED.DOWN
DEFB 1059-0FA4;ED.UP
DEFB 1015-0FA5;ED.DELETE
DEFB 1024-0FA6;ED.ENTER
DEFB 1076-0FA7;ED.SYMBOL
DEFB 107C-0FA8;ED.GRAPH

1029
0FA9 ED.EDIT LD HL,(5C49);E.PPC
BIT 5,(IY+37);FLAGX
JP NZ,1097;CLEAR.SP
CALL 196E;LINE.ADDR
CALL 1695;LINE.NO
LD A,D
OR E
JP Z,1097;CLEAR.SP
PUSH HL
INC HL
LD C,(HL)
INC HL
LD B,(HL)
LD HL,000A
ADD HL,BC
LD B,H
LD C,L
CALL 1F05;TEST.ROOM
CALL 1097;CLEAR.SP
LD HL,(5C51);CURCHL
EX (SP),HL
PUSH HL
LD A,FF
CALL 1601;CHAN.OPEN
POP HL
DEC HL
DEC (IY+0F);E.PPC
CALL 1855;OUT.LINE
INC (IY+0F);E.PPC
LD HL,(5C59);E.LINE
INC HL
INC HL
INC HL
INC HL
LD (5C5B),HL;K.CUR
POP HL
CALL 1615;CHAN.FLAG

1030
RET

0FF3 ED.DOWN BIT 5,(IY+37);FLAGX


JR NZ,1001;ED.STOP
LD HL,5C49;E.PPC
CALL 190F;LN.FETCH
JR 106E;ED.LIST
1001 ED.STOP LD (IY+00);ERR.NR
JR 1024;ED.ENTER

1007 ED.LEFT CALL 1031;ED.EDGE


JR 1011;ED.CUR

100C ED.RIGHT LD A,(HL)


CP 0D
RET Z
INC HL
1011 ED.CUR LD (5C5B),HL;K.CUR
RET

1015 ED.DELETE CALL 1031;ED.EDGE


LD BC,0001
JP 19E8;RECLAIM.2
101E ED.IGNORE CALL 15D4;WAIT.KEY
CALL 15D4;WAIT.KEY

1024 ED.ENTER POP HL


POP HL
1026 ED.END POP HL
LD (5C3D),HL;ERR.SP
BIT 7,(IY+00);ERR.NR
RET NZ
LD SP,HL
RET

1031 ED.EDGE SCF


CALL 1195;SET.DE

1031
SBC HL,DE
ADD HL,DE
INC HL
POP BC
RET C
PUSH BC
LD B,H
LD C,L
103E ED.EDGE.1 LD H,D
LD L,E
INC HL
LD A,(DE)
AND F0
CP 10
JR NZ,1051;ED.EDGE.2
INC HL
LD A,(DE)
SUB 17
ADC A,00
JR NZ,1051;ED.EDGE.2
INC HL
1051 ED.EDGE.2 AND A
SBC HL,BC
ADD HL,BC
EX DE,HL
JR C,103E;ED.EDGE.1
RET

1059 ED.UP BIT 5,(IY+37);FLAGX


RET NZ
LD HL,(5C49);E.PPC
CALL 196E;LINE.ADDR
EX DE,HL
CALL 1695;LINE.NO
LD HL,5C4A;E.PPC hi
CALL 191C;LN.STORE
106E ED.LIST CALL 1795;AUTO.LIST

1032
LD A,00
JP 1601;CHAN.OPEN

1076 ED.SYMBOL BIT 7,(IY+37);FLAGX


JR Z,1024;ED.ENTER
107C ED.GRAPH JP 0F81;ADD.CHAR

107F ED.ERROR BIT 4,(IY+30);FLAGS2


JR Z,1026;ED.END
LD (IY+00),FF;ERR.NR
LD D,00
LD E,(IY-02);RASP
LD HL,1A90
CALL 03B5;BEEPER
JP 0F30;ED.AGAIN

1097 CLEAR.SP PUSH HL


CALL 1190;SET.HL
DEC HL
CALL 19E5;RECLAIM.1
LD (5C5B),HL;K.CUR
LD (IY+07),00;MODE
POP HL
RET

10A8 KEY.INPUT BIT 3,(IY+02);TV.FLAG


CALL NZ,111D;ED.COPY
AND A
BIT 5,(IY+01);FLAGS
RET Z
LD A,(5C08);LAST.K
RES 5,(IY+01);FLAGS
PUSH AF
BIT 5,(IY+02);TV.FLAG
CALL NZ,0D6E;CLS.LOWER
POP AF
CP 20

1033
JR NC,111B;KEY.DONE
CP 10
JR NC,10FA;KEY.CONTR
CP 06
JR NC,10DB;KEY.M&CL
LD B,A
AND 01
LD C,A
LD A,B
RRA
ADD A,12
JR 1105;KEY.DATA
10DB KEY.M&CL JR NZ,10E6;KEY.MODE
LD HL,5C6A;FLAGS2
LD A,08
XOR (HL)
LD (HL),A
JR 10F4;KEY.FLAG
10E6 KEY.MODE CP 0E
RET C
SUB 0D
LD HL,5C41;MODE
CP (HL)
LD (HL),A
JR NZ,10F4;KEY.FLAG
LD (HL),00
10F4 KEY.FLAG SET 3,(IY+02);TV.FLAG
CP A
RET
10FA KEY.CONTR LD B,A
AND 07
LD C,A
LD A,10
BIT 3,B
JR NZ,1105;KEY.DATA
INC A
1105 KEY.DATA LD (IY-2D),C;K.DATA

1034
LD DE,110D;KEY.NEXT
JR 1113;KEY.CHAN
110D KEY.NEXT LD A,(5C0D);K.DATA
LD DE,10A8;KEY.INPUT
1113 KEY.CHAN LD HL,(5C4F);CHANS
INC HL
INC HL
LD (HL),E
INC HL
LD (HL),D
1118 KEY.DONE SCF
RET
111D ED.COPY CALL 0D4D;TEMPS
RES 3,(IY+02);TV.FLAG
RES 5,(IY+02);TV.FLAG
LD HL,(5C8A);SPOSNL
PUSH HL
LD HL,(5C3D);ERR.SP
PUSH HL
LD HL,1167;ED.FULL
PUSH HL
LD (5C3D),SP;ERR.SP
LD HL,(5C82);ECHO.E
PUSH HL
SCF
CALL 1195;SET.DE
EX DE,HL
CALL 187D;OUT.LINE.2
EX DE,HL
CALL 18E1;OUT.CURS
LD HL,(5C8A);SPOSNL
EX (SP),HL
EX DE,HL
CALL 0D4D;TEMPS
1150 ED.BLANK LD A,(5C8B);SPOSNL hi
SUB D
JR C,117C;ED.C.DONE

1035
JR NZ,115E;ED.SPACES
LD A,E
SUB A,(IY+50);SPOSNL
JR NC,117C;ED.C.DONE
11BE ED.SPACES LD A,20
PUSH DE
CALL 09F4;PRINT.OUT
POP DE
JR 1150;ED.BLANK
1167 ED.FULL LD D,00
LD E,(IY-02);RASP
LD HL,1A90
CALL 03B5;BEEPER
LD (IY+00),FF;ERR.NR
LD DE,(5C8A);SPOSNL
JR 117E;ED.C.END
117C ED.C.DONE POP DE
POP HL
117E ED.C.END POP HL
LD (5C3D),HL;ERR.SP
POP BC
PUSH DE
CALL 0DD9;CL.SET
POP HL
LD (5C82),HL;ECHO.E
LD (IY+26),00;X.PTR hi
RET

1190 SET.HL LD HL,(5C61);WORKSP


DEC HL
AND A
1195 SET.DE LD DE,(5C59);E.LINE
BIT 5,(IY+37);FLAGX
RET Z
LD DE,(5C61);WORKSP
RET C
LD HL,(5C63);STKBOT

1036
RET

11A7 REMOVE.FP LD A,(HL)


CP 0E
LD BC,0006
CALL Z,19E8;RECLAIM.2
LD A,(HL)
INC HL
CP 0D
JR NZ,11A7;REMOVE.FP
RET

11B7 NEW DI
LD A,FF
LD DE,(5CB2);RAMTOP
EXX
LD BC,(5CB4);P.RAMT
LD DE,(5C38);RASP/PIP
LD HL,(5C7B);UDG
EXX
11CB START.NEW LD B,A
LD A,07
OUT (FE),A
LD A,3F
LD I,A
DEFB 00,00,00,00,00,00
11DA RAM.CHECK LD H,D
LD L,E
11DC RAM.FILL LD (HL),02
DEC HL
CP H
JR NZ,11DC;RAM.FILL
11E2 RAM.READ AND A
SBC HL,DE
ADD HL,DE
INC HL
JR NC,11EF;RAM.DONE

1037
DEC (HL)
JR Z,11EF;RAM.DONE
DEC (HL)
JR Z,11E2;RAM.READ
11EF RAM.DONE DEC HL
EXX
LD (5CB4),BC;P.RAMT
LD (5C38),DE;RASP/PIP
LD (5C7B),HL;UDG
EXX
INC B
JR Z,1219;RAM.SET
LD (5CB4),HL;P.RAMT
LD DE,3EAF;last UDG byte
LD BC,00A8;8 x 21 bytes
EX DE,HL
LDDR
EX DE,HL
INC HL
LD (5C7B),HL;UDG
DEC HL
LD BC,0040
LD (5C38),BC;RASP/PIP

1219 RAM.SET LD (5CB2),HL;RAM.TOP


LD HL,3C00
LD (5C36),HL;CHARS
LD HL,(5CB2);RAM.TOP
LD (HL),3E
DEC HL
LD SP,HL
DEC HL
DEC HL
LD (5C3D),HL;ERR.SP
IM 1
LD IY,5C3A;ERR.NR
EI

1038
LD HL,5CB6
LD (5C4F),HL;CHANS
LD DE,15AF;initial channel data
LD BC,0015
EX DE,HL
LDIR
EX DE,HL
DEC HL
LD (5C57),HL;DATADD
INC HL
LD (5C53),HL;PROG
LD (5C4B),HL;VARS
LD (HL),80
INC HL
LD (5C59),HL;E.LINE
LD (HL),0D
INC HL
LD (HL),80
INC HL
LD (5C61),HL;WORKSP
LD (5C63),HL;STKBOT
LD (5C65),HL;STKEND
LD A,38
LD (5C8D),A;ATTR.P
LD (5C8F),A;ATTR.T
LD (5C48),A;BORDCR
LD HL,0523
LD (5C09),HL;REPDEL/REPPER
DEC (IY-3A);KSTATE0
DEC (IY-36);KSTATE4
LD HL,15C6;initial stream data
LD DE,5C10;STRMS
LD BC,000E
LDIR
SET 1,(IY+01);FLAGS
CALL 0EDF;CLEAR.PRB
LD (IY+31),02;DF.SZ

1039
CALL 0D6B;CLS
XOR A
LD DE,1538;copyright message
CALL 0C0A;PO.MSG
SET 5,(IY+02);TV.FLAG
JR 12A9;MAIN.1

12A2 MAIN.EXEC LD (IY+31),02;DF.SZ


CALL 1795;AUTO.LIST
12A9 MAIN.1 CALL 16B0;SET.MIN
12AC MAIN.2 LD A,00
CALL 1601;CHAN.OPEN
CALL 0F2C;EDITOR
CALL 1B17;LINE.SCAN
BIT 7,(IY+00);ERR.NR
JR NZ,12CF;MAIN.3
BIT 4,(IY+30);FLAGS2
JR Z,1303;MAIN.4
LD HL,(5C59);E.LINE
CALL 11A7;REMOVE.FP
LD (IY+00),FF;ERR.NR
JR 12AC;MAIN.2
12CF MAIN.3 LD HL,(5C59);E.LINE
LD (5C5D),HL;CH.ADD
CALL 19FB;E.LINE.NO
LD A,B
OR C
JP NZ,155D;MAIN.ADD
RST 0018;GET.CHAR
CP 0D
JR Z,12A2;MAIN.EXEC
BIT 0,(IY+46);FLAGS2
CALL NZ,0DAF;CL.ALL
CALL 0D6E;CLS.LOWER
LD A,19
SUB A,(IY+4F);S.POSN hi
LD (5C8C),A;SCR.CT

1040
SET 7,(IY+01);FLAGS
LD (IY+00),FF;ERR.NR
LD (IY+0A),01;NSPPC
CALL 1B8A;LINE.RUN

1303 MAIN.4 HALT


RES 5,(IY+01);FLAGS
BIT 1,(IY+30);FLAGS2
CALL NZ,0ECD;COPY.BUFF
LD A,(5C3A);ERR.NR
INC A
1313 MAIN.G PUSH AF
LD HL,0000
LD (IY+37),H;FLAGX
LD (IY+26),H;X.PTR hi
LD (5C0B),HL;DEFADD
LD HL,0001
LD (5C16),HL;STRMS 6
CALL 16B0;SET.MIN
RES 5,(IY+37);FLAGX
CALL 0D6E;CLS.LOWER
SET 5,(IY+02);TV.FLAG
POP AF
LD B,A
CP 0A
JR C,133C;MAIN.5
ADD A,07
133C MAIN.5 CALL 15EF;OUT.CODE
LD A,20
RST 0010;PRINT.A.1
LD A,B
LD DE,1391;report table
CALL 0C0A;PO.MSG
XOR A [Plus 2: CALL 3B3B
LD DE,1536;comma space NOP ]
CALL 0C0A;PO.MSG
LD BC,(5C45);PPC

1041
CALL 1A1B;OUT.NUM.1
LD A,3A
RST 0010;PRINT.A.1
LD C,(IY+0D);SUBPPC
LD B,00
CALL 1A1B;OUT.NUM.1
CALL 1097;CLEAR.SP
LD A,(5C3A);ERR.NR
INC A
JR Z,1386;MAIN.9
CP 09
JR Z,1373;MAIN.6
CP 15
JR NZ,1376;MAIN.7
1373 MAIN.6 INC (IY+0D);SUBPPC
1376 MAIN.7 LD BC,0003
LD DE,5C70;OSPCC
LD HL,5C44;NSPPC
BIT 7,(HL);NSPPC
JR Z,1384;MAIN.8
ADD HL,BC
1384 MAIN.8 LDDR
1386 MAIN.9 LD (IY+0A),FF;NSPPC
RES 3,(IY+01);FLAGS
JP 12AC;MAIN.2

(last byte in each message +80)


1391 DEFB 80
DEFM 'OK'
DEFM 'NEXT without FOR'
DEFM 'Variable not found'
DEFM 'Subscript wrong'
DEFM 'Out of memory'
DEFM 'Out of screen'
DEFM 'Number too big'
DEFM 'RETURN without GOSUB'
DEFM 'End of file'

1042
DEFM 'STOP statement'
DEFM 'Invalid argument'
DEFM 'Integer out of range'
DEFM 'Nonsense in BASIC'
DEFM 'BREAK - CONT repeats'
DEFM 'Out of DATA'
DEFM 'Invalid file name'
DEFM 'No room for line'
DEFM 'STOP in INPUT'
DEFM 'FOR without NEXT'
DEFM 'Invalid I/O device'
DEFM 'Invalid colour'
DEFM 'BREAK into program'
DEFM 'RAMTOP no good'
DEFM 'Statement lost'
DEFM 'Invalid stream'
DEFM 'FN without DEF'
DEFM 'Parameter error'
DEFM 'Tape loading error'
1537 DEFM ', '
1539 DEFM ' 1982 Sinclair Research Ltd'

1555 REPORT.G LD A,10


LD BC,0000
JP 1313;MAIN.G
155D MAIN.ADD LD (5C49),BC;E.PPC
LD HL,(5C5D);CH.ADD
EX DE,HL
LD HL,1555;REPORT.G
PUSH HL
LD HL,(5C61);WORKSP
SCF
SBC HL,DE
PUSH HL
LD H,B
LD L,C
CALL 196E;LINE.ADDR

1043
JR NZ,157D;MAIN.ADD1
CALL 19B8;NEXT.ONE
CALL 19E8;RECLAIM.2
157D MAIN.ADD.1 POP BC
LD A,C
DEC A
OR B
JR 15AB;MAIN.ADD.2
PUSH BC
INC BC
INC BC
INC BC
INC BC
DEC HL
LD DE,(5C53);PROG
PUSH DE
CALL 1655;MAKE.ROOM
POP HL
LD (5C53),HL;PROG
POP BC
PUSH BC
INC DE
LD HL,(5C61);WORKSP
DEC HL
DEC HL
LDDR
LD HL,(5C49);E.PPC
EX DE,HL
POP BC
LD (HL),B
DEC HL
LD (HL),C
DEC HL
LD (HL),E
DEC HL
LD (HL),D
15AB MAIN.ADD.2 POP AF

1044
JP 12A2;MAIN.EXEC

15AF DEFW F409;PRINT.OUT


DEFW A810;KEY.INPUT
DEFB 'K'
DEFW F409;PRINT.OUT
DEFW C415;REPORT.J
DEFB 'S'
DEFW 810F;ADD.CHAR
DEFW C415;REPORT.J
DEFB 'R'
DEFW F409;PRINT.OUT
DEFW C415;REPORT.J
DEFB 'P',80

15C4 REPORT.J RST 0008;ERROR.1


DEFB 12;"Invalid I/O device"

15C6 DEFW 0100; stream FD - K


DEFW 0600; stream FE - S
DEFW 0B00; stream FF - R
DEFW 0100; stream 00 - K
DEFW 0100; stream 01 - K
DEFW 0600; stream 02 - S
DEFW 1000; stream 03 - P

15D4 WAIT.KEY BIT 5,(IY+02);TV.FLAG


JR NZ,15DE;WAIT.KEY1
SET 3,(IY+02);TV.FLAG
15DE WAIT.KEY.1 CALL 15E6;INPUT.AD
RET C
JR Z,15DE;WAIT.KEY1
15E4 REPORT.8 RST 0008;ERROR.1
DEFB 07;"End of file"

15E6 INPUT.AD EXX


PUSH HL

1045
LD HL,(5C51);CURCHL
INC HL
INC HL
JR 15F7;CALL.SUB
15EF OUT.CODE LD E,30
ADD A,E
15F2 PRINT.A.2 EXX
PUSH HL
LD HL,(5C51);CURCHL
15F7 CALL.SUB LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL
CALL 16C2;CALL.JUMP
POP HL
EXX
RET

1601 CHAN.OPEN ADD A,A


ADD A,16
LD L,A
LD H,5C;5C16 is stream 00
LD E,(HL)
INC HL
LD D,(HL)
LD A,D
OR E
JR NZ,1610;CHAN.OP.1
160E REPORT.O RST 0008;ERROR.1
DEFB 17;"Invalid stream"
1610 CHAN.OP.1 DEC DE
LD HL,(5C4F);CHANS
ADD HL,DE
1615 CHAN.FLAG LD (5C51),HL;CURCHL
RES 4,(IY+30);FLAGS2
INC HL
INC HL

1046
INC HL
INC HL
LD C,(HL)
LD HL,162D;channel code look-up table
CALL 16DC;INDEXER
RET NC
LD D,00
LD E,(HL)
ADD HL,DE
162C CALL.JUMP JP (HL)

162D DEFB 'K',1634-162D;CHAN.K


DEFB 'S',1635-1642;CHAN.S
DEFB 'P',1636-164D;CHAN.P
DEFB 0

1634 CHAN.K SET 0,(IY+02);TV.FLAG


RES 5,(IY+01);FLAGS
SET 4,(IY+30);FLAGS2
JR 1646;CHAN.S.1
1642 CHAN.S RES 0,(IY+02);TV.FLAG
1646 CHAN.S.1 RES 1,(IY+01);FLAGS
JP 0D4D;TEMPS
164D CHAN.P SET 1,(IY+01);FLAGS
RET

1652 ONE.SPACE LD BC,0001


1655 MAKE.ROOM PUSH HL
CALL 1F05;TEST.ROOM
POP HL
CALL 1664;POINTERS
LD HL,(5C65);STKEND
EX DE,HL
LDDR
RET

1664 POINTERS PUSH AF

1047
PUSH HL
LD HL,5C4B;VARS
LD A,0E
166B PTR.NEXT LD E,(HL)
INC HL
LD D,(HL)
EX (SP),HL
AND A
SBC HL,DE
ADD HL,DE
EX (SP),HL
JR NC,167F;PTR.DONE
PUSH DE
EX DE,HL
ADD HL,BC
EX DE,HL
LD (HL),D
DEC HL
LD (HL),E
INC HL
POP DE
1675 PTR.DONE INC HL
DEC A
JR NZ,166B;PTR.NEXT
EX DE,HL
POP DE
POP AF
AND A
SBC HL,DE
LD B,H
LD C,L
INC BC
ADD HL,DE
EX DE,HL
RET

168F LINE.ZERO DEFW 0000

1048
1691 LINE.NO.A EX DE,HL
LD DE,168F;LINE.ZERO
1695 LINE.NO LD A,(HL)
AND C0
JR NZ,1691;LINE.NO.A
LD D,(HL)
INC HL
LD E,(HL)
RET

169E RESERVE LD HL,(5C63);STKBOT


DEC HL
CALL 1655;MAKE.ROOM
INC HL
INC HL
POP BC
LD (5C63),BC;WORKSP
POP BC
EX DE,HL
INC HL
RET

16B0 SET.MIN LD HL,(5C59);E.LINE


LD (HL),0D
LD (5C5B),HL;K.CUR
INC HL
LD (HL),80
INC HL
LD (5C61),HL;WORKSP
16BF SET.WORK LD HL,(5C61);WORKSP
LD (5C63),HL;STKBOT
16C5 SET.STK LD HL,(5C63);STKBOT
LD (5C65),HL;STKEND
PUSH HL
LD HL,5C92;MEMBOT
LD (5C68),HL;MEM
POP HL

1049
RET

16D4 REC.EDIT LD DE,(5C59);E.LINE


JP 19E5;RECLAIM.1

16DB INDEXER.1 INC HL


16DC INDEXER LD A,(HL)
AND A
RET Z
CP C
INC HL
JR NZ,16DB;INDEXER.1
SCF
RET

16E5 CLOSE CALL 171E;STR.DATA


CALL 1701;CLOSE.2
LD BC,0000
LD DE,A3E2;-5C1E
EX DE,HL
ADD HL,DE
JR C,16FC;CLOSE.1
LD BC,15D4;initial stream data
ADD HL,BC
LD C,(HL)
INC HL
LD B,(HL)
16FC CLOSE.1 EX DE,HL
LD (HL),C
INC HL
LD (HL),B
RET
1701 CLOSE.2 PUSH HL
LD HL,(5C4F);CHANS
ADD HL,BC
INC HL
INC HL

1050
INC HL
LD C,(HL)
EX DE,HL
LD HL,1716;CLOSE stream look-up table
CALL 16DC;INDEXER
LD C,(HL)
LD B,00
ADD HL,BC
JP (HL)

1716 DEFB 'K',171C-1716


DEFB 'S',171C-1718
DEFB 'P',171C-171A

171C CLOSE.STR POP HL


RET
171E STR.DATA CALL 1E9A;FIND.INT1
CP 10
JR C,1727;STR.DATA.1
1725 REPORT.O RST 0008;ERROR.1
DEFB 17;"Invalid stream"
1727 STR.DATA.1 ADD A,03
RLCA
LD HL,5C10;STRMS
LD C,A
LD B,00
ADD HL,BC
LD C,(HL)
INC HL
LD B,(HL)
DEC HL
RET

1736 OPEN RST 0028;FP.CALC


DEFB 01;exchange
DEFB 38;end-calc
CALL 171E;STR.DATA

1051
LD A,B
OR C
JR Z,1756;OPEN.1
EX DE,HL
LD HL,(5C4F);CHANS
ADD HL,BC
INC HL
INC HL
INC HL
LD A,(HL)
EX DE,HL
CP 'K'
JR Z,1756;OPEN.1
CP 'S'
JR Z,1756;OPEN.1
CP 'P'
JR NZ,1725;REPORT.O
1756 OPEN.1 CALL 175D;OPEN.2
LD (HL),E
INC HL
LD (HL),D
RET
175D OPEN.2 PUSH HL
CALL 2BF1;STK.FETCH
LD A,B
OR C
JR NZ,1767;OPEN.3
1765 REPORT.F RST 0008;ERROR.1
DEFB 0E;"Invalid file name"
1767 OPEN.3 PUSH BC
LD A,(DE)
AND DF
LD C,A
LD HL,177A;"OPEN stream look-up table"
CALL 16DC;INDEXER
JR NC,1765;REPORT.F
LD C,(HL)

1052
LD B,00
ADD HL,BC
POP BC
JP (HL)
177A DEFB 'K',1781-177A;OPEN.K
DEFB 'S',1785-177C;OPEN.S
DEFB 'P',1789-177E;OPEN.P
DEFB 00
1781 OPEN.K LD E,01
JR 178B;OPEN.END
1785 OPEN.S LD E,06
JR 178B;OPEN.END
1789 OPEN.P LD E,10
178B OPEN.END DEC BC
LD A,B
OR C
JR NZ,1765;REPORT.F
LD D,A
POP HL
RET

1793 CAT.ETC JR 1725;REPORT.O

1795 AUTO.LIST LD (5C3F),SP;LIST.SP


LD (IY+02),10;TV.FLAG
CALL 0DAF;CL.ALL
SET 0,(IY+02);TV.FLAG
LD B,(IY+31);DF.SZ
CALL 0E44;CL.LINE
RES 0,(IY+02);TV.FLAG
SET 0,(IY+30);FLAGS2
LD HL,(5C49);E.PPC
LD DE,(5C6C);S.TOP
AND A
SBC HL,DE
ADD HL,DE
JR C,17E1;AUTO.L.2

1053
PUSH DE
CALL 196E;LINE.ADDR
LD DE,02C0;22d x 32d
EX DE,HL
SBC HL,DE
EX (SP),HL
CALL 196E;LINE.ADDR
POP BC
17CE AUTO.L.1 PUSH BC
CALL 19B8;NEXT.ONE
POP BC
ADD HL,BC
JR C,17E4;AUTO.L.3
EX DE,HL
LD D,(HL)
INC HL
LD E,(HL)
DEC HL
LD (5C6C),DE;S.TOP
JR 17CE;AUTO.L.1
17E1 AUTO.L.2 LD (5C6C),HL;S.TOP
17E4 AUTO.L.3 LD HL,(5C6C);S.TOP
CALL 196E;LINE.ADDR
JR Z,17ED;AUTO.L.4
EX DE,HL
17ED AUTO.L.4 CALL 1833;LIST.ALL
RES 4,(IY+02);TV.FLAG
RET

17F5 LLIST LD A,03


JR 17FB;LIST.1
17F9 LIST LD A,02
17FB LIST.1 LD (IY+02),00;TV.FLAG
CALL 2530;SYNTAX.Z
CALL NZ,1601;CHAN.OPEN
RST 0018;GET.CHAR
CALL 2070;STR.ALTER

1054
JR C,181F;LIST.4
RST 0018;GET.CHAR
CP ';'
JR Z,1814;LIST.2
CP ','
JR NZ,181A;LIST.3
1814 LIST.2 RST 0020;NEXT.CHAR
CALL 1C82;EXPT.1NUM
JR 1822;LIST.5
181A LIST.3 CALL 1CE6;USE.ZERO
JR 1822;LIST.5
181F LIST.4 CALL 1CDE;FETCH.NUM
1822 LIST.5 CALL 1BEE;CHECK.END
CALL 1E99;FIND.INT2
LD A,B
AND 3F
LD H,A
LD L,C
LD (5C49),HL;E.PPC
CALL 196E;LINE.ADDR
1833 LIST.ALL LD E,01
1835 LIST.ALL.1 CALL 1855;OUT.LINE
RST 0010;PRINT.A.1
BIT 4,(IY+02);TV.FLAG
JR Z,1835;LIST.ALL.1
LD A,(5C6B);DF.SZ
SUB A,(IY+4F);S.POSN hi
JR NZ,1835;LIST.ALL.1
XOR E
RET Z
PUSH HL
PUSH DE
LD HL,5C6C;S.TOP
CALL 190F;LN.FETCH
POP DE
POP HL
JR 1835;LIST.ALL

1055
1855 OUT.LINE LD BC,(5C49);E.PPC
CALL 1980;CP.LINES
LD D,'>'
JR Z,1865;OUT.LINES
LD DE,0000
RL E
1865 OUT.LINE1 LD (IY+2D),E;B.REG
LD A,(HL)
CP 40
POP BC
RET NC
PUSH BC
CALL 1A28;OUT.NUM.2
INC HL
INC HL
INC HL
RES 0,(IY+01);FLAGS
LD A,D
AND A
JR Z,1881;OUT.LINE3
RST 0010;PRINT.A.1
187D OUT.LINE2 SET 0,(IY+01);FLAGS
1881 OUT.LINE3 PUSH DE
EX DE,HL
RES 2,(IY+30);FLAGS2
LD HL,5C3B;FLAGS
RES 2,(HL)
BIT 5,(IY+37);FLAGX
JR Z,1894;OUT.LINE4
SET 2,(HL)
1894 OUT.LINE4 LD HL,(5C5F);X.PTR
AND A
SBC HL,DE
JR NZ,18A1;OUT.LINE5
LD A,'?'
CALL 18C1;OUT.FLASH

1056
18A1 OUT.LINE5 CALL 18E1;OUT.CURS
EX DE,HL
LD A,(HL)
CALL 18B6;NUMBER
INC HL
CP 0D
JR Z,18B4;OUT.LINE6
EX DE,HL
CALL 1937;OUT.CHAR
JR 1894;OUT.LINE4
18B4 OUT.LINE6 POP DE
RET

18B6 NUMBER CP 0E
RET NZ
INC HL
INC HL
INC HL
INC HL
INC HL
INC HL
LD A,(HL)
RET

18C1 OUT.FLASH EXX


LD HL,(5C8F);ATTR.T
PUSH HL
RES 7,H
SET 7,L
LD (5C8F),HL;ATTR.T
LD HL,5C91;P.FLAG
LD D,(HL)
PUSH DE
LD (HL),00
CALL 09F4;PRINT.OUT
POP HL
LD (IY+57),H;P.FLAG

1057
POP HL
LD (5C8F),HL;ATTR.T
EXX
RET

18E1 OUT.CURS LD HL,(5C5B);K.CUR


AND A
SBC HL,DE
RET NZ
LD A,(5C41);MODE
RLC A
JR Z,18F3;OUT.C.1
ADD A,43
JR 1909;OUT.C.2
18F3 OUT.C.1 LD HL,5C3B;FLAGS
RES 3,(HL)
LD A,'K'
BIT 2,(HL)
JR Z,1909;OUT.C.2
SET 3,(HL)
INC A
BIT 3,(IY+30);FLAGS2
JR Z,1909;OUT.C.2
LD A,'C'
1909 OUT.C.2 PUSH DE
CALL 18C1;OUT.FLASH
POP DE
RET

190F LN.FETCH LD E,(HL)


INC HL
LD D,(HL)
PUSH HL
EX DE,HL
INC HL
CALL 196E;LINE.ADDR
CALL 1695;LINE.NO

1058
POP HL
191C LN.STORE BIT 5,(IY+37);FLAGX
RET NZ
LD (HL),D
DEC HL
LD (HL),E
RET

1925 OUT.SP.2 LD A,E


AND A
RET M
JR 1937;OUT.CHAR
192A OUT.SP.NO XOR A
192B OUT.SP.1 ADD HL,BC
INC A
JR C,192B;OUT.SP.1
SBC HL,BC
DEC A
JR Z,1925;OUT.SP.2
JP 15EF;OUT.CODE

1937 OUT.CHAR CALL 2D1B;NUMERIC


JR NC,196C;OUT.CH.3
CP 21
JR C,196C;OUT.CH.3
RES 2,(IY+01);FLAGS
CP 'THEN'
JR Z,196C;OUT.CH.3
CP ':'
JR NZ,195A;OUT.CH.1
BIT 5,(IY+37);FLAGX
JR NZ,1968;OUT.CH.2
BIT 2,(IY+30);FLAGS2
JR Z,196C;OUT.CH.3
JR 1968;OUT.CH.2
195A OUT.CH.1 CP '"'
JR NZ,1968;OUT.CH.2

1059
PUSH AF
LD A,(5C6A);FLAGS2
XOR 04
LD (5C6A),A;FLAGS2
POP AF
1968 OUT.CH.2 SET 2,(IY+01);FLAGS
196D OUT.CH.3 RST 0010;PRINT.A.1
RET

196E LINE.ADDR PUSH HL


LD HL,(5C53);PROG
LD D,H
LD E,L
1974 LINE.AD.1 POP BC
CALL 1980;CP.LINES
RET NC
PUSH BC
CALL 19B8;NEXT.ONE
EX DE,HL
JR 1974;LINE.AD.1

1980 CP.LINES LD A,(HL)


CP B
RET NZ
INC HL
LD A,(HL)
DEC HL
CP C
RET
1988 INC HL
INC HL
INC HL

198B EACH.STMT LD (5C5D),HL;CH.ADD


LD C,00
1990 EACH.S.1 DEC D
RET Z

1060
RST 0020;NEXT.CHAR
CP E
JR NZ,199A;EACH.S.3
AND A
RET
1998 EACH.S.2 INC HL
LD A,(HL)
199A EACH.S.3 CALL 18B6;NUMBER
LD (5C5D),HL;CH.ADD
CP '"'
JR NZ,19A5;EACH.S.4
DEC C
19A5 EACH.S.4 CP ':'
JR Z,19AD;EACH.S.5
CP 'THEN'
JR NZ,19B1;EACH.S.6
19AD EACH.S.5 BIT 0,C
JR Z,1990;EACH.S.1
19B1 EACH S.6 CP 0D
JR NZ,1998;EACH.S.2
DEC D
SCF
RET

19B8 NEXT.ONE PUSH HL


LD A,(HL)
CP 40
JR C,19D5;NEXT.O.3
BIT 5,A
JR Z,19D6;NEXT.O.4
ADD A,A
JP M,19C7;NEXT.O.1
CCF
19C7 NEXT.O.1 LD BC,0005
JR NC,19CE;NEXT.O.2
LD C,12
19CE NEXT.O.2 RLA

1061
INC HL
LD A,(HL)
JR NC,19CE;NEXT.O.2
JR 19DB;NEXT.O.5
19D5 NEXT.O.3 INC HL
19D6 NEXT.O.4 INC HL
LD C,(HL)
INC HL
LD B,(HL)
INC HL
19DB NEXT.O.5 ADD HL,BC
POP DE

19DD DIFFER AND A


SBC HL,DE
LD B,H
LD C,L
ADD HL,DE
EX DE,HL
RET

19E5 RECLAIM.1 CALL 19DD;DIFFER


19E8 RECLAIM.2 PUSH BC
LD A,B
CPL
LD B,A
LD A,C
CPL
LD C,A
INC BC
CALL 1664;POINTERS
EX DE,HL
POP HL
ADD HL,DE
PUSH DE
LDIR
POP HL

1062
RET

19FB E.LINE.NO LD HL,(5C59);E.LINE


DEC HL
LD (5C5D),HL;CH.ADD
RST 0020;NEXT.CHAR
LD HL,5C92;MEMBOT
LD (5C65),HL;STKEND
CALL 2D3B;INT.YO.FP
CALL 2DA2;FP.TO.BC
JR C,1A15;E.L.1
LD HL,D8F0;10,000d
ADD HL,BC
1A15 E.L.1 JP C,1C8A;REPORT.C
JP 16C5;SET.STK

1A1B OUT.NUM.1 PUSH DE


PUSH HL
XOR A
BIT 7,B
JR NZ,1A42;OUT.NUM.4
LD H,B
LD L,C
LD E,FF
JR 1A30,OUT.NUM.3
1A28 OUT.NUM.2 PUSH DE
LD D,(HL)
INC HL
LD E,(HL)
PUSH HL
EX DE,HL
LD E,20
1A30 OUT.NUM.3 LD BC,FC18;-1,000d
CALL 192A;OUT.SP.NO
LD BC,FF9C;-100d
CALL 192A;OUT.SP.NO
LD C,F6;-10d

1063
CALL 192A;OUT.SP.NO
LD A,L
1A42 OUT.NUM.4 CALL 15EF;OUT.CODE
POP HL
POP DE
RET

1A48 DEFB 1AF9-1A48;P.DEF.FN


DEFB 1B14-1A49;P.CAT
DEFB 1B06-1A4A;P.FORMAT
DEFB 1B0A-1A4B;P.MOVE
DEFB 1B10-1A4C;P.ERASE
DEFB 1AFC-1A4D;P.OPEN
DEFB 1B02-1A4E;P.CLOSE
DEFB 1AE2-1A4F;P.MERGE
DEFB 1AE1-1A50;P.VERIFY
DEFB 1AE3-1A51;P.BEEP
DEFB 1AE7-1A52;P.CIRCLE
DEFB 1AEB-1A53;P.INK
DEFB 1AEC-1A54;P.PAPER
DEFB 1AED-1A55;P.FLASH
DEFB 1AEE-1A56;P.BRIGHT
DEFB 1AEF-1A57;P.INVERSE
DEFB 1AF0-1A58;P.OVER
DEFB 1AF1-1A59;P.OUT
DEFB 1AD9-1A5A;P.LPRINT
DEFB 1ADC-1A5B;P.LLIST
DEFB 1A8A-1A5C;P.STOP
DEFB 1AC9-1A5D;P.READ
DEFB 1ACC-1A5E;P.DATA
DEFB 1ACF-1A5F;P.RESTORE
DEFB 1AA8-1A60;P.NEW
DEFB 1AF5-1A61;P.BORDER
DEFB 1AB8-1A62;P.CONT
DEFB 1AA2-1A63;P.DIM
DEFB 1AA5-1A64;P.REM
DEFB 1A90-1A65;P.FOR

1064
DEFB 1A7D-1A66;P.GO.TO
DEFB 1A86-1A67;P.GO.SUB
DEFB 1A9F-1A68;P.INPUT
DEFB 1AE0-1A69;P.LOAD
DEFB 1AAE-1A6A;P.LIST
DEFB 1A7A-1A6B;P.LET
DEFB 1AC5-1A6C;P.PAUSE
DEFB 1A98-1A6D;P.NEXT
DEFB 1AB1-1A6E;P.POKE
DEFB 1A9C-1A6F;P.PRINT
DEFB 1AC1-1A70;P.PLOT
DEFB 1AAB-1A71;P.RUN
DEFB 1ADF-1A72;P.SAVE
DEFB 1AB5-1A73;P.RANDOM
DEFB 1A81-1A74;P.IF
DEFB 1ABE-1A75;P.CLS
DEFB 1AD2-1A76;P.DRAW
DEFB 1ABB-1A77;P.CLEAR
DEFB 1A8D-1A78;P.RETURN
DEFB 1AD6-1A79;P.COPY

1A7A P.LET DEFB 01,'=',02


1A7D P.GO.TO DEFB 06,00
DEFW 1E67;GO.TO
1A81 P.IF DEFB 06,'THEN',05
DEFW 1CF0;IF
1A86 P.GO.SUB DEFB 06,00
DEFW 1EED;GO.SUB
1A8A P.STOP DEFB 00
DEFW 1CEE;STOP
1A8D P.RETURN DEFB 00
DEFW 1F23;RETURN
1A90 P.FOR DEFB 04,'=',06,'TO',06,05
DEFW 1D03;FOR
1A98 P.NEXT DEFB 04,00
DEFW 1DAB;NEXT
1A9C P.PRINT DEFB 05

1065
DEFW 1FCD;PRINT
1A9F P.INPUT DEFB 05
DEFW 2089;INPUT
1AA2 P.DIM DEFB 05
DEFW 2C02;DIM
1AA5 P.REM DEFB 05
DEFW 1BB2;REM
1AA8 P.NEW DEFB 00
DEFW 11B7;NEW
1AAB P.RUN DEFB 03
DEFW 1EA1;RUN
1AAE P.LIST DEFB 05
DEFW 17F9;LIST
1AB1 P.POKE DEFB 08,00
DEFW 1E80;POKE
1AB5 P.RANDOM DEFB 03
DEFW 1E4F;RANDOMIZE
1AB8 P.CONT DEFB 00
DEFW 1E5F;CONTINUE
1ABB P.CLEAR DEFB 03
DEFW 1EAC;CLEAR
1ABE P.CLS DEFB 00
DEFW 0D6B;CLS
1AC1 P.PLOT DEFB 09,00
DEFW 22DC;PLOT
1AC5 P.PAUSE DEFB 06,00
DEFW 1F3A;PAUSE
1AC8 P.READ DEFB 05
DEFW 1DED;READ
1ACC P.DATA DEFB 05
DEFW 1E27;DATA
1ACF P.RESTORE DEFB 03
DEFW 1E42;RESTORE
1AD2 P.DRAW DEFB 09,05
DEFW 2382;DRAW
1AD6 P.COPY DEFB 00
DEFW 0EAC;COPY

1066
1AD9 P.LPRINT DEFB 05
DEFW 1FC9;LPRIMT
1ADC P.LLIST DEFB 05
DEFW 17F5;LLIST
1ADF P.SAVE DEFB 0B
1AE0 P.LOAD DEFB 0B
1AE1 P.VERIFY DEFB 0B
1AE2 P.MERGE DEFB 0B
1AE3 P.BEEP DEFB 08,00
DEFW 03F8;BEEP
1AE7 P.CIRCLE DEFB 09,05
DEFW 2320;CIRCLE
1AEB P.INK DEFB 07
1AEC P.PAPER DEFB 07
1AED P.FLASH DEFB 07
1AEE P.BRIGHT DEFB 07
1AEF P.INVERSE DEFB 07
1AF0 P.OVER DEFB 07
1AF1 P.OUT DEFB 08,00
DEFW 1E7A;OUT
1AF5 P.BORDER DEFB 06,00
DEFW 2294;BORDER
1AF9 P.DEF.FN DEFB 05
DEFW 1F60;DEF.FN
1AFC P.OPEN DEFB 06,','0A,00
DEFW 1736;OPEN
1B02 P.CLOSE DEFB 06,00
DEFW 16E5;CLOSE
1B06 P.FORMAT DEFB 0A,00
DEFW 1793;CAT.ETC
1B0A P.MOVE DEFB 0A,','0A,00
DEFW 1793;CAT.ETC
1B10 P.ERASE DEFB 0A,00
DEFW 1793;CAT.ETC
1B14 P.CAT DEFB 00
DEFW 1793;CAT.ETC

1067
1B17 LINE.SCAN RES 7,(IY+01);FLAGS
CALL 19FB;E.LINE.NO
XOR A
LD (5C47),A;SUBPPC
DEC A
LD (5C3A),A;ERR.NR
JR 1B29;STMT.L.1

1B28 STMT.LOOP RST 0020;NEXT.CHAR


1B29 STMT.L.1 CALL 16BF;SET.WORK
INC (IY+0D);SUBPPC
JP M,1C8A;REPORT.C
RST 0018;GET.CHAR
LD B,00
CP 0D
JR Z,1BB3;LINE.END
CP ':'
JR Z,1B28;STMT.LOOP
LD HL,1B76;STMT.RET
PUSH HL
LD C,A
RST 0020;NEXT.CHAR
LD A,C
SUB CE
JP C,1C8A;REPORT.C
LD C,A
LD HL,1A48;syntax offset table
ADD HL,BC
LD C,(HL)
ADD HL,BC
JR 1B55;GET.PARAM

1B52 SCAN.LOOP LD HL,(5C74);T.ADDR


1B55 GET.PARAM LD A,(HL)
INC HL
LD (5C74),HL;T.ADDR
LD BC,1B52;SCAN.LOOP

1068
PUSH BC
LD C,A
CP 20
JR NC,1B6F;SEPARATOR
LD HL,1C01;command class table
LD B,00
ADD HL,BC
LD C,(HL)
ADD HL,BC
PUSH HL
RST 0018;GET.CHAR
DEC B
RET

1B6F SEPARATOR RST 0018;GET.CHAR


CP C
JP NZ,1C8A;REPORT.C
RST 0020;NEXT.CHAR
RET

1B76 STMT.RET CALL 1F54;BREAK.KEY


JR C,1B7D;STMT.R.1
1B7B REPORT.L RST 0008;ERROR.1
DEFB 14;"BREAK into program"
1B7D STMT.R.1 BIT 7,(IY+0A);NSPPC [PLUS 2: CALL 3B4D
JR NZ,1BF4;STMT.NEXT NOP]
LD HL,(5C42);NEWPPC
BIT 7,H
JR Z,1B9E;LINE.NEW

1B8A LINE.RUN LD HL,FFFE


LD (5C45),HL;PPC
LD HL,(5C61);WORKSP
DEC HL
LD DE,(5C59);E.LINE
DEC DE
LD A,(5C44);NSPPC

1069
JR 1BD1;NEXT.LINE

1B9E LINE.NEW CALL 196E;LINE.ADDR


LD A,(5C44);NSPPC
JR Z,1BBF;LINE.USE
AND A
JR NZ,1BEC;REPORT.N
LD B,A
LD A,(HL)
AND 11000000B
LD A,B
JR Z,1BBF;LINE.USE
1B80 REPORT.0 RST 0008;ERROR.1
DEFB FF;"OK"

1BB2 REM POP BC

1BB3 LINE.END CALL 2530;SYNTAX.Z


RET Z
LD HL,(5C55);NXTLIN
LD A,11000000B
AND (HL)
RET NZ
XOR A

1BBF LINE.USE CP 01
ADC A,00
LD D,(HL)
INC HL
LD E,(HL)
LD (5C45),DE;PPC
INC HL
LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL
ADD HL,DE

1070
INC HL

1BD1 NEXT.LINE LD (5C55),HL;NXTLINE


EX DE,HL
LD (5C5D),HL;CH.ADD
LD D,A
LD E,00
LD (IY+0A),FF;NSPPC
DEC D
LD (IY+0D),D;SUBPPC
JP Z,1B28;STMT.LOOP
INC D
CALL 198B;EACH.STMT
JR Z,1BF4;STMT.NEXT
1BEC REPORT.N RST 0008;ERROR.1
DEFB 16;"Statement lost"

1BEE CHECK.END CALL 2530;SYNTAX.Z


RET NZ
POP BC
POP BC

1BF4 STMT.NEXT RST 0018;GET.CHAR [PLUS 2: CALL 3B5D


CP 0D - ]
JR Z,1BB3;LINE.END
CP ':'
JP Z,1B28;STMT.LOOP
JP 1C8A;REPORT.C

1C01 DEFB 1C10-1C01;CLASS 00


DEFB 1C1F-1C02;CLASS 01
DEFB 1C4E-1C03;CLASS 02
DEFB 1C0D-1C04;CLASS 03
DEFB 1C6C-1C05;CLASS 04
DEFB 1C11-1C06;CLASS 05
DEFB 1C82-1C07;CLASS 06
DEFB 1C96-1C08;CLASS 07

1071
DEFB 1C7A-1C09;CLASS 08
DEFB 1CBE-1C0A;CLASS 09
DEFB 1C8C-1C0B;CLASS 0A
DEFB 1CDB-1C0C;CLASS 0B

1C03 CLASS.03 CALL 1CDE;FETCH.NUM


1C10 CLASS.00 CP A
1C11 CLASS.05 POP BC
CALL Z,1BEE;CHECK.END
EX DE,HL

1C16 JUMP.C.R LD HL,(5C74);T.ADDR


LD C,(HL)
INC HL
LD B,(HL)
EX DE,HL
PUSH BC
RET

1C1F CLASS.01 CALL 28B2;LOOK.VARS


1C22 VAR.A.1 LD (IY+37),00;FLAGX
JR NC,1C30;VAR.A.2
SET 1,(IY+37);FLAGX
JR NZ,1C46;VAR.A.3
1C2E REPORT.2 RST 0008;ERROR.1
DEFB 01;"Variable not found"
1C30 VAR.A.2 CALL Z,2996;STK.VARS
BIT 6,(IY+01);FLAGS
JR NZ,1C46;VAR.A.3
XOR A
CALL 2530;SYNTAX.Z
CALL NZ,2BF1;STK.FETCH
LD HL,5C71;FLAGX
OR (HL)
LD (HL),A
EX DE,HL
1C46 VAR.A.3 LD (5C72),BC;STRLEN

1072
LD (5C4D),HL;DEST
RET

1C4E CLASS.02 POP BC


CALL 1C56;VAL.FET.1
CALL 1BEE;CHECK.END
RET

1C56 VAL.FET.1 LD A,(5C3B);FLAGS


1C59 VAL.FET.2 PUSH AF
CALL 24FB;SCANNING
POP AF
LD D,(5C3B);FLAGS
XOR D
AND 01000000B
JR NZ,1C8A;REPORT.C
BIT 7,D
JP NZ,2AFF;LET
RET

1C6C CLASS.04 CALL 28B2;LOOK.VARS


PUSH AF
LD A,C
OR 10011111B
INC A
JR NZ,1C8A;REPORT.C
POP AF
JR 1C22,VAR.A.1

1C79 NEXT.2NUM RST 0020;NEXT.CHAR


1C7A CLASS.08 CALL 1C82;EXPT.1NUM
(EXPT.2NUM) CP ','
JR NZ,1C8A;REPORT.C
RST 0020;NEXT.CHAR
1C82 CLASS.06 CALL 24FB;SCANNING
(EXPT.1NUM) BIT 6,(IY+01);FLAGS
RET NZ

1073
1C8A REPORT.C RST 0008;ERROR.1
DEFB 0B;"Nonsense in BASIC"

1C8C CLASS.0A CALL 24FB;SCANNING


(EXPT.EXP) BIT 6,(IY+01);FLAGS
RET Z
JR 1C8A;REPORT.C

1C96 CLASS.07 BIT 7,(IY+01);FLAGS


(PERMS) RES 0,(IY+02);TV.FLAG
CALL NZ,0D4D;TEMPS
POP AF
LD A,(5C74);T.ADDR
SUB 13
CALL 21FC;CO.TEMP.4
CALL 1BEE;CHECK.END
LD HL,(5C8F);ATTR.T
LD (5C8D),HL;ATTR.P
LD HL,5C91;P.FLAG
LD A,(HL)
RLCA
XOR (HL)
AND 10101010B
XOR (HL)
LD (HL),A
RET

1CBE CLASS.09 CALL 2530;SYNTAX.Z


JR Z,1CD6;CL.09.1
RES 0,(IY+02);TV.FLAG
CALL OD4D;TEMPS
LD HL,5C90;MASK.T
LD A,(HL)
OR 11111000B
LD (HL),A
RES 6,(IY+57);P.FLAG
RST 0018;GET.CHAR

1074
1CD6 CL.09.1 CALL 21E2;CO.TEMP
JR 1C7A;EXPT.2NUM

1CDB CLASS.0B JP 0605;SAVE.ETC

1CDE FETCH.NUM CP 0D
JR Z,1CE6;USE.ZERO
CP ':'
JR NZ;1C82;EXPT.2NUM

1CE6 USE.ZERO CALL 2530;SYNTAX.Z


RET Z
RST 0028;FP.CALC
DEFB A0;stk-zero
DEFB 38;end-calc
RET

1CEE STOP RST 0008;ERROR.1


(REPORT.9) DEFB 08;"STOP statement"

1CF0 IF POP BC
CALL 2530;SYNTAX.Z
JR Z,1D00;IF.1
RST 0028;FP.CALC
DEFB 02;delete
DEFB 38;end.calc
EX DE,HL
CALL 34E9;TEST.ZERO
JP C,1BB3;LINE.END
1D00 IF.1 JP 1B29;STMT.L.1

1D03 FOR CP 'STEP'


JR NZ,1D10;F.USE.1
RST 0020;NEXT.CHAR
CALL 1C82;EXPT.1NUM
CALL 1BEE;CHECK.END
JR 1D16;F.REORDER

1075
1D10 F.USE.1 CALL 1BEE;CHECK.END
RST 0028;FP.CALC
DEFB A1;stk-one
DEFB 38;end-calc
1D16 F.REORDER RST 0028;FP.CALC
DEFB C0;st-mem-0
DEFB 02;delete
DEFB 01;exchange
DEFB E0;get-mem-0
DEFB 01;exchange
DEFB 38;end-calc
CALL 2AFF;LET
LD (5C6B),HL;MEM
DEC HL
LD A,(HL)
SET 7,(HL)
LD BC,0006
ADD HL,BC
RLCA
JR C,1D34;F.L&S
LD C,0D
CALL 1655;MAKE.ROOM
INC HL
1D34 F.L&S PUSH HL
RST 0028;FP.CALC
DEFB 02;delete
DEFB 02;delete
DEFB 38;end-calc
POP HL
EX DE,HL
LD C,0A
LDIR
LD HL,(5C45);PPC
EX DE,HL
LD (HL),E
INC HL
LD (HL),D

1076
LD D,(IY+0D);SUBPPC
INC D
INC HL
LD (HL),D
CALL 1DDA;NEXT,LOOP
RET NC
LD B,(IY+38);STRLEN
LD HL,(5C45);PPC
LD (5C42),HL;NEWPPC
LD A,(5C47);SUBPPC
NEG
LD D,A
LD HL,(5C5D);CH.ADD
LD E,NEXT
1D64 F.LOOP PUSH BC
LD BC,(5C55);NXTLIN
CALL 1D86;LOOK.PROG
LD (5C55),BC;NXTLIN
POP BC
JR C,1D84;REPORT.I
RST 0020;NEXT.CHAR
OR 00100000B
CP B
JR Z,1D7C;F.FOUND
RST 0020;NEXT.CHAR
JR 1D64;F.LOOP
1D7C F.FOUND RST 0020;NEXT.CHAR
LD A,01
SUB D
LD (5C44),A;NSPPC
RET
1D84 REPORT.I RST 0008;ERROR.1
DEFB 11;"FOR without NEXT"

1D86 LOOK.PROG LD A,(HL)


CP ':'
JR Z,1DA3;LOOK.P.2

1077
1D8B LOOK.P.1 INC HL
LD A,(HL)
AND 11000000B
SCF
RET NZ
LD B,(HL)
INC HL
LD C,(HL)
LD (5C42),BC;NEWPPC
INC HL
LD C,(HL)
INC HL
LD B,(HL)
PUSH HL
ADD HL,BC
LD B,H
LD C,L
POP HL
LD D,00
1DA3 LOOK.P.2 PUSH BC
CALL 198B;EACH.STMT
POP BC
RET NC
JR 1D8B;LOOK.P.1

1DAB NEXT BIT 1,(IY+37);FLAGX


JP NZ,1C2E;REPORT.2
LD HL,(5C4D);DEST
BIT 7,(HL)
JR Z,1DDB;REPORT.1
INC HL
LD (5C68),HL;MEM
RST 0028;FP.CALC
DEFB E0;get-mem-0
DEFB E2;get-mem-2
DEFB 0F;addition
DEFB C0;st-mem-0

1078
DEFB 02;delete
DEFB 38;end-calc
CALL 1DDA;NEXT.LOOP
RET C
LD HL,(5C68);MEM
LD DE,000F
ADD HL,DE
LD E,(HL)
INC HL
LD D,(HL)
INC HL
LD H,(HL)
EX DE,HL
JP 1E73;GO.TO.2
1DD8 REPORT.1 RST 0008;ERROR.1
DEFB 00;"NEXT without FOR"
1DDA NEXT.LOOP RST 0028;FP.CALC
DEFB E1;get-mem-1
DEFB E0;get-mem-0
DEFB E2;get-mem-2
DEFB 36;less-0
DEFB 00;jump-true
DEFB 02;to NEXT.1
DEFB 01;exchange
1DE2 NEXT.1 DEFB 03;subtract
DEFB 37;greater-0
DEFB 00;jump-true
DEFB 04;to NEXT.2
DEFB 38;end-calc
AND A
RET
1DE9 NEXT.2 DEFB 38;end-calc
SCF
RET

1DEC READ.3 RST 0020;NEXT.CHAR


1DED READ CALL 1C1F;CLASS.01

1079
CALL 2530;SYNTAX.Z
JR Z,1E1E;READ.1
RST 0018;GET.CHAR
LD (5C5F),HL;X.PTR
LD HL,(5C57);DATADD
LD A,(HL)
CP ','
JR Z,1E0A;READ.1
LD E,'DATA'
CALL 1D86;LOOK.PROG
JR NC,1E0A;READ.1
1E08 REPORT.E RST 0008;ERROR.1
DEFB 0D;"Out of DATA"
1E0A READ.1 CALL 0077;TEMP.PTR1
CALL 1C56;VAL.FET.1
RST 0018;GET.CHAR
LD (5C57),HL;DATADD
LD HL,(5C5F);X.PTR
LD (IY+26),00;X.PTR
CALL 0078;TEMP.PTR2
1E1E READ.2 RST 0018;GET.CHAR
CP ','
JR Z,1DEC;READ.1
CALL 1BEE;CHECK.END
RET

1E27 DATA CALL 2530;SYNTAX.Z


JR NZ,1E37;DATA.2
1E2C DATA.1 CALL 24FB;SCANNING
CP ','
CALL NZ,1BEE;CHECK.END
RST 0020;NEXT.CHAR
JR 1E2C;DATA.1
1E37 DATA.2 LD A,'DATA'

1E39 PASS.BY LD B,A


CPDR

1080
LD DE,0200
JP 198B;EACH.STMT

1E42 RESTORE CALL 1E99;FIND.INT2


1E45 REST.RUN LD H,B
LD L,C
CALL 196E;LINE.ADDR
DEC HL
LD (5C57),HL;DATADD
RET

1E4F RANDOMIZE CALL 1E99;FIND.INT2


LD A,B
OR C
JR NZ,1E5A;RAND.1
LD BC,(5C78);FRAMES
1E5A RAND.1 LD (5C76),BC;SEED
RET

1E5F CONTINUE LD HL,(5C6E);OLDPPC


LD D,(IY+36);OSPCC
JR 1E73;GO.TO.2

1E67 GO.TO CALL 1E99;FIND.INT2


LD H,B
LD L,C
LD D,00
LD A,H
CP 11110000B
JR NC,1E9F;REPORT.B
1E73 GO.TO.2 LD (5C42),HL;NEWPPC
LD (IY+0A),D;NSPPC
RET

1E7A OUT CALL 1E85;TWO.PARAM


OUT (C),A
RET

1081
1E60 POKE CALL 1E85;TWO.PARAM
LD (BC),A
RET

1E85 TWO.PARAM CALL 2DD5;FP.TO.A


JR C,1E9F;REPORT.B
JR Z,1E8E;TWO.P.1
NEG
1E8E TWO.P.1 PUSH AF
CALL 1E99;FIND.INT2
POP AF
RET

1E94 FIND.INT1 CALL 2DD5;FP.TO.A


JR 1E9C FIND.I.1
1E99 FIND.INT2 CALL 2DA2;FP.TO.BC
1E9C FIND.I.1 JR C,1E9F;REPORT.B
RET Z
1E9F REPORT.B RST 0008;ERROR.1
DEFB 0A;"Integer out of range"

1EA1 RUN CALL 1E67 GO.TO


LD BC,0000
CALL 1E45;REST.RUN
JR 1EAF;CLEAR.RUN

1EAC CLEAR CALL 1E99;FIND.INT2


1EAF CLEAR.RUN LD A,B
OR C
JR NZ,1EB7;CLEAR.1
LD BC,(5CB2);RAMTOP
1EB7 CLEAR.1 PUSH BC
LD DE,(5C4B);VARS
LD HL,(5C59);E.LINE
DEC HL
CALL 19E5;RECLAIM.1

1082
CALL 0D6B;CLS
LD HL,(5C65);STKEND
LD DE,0032;50d
ADD HL,DE
POP DE
SBC HL,DE
JR NC,1EDA;REPORT.M
LD HL,(5CB4);P.RAMT
AND A
SBC HL,DE
JR NC,1EDC;CLEAR.2
1EDA REPORT.M RST 0008;ERROR.1
DEFB 15;"RAMTOP no good"
1EDC CLEAR.2 EX DE,HL
LD (5CB2),HL;RAMTOP
POP DE
POP BC
LD (HL),3E
DEC HL
LD SP,HL
PUSH BC
LD (5C3D),SP;ERR.SP
EX DE,HL
JP (HL)

1EED GO.SUB POP DE


LD H,(IY+0D);SUBPPC
INC H
EX (SP),HL
INC SP
LD BC,(5C45);PPC
PUSH BC
PUSH HL
LD (5C3D),SP;ERR.SP
PUSH DE
CALL 1E67;GO.TO.1
LD BC,0014

1083
1F05 TEST.ROOM LD HL,(5C65);STKEND
ADD HL,BC
JR C,1F15;REPORT.4
EX DE,HL
LD HL,0050;80d
ADD HL,DE
JR C,1F15;REPORT.4
SBC HL,SP
RET C
1F15 REPORT.4 LD L,03;"Out of memory"
JP 0055,ERROR.3

1F1A FREE.MEM LD BC,0000


CALL 1F05;TEST.ROOM
LD B,H
LD C,L
RET

1F23 RETURN POP BC


POP HL
POP DE
LD A,D
CP 3E
JR Z,1F36;REPORT.7
DEC SP
EX (SP),HL
EX DE,HL
LD (5C3D),SP;ERR.SP
PUSH BC
JP 1E73;GO.TO.2
1F36 REPORT.7 PUSH DE
PUSH HL
RST 0008;ERROR.1
DEFB 06;"RETURN without GO SUB"

1F3A PAUSE CALL 1E99;FIND.INT2

1084
1F3D PAUSE.1 HALT
DEC BC
LD A,B
OR C
JR Z,1F4F;PAUSE.END
LD A,B
AND C
INC A
JR NZ,1F49;PAUSE.2
INC BC
1F49 PAUSE.2 BIT 5,(IY+01);FLAGS
JR Z,1F3D;PAUSE.1
1F4F PAUSE.END RES 5,(IY+01);FLAGS
RET

1F54 BREAK.KEY LD A,7F


IN A,(FE)
RRA
RET C
LD A,FE
IN A,(FE)
RRA
RET

1F60 DEF.FN CALL 2530;SYNTAX.Z


JR Z,1F6A;DEF.FN.1
LD A,'DEF FN'
JP 1E39;PASS.BY
1F6A DEF.FN.1 SET 6,(IY+01);FLAGS
CALL 2C8D;ALPHA
JR NC,1F89;DEF.FN.4
RST 0020;NEXT.CHAR
CP '$'
JR NZ,1F7D;DEF.FN.2
RES 6,(IY+01);FLAGS
RST 0020;NEXT.CHAR
1F7D DEF.FN.2 CP '('

1085
JR NZ,1FBD;DEF.FN.7
RST 0020;NEXT.CHAR
CP ')'
JR Z,1FA6;DEF.FN.6
1F86 DEF.FN.3 CALL 2C8D;ALPHA
1F89 DEF.FN.4 JP NC,1C8A;REPORT.C
EX DE,HL
RST 0020;NEXT.CHAR
CP '$'
JR NZ,1F94;DEF.FN.5
EX DE,HL
RST 0020;NEXT.CHAR
1F94 DEF.FN.5 EX DE,HL
LD BC,0006
CP 1655;MAKE.ROOM
INC HL
INC HL
LD (HL),0E
CP ','
JR NZ,1FA6;DEF.FN.6
RST 0020;NEXT.CHAR
JR 1F86;DEF.FN.3
1FA6 DEF.FN.6 CP ')'
JR NZ,1FBD;DEF.FN.7
RST 0020;NEXT.CHAR
CP '='
JR NZ,1FBD;DEF.FN.7
RST 0020;NEXT.CHAR
LD A,(5C3B);FLAGS
PUSH AF
CALL 24FB;SCANNING
POP AF
XOR (IY+01);FLAGS
AND 01000000B
1FBD DEF.FN.7 JP NZ,1C8A;REPORT.C
CALL 1BEE;CHECK.END

1086
1FC3 UNSTACK.Z CALL 2530;SYNTAX.Z
POP HL
RET Z
JP (HL)

1FC9 LPRINT LD A,03


JR 1FCF;PRINT.1
1FCD PRINT LD A,02
1FCF PRINT.1 CALL 2530;SYNTAX.Z
CALL NZ,1601;CHAN.OPEN
CALL 0D4D;TEMPS
CALL 1FDF;PRINT.2
CALL 1BEE;CHECK.END
RET
1FDF PRINT.2 RST 0018;GET.CHAR
CALL 2045;PR.END.Z
JR Z,1FF2;PRINT.4
1FE5 PRINT.3 CALL 204E;PR.POSN.1
JR Z,1FE5;PRINT.3
CALL 1FFC;PR.ITEM.1
CALL 204E;PR.POSN.1
JR 1FE5;PRINT.3
1FF2 PRINT.4 CP ')'
RET Z
1FF5 PRINT.CR CALL 1FC3;UNSTACK.Z
LD A,0D
RST 0010;PRINT.A.1
RET
1FFC PR.ITEM.1 RST 0018;GET.CHAR
CP 'AT'
JR NZ,200E;PR.ITEM.2
CALL 1C79;NEXT.2NUM
CALL 1FC3;UNSTACK.Z
CALL 2307;STACK.TO.BC
LD A,16;AT control
JR 201E;PR.AT.TAB
200E PR.ITEM.2 CP 'TAB'

1087
JR NZ,2024;PR.ITEM.3
RST 0020;NEXT.CHAR
CALL 1C82;EXPT.1NUM
CALL 1FC3;UNSTACK.Z
CALL 1E99;FIND.INT2
LD A,17;TAB control
201E PR.AT.TAB RST 0010;PRINT.A.1
LD A,C
RST 0010;PRINT.A.1
LD A,B
RST 0010;PRINT.A.1
RET
2024 PR.ITEM.3 CALL 21F2;CO.TEMP.3
RET NC
CALL 2070;STR.ALTER
RET NC
CALL 24FB;SCANNING
CALL 1FC3;UNSTACK.Z
BIT 6,(IY+01);FLAGS
CALL Z,2BF1;STK.FETCH
JP NZ,2DE3;PRINT.FP
203C PR.STRING LD A,B
OR C
DEC BC
RET Z
LD A,(DE)
INC DE
RST 0010;PRINT.A.1
JR 203C;PR.STRING
2045 PR.END.Z CP ')'
RET Z
2048 PR.ST.END CP 0D
RET Z
CP ':'
RET
204E PR.POSN.1 RST 0018;GET.CHAR
CP ';'

1088
JR Z,2067;PR.POSN.3
CP ','
JR NZ,2061;PR.POSN.2
CALL 2530;SYNTAX.Z
JR Z,2067;PR.POSN.3
LD A,06;PRINT comma
RST 0010;PRINT.A.1
JR 2067;PR.POSN.3
2061 PR.POSN.2 CP ''''
RET NZ
CALL 1FF5;PRINT.CR
2067 PR.POSN.3 RST 0020;NEXT.CHAR
CALL 2045;PR.END.Z
JR NZ,206E;PR.POSN.4
POP BC
208E PR.POSN.4 CP A
RET

2070 STR.ALTER CP '#'


SCF
RET NZ
RST 0020;NEXT.CHAR
CALL 1C82;EXPT.1NUM
AND A
CALL 1FC3;UNSTACK.Z
CALL 1E94;FIND.INT.1
CP 10
JP NC,160E;REPORT.O
CALL 1601;CHAN.OPEN
AND A
RET

2089 INPUT CALL 2530 5;SYNTAX.Z


JR Z,2096;INPUT.1
LD A,01
CALL 1601;CHAN.OPEN
CALL 0D6E;CLS.LOWER

1089
2096 INPUT.1 LD (IY+02),01;TV.FLAG
CALL 20C1;IN.ITEM.1
CALL 1BEE;CHECK.END
LD BC,(5C88);S.POSN
LD A,(5C6B);DF.SZ
CP B
JR C,20AD;INPUT.2
LD C,21;top of screen
LD B,A
20AD INPUT.2 LD (5C88),BC;S.POSN
LD A,19;23d lines
SUB B
LD (5C8C),A;SCR.CT
RES 0,(IY+02);TV.FLAG
CALL 0DD9;CL.SET
JP 0D6E;CLS.LOWER
20C1 IN.ITEM.1 CALL 204E;PR.POSN.1
JR Z,20C1;IN.ITEM.1
CP '('
JR NZ,20D8;IN.ITEM.2
RST 0020;NEXT.CHAR
CALL 1FDF;PRINT.2
RST 0018;GET.CHAR
CP ')'
JP NZ,1C8A;REPORT.C
RST 0020;NEXT.CHAR
JP 21B2;IN.NEXT.2
20D8 IN.ITEM.2 CP 'LINE'
JR NZ,20ED;IN.ITEM.3
RST 0020;NEXT.CHAR
CALL 1C1F;CLASS.01
SET 7,(IY+37);FLAGX
BIT 6,(IY+01);FLAGS
JP NZ,1C8A;REPORT.C
JR 20FA;IN.PROMPT
20ED IN.ITEM.3 CALL 2C8D;ALPHA
JP NC,21AF;IN.NEXT.1

1090
CALL 1C1F;CLASS.01
RES 7,(IY+37);FLAGX
20FA IN.PROMPT CALL 2530;SYNTAX.Z
JP Z,21B2;IN.NEXT.2
CALL 16BF;SET.WORK
LD HL,5C71;FLAGX
RES 6,(HL)
SET 5,(HL)
LD BC,0001
BIT 7,(HL)
JR NZ,211C;IN.PR.2
LD A,(5C3B);FLAGS
AND 01000000B
JR 211A;IN.PR.1
LD C,03
211A IN.PR.1 OR (HL)
LD (HL),A
211C IN.PR.2 RST 0030;BC.SPACES
LD (HL),0D
LD A,C
RRCA
RRCA
JR NC,2129;IN.PR.3
LD A,22
LD (DE),A
DEC HL
LD (HL),A
2129 IN.PR.3 LD (5C5B),HL;K.CUR
BIT 7,(IY+37);FLAGX
JR NZ,215E;IN.VAR.3
LD HL,(5C5D);CH.ADD
PUSH HL
LD HL,(5C3D);ERR.SP
PUSH HL
213A IN.VAR.1 LD HL,213A;IN.VAR.1
PUSH HL
BIT 4,(IY+30);FLAGS2

1091
JR Z,2148;IN.VAR.2
LD (5C3D),SP;ERR.SP
2148 IN.VAR.2 LD HL,(5C61);WORKSP
CALL 11A7;REMOVE.FP
LD (IY+00),FF;ERR.NR
CALL 0F2C;EDITOR
RES 7,(IY+01);FLAGS
CALL 21B9;IN.ASSIGN
JR 2161;IN.VAR.4
215E IN.VAR.3 CALL 0F2C;EDITOR
2161 IN.VAR.4 LD (IY+22),00;K.CUR hi
CALL 21D6;IN.CHAN.K
JR NZ,2174;IN.VAR.5
CALL 111D;ED.COPY
LD BC,(5C82);ECHO.E
CALL 0DD9;CL.SET
2174 IN.VAR.5 LD HL,5C71;FLAGX
RES 5,(HL)
BIT 7,(HL)
RES 7,(HL)
JR NZ,219B;IN.VAR.6
POP HL
POP HL
LD (5C3D),HL;ERR.SP
POP HL
LD (5C5F),HL;X.PTR
SET 7,(IY+01);FLAGS
CALL 21B9;IN.ASSIGN
LD HL,(5C5F);X.PTR
LD (IY+26),00;X.PTR hi
LD (5C5D),HL;CH.ADD
JR 21B2;IN.NEXT.2
219B IN.VAR.6 LD HL,(5C63);STKBOT
LD DE,(5C61);WORKSP
SCF
SBC HL,DE
LD B,H

1092
LD C,L
CALL 2AB2;STK.STO.$
CALL 2AFF;LET
JR 21B2;IN.NEXT.2
21AF IN.NEXT.1 CALL 1FFC;PR.ITEM.1
21B2 IN.NEXT.2 CALL 204E;PR.POSN.1
JP Z,20C1;IN.ITEM.1
RET

21B9 IN.ASSIGN LD HL,(5C61);WORKSP


LD (5C5D),HL;CH.ADD
RST 0018;GET.CHAR
CP 'STOP'
JR Z,21D0;IN.STOP
LD A,(5C71);FLAGX
CALL 1C59;VAL.FET.2
RST 0018;GET.CHAR
CP 0D
RET Z
21CE REPORT.C RST 0008;ERROR.1
DEFB 0B;"Nonsense in BASIC"
21D0 IN.STOP CALL 2530;SYNTAX.Z
RET Z
21D4 REPORT.H RST 0008;ERROR.1
DEFB 10;"STOP in INPUT"

21D6 IN.CHAN.K LD HL,(5C51);CURCHL


INC HL
INC HL
INC HL
INC HL
LD A,(HL)
CP 'K'
RET

21E1 CO.TEMP.1 RST 0020;NEXT.CHAR


21E2 CO.TEMP.2 CALL 21F2;CO.TEMP.3

1093
RET C
RST 0018;GET.CHAR
CP ','
JR Z,21E1;CO.TEMP.1
CP ';'
JR Z,21E1;CO.TEMP.1
JP 1C8A;REPORT.C
21F2 CO.TEMP.3 CP 'INK'
RET C
CP 'OVER'
CCF
RET C
PUSH AF
RST 0020;NEXT.CHAR
POP AF
21FC CO.TEMP.4 SUB C9
PUSH AF
CALL 1C82;EXPT.1NUM
POP AF
AND A
CALL 1FC3;UNSTACK.Z
PUSH AF
CALL 1E94;FIND.INT1
LD D,A
POP AF
RST 0010;PRINT.A.1
LD A,D
RST 0010;PRINT.A.1
RET

2211 CO.TEMP.5 SUB 11


ADC A,00
JR Z,2234;CO.TEMP.7
SUB 02
ADC A,00
JR Z,2273;CO.TEMP.C
CP 01

1094
LD A,D
LD B,01
JR NZ,2228;CO.TEMP.6
RLCA
RLCA
LD B,04
2228 CO.TEMP.6 LD C,A
LD A,D
CP 02
JR NC,2244;REPORT.K
LD A,C
LD HL,5C91;P.FLAG
JR 226C;CO.CHANGE
2234 CO.TEMP.7 LD A,D
LD B,07
JR C,223E;CO.TEMP.8
RLCA
RLCA
RLCA
LD B,00111000B
223E CO.TEMP.8 LD C,A
LD A,D
CP 0A
JR C,2246;CO.TEMP.9
2244 REPORT.K RST 0008;ERROR.1
DEFB 13;"Invalid colour"
2246 CO.TEMP.9 LD 5C8F;ATTR.T/MASK.T
CP 08
JR C,2258;CO.TEMP.B
LD A,(HL)
JR Z,2257;CO.TEMP.A
OR B
CPL
AND 00100100B
JR Z,2257;CO.TEMP.A
LD A,B
2257 CO.TEMP.A LD C,A

1095
2258 CO.TEMP.B LD A,C
CALL 226C;CO.CHANGE
LD A,07
CP D
SBC A,A
CALL 226C;CO.CHANGE
RLCA
RLCA
AND 01010000B
LD B,A
LD A,00001000B
CP D
SBC A,A

226C CO.CHANGE XOR (HL)


AND B
XOR (HL)
LD (HL),A
INC HL
LD A,B
RET
2273 CO.TEMP.C SBC A,A
LD A,D
RRCA
LD B,10000000B
JR NZ,227D;CO.TEMP.D
RRCA
LD B,01000000B
227D CO.TEMP.D LD C,A
LD A,D
CP 08
JR Z,2287;CO.TEMP.E
CP 02
JR NC,2244;REPORT.K
2287 CO.TEMP.E LD A,C
LD HL,5C8F;ATTR.T
CALL 226C;CO.CHANGE

1096
LD A,C
RRCA
RRCA
RRCA
JR 226C;CO.CHANGE

2294 BORDER CALL 1E94;FIND.INT1


CP 08
JR NC,2244;REPORT.K
229B OUT (FE),A
RLCA
RLCA
RLCA
BIT 5,A
JR NZ,22A6;BORDER.1
XOR 00000111B
22A6 BORDER.1 LD (5C4B),A;BORDCR
RET

22AA PIXEL.ADD LD A,AF;175d


SUB B
JP C,24F9;REPORT.B
LD B,A
AND A
RRA
SCF
RRA
AND A
RRA
XOR B
AND 11111000B
XOR B
LD H,A
LD A,C
RLCA
RLCA
RLCA

1097
XOR B
AND 11000111B
XOR B
RLCA
RLCA
LD L,A
LD A,C
AND 00000111B
RET

22CB POINT.SUB CALL 2307;STK.TO.BC


CALL 22AA;PIXEL.ADD
LD B,A
INC B
LD A,(HL)
22D4 POINT.LP RLCA
DJNZ 22D4;POINT.LP
AND 01
JP 2D28;STACK.A

22DC PLOT CALL 2307;STK.TO.BC


CALL 22E5;PLOT.SUB
JP 0D4D;TEMPS
22E5 PLOT.SUB LD (5C7D),BC;COORDS
CALL 22AA;PIXEL.ADD
LD B,A
INC B
LD A,FE
22F0 PLOT.LOOP RRCA
DJNZ 22F0;PLOT.LOOP
LD B,A
LD A,(HL)
LD C,(IY+57);P.FLAG
BIT 0,C
JR NZ,22FD;PL.TST.IN
AND B
22FD PL.TST.IN BIT 2,C

1098
JR NZ,2303;PLOT.END
XOR B
CPL
2303 PLOT.END LD (HL),A
JP 0BDB;PO.ATTR

2307 STK.TO BC CALL 2314;STK.TO.A


LD B,A
PUSH BC
CALL 2314;STK.TO.A
LD E,C
POP BC
LD D,C
LD C,A
RET

2314 STK.TO.A CALL 2DD5;FP.TO.A


JP C,24F9;REPORT.B
LD C,01
RET Z
LD C,FF
RET

2320 CIRCLE RST 0018;GET.CHAR


CP ','
JP NC,1C8A;REPORT.C
RST 0020;NEXT.CHAR
CALL 1C82;EXPT.1NUM
CALL 1BEE;CHECK.END
RST 0028;FP.CALC
DEFB 2A;abs
DEFB 3D;re-stack
DEFB 38;end-calc
LD A,(HL)
CP 81
JR NC,233B;C.R.GRE.1
RST 0028;FP.CALC

1099
DEFB 02;delete
DEFB 38;end-calc
JR 22DC;PLOT
233B C.R.GRE.1 RST 0028;FP.CALC
DEFB A3;stk-pi/2
DEFB 38;end-calc
LD (HL),83
RST 0028;FP.CALC
DEFB C5;st-mem-5
DEFB 02;delete
DEFB 38;end-calc
CALL 247D;CD.PRMS1
2347 PUSH BC
RST 0028;FP.CALC
DEFB 31;duplicate
DEFB E1;get-mem-1
DEFB 04;multiply
DEFB 38;end-calc
LD A,(HL)
CP 80
JR NZ,235A;C.ARC.GE.1
RST 0028;FP.CALC
DEFB 02;delete
DEFB 02;delete
DEFB 38;end-calc
POP BC
JP 22DC;PLOT

235A C.ARC.GE1 RST 0028;FP.CALC


DEFB C2;st-mem-2
DEFB 01;exchange
DEFB C0;st-mem-0
DEFB 02;delete
DEFB 03;subtract
DEFB 01;exchange
DEFB E0;get-mem-0
DEFB 0F;addition

1100
DEFB C0;st-mem-0
DEFB 01;exchange
DEFB 31;duplicate
DEFB E0;get-mem-0
DEFB 01;exchange
DEFB 31;duplicate
DEFB E0;get-mem-0
DEFB A0;stk-zero
DEFB C1;st-mem-1
DEFB 02;delete
DEFB 38;end-calc
INC (IY+62);mem-2
CALL 1E94;FIND.INT1
LD L,A
PUSH HL
CALL 1E94;FIND.INT1
POP HL
LD H,A
LD (5C7D),HL;COORDS
POP BC
JP 2420;DRW.STEPS

2382 DRAW RST 0018;GET.CHAR


CP ','
JR Z,238D;DR.3.PRMS
CALL 1BEE;CHECK.END
JP 2477;LINE.DRAW
238D DR.3.PRMS RST 0020;NEXT.CHAR
CALL 1C82;EXPT.2NUM
CALL 1BEE;CHECK.END
RST 0028;FP.CALC
DEFB C5;st-mem-5
DEFB A2;stk-half
DEFB 04;multiply
DEFB 1F;sin
DEFB 31;duplicate
DEFB 30;not

1101
DEFB 30;not
DEFB 00;jump-true
DEFB 06;to DR.SIN.NZ
DEFB 02;delete
DEFB 38;end-calc
JP 2477;LINE.DRAW
23A3 DR.SIN.NZ DEFB C0;st-mem-0
DEFB 02;delete
DEFB C1;st-mem-1
DEFB 02;delete
DEFB 31;duplicate
DEFB 2A;abs
DEFB E1;get-mem-1
DEFB 01;exchange
DEFB E1;get-mem-1
DEFB 2A;abs
DEFB 0F;addition
DEFB E0;get-mem-0
DEFB 05;division
DEFB 2A;abs
DEFB E0;get-mem-0
DEFB 01;exchange
DEFB 3D;re-stack
DEFB 38;end-calc
LD A,(HL)
CP 81
JR NC,23C1;DR.PRMS
RST 0028;FP.CALC
DEFB 02;delete
DEFB 02;delete
DEFB 38;end-calc
JP 2477;LINE.DRAW
23C1 DR.PRMS CALL 247D;CD.PRMS1
23C4 PUSH BC
RST 0028;FP.CALC
DEFB 02;delete
DEFB E1;get-mem-1

1102
DEFB 01;exchange
DEFB 05;division
DEFB C1;st-mem-1
DEFB 02;delete
DEFB 01;exchange
DEFB 31;duplicate
DEFB E1;get-mem-1
DEFB 04;multiply
DEFB C2;st-mem-2
DEFB 02;delete
DEFB 01;exchange
DEFB 31;duplicate
DEFB E1;get-mem-1
DEFB 04;multiply
DEFB E2;get-mem-2
DEFB E5;get-mem-5
DEFB E0;get-mem-0
DEFB 03;subtract
DEFB A2;stk-half
DEFB 04;multiply
DEFB 31;duplicate
DEFB 1F;sin
DEFB C5;st-mem-5
DEFB 02;delete
DEFB 20;cos
DEFB C0;st-mem-0
DEFB 02;delete
DEFB C2;st-mem-2
DEFB 02;delete
DEFB C1;st-mem-1
DEFB E5;get-mem-5
DEFB 04;multiply
DEFB E0;get-mem-0
DEFB E2;get-mem-2
DEFB 04;multiply
DEFB 0F;addition
DEFB E1;get-mem-1

1103
DEFB 01;exchange
DEFB C1;st-mem-1
DEFB 02;delete
DEFB E0;get-mem-0
DEFB 04;multiply
DEFB E2;get-mem-2
DEFB E5;get-mem-5
DEFB 04;multiply
DEFB 03;subtract
DEFB C2;st-mem-2
DEFB 2A;abs
DEFB E1;get-mem-1
DEFB 2A;abs
DEFB 0F;addition
DEFB 02;delete
DEFB 38;end-calc
LD A,(DE)
CP 81
POP BC
JP C,2477;LINE.DRAW
PUSH BC
RST 0028;FP.CALC
DEFB 01;exchange
DEFB 38;end-calc
LD A,(5C7D);COORDS
CALL 2D28;STACK.A
RST 0028;FP.CALC
DEFB C0;st-mem-0
DEFB 0F;addition
DEFB 01;exchange
DEFB 38;end-calc
LD A,(5C7E);COORDS hi
CALL 2D28;STACK.A
RST 0028;FP.CALC
DEFB C5;st-mem-5
DEFB 0F;addition
DEFB E0;get-mem-0

1104
DEFB E5;get-mem-5
DEFB 38;end-calc
POP BC
2420 DRW.STEPS DEC B
JR Z,245F;ARC.END
JR 2439;ARC.START
2425 ARC.LOOP RST 0028;FP.CALC
DEFB E1;get-mem-1
DEFB 31;duplicate
DEFB E3;get-mem-3
DEFB 04;multiply
DEFB E2;get-mem-2
DEFB E4;get-mem-4
DEFB 04;multiply
DEFB 03;subtract
DEFB C1;st-mem-1
DEFB 02;delete
DEFB E4;get-mem-4
DEFB 04;multiply
DEFB E2;get-mem-2
DEFB E3;get-mem-3
DEFB 04;multiply
DEFB 0F;addition
DEFB C2;st-mem-2
DEFB 02;delete
DEFB 38;end-calc
2439 ARC.START PUSH BC
RST 0028;FP.CALC
DEFB C0;st-mem-0
DEFB 02;delete
DEFB E1;get-mem-1
DEFB 0F;addition
DEFB 31;duplicate
DEFB 38;end-calc
LD A,(5C7D);COORDS
CALL 2D28;STACK.A
RST 0028;FP.CALC

1105
DEFB 03;subtract
DEFB E0;get-mem-0
DEFB E2;get-mem-2
DEFB 0F;addition
DEFB C0;st-mem-0
DEFB 01;exchange
DEFB E0;get-mem-0
DEFB 38;end-calc
LD A,(5C7E);COORDS hi
CALL 2D28;STACK.A
RST 0028;FP.CALC
DEFB 03;subtract
DEFB 38;end-calc
CALL 24B7;DRAW.LINE
POP BC
DJNZ 2425;ARC.LOOP
245F ARC.END RST 0028;FP.CALC
DEFB 02;delete
DEFB 02;delete
DEFB 01;exchange
DEFB 38;end-calc
LD A,(5C7D);COORDS
CALL 2D28;STACK.A
RST 0028;FP.CALC
DEFB 03;subtract
DEFB 01;exchange
DEFB 38;end-calc
LD A,(5C7E);COORDS hi
CALL 2D28;STACK.A
RST 0028;FP.CALC
DEFB 03;subtract
DEFB 38;end-calc
2477 LINE.DRAW CALL 24B7;DRAW.LINE
JP 0D4D;TEMPS
247D CD.PRMS1 RST 0028;FP.CALC
DEFB 31;duplicate
DEFB 28;sqr

1106
DEFB 34;stk-data
DEFB 32;exponent 82
DEFB 00;(00,00,00)
DEFB 01;exchange
DEFB 05;division
DEFB E5;get-mem-5
DEFB 01;exchange
DEFB 05;division
DEFB 2A;abs
DEFB 38;end-calc
CALL 2DD5;FP.TO.A
JR C,2495;USE.252
AND 11111100B
ADD A,04
JR NC,2497;DRAW.SAVE
2495 USE.252 LD A,FC;252d
2497 DRAW.SAVE PUSH AF
CALL 2D28;STACK.A
RST 0028;FP.CALC
DEFB E5;get-mem-5
DEFB 01;exchange
DEFB 05;division
DEFB 31;duplicate
DEFB 1F;sin
DEFB C4;st-mem-4
DEFB 02;delete
DEFB 31;duplicate
DEFB A2;stk-half
DEFB 04;multiply
DEFB 1F;sin
DEFB C1;st-mem-1
DEFB 01;exchange
DEFB C0;st-mem-0
DEFB 02;delete
DEFB 31;duplicate
DEFB 04;multiply
DEFB 31;duplicate

1107
DEFB 0F;addition
DEFB A1;stk-one
DEFB 03;subtract
DEFB 1B;negate
DEFB C3;st-mem-3
DEFB 02;delete
DEFB 38;end-calc
POP BC
RET

24B7 DRAW.LINE CALL 2307;STK.TO.BC


LD A,C
CP B
JR NC,24C4;DL.X.GE.Y
LD L,C
PUSH DE
XOR A
LD E,A
JR 24CB;DL.LARGER
24C4 DL.X.GE.Y OR C
RET Z
LD L,B
LD B,C
PUSH DE
LD D,00
14CB DL.LARGER LD H,B
LD A,B
RRA
24CE D.L.LOOP ADD A,L
JR C,24D4;D.L.DIAG
CP H
JR C,24DB;D.L.HR.VT
24D4 D.L.DIAG SUB H
LD C,A
EXX
POP BC
PUSH BC

1108
JR 24DF;DL.STEP
24DB D.L.HR.VT LD C,A
PUSH DE
EXX
POP BC
24DF D.L.STEP LD HL,(5C7D);COORDS
LD A,B
ADD A,H
LD B,A
LD A,C
INC A
ADD A,L
JR C,24F7;D.L.RANGE
JR Z,24F9;REPORT.B
24EC D.L.PLOT DEC A
LD C,A
CALL 22E5;PLOT.SUB
EXX
LD A,C
DJNZ 24CE;D.L.LOOP
POP DE
RET
24F7 D.L.RANGE JR Z,24EC;D.L.PLOT
24F9 REPORT.B RST 0008;ERROR.1
DEFB 0A;"Integer out of range"

24FB SCANNING RST 0018;GET.CHAR


LD B,00
PUSH BC
24FF S.LOOP.1 LD C,A
LD HL,2596;scanning function table
CALL 16DC;INDEXER
LD A,C
JP NC,2684;S.ALPHNUM
LD B,00
LD C,(HL)
ADD HL,BC

1109
JP (HL)
250F S.QUOTE.S CALL 0074;CH.ADD+1
INC BC
CP 0D
JP Z,1C8A;REPORT.C
CP '"'
JR NZ,250F;S.QUOTE.S
CALL 0074;CH.ADD+1
CP '"'
RET
2522 S.2.COORD RST 0020;NEXT.CHAR
CP '('
JR NZ,252D;S.RPORT.C
CALL 1C79;NEXT.2NUM
RST 0018;GET.CHAR
CP ')'
252D S.RPORT.C JP NZ,1C8A;REPORT.C

2530 SYNTAX.Z BIT 7,(IY+01);FLAGS


RET

2535 S.SCRN$.S CALL 2307;STK.TO.BC


LD HL,(5C36);CHARS
LD DE,0100
ADD HL,DE
LD A,C
RRCA
RRCA
RRCA
AND 11000000B
XOR B
LD E,A
LD A,C
AND 00011000B
XOR 01000000B
LD D,A
LD B,60;96d characters

1110
254F S.SCRN.LP PUSH BC
PUSH DE
PUSH HL
LD A,(DE)
XOR (HL)
JR Z,255A;S.SC.MATCH
INC A

1111
1112
1113
Index to the
Spectrum
ROM
For machine code programmers
“Digital archaeology at its finest: A lost book, converted all the way from
Spectrum format to PDF; with new insights on THE Bible of the ZX
Spectrum 48k ROM for the ZX Spectrum Community. A lot of know-
how and insightful tips about the innards of the original ZX Spectrum
operating system.”
Rui Ribeiro, Planeta Sinclar team, emulator author, WSpecem/QtSpecem

-----------------
I'm glad to have found the information contained within this book.
It hasn't been very easy transforming it from Tasword II files into
something that can be viewed with relative ease on a modern
computer. Enjoy the contents and accept the mistakes.
If anyone cares to format it further, then please feel free and leave the
text as you have found it so far, add some of your own.

Matthew Logue, Spectrum user & ZX archaeologist.

1114

You might also like