Project 6 · Assembler
Assembly language and the assembler
Assembly language is a low-level language that maps almost one to one onto machine instructions: one statement is usually one instruction, with mnemonics in place of 0s and 1s and symbols in place of addresses. It still faces registers, memory and jumps directly, with no functions in between. The program that turns those mnemonics into machine code is the assembler.
01Assembly: machine instructions written with symbols
It is called a language because the grammar is fixed: every line is one of three things — give an address, do a computation, or just mark a position.
| Form | Meaning | Emits an instruction? |
|---|---|---|
| @value | An A instruction: puts a number, or a symbol’s address, into A | yes, one |
| dest=comp;jump | A C instruction: compute, store, maybe jump | yes, one |
| (LABEL) | A label: names the address of the next instruction | no |
Comments and blank lines emit nothing either. So line number ≠ address — exactly what the first pass works out.
02Why two passes: the forward-reference crisis
@END // crisis: on this line the assembler has no idea what END is! D;JEQ ... (END) // only here does the assembler learn the address of ENDIn real code a conditional jump usually targets a label further down (used before defined). Scanning once, the assembler is stuck on the very first line: it cannot produce a complete 16-bit word.
The fix is a clean piece of engineering decoupling — two passes:
| Pass | Role | What it does | Output |
|---|---|---|---|
| First | Surveyor | Measures only: counts instruction addresses, records the current address at each (LABEL) | A complete label table |
| Second | Builder | With the table in hand, turns every mnemonic and symbol into machine code with absolute addresses | 16-bit instructions |
The first pass measures without building; the second builds to the plan. From then on, jumps forward or backward always land on a known address.
03Three kinds of names in the symbol table
| Kind | Example | Where the address comes from |
|---|---|---|
| Predefined | R0–R15, SCREEN, KBD, SP, LCL, ARG, THIS, THAT | Fixed values |
| Label | (LOOP) | The instruction address counted in pass one |
| Variable | @sum | Starts at 16, allocated in order of first appearance |
Allocation order changes addresses (whichever appears first gets 16), but behaviour does not change — names follow the addresses.
04Hands-on: edit the code and watch the translation
Edit assembly on the left; the symbol table and line-by-line machine code update on the right.
Symbol table
| Name | Address | Type |
|---|---|---|
| R0 | 0 | predefined |
| R1 | 1 | predefined |
| LOOP | 6 | label |
| n | 16 | variable |
| sum | 17 | variable |
| END | 20 | label |
Line by line
| Line | Source | Address | Machine code | Hex | Note |
|---|---|---|---|---|---|
| 2 | @R0 | 0 | 0000000000000000 | 0x0000 | predefined R0, RAM address 0 |
| 3 | D=M | 1 | 1111110000010000 | 0xFC10 | C instruction |
| 4 | @n | 2 | 0000000000010000 | 0x0010 | new variable n, RAM address 16 |
| 5 | M=D | 3 | 1110001100001000 | 0xE308 | C instruction |
| 6 | @sum | 4 | 0000000000010001 | 0x0011 | new variable sum, RAM address 17 |
| 7 | M=0 | 5 | 1110101010001000 | 0xEA88 | C instruction |
| 8 | (LOOP) | — | — | a label, no instruction | |
| 9 | @n | 6 | 0000000000010000 | 0x0010 | variable n, RAM address 16 |
| 10 | D=M | 7 | 1111110000010000 | 0xFC10 | C instruction |
| 11 | @END | 8 | 0000000000010100 | 0x0014 | label END, instruction address 20 |
| 12 | D;JEQ | 9 | 1110001100000010 | 0xE302 | C instruction JEQ |
| 13 | @sum | 10 | 0000000000010001 | 0x0011 | variable sum, RAM address 17 |
| 14 | D=M | 11 | 1111110000010000 | 0xFC10 | C instruction |
| 15 | @n | 12 | 0000000000010000 | 0x0010 | variable n, RAM address 16 |
| 16 | D=D+M | 13 | 1111000010010000 | 0xF090 | C instruction |
| 17 | @sum | 14 | 0000000000010001 | 0x0011 | variable sum, RAM address 17 |
| 18 | M=D | 15 | 1110001100001000 | 0xE308 | C instruction |
| 19 | @n | 16 | 0000000000010000 | 0x0010 | variable n, RAM address 16 |
| 20 | M=M-1 | 17 | 1111110010001000 | 0xFC88 | C instruction |
| 21 | @LOOP | 18 | 0000000000000110 | 0x0006 | label LOOP, instruction address 6 |
| 22 | 0;JMP | 19 | 1110101010000111 | 0xEA87 | C instruction JMP |
| 23 | (END) | — | — | a label, no instruction | |
| 24 | @sum | 20 | 0000000000010001 | 0x0011 | variable sum, RAM address 17 |
| 25 | D=M | 21 | 1111110000010000 | 0xFC10 | C instruction |
| 26 | @R1 | 22 | 0000000000000001 | 0x0001 | predefined R1, RAM address 1 |
| 27 | M=D | 23 | 1110001100001000 | 0xE308 | C instruction |
No errors: 24 instructions, 6 symbols.
05The three usual mistakes
| Symptom | Cause | Fix |
|---|---|---|
| The label is “not found” | Missing brackets when defining, or a colon when referencing | Define (END), reference @END |
| A variable lands at an unexpected address | Assuming variables start at 0 | They start at 16; 0–15 are R0–R15 |
@40000 is rejected | An A instruction carries 15 bits | The maximum is 32767 |
06After translation
source (.asm) → assembler → machine code (.hack) → ROM → CPU executes
The output is a stream of 16-bit words loaded into ROM in order (starting at address 0); after reset PC = 0 and the CPU runs the first one. How that loading and running works is on the whole machine page.
That is why the machine language page covers the instruction format first: the assembler only removes symbols; the encoding itself is unchanged.