An interpreter is a program that reads another program and carries out its operations. We will follow a simple interpreter that reads a statement, works out which operation it requests, and performs that operation.
The processor executes the interpreter’s machine instructions throughout. If our statement asks for an addition, the interpreter uses its arithmetic routines to calculate the result. It does not have to generate a new piece of machine code for that statement.
What Spectrum BASIC stores
On a 48K Spectrum, the BASIC interpreter is built into ROM, memory containing the machine’s supplied software. Our BASIC program is kept separately in RAM, where it can be edited.
The stored BASIC is already partly prepared. Keywords such as PRINT have
single-byte codes called tokens. A token saves the interpreter from having
to recognise the letters P, R, I, N, T each time. It is still a BASIC
keyword for the interpreter to handle, not a Z80 instruction to print something.
Pressing RUN asks the interpreter to carry out the stored program. It does not
compile that program into a separate sequence of Z80 instructions first. The
processor runs the interpreter, which reads the tokens and other stored values.
Our model below leaves out tokenisation and other preparation so we can follow the repeated work. Real interpreters need not re-read every character or start from scratch on each visit.
Inside a loop
A short loop lets us follow the repeated work. We use teaching notation here:
LET gives a named value its new contents, FOR i = 1 TO 1000 visits each whole
number from 1 through 1000, and END closes its body. SHOW displays the result.
The body adds one to total each time; it does not change i or the bounds.
For more practice, Doing It Again explains the loop. You can trace this example on paper without installing a language.
LET total = 0
FOR i = 1 TO 1000
LET total = total + 1
END
SHOW total1000
Trace the expected result: 1000. The addition statement runs a thousand times.
On every pass, our interpreter must handle that statement: identify the operation,
find the value of total, add one and store the result.
| Pass | Read the statement | Work out what it means | Do it | total |
|---|---|---|---|---|
| 1 | LET total = total + 1 | put total plus one in total | 0 + 1 | 1 |
| 2 | LET total = total + 1 | put total plus one in total | 1 + 1 | 2 |
| 3 | LET total = total + 1 | put total plus one in total | 2 + 1 | 3 |
| 1000 | LET total = total + 1 | put total plus one in total | 999 + 1 | 1000 |
The requested operation stays the same, but its input and result change. Our interpreter handles the statement on each pass. This table omits work such as controlling the loop; it is not a count of processor instructions.
Change the starting total to 10 and the loop's last number to 3. What does SHOW total display, and how many times does the addition statement run?
Show the explanation
It displays 13. The loop visits 1, 2 and 3. The total becomes 11, then 12, then 13: three additions of one. We initialise it once before the loop and show it once after the loop. Starting at 10 changes the total, not the number of visits.
Three visits mean three executions of the addition statement. Have we counted three processor instructions, or measured the interpreter's extra time?
Show the explanation
Neither. A source statement can require many machine instructions. Our table follows the value being updated; it also leaves out loop control and output work. The processor is running the interpreter throughout. We would need to inspect that execution to count its machine instructions, and measure a named workload to investigate its time. This paper trace supplies neither count nor timing.
Count what happened: a thousand additions, plus the interpreter’s repeated work to handle the statement. That extra work is part of this implementation even though we did not write it in our listing.
Why a game notices
A game repeatedly samples input, updates its world and prepares a picture. Its update rate need not match the display refresh. Repeated interpreter work uses some of the time available at the update rate the game needs.
The enemy that decides where to move lives inside that loop. So does the check for whether the player has been hit, and the code that draws every object. Our interpreter model repeats its lookup work as it reaches each operation.
On a small machine that can become a significant part of the budget. Reading and handling the program uses time alongside the calculations and drawing it requests.
Why anyone would build it this way
It gives us a convenient way to experiment. We can enter a command and see its result, or edit a stored line and run again without a separate build step. The interpreter can report many errors with the BASIC line number.
That is exactly the experience the BASIC in these machines gave you, and it is why a whole generation learned to program on it. An interpreter can support both experimentation and finished games. We look for a different approach when a measured requirement calls for it.
When it’s wrong, see why
- The loop is slow and its visible result is small. Interpreter work is one possible cost. Arithmetic, drawing and waiting may matter too; investigate which.
- You made the loop run fewer times and it got faster. Both the requested work and the interpreter work ran fewer times. That alone does not tell us how much time each took.
- You cannot see where the time goes. Our listing does not show the interpreter implementation. Source inspection and measurements can help expose that extra work.
What you’ve learnt
- The processor executes the interpreter, which reads our program as data.
- Spectrum BASIC stores keyword tokens; they are not native Z80 instructions.
- Inside a loop our model repeatedly handles the same statement, as well as performing the operation it requests.
- Game updates and display refresh are distinct; repeated work uses update time.
- Interpreters make experimentation convenient and can run finished games too.
What’s next
In Unit 3 we prepare machine instructions before execution. We compare a native-code compiler with an assembler, then trace a toy assembler of our own.
Sources
Sinclair Research, ZX Spectrum BASIC Programming (1982), chapter 14, “The Character Set”, for keyword tokens, and chapter 24, “The memory”, for ROM instructions and stored BASIC lines.