Registers & the Register File
Assembly Fundamentals
Chapter 4 · Registers & the Register File
R0 through R7 have already shown up quietly in every code example since assembly1-1 — as operands in ADD, as a base in LDR, as a destination for LD. This chapter finally makes them the main subject: what the register file actually is, the two special-purpose pieces of CPU state that sit alongside it (the PC and the condition codes), and — the central question this chapter exists to answer — why LC-3 has exactly eight of them.
The Register File — R0 Through R7
LC-3 has eight general-purpose registers, R0 through R7, each holding exactly one 16-bit word — the same width as a memory location (per assembly1-3). "General-purpose" means exactly what it says: any register can hold a number, a memory address, or a character code, with nothing about the hardware restricting what a given register is "for." Unlike some historical designs with one fixed accumulator register that every arithmetic operation is forced to use, any of R0–R7 can serve as a source or destination for any instruction that takes register operands.
The Program Counter — Not Part of the Register File
assembly1-2 already introduced the PC as the register holding the address of the next instruction to fetch, incremented automatically during every Fetch phase. It's worth being explicit here: the PC is not one of R0–R7 — it's a separate, special-purpose register that ordinary instructions like ADD or AND can never read or write directly. The only way to influence it is through the branch and subroutine-call instructions assembly1-6 and assembly1-7 cover.
Condition Codes — N, Z, P
Alongside the register file sits one more small piece of state: three single-bit flags called N (negative), Z (zero), and P (positive). Exactly one of the three is set to 1 at any given time — never zero of them, never more than one.
Any instruction that writes a result into a register — ADD, AND, NOT, and the register-loading instructions from assembly1-3 (LD, LDR, LDI, LEA) — automatically sets N/Z/P based on that result: N if the result was negative, Z if it was exactly zero, P if it was positive. Instructions that don't write to a register — ST-family instructions, for example — never touch the condition codes at all.
AND R1, R1, #0 ; R1 becomes 0 -> Z is set (N and P cleared) ADD R1, R1, #5 ; R1 becomes 5 -> P is set (N and Z cleared) ADD R1, R1, #-9 ; R1 becomes -4 -> N is set (Z and P cleared)
Condition codes exist for exactly one reason: so a later instruction can make a decision based on the result of an earlier one. assembly1-6's branch instructions read N/Z/P directly to implement everything from if statements to loops — this chapter is what makes that possible.
LEA just to compute an address. If you need to branch on a result, the branch has to come immediately after the instruction that produced it — anything else that touches a register in between will silently overwrite the flags you meant to check.
Load/Store Architecture — Why So Few Registers?
assembly1-1 named the 6502's tiny register set as a direct consequence of 1975 manufacturing costs — fewer transistors, cheaper chip. LC-3's own small register file has a completely different origin: it's a deliberate consequence of a design choice called load/store architecture.
In a load/store design, ALU operations like ADD and AND are only ever allowed to work on values already sitting in registers — never directly on a value in memory. If you want to add something to a value stored in memory, you have to explicitly LD it into a register first, operate on it there, and ST it back out if you need the result saved. There's no shortcut instruction that reaches into memory and modifies it in one step.
That constraint is exactly what keeps the register file small and still practical: because every actual computation happens in registers, and memory is only ever touched deliberately through LD/ST-family instructions, the CPU never needs dozens of registers to avoid constantly re-loading values — a handful is enough to hold whatever a given calculation is actively working with. Eight also isn't an arbitrary round number: per assembly1-3's own bit-budget reasoning, a register operand only needs 3 bits to select one of 8 registers (23 = 8) — comfortably fitting several register fields into a single 16-bit instruction alongside an opcode.
| Question | LC-3 (load/store, this course) | 6502 (assembly1-1's own example) |
|---|---|---|
| Why so few registers? | Deliberate design principle — ALU ops never touch memory directly, so a small register file is genuinely sufficient | 1975 manufacturing cost — fewer transistors meant a cheaper, more competitive chip |
| Can an instruction operate on memory directly? | Never — must LD into a register first | Covered in full when cpu8bit1 reaches the 6502's own addressing modes |
cpu8bit1-3 asks "why does the 6502 have so few registers?", you'll already know the shape of that question from this chapter — but the 6502's answer is about 1975 economics, not clean architectural principle. Comparing the two answers directly is exactly the kind of "real, nameable trade-off" reasoning assembly1-1 promised this course would build toward.
Hands-On Exercises
Using this chapter's own bit-budget explanation, explain why LC-3 has exactly 8 general-purpose registers rather than, say, 16 or 4. Tie your answer to the number of bits needed to select a register.
📄 View solutionTrace the condition codes through this sequence, stating which flag (N, Z, or P) is set after each line: AND R2, R2, #0, then ADD R2, R2, #7, then LEA R3, LABEL (assume LABEL's computed address, as a signed value, is negative), then ADD R2, R2, #-7. Which instruction's result do the final condition codes actually reflect?
Explain, using this chapter's own load/store architecture section, why ADD R1, VALUE, #1 (attempting to add 1 directly to a value in memory) is not a valid LC-3 instruction, and what the correct two-instruction sequence would look like instead.
Chapter 4 Quick Reference
- R0–R7 — 8 general-purpose 16-bit registers, no register has a fixed, hardwired purpose
- The PC is a separate special-purpose register, not part of R0–R7, only modifiable via branch/subroutine-call instructions
- N/Z/P condition codes — exactly one set at a time, automatically updated by any instruction that writes a register result
- Condition codes reflect only the most recent register-writing instruction — anything in between silently overwrites them
- Load/store architecture — ALU instructions only ever operate on registers; memory is touched only via explicit LD/ST
- LC-3's small register file is a deliberate design choice — contrast with the 6502's own manufacturing-cost-driven minimalism (assembly1-1)
- 8 registers = exactly 3 bits to select one, fitting cleanly into a 16-bit instruction (per assembly1-3's bit-budget reasoning)