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.
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
5889). 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').
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 0x80 is used when testing the
fourth byte of x at 12848.)