More and more programs are appearing in SYNC. under the rubric of “machine language.” There was a time, not too long ago, when personal computer users were notoriously shy about venturing into these jungles. Nowadays, though, even someone who is not quite comfortable in Basic can manage to key in and run programs in Z80 machine code.
Unfortunately, there are still a few bugs in the system. The Sinclair machines are really designed for Basic, not for special machine code routines. True, there are commands—PEEK, POKE, and USR—that are handy for putting these fast, specialized routines wherever the programmer desires.
Unfortunately, there is nowhere to put them.
“This routine is held in a REM statement that must be kept off-screen. Don’t try to LIST the program,” says one author. Says another, “I’ve included extra code to prevent the system from bombing if you try to LIST it.”
Some programmers like to put their machine code routines in the space reserved for variables—but, as one writer points out, “I haven’t yet figured out how to keep from losing the routine when you RUN the program.”
What’s the problem, anyway? Why can’t these programmers find a safe, sensible place to put their clever little special routines?
Why use Z80 machine code, anyway? Because it is very compact and very fast. The equivalent of
LET A=A+1
in machine code takes just one byte, and the Z80 can execute that command in a little more than a millionth of a second. For speed, Basic cannot come close.
Basic is a “high level” language. A program in Basic reads a lot like English, and it can be moved rather easily from one computer to another. But computers do not really understand Basic. To the ZX80, the line
10 LET A=A+1
is just a string of numbers:
0 10 248 38 227 38 221 29 118.
The only thing the ZX80’s microprocessor really understands is its own language. The 4K or 8K Basic ROM contains a program that enables the computer to figure out what to do for each Basic command. (That program, of course, is written in machine language.)
In other words, Basic takes the computer extra work—and extra time.
Machine code programs speak the computer’s own language. They are strings of numbers, too—but the processor can understand each number on its own, without a special translator or interpreter program. Unlike Sinclair Basic, Z80 machine language uses all of the numbers it can, from 0 to 255—every different value a byte of memory can hold.
That is where the problems begin.
The ZX80 is a very small system. It is cleverly designed, and for the small number of components on its printed circuit board, it is amazing for what it does.
But it all depends on several assumptions. For example, when it is time to refresh the TV screen, the ZX80 zips through display memory at incredible speed to throw the proper information into the proper order fast enough to keep up with the TV’s electron gun.
To work that fast, the ZX80 was designed so that only regular or reversed characters would be stored in the display section of memory. (Remember, regular characters are represented by numbers from 0 to 63, and reversed characters run from 128 to 191.) The one exception is the NEWLINE code, which is 118.
What happens when a number between 64 and 127, or higher than 192, is in display memory? It is easy enough to see. Enter this line:
10 REM THIS IS HOW IT LOOKS
Now POKE one of the “forbidden” numbers into 16440, and watch what happens.
Not all of the numbers have the same effect. Sometimes the display rolls; sometimes it is warped; sometimes it just gets a bit jittery. Almost always, given enough time, the whole system will crash.
That is why some authors tell you not to LIST the REM line that contains a machine code routine. If the routine contains any of the “forbidden” numbers—half of all the available instructions—you will probably crash the system and have to start over from the beginning.
There are other assumptions built into the ZX80 system, too. For example, when you RUN a program, the ZX80 assumes that you will want all the variables cleared out, and that you will want to have as much space as possible for your Basic program and display. That means memory is used very efficiently in the ZX80.
It also means that most machine language programs cannot be safely stored in REM lines, strings, or the like. It is tough to find a safe place to put a machine code routine.
How can it be done? Listing 1 demonstrates one way. This method takes up program space, but it has three very large advantages. First, it puts the routine in every time you run the program. That means you do not have to remember to replace RUN with GOTO.
Second, you do not have to remember a five-digit number to use the routine. Instead of something like USR(17694), simply use USR (Z).
Finally, you can check, edit, or LIST the routine any time you like, in hexadecimal notation—the form in which most machine code programs are written. It is easier to read, easier to change, and much less prone to mistakes.
Enter and run the program in Listing 1. The simple machine code routine in the REM line calculates the amount of free memory left in a ZX80 system. It works for any size of RAM, but it is designed for 4K Basic, not the 8K ROM.
Listing 1.
1 REM 21.00.00.39.ED.5B.10.40.ED.52.C9
2 DIM Z(5)
3 LET Z=USR(48)
4 FOR X=1 TO 11
5 LET Z=Z-1
6 POKE Z, PEEK(16428+3*11-3*X)-28+16*(PEEK(16427+3*11-3*X)-28)
7 NEXT X
10 PRINT "FREE MEMORY=";USR(Z);" BYTES"
Listing 2.
21 00 00 LD HL, 0000
39 ADD HL, SP get stack pointer
ED 5B 10 40 LD DE, (4010) get end of display
ED 52 SBC HL, DE subtract
C9 RET
To put the machine code routine into memory properly, line 2 sets up an array, and line 3 finds the end of the array. USR(48) always returns the location of the end of the storage space for Basic variables. Then the FOR…NEXT loop takes each pair of characters in the REM line, converts it to a single value betwen 0 and 255, and POKEs it into a byte in the array. When the loop is finished, Z points to the start of the routine in memory.
Line 10 simply demonstrates the routine.
Now add these lines and run the program again:
20 FOR X=0 TO 5
30 PRINT "Z(";X;")=",Z(X)
40 NEXT X
What you get should look something like Figure A.
Figure A.
FREE MEMORY=728 BYTES Z(0)= 8448 Z(1)= 0 Z(2)= -4807 Z(3)= 4187 Z(4)= -4800 Z(5)= -13998
It is the machine code routine again—this time as it is stored in the Z array.
Listing 2 shows the routine in the traditional “assembly language” notation.
Many of the machine-language routines printed in SYNC can be adapted directly to this method. Simply copy the two-digit hexadecimal numbers into the REM line and adjust lines 2, 5, and 6.
In line 2, DIM the array for half the number of bytes in the routine. For example, a routine 77 bytes long should use DIM Z(38).
Then line 5 would be
FOR X=1 TO 77.
And line 6:
POKE Z, PEEK(16428+3*77-3*X)-28+16*(PEEK(16427+3*77-3*X)-28).
Line 6 is in expanded form here simply to make it clear that 77 must be inserted in the line twice—once for each of the PEEKs. It could be simplified to:
6 POKE Z, PEEK(16659-3*X)+16*PEEK(16658-3*X)-476
and work just as well.
There are a few things to be careful about.
Be sure to put the periods into the REM line. You can replace them with commas or spaces if you like, but be sure to put something there: they equally space the bytes, and allow the program to put the machine code routine together properly.
If you have a longer routine and want to save space, you can leave out the periods—but you will have to change line 6. In the 77-byte example, it would now read:
6 POKE Z, PEEK(16428+2*77-2*X)-28+16*(PEEK(16427+2*77-2*X)-28)
Notice that a “3” has been changed to a “2” in four places—twice for each PEEK.
Be sure to put this routine at the beginning of your program—before you use any of the variables. If you like, you can choose a different variable and array. But do not use the array or variable for anything else in your program, or you may crash the system.
Finally, if you are not sure whether a particular machine language routine will work with this system, the easiest way is to try it! Instead of the tedious system of keying in two- and three-digit numbers, where one slip could be fatal, simply SAVE the program and change the REM line to install the new routine.
It is an easy way to experiment with machine language programs—and to get more out of your ZX80.