Revovery Utility

'Bad Program' messages? Why not use our invaluable program recovery routine?

Volume 2

Number 1

March 1984

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?