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.
- Vim-style navigation: Step through execution with intuitive keyboard controls
 - Change highlighting: See exactly what changed at each step (registers, flags, memory)
 - Advanced search: Find instructions, track register/memory changes, locate PC addresses
 - Marks and bookmarks: Set and jump to important execution points
 - Vertical scrolling: Handle programs with large memory footprints
 
- Generate high-quality GIF/MP4 videos of program execution
 - Visualize register evolution, memory access patterns, and flag changes
 - Perfect for presentations, documentation, and learning materials
 
- 32 general-purpose registers (R0-R31)
 - 8-bit ALU with arithmetic, logical, and bit manipulation operations
 - Status register (SREG) with 8 condition flags
 - 2KB address space for unified memory and I/O
 - Stack operations with dedicated stack pointer
 - AVR-inspired instruction set with 60+ instructions
 
- Clean, readable Python implementation
 - Comprehensive examples (Fibonacci, bubble sort, factorial, and more)
 - Step-by-step execution traces for debugging
 - Full API documentation and instruction set reference
 
pip install tiny8Create 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 endRun 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.
- Full Documentation โ Complete API reference and guides
 - Instruction Set Reference โ All 60+ instructions
 - CLI Guide โ Terminal debugger keyboard shortcuts
 - Examples โ Sample programs with explanations
 - Contributing โ Guidelines for contributors
 
The terminal-based debugger provides powerful navigation and inspection capabilities.
l/horโ/โโ Step forward/backwardw/bโ Jump ยฑ10 steps0/$โ Jump to first/last stepSpaceโ Play/pause auto-execution[/]โ Decrease/increase playback speed
rโ 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 Z
maโ Set mark 'a' at current step'aโ Jump to mark 'a'/โ Show help screenqorESCโ Quit
The 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.
- Comments: Use 
;for line comments - Labels: Must end with 
:(e.g.,loop:) - Registers: Case-insensitive R0-R31 (r16, R16 equivalent)
 - Immediates: Decimal (42), hex ($2A, 0x2A), binary (0b00101010)
 - Whitespace: Flexible indentation, spaces/tabs interchangeable
 
- 32 General-Purpose Registers (R0-R31) โ 8-bit working registers
 - Program Counter (PC) โ 16-bit, addresses up to 64KB
 - Stack Pointer (SP) โ 16-bit, grows downward from high memory
 - Status Register (SREG) โ 8 condition flags (I, T, H, S, V, N, Z, C)
 - 64KB Address Space โ Unified memory for RAM and I/O
 
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 fileContributions welcome! See CONTRIBUTING.md for guidelines.
Areas for contribution: New instructions, example programs, documentation, visualizations, performance optimizations.
MIT License โ see LICENSE for details.
- Issues: GitHub Issues
 - Discussions: GitHub Discussions
 - Documentation: sql-hkr.github.io/tiny8
 
Made with โค๏ธ for learners, educators, and curious minds
Star โญ the repo if you find it useful!
