The ZX81 Parser and User-Defined Commands
In this issue we begin a series of articles which will describe parsing, the ZX81’s parser, and user-defined commands. Parsing is the process used by a computer system to extract the syntactic and semantic elements of a user’s program. These elements, once isolated, are either acted upon directly or stored in a data structure (or two) for later examination. In the ZX81, and all other computers, it is the parser that is responsible for a) syntax checking, and b) execution of a program. It is one of the most important elements of any system designed to execute a language.
The Parsing Steps
When a parser is called to look at a line or a series of lines, it must perform many steps. An overview of these is as follows:
A) Let IP be a pointer to the first character to be looked-at. The character is the first one in the line to be syntax-checked/executed.
B) Let CCHAR be the character stored at location IP.
C) Process CCHAR according to the current context.
D) Increment IP.
E) If IP does not point to an End-Of-Line (EOL) character, go back to step B.
Let us now take a look at these steps. Before entry into the “SCANNING-LOOP,” which ranges from steps B to E, inclusive, the parser sets up a pointer to the first character to be looked-at. This pointer, called the Interpreter Pointer, is given the label IP. Step B fetches the next character to be looked-at. The next step, C, is the most important one and will be discussed further in the next issue of SYNC.
Step D increments the IP. In most systems line elements are stored in contiguous memory locations, so the action performed in step D is actually an increment, i.e., IP=IP+1. To state the operation more correctly, we should say: D) Point IP to the next character to be interpreted.
Step E is very straight forward. It is, conceptually, a test to see if we have looked through the whole line yet. It is implemented by a compare or a series of compares with the set of legal line-end markers.
The RST 18 and RST 20 Instructions
The ZX81’s parser follows this basic outline, but it is structured a bit differently. The process used by the ZX81 to “get” characters involves two interconnected routines that are subroutines which can be called by the code responsible for line execution. These routines are labeled “GET-CURRENT-CHARACTER” and “GET-NEXT-CHARACTER.” They are called by way of the RST (ReSTart) instructions: RST 18 and RST 20. The RST instruction is theoretically a CALL instruction, but it has two advantages.
The first is that it is a single byte long. The address of the routine to CALL is implicitly specified in the instruction byte itself, saving the two bytes, required by a CALL, which specify the subroutine address. With an RST, the address of the subroutine is calculated by taking the lowest three bits from the instruction and multiplying them by eight. This yields: RST 0, RST 8, RST 10, RST 18, RST 20 . . . RST 38 (all the addresses are in hex). RSTs are used for the most commonly needed/accessed routines.
The second advantage of using RSTs as opposed to CALLs is that the RST instruction is faster. This stems from the fact that the RST does not have to make two extra memory accesses to find out where to send the processor since this address is specified in the instruction itself.
Listing 1: The RST 18 and RST 20 Routines.
RST 0018 GET-CH: LD HL,(CH-ADD) ; The "GET-CURRENT-CHAR" routine.
LD A,(HL) ; Get the char into A.
001C TEST-SP: AND A ; This instruction will test to see if
; character in A is a space (i.e., 0).
; It will set Z if it is a space.
; It will set NZ if it is not.
RET NZ ; Return if it is not a space.
NOP ; Padding.
NOP ; Padding.
RST 0020 NEXT-CH: CALL CH-ADD+1 ; Increment CH-ADD.
JR TEST-SP ; Go back to 001C.
Listing 2: The CH-ADD+1 Routine.
0049 CHADD+1: LD HL, (CH-ADD) ; the "Bump CH-ADD" routine.
004C CURSOR-SO: INC HL ; bump it.
004D TEMP-PTR: LD (CH-ADD),HL ; STore it.
LD A, (HL) ; Get the char.
CP CURSOR ; Compare it with CURSOR (7Fh).
RET NZ ; If it is not a CURSOR, then return
JR CURSOR-SO ; If it is, then go back, and get
another character to check.
The RST 18 and RST 20 routines are shown in Listing 1. They use an auxiliary routine called CH-ADD+1, shown in Listing 2, to move CH-ADD along to the next character. The operation performed by this routine is basically analogous to the operation performed in step D above. It is also responsible for a secondary task, namely, skipping any CURSOR characters. The CH-ADD pointer is a system variable and is used as the Interpreter Pointer in the 8K ROM.
In reviewing the routines (RST 18 and RST 20), we must note that they simply refuse to return a space character. This is why you can put spaces almost anywhere in a program without having them affect the execution of your program. The only restriction on the space-insertion rule is that all spaces inside quotation marks, i.e., valid string constants, are significant. The system facilitates this by having a separate routine to parse string constants.
These RSTs provide the base of the parser. Next time I will dive head on into the main parser routines.
Bibliography
Two bibliographic references are important for this article:
The ZX81 Monitor Listing, Part A by Ian Logan. Melbourne House, 1981.
Writing Interactive Compilers and Interpreters by P. J. Brown. New York: John Wiley & Sons, 1979.
I wish to express special thanks to Dr. Logan for writing The ZX81 Monitor Listing. The labels used in this article are from this work.
More on the 16K RAM Pack Schematic
I have received many calls and letters from people who want to build the RAM expansion on their own. The biggest problem with this is that some of the components listed in the power supply section are U.K. parts and are not available in the U.S. I have not been able to find cross-references for several, including the ZTX750 transistor. My suggestion to those who want to build the memory expansion is that you use the +5-volt-only version of the 4116 dynamic memory. This way the power converter section, and all the headaches that go along with it, can be eliminated.
As noted in SYNC 2:1, the 16K RAM pack schematic published in SYNC 1:5 needed some corrections and clarifications. A further correction should be made: on each 4116 (ICs 8-15) two pin 7’s are shown. The one going to -5 volts should be shown as pin number 1.
Addenda to “The ZX80/81 Video Display System”
In the discussion of “The ZX80/81 Video Display System” in SYNC 2:1, one further aspect of the Display System (more specifically, the Display File) must be included. As you will note in the column, each of the 24 records in the display file is terminated by a NEWLINE (ENTER) character (76h). This is interpreted as a HALT instruction by the Z80 microprocessor. If a line in the display file is shorter than 32 characters, the system will reach and read the suffix NEWLINE.
Meanwhile, the NOP-Forcing logic has been happily forcing NOPs onto the data bus, overriding whatever happened to be coming from RAM. (Whenever the Z80 is in the middle of a display sequence and D6 from the RAM is low, the Force logic is enabled.) Whenever a character is read from the RAM, whose D6 bit is low, the NOP-Force logic is disabled, and the instruction (byte) read will be allowed to pass through to the Z80 for execution.
In the case of the record-terminating HALT instruction, this is the desired effect. The Z80 will enter a HALT loop (NOPs), not exiting from this loop until the R (Refresh) register times out and interrupts the system. Herein lies the problem with placing some special characters (those with D6 high)—usually machine code—into REM statements. If the character has the “killer-bit” set, it will pass through the NOP-Force logic and enter the Z80 for execution. This can have disasterous results.
Until next time, same relativistic time period, same non-Euclidian universe.