Saving grace
FRANK DART shows how to rescue your latest masterpiece when
the tape refuses to load correctly
"THE difficulty experienced in loading a program from tape
is inversely proportional to the number of copies of the program
you have made".
Even with a sound understanding of the stated law, there are
occasions when it is only possible to save your latest masterpiece
once. It may be a long program and you are short of either time
or tape!
Whatever the reason, the fact that you have only one copy puts
you at risk of losing the whole program.
If just one block refuses to load correctly, pressing Escape
is likely to prompt a rather curt, and singularly unsympathetic,
"Bad Program" in reply.
A student who had been typing for two hours on a previous day
suffered this precise fate. He was, quite correctly, convinced
that the part that had been loaded was in there somewhere.
The problem was that the machine would not let him at it.
Fortunately, his lost block being near the end, it was possible
to retrieve the situation so that he could LIST the complete lines
which had been loaded.
He had then to retype only the last few lines which had been
lost.
To follow how this can be done it is necessary to understand
how a Basic program is stored in memory. The best way to do this
is to examine the appropriate area of memory using the operator.
First, set up a function key to show the contents of locations
PAGE, where your Basic program is stored, up to PAGE+20.
For example:
*KEY9 FORI=PAGE TO PAGE+20 :PRINTI,?I :NEXT¦M
Then type NEW, press Return, and enter the simple program which
we shall then examine:
10REM
20REMARK
Note that there are no spaces.
Pressing key f9 will now display Figure I.
|
ADDRESS
|
CONTENTS
|
COMMENTS
|
|
3584
|
13
|
START OF BASIC LINE
|
|
3585
|
0
|
LINE NUMBER,HIGH
|
|
3586
|
10
|
LINE NUMBER,LOW
|
|
3587
|
5
|
LENGTH OF LINE
|
|
3588
|
244
|
TOKEN FOR REM
|
|
3589
|
13
|
START OF BASIC LINE
|
|
3590
|
0
|
LINE NUMBER,HIGH
|
|
3591
|
20
|
LINE NUMBER,LOW
|
|
3592
|
8
|
LENGTH OF LINE
|
|
3593
|
244
|
TOKEN FOR REM
|
|
3594
|
65
|
ASCII A
|
|
3595
|
82
|
ABCII R
|
|
3596
|
75
|
ASCII K
|
|
3597
|
13
|
START OF BASIC LINE
|
|
3598
|
255
|
LINE NUMBER,HIGH
|
|
3599
|
?
|
LINE NUMBER,LOW
|
|
3600
|
0
|
does not matter
|
|
3601
|
0
|
does not matter
|
|
3602
|
141
|
does not matter
|
|
3603
|
72
|
does not matter
|
|
3604
|
160
|
does not matter
|
Figure I: Basic program storage
On machines with the Acorn Disc Filing System installed the
addresses win run from 6400 to 6420. These figures will vary with
other DFS.
Basic is stored in a compressed fashion using "tokens"
for the keywords - that is, one byte is used as a code for the
keyword. The tokens are listed on page 483 of the User Guide.
Of greater interest at the moment are the four extra bytes on
each line. In line 10 these are 13, 0, 10 and 5. In line 20 they
are 13, 0, 20 and 8.
Each line of the Basic program can be distinguished by the 13s
which can be thought of as start of line markers.
The two numbers which follow each 13 are a coded version of
the Basic line number.
The first (LINE NUMBER,HIGH) is "how many 256s in the line
number". The second (LINE NUMBER,LOW) is "how many leftover".
So line 10 is recorded as:
0 256s
10 left over
Line 300 would be:
1 256s
44 left over
and line 1000 would be:
3 256s
232 left over.
The third number after each 13 is of particular importance.
This gives the length of the stored version of each line.
A consequence of the way that this value is worked out is that
the "line length" byte gives the address of the next
13 when added to the address of the previous 13.
In our example the first line, 10REM, is stored from location
3584 and upwards. Since the line length of this line is 5, the
next line is stored from location 3584+5=3589 and upwards.
One advantage of this method is that the Basic interpreter can
quickly scan through the program, looking for a particular line
number without having to read all the text.
Let's refer to the line length bytes as "links", since
they link one line to the next.
A correctly linked program is one in which you can keep adding
the link to the address of the previous 13 to get the address
at which the next line starts. This should contain 13.
If it does not, then something has gone wrong and it is a "Bad
Program".
A lost block would, at some point, give a link to an address
which had not been loaded with the required 13, hence "Bad
Program".
To retrieve what has been loaded correctly it is necessary to
make the interpreter think it has reached the last line of the
program before it reaches the bad link.
Fortunately this is relatively simple. Looking at the example
once more you will see that the last line links correctly to a
13 (3589+8=3597) which should mark the start of the third line.
"But", I hear you say, "there is no third line".
Well, this is the way the end of the program is stored — the
physical end that is, whether or not it be an END statement.
There is a 13 just as if it is another line, but the LINE NUMBER,HIGH
is a rogue value. A 255 in this position would mean a line number
of at least 255*256=65280.
Since the highest you can use is 32767 this particular value
signals the end of the program.
All we need to do to stop the "Bad Program" error
is to put 255 into the first of the two line number bytes before
the bad link is reached.
This will fool the interpreter into believing it has reached
the end and it will therefore look no further. The bad link will
not be seen.
The secret is to put the 255 in the correct position to maximise
the number of lines retrieved.
The program below does just this:
10 P%=3584
20 REPEAT:PRINT256*(P%?1)+P%?2
30 L%=P%?3:P%=P%+L%
40 UNTIL?P%<>13:PRINT"FAILS HERE"
50 P%=P%-L%:P%?1=255
60 PAGE=3584
This may be typed in when needed, or can be loaded from tape.
In either case it is vital that it is located in some free area
of memory so that it does not overwrite the program that is being
retrieved.
A convenient way of doing this would be to ensure you were in
Mode 7. This gives the maximum room to play with. Since HIMEM
is &7C00 and the retrieve program is less than one block long,
it can be safely placed at &7B00.
The way it works is to use a pointer, P%, to point at each successive
13 starting at PAGE. The line number is calculated from the coded
form and printed onto the screen.
The link, L%, is picked up from location P%+3 and is used to
calculate the address at which the next line should start.
So long as this address contains a 13, the process continues.
When a 13 is not found in the calculated position - in other
words, the link is broken - L% is taken off the pointer to go
back to the previous 13.
P% is now pointing at a line which does not link. The 255 is
then put in at P%+1 to terminate the stored version correctly.
Finally PAGE is set back to its original value so that typing
LIST will display what has been recovered. This can now be edited
or added to in the normal way.
Try it out using the following procedure:
1. Load a fairly long Basic program but press Escape before
it has finished loading. You should get the "Bad Program"
error message. Having thus simulated the problem of the lost block:
2. Type:
MODE 7 Return
PAGE=&7B00 Return
NEW Return
The use of &7B00 for PAGE is convenient, being safe for
both cassette and disc machines.
NEW will not lose your previous program as PAGE has been reset
so that the retrieve program will be stored well out of the way.
3. Type in, or load, the retrieve program.
4. If using an Acorn DFS change the number 3584 to 6400 in both
lines 10 and 60. For other DFS change this number to the appropriate
page number.
5. RUN the program. You should see the retrieved line numbers
scrolling the screen.
6. Type LIST to display what has been recovered.
Should you need to use this utility in earnest then you should
perform steps 2 to 6 only.
You may never need this program but, being so short, it is worth
typing in and saving just in case you do.
Make a note of the instructions (2 to 6 above) and copy the
listing on a piece of paper. Store this in the cassette box with
the program on cassette. The copy of the listing is just in case
the program won't load.
But isn't this where we came in?