Basil’s Compendium: “Painless” MC Development

“Painless” MC Development

This chapter will give you a look into “real life”, and will show you how to make a “rough draft” of your programs in BASIC.

I always find it easier to debug my programs in BASIC before actually translating them into machine code, as the results of an error are much less drastic in BASIC. However, I try to make my BASIC program look as much like the final machine code as I can, in order to minimize errors in translation. This results in a far-from-efficient BASIC program, of course. You’d have much the same situation if you tried to write a sentence in English so that you could translate it word for word into correct Spanish. Your final product could look very much like: “Themselves to me they lost my pencils”.

But even before the rough draft, you’ll have to have an idea of where you are going. Here you’ll find the table called “The System Variables” very useful. This table begins on page 134 in the ZX81 manual, and on page 262 for the TS 2068.

So What To Do?

Let’s start out with that famous line renumbering program. For this application, you should remember the following facts:

1. The first byte of the program is at 16509 for the ZX81. For the TS2068, the location of the first byte is given by

PEEK 23635 + 256 * PEEK 23636

which is 26710, unless you have POKEd 23635 or 23636.

2. The end of each line is marked by 118 in the ZX81, by 13 in the 2068.

3. Each line invariably starts with a sequence of the following four bytes:

a. Two bytes giving the line number in 256-imal, MOST significant byte first.

b. Two bytes giving the line length in 256-imal, LEAST significant byte first. This length is the number of all bytes FOLLOWING the line-length bytes, INCLUDING the 118 (or 13) at the end of the line. It does not, of course, include the bytes marking the line number nor does it include the line-length bytes.

To illustrate, write the very short program

10 LET BC=10

and PEEK the first 17 bytes. You will see:

     0          0
    10         10
    13         13
     0          0
     .          .
(. .12 more bytes. .)
     .          .
    13        118

for the 2068 and the ZX81, respectively. The two digits 13 and 0, in 256-imal (least significant byte first), give the number of bytes remaining in the line, INCLUDING the line-end indicator 13 or 118. The two bytes 0 and 10 give the line number, MOST significant byte first.

Now to put our knowledge to work. Remember that this program will foul up all GO TOs and GO SUBs, unless you carefully design the program (as we will) to avoid that problem.

First, you must decide which register to use for each variable that you have to work with. The most significant factor affecting your choice will be that you can do things with HL (a 2-byte register) and A (one byte) that you can’t with the others. They are the true workhorses of the computer. The next consideration is that instruction 235 provides a one-byte provision for exchanging DE and HL. This makes DE the best choice for an alternate workhorse. BC gets whatever is left.

Now, what we’re going to do in the line-renumbering program is to look at each byte in succession. When we reach an end-of-line, we pay special attention to the next two bytes (the line number), POKEing in the number that we have planned for that location. We have to include some provision for stopping the program when we’re finished. And we mustn’t forget the RETURN to BASIC when we finally get around to writing the machine code routine. We’ll leave the first program line number unchanged, at 1. This might very well be the place where we store the machine code in the final version.

Apparently, we’re going to have to search through the memory, byte by byte. HL seems a logical choice for this function. Then we’re going to have to keep a record of what the next line number must be. A logical use for DE. BC, then, can remember the interval between line numbers–a value that will not change. And A will be useful to set the target, as it were, for each phase of our search. Register A is a particularly happy choice for this purpose, because of the existence of the CP instruction.

For the ZX81

Let’s take the ZX81 case first. Start with the following program:

  1 REM
10 LET HL=16509
20 LET BC=10
note: interval between line numbers
30 LET DE=10
note: number of first line
40 LET A=117+1
note: remember that 118 is a "no-no"
50 LET HL=HL+1
60 LET CP=A-PEEK HL
70 IF CP<>0 THEN GOTO 50
80 PRINT 256*PEEK (HL+1)+PEEK
(HL+2)

Now RUN this much of the program. The number PRINTed should be 10. Insert (temporarily)

  2 REM

and RUN again. The result should be 2. DELETE line 2. Now add

 90 LET A=35
100 LET HL=HL+1
110 LET CP=A-PEEK HL
120 IF CP<=0 THEN STOP
note: will be RETurn in M/C routine
note: this will stop the program as soon as
the line number under consideration exceeds 8960
(=35*256)
210 GOTO 40

Now RUNning the program will give a list of all line numbers from 2 to the first one over 8960. Experiment by inserting other REM lines, and RUNning again. Be careful, however, that you DELETE all the extra lines, and restore the program to its present form before going on to the next step. This is so that the GOTOs will still work after the renumbering process is carried out.

Now continue:

130 LET D=INT (DE/256)
140 LET E=DE-256*D
150 POKE HL,D
160 LET HL=HL+1
170 POKE HL,E
171 STOP

Now another test. Change line number 10 to 2: then RUN. If you have followed instructions correctly, the line number will be changed back to 10 again. Now we’re coming down the homestretch. DELETE line 171, and add lines 9000 to 9030 to provide a subroutine that simulates the function that exchanges DE and HL:

9000 LET TEMP=HL
9010 LET HL=DE
9020 LET DE=TEMP
9030 RETURN

and

 180 GOSUB 9000
190 LET HL=HL+BC
200 GOSUB 9000

And line 210, of course, still reads GOTO 40.

For a final test, add a few REM lines betwen 211 and 8960. You may change line numbers lower than 210, if you wish, but make sure that the GOTOs and GOSUBs will be valid BOTH BEFORE AND AFTER the renumbering. Any numbers above 8960 (=256*35) will be unchanged. And, of course, you can change this limit to any multiple of 256 that you wish. You can also set it to other limits, but the procedure takes a couple more bytes of machine language program.

For convenience, I’d suggest that you change line 80 to a REM (but don’t DELETE it, lest you mess up the GOTO addresses). If you’d prefer to keep it, then you’ll have to CONTINUE after the screen fills up with line numbers.

For the TS2068

The 20608 procedure is a little bit more scary, because, as you have seen, you can run into 13’s that aren’t end-of-line indicators. (Theoretically, you could have this problem with the ZX81, but only if you run into a line long enough so that the line-length indicator contains a 118). With that one warning, the 2068 procedure follows very closely that of the ZX81, like this:

  1 REM
10 LET hl=PEEK 23635+256*PEEK
23636
20 LET bc=10
30 LET de=10
40 LET a=13
50 LET hl=hl+1
60 LET cp=a-PEEK hl
70 IF cp<>0 THEN GO TO 50
80 PRINT 256*PEEK (hl+1)+PEEK
(hl+2)
210 GO TO 40

When you RUN this program, you get all of the line numbers, but there are also spurious responses after 20, 30, and 40. The first result from the fact that lines 20 and 30 each have indicated line length of 13 bytes; line 40 gives trouble because the 13 is explicitly expressed. The search through all the hl’s reads these 13’s as if they were ends of lines.

This problem can be corrected by changing lines 20 through 40 as follows:

 20 LET bc=9+1
30 LET DE=9+1
40 LET a=12+1

Now fill in the rest of lines 90 through 200 as above.

The translation to machine code should be very straightforward. Just remember that the POKE and PEEK functions are represented by parentheses, like

LD (HL),N        LD hl,(NN)

and the like.

An Unorthodox Application

And now, what do you do with such a routine? Well, of course, you could use it to clean up your programs (taking due care with GO TO and GO SUB addresses). But here is another application that might not have occurred to you:

Suppose you are preparing an index–say, of composers. You may want a simple LISTing, in which case you just put the names in REM lines, or you may want it to provide printout, in which case you use the command PRINT. You start out confidently like this:

10 REM BACH
20 REM BEETHOVEN
30 REM BRAHMS

BERLIOZ can go in as line 25: BORODIN as 27. BERLIN would take line 22. Maybe BASIL in line 15. Pretty soon, your list looks like this:

 2 REM BACH, CPE
5 REM BACH, JC
10 REM BACH, JS
15 REM BASIL
20 REM BEETHOVEN
22 REM BERLIN
25 REM BERLIOZ
26 REM BIZET
27 REM BORODIN
30 REM BRAHMS

But HEY! Where are you going to put Bordogni? Or Seth Bingham? And what to do with the rest of that huge Bach tribe?

Easy. Just renumber, and the above set will look like this:

 10 REM BACH, CPE
20 REM BACH, JC
30 REM BACH, JS
40 REM BASIL
50 REM BEETHOVEN
60 REM BERLIN
70 REM BERLIOZ
80 REM BIZET
90 REM BORODIN
100 REM BRAHMS

Plenty of room for insertions, even when a Billings or a Birmingham turns up.

That’s all for now. There will be more.