Skip to content

Translating As You Go

The processor runs the interpreter; the interpreter reads our program. Follow the work it repeats inside a loop.

  • Foundations

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.

Code
LET total = 0
FOR i = 1 TO 1000
  LET total = total + 1
END
SHOW total
Output
1000

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.

PassRead the statementWork out what it meansDo ittotal
1LET total = total + 1put total plus one in total0 + 11
2LET total = total + 1put total plus one in total1 + 12
3LET total = total + 1put total plus one in total2 + 13
1000LET total = total + 1put total plus one in total999 + 11000

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.