A tiny CPU simulator written in Python
See the codeAn 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.
Real-time visualization of a bubble sort algorithm executing on Tiny8
pip install tiny8
Create fibonacci.asm:
; Fibonacci Sequence Calculator
; Calculates the 10th Fibonacci number (F(10) = 55)
; F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2)
;
; Results stored in registers:
; R16 and R17 hold the two most recent Fibonacci numbers
ldi r16, 0 ; F(0) = 0
ldi r17, 1 ; F(1) = 1
ldi r18, 9 ; Counter: 9 more iterations to reach F(10)
loop:
add r16, r17 ; F(n) = F(n-1) + F(n-2)
mov r19, r16 ; Save result temporarily
mov r16, r17 ; Shift: previous = current
mov r17, r19 ; Shift: current = new result
dec r18 ; Decrement counter
brne loop ; Continue if counter != 0
done:
jmp done ; Infinite loop at end
Run it:
tiny8 fibonacci.asm # Interactive debugger
tiny8 fibonacci.asm -m ani -o fibonacci.gif # Generate animation
from 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 number
For 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 GIF
from 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:)0x0000 - 0x001F Memory-mapped I/O (optional)
0x0020 - 0xFFFF Available RAM (stack grows downward from top)
pytest # Run all tests
pytest --cov=src/tiny8 --cov-report=html # With coverage
pytest tests/test_arithmetic.py # Specific test file
Contributions welcome! See CONTRIBUTING.md for guidelines.
Areas for contribution: New instructions, example programs, documentation, visualizations, performance optimizations.
MIT License — see LICENSE for details.
Made with ❤️ for learners, educators, and curious minds
Star ⭐ the repo if you find it useful!
225,984 followers · starred Oct 2025
10,747 followers · starred Oct 2025
408 followers · starred Oct 2025
247 followers · starred Oct 2025
A tiny CPU simulator written in Python
See the codeAn 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.
Real-time visualization of a bubble sort algorithm executing on Tiny8
pip install tiny8
Create fibonacci.asm:
; Fibonacci Sequence Calculator
; Calculates the 10th Fibonacci number (F(10) = 55)
; F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2)
;
; Results stored in registers:
; R16 and R17 hold the two most recent Fibonacci numbers
ldi r16, 0 ; F(0) = 0
ldi r17, 1 ; F(1) = 1
ldi r18, 9 ; Counter: 9 more iterations to reach F(10)
loop:
add r16, r17 ; F(n) = F(n-1) + F(n-2)
mov r19, r16 ; Save result temporarily
mov r16, r17 ; Shift: previous = current
mov r17, r19 ; Shift: current = new result
dec r18 ; Decrement counter
brne loop ; Continue if counter != 0
done:
jmp done ; Infinite loop at end
Run it:
tiny8 fibonacci.asm # Interactive debugger
tiny8 fibonacci.asm -m ani -o fibonacci.gif # Generate animation
from 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 number
For 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 GIF
from 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:)0x0000 - 0x001F Memory-mapped I/O (optional)
0x0020 - 0xFFFF Available RAM (stack grows downward from top)
pytest # Run all tests
pytest --cov=src/tiny8 --cov-report=html # With coverage
pytest tests/test_arithmetic.py # Specific test file
Contributions welcome! See CONTRIBUTING.md for guidelines.
Areas for contribution: New instructions, example programs, documentation, visualizations, performance optimizations.
MIT License — see LICENSE for details.
Made with ❤️ for learners, educators, and curious minds
Star ⭐ the repo if you find it useful!
225,984 followers · starred Oct 2025
10,747 followers · starred Oct 2025
408 followers · starred Oct 2025
247 followers · starred Oct 2025