Two-Way Translation & Symbolic Addressing
1. Translation Recap — Compiler vs Interpreter
Translation = turning your source code into something the CPU understands (machine code = the language the computer can understand). Two ways:
| Compiler | Interpreter | |
|---|---|---|
| How | translates the whole program first, then it is run | translates + runs it line by line while running |
| Output | complete machine code for that CPU / OS | no separate executable produced |
| Good / bad | fast to run, but only runs where it was compiled | portable, easy to test, but slower |
(recap row facts are the standard textbook ones from System Software; the recording only points back to them.)
2. Partial Compilation + Partial Interpretation
Idea: do half of the translation by compiling, then do the other half by interpreting ⇒ we combine the advantages of compilation and of interpretation.
(“half compiled, half interpreted”)
3. Example — How Java Source Code is Translated
Java is the language that uses the two-way process. Past paper: “Describe how a Java source code program is translated.”
Steps to remember:
- The code you write (source code) first goes into the Java compiler.
- The compiler does only an intermediate translation — not a complete translation into machine code. It leaves byte code.
- The byte code is then interpreted by the Java Virtual Machine (JVM).
Mark-scheme style answer:
- Java uses a two-step translation process / is partially compiled and partially interpreted
- code is first translated into byte code by compilation using the Java compiler
- the byte code is then interpreted by using the Java Virtual Machine
What is byte code? (sir’s link to assembly)
Remember the assembly questions: LDM #97 → you wrote the binary op-code of LDM from the table, then converted 97 to binary in a second box. That pair of boxes is exactly the idea of a byte code: instruction + the data to work on, written in 0s and 1s.
| Assembly | box 1 · op-code | box 2 · data |
|---|---|---|
| LDM #97 | 1100 0001 | 0110 0001 |
(LDM op-code from this paper’s table; 97 = 64 + 32 + 1 = 0110 0001. Used only as the analogy — real Java byte code has its own op-codes.)
The Java compiler makes this half-way (“intermediate”) code; it is not complete machine code, so nothing is run yet.
What is the JVM?
A virtual machine = a software “device” that behaves like a machine. Like running another OS on a normal PC, the JVM lets many different CPUs / PCs each interpret the same byte code. So: byte code → interpreted through the Java Virtual Machine.
4. Why Partially Compile & Partially Interpret?
“Explain why high-level languages might be partially compiled and partially interpreted.”
Answer (2 marks = 2 points):
- ① Partially compiled programs (byte code) can be used on different platforms as they are interpreted when run.
- ② The code is optimised for the CPU as the machine code is generated at run time.
Advantage ① — runs on ANY platform, no re-compiling
- Normal full compilation on Windows ⇒ complete machine code for that CPU/OS only. It will not work on Linux or iOS (it works only where it was compiled).
- Byte code is just numbers / bits (0s and 1s) ⇒ platform-neutral ⇒ can be used on any platform.
- The same Java program runs on Windows, Mac or Linux without re-compiling (no “compile on Windows, then compile again for Linux”). Compile once to byte code and you are done.
Advantage ② — optimised for each CPU
- The byte code is optimised for that particular CPU (whatever OS / hardware is installed) during the interpretation phase.
- The virtual machine can use information about the hardware architecture: number of cores, memory system / RAM, memory consumption, storage — different for every CPU.
- It uses this to generate the most efficient machine code = the code that makes the CPU run fastest.
- So the same byte code executes on different CPUs, but the machine code is generated at run time for that particular CPU ⇒ optimised.
- Partial compilation + partial interpretation = two-way translation process
- Java: source → Java compiler → byte code → JVM interprets
- Benefit 1: byte code runs on any platform, no re-compiling
- Benefit 2: machine code made at run time ⇒ optimised for that CPU
5. Instruction Set & Addressing Modes (Board)
Processor has one general-purpose register — the Accumulator (ACC) — and an Index Register (IX). Part of its instruction set:
| Mnemonic | Operand | Op code (binary) | Explanation |
|---|---|---|---|
| LDM | #n | 1100 0001 | Immediate addressing. Load number n to ACC. |
| LDD | <address> | 1100 0010 | Direct addressing. Load the contents of the given address to ACC. |
| LDV | #n | 1100 0011 | Relative addressing. Move to the address n locations from the address of the current instruction; load the contents of that address to ACC. |
| STO | <address> | 1100 0100 | Store the contents of ACC at the given address. |
| DEC | — | 1100 0101 | Decrement the contents of ACC. |
| OUTCH | — | 1100 0111 | Output the character for the ASCII code in ACC. |
| JNE | <address> | 1110 0110 | After a compare, jump to <address> if the compare was False. |
| JMP | <address> | 1110 0011 | (Unconditionally) jump to the given address. |
| CMP | #n | 1110 0100 | Compare the contents of ACC with number n. |
Absolute = the actual fixed memory address (e.g. 112). | Symbolic = a label / name used instead of a number (e.g. CountDown) — the assembler converts it. | Relative = a position given as a distance (offset) from a reference point — in LDV the reference is the current instruction; in the part-(e) symbol table it is the start of the program (0).
Each instruction and each data line takes one memory location ⇒ one line = one address (add 1 per line).
6. Two-Pass Assembler & Symbol Table
An assembler translates assembly language into machine code. A two-pass assembler reads the program twice because a label can be used before it is defined (e.g. JMP Finish comes before the line Finish:).
Pass 1 — building the symbol table
- ORG directive: tells the assembler to load the first instruction at that address (e.g. ORG #0100 ⇒ first instruction at address 100). With no ORG the program starts at relative address 0.
- Read line by line, counting the address (+1 per line).
- A label on a line (e.g. StartProg:) → enter label + that line’s address.
- A label used in an operand before its line has been reached → enter it with UNKNOWN (address not known yet).
- When the assembler later reaches the line that defines it → cross out UNKNOWN and write the real address.
Pass 2 — building the executable
- Uses the symbol table to replace every symbolic operand with its address.
- Uses the opcode table (the second table needed) to replace each mnemonic with its binary op code → machine code.
7. Question (b) — Trace Table with LDV (Relative Addressing)
Complete the trace table for the assembly program (columns ACC · CountDown · OUTPUT). The first two rows are given: CountDown = 15 at the start, then ACC = 67 and output C.
ASCII table given (character = code): space = 32 · “3” = 51 · B = 66 · C = 67 · X = 88 (B = 66 is never used — a distractor).
| Addr | Label | Instruction |
|---|---|---|
| 100 | StartProg: | LDV #CountDown |
| 101 | CMP Num1 | |
| 102 | JNE CarryOn | |
| 103 | JMP Finish | |
| 104 | CarryOn: | OUTCH |
| 105 | LDD CountDown | |
| 106 | DEC | |
| 107 | STO CountDown | |
| 108 | JMP StartProg | |
| 109 | Finish: | LDM #88 |
| 110 | OUTCH | |
| 111 | END | |
| 112 | CountDown: | 15 |
| 113 | 32 | |
| 114 | 51 | |
| 115 | 67 | |
| 116 | Num1: | 32 |
Dry run — one round per column
Whole output: C 3 X
Completed trace table:
| ACC | CountDown | OUTPUT | caused by |
|---|---|---|---|
| 15 | start (given) | ||
| 67 | C | LDV, OUTCH (given) | |
| 15 | LDD | ||
| 14 | DEC | ||
| 14 | STO | ||
| 51 | 3 | LDV, OUTCH | |
| 14 | LDD | ||
| 13 | DEC | ||
| 13 | STO | ||
| 32 | LDV (CMP equal) | ||
| 88 | X | LDM #88, OUTCH |
8. Question (c) — Symbol Table & Explanation
The same program is translated by a two-pass assembler. It now begins with the directive ORG #0100, which loads the first instruction at address 100. Complete the symbol table (5 blank rows).
Count addresses from 100: ORG is only an instruction to the assembler — it takes no memory location (LDV = 100 ✓ board shows CarryOn = 104).
| Symbolic address | Absolute address | how |
|---|---|---|
| StartProg | 100 | 1st instruction (ORG) |
| CountDown | 112 | 100 + 12 lines |
| Num1 | 116 | last data line |
| CarryOn | 104 | 5th instruction (OUTCH) |
| Finish | 109 | LDM #88 line |
Board’s part-way table (first 11 entries) — what each circle means
| Circles | Symbolic → Absolute | Why the assembler did it |
|---|---|---|
| ① ② | StartProg → 100 | first label on line 1; ORG #0100 puts the first instruction at 100 |
| ③ ④ | CountDown → UNKNOWN | used in LDV #CountDown before its own line is reached |
| ⑤ ⑥ | Num1 → UNKNOWN | used in CMP Num1, line not reached yet |
| ⑦ ⑧ | CarryOn → UNKNOWN | used in JNE CarryOn, line not reached yet |
| ⑨ ⑩ | Finish → UNKNOWN | used in JMP Finish, line not reached yet |
| ⑪ | CarryOn: | assembler now reaches the line labelled CarryOn (5th line: 100 + 4) → crosses out UNKNOWN, writes 104 |
- ① StartProg is the first label, so it is entered with address 100 — the address of the first instruction given by the ORG directive.
- ② CountDown, Num1, CarryOn, Finish are found in operands before the lines that define them (forward references), so each is entered with UNKNOWN.
- ③ On reaching the line labelled CarryOn the assembler works out its address (104, counting one per line from 100) and replaces UNKNOWN; the same happens later for Finish, CountDown and Num1.
(d)(i) second table in pass 2: Opcode table
9. Question (e) — Relative-Address Symbol Table
A different program is translated by a two-pass assembler, with no ORG, so the table holds relative addresses (offset from the start, first instruction = 0). Complete the symbol table and use circled numbers for the order of entries. Given: ① StartProg, ② 0, ③ Offset, ④ UNKNOWN, ⑤ Value.
| Rel. | Label | Instruction |
|---|---|---|
| 0 | StartProg: | LDV #Offset |
| 1 | CMP Value | |
| 2 | JPE EndProg | |
| 3 | OUTCH | |
| 4 | LDD Offset | |
| 5 | INC | |
| 6 | STO Offset | |
| 7 | JMP StartProg | |
| 8 | EndProg: | END |
| 9 | Offset: | 10 |
| 10 | 50 | |
| 11 | 65 | |
| 12 | 89 | |
| 13 | 32 | |
| 14 | Value: | 32 |
Completed symbol table
| Symbolic address | Relative address |
|---|---|
| StartProg ① | 0 ② |
| Offset ③ | |
| Value ⑤ | |
| EndProg ⑦ |
Order of entries (circles)
- ① ② StartProg is the first label ⇒ relative address 0.
- ③ ④ LDV #Offset: Offset not seen yet ⇒ enter with UNKNOWN.
- ⑤ ⑥ CMP Value: Value not seen yet ⇒ UNKNOWN.
- ⑦ ⑧ JPE EndProg: EndProg not seen yet ⇒ UNKNOWN (4th row of the table).
- ⑨ reaches line EndProg: END = line 8 ⇒ UNKNOWN → 8.
- ⑩ reaches Offset: (first data line) = line 9 ⇒ UNKNOWN → 9.
- ⑪ reaches Value: (last data line, 6 data items 9–14) ⇒ UNKNOWN → 14.
- Two-way translation = partially compiled + partially interpreted. Java: source → Java compiler → byte code → JVM interprets.
- Why: ① byte code works on any platform / no re-compile ② machine code generated at run time ⇒ optimised for the CPU.
- Addressing: immediate LDM #n · direct LDD addr · relative LDV #n (n locations from the current instruction) · symbolic = labels · absolute = fixed address.
- ORG #0100 = first instruction at address 100 (no memory used). No ORG ⇒ relative addresses from 0.
- Pass 1: build symbol table (count +1 per line; forward references = UNKNOWN, replaced when the label’s line is reached).
- Pass 2: swap symbols for addresses (symbol table) and mnemonics for binary (opcode table) ⇒ executable machine code.
- (c) answer: StartProg 100 · CountDown 112 · Num1 116 · CarryOn 104 · Finish 109.
- (b) output: C 3 X; ACC sequence 67, 15, 14, 51, 14, 13, 32, 88; CountDown 15 → 14 → 13.
- (e) answer: StartProg 0 · Offset 9 · Value 14 · EndProg 8 (order ①–⑪).