Input/Output & System Calls
Assembly Fundamentals
Chapter 8 · Input/Output & System Calls
HALT has quietly ended nearly every code example since assembly1-1, and assembly1-2's own tip-box promised it would eventually be explained in full. This chapter finally does that — and, along the way, introduces the single instruction behind every bit of keyboard and screen interaction an LC-3 program can perform: TRAP.
The TRAP Instruction — Calling Into the Operating System
TRAP behaves a lot like assembly1-7's own JSR — it saves a return address into R7 and transfers control elsewhere — but instead of jumping to a label you wrote yourself, it jumps to a pre-defined operating system service routine, looked up through a small table of addresses called the trap vector table.
The 8-bit trapvect8 field isn't an offset the way BR's or LD's fields were — it's an index into the trap vector table, a small region of memory holding the actual starting address of each OS routine. TRAP x25 doesn't jump to address x25 directly; it looks up whatever address is stored at vector table entry x25, and jumps there.
The OS/Hardware Boundary
This indirection exists for a real reason: your program never needs to know exactly how the keyboard or display hardware actually works, what memory-mapped registers they use, or how their timing quirks are handled. It only needs to know the trap number for "read a character" or "print a string." The operating system's own service routines — sitting behind that vector table — are the only code that actually deals with the hardware directly. TRAP is the boundary between "your program's logic" and "the OS/hardware layer that talks to the real world," and it's a boundary this course has been relying on silently since its very first example.
The Standard TRAP Routines
| Mnemonic | Vector | What it does |
|---|---|---|
| GETC | x20 | Reads one character from the keyboard into R0 — no echo to the screen |
| OUT | x21 | Writes the character currently in R0 to the display |
| PUTS | x22 | Writes a null-terminated string, starting at the address in R0, to the display |
| IN | x23 | Prints a prompt, reads one character (with echo this time), stores it in R0 |
| HALT | x25 | Stops the fetch-decode-execute cycle entirely |
TRAP x20 (GETC) to read a character and expecting to see it appear on screen automatically. It doesn't — GETC deliberately reads silently. If you want the typed character to actually show up, you have to explicitly follow it with TRAP x21 (OUT), or use TRAP x23 (IN) instead, which reads and echoes in one call.
HALT, In Full
Recall assembly1-2's own warning: "the CPU has no concept of 'done' — it fetches forever until an instruction like HALT explicitly stops it." Now the mechanism behind that is fully visible: HALT is simply TRAP x25. Its OS service routine doesn't return control back to your program the way GETC or OUT do — instead, it halts the fetch-decode-execute cycle at the hardware level, the one and only way execution actually stops.
A Small Working Example
TRAP x20 ; GETC — read one character into R0 (no echo) TRAP x21 ; OUT — echo it back manually LEA R0, MSG ; R0 <- address of the message (assembly1-3/assembly1-5's LEA) TRAP x22 ; PUTS — print the null-terminated string at that address TRAP x25 ; HALT MSG .STRINGZ "Thanks!"
This reads one character, echoes it manually (per this chapter's own warn-box), prints a short message using LEA to get the string's address into R0 exactly the way assembly1-5 introduced, and halts.
assembly1-7 established about nested calls applies here too: if a subroutine you wrote uses a TRAP call and R7 hasn't been saved on the stack first, that TRAP will silently overwrite the subroutine's own pending return address, the same bug from assembly1-7's own warn-box — just triggered by TRAP instead of a nested JSR.
Polling vs. Interrupts — A First Preview
Under the hood, the OS's GETC routine has to somehow know when a key has actually been pressed. The simple technique — and the one this course's examples rely on without you ever needing to think about it — is polling: repeatedly checking a status register in a tight loop until it indicates a key is ready. It works, but it wastes CPU cycles doing nothing but asking "is it ready yet?" over and over.
A more advanced alternative is interrupts: instead of the CPU repeatedly asking, the keyboard hardware itself signals the CPU the instant a key is pressed, letting the CPU do other useful work in between. This course doesn't go further than naming the distinction — interrupt-driven I/O is real, genuinely more efficient LC-3 territory, but it adds real complexity this Fundamentals course deliberately leaves for later, more advanced material, the same way assembly1-1 deferred real-hardware quirks to cpu8bit1 and the future x86-64 course.
Hands-On Exercises
Explain, using this chapter's own "OS/Hardware Boundary" section, why TRAP looks up an address through a vector table instead of just jumping directly to a fixed address the way BR or JSR does with a PC-relative offset.
📄 View solutionA learner writes TRAP x20 followed immediately by TRAP x25, expecting to see the character they typed appear on screen before the program halts. Using this chapter's own warn-box, explain what they'll actually observe, and fix their code.
A subroutine called via JSR itself calls TRAP x22 (PUTS) to print a status message before returning. Using this chapter's own tip-box and assembly1-7's calling convention, explain what safeguard this subroutine needs around its TRAP call, and why.
📄 View solutionChapter 8 Quick Reference
- TRAP — opcode 1111 + an 8-bit trapvect8 index into the trap vector table; saves a return address into R7 like JSR
- TRAP is the boundary between your program's logic and the OS/hardware layer that actually talks to the keyboard/display
- GETC (x20) — read a character, no echo · OUT (x21) — print R0's character · PUTS (x22) — print a null-terminated string · IN (x23) — read with prompt+echo · HALT (x25) — stop the fetch-decode-execute cycle for good
- GETC never echoes automatically — pair it with OUT, or use IN instead
- HALT is just TRAP x25 — this is the actual mechanism behind assembly1-2's "a program stops via an explicit HALT instruction"
- TRAP inherits JSR's R7-overwrite risk — save R7 first if a subroutine both calls another routine AND uses TRAP
- Polling (repeatedly checking a status register) vs. interrupts (hardware signals the CPU) — interrupts are real, more advanced LC-3 material deliberately left for later