Skip to main content
The emulator executes assembled Intel 4004 binaries with virtual keyboard and monitor devices. It runs programs in real-time with support for concurrent I/O operations.

Command line usage

The emulator requires a single argument:
./emulator <executable_file>
executable_file
string
required
Path to the assembled binary file (4096 bytes)

Terminal mode

The emulator automatically sets the terminal to cbreak mode (character-at-a-time input with no echo). This mode is restored when the emulator exits.
In cbreak mode, input is processed immediately without waiting for Enter, and typed characters are not echoed to the terminal.

Virtual devices

The emulator provides two virtual I/O devices connected via ROM ports:

Keyboard device

The virtual keyboard allows programs to read user input.
1

Port configuration

Connected to ROM ports 0-2:
  • Port 0: Character high nibble
  • Port 1: Character low nibble
  • Port 2: Character ready flag
2

Input processing

Characters are read from stdin and queued asynchronously
3

Character delivery

Each keystroke is split into two nibbles and made available to the CPU
The keyboard runs in a separate thread and uses a lock-free queue to prevent blocking the CPU.

Monitor device

The virtual monitor allows programs to output text.
1

Port configuration

Connected to ROM ports 3-5:
  • Port 3: Character high nibble
  • Port 4: Character low nibble
  • Port 5: Character ready flag
2

Character assembly

The monitor reads two nibbles from ports and combines them into a full byte
3

Display output

Assembled characters are written to stdout
The monitor runs in a separate thread, continuously checking for output from the program.

Threading architecture

The emulator uses a multi-threaded design:
Executes 4004 instructions continuously
Reads from keyboard ports
Writes to monitor ports
All threads run indefinitely. The emulator only stops when the process is killed or encounters an error.

CPU execution

The CPU executes instructions continuously via the single_step() method:
while (true) {
    try intel4004.single_step();
}
Each step:
  1. Fetches instruction from program memory
  2. Decodes opcode
  3. Executes operation
  4. Updates program counter

Memory layout

The emulator initializes an Intel4004 structure with:
  • PRAM: 4096 bytes of program memory (loaded from executable file)
  • DRAM: 16 banks × 4 chips × 4 registers × 16 nibbles
  • ROM ports: 16 ports for I/O operations
  • Index registers: 16 registers (4 bits each)
  • Program counter: 12-bit address

Port access synchronization

I/O port access is synchronized using an atomic lock:
1

Acquire lock

Thread attempts compare-and-swap from false to true
2

Spin wait

If lock is held, thread spins with hint for CPU optimization
3

Critical section

Update queue or keyboard state
4

Release lock

Store false to release other threads
This ensures keyboard input is not corrupted by concurrent access.

Cycle-accurate timing

The emulator implements cycle-accurate timing to match the original Intel 4004’s 740 kHz clock speed. Each instruction executes at the correct speed using busy-wait synchronization.
const clock_speed_hz: u64 = 740_000;
const ns_per_cycle: u64 = 1_000_000_000 / clock_speed_hz;
After executing each instruction, the emulator calculates how long the instruction should take based on its byte count and busy-waits if needed to match the real hardware timing.

Error handling

The emulator returns errors for:
  • Missing executable file argument
  • File not found or cannot be opened
  • Invalid binary format
  • Thread spawn failures

Memory nibble access

The emulator provides a helper function to read DRAM nibbles using a 12-bit address:
fn get_mem_nibble(intel4004: *Intel4004, n: u12) u4
The address is decomposed into:
  • Bits 8-11: Bank number (0-15)
  • Bits 4-7: Chip number (0-15) and register (0-3)
  • Bits 0-3: Character position (0-15)
This function could be used for debugging or memory inspection tools.

Runtime behavior

The emulator runs until interrupted:
  • Press Ctrl+C to stop execution
  • Terminal settings are restored on exit
  • All threads terminate when main thread exits
The emulator implements cycle-accurate timing at 740 kHz, matching the original Intel 4004 clock speed. Programs run at authentic speed.

Build docs developers (and LLMs) love