SLIDESHOW LC-3 Assembly Language
Overview of LC-3 Assembly Language and Tooling
Transition from Machine Code to Assembly Language:
Writing programs directly in hexadecimal or binary machine code requires manually calculating opcodes, register specifiers, and addressing offsets.
Assembly language introduces human-readable mnemonics and symbolic labels, allowing automated translation through an assembler.
The assembler performs background computations, automatically determining memory addresses, resolving relative offsets, and reserving storage blocks.
Core Components of Assembly Language:
Machine Instructions: Symbolic representations of hardware operations defined in the LC-3 Instruction Set Architecture (ISA), including
ADD,AND,NOT,LD,ST,LDR,STR,JSR,JSRR,BR, andRET.Pseudo-operations (Assembler Directives): Instructions directed to the assembler rather than the processor. Examples include
.ORIG,.FILL,.BLKW,.STRINGZ,.END, and.SUB.Labels: Symbolic names placed at the beginning of an instruction or directive to represent its specific memory location.
Comments: Prefaced by a semicolon (
;). All characters from the semicolon to the end of the line are ignored by the assembler and do not generate machine code.Literal Prefixes: Decimal immediate values are prefixed with
#(e.g.,ADD R0, R0, #1), while hexadecimal values are prefixed withx(e.g.,.ORIG x3000).
Assembler Execution Workflow:
Programs are written as source text files and saved with the
.asmfile extension.The source file is loaded into the LC-3 assembler tool.
Upon loading or initiating assembly, the assembler analyzes the text, performs validation, and generates a hexadecimal machine code object file (
.hexor.obj), a symbol table file (.sym), and an assembly listing file (.lst).Any syntax errors, unresolved symbols, or invalid offsets are displayed in the status pane of the assembler alongside the assembly timestamp.
Formatting Standards and Program Structure Rules
Strict Column Alignment and Whitespace Rules:
Labels: Must be placed flush against the far-left margin (Column 0). Labels must never be indented.
Instructions and Directives: Must be indented by exactly one tab stop from the left margin.
Pseudo-ops: Must be indented by exactly one tab stop from the left margin.
Operands: Must follow the instruction or directive separated by exactly one space.
Commas: Must immediately follow the operand being delimited with no preceding space, followed by exactly one space before the subsequent operand (e.g.,
ADD R1, R2, #0).Comments: May begin flush at the left margin on dedicated lines, indented with tabs, or placed after instruction operands.
Strict Execution and Program Structure Constraints:
Prohibition of Manual Hex Conversion: Code must not be converted to hexadecimal by hand; the automated assembler must be used to calculate offsets and generate binary representations.
Single Program Entry and Exit: Every complete LC-3 program must begin with exactly one
.ORIGdirective and contain exactly oneHALTinstruction.Reachability of HALT: The program must deterministically reach its single
HALTinstruction during simulation. Entering an infinite loop or terminating improperly results in failure.Subroutine Structure: Every subroutine must be declared with an initial identifying label and must exit through exactly one
RETinstruction.
Labels and Offset Resolution
Function of Assembly Labels:
A label instructs the assembler to record and remember a specific memory address within the symbol table.
Labels act as target destinations for conditional branching (
BR) and subroutine invocation (JSR).Labels function like variable identifiers when performing memory access operations (
LD,LDI,LEA,ST,STI).The assembler calculates the exact relative offset between the incremented Program Counter () and the target label address ( or ).
Prohibition of Numerical Offsets:
Specifying explicit numerical constants for relative offsets in PC-relative instructions is strictly prohibited.
Incorrect and Invalid Syntax:
LD R1, #5BRN #-7LEA R0, 5JSR 100Correct and Required Syntax (Using Labels):
LD R1, N48BRN TOPLEA R0, PROMPTJSR GETNUM
Assembler Directives (Pseudo-Ops)
Definition: Directives provide control information to the assembler during translation and are not part of the physical LC-3 hardware instruction set.
Catalog of Directives:
.ORIG:Specifies the starting memory address where the translated machine code is placed.
Example:
.ORIG x3000sets the initial address counter to..FILL:Allocates a single -bit memory location and initializes it with the specified value.
Example:
A .FILL 65initializes memory locationAwith the value()..BLKW(Block of Words):Allocates a contiguous block of memory consisting of consecutive -bit words.
Reserves the space, initializing words to
.Example:
C .BLKW 5reserves consecutive words..STRINGZ:Allocates contiguous memory words initialized with the ASCII values of a given character string.
Automatically appends an additional terminating null character (
) at the end of the character array..END:Signifies the physical end of the source program for the assembler.
Any text or instructions appearing after
.ENDare ignored..SUB:Declares a subroutine identifier to the assembler.
Must be placed at the top of the program, immediately beneath the
.ORIGdirective (e.g.,.SUB MULT).
Memory Allocation Example (.FILL and .BLKW):
Assembly Source Code:
text .orig x3000 LEA R6, C LD R0, A HALT A .fill 65 B .fill 66 C .BLKW 5 D .fill 68 .end Memory Layout and Machine Code Translation:
:EC04(LEA R6, C):2001(LD R0, A):F025(HALT)(LabelA):0041(Stores decimal )(LabelB):0042(Stores decimal )(LabelC):0000(First reserved word ofBLKW 5):0000(Second reserved word):0000(Third reserved word):0000(Fourth reserved word):0000(Fifth reserved word)(LabelD):0044(Stores decimal )

String Allocation Example (.STRINGZ):
Assembly Source Code:
text .ORIG x3000 LEA R0, HELLO PUTS HALT HELLO .STRINGZ "Hello, World!" A .fill 5 Memory Layout and Machine Code Translation:
:E002(LEA R0, HELLO):F022(PUTS):F025(HALT):0048(ASCII 'H'):0065(ASCII 'e'):006C(ASCII 'l'):006C(ASCII 'l'):006F(ASCII 'o'):002C(ASCII ','):0020(ASCII Space):0057(ASCII 'W'):006F(ASCII 'o'):0072(ASCII 'r'):006C(ASCII 'l'):0064(ASCII 'd'):0021(ASCII '!'):0000(Null terminator added by.STRINGZ):0005(.FILL 5)

System TRAP Routines vs. Assembly Mnemonics
TRAP Operations in LC-3:
TRAP instructions invoke operating system service routines handling hardware I/O and program lifecycle control.
Programmers must use the designated assembly mnemonics instead of direct
TRAP x##opcodes.
Mandatory Trap Mnemonics:
Use
GETCinstead ofTRAP x20(Reads a single character from the keyboard into; no character echo).Use
OUTinstead ofTRAP x21(Outputs the character into the console display).Use
PUTSinstead ofTRAP x22(Outputs a contiguous null-terminated string to the console, starting at the memory address held in).Use
HALTinstead ofTRAP x25(Halts program execution and prints a halt message to the console).
Explicitly Prohibited Trap Mnemonics:
IN(TRAP x23) must not be used.PUTSP(TRAP x24) must not be used.
Grading Penalty:
Using
TRAP x##in place of mnemonic keywords (GETC,OUT,PUTS,HALT) incurs an explicit penalty on quizzes and examinations.
The Two-Pass Assembler Process and Output Files
Mechanics of the Two-Pass Assembler:
Pass 1 (Address Calculation and Symbol Table Construction):
Scans the source file sequentially from
.ORIGto.END.Maintains an internal Location Counter (initialized to the address specified in
.ORIG).Determines the address for each instruction, accounting for words allocated by directives like
.BLKWand.STRINGZ.Identifies all labels and records their corresponding memory addresses in the Symbol Table file (
.sym).Pass 2 (Machine Code Generation):
Scans the source file a second time.
Translates symbolic opcodes and operands into binary/hexadecimal instructions.
Resolves relative addresses by subtracting the incremented instruction address from the target label address found in the Symbol Table.
Outputs the machine code file (
.hexor.obj) and the human-readable listing file (.lst).
Symbol Table Example (
assign5.sym):Symbol names mapped to assigned -bit addresses:
A:B:C:MULT:MTOP:SAVE_R1:SAVE_R2:
Assembly Listing File Structure (
assign5.lst):Displays the source line number, hexadecimal memory address, machine code bit pattern (-bit binary), and original source statement:
Line 2:
(3000) 0011000000000000->.ORIG x3000Line 3:
(3000) 2005 0010000000000101->LD R0 ALine 4:
(3001) 2205 0010001000000101->LD R1 BLine 5:
(3002) 4806 0100100000000110->JSR MULTLine 6:
(3003) F021 1111000000100001->TRAP x21Line 7:
(3004) 3003 0011000000000011->ST R0 CLine 8:
(3005) F025 1111000000100101->TRAP x25Line 9:
(3006) 0008 0000000000001000->A .FILL x0008Line 10:
(3007) 0009 0000000000001001->B .FILL x0009Line 11:
(3008) 0000 0000000000000000->C .FILL x0000Line 22:
(3009) 3209 0011001000001001->MULT ST R1 SAVE_R1Line 23:
(300A) 3409 0011010000001001->ST R2 SAVE_R2Line 26:
(300B) 1420 0001010000100000->ADD R2 R0 #0Line 27:
(300C) 5020 0101000000100000->AND R0 R0 #0Line 33:
(300D) 1040 0001000001000000->MTOP ADD R0 R1 R0Line 34:
(300E) 14BF 0001010010111111->ADD R2 R2 #-1Line 35:
(300F) 03FD 0000001111111101->BRP MTOPLine 38:
(3010) 2202 0010001000000010->LD R1 SAVE_R1Line 39:
(3011) 2402 0010010000000010->LD R2 SAVE_R2Line 40:
(3012) C1C0 1100000111000000->RETLine 51:
(3013) 0000 0000000000000000->SAVE_R1 .FILL x0000Line 52:
(3014) 0000 0000000000000000->SAVE_R2 .FILL x0000
Subroutine Linkage, Link Registers, and Save Conventions
Subroutine Invocation Instructions:
JSR Label(Jump to Subroutine): Evaluates relative offset, stores the incremented Program Counter into link register, and sets.JSRR BaseR(Jump to Subroutine Register): Stores the incremented Program Counter intoand sets.Usage Constraint:
JSRandJSRRmust only be used when returning to the calling sequence is intended. For arbitrary jumps where return is not expected, useBRNZPorJMP.Return Linkage: Every subroutine executes
RET(JMP R7), restoringto resume execution after the call site.
Passing Arguments and Return Values:
High-level languages frequently use the runtime stack in memory to pass parameters.
In baseline LC-3 assembly, parameters and return values are passed directly within general-purpose registers.
The Register Modification Problem:
Subroutines typically require intermediate registers to perform computations.
If a subroutine alters registers containing live variables required by the calling code, execution state becomes corrupted.
Caller: The code block that issues the
JSRinstruction.Callee: The subroutine code invoked by
JSR.
Callee-Save Convention:
The callee knows precisely which registers it uses for its local computations.
Therefore, the callee-save methodology is mandated: the subroutine itself must preserve and restore all registers it modifies.
Core Callee-Save Rules:
Save upon Entry: The very first operations in a subroutine must store the values of all registers modified by the subroutine into dedicated memory locations using
ST.Exception for Return Registers: Never save or restore registers explicitly used to pass return values back to the caller (restoring would overwrite the newly computed result with the stale original value).
Restore before Return: The very last operations prior to
RETmust reload the original values usingLDin reverse or matching order.Unique Labeling: Dedicated
.FILL 0memory locations used for register saving must have unique labels for each subroutine.
Caller-Save Exception: If the caller requires the value of a register that it knows will be overwritten by the callee's return output, the caller must preserve that register prior to calling the subroutine.
Handling Nested Subroutines and System Traps (The Overwrite Problem):
Calling another subroutine via
JSRor invoking anyTRAPinstruction (e.g.,OUT,PUTS,GETC) modifies link register.If a subroutine calls an inner subroutine or TRAP without preserving
, the outer subroutine's original return address is permanently lost, causing an infinite loop or crash upon executingRET.Rule: Any subroutine that invokes another subroutine or TRAP must save
at its start and restoreimmediately beforeRET.
Callee-Save Implementation Pattern:
.orig x3000
.sub TEST
MAIN
JSR TEST
HALT
TEST
ST R1, T_SR1 ; Save R1
ST R2, T_SR2 ; Save R2
ST R7, T_SR7 ; Save R7 (Required because OUT modifies R7)
; Subroutine body
OUT
BRNZP T_END
T_END
LD R1, T_SR1 ; Restore R1
LD R2, T_SR2 ; Restore R2
LD R7, T_SR7 ; Restore R7
RET ; Return to caller
T_SR1 .fill 0 ; Storage for R1
T_SR2 .fill 0 ; Storage for R2
T_SR7 .fill 0 ; Storage for R7
.end
```
# Control Flow and Structured Programming Constructs
* **Conditional Branching (If-Else Pattern):**
* High-level languages implement branching based on relational comparisons (e.g., `if (R1 > R2)`).
* LC-3 requires evaluating conditions using two's complement subtraction: ``.
* To negate ``, compute `` followed by `\text{ADD } R_3, R_3, #1`.
* Add `` and `` (``), which updates condition codes ().

* **If-Else Assembly Code:**
text .orig x3000 NOT R3, R2 ADD R3, R3, #1 ADD R3, R1, R3 ; R3 = R1 - R2 BRNZ Block2 ; If R1 <= R2, branch to Block 2
BLOCK1 ; High-level: R1 = R1 - R2 NOT R2, R2 ADD R2, R2, #1 ADD R1, R2, R1 BRNZP IF_END_1 ; Skip Block 2
Block2 ; High-level: R1 = R2 - R1 NOT R1, R1 ADD R1, R1, #1 ADD R1, R2, R1
IF_END_1 HALT .end ```
Loop Construction with Labels:
Iterative loops maintain a counter or test condition evaluated on each iteration.
Unlabeled offset loops (e.g.,
BRP #-3) are prohibited because code modifications alter line distances, breaking the branch target.Correct Labeled Loop Pattern:
text AND R0, R0, #0 AND R1, R1, #0 ADD R0, R0, #6 ; Initialize counter to 6 TOP ADD R1, R1, R0 ; Accumulate sum ADD R0, R0, #-1 ; Decrement counter BRP TOP ; Branch while counter > 0 HALT
Short-Circuit Evaluation of Boolean Logic:
Logical AND (
A > B && A > C):Involves positional, cascading checks. Every condition must pass; failure of any test skips immediately to the end.
Source Code:
; Assume R0 = A, R1 = B, R2 = C NOT R3, R0 ADD R3, R3, #1 ; R3 = -A ADD R4, R3, R1 ; R4 = B - A BRZP ENDIF ; If A <= B, condition fails; skip block ADD R4, R3, R2 ; R4 = C - A BRZP ENDIF ; If A <= C, condition fails; skip block OUT ; Executes only if both tests pass ENDIF HALT ``` * **Generalized Rule for Multiple ANDs:** For `if (test1 && test2 && ... && testn) { BLOCK }`, if any individual test fails, branch immediately to `END`. The block is placed after the final test. * **Logical OR (`A > B || A > C`):** * Passes if any test succeeds. Success jumps immediately to the executed code block. * Source Code (Pattern 1):text ; Assume R0 = A, R1 = B, R2 = C NOT R3, R0 ADD R3, R3, #1 ; R3 = -A ADD R4, R3, R1 ; R4 = B - A BRN PRINTA ; If A > B, success; execute block
ADD R4, R3, R2 ; R4 = C - A BRN PRINTA ; If A > C, success; execute block BRNZP ENDIF ; Both failed; skip block
PRINTA OUT ENDIF HALT ```
Source Code (Pattern 2 - Negating the Terminal Test):
text NOT R3, R0 ADD R3, R3, #1 ADD R4, R3, R1 BRN PRINTA ; Test 1 passes: execute block ADD R4, R3, R2 BRZP ENDIF ; Test 2 fails: skip block PRINTA OUT ENDIF HALT Generalized Rule for Multiple ORs: For
if (test1 || test2 || ... || testn) { BLOCK }, if any test succeeds, branch immediately toCODE. Aftertestn, place an unconditional jump (BRNZP END) to skipCODEif all tests fail.Mixed Logic (
test1 && test2 || test3):If
test1fails, skip to evaluatetest3.If
test2fails, skip to evaluatetest3.If both
test1andtest2pass, jump unconditionally to executeCODE.If reaching label
T3, evaluatetest3; if it fails, jump toENDIF.Execute
CODEblock and exit through labelENDIF.
Rules for Clearing Registers
When NOT to Clear a Register:
Before Overwriting Operations: Clearing a register immediately prior to an operation that completely overwrites it is redundant.
Before
GETC: The character input is loaded directly into, overwriting its previous contents.Before
LD,LDR,LDI: The memory load directly writes a -bit word to the destination register, overwriting previous contents.Before Non-Accumulative Additions: In statements like
ADD R1, R2, #5() orADD R1, R2, R3(), the result overwrites. Prior clearing ofis redundant.
When Clearing a Register IS Required:
Accumulators in Iterative Loops: When summing values into a register (e.g.,
ADD R1, R1, R2within a loop), the register must be initialized to zero before the loop starts:
AND R1, R1, #0 ; Initialize accumulator to 0 TOP ADD R1, R1, R2 BRN TOP ``` * **Constructing Constants via Immediate Addition:** To place a known constant into a register using immediate addition, the register must be cleared first:text AND R0, R0, #0 ; Clear R0 ADD R0, R0, #10 ; R0 is now 10 ```
Diagnostic Criterion:
If an operation adds a register to itself (
), evaluate whetherneeds clearing based on whether it is an accumulator.If an operation writes the sum of two distinct source registers into a destination (
), clearingis never necessary.
Debugging and Simulator Commands
Interactive Simulator Tools (
lc3simCommand Interface):step: Executes exactly one instruction. If the instruction is a subroutine call (JSRorJSRR), it enters into the subroutine code.next: Executes exactly one instruction. If the instruction isJSRorJSRR, it runs the entire subroutine atomically and stops at the subsequent instruction in the calling code.finish: If currently executing within a subroutine, runs the remaining subroutine instructions until it returns viaRET, then pauses.continue: Resumes continuous execution until the program reaches a breakpoint orHALT.printregs: Displays the contents of all general-purpose registers (through), the Program Counter (), and condition codes ().dump: Displays memory addresses and their contents across specified memory ranges.help: Prints the complete list of available simulator commands.
Grading Criteria and Common Coding Errors
Point Deduction Violations:
Redundant Register Clearing: Clearing a register prior to
LD,GETC, or non-accumulating instructions.Formatting Non-Compliance:
Placing labels anywhere other than the far-left margin.
Failing to indent instructions and directives with a single tab.
Separating operands with formatting other than a single space.
Adding a space before a comma, or omitting the space following a comma.
Missing Core Directives: Omitting
.ORIGorHALTin an LC-3 program.Malformed Subroutines: Omitting an entry label or omitting
RETin a subroutine.Unnecessary Arithmetic for Negative Constants: Allocating a positive constant using
.FILLand then executingNOT/ADD #1in code to negate it. Negative values must be stored directly in.FILL(e.g.,.FILL -15).Multiple HALT Instructions: Including more than one
HALTin a program file.Multiple RET Instructions: Including more than one
RETwithin a single subroutine.Using Numerical Offsets: Writing numerical offsets instead of assembly labels for PC-relative operations.
Excessive Immediate Additions: Using more than two consecutive
ADDinstructions to build a constant value. If a constant cannot be built in one or two additions, it must be loaded using.FILLandLD.Redundant Constant Reloading: Re-executing
LDinstructions to fetch constants inside loop bodies instead of loading them once prior to loop entry.Direct TRAP Calls: Using
TRAP x##opcodes rather than designated assembly mnemonics (GETC,OUT,PUTS,HALT).Excessively Complex or External Code: Implementing solutions using unintroduced syntax or copied internet code leads to zero credit.