A complete software implementation of the LC-3 (Little Computer 3) 16-bit educational virtual machine architecture written in C++. This emulator executes compiled LC-3 programs and includes two playable games: 2048 and Rogue.
- Overview
- Features
- Architecture
- Project Structure
- Getting Started
- Usage
- Technical Details
- Included Programs
- Author
The LC-3 Virtual Machine is an instruction set simulator that emulates the complete functionality of the LC-3 computer architecture. It provides a runtime environment for executing LC-3 binary programs with full support for:
- 15 LC-3 opcodes (arithmetic, logical, control flow, memory operations)
- Memory-mapped I/O for keyboard input
- Trap routines for system services (I/O operations)
- Real-time console interaction with unbuffered input
- Binary image loading with proper byte-order conversion
This project demonstrates:
- Low-level computer architecture implementation
- Instruction fetch-decode-execute cycle
- Memory management and addressing modes
- System-level programming with I/O handling
- Object-oriented design for hardware simulation
- Complete LC-3 ISA Implementation: All 15 opcodes fully functional
- 10 Registers: 8 general-purpose (R0-R7), Program Counter (PC), Condition Flags (COND)
- 128 KB Addressable Memory: 65,536 16-bit words
- Condition Flag System: Automatic updates for POSITIVE, ZERO, NEGATIVE
- Three Addressing Modes: Register, immediate, and memory addressing
- Binary Program Loader: Reads and loads LC-3 object files
- Memory-Mapped Keyboard: Real-time input via KBSR/KBDR registers (0xFE00/0xFE02)
- Console Output: Character and string output routines
- Unbuffered Input: Immediate keyboard response with disabled echo
- Trap Routines: 6 system calls for I/O operations
- Sign Extension: Proper 2's complement arithmetic for immediate values
- Byte-Order Conversion: Big-endian to little-endian swapping
- Interrupt Handling: Graceful Ctrl+C handling with cleanup
- Program Counter Management: Automatic PC increment and branching
The virtual machine consists of five primary modules:
┌──────────────────────────────────────────────┐
│ VirtualMachine │
│ (Main execution orchestrator) │
└───────────┬────────────────────────────────┘
│
┌─────────┴──────┬──────────┬──────────┬─────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌─────────────┐ ┌─────────┐ ┌────┐ ┌────┐
│ CPU │ │ ALU │ │MemoryIO │ │Trap│ │ OS │
│ │ │ │ │ │ │ │ │ │
│ 10 Regs│ │ 15 Opcodes │ │ Keyboard │ │ 6 │ │Con-│
│ 128KB │ │ Arithmetic │ │ Mapped │ │Traps│ │sole│
│ Flags │ │ Control Flow│ │ I/O │ │ I/O │ │I/O │
└────────┘ └─────────────┘ └─────────┘ └────┘ └────┘
- Registers: R0-R7 (general-purpose), R_PC (Program Counter), R_COND (Condition Flags)
- Memory: 65,536 × 16-bit words (128 KB)
- Program Start: 0x3000
- Functions:
UpdateFlags(): Sets condition flags based on register valuesReadImage(): Loads binary object files into memoryReadImageFile(): Handles file I/O with byte swapping
Implements all 15 LC-3 instructions:
| Category | Instructions |
|---|---|
| Arithmetic | ADD, AND, NOT |
| Control Flow | BR (conditional branch), JMP, JSR |
| Memory Load | LD, LDR, LDI, LEA |
| Memory Store | ST, STR, STI |
| System | TRAP, RTI (reserved) |
Key methods:
SignExtend(): Extends immediate values for 2's complementSwap16(): Converts byte order (big ↔ little endian)
- Manages memory-mapped I/O for keyboard input
- Registers:
MR_KBSR (0xFE00): Keyboard Status RegisterMR_KBDR (0xFE02): Keyboard Data Register
- Methods:
Read(): Memory read with automatic keyboard pollingWrite(): Standard memory write
Provides 6 system services:
| Trap Code | Name | Function |
|---|---|---|
| 0x20 | GETC | Read character (no echo) |
| 0x21 | OUT | Output single character |
| 0x22 | PUTS | Output null-terminated string |
| 0x23 | IN | Read character (with prompt) |
| 0x24 | PUTSP | Output packed byte string |
| 0x25 | HALT | Stop execution |
- Windows Console API integration
- Disables input buffering for real-time response
- Keyboard polling with timeout
- Signal handling for Ctrl+C interrupts
LC3-Virtual-Machine/
├── README.md # This file
├── LICENSE.txt # MIT License
├── .gitignore # Git ignore rules
├── .gitattributes # Git attributes
│
├── src/ # Source code
│ ├── main.cpp # Entry point
│ ├── VirtualMachine.cpp/h # Main VM orchestrator
│ ├── CPU.cpp/h # CPU implementation
│ ├── ArithmeticLogicUnit.cpp/h # ALU (15 opcodes)
│ ├── MemoryIO.cpp/h # Memory-mapped I/O
│ ├── Trap.cpp/h # Trap handler
│ └── OS.cpp/h # OS interface
│
├── programs/ # LC-3 executable programs
│ ├── 2048.obj # 2048 game (2.3 KB)
│ └── rogue.obj # Rogue game (762 bytes)
│
├── docs/ # Documentation
│ ├── ARCHITECTURE.md # System architecture details
│ ├── BUILD_INSTRUCTIONS.md # Build and compile guide
│ └── LC3_SPECIFICATION.md # LC-3 ISA reference
│
├── visual-studio/ # Visual Studio project files
│ ├── Virtual-Machine.sln # Solution file
│ ├── Virtual-Machine.vcxproj # Project file
│ └── Virtual-Machine.vcxproj.filters
│
└── build/ # Build output directory
Core Source Files:
- Program entry point
- Instantiates all VM components (CPU, ALU, MemoryIO, Trap, OS)
- Passes control to VirtualMachine orchestrator
- Main execution engine
- Validates command-line arguments
- Loads object files into memory
- Fetch-Decode-Execute Loop:
- Fetches instruction at PC
- Extracts opcode (bits 15-12)
- Dispatches to ALU or Trap handler
- Continues until HALT
- Register file (10 registers)
- Memory array (65,536 words)
- Flag update logic (POS/ZERO/NEG)
- Binary image loading with byte swapping
- All 15 LC-3 instruction implementations
- Sign extension for immediate values
- Endianness conversion utilities
- Memory read/write operations
- Keyboard status checking at 0xFE00
- Keyboard data retrieval at 0xFE02
- Trap instruction dispatcher
- 6 trap routine implementations
- String and character I/O
- Windows console mode manipulation
- Input buffering control
- Keyboard polling with timeout
- Interrupt signal handling
Software:
- Operating System: Windows (uses Windows API)
- Compiler: Visual Studio 2017 or later (or any C++11 compatible compiler)
- Standard Libraries:
<iostream>,<cstdint>,<cstdio>,<csignal>, Windows.h
Hardware:
- Any modern x86/x64 processor
- Minimal memory requirements (~10 MB)
-
Open Solution:
visual-studio/Virtual-Machine.sln -
Update Project Paths (if needed):
- Right-click project → Properties
- C/C++ → General → Additional Include Directories:
../src - Linker → General → Output Directory:
../build
-
Build:
- Build → Build Solution (Ctrl+Shift+B)
- Output:
build/Virtual-Machine.exe
# From project root directory
cl /EHsc /std:c++11 /I src src/*.cpp /Fe:build/vm.exeg++ -std=c++11 -I src src/*.cpp -o build/vm.exeExpected Build Output:
Compiling: main.cpp
Compiling: VirtualMachine.cpp
Compiling: CPU.cpp
Compiling: ArithmeticLogicUnit.cpp
Compiling: MemoryIO.cpp
Compiling: Trap.cpp
Compiling: OS.cpp
Linking: build/Virtual-Machine.exe
Build succeeded
The VM requires at least one LC-3 object file as a command-line argument:
# Run 2048 game
build\vm.exe programs\2048.obj
# Run Rogue game
build\vm.exe programs\rogue.obj
# Run custom program
build\vm.exe path\to\your_program.obj- W: Move up
- A: Move left
- S: Move down
- D: Move right
- Q: Quit game
Objective: Combine tiles to reach 2048
- W: Move up
- A: Move left
- S: Move down
- D: Move right
- Explore dungeons, avoid enemies, collect treasures
Visual Studio:
- Right-click project → Properties → Debugging
- Command Arguments:
programs\2048.obj - Press F5 to debug
Command Line:
# Add verbose output (requires code modification)
build\vm.exe programs\2048.obj --debugArchitecture Specifications:
- Word Size: 16 bits
- Address Space: 16-bit (65,536 locations)
- Register Count: 10 (8 general + PC + COND)
- Opcode Size: 4 bits (16 possible opcodes, 15 implemented)
┌────────┬──────────────────────┐
│ Opcode │ Operands │
│ [15:12]│ [11:0] │
└────────┴──────────────────────┘
4 bits 12 bits
| Opcode | Binary | Mnemonic | Description |
|---|---|---|---|
| 0000 | 0b0000 | BR | Conditional branch |
| 0001 | 0b0001 | ADD | Addition |
| 0010 | 0b0010 | LD | Load (PC-relative) |
| 0011 | 0b0011 | ST | Store (PC-relative) |
| 0100 | 0b0100 | JSR | Jump to subroutine |
| 0101 | 0b0101 | AND | Bitwise AND |
| 0110 | 0b0110 | LDR | Load register |
| 0111 | 0b0111 | STR | Store register |
| 1000 | 0b1000 | RTI | Return from interrupt (reserved) |
| 1001 | 0b1001 | NOT | Bitwise NOT |
| 1010 | 0b1010 | LDI | Load indirect |
| 1011 | 0b1011 | STI | Store indirect |
| 1100 | 0b1100 | JMP/RET | Jump |
| 1101 | 0b1101 | RES | Reserved |
| 1110 | 0b1110 | LEA | Load effective address |
| 1111 | 0b1111 | TRAP | System call |
0x0000 ┌──────────────────┐
│ Trap Vector Table │
0x0200 ├──────────────────┤
│ Interrupt Vector │
0x0300 ├──────────────────┤
│ OS/Supervisor │
0x3000 ├──────────────────┤ ← Program Start
│ │
│ User Programs │
│ │
0xFE00 ├──────────────────┤
│ Device Registers│
│ KBSR/KBDR │
0xFFFF └──────────────────┘
Stored in R_COND register:
- FL_POSITIVE (1 << 0): Result > 0
- FL_ZERO (1 << 1): Result == 0
- FL_NEGATIVE (1 << 2): Result < 0
Automatically updated after arithmetic/load operations.
-
Immediate Mode: Value embedded in instruction
ADD R0, R1, #5 ; R0 = R1 + 5 -
Register Mode: Operand in register
ADD R0, R1, R2 ; R0 = R1 + R2 -
PC-Relative: Offset from Program Counter
LD R0, LABEL ; R0 = memory[PC + offset] -
Indirect: Double indirection
LDI R0, LABEL ; R0 = memory[memory[PC + offset]] -
Base + Offset: Register base with offset
LDR R0, R1, #6 ; R0 = memory[R1 + 6]
Classic sliding tile puzzle game where you combine numbered tiles.
How to Play:
- Use WASD to slide tiles in all directions
- Tiles with the same number merge when they touch
- Create a tile with the number 2048 to win
- Game over when no moves are possible
Dungeon exploration game inspired by the classic Rogue.
How to Play:
- Navigate through procedurally generated dungeons
- Use WASD for movement
- Avoid enemies and collect items
- Reach the exit to progress
| Metric | Value |
|---|---|
| Memory Usage | ~130 KB (VM + programs) |
| Instruction Cycle Time | ~1 µs (depends on host CPU) |
| Startup Time | <100 ms |
| Max Program Size | 128 KB (65,536 words) |
| Supported Programs | Any valid LC-3 binary |
Potential improvements:
- Cross-platform support (Linux/macOS with ncurses)
- Debugger with breakpoints and step execution
- Disassembler for LC-3 object files
- LC-3 assembler integration
- Performance profiling and instruction tracing
- GUI with register/memory visualization
- Multi-program loading support
Mehmet Arslan
Systems programmer specializing in low-level software development, computer architecture, and virtual machine implementation.
Technical Skills Demonstrated:
- Computer architecture simulation
- Instruction set implementation
- Memory management and addressing
- Binary file parsing
- Real-time I/O handling
- Object-oriented design
- Systems-level C++ programming
- Windows API integration
GitHub: htmos6
This project is licensed under the MIT License - see LICENSE.txt for details.
Copyright © 2024 Mehmet Arslan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction...
- LC-3 Architecture: Created by Yale Patt and Sanjay Patel
- Reference Book: "Introduction to Computing Systems: From Bits and Gates to C and Beyond"
- Game Implementations: Community-contributed LC-3 programs
- Inspired by educational computer architecture courses
Contributions are welcome! Please feel free to submit pull requests or open issues for:
- Bug fixes
- New features
- Documentation improvements
- Additional LC-3 programs
- Cross-platform support
For questions, suggestions, or collaboration:
- GitHub: @htmos6
- Repository: Virtual-Machine
Last Updated: January 2026 Version: 1.0