Skip to content
tnb24Public

About

a vm for lc3 built to learn ocaml

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lc3vm

lc3vm is a simulator for the LC-3 computer. It runs LC-3 programs on your machine. No hardware needed.

What is the LC-3?

The LC-3 (Little Computer 3) is a teaching computer. Many universities use it to teach systems programming. It is small and simple on purpose.

It has:

  • 16-bit words
  • 8 general-purpose registers (R0–R7)
  • 65,536 words of memory
  • 15 instructions
  • Keyboard and display I/O

You write a program, assemble it to a .obj file, and run it with lc3vm.

Why use lc3vm?

  • Run LC-3 programs without hardware
  • Use --trace to print every instruction as it runs — good for debugging
  • The source code is small (under 400 lines) and easy to follow

Install

You need OCaml and opam. Run these four commands:

opam switch create lc3 5.4.1
eval $(opam env)
opam install dune alcotest
dune build

Important

This project was built with OCaml 5.4.1. Earlier versions may not work.


Quick start

Step 1 — Build:

dune build

Step 2 — Generate the example program:

dune exec lc3vm-gen-hello
# Writes: temp/example_hello.obj

Step 3 — Run it:

dune exec lc3vm -- temp/example_hello.obj

Expected output:

Hello, LC-3!
--- HALT ---

Step 4 — Run with trace:

dune exec lc3vm -- --trace temp/example_hello.obj

Usage

lc3vm [--trace] <program.obj>
Flag Short What it does
--trace -t Print machine state and instruction before each step
--help -h Show usage

Input file format

The input must be a big-endian LC-3 object file. The first word is the load address. The words that follow are the program. Most LC-3 assemblers — such as lc3as and complx — produce this format.

Note

To generate .obj files from OCaml, see bin/gen_hello.ml. It writes the exact binary format step by step.

Trace output

Each line shows the full machine state, then the current instruction:

PC=x3000 [--P] R0=x0000 R1=x0000 ... R7=x0000 | LEA R0, #2
PC=x3001 [--P] R0=x3003 R1=x0000 ... R7=x0000 | TRAP x22

The [NZP] field shows the condition flags. N = negative, Z = zero, P = positive. A - means that flag is off.


Instruction set

lc3vm supports all 15 LC-3 opcodes:

Group Instructions
Arithmetic ADD (reg/imm), AND (reg/imm), NOT
Control flow BR (n/z/p), JMP, RET, JSR, JSRR
Load LD, LDR, LDI, LEA
Store ST, STR, STI
Trap GETC, OUT, PUTS, IN, PUTSP, HALT
System RTI

Warning

RTI is not supported in user mode. It raises a runtime error.


Project layout

lc3vm/
├── bin/
│   ├── main.ml          CLI: parses flags, calls loader and executor
│   └── gen_hello.ml     Writes a hello-world .obj to temp/
├── lib/
│   ├── machine.ml       CPU state: memory, registers, flags, I/O
│   ├── decode.ml        Converts raw 16-bit words into typed instructions
│   ├── exec.ml          Runs instructions; fetch-decode-execute loop
│   └── loader.ml        Reads .obj files into machine memory
└── test/
    └── test_lc3vm.ml    Unit tests (Alcotest)

The three library modules follow the same shape as a compiler:

  • Machine holds the data (like an AST)
  • Decode parses bits into typed instructions (like a parser)
  • Exec runs instructions and produces output (like code generation)

Each module enforces its own rule at the boundary: mask16 keeps all values in 16-bit range, and sign_extend converts offsets at decode time. Because of this, the executor never needs to check overflow.


Development

dune build          # Build everything
dune test --force   # Run all unit tests
dune build @check   # Type-check only — no linking (fast)

Tip

Use dune build @check for quick feedback while you edit. It takes under a second.

Tests are in test/test_lc3vm.ml. They cover:

  • Sign extension and 16-bit masking
  • Register and memory read/write
  • Condition codes
  • All instruction decoding variants
  • All executor behaviors
  • Loader error cases (empty file, truncated file)

Contributing

  1. Fork the repository
  2. Create a branch for your change
  3. Run dune test --force to check your work
  4. Open a pull request

Build environment and portability

  • Developed on Debian with OCaml 5.4.1 in an opam local switch
  • Requires the unix library (included with OCaml)
  • Uses Unix.select for non-blocking keyboard input

Warning

Unix.select does not work on Windows without a compatibility layer. Keyboard I/O (KBSR/KBDR reads) will not work correctly on Windows.

About

a vm for lc3 built to learn ocaml

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages