A tiny CPU simulator written in Python
An educational 8-bit CPU simulator with interactive visualization
Tiny8 is a lightweight and educational toolkit for exploring the fundamentals of computer architecture through hands-on assembly programming and real-time visualization. Designed for learning and experimentation, it features an AVR-inspired 8-bit CPU with 32 registers, a rich instruction set, and powerful debugging tools — all with zero heavy dependencies.
pip install tiny8Create fibonacci.asm:
…Run it:
tiny8 fibonacci.asm # Interactive debugger
tiny8 fibonacci.asm -m ani -o fibonacci.gif # Generate animationfrom tiny8 import CPU, assemble_file
asm = assemble_file("fibonacci.asm")
cpu = CPU()
cpu.load_program(asm)
cpu.run(max_steps=1000)
print(f"Result: R17 = {cpu.read_reg(17)}") # Final Fibonacci numberFor Students — Write assembly, see immediate results with visual feedback. Understand how each instruction affects CPU state without abstractions.
For Educators — Interactive demonstrations, easy assignment creation, and generate animations for lectures.
For Hobbyists — Rapid algorithm prototyping at the hardware level with minimal overhead and an extensible, readable codebase.
The terminal-based debugger provides powerful navigation and inspection capabilities.
l / h or → / ← — Step forward/backwardw / b — Jump ±10 steps0 / $ — Jump to first/last stepSpace — Play/pause auto-execution[ / ] — Decrease/increase playback speedr — Toggle register display (all/changed only)M — Toggle memory display (all/non-zero only)= — Show detailed step informationj / k — Scroll memory view up/down:):123 — Jump to step 123:+50 / :-20 — Relative jumps:/ldi — Search forward for instruction "ldi":?add — Search backward for "add":@0x100 — Jump to PC address 0x100:r10 — Find next change to register R10:r10=42 — Find where R10 equals 42:m100 — Find next change to memory[100]:fZ — Find next change to flag Zma — Set mark 'a' at current step'a — Jump to mark 'a'/ — Show help screenq or ESC — QuitThe examples/ directory contains programs demonstrating key concepts:
| Example | Description |
|---|---|
fibonacci.asm |
Fibonacci sequence using registers |
bubblesort.asm |
Sorting algorithm with memory visualization |
factorial.asm |
Recursive factorial calculation |
find_max.asm |
Finding maximum value in array |
is_prime.asm |
Prime number checking algorithm |
gcd.asm |
Greatest common divisor (Euclidean algorithm) |
Sort 32 bytes in memory:
tiny8 examples/bubblesort.asm -ms 0x60 -me 0x80 # Watch live
tiny8 examples/bubblesort.asm -m ani -o sort.gif -ms 0x60 -me 0x80 # Create GIFfrom tiny8 import CPU, assemble_file
cpu = CPU()
cpu.load_program(assemble_file("examples/bubblesort.asm"))
cpu.run()
print("Sorted:", [cpu.read_ram(i) for i in range(0x60, 0x80)])tiny8 FILE [OPTIONS]| Option | Description |
|---|---|
-m, --mode {cli,ani} |
Visualization mode: cli for interactive debugger (default), ani for animation |
-v, --version |
Show version and exit |
--max-steps N |
Maximum execution steps (default: 15000) |
| Option | Description |
|---|---|
-ms, --mem-start ADDR |
Starting memory address (decimal or 0xHEX, default: 0x00) |
-me, --mem-end ADDR |
Ending memory address (decimal or 0xHEX, default: 0xFF) |
| Option | Description |
|---|---|
-d, --delay SEC |
Initial playback delay in seconds (default: 0.15) |
| Option | Description |
|---|---|
-o, --output FILE |
Output filename (.gif, .mp4, .png) |
-f, --fps FPS |
Frames per second (default: 60) |
-i, --interval MS |
Update interval in milliseconds (default: 1) |
-pe, --plot-every N |
Update plot every N steps (default: 100, higher = faster) |
Windows: CLI debugger requires WSL or
windows-curses. Animation works natively.
Tiny8 implements an AVR-inspired instruction set with 62 instructions organized into logical categories. All mnemonics are case-insensitive. Registers are specified as R0-R31, immediates support decimal, hex ($FF or 0xFF), and binary (0b11111111) notation.
| Instruction | Description | Example |
|---|---|---|
LDI Rd, K |
Load 8-bit immediate into register | ldi r16, 42 |
MOV Rd, Rr |
Copy register to register | mov r17, r16 |
LD Rd, Rr |
Load from RAM at address in Rr | ld r18, r16 |
ST Rr, Rs |
Store Rs to RAM at address in Rr | st r16, r18 |
IN Rd, port |
Read from I/O port into register | in r16, 0x3F |
OUT port, Rr |
Write register to I/O port | out 0x3F, r16 |
PUSH Rr |
Push register onto stack | push r16 |
POP Rd |
Pop from stack into register | pop r16 |
| Instruction | Description | Example |
|---|---|---|
ADD Rd, Rr |
Add registers | add r16, r17 |
ADC Rd, Rr |
Add with carry | adc r16, r17 |
SUB Rd, Rr |
Subtract registers | sub r16, r17 |
SUBI Rd, K |
Subtract immediate | subi r16, 10 |
SBC Rd, Rr |
Subtract with carry | sbc r16, r17 |
SBCI Rd, K |
Subtract immediate with carry | sbci r16, 5 |
INC Rd |
Increment register | inc r16 |
DEC Rd |
Decrement register | dec r16 |
MUL Rd, Rr |
Multiply (result in Rd:Rd+1) | mul r16, r17 |
DIV Rd, Rr |
Divide (quotient→Rd, remainder→Rd+1) | div r16, r17 |
NEG Rd |
Two's complement negation | neg r16 |
ADIW Rd, K |
Add immediate to word (16-bit) | adiw r24, 1 |
SBIW Rd, K |
Subtract immediate from word | sbiw r24, 1 |
| Instruction | Description | Example |
|---|---|---|
AND Rd, Rr |
Logical AND | and r16, r17 |
ANDI Rd, K |
AND with immediate | andi r16, 0x0F |
OR Rd, Rr |
Logical OR | or r16, r17 |
ORI Rd, K |
OR with immediate | ori r16, 0x80 |
EOR Rd, Rr |
Exclusive OR | eor r16, r17 |
EORI Rd, K |
XOR with immediate | eori r16, 0xFF |
COM Rd |
One's complement | com r16 |
CLR Rd |
Clear register (XOR with self) | clr r16 |
SER Rd |
Set register to 0xFF | ser r16 |
TST Rd |
Test for zero or negative | tst r16 |
SWAP Rd |
Swap nibbles (high/low 4 bits) | swap r16 |
SBI port, bit |
Set bit in I/O register | sbi 0x18, 3 |
CBI port, bit |
Clear bit in I/O register | cbi 0x18, 3 |
| Instruction | Description | Example |
|---|---|---|
LSL Rd |
Logical shift left | lsl r16 |
LSR Rd |
Logical shift right | lsr r16 |
ROL Rd |
Rotate left through carry | rol r16 |
ROR Rd |
Rotate right through carry | ror r16 |
| Instruction | Description | Example |
|---|---|---|
JMP label |
Unconditional jump | jmp loop |
RJMP offset |
Relative jump | rjmp -5 |
CALL label |
Call subroutine | call function |
RCALL offset |
Relative call | rcall -10 |
RET |
Return from subroutine | ret |
RETI |
Return from interrupt | reti |
BRNE label |
Branch if not equal (Z=0) | brne loop |
BREQ label |
Branch if equal (Z=1) | breq done |
BRCS label |
Branch if carry set (C=1) | brcs overflow |
BRCC label |
Branch if carry clear (C=0) | brcc no_carry |
BRGE label |
Branch if greater/equal | brge positive |
BRLT label |
Branch if less than | brlt negative |
BRMI label |
Branch if minus (N=1) | brmi negative |
BRPL label |
Branch if plus (N=0) | brpl positive |
| Instruction | Description | Example |
|---|---|---|
CP Rd, Rr |
Compare registers (Rd - Rr) | cp r16, r17 |
CPI Rd, K |
Compare with immediate | cpi r16, 42 |
CPSE Rd, Rr |
Compare, skip if equal | cpse r16, r17 |
| Instruction | Description | Example |
|---|---|---|
SBRS Rd, bit |
Skip if bit in register is set | sbrs r16, 7 |
SBRC Rd, bit |
Skip if bit in register is clear | sbrc r16, 7 |
SBIS port, bit |
Skip if bit in I/O register is set | sbis 0x16, 3 |
SBIC port, bit |
Skip if bit in I/O register is clear | sbic 0x16, 3 |
| Instruction | Description | Example |
|---|---|---|
NOP |
No operation | nop |
SEI |
Set global interrupt enable | sei |
CLI |
Clear global interrupt enable | cli |
The 8-bit status register contains condition flags updated by instructions:
| Bit | Flag | Description |
|---|---|---|
| 7 | I | Global interrupt enable |
| 6 | T | Bit copy storage |
| 5 | H | Half carry (BCD arithmetic) |
| 4 | S | Sign bit (N ⊕ V) |
| 3 | V | Two's complement overflow |
| 2 | N | Negative |
| 1 | Z | Zero |
| 0 | C | Carry/borrow |
Flags are used for conditional branching and tracking arithmetic results.
; for line comments: (e.g., loop:)No open issues yet, or sync has not completed.