Common Patterns & Idioms
The reusable building blocks you'll combine to solve any LC-3 problem.
Why Patterns Matter
You know what ADD, AND, NOT, LD, ST, LEA, LDR, STR, and BR do individually. But when you face a problem like "check if a string is a palindrome," it's hard to know where to start.
The secret: every LC-3 program is built from a small set of patterns. Once you recognize them, complex programs become a matter of snapping pieces together.
Pattern 1: Clear and Initialize a Register
Almost every program starts by clearing registers and loading initial values.
; Clear a register to 0
AND R0, R0, #0 ; R0 = 0
; Set to a small value (-16 to 15)
AND R1, R1, #0
ADD R1, R1, #7 ; R1 = 7
; Set to a larger value (build it up)
AND R2, R2, #0
ADD R2, R2, #15 ; 15
ADD R2, R2, #15 ; 30
ADD R2, R2, #2 ; 32 (ASCII space)
; Load a value from memory (any size)
LD R3, MY_VALUE ; R3 = whatever is at MY_VALUEADD #imm for values -16 to 15. For larger constants, either chain multiple ADDs or store the value in memory with .FILL and use LD. Memory is cleaner for values like ASCII codes or addresses.Pattern 2: Copy a Register
You often need the same value in two places. There is no MOV instruction — use ADD with 0.
; Copy R0 into R1
ADD R1, R0, #0 ; R1 = R0 + 0 = R0Pattern 3: Compare Two Values
This is the most important pattern for problem solving. The LC-3 has no CMP instruction — you compare by subtracting and checking the condition codes.
; Are R0 and R1 equal?
; Strategy: compute R0 - R1, check if zero
NOT R2, R1
ADD R2, R2, #1 ; R2 = -R1
ADD R2, R0, R2 ; R2 = R0 - R1
BRz THEY_ARE_EQUAL ; branch if R0 == R1
; ... they are not equal ...
; Is R0 > R1?
; Same subtraction, but check positive
NOT R2, R1
ADD R2, R2, #1
ADD R2, R0, R2 ; R2 = R0 - R1
BRp R0_IS_GREATER ; branch if R0 > R1
; Is R0 a specific value (e.g., ASCII space = 32)?
; Strategy: subtract the constant and check zero
LD R2, NEG_SPACE ; R2 = -32
ADD R2, R0, R2 ; R2 = R0 - 32
BRz ITS_A_SPACE ; branch if R0 == 32
; ...
NEG_SPACE .FILL #-32Subtraction destroys a value! NOT R1, R1 / ADD R1, R1, #1 overwrites R1. If you need R1 later, do the subtraction into a temporary register instead, or save R1 first.
Pattern 4: Walk Through a String or Array
Use a pointer register and LDR to visit each element.
; Walk a null-terminated string
LEA R1, STR ; R1 = pointer to first character
LOOP
LDR R0, R1, #0 ; R0 = current character
BRz DONE ; null terminator? stop
; ... do something with R0 ...
ADD R1, R1, #1 ; advance pointer
BR LOOP
DONEPattern 5: Two-Pointer Technique
Many string/array problems use two pointers that move toward each other. This is exactly what palindrome, reverse, and similar problems need.
; R1 = left pointer (start of data)
; R3 = right pointer (end of data)
;
; Loop while R1 < R3:
TWO_PTR_LOOP
NOT R4, R1
ADD R4, R4, #1
ADD R4, R3, R4 ; R4 = R3 - R1
BRnz DONE ; if R3 <= R1, pointers crossed → stop
LDR R0, R1, #0 ; load left element
LDR R2, R3, #0 ; load right element
; ... compare or swap R0 and R2 ...
ADD R1, R1, #1 ; left moves right
ADD R3, R3, #-1 ; right moves left
BR TWO_PTR_LOOP
DONEPattern 6: Find the End of a String
Before using a right pointer, you need to find where the string ends.
; R1 = start of string
; After this, R3 = pointer to last character
ADD R3, R1, #0 ; R3 starts at beginning
FIND_END
LDR R0, R3, #0 ; load character
BRz FOUND_END ; null? we passed the end
ADD R3, R3, #1 ; keep going
BR FIND_END
FOUND_END
ADD R3, R3, #-1 ; back up to last real characterPattern 7: Skip/Filter Characters
Need to skip spaces, ignore certain characters, or find the next vowel? Load, check, and conditionally advance.
; Skip spaces going forward (R1 = pointer)
SKIP_FWD
LDR R0, R1, #0 ; load character
LD R4, NEG_SPACE ; R4 = -32
ADD R4, R0, R4 ; R4 = char - 32
BRnp NOT_SPACE_FWD ; not a space? continue
ADD R1, R1, #1 ; skip it, advance
BR SKIP_FWD
NOT_SPACE_FWD
; R1 now points to a non-space character
; Skip spaces going backward (R3 = pointer)
SKIP_BACK
LDR R0, R3, #0
LD R4, NEG_SPACE
ADD R4, R0, R4
BRnp NOT_SPACE_BACK
ADD R3, R3, #-1
BR SKIP_BACK
NOT_SPACE_BACK
NEG_SPACE .FILL #-32Pattern 8: Store a Result to Memory
Many projects ask you to put a result at a specific address (like x6000).
; Store R0 to a nearby label
ST R0, RESULT
RESULT .FILL #0
; Store R0 to a far-away address (like x6000)
STI R0, RESULT_PTR
RESULT_PTR .FILL x6000ST when the target is close (within ±256 of the PC). Use STI when the target is far away — STI follows a pointer, so you store the far address in a nearby .FILL.Quick Reference: "I need to ___"
| I need to... | Use this pattern |
| Set a register to 0 | AND Rx, Rx, #0 |
| Copy a register | ADD Rdst, Rsrc, #0 |
| Check if two values are equal | Subtract, then BRz |
| Check if A > B | Subtract (A - B), then BRp |
| Check if a character is a space | Subtract 32, then BRz |
| Walk through a string | Pointer + LDR + ADD ptr, ptr, #1 |
| Find end of string | Walk until LDR gives null (BRz) |
| Use two pointers | One at start, one at end, move inward |
| Store result at far address | STI with a .FILL pointer |
| Negate a value | NOT then ADD #1 |
| Build a constant > 15 | Chain ADD #15 or use .FILL + LD |
You need to check if the character in R0 is the letter "A" (ASCII 65). What's the best approach?
You have two pointers R1 (left) and R3 (right). How do you check if R1 has passed R3 (i.e., R1 >= R3)?