You might, because of the cursor-right control character (CHR$ 9) between '12'
and '34', expect it to print:
12 34
But in fact it prints:
1234
The reason is that the routine at PO_RIGHT, which is responsible for handling
CHR$ 9, does not update the print position after overprinting a space to the
right of the '2', and so effectively prints nothing at all.
Note, however, that even if the print position were updated, CHR$ 9 would still
not be a true cursor-right control character, because it overprints a space
using the current colours rather than leaving the colours as they are. The
following PRINT command demonstrates this bug:
PRINT AT 0,0;1; INK 2;AT 0,0;CHR$ 9
No step back
Consider the following PRINT commands:
PRINT 1
PRINT CHR$ 8;2
PRINT CHR$ 8;3
They should print '2' at the right end of the top line of the display - because
of the cursor-left character (CHR$ 8) before the '2' - but instead the '2' is
printed at the beginning of the second line, as if the CHR$ 8 were not there.
However, the '3' is (correctly) printed at the right end of the second line.
The reason is that the instruction at 2610 is 'LD A,24' instead of
'LD A,25', which prevents CHR$ 8 from moving the print position up to
the top line of the display.
A step back too far
In addition to sometimes not working when it should, the cursor-left character
(CHR$ 8) also sometimes works when it shouldn't - specifically, when the print
position is at the top-left of the screen. For example:
PRINT AT 0,0;CHR$ 8;12
Here the CHR$ 8 should have no effect, but instead the '1' is printed at
(-1,0), which the ROM calculates to be at address 22783, or (7,31) in the
attribute file.
The reason, again, is that the instruction at 2610 is 'LD A,24'
instead of 'LD A,25', which allows CHR$ 8 to move the print position up
beyond the top line of the display.
Saving a simple string
It is possible to save a simple string (as opposed to an array) to tape, but it
cannot be restored. For example:
LET a$="12"
SAVE "a" DATA a$()
saves the simple string variable a$ to tape without giving an error. When the
same variable is loaded from tape:
LOAD "" DATA a$()
no error is given until a$ is accessed - both PRINT a$ and
PRINT a$(1) result in a '3 Subscript wrong' error.
The reason it's possible to save a simple string to tape is that the routine at
SAVE_ETC, when it finds the string variable to be saved, does not check whether
it is an array.
The neverending RESTORE
When the following program is run, the Spectrum will enter an infinite loop:
10 RESTORE 60000
The reason is that the routine at NEXT_ONE - which is called from LINE_ADDR to
find the next line in the BASIC program - does not stop at the end of the BASIC
program (if there even is one), but keeps going through the variables area and
the rest of the RAM. Depending on the contents of the RAM, the routine can end
up examining the same memory area over and over again, never finding anything
that looks like a BASIC line with a suitably high number.
Curse the cursor
In some circumstances, pressing EDIT can bring the current line cursor down to
the editing area along with the current line, and the cursor must be removed
before the line is re-entered. For example, type in the following program,
pressing ENTER where shown:
10 PRINT<ENTER>
11<ENTER>
Then press EDIT, and see line 10 come down to the editing area with the cursor
in tow.
The reason this happens is that the routine at ED_EDIT decrements the line
number at E-PPC (from 11 to 10) so as to avoid printing the cursor,
but doesn't take into account that the number of the line that will be brought
down for editing is one less than its current value.
STR$ and small numbers
Because of a bug in the handling of the calculator stack at PF_SMALL, a binary
expression with STR$ on the right hand side is evaluated as if its left hand
side is empty. For example:
PRINT "2"+STR$ 0.5
prints '0.5' instead of '20.5'.
Note that the bug is triggered only if the argument of STR$ is a non-zero
number strictly between -1 and 1.
GO TO and large numbers
Since BASIC line numbers cannot be greater than 9999, you might expect a 'GO TO
n' command where n>9999 to either do nothing or give an
appropriate error message. But GO TO does not behave consistently when n
is large.
The command 'GO TO n' always (appropriately) does nothing when
n<32768, so long as there is no line number greater than or equal to
n and there are no variables present.
But if there are variables present, the command 'GO TO n' can give a 'C
Nonsense in BASIC' error when 9999<n<=32767; for example:
LET a=1: GO TO 24832
gives the error 'C Nonsense in BASIC, H832:1'. This happens because the routine
at LINE_ADDR (called from LINE_NEW) finds the definition of the variable 'a' in
the variables area, the first two bytes of which are 97 (the ASCII code for
'a') and 0, and takes it to be line number 24832 (97*256).
The command 'GO TO n' gives an 'N Statement lost, 0:255' error when
32768<=n<=61439. (See the check at 7043: a line number greater
than 32767 indicates the editing area.)
The command 'GO TO n' (appropriately) gives a 'B Integer out of range'
error when n>=61440. (See the check at 7790.)
Don't close the streams
It can be dangerous to close a stream, especially if it's aleady been closed or
was never opened. For example, on a freshly booted Spectrum:
CLOSE #4
makes the computer hang.
The root cause is that there is no end marker in the close stream
lookup table. This makes the 'JP (HL)' at the end of the routine at
CLOSE_2 jump to 5979, and the 'RET' that follows at 5980 causes an
indirect jump to 23582 (an address pushed onto the stack earlier at
CLOSE_2). After proceeding through the RAM at 23582 onwards, and
disabling interrupts along the way, the Spectrum finally hangs on the
'HALT' instruction at MAIN_4.
If there were an end marker (0) in the close stream lookup table, the
'JP (HL)' at the end of the routine at CLOSE_2 would jump to CLOSE_STR, the
'RET' that follows at 5917 would return to 5867, and the stream would
be closed successfully.
Press (almost) any key
Not every key besides 'n', 'N', BREAK and STOP will work at the 'scroll?' and
'Start tape then press any key.' prompts: pressing CAPS LOCK, or GRAPHICS, or
CAPS SHIFT and SYMBOL SHIFT together at these prompts causes the previous edit
line to appear at the bottom of the screen instead.
This is because the routine at KEY_INPUT, which is used (via WAIT_KEY) to decode a
keypress, copies the edit line to the lower part of the screen when the mode
changes (from 'L').
The scrolling tokens
When the program
10 PRINT "a": GO TO 10
is RUN, pressing CAPS LOCK (or GRAPHICS, or CAPS SHIFT and SYMBOL SHIFT
together) at the 'scroll?' prompt places 'RUN' in the edit area, and then
pressing ENTER fills the screen with BASIC tokens. Pressing ENTER at the next
'scroll?' prompt leads to more BASIC tokens and other characters being printed,
and then the program stops with a 'K Invalid colour' error.
This happens because the DE register pair - which is used by the section of
code at PR_STRING to hold the address of the next character to be printed - is
corrupted during the call to PRINT_A_1 (made by the 'RST 16' instruction at
8258) and ends up pointing at 184 after CAPS LOCK and ENTER are pressed
at the 'scroll?' prompt. DE is then incremented through the token
table until it reaches 500 and the 'scroll?' prompt is printed again.
After ENTER is pressed at this prompt, DE is incremented through the
remainder of the token table, and then through the key tables, and
then into the routine at KEY_SCAN, until it reaches 656, where it finds the
control character 17 (PAPER) followed by 255 (the first two bytes of the
'LD DE,65535' instruction at 656), which produces the 'K Invalid
colour' error.
This bug would be prevented if DE were saved before the call to WAIT_KEY at
3251, and restored afterwards.
The impatient 'scroll?' prompt
When the program
10 FOR n=1 TO 100: PRINT n: NEXT n
is RUN, pressing TRUE VIDEO or INV. VIDEO at the 'scroll?' prompt causes the
screen to scroll once, prints a 'scroll?' prompt, scrolls the screen again
without waiting for a keypress, and then prints another 'scroll?' prompt
indented by 7 spaces.
This happens because when TRUE VIDEO or INV. VIDEO is pressed at the initial
'scroll?' prompt, the routine at KEY_INPUT interprets the keypress as an INVERSE
control code (4 or 5) and changes the input address for the current channel
from KEY_INPUT to KEY_NEXT (see KEY_DATA). This means that after the next 'scroll?'
prompt is printed and WAIT_KEY is called, KEY_NEXT (which picks up the parameter
for the INVERSE control code instead of waiting for a keypress, and does not
reset the print position for the lower part of the display in preparation for
the next 'scroll?' prompt) is executed instead of KEY_INPUT (which would wait
for a keypress, and then reset the print position for the lower part of the
display).
Anything equals SCREEN$ (x,y)
Some care is needed when using SCREEN$, because it doesn't always work as you
would expect. For example:
IF ""=SCREEN$ (0,0) THEN PRINT "?"
will print "?" no matter what is on the screen at (0,0) at the time.
The reason is that the routine at S_SCRN_S exits via STK_STO, which results in
the character found at (0,0) being stored on the calculator stack twice instead
of just once. So when the '=' operation above is performed, the last two items
on the stack - the two copies of SCREEN$ (0,0) - are equal, and the result of
the operation is always true, regardless of what's on the left hand side of the
equation.
Note that the calculator stack is cleared before each statement of a BASIC
program (or the edit line) is executed, so the bug is confined to the statement
that contains SCREEN$, and will not affect any other statements.
The 34th bit of division
In the division loop at DIV_LOOP, the 34th bit of the quotient is always set to
0, meaning that some results - such as 1/10 and 1/1000 - are not rounded up as
they should be. For example:
PRINT 1/2-.5
prints '2.3283064E-10' instead of '0', because while '1/2' is calculated
correctly, '.5' is evaluated as '5 * 1/10', which is not calculated correctly.
Specifically, '1/10' is represented as 7D 4C CC CC CC (0.0999999999767) in
floating point form, but if the 34th bit were included, it would be rounded up
to 7D 4C CC CC CD (0.100000000006), which is more accurate.
The problem is caused by the jump at 12799, which should be to DIV_34TH
instead of DIV_START.
The trouble with -65536
In some circumstances, the number -65536 is stored in short form instead of
full floating point form, which causes problems. For example:
PRINT -65535-1
prints '-1E-38', and:
PRINT INT -65536
prints '-1'.
The first of these problems is caused by the routine at addition, which fails to
detect an overflow when adding two small negative integers whose sum is -65536.
Specifically, when 12331 is reached, the carry flag is set, A holds 255
(the sign byte of the second number), and (HL) also holds 255 (the sign
byte of the first number). Then:
The result is then stored as 00 FF 00 00 00, which other ROM routines do
not always handle correctly.
The second problem is caused by the routine at truncate. Specifically, the
section of code at 12837 checks whether the argument x satisfies
-65537<x<=-65536; if it does, its floating point form is
(erroneously) modified to 00 FF 00 00 00. (-65537 in floating point form is
91 80 00 80 00, which explains why the mask 128 is used when testing the fourth
byte of x at 12848.)