LC-3 control instructions: BR, JMP, TRAP and loops
The BR instruction that looks at the condition codes, offset calculation, JMP/RET and TRAP; counter and sentinel loops, the if/else pattern, and reading and tracing a program from hex machine code.
Last week the instructions always ran in order: every FETCH incremented the PC by one. Real programs, however, make decisions (if) and repeat (loops). For this there are control instructions: by writing a new value into the PC, they change the next instruction. In the LC-3 these are BR, JMP, JSR, TRAP and RTI. On this page you see BR, JMP and TRAP, and then the loop and if patterns built with them.
Exam questions: encode or decode a BR (offset calculation), decode and trace a program given in hex, find how many times a loop runs and the register values at the end.
BR: conditional branch
0000 | n | z | p | PCoffset9
15-12 11 10 9 8-0
BR looks at the condition codes. If one of the bits that is 1 in the instruction matches the current condition code, it branches: PC ← (incremented PC) + SEXT(PCoffset9). If it does not match, it does nothing, and the program moves on to the next instruction.
| Assembly | n z p | When does it branch? |
|---|---|---|
BRn | 1 0 0 | the last value is negative |
BRz | 0 1 0 | the last value is zero |
BRp | 0 0 1 | the last value is positive |
BRnz | 1 1 0 | ≤ 0 |
BRzp | 0 1 1 | ≥ 0 |
BRnp | 1 0 1 | ≠ 0 |
BR / BRnzp | 1 1 1 | always |
| (000) | 0 0 0 | never (NOP) |
TIP · What does BR look at? BR looks at the result of the last instruction that wrote to a register for the condition code. In between there can be instructions that do not change the CC, like ST, STR, BR; they do not count. If you want to test a register, first write it somewhere:
ADD R1, R1, #0sets the CC according to R1.
Offset calculation
The range is -256..+255. When branching backward (to the start of a loop), the offset is negative.
Encoding example: BRn LOOP at x3008, LOOP = x3002.
offset = x3002 - x3009 = -7
-7 (9 bits) = 1 1111 1001
0000 100 111111001 = x09F9
Decoding example: x0BFC, at x3020.
0000 101 111111100
n=1 z=0 p=1 → BRnp
offset = 1 1111 1100 = -4
target = x3021 - 4 = x301D
JMP and RET
1100 | 000 | BaseR | 000000
PC ← BaseR. Because the target is in a register, it can jump anywhere in memory (BR reaches only ±256). JMP R2 = xC080. RET is an alias of JMP R7 (xC1C0); it is used to return from a subroutine or a TRAP routine.
TRAP
1111 | 0000 | trapvect8
- R7 ← PC (the incremented PC, the return address).
- PC ← M[ZEXT(trapvect8)]: take the address of the routine from the trap vector table between x0000-x00FF.
| Code | Alias | Job |
|---|---|---|
| xF020 | GETC | read a character → R0 |
| xF021 | OUT | write R0 |
| xF022 | PUTS | write the string at the address in R0 |
| xF023 | IN | prompt + read a character + echo → R0 |
| xF025 | HALT | stop |
Loop patterns
Counter loop (counter-controlled)
How many times to loop is known from the start. The counter is kept in a register, decreased in every round, and the loop exits when it reaches zero.
Worked example: multiplication. There is no MUL in the LC-3; 6 × 4 = 6 + 6 + 6 + 6.
.ORIG x3000
AND R3, R3, #0 ; R3 = 0 (result)
LD R1, X ; R1 = 6
LD R2, Y ; R2 = 4 (counter)
LOOP ADD R3, R3, R1 ; R3 += 6
ADD R2, R2, #-1 ; counter--
BRp LOOP ; repeat if counter > 0
HALT
X .FILL #6
Y .FILL #4
.END
round R3 R2 CC BRp
1 6 3 P loop back
2 12 2 P loop back
3 18 1 P loop back
4 24 0 Z fall through
The result is R3 = 24. The machine code:
x3000 x56E0 AND R3, R3, #0
x3001 x2205 LD R1, X (x3002 + 5 = x3007)
x3002 x2405 LD R2, Y (x3003 + 5 = x3008)
x3003 x16C1 ADD R3, R3, R1
x3004 x14BF ADD R2, R2, #-1
x3005 x03FD BRp x3003 (x3006 - 3)
x3006 xF025 HALT
PITFALL · one round too many. If you write BRzp instead of BRp, it also loops back when R2 = 0, runs 5 rounds, and the result is 30. If you want the counter to end at 0, use BRp.
Sentinel loop
The number of elements is not known; at the end of the list there is a special value (the sentinel, usually 0). Each element is loaded, and if it is the sentinel the loop exits.
Worked example: count the negatives.
AND R2, R2, #0 ; counter = 0
LEA R1, DATA ; R1 = address of the array
LOOP LDR R3, R1, #0 ; R3 = *R1, the CC is set
BRz DONE ; 0 → finished
BRp NEXT ; positive → do not count
ADD R2, R2, #1 ; negative → count
NEXT ADD R1, R1, #1 ; next element
BRnzp LOOP
DONE ST R2, RESULT
HALT
RESULT .BLKW 1
DATA .FILL #5
.FILL #-3
.FILL #-8
.FILL #2
.FILL #-1
.FILL #0
The result is RESULT = 3. BRz and BRp back to back look at the same CC, because BR does not change the CC.
if and if/else
The pattern: skip with the opposite of the condition. To write “if x is negative do this”, you write “if x is not negative (zp) skip”.
Absolute value:
LD R1, V ; CC = the sign of V
BRzp DONE ; skip if not negative
NOT R1, R1 ; R1 = -R1
ADD R1, R1, #1
DONE ST R1, V
HALT
V .FILL #-25
The result is V = 25.
if/else: the larger of two numbers.
LD R1, A
LD R2, B
NOT R3, R2
ADD R3, R3, #1
ADD R3, R1, R3 ; R3 = A - B, the CC is set
BRn BBIG ; if A < B go to the else part
ADD R4, R1, #0 ; then: R4 = A
BRnzp END ; skip the else
BBIG ADD R4, R2, #0 ; else: R4 = B
END HALT
A .FILL #-4
B .FILL #9
The result is R4 = 9. Without the unconditional BRnzp END at the end of the then block, the program would fall into the else block too.
Reading a program from hex
In the exam sometimes only the machine code is given. The recipe:
- Expand each word to 16 bits, look at the opcode.
- Write the instruction in assembly; compute the BR targets with addresses.
- Set up a trace table (registers, CC, branches).
x3000 x5020 AND R0, R0, #0
x3001 x1225 ADD R1, R0, #5
x3002 x1001 ADD R0, R0, R1
x3003 x127E ADD R1, R1, #-2
x3004 x03FD BRp x3002
x3005 xF025 HALT
R1: 5 → 3 → 1 → -1 R0: 5 → 8 → 9 result R0 = 9
Common mistakes
| Wrong idea | The truth |
|---|---|
| Offset = target - BR address | target - (BR address + 1) |
| BR produces its own CC | BR looks at the CC of the last register write |
| BRzp and BRp loop the same number of times | BRzp is one round more |
| x0000 is an invalid instruction | A BR with nzp = 000 is a NOP |
| JMP reaches only ±256 | BaseR is 16 bits, anywhere |
| TRAP writes the return address to the PC | It writes it to R7 |
| For an if, branch on the condition itself | Usually skip with the opposite of the condition |
| BRnzp is not needed at the end of then | Otherwise the else runs too |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
Encode BRz with PCoffset9 = -6 in hex.
Show answer
Format: 0000 | n z p | PCoffset9. BRz → n=0, z=1, p=0. -6 (9 bits) = 1 1111 1010.
0000 010 111111010 → 0000 0101 1111 1010 = x05FA.
Decode x0E05 located at address x3010. Where does it go, and when?
Show answer
0000 111 000000101: n = z = p = 1, that is an unconditional branch (BR or BRnzp). Target = x3011 + 5 = x3016.
A BRn LOOP instruction is at x3008 and LOOP is at x3002. What is the PCoffset9 and the machine code?
Show answer
offset = target - (instruction address + 1) = x3002 - x3009 = -7. -7 = 1 1111 1001.
0000 100 111111001 = 0000 1001 1111 1001 = x09F9.
What does an LC-3 BR instruction with n = z = p = 0 (for example x0000) do?
Show answer
BR branches if one of the bits it tests matches the condition code. If no bit is selected, it never matches. The PC advances only with the +1 in FETCH. That is why x0000 behaves like a NOP.
Encode JMP R2 and RET in hex.
Show answer
Format: 1100 | 000 | BaseR | 000000. JMP R2: 1100 000 010 000000 = xC080. RET is an alias of JMP R7: 1100 000 111 000000 = xC1C0. Both do PC ← BaseR.
What are R3 and R2 when this program halts, and how many times is the instruction at LOOP executed?
AND R3, R3, #0
LD R1, X
LD R2, Y
LOOP ADD R3, R3, R1
ADD R2, R2, #-1
BRp LOOP
HALT
X .FILL #6
Y .FILL #4Show answer
R2 is the counter (4). In each round 6 is added to R3 and R2 decreases by one. R2: 3, 2, 1 (P → loop back), 0 (Z → fall through). The loop runs 4 times: R3 = 4 × 6 = 24. This is how multiplication is done in the LC-3 (there is no MUL instruction).
In the previous program, BRp is replaced by BRzp. What is R3 now?
Show answer
BRzp also loops back when R2 = 0. The loop runs 5 times until R2 = -1: 5 × 6 = 30. In a counter loop, the branch condition is the most common cause of running one round too many or too few.
The memory starting at x3000 contains x5020, x1225, x1001, x127E, x03FD, xF025. Decode the program and give R0 when it halts.
Show answer
x5020 AND R0,R0,#0 · x1225 ADD R1,R0,#5 · x1001 ADD R0,R0,R1 · x127E ADD R1,R1,#-2 · x03FD BRp x3002 · xF025 HALT.
R1 = 5 → R0 = 5, R1 = 3 → R0 = 8, R1 = 1 → R0 = 9, R1 = -1 (N) → the loop ends. R0 = 5 + 3 + 1 = 9.
This program counts something in a list that ends with 0. What value is stored in RESULT?
AND R2, R2, #0
LEA R1, DATA
LOOP LDR R3, R1, #0
BRz DONE
BRp NEXT
ADD R2, R2, #1
NEXT ADD R1, R1, #1
BRnzp LOOP
DONE ST R2, RESULT
HALT
RESULT .BLKW 1
DATA .FILL #5
.FILL #-3
.FILL #-8
.FILL #2
.FILL #-1
.FILL #0Show answer
LDR loads each element and sets the CC. If it is 0, finish (sentinel), if positive, do not count, if negative, increment R2. The two BRs back to back look at the same CC, because BR does not change the CC. The negatives: -3, -8, -1 → 3.
R1 = x8000. After ADD R2, R1, #0, is the following BRn taken? Why?
Show answer
ADD R2, R1, #0 copies R1 to R2 and sets the CC. x8000 = 1000 0000 0000 0000, bit 15 = 1 → -32768, N = 1. BRn branches. The standard way to test the sign of a register is to write it to itself or to another register by adding #0.
TRAP x25 is located at address x3001. What value is written to R7, and what is loaded into the PC?
Show answer
TRAP writes the return address into R7: the incremented PC = x3002. Then trapvect8 = x25 is zero-extended and PC ← M[x0025] (the start of the service routine). The routine returns to x3002 with RET (HALT does not return, it stops the machine).
A loop starts with R1 = 10. Each iteration adds 1 to R2 (initially 0), then executes ADD R1, R1, #-3 and BRzp back to the loop start. What are R2 and R1 at the end?
Show answer
R1: 10 → 7 (P, loop back) → 4 (loop back) → 1 (loop back) → -2 (N, fall through). The body ran 4 times: R2 = 4, R1 = -2.
Verification: All BR/JMP/TRAP encodings, offsets and program traces were produced and run on our own LC-3 assembler + simulator (verify/SEN2201/lc3.mjs); loop counts were counted instruction by instruction (verify/SEN2201/08.mjs).