Theory: MARIE Assembly Language & the Two-Pass Assembler
Mnemonics vs Machine Code
The CPU only understands machine code: bit patterns such as 0001 0001 0000 0100. Humans write assembly language: one mnemonic instruction per line, such as Load 104. Each assembly instruction corresponds to exactly one machine instruction. An assembler is the program that translates assembly into machine code.
| Level | Example (A=B+C) |
|---|
| High-level language | A = B + C; (one statement, many machine instructions) |
| MARIE assembly | Load B / Add C / Store A |
| MARIE machine code (hex) | e.g. 1105 / 3106 / 2104 |
Different architectures use different mnemonics and formats for the same operation, which is why assembly is not portable.
Anatomy of a MARIE Source Line
Label, Mnemonic Operand / comment
Loop, Load Count / AC <- Count
| Part | Rules |
|---|
| Label (optional) | a name followed by a comma; it stands for the address of this line |
| Mnemonic | an instruction (Load, Add, Skipcond, …) or a directive (ORG, DEC, HEX, END) |
| Operand | a label or a hex address for instructions; a number for DEC/HEX; none for Input, Output, Halt, Clear |
| Comment | everything after / is ignored by the assembler |
Directives (pseudo-instructions)
Directives tell the assembler what to do; they are not translated into CPU instructions themselves.
| Directive | Meaning | Example | Produces |
|---|
ORG n | start placing code at hex address n | ORG 100 | (nothing; sets the location counter to 100) |
DEC n | a data word given in decimal | DEC -23 | FFE9 |
HEX n | a data word given in hex | HEX 0023 | 0023 |
END | end of the program | END | (nothing) |
Mini-example
X, DEC 35 reserves one word labelled X containing 35=002316. Y, DEC -23 stores the two’s complement of 23: 23=0017 → invert → FFE8 → +1 → FFE9.
Data must be placed after the Halt (or otherwise out of the instruction stream). The CPU cannot tell data from instructions: if execution runs into DEC 35 it will happily execute 0023 as JnS 023.
The Two-Pass Assembler
Labels can be used before they are defined (a forward reference: Load X appears before the line X, DEC 35). So the assembler cannot finish line 1 until it has seen the whole program. The solution is to read the source twice.
Pass 1: build the symbol table.
- Set the location counter (LC) from
ORG (default 0).
- For each line: if it has a label, record label → LC in the symbol table. Translate the mnemonic into its opcode (partial code; the address is left blank if it is a label). Increment LC by 1 (every instruction and every
DEC/HEX takes one word).
Pass 2: complete the code.
3. For each line: replace each label operand by its address from the symbol table, convert DEC/HEX constants to 16-bit hex, and output the finished machine word.
Mini-example (as in the lecture)
ORG 100
Load X
Add Y
Store Z
Halt
X, DEC 35
Y, DEC -23
Z, HEX 0000
END
Pass 1: LC runs 100, 101, … ; labels seen at 104, 105, 106 → symbol table X = 104, Y = 105, Z = 106. Partial code: 1 ???, 3 ???, 2 ???, 7000, …
Pass 2: fill in → 1104, 3105, 2106, 7000, 0023, FFE9, 0000. This is exactly the addition program of 6.3.
Input copies a value typed by the user into the AC (AC ← InREG); Output displays the AC (OutREG ← AC). Neither uses its address field.
Mini-example
Read two numbers and print the second minus the first:
ORG 100
Input / AC <- first number
Store A
Input / AC <- second number
Subt A / AC <- second - first
Output
Halt
A, DEC 0
Hex: 5000 2106 5000 4106 6000 7000 0000. With inputs 9 then 30 it outputs 30−9=21 (hex 0015).
Translating Control Structures
MARIE has only one conditional instruction, Skipcond, which skips the next instruction. Every if and loop is built as “test, Skipcond, Jump”:
- Compute a value in the AC whose sign answers the question (usually by subtracting: X − Y).
Skipcond with the condition under which you want to stay on the straight path.
- The next line is a
Jump to the “other” path; it is skipped when the condition holds.
| High-level condition | Compute in AC | Skipcond to enter the body |
|---|
| X > Y | X − Y | Skipcond 800 (skip the jump if X − Y > 0) |
| X < Y | X − Y | Skipcond 000 |
| X = Y | X − Y | Skipcond 400 |
| X > 1 | X − 1 | Skipcond 800 |
For X≥Y (or any “not” condition), test the opposite condition, X<Y, and simply swap which code is the THEN part and which is the ELSE part.
If / else template
Load X
Subt Y
Skipcond 800 / X - Y > 0 ? -> skip the Jump, run THEN
Jump Else
Then, ... / THEN part
Jump EndIf
Else, ... / ELSE part
EndIf, ...
While-loop template (while X < Y do body)
While, Load X
Subt Y
Skipcond 000 / X - Y < 0 ? -> skip the Jump, run the body
Jump EndW
... / body
Jump While
EndW, ...
Counting-loop template (repeat N times, N counts down)
Loop, Load N
Skipcond 800 / N > 0 ? -> skip the Jump, run the body
Jump Done
... / body
Load N
Subt One
Store N
Jump Loop
Done, ...
Subroutines with JnS and JumpI
A subroutine starts with one word reserved for the return address, followed by its code, and ends with JumpI on that word:
JnS Sub / M[Sub] <- return address, PC <- Sub + 1
... / execution resumes here
Sub, HEX 0000 / holds the return address
... / subroutine body (starts at Sub + 1)
JumpI Sub / PC <- M[Sub]: return
Parameters are passed through agreed memory locations (MARIE has no stack). Remember that JnS overwrites the AC.
Worked Examples
Exercise 1
Assemble the labelled addition program (as in the lecture) by hand using the two-pass method. Show the symbol table after pass 1, the partial code, and the final hex code. What is in Z after the program runs?
ORG 100
Load X
Add Y
Store Z
Halt
X, DEC 35
Y, DEC -23
Z, HEX 0000
END
Show solution ↓Hide solution ↑
Solution
Pass 1: walk the lines with the location counter starting at 100 (hex).
| LC | Label | Source | Partial code |
|---|
| 100 | | Load X | 1 ??? |
| 101 | | Add Y | 3 ??? |
| 102 | | Store Z | 2 ??? |
| 103 | | Halt | 7000 |
| 104 | X | DEC 35 | (constant) |
| 105 | Y | DEC -23 | (constant) |
| 106 | Z | HEX 0000 | (constant) |
Symbol table
| Symbol | Address |
|---|
| X | 104 |
| Y | 105 |
| Z | 106 |
Pass 2: substitute addresses and convert constants. 35=32+3=2316 → 0023. −23: 23=0017, invert → FFE8, +1 → FFE9.
| Address | Source | Hex |
|---|
| 100 | Load X | 1104 |
| 101 | Add Y | 3105 |
| 102 | Store Z | 2106 |
| 103 | Halt | 7000 |
| 104 | X, DEC 35 | 0023 |
| 105 | Y, DEC -23 | FFE9 |
| 106 | Z, HEX 0000 | 0000 |
Run: AC = 0023 → AC = 0023 + FFE9 = 000C → Z = 000C = 12 ✓ (35−23).
Exercise 2
Write a MARIE program for
if (A < B) Max = B; else Max = A;
output Max;
with A = 7, B = 12, starting at ORG 100. Hand-assemble it, give the symbol table and trace the execution.
Show solution ↓Hide solution ↑
Solution
A<B⟺A−B<0, so compute A − B and use Skipcond 000 to enter the THEN part.
| Addr | Label | Source | Hex |
|---|
| 100 | | Load A | 110C |
| 101 | | Subt B | 410D |
| 102 | | Skipcond 000 | 8000 |
| 103 | | Jump Else | 9107 |
| 104 | | Load B | 110D |
| 105 | | Store Max | 210E |
| 106 | | Jump Done | 9109 |
| 107 | Else, | Load A | 110C |
| 108 | | Store Max | 210E |
| 109 | Done, | Load Max | 110E |
| 10A | | Output | 6000 |
| 10B | | Halt | 7000 |
| 10C | A, | DEC 7 | 0007 |
| 10D | B, | DEC 12 | 000C |
| 10E | Max, | DEC 0 | 0000 |
Symbol table: Else = 107, Done = 109, A = 10C, B = 10D, Max = 10E.
Trace
| Addr | Instruction | AC after | Note |
|---|
| 100 | Load A | 0007 | |
| 101 | Subt B | FFFB | 7−12=−5 |
| 102 | Skipcond 000 | FFFB | AC <0 → skip 103 |
| 104 | Load B | 000C | |
| 105 | Store Max | 000C | Max = 000C |
| 106 | Jump Done | 000C | PC ← 109 |
| 109 | Load Max | 000C | |
| 10A | Output | 000C | prints 12 |
| 10B | Halt | | |
Output 12 = max(7, 12) ✓.
Exercise 3
Write a counting loop that computes Sum=5+4+3+2+1 (count N down from 5 to 0) and outputs it. Start at ORG 100. Give the hex code, symbol table, and the values of N and Sum at the top of each iteration.
Show solution ↓Hide solution ↑
Solution
ORG 100
Loop, Load N
Skipcond 800 / N > 0 ? skip the Jump
Jump Done
Load Sum
Add N / Sum = Sum + N
Store Sum
Load N
Subt One / N = N - 1
Store N
Jump Loop
Done, Load Sum
Output
Halt
N, DEC 5
Sum, DEC 0
One, DEC 1
| Addr | Source | Hex | | Addr | Source | Hex |
|---|
| 100 | Loop, Load N | 110D | | 108 | Store N | 210D |
| 101 | Skipcond 800 | 8800 | | 109 | Jump Loop | 9100 |
| 102 | Jump Done | 910A | | 10A | Done, Load Sum | 110E |
| 103 | Load Sum | 110E | | 10B | Output | 6000 |
| 104 | Add N | 310D | | 10C | Halt | 7000 |
| 105 | Store Sum | 210E | | 10D | N, DEC 5 | 0005 |
| 106 | Load N | 110D | | 10E | Sum, DEC 0 | 0000 |
| 107 | Subt One | 410F | | 10F | One, DEC 1 | 0001 |
Symbol table: Loop = 100, Done = 10A, N = 10D, Sum = 10E, One = 10F.
| Iteration | N at top | Skip? | Sum after body |
|---|
| 1 | 5 | yes (5 > 0) | 5 |
| 2 | 4 | yes | 9 |
| 3 | 3 | yes | 12 |
| 4 | 2 | yes | 14 |
| 5 | 1 | yes | 15 |
| exit | 0 | no → Jump Done | |
Output: Sum = 15 = 000F ✓ (5⋅6/2=15).
Exercise 4
Translate the while loop
while (X < Limit) X = X + X;
output X;
for X = 3, Limit = 20, starting at ORG 200. Give the hex code, symbol table and a trace of X.
Show solution ↓Hide solution ↑
Solution
Test X−Limit<0 with Skipcond 000.
| Addr | Label | Source | Hex |
|---|
| 200 | While, | Load X | 120B |
| 201 | | Subt Limit | 420C |
| 202 | | Skipcond 000 | 8000 |
| 203 | | Jump EndW | 9208 |
| 204 | | Load X | 120B |
| 205 | | Add X | 320B |
| 206 | | Store X | 220B |
| 207 | | Jump While | 9200 |
| 208 | EndW, | Load X | 120B |
| 209 | | Output | 6000 |
| 20A | | Halt | 7000 |
| 20B | X, | DEC 3 | 0003 |
| 20C | Limit, | DEC 20 | 0014 |
Symbol table: While = 200, EndW = 208, X = 20B, Limit = 20C.
| Test | X | X − Limit | AC (hex) | Skipcond 000 | New X |
|---|
| 1 | 3 | −17 | FFEF | skip → body | 6 |
| 2 | 6 | −14 | FFF2 | skip → body | 12 |
| 3 | 12 | −8 | FFF8 | skip → body | 24 |
| 4 | 24 | 4 | 0004 | no skip → Jump EndW | |
Output 24 (hex 0018) ✓: the first doubling of 3 that is not below 20.
Exercise 5
MARIE has no multiply instruction. Write a program that computes Prod=X×Y by repeated addition for X = 4, Y = 3 (ORG 100), assemble it, and trace it.
Show solution ↓Hide solution ↑
Solution
Add X to Prod, Y times (Y counts down):
| Addr | Label | Source | Hex |
|---|
| 100 | Loop, | Load Y | 110E |
| 101 | | Skipcond 800 | 8800 |
| 102 | | Jump Done | 910A |
| 103 | | Load Prod | 110F |
| 104 | | Add X | 310D |
| 105 | | Store Prod | 210F |
| 106 | | Load Y | 110E |
| 107 | | Subt One | 4110 |
| 108 | | Store Y | 210E |
| 109 | | Jump Loop | 9100 |
| 10A | Done, | Load Prod | 110F |
| 10B | | Output | 6000 |
| 10C | | Halt | 7000 |
| 10D | X, | DEC 4 | 0004 |
| 10E | Y, | DEC 3 | 0003 |
| 10F | Prod, | DEC 0 | 0000 |
| 110 | One, | DEC 1 | 0001 |
Symbol table: Loop = 100, Done = 10A, X = 10D, Y = 10E, Prod = 10F, One = 110.
| Pass | Y at test | Action | Prod |
|---|
| 1 | 3 | skip, add | 4 |
| 2 | 2 | skip, add | 8 |
| 3 | 1 | skip, add | 12 |
| 4 | 0 | Jump Done | 12 |
Output 12 (000C) = 4×3 ✓.
Exercise 6
Write a subroutine Double that doubles the value in location Temp, and a main program that copies Num = 21 to Temp, calls Double with JnS, then outputs Temp. Assemble at ORG 100, give the symbol table and trace the call and return.
Show solution ↓Hide solution ↑
Solution
| Addr | Label | Source | Hex |
|---|
| 100 | | Load Num | 1106 |
| 101 | | Store Temp | 2107 |
| 102 | | JnS Double | 0108 |
| 103 | | Load Temp | 1107 |
| 104 | | Output | 6000 |
| 105 | | Halt | 7000 |
| 106 | Num, | DEC 21 | 0015 |
| 107 | Temp, | DEC 0 | 0000 |
| 108 | Double, | HEX 0000 | 0000 |
| 109 | | Load Temp | 1107 |
| 10A | | Add Temp | 3107 |
| 10B | | Store Temp | 2107 |
| 10C | | JumpI Double | C108 |
Symbol table: Num = 106, Temp = 107, Double = 108.
Trace
| Addr | Instruction | Effect |
|---|
| 100 | Load Num | AC = 0015 |
| 101 | Store Temp | Temp = 0015 |
| 102 | JnS Double | PC was 103 → M[108] = 0103; AC = 108 + 1 = 0109; PC = 109 |
| 109 | Load Temp | AC = 0015 |
| 10A | Add Temp | AC = 002A |
| 10B | Store Temp | Temp = 002A |
| 10C | JumpI Double | PC ← M[108] = 103 (return) |
| 103 | Load Temp | AC = 002A |
| 104 | Output | prints 42 |
| 105 | Halt | |
Output 42 = 2×21 ✓. Note JnS encodes as 0108 because its opcode is 0.
Exercise 7
Use AddI and a pointer to add the four array elements 10, 15, 20, 25 stored from address 112, starting at ORG 100. Give the hex code and symbol table and trace Ptr, Ctr and Sum.
Show solution ↓Hide solution ↑
Solution
Ptr holds the address of the current element; AddI Ptr adds the element Ptr points to. Ctr counts down from 4; Skipcond 400 leaves the loop when Ctr reaches 0.
| Addr | Label | Source | Hex |
|---|
| 100 | Loop, | Load Sum | 110F |
| 101 | | AddI Ptr | B10E |
| 102 | | Store Sum | 210F |
| 103 | | Load Ptr | 110E |
| 104 | | Add One | 3111 |
| 105 | | Store Ptr | 210E |
| 106 | | Load Ctr | 1110 |
| 107 | | Subt One | 4111 |
| 108 | | Store Ctr | 2110 |
| 109 | | Skipcond 400 | 8400 |
| 10A | | Jump Loop | 9100 |
| 10B | | Load Sum | 110F |
| 10C | | Output | 6000 |
| 10D | | Halt | 7000 |
| 10E | Ptr, | HEX 112 | 0112 |
| 10F | Sum, | DEC 0 | 0000 |
| 110 | Ctr, | DEC 4 | 0004 |
| 111 | One, | DEC 1 | 0001 |
| 112 | Arr, | DEC 10 | 000A |
| 113 | | DEC 15 | 000F |
| 114 | | DEC 20 | 0014 |
| 115 | | DEC 25 | 0019 |
Symbol table: Loop = 100, Ptr = 10E, Sum = 10F, Ctr = 110, One = 111, Arr = 112.
| Iteration | Ptr (before) | M[Ptr] | Sum after | Ctr after | Skipcond 400 |
|---|
| 1 | 112 | 10 | 10 (000A) | 3 | no skip → Jump Loop |
| 2 | 113 | 15 | 25 (0019) | 2 | no skip |
| 3 | 114 | 20 | 45 (002D) | 1 | no skip |
| 4 | 115 | 25 | 70 (0046) | 0 | skip the Jump |
Output 70 (0046) ✓ =10+15+20+25.