Professional Documents
Culture Documents
Spectrum
ROM
For machine code programmers
1
Index to the Spectrum ROM
For machine code programmers
By Francis G Miles
2
© Lookback books
3
To Francis - The Author.
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.
5
The items indexed are of five kinds:
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
FLAGS is IY+1
TV FLAG is IY+2
FLAGS2 is IY+48d
FLAGX is IY+55d
P FLAG is IY+87d
8
The alphabetical order observed throughout is as
follows:
9
A
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
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
12
3186 NORML NOW
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
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
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
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
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
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
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
23
2439 ARC START
24
295A SFA LOOP go to next argument
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
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.
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)
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
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
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
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
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
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
auto
Abbreviation used in this index to mean "contains a jump
back to the same routine"
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
39
B
backspace see CURSOR LEFT, 1007 ED LEFT
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.
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
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
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
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)
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!
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
63
block graphic characters see graphics keys
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
65
11CB START/NEW make border white on start-up
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
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
68
3453 G LOOP left undisturbed
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
71
C
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
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
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
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.
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)
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
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
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)
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 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.
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
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
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
103
Written by:
1219 RAM SET
166B PTR NEXT
Read by:
1113 KEY CHAN
1610 CHAN OP 1
1701 CLOSE 2
1736 OPEN
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 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".
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
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
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:
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
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
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.
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
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
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
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
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
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
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
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
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
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)
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
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
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
138
position.
Called from:
0DAF CL ALL
Exit from:
0D8E CLS 3 (0D6B CLS)
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
141
Called from:
0EFD COPY L 1
1219 RAM SET
Exit from:
0EDA COPY END (0EAC COPY, 0ECD COPY BUFF)
142
1EAF CLEAR RUN
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
145
Jumps from:
0E4D CL LINE 2
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
147
close stream look-up table 1716 see tables
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.
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
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
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
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
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
158
COLLECT CHARACTER RESTART see 0018 GET CHAR
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)
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
163
CLASS 00 to 1CDB CLASS 0B, commands, functions and
operators
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:
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
---------+--+-+-+------------+--+--+--------------+------------
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
---------+--+-+--------------+--+--+--------------+------------
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 variable of FOR ... NEXT loop see 1D93 FOR, 1DAB
NEXT
173
coordinates see DISPLAY AREA; of arc see 247D CD PRMS1
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.
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
copying over
An expression used only at 117C ED C DONE, meaning
copying the edit line or INPUT expression to the lower screen.
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
179
Jumps from:
0EC9 COPY 2
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
181
2246 CO TEMP 9 (twice)
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
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
184
CO TEMP 9 2246 (09F4 PRINT OUT)
Jumps from:
223E CO TEMP 8
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
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
187
CURSOR LEFT SUBROUTINE see 0A23 PO BACK 1
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
189
D
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)
191
1E37 DATA 2 passed by in run-time
192
2DE3 PRINT FP
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
194
decoding key input see KEYBOARD SCANNING, 0333 K
DECODE
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.
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
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$
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.
200
variable names
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.
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
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
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
207
current print position in the lower part of the screen.
Written by:
0AF0 PO ST E
Read by:
0B03 PO FETCH
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)
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
213
DIM SIZES 2C7F (2C02 DIM)
Jumps from:
auto
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:
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
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
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
230
D L STEP 24DF (24B7 DRAW LINE)
Jumps from:
24D4 D L DIAG
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.
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
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
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:
s T
1 + --- > ---
d D
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
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
242
DR SIN NZ 23A3 (2382 DRAW)
Jumps from:
238D DR 3 PRMS
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
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
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.
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
251
EACH S 6 19B1 (198B EACH STMT)
Exit from:
19A5 EACH S 4 (198B EACH STMT)
252
ED C END 117E (111D ED COPY)
Exit from:
117C ED C DONE (111D ED COPY)
1167 ED FULL
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
255
2161 IN VAR 4
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.
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
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
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
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
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.
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
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
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]
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.
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
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
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 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.
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
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
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
276
embedded colours, embedded control characters,
embedded print items see colours, control characters
277
end marker of tables see 16DC INDEXER
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
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
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".
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 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".
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
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.
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
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
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
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.
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
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
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
299
F
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.
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
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
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
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
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
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)
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
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
310
Turned on:
0BA4 PR ALL 2
Turned off:
0EE7 PRB BYTES
Read by:
1303 MAIN 4
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
312
1031 ED EDGE line start address depends on state of
flag read in 1195 SET DE
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.
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
316
2852 SF ARG VL (twice)
295A SFA LOOP
296B SFA CP VR (twice)
Jumps from:
auto
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.
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".
FOR ... NEXT control variables see FOR ... NEXT loops,
variables
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
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
330
2DA2 FP TO BC
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
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
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
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)
341
G
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
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
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
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
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.
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)
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 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
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)
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
359
H
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
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
361
I
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.
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.
364
Output parameters: none.
Called from:
2148 IN VAR 2
2174 IN VAR 5
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
366
auto
21B2 IN NEXT 2
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
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.
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
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)
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.]
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 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/output routines
A major section of ROM, see Introduction, from 028E KEY
380
SCAN to 11A7 REMOVE FP: keyboard, speaker, cassettes,
screen,
printer handling.
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
382
3474 NEG TEST (346E negate)
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
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
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
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
389
Called from:
19FB E LINE NO
2CBB NOT BIN
2CFF ST E PART
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.
391
255A S SC MTCH save mask to match seven remaining
bytes
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
393
2AE8 I CARRY (2ACC INT EXP1, 2ACD INT EXP2)
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)
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
397
next literal will be read.
Called from:
2D60 E LOOP
37AA cos
37E2 atn
Exit from:
368F jump-true (as JUMP)
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
400
K
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
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$
406
KEY BITS 02A1 (028E KEY SCAN)
Jumps from:
auto
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
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.
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
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
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.
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
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
420
Jumps from:
02AB KEY DONE
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$
424
02A1 KEY BITS
425
02D1 K CH SET
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$
428
L
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.
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
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
437
Jumps from:
05B3 LD FLAG
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
440
2B66 L EXISTS (2AFF LET)
441
082E LD DATA
442
leader (save/load) see timing
443
length of expression see strings
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
445
37E2 atn
Rems:
34F9 greater-0 exits into, with opposite effect
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
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
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
455
155D MAIN ADD
1795 AUTO LIST (twice)
17E4 AUTO L 3
1822 LIST 5
190F LN FETCH
1B9E LINE NEW
1E45 REST RUN
456
1B7D STMT R 1
457
Called from:
0FA9 ED EDIT
1059 ED UP
190F LN FETCH
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.
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
466
L IN W/S 2BA3 (2AFF LET)
Jumps from:
2B9B L LENGTH
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.
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)
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
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.
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
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
476
- DE holds the required line number
- BC unchanged.
Called from:
0FF3 ED DOWN
1835 LIST ALL
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
478
LOADing routines
Ie LOAD (0808 LD CONTRL),
MERGE (08B6 ME CONTRL),
VERIFY (07CB VR CONTRL).
0802 LD BLOCK common to all
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
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
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
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
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.
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$
499
M
500
MAIN PRINTING SUBROUTINE see 15EF OUT CODE
501
MAIN 8 1384 (see BASIC INTERPRETER)
Jumps from:
1376 MAIN 7
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
503
marker bit see 8-bit loop after end of alphabet
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:
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
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
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
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 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.
513
ME OLD V2 0909 (08B6 ME CONTRL)
Jumps from:
08F9 ME OLD VP
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
515
MESSAGE PRINTING SUBROUTINE see 0C0A PO MSG
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
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.
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
525
routine 1793 CAT ETC.
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
532
N
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
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
538
new key see 5C3B FLAGS bit 5, 5C00 KSTATE
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
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.
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
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
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
547
1DE2 NEXT 1
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
549
'no key yet' see FLAGS bit 5
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
554
316E SHIFT ONE
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)
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.
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
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
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
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
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
563
Read by:
1E5F CONTINUE
564
0F81 ADD CHAR
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.
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
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.
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
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.
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
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.
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)
573
1937 OUT CHAR (twice)
195A OUT CH 1
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
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
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
579
Jumps from:
1894 OUT LINE4
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!)
581
output address of channel, output routine of streams see
channels and streams
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
583
192B OUT SP 1
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
-----
587
OBD9 PO ATTR jump for PAPER 9
18C1 OUT FLASH eliminate PAPER 9
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
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.
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
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.
592
Jumps from:
2EB8 PF ALL 9
593
Jumps from:
2EEF PF FR EXX
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
595
P FLASH 1AED see 1A7A syntax offset table
P FOR 1A90
P FORMAT 1B06
see 1A7A syntax offset table
596
PF POSTVE 2DF8 (2DE3 PRINT FP)
Jumps from:
2DE3 PRINT FP
P GO SUB 1A86
P GO TO 1A7D
see 1A7A syntax offset table
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
598
11B7 NEW
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
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
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 MERGE 1AE2
P MOVE 1B0A
P NEW 1AA8
P NEXT 1A98
see 1A7A syntax offset table
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
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)
607
PO BACK 2 0A38 (09FD PRINT OUT)
Jumps from:
0A23 PO BACK 1
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
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
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
612
auto
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
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
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
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
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
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
628
0AC2 PO TAB (0A7A PO 1 OPER)
Jumps from:
auto
629
Called from:
0B5F PO T
P OUT 1AF1
P OVER 1AF0
see 1A7A syntax offset table
P PAPER 1AEC
P PAUSE 1AC5
see 1A7A syntax offset table
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
631
0B1D PO F PR
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
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
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
635
See 1FCD PRINT.
Called from:
1FDF PRINT 2
2067 PR POSN 3
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)
637
Called from:
30F0 MULT LONG (twice)
31AF division (twice)
P RESTORE 1ACF
P RETURN 1A8D
see 1A7A syntax parameter table
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.
642
area" in 0B6A PO CHAR 2; see DISPLAY AREA
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
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"
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)
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
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
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
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
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
687
Called from:
1FCF PRINT 1
20C1 IN ITEM 1
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
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
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
693
Q
694
R
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
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.
697
107F ED ERROR
1167 ED FULL
11B7 NEW
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
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)
700
1DED READ (twice)
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
703
Exit from:
1015 ED DELETE
2BAF L ADD$
Rems:
19E5 RECLAIM 1 merely different input parameters
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.
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
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
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 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 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.
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 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 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 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 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
716
3146 OFLW1 CLR
3186 NORML NOW
31AF division
36C4 EXP (twice)
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".
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
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)
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
721
3297 re-stack twice in succession with a switch of pointers.
See
3293 RE ST TWO.
722
Exit: RET, from 1E45 REST RUN.
Output parameters: none
- X is off the stack.
Rems:
1EA1 RUN always does RESTORE 0
723
Exit from:
36C4 EXP (twice)
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.
725
36C4 exp through
3705 N NEGTV (twice)
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
727
S
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
734
SA DATA 1 0692 (0605 SAVE ETC)
Jumps from:
0672 SA V OLD
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
736
Jumps from:
0629 SA BLANK
737
auto
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
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.
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
757
SA V NEW 0685 (0605 SAVE ETC)
Jumps from:
0652 SA DATA
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
760
1C4E CLASS 02 address dropped
1C96 PERMS address dropped
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
769
scanning DECIMAL routine see 268D S BIN
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.
770
S CONT 3 2713 (24FB SCANNING)
Jumps from:
auto
771
0D2D PO SCR 4B scroll B lines
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
774
SECND LOW 356B (353B no-l-eql)
Jumps from:
3575 SEC PLUS
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.
775
Read by:
25F8 S RND
776
Output parameters: none.
Exit from:
1B55 GET PARAM
Rems:
1C16 JUMP C R all separators have been considered
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
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
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
782
20FA IN PROMPT
783
SF BRKT 2 27E4 (25F5 S FN)
Jumps from:
27D9 SF ARGMTS
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.
790
Jumps from:
2831 SF VALUES
2852 SF ARG VL
791
offset for 3492 sgn.
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
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
796
Jumps from:
2CF2 SIGN FLAG
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
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$
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
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
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
806
Jumps from:
2A94 SL DEFINE
807
Jumps from:
26DF S NEGAT
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
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
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
811
12CF MAIN 3 (hi byte)
1835 LIST ALL (hi byte)
2096 INPUT 1
812
S Q PRMS 25D9 (25B3 S QUOTE)
Jumps from:
2583 S QUOTE
25BE S Q AGAIN
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)
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
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)
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$
821
Jumps from:
254F S SCRN LP
255D S SC ROWS
822
25D9 S Q PRMS
2668 S SCREEN$
Rems:
257D S SCR STO mistake; parameters stacked twice
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.
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
'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.
827
steps (line drawing) see 24B7 DRAW LINE
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
831
2CDA NXT DGT 1
2D40 NXT DGT 2
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.
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
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)
835
Exit from:
257D S SCR STO
2AB1 STK ST 0
836
Called from:
2A2C SV ELEM$
Exit from:
2AAD SL STORE
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
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
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
843
1BD1 NEXT LINE
1BF4 STMT NEXT
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
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
847
strings
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
855
string variable see string, variables
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
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
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
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
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.
861
SUBN ONLY 31F2 (31AF division)
Jumps from:
31DB DIV 34TH
862
1EED GO SUB
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
864
Jumps from:
29EA SV LOOP
865
Jumps from:
2A12 SV RPT C
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.
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
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.
871
T
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.]
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
875
copied by 1219 RAM SET from ROM into the channel
information area in RAM on start-up.
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.
877
1B6F SEPARATOR checks for presence of separator
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
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.
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
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
882
terminator see BASIC line
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
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)
889
3501 not
351B or
3524 no-&-no
352D str-&-no
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
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
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
897
top of screen see DISPLAY AREA
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
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
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
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
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
906
17FB LIST 1
Read by:
10A8 KEY INPUT
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
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.
908
U
909
Rems:
0B24 PO ANY separates UDGs from other codes
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)
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.
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.
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-$.
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.
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
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).
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.
924
Output parameters: none
- the evaluation result is on the calculator stack.
Called from:
1C4E CLASS 02
1E0A READ 1
VALUE of FOR ... NEXT loop see FOR ... NEXT loops
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.
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
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
933
2934 V SYNTAX
2943 V PASS
934
VERIFY CONTROL ROUTINE see 07CB VR CONTRL (in 0605
SAVE ETC)
935
VR CONT 2 07F4 (0605 SAVE ETC)
Jumps from:
07E9 VR CONT 1
936
V TEST FN 28E3 (28B2 LOOK VARS)
Jumps from:
28B2 LOOK VARS
937
W
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)
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
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
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
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
949
Z
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
951
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
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
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
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
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
959
The "/"s here indicate the sections in which the report is
printed.
133C MAIN 5 printed after report message
-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
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
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
963
039D K KLC DGT special case in key decoding
03B2 K @ CHR special case in key decoding
key (5Eh) see "up arrow"; the symbol can't be printed with
this word processor
<= key (C7), >= key (C8), <> key (C9) see < key
964
APPENDIX - BASIC programs
10 REM "hex
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
5 REM "fpdec
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
9000 STOP
9999 SAVE d1"fpdec" LINE 9000
5 REM "fpnum
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
968
LET n=n-INT n:
NEXT f:
STOP
969
1900 CLS:
PRINT "FP format", f$'
"Decimal",
2**(exp-32)*mant*sign:
POKE 23658,0: REM CAPS LOCK off
1950 STOP
5 REM "fpconst
970
20 PRINT "Press any key and enter decimal
number or expression."
30 PAUSE 0
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
972
100 INPUT x: CLS: FOR f=x to 70000: PRINT f, PEEK f:
NEXT f
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 REM "hexpt
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))
1400 STOP
974
9999 SAVE d1 "hexpt" LINE 1000
975
ROM LISTING
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
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'
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
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
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
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
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
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
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
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
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
1000
DEFB 1A;"Tape loading error"
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
1007
JP 0B03;PO.FETCH
1008
PUSH AF
LD (IY+57),01;P.FLAG
LD A,20
CALL 0B65;PO.CHAR
POP AF
LD (5C91),A;P.FLAG
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
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
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
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
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
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
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
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
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
1027
DJNZ 0F18;COPY.L.4
DEC C
JR NZ,0F14;COPY.L.3
RET
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
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
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
1032
LD A,00
JP 1601;CHAN.OPEN
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
1036
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
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
1040
SET 7,(IY+01);FLAGS
LD (IY+00),FF;ERR.NR
LD (IY+0A),01;NSPPC
CALL 1B8A;LINE.RUN
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
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'
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
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
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)
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
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
1049
RET
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)
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
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
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
1057
POP HL
LD (5C8F),HL;ATTR.T
EXX
RET
1058
POP HL
191C LN.STORE BIT 5,(IY+37);FLAGX
RET NZ
LD (HL),D
DEC HL
LD (HL),E
RET
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
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
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
1062
RET
1063
CALL 192A;OUT.SP.NO
LD A,L
1A42 OUT.NUM.4 CALL 15EF;OUT.CODE
POP HL
POP DE
RET
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
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
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
1069
JR 1BD1;NEXT.LINE
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
1071
DEFB 1C7A-1C09;CLASS 08
DEFB 1CBE-1C0A;CLASS 09
DEFB 1C8C-1C0B;CLASS 0A
DEFB 1CDB-1C0C;CLASS 0B
1072
LD (5C4D),HL;DEST
RET
1073
1C8A REPORT.C RST 0008;ERROR.1
DEFB 0B;"Nonsense in BASIC"
1074
1CD6 CL.09.1 CALL 21E2;CO.TEMP
JR 1C7A;EXPT.2NUM
1CDE FETCH.NUM CP 0D
JR Z,1CE6;USE.ZERO
CP ':'
JR NZ;1C82;EXPT.2NUM
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
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"
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
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
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
1080
LD DE,0200
JP 198B;EACH.STMT
1081
1E60 POKE CALL 1E85;TWO.PARAM
LD (BC),A
RET
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)
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
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
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)
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
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
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
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
1096
LD A,C
RRCA
RRCA
RRCA
JR 226C;CO.CHANGE
1097
XOR B
AND 11000111B
XOR B
RLCA
RLCA
LD L,A
LD A,C
AND 00000111B
RET
1098
JR NZ,2303;PLOT.END
XOR B
CPL
2303 PLOT.END LD (HL),A
JP 0BDB;PO.ATTR
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
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
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
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"
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
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.
1114