Languages and translators
High and low level, machine code and assembly; compilers, interpreters and assemblers.
Covers OCR J277 2.5.1 Languages · AQA 8525 3.4.4 Classification of programming languages and translators · Edexcel 1CP2 3.3.1 Low-level and high-level languages · Edexcel 1CP2 3.3.2 Interpreters and compilers
Do this lesson in the simulatorPrint it: worksheet (PDF) · answers (PDF) · as a web page
BugBot's processor cannot read Python. Nor can the one in your laptop. A processor understands only its own machine code: patterns of binary that mean things like "add these two numbers" or "jump to that instruction". Every program in every other language has to be translated into machine code before it can run. This lesson is about the kinds of language, and the programs that translate between them.
High level and low level
| High-level languages | Low-level languages | |
|---|---|---|
| Examples | Python, Java, C#, Scratch | machine code, assembly language |
| Looks like | English words and maths: if distance() < 30: |
binary, or short codes: LOAD 4, JNZ 1 |
| One statement does | many processor instructions | one processor instruction |
| Runs on | any processor, once translated | one family of processors |
| Good for | writing programs quickly and readably | total control of speed, memory and hardware |
Most programs are written in high-level languages, because they are faster to write, easier to read and to fix, and not tied to one kind of processor. Low-level code is used where every byte and microsecond counts: in device drivers, in parts of operating systems, and in small pieces of firmware, such as the code that starts a processor when it powers on.
Assembly language
Machine code is unreadable binary, so each instruction is given a short name instead, called a mnemonic. That is assembly language: one line for each processor instruction. Here is a made-up assembly language for the robot, with a register, a single memory slot called R:
| Instruction | Meaning |
|---|---|
LOAD n |
put the number n in R |
FWD n |
drive forward n cm |
TURN n |
turn n degrees |
BEEP n |
play a note of n hertz |
DEC |
take 1 away from R |
JNZ a |
if R is not zero, jump to instruction number a |
HALT |
stop |
There is no for and no while. A loop is made with a counter and a jump back:
0 LOAD 4
1 FWD 20
2 TURN 90
3 BEEP 660
4 DEC
5 JNZ 1
6 HALT
This drives a square: R starts at 4, and each time round DEC takes one off and JNZ 1 jumps back to instruction 1, until R reaches zero.
Translators
A translator turns a program into machine code. There are three kinds:
- An assembler translates assembly language into machine code, one instruction for one instruction.
- A compiler translates a whole high-level program into machine code in one go, before it runs. The result is a file that runs on its own, fast, without the compiler. Errors are all reported at the end of compiling.
- An interpreter translates and runs a high-level program one statement at a time. There is no separate file: the interpreter is needed every time the program runs. It stops at the first error it meets, which makes it good for developing and testing.
| Compiler | Interpreter | |
|---|---|---|
| Translates | the whole program, before running | one statement at a time, while running |
| Produces | a standalone machine code file | no file |
| Speed when running | fast | slower |
| Errors | all reported after translation | the program stops at the first one |
| Source code needed to run | no | yes, and the interpreter |
Which does BugBot use?
Both, and more. The Python you write in these lessons runs in an interpreter: CPython, itself compiled to run inside your web browser. On the real robot, a small Python interpreter called pocketpy runs your program, while the motor and camera code underneath is written in C and compiled to machine code, because it has to be fast.
Python actually does a bit of both. Before running, it compiles your program into bytecode, simple instructions for its own virtual processor, then interprets the bytecode. You can see it:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
import dis
dis.dis(compile("speed = 20 + boost * 2", "sum", "exec"))
Each line is one bytecode instruction. RESUME at the top and RETURN_CONST at the bottom start and finish every compiled block; between them are the real work: load 20, load boost, load 2, multiply, add, store in speed. One line of Python became six low-level steps.
An interpreter of your own
An interpreter is just a program that reads instructions and carries them out. Here is the start of one for the robot's assembly language:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
program = ["FWD 20", "TURN 90", "BEEP 660", "FWD 20", "HALT"]
pc = 0 # program counter: which instruction is next
while True:
op, *args = program[pc].split()
print(pc, op, args)
if op == "FWD":
forward(60, distance=int(args[0]))
elif op == "TURN":
turn_right(30, angle=int(args[0]))
elif op == "BEEP":
tone(int(args[0]), 0.2)
elif op == "HALT":
break
pc = pc + 1
The program counter pc holds the number of the next instruction, the same job a real processor's program counter does. Each time round, the interpreter fetches an instruction, works out what it means, and does it. That fetch, decode, execute loop is exactly what a processor does with machine code, which is module F8.
Task: robot assembly
Finish the interpreter so it runs the robot assembly program below, which uses LOAD, DEC and JNZ to drive a square with a beep at each corner. Store the value of R in a variable, and make JNZ change the program counter.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
program = ["LOAD 4", "FWD 20", "TURN 90", "BEEP 660", "DEC", "JNZ 1", "HALT"]
pc = 0
while True:
op, *args = program[pc].split()
if op == "FWD":
forward(60, distance=int(args[0]))
elif op == "TURN":
turn_right(30, angle=int(args[0]))
elif op == "HALT":
break
pc = pc + 1
Challenges
- Add a
WAIT ninstruction, and write an assembly program that flashes a beep three times. - Add
JMP a, an unconditional jump. What happens if a program jumps back with no way to stop? - Write your own compiler of sorts: a function that turns the assembly program into a Python program as text, and print the text.