Skip to content

Zyrs9/LC3_VM-in_C

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LC-3 Virtual Machine & Complete Toolchain

A comprehensive, production-ready implementation of the Little Computer 3 (LC-3) architecture featuring a virtual machine, two-pass assembler, and interactive TUI debugger.


Table of Contents


Features

Virtual Machine (lc3vm)

  • Complete LC-3 ISA: All 15 instructions fully implemented
  • PSR (Processor Status Register): Privilege modes (Supervisor/User), priority levels, condition codes
  • Exception Handling: Privilege violations, illegal opcodes, access violations
  • Memory-Mapped I/O: Keyboard status/data registers (KBSR/KBDR)
  • Dual Stack Management: Separate User and Supervisor stack pointers
  • Cross-Platform: Linux, macOS, Windows support via platform abstraction layer

Assembler (lc3asm)

  • Two-Pass Assembly: Efficient symbol resolution with forward reference support
  • Full Instruction Set: All 15 LC-3 opcodes + pseudo-instructions (trap aliases)
  • Pseudo-Operations: .ORIG, .FILL, .STRINGZ, .BLKW, .END
  • Symbol Table: Hash table implementation for O(1) label lookup
  • Smart Parsing: Labels work with or without colons (:)
  • Error Reporting: Clear, line-numbered error messages
  • Verbose Mode: Optional symbol table output for debugging

TUI Debugger (lc3vm -d)

  • ncurses-Based Interface: Professional split-screen layout
  • Live Register Display: All registers including PC, PSR with privilege/condition flags
  • Disassembly View: Real-time instruction decoding with PC indicator
  • Memory Inspector: Hex dump with scrolling capability
  • Breakpoints: Set/clear at any address with visual markers
  • Step Execution: Single-step through instructions
  • Run Controls: Run until breakpoint or HALT

Quick Start

# Clone and build
git clone <repository-url>
cd LC3_VM-in_C
make

# Write assembly
cat > program.asm << 'EOF'
.ORIG x3000
    LEA R0, MSG
    PUTS
    HALT
MSG .STRINGZ "Hello, LC-3!"
.END
EOF

# Assemble
./lc3asm program.asm

# Run
./lc3vm program.obj

# Debug (interactive TUI)
./lc3vm -d program.obj

Output:

Hello, LC-3!
HALT

Installation

Prerequisites

Requirement Version Purpose
GCC/Clang 4.9+ C compiler
Make 3.81+ Build system
ncurses-dev 5.0+ TUI debugger

Ubuntu/Debian

sudo apt install build-essential libncurses-dev

macOS

brew install gcc ncurses

Build

make          # Build everything
make clean    # Clean build artifacts
make rebuild  # Clean + build

Build Outputs:

  • lc3vm - Virtual machine executable
  • lc3asm - Assembler executable
  • build/*.o - Object files

Usage

Virtual Machine

./lc3vm [options] <program.obj>

Options:

Flag Description Example
-v Verbose mode (log every instruction) ./lc3vm -v program.obj
-d TUI debugger mode ./lc3vm -d program.obj

Example (Verbose Mode):

$ ./lc3vm -v hello.obj
[PC:0x3000] 0xE005 | LEA R0, #5           | PSR:0x0002[S,Z] | R0:0x3006
[PC:0x3001] 0xF022 | PUTS                 | PSR:0x0002[S,Z] | R0:0x3006
Hello, World!
[PC:0x3002] 0xF025 | HALT                 | PSR:0x0002[S,Z]
HALT

Assembler

./lc3asm [options] <input.asm>

Options:

Flag Description Example
-v Verbose mode (show symbol table) ./lc3asm -v program.asm
-o <file> Specify output file ./lc3asm -o out.obj program.asm

Example Assembly File:

.ORIG x3000

; Calculate 5 + 3
    AND R0, R0, #0      ; Clear R0
    ADD R0, R0, #5      ; R0 = 5
    ADD R1, R0, #3      ; R1 = 8
    
    ; Print result (hardcoded for demo)
    LD  R0, ASCII_8
    OUT
    HALT

ASCII_8 .FILL x38       ; ASCII '8'

.END

Assemble & Run:

$ ./lc3asm -v example.asm

LC-3 Assembler
Input:  example.asm
Output: example.obj

Pass 1: Building symbol table...
  Origin: 0x3000
  Symbols: 1

=== Symbol Table (1 symbols) ===
Symbol               Address
----------------------------------------
ASCII_8              0x3007

Pass 2: Generating code...

✓ Assembly successful!
  example.obj created

$ ./lc3vm example.obj
8HALT

TUI Debugger

Launch:

./lc3vm -d program.obj

Interface Layout:

┌─ Registers ─────────────────┬─ Disassembly ─────────────────────────────┐
│ R0: 0x0005  R4: 0x0000      │ → 0x3000: 0x5020  AND R0, R0, #0          │
│ R1: 0x0008  R5: 0x0000      │   0x3001: 0x1025  ADD R0, R0, #5          │
│ R2: 0x0000  R6: 0xFE00      │ * 0x3002: 0x1243  ADD R1, R0, #3  [BP]    │
│ R3: 0x0000  R7: 0x0000      │   0x3003: 0x2004  LD R0, #4               │
│ PC: 0x3000  PSR:S,Z         │   0x3004: 0xF021  OUT                     │
├─ Memory ────────────────────┴───────────────────────────────────────────┤
│ 3000: 5020 1025 1243 2004 F021 F025 0000 0038  .%"C..!.%...8            │
│ 3008: 0000 0000 0000 0000 0000 0000 0000 0000  ........                 │
├─ Command ───────────────────────────────────────────────────────────────┤
│ s=step  r=run  b=break  m=mem  q=quit                                   │
└─────────────────────────────────────────────────────────────────────────┘

Key Bindings:

Key Action Description
s / Space Step Execute single instruction
r Run Run until breakpoint or HALT
b Breakpoint Toggle breakpoint at current PC
m Memory Set memory view to current PC
/ Scroll Scroll memory view up/down
q Quit Exit debugger

Visual Indicators:

  • Current PC position
  • * Breakpoint marker
  • S/U Privilege mode (Supervisor/User)
  • N/Z/P Condition codes (Negative/Zero/Positive)

Architecture

LC-3 Processor Model

┌──────────────────────────────────────────────────────────┐
│                     LC-3 Virtual Machine                 │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  ┌─────────────┐  ┌──────────────┐  ┌────────────────┐   │
│  │  Registers  │  │    Memory    │  │  I/O Devices   │   │
│  ├─────────────┤  ├──────────────┤  ├────────────────┤   │
│  │ R0 - R7     │  │ 65,536 words │  │ KBSR (xFE00)   │   │
│  │ PC          │  │ 16-bit each  │  │ KBDR (xFE02)   │   │
│  │ PSR         │  │              │  │ DSR  (xFE04)   │   │
│  │ IR          │  │              │  │ DDR  (xFE06)   │   │
│  │ USP/SSP     │  │              │  │                │   │
│  │ CC (N/Z/P)  │  │              │  │                │   │
│  └─────────────┘  └──────────────┘  └────────────────┘   │
│                                                          │
├──────────────────────────────────────────────────────────┤
│  Instruction Cycle: FETCH → DECODE → EVALUATE → EXECUTE  │
└──────────────────────────────────────────────────────────┘

Registers

Register Width Description
R0-R7 16-bit General-purpose registers
PC 16-bit Program Counter
PSR 16-bit Processor Status Register
IR 16-bit Instruction Register (internal)
USP 16-bit User Stack Pointer (saved)
SSP 16-bit Supervisor Stack Pointer (saved)

PSR (Processor Status Register)

 15   14-11   10-8    7-3     2     1     0
┌───┬───────┬──────┬───────┬─────┬─────┬─────┐
│ P │ (res) │ PRI  │ (res) │  N  │  Z  │  P  │
└───┴───────┴──────┴───────┴─────┴─────┴─────┘
  │             │              └─────────┘
  │             │              Condition Codes
  │             │
  │             Priority Level (0-7)
  │
  Privilege Mode (0=Supervisor, 1=User)

Memory Map

Address Range Size Description
0x0000-0x00FF 256 words Trap Vector Table
0x0100-0x01FF 256 words Interrupt Vector Table
0x0200-0x2FFF 11,776 words Operating System
0x3000-0xFDFF 52,224 words User Program Space
0xFE00-0xFE01 2 words Keyboard Status/Data (KBSR/KBDR)
0xFE02-0xFE03 2 words Display Status/Data (DSR/DDR)
0xFE04-0xFFFF 508 words Device Registers

Instruction Set

Arithmetic/Logic Operations

ADD   DR, SR1, SR2        ; DR = SR1 + SR2
ADD   DR, SR1, imm5       ; DR = SR1 + SEXT(imm5)
AND   DR, SR1, SR2        ; DR = SR1 & SR2
AND   DR, SR1, imm5       ; DR = SR1 & SEXT(imm5)
NOT   DR, SR              ; DR = ~SR

Data Movement

LD    DR, label           ; DR = mem[PC + SEXT(PCoffset9)]
LDI   DR, label           ; DR = mem[mem[PC + SEXT(PCoffset9)]]
LDR   DR, BaseR, offset6  ; DR = mem[BaseR + SEXT(offset6)]
LEA   DR, label           ; DR = PC + SEXT(PCoffset9)
ST    SR, label           ; mem[PC + SEXT(PCoffset9)] = SR
STI   SR, label           ; mem[mem[PC + SEXT(PCoffset9)]] = SR
STR   SR, BaseR, offset6  ; mem[BaseR + SEXT(offset6)] = SR

Control Flow

BR[nzp] label             ; Conditional branch
JMP   BaseR               ; PC = BaseR (RET is JMP R7)
JSR   label               ; R7 = PC; PC = PC + SEXT(PCoffset11)
JSRR  BaseR               ; R7 = PC; PC = BaseR

System/Trap

TRAP  trapvect8           ; Execute system call
RTI                       ; Return from interrupt (privileged)

; Trap Aliases:
GETC    ; TRAP x20 (Read character, no echo)
OUT     ; TRAP x21 (Write character in R0)
PUTS    ; TRAP x22 (Write string at address in R0)
IN      ; TRAP x23 (Prompt + read character)
PUTSP   ; TRAP x24 (Write packed string)
HALT    ; TRAP x25 (Stop execution)

LC-3 Reference Card


Test Suite

The project includes a comprehensive test suite covering all VM features:

Available Tests

Test Description Features Tested
hello.asm Print "Hello, World!" LEA, PUTS, .STRINGZ
test_basic.asm Arithmetic operations ADD, AND, immediate values
test_loop.asm Countdown 9→0 + "Blastoff!" Loops, BR, LD, ST, OUT
test_fibonacci.asm Calculate Fib(10) = 55 JSR, RET, subroutines
test_stack.asm Stack push/pop (1,2,3 → 3,2,1) LDR, STR, R6 manipulation
test_io.asm Interactive echo (quit on 'q') GETC, OUT, input buffering

Running Tests

# Assemble all tests
for f in test_*.asm hello.asm; do ./lc3asm "$f"; done

# Run test_loop
./lc3vm test_loop.obj

Expected Output (test_loop.obj):

9
8
7
6
5
4
3
2
1
0
Blastoff!
HALT

Debug a Test

./lc3vm -d test_fibonacci.obj

Press s repeatedly to step through Fibonacci calculation, or r to run to completion.


Project Structure

LC3_VM-in_C/
├── src/                    # Virtual Machine source
│   ├── lc3.h               # Core definitions, VM struct, prototypes
│   ├── main.c              # Entry point, argument parsing
│   ├── cpu.c               # Instruction execution (op_add, op_ld, etc.)
│   ├── memory.c            # Memory read/write with MMIO
│   ├── platform.c          # OS abstraction (terminal control)
│   ├── disasm.c            # Instruction disassembler
│   ├── utils.c             # Utility functions (sign extend, swap)
│   ├── privilege.c         # Privilege mode switching
│   ├── exception.c         # Exception handling (violations, illegal ops)
│   ├── tui.c               # TUI debugger implementation
│   └── tui.h               # TUI structures and prototypes
│
├── assembler/              # Assembler source
│   ├── asm.h               # Token types, symbol table, prototypes
│   ├── lc3asm.c            # Main assembler entry point
│   ├── lexer.c             # Tokenization (labels, opcodes, numbers)
│   ├── parser.c            # Two-pass logic
│   ├── symtab.c            # Symbol table (hash table)
│   ├── encoder.c           # Instruction encoding
│   ├── error.c             # Error reporting
│   └── util.c              # Number parsing, label resolution
│
├── build/                  # Build artifacts (*.o files)
├── Makefile                # Build system
├── README.md               # Project documentation
├── .gitignore              # Git ignore rules
│
└── Tests:
    ├── hello.asm
    ├── test_basic.asm
    ├── test_loop.asm
    ├── test_fibonacci.asm
    ├── test_stack.asm
    └── test_io.asm

Module Responsibilities

Virtual Machine (src/)

  • cpu.c (250 lines): Implements all 15 LC-3 instructions
  • memory.c (1.5K): Memory read/write with MMIO device registers
  • privilege.c (1.2K): Supervisor/User mode switching and stack management
  • exception.c (1.7K): Exception handling (push PSR/PC, vector table jump)
  • tui.c (10K): Interactive debugger with ncurses windows

Assembler (assembler/)

  • lexer.c (140 lines): Tokenizes assembly source into structured tokens
  • parser.c (200 lines): Two-pass assembly (symbol table build + code generation)
  • symtab.c (90 lines): Hash table for O(1) label lookup
  • encoder.c (500+ lines): Encodes all 15 instructions + pseudo-ops to machine code

Development

Building

# Full rebuild
make clean && make

# Build only VM
make lc3vm

# Build only assembler
make lc3asm

# Debug build (with symbols)
make debug

Makefile Targets

Target Description
all Build VM and assembler (default)
lc3vm Build VM only
lc3asm Build assembler only
clean Remove build artifacts
rebuild Clean + build all
debug Build with -g -DDEBUG

Compiler Flags

CFLAGS = -Wall -Wextra -std=c11 -O2
LDFLAGS = -lncurses  # For TUI

Technical Reference

Exception Handling Flow

1. Illegal Instruction Detected
2. Check Privilege Mode
3. Switch to Supervisor Mode
4. Save USP, restore SSP
5. Push PSR to supervisor stack
6. Push PC to supervisor stack
7. Load exception vector (0x0100-0x01FF)
8. Jump to handler

Exception Vectors:

  • 0x0100: Privilege Mode Violation
  • 0x0101: Illegal Opcode
  • 0x0102: Access Violation

Assembler Two-Pass Algorithm

Pass 1: Symbol Table Construction

1. address = 0
2. For each line:
   a. If label: add (label, address) to symtab
   b. If .ORIG: address = value
   c. If instruction: address += 1
   d. If .FILL: address += 1
   e. If .STRINGZ "str": address += len(str) + 1
   f. If .BLKW #n: address += n

Pass 2: Code Generation

1. For each line:
   a. If instruction: encode to 16-bit machine code
   b. Resolve labels from symtab (PC-relative offsets)
   c. Write to output file (big-endian)

PC-Relative Addressing

For BR, LD, LDI, LEA, ST, STI:

offset = target_address - (PC + 1)
// Must fit in 9-bit signed (-256 to +255)

Instruction Encoding Example

ADD R0, R1, #5

Encoding:

 15-12  11-9   8-6    5     4-0
┌─────┬─────┬─────┬─────┬───────┐
│0001 │ 000 │ 001 │  1  │ 00101 │
└─────┴─────┴─────┴─────┴───────┘
 ADD    DR=R0  SR1=R1 imm  imm5=5

Result: 0x1265

License

This project is open source and available under the MIT License.


Acknowledgments

Foundational Work

  • LC-3 Architecture: Designed by Yale N. Patt and Sanjay J. Patel
  • Educational Resource: Introduction to Computing Systems: From Bits and Gates to C and Beyond (2nd Edition)

Inspiration

Development

  • Built with C11, ncurses, and GNU Make
  • Cross-platform terminal handling inspired by various open-source projects

Built with ❤️ for systems programming education

Note: This is an educational project. The LC-3 architecture is designed for teaching computer architecture and assembly language programming. This implementation extends the basic VM with professional features like exception handling, TUI debugging, and a complete assembler toolchain.


Support & Contributing

Found a bug or want to contribute?

  1. Check existing issues
  2. Create a detailed bug report or feature request
  3. Submit a pull request with tests

**Happy Hacking! **

About

A robust, cross-platform implementation of the Little Computer 3 (LC-3) virtual machine written in C. This project simulates the instruction set architecture of the LC-3 computer, offering a functional environment for executing and debugging LC-3 assembly programs.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors