Profile
Back to NewsBack
GitHub Trending 15 min
Reader Mode
sql-hkr/tiny8: A tiny CPU simulator written in Python

sql-hkr/tiny8: A tiny CPU simulator written in Python

13 hours ago

Tiny8

PyPI version</a> License</a> Python versions</a> CI</a> codecov</a>

From bare-metal computer architecture to Generative AI — an educational 8-bit AVR RISC CPU simulator with real-time interactive visualization

Tiny8 is a lightweight educational toolkit that bridges bare-metal computer architecture and modern Generative AI. Designed for hands-on learning and experimentation, it guides you through the full stack of computing — from 32 general-purpose registers, ALU operations, stack frames, and C compilation up to executing an iterative reverse-diffusion model synthesizing 28x28 MNIST digits directly on an AVR 8-bit RISC processor. With an interactive Vim-inspired terminal debugger, time-travel reverse execution, and a 32x32 TrueColor memory heatmap, Tiny8 lets you see, step through, and understand computation at every level.

Native 28x28 Generative Diffusion synthesis executing at x5,000 speed in Tiny8 TUI

Real-time generative reverse-diffusion synthesis (MNIST digit 8) executing at x5,000 speed in Tiny8 TUI (HEX mode for weight/LFSR init → PIXEL mode for reverse denoising trajectory)

Features

Modern Textual TUI Debugger & Dashboard

  • Reactive Terminal Dashboard: Real-time panels with gradient heatmap bars, SREG condition timeline, call stack, and memory dump
  • Bottom statusline: High-contrast mode badges (NORMAL, RUNNING, INSERT, COMMAND), file progress, and key hints (h: help)
  • Interactive controls: Step forward/back (n/p), continue (c), toggle breakpoint (b), play/pause (Space), speed control (+/-), TUI application help guide (h / ?), and instruction inspector modal (i)
  • Seamless Vim Editor: Press e anytime to jump into the built-in editor, modify code, and hit :run to debug

Built-in Vim-Style Modal Editor

  • Standalone file creation & editing: Run tiny8 or tiny8 my_prog.c to create and edit files directly inside the terminal
  • Complete Vim modes:
- NORMAL: h/j/k/l movement, w/b word jump, 0/$ line limits, gg/G bounds, x delete char, dd delete line, yy yank, p paste, u undo, Ctrl+r redo - INSERT: Full typing with syntax highlighting, line numbers, and to return to normal mode - COMMAND (:): :w (save), :w , :q (exit), :wq / :x (save and run), :run (compile/assemble and launch debugger), :e (switch file)

32x32 Heatmap Memory Dump & Diffusion Synthesis

  • 32x32 Heatmap Memory Grid (1024 Bytes): TrueColor gradient heatmap background mapped directly to memory (0x0200..0x05FF VRAM default), rendering 28x28 MNIST handwritten digits directly inside the memory dump with 1:1 pixel aspect ratio.
  • Embedded Generative AI: Iterative reverse-diffusion synthesis generating MNIST digits on Tiny8 AVR CPU (tiny8 examples/diffusion_mnist8.asm).
  • Viridis & Grayscale Palettes: Supports both colorful Viridis heatmap and realistic MNIST Grayscale pixel display modes (toggle with g for gray/viridis and x for hex/pixel).
  • Sample Programs: Native 28x28 diffusion generation (tiny8 examples/diffusion_mnist8.asm) showcasing real-time image synthesis on the 32x32 VRAM heatmap monitor.

Built-in C Compiler & Extensions

  • Compile and run C programs directly: Write C code (.c) and execute on Tiny8 (tiny8 program.c)
  • Enhanced C features: Character literals ('A', '#'), string literals, fixed-size arrays (int arr[4];), for loops, break;, logical && and ||, function calls, peek()/poke(), and VRAM helpers (vram_set, vram_get, vram_puts, vram_clear)
  • Compile-to-assembly inspection: Generate and inspect clean Tiny8 assembly with -c flag

Optimized AVR Instruction Set

  • Extended SRAM Addressing: LDS Rd, k (Load Direct from SRAM) and STS k, Rr (Store Direct to SRAM) for direct 16-bit address access
  • 16-bit Pointer Pairs: Full support for pointer pairs Z (R31:R30), X (R27:R26), and Y (R29:R28) with LD and ST across the entire 64KB address space

Complete 8-bit Architecture

  • 32 general-purpose registers (R0-R31)
  • 8-bit ALU with arithmetic, logical, and bit manipulation operations
  • Status register (SREG) with 8 condition flags
  • 64KB address space for unified memory and I/O
  • Stack operations with dedicated stack pointer
  • AVR instruction set with 67 instructions compliant with Microchip DS40002198A

Educational Focus

  • 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

Quick Start

Installation

pip install tiny8

Your First Program

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 Textual debugger
tiny8 examples/factorial.c     # Run C program directly in debugger
tiny8 examples/factorial.c -c  # Print compiled Tiny8 assembly

Python API

from tiny8 import CPU, assemble_file, compile_c

Run assembly program

asm = assemble_file("fibonacci.asm") cpu = CPU() cpu.load_program(asm) cpu.run(max_steps=1000) print(f"Result: R17 = {cpu.read_reg(17)}")

Or compile and run C code directly!

asm_from_c = compile_c("int main() { return 10 + 32; }")

Why Tiny8?

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.

Documentation

Interactive CLI Controls

The terminal debugger provides modern modal navigation and inspection capabilities.

Navigation & Execution Shortcuts

  • n / p — Step forward / backward (+1 / -1)
  • c — Continue run forward until next breakpoint, watchpoint, or program completion
  • C (Shift+C) — Reverse continue run backward until previous breakpoint or watchpoint
  • b — Toggle breakpoint at current PC
  • Space — Play / pause automatic step execution
  • + / - — Increase / decrease playback speed
  • j / k (or J / K) — Scroll memory dump down / up (by 32-byte line or fast 128-byte page)
  • e — Open full-screen Vim Editor for source code
  • r — Reset execution to step 0
  • h / ? — Open TUI application usage & keyboard guide modal
  • i — Inspect specification & status details for the currently selected instruction
  • q — Quit debugger

Tabbed Views (Inspector & Telemetry)

  • Registers & Memory — Gradient heatmap bars for R0..R31 alongside 32x32 memory dump with TrueColor heatmap background (0x0200..0x05FF VRAM)
  • C Variables — Live symbol table tracking C variable names, types, memory addresses, hex/dec values, and update status
  • Call Stack — Dynamic function call backtrace reconstructed from execution calls and returns

Command Bar (press :)

  • : or :goto — Jump directly to step number
  • :b or :break — Toggle breakpoint at PC (0x000E or 14)
  • :b if — Set conditional breakpoint (e.g. :b 10 if R16 == 42, :b 0x12 if mem[0x60] > 0)
  • :bc or :clear — Clear all breakpoints and conditional breakpoints
  • :watch or :w — Add watchpoint on register, memory address, or condition (e.g. :watch R0, :watch mem[0x60])
  • :unwatch [expr] — Remove watchpoint or clear all watchpoints
  • :mark — Create bookmark for current execution state
  • :' or :goto — Jump to bookmarked step
  • :c or :run — Continue execution to next breakpoint
  • :rc or :rev — Reverse continue execution backward
  • :set R — Live modify register value in current state (e.g. :set R16 42)
  • :poke — Live modify memory value at address (e.g. :poke 0x60 255)
  • :exec — Mini REPL: assemble and execute an instruction on the fly (e.g. :exec LDI R17, 88)
  • :mem or :m — Jump memory view to address (0x0060)
  • :find — Search forward for instruction
  • :e [file] — Open Vim editor for file
  • :r or :reset — Reset to step 0
  • :h or :help — Show instruction reference modal
  • :q or :quit — Exit tiny8
  • — Cancel command bar / close modal dialog

Examples

The examples/ directory contains programs demonstrating key concepts:

| Example | Description | |---------|-------------| | fibonacci.asm | Fibonacci sequence using registers | | bubblesort.asm | Sorting algorithm with memory visualization | | diffusion_mnist8.asm | Native 28x28 MNIST digit generation using an iterative reverse diffusion process | | find_max.asm | Finding maximum value in array | | is_prime.asm | Prime number checking algorithm | | gcd.asm | Greatest common divisor (Euclidean algorithm) |

Bubble Sort

Sort 32 pseudo-random bytes in ascending order (RAM 0x60..0x7F):

tiny8 examples/bubblesort.asm

Using Python

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)])

CLI Options

Command Syntax

tiny8 [FILE] [OPTIONS]

General Options

| Option | Description | |--------|-------------| | -v, --version | Show version and exit | | -e, --edit | Launch directly into Vim editor mode | | --max-steps N | Maximum execution steps (default: until infinite loop or halt) | | -c, --compile-only | Compile C program and print generated Tiny8 assembly without running |

Interactive Controls (Textual TUI)

| Key | Action | |-----|--------| | Space | Play / Pause auto execution | | Shift+Space | Reverse playback auto execution | | n | Step forward (+1 instruction) | | p | Step backward (-1 instruction, time-travel) | | c | Continue execution forward to next breakpoint | | C (Shift+c) | Reverse continue backward to previous breakpoint | | b | Toggle breakpoint on current instruction | | : | Open command line prompt (e.g. :goto , :b , :q) | | j / k (or J / K) | Scroll memory viewport down / up by 32 bytes (or 128-byte fast page) | | g | Toggle Grayscale / Viridis color palette | | x | Toggle Hexadecimal digits (02X) / Graphic Pixel blocks | | m | Cycle view modes (Viridis+Hex -> Viridis+Pixel -> Grayscale+Pixel -> Grayscale+Hex) | | e | Launch full-screen Vim-style modal editor | | h / ? | Open TUI Application Usage & Keyboard Guide | | i | Open Instruction Reference modal (all 67 instructions) | | + / - | Increase / Decrease playback speed | | r | Reset execution back to step 0 | | q | Quit debugger |

Instruction Set Reference

Tiny8's instruction set strictly adheres to the official Microchip AVR® Instruction Set Manual (DS40002198A). All mnemonics are case-insensitive. Registers are specified as R0–R31, and immediates support decimal, hex (0xFF), and binary (0b11111111) notation.

Data Transfer

| Instruction | Description (DS40002198A) | Operation | Example | |-------------|---------------------------|-----------|---------| | LDI Rd, K | Load Immediate | Rd ← K (16 ≤ d ≤ 31) | ldi r16, 42 | | MOV Rd, Rr | Copy Register | Rd ← Rr | mov r17, r16 | | LD Rd, Rr | Load Indirect from Data Space | Rd ← (Rr) | ld r18, r26 | | ST Rr, Rs | Store Indirect to Data Space | (Rr) ← Rs | st r26, r18 | | LDS Rd, k | Load Direct from Data Space | Rd ← (k) | lds r16, 0x0200 | | STS k, Rr | Store Direct to Data Space | (k) ← Rr | sts 0x0200, r16 | | IN Rd, P | Load an I/O Location to Register | Rd ← P | in r16, 0x3F | | OUT P, Rr | Store Register to an I/O Location | P ← Rr | out 0x3F, r16 | | PUSH Rr | Push Register on Stack | STACK ← Rr, SP ← SP - 1 | push r16 | | POP Rd | Pop Register from Stack | SP ← SP + 1, Rd ← STACK | pop r16 |

Arithmetic Operations

| Instruction | Description (DS40002198A) | Operation | Flags | Example | |-------------|---------------------------|-----------|-------|---------| | ADD Rd, Rr | Add without Carry | Rd ← Rd + Rr | H, S, V, N, Z, C | add r16, r17 | | ADC Rd, Rr | Add with Carry | Rd ← Rd + Rr + C | H, S, V, N, Z, C | adc r16, r17 | | ADIW Rd, K | Add Immediate to Word | Rd+1:Rd ← Rd+1:Rd + K | S, V, N, Z, C | adiw r24, 1 | | SUB Rd, Rr | Subtract without Carry | Rd ← Rd - Rr | H, S, V, N, Z, C | sub r16, r17 | | SUBI Rd, K | Subtract Immediate | Rd ← Rd - K | H, S, V, N, Z, C | subi r16, 10 | | SBC Rd, Rr | Subtract with Carry | Rd ← Rd - Rr - C | H, S, V, N, Z, C | sbc r16, r17 | | SBCI Rd, K | Subtract Immediate with Carry | Rd ← Rd - K - C | H, S, V, N, Z, C | sbci r16, 5 | | SBIW Rd, K | Subtract Immediate from Word | Rd+1:Rd ← Rd+1:Rd - K | S, V, N, Z, C | sbiw r24, 1 | | INC Rd | Increment | Rd ← Rd + 1 | S, V, N, Z | inc r16 | | DEC Rd | Decrement | Rd ← Rd - 1 | S, V, N, Z | dec r16 | | MUL Rd, Rr | Multiply Unsigned | R1:R0 ← Rd × Rr | Z, C | mul r16, r17 | | MULS Rd, Rr | Multiply Signed | R1:R0 ← Rd × Rr (signed) | Z, C | muls r16, r17 | | MULSU Rd, Rr | Multiply Signed with Unsigned | R1:R0 ← Rd × Rr (Rd signed, Rr unsigned) | Z, C | mulsu r16, r17 | | NEG Rd | Two's Complement Negation | Rd ← 0x00 - Rd | H, S, V, N, Z, C | neg r16 |

Logical & Bit Operations

| Instruction | Description (DS40002198A) | Operation | Flags | Example | |-------------|---------------------------|-----------|-------|---------| | AND Rd, Rr | Logical AND | Rd ← Rd · Rr | S, V=0, N, Z | and r16, r17 | | ANDI Rd, K | Logical AND with Immediate | Rd ← Rd · K | S, V=0, N, Z | andi r16, 0x0F | | OR Rd, Rr | Logical OR | Rd ← Rd ∨ Rr | S, V=0, N, Z | or r16, r17 | | ORI Rd, K | Logical OR with Immediate | Rd ← Rd ∨ K | S, V=0, N, Z | ori r16, 0x80 | | EOR Rd, Rr | Exclusive OR | Rd ← Rd ⊕ Rr | S, V=0, N, Z | eor r16, r17 | | COM Rd | One's Complement | Rd ← 0xFF - Rd | S, V=0, N, Z, C=1 | com r16 | | CLR Rd | Clear Register (Alias: EOR Rd, Rd) | Rd ← Rd ⊕ Rd | S=0, V=0, N=0, Z=1 | clr r16 | | SER Rd | Set Register (Alias: LDI Rd, 0xFF) | Rd ← 0xFF | None | ser r16 | | TST Rd | Test for Zero or Minus (Alias: AND Rd, Rd) | Rd ← Rd · Rd | S, V=0, N, Z | tst r16 | | SWAP Rd | Swap Nibbles | Rd(7..4) ↔ Rd(3..0) | None | swap r16 | | SBI P, b | Set Bit in I/O Register | I/O(P, b) ← 1 | None | sbi 0x18, 3 | | CBI P, b | Clear Bit in I/O Register | I/O(P, b) ← 0 | None | cbi 0x18, 3 |

Shifts & Rotates

| Instruction | Description (DS40002198A) | Operation | Flags | Example | |-------------|---------------------------|-----------|-------|---------| | LSL Rd | Logical Shift Left (Alias: ADD Rd, Rd) | Rd ← Rd ≪ 1 | H, S, V, N, Z, C | lsl r16 | | LSR Rd | Logical Shift Right | 0 → Rd7 … Rd0 → C | S, V, N, Z, C | lsr r16 | | ASR Rd | Arithmetic Shift Right | Rd7 → Rd7 … Rd0 → C | S, V, N, Z, C | asr r16 | | ROL Rd | Rotate Left through Carry (Alias: ADC Rd, Rd) | C ← Rd7 … Rd0 ← C | H, S, V, N, Z, C | rol r16 | | ROR Rd | Rotate Right through Carry | C → Rd7 … Rd0 → C | S, V, N, Z, C | ror r16 |

Control Flow & Branches

| Instruction | Description (DS40002198A) | Condition / Target | Flags | Example | |-------------|---------------------------|--------------------|-------|---------| | JMP k | Jump | PC ← k | None | jmp loop | | RJMP k | Relative Jump | PC ← PC + k + 1 | None | rjmp -5 | | CALL k | Long Call to Subroutine | STACK ← PC + 1, PC ← k | None | call function | | RCALL k | Relative Call to Subroutine | STACK ← PC + 1, PC ← PC + k + 1 | None | rcall -10 | | RET | Return from Subroutine | PC ← STACK | None | ret | | RETI | Return from Interrupt | PC ← STACK, I ← 1 | I | reti | | BREQ k | Branch if Equal | Z = 1 | None | breq done | | BRNE k | Branch if Not Equal | Z = 0 | None | brne loop | | BRCS k | Branch if Carry Set | C = 1 | None | brcs overflow | | BRCC k | Branch if Carry Clear | C = 0 | None | brcc no_carry | | BRLO k | Branch if Lower (Unsigned) | C = 1 (Alias for BRCS) | None | brlo lower | | BRSH k | Branch if Same or Higher (Unsigned) | C = 0 (Alias for BRCC) | None | brsh same_or_higher | | BRGE k | Branch if Greater or Equal (Signed) | S = 0 (N ⊕ V = 0) | None | brge positive | | BRLT k | Branch if Less Than (Signed) | S = 1 (N ⊕ V = 1) | None | brlt negative | | BRMI k | Branch if Minus | N = 1 | None | brmi negative | | BRPL k | Branch if Plus | N = 0 | None | brpl positive |

Compare & Skip Instructions

| Instruction | Description (DS40002198A) | Operation | Flags | Example | |-------------|---------------------------|-----------|-------|---------| | CP Rd, Rr | Compare | Rd - Rr | H, S, V, N, Z, C | cp r16, r17 | | CPI Rd, K | Compare with Immediate | Rd - K | H, S, V, N, Z, C | cpi r16, 42 | | CPSE Rd, Rr | Compare Skip if Equal | Skip next if Rd = Rr | None | cpse r16, r17 | | SBRS Rd, b | Skip if Bit in Register is Set | Skip next if Rd(b) = 1 | None | sbrs r16, 7 | | SBRC Rd, b | Skip if Bit in Register is Cleared | Skip next if Rd(b) = 0 | None | sbrc r16, 7 | | SBIS P, b | Skip if Bit in I/O Register is Set | Skip next if I/O(P, b) = 1 | None | sbis 0x16, 3 | | SBIC P, b | Skip if Bit in I/O Register is Cleared | Skip next if I/O(P, b) = 0 | None | sbic 0x16, 3 |

MCU Control

| Instruction | Description (DS40002198A) | Operation | Flags | Example | |-------------|---------------------------|-----------|-------|---------| | NOP | No Operation | None | None | nop | | SEI | Set Global Interrupt Flag | I ← 1 | I | sei | | CLI | Clear Global Interrupt Flag | I ← 0 | I | cli |

Status Register (SREG) Flags

The 8-bit Status Register (SREG) contains condition flags updated by ALU instructions according to exact formulas defined in DS40002198A:

| Bit | Flag | Name | DS40002198A Formula & Meaning | |-----|------|------|-------------------------------| | 7 | I | Global Interrupt Enable | Set by SEI, cleared by CLI | | 6 | T | Bit Copy Storage | Bit copy buffer for BST / BLD | | 5 | H | Half Carry Flag | Carry/borrow between bit 3 and bit 4 (BCD arithmetic) | | 4 | S | Sign Bit | True algebraic sign test: S = N ⊕ V | | 3 | V | Two's Complement Overflow | Set when signed arithmetic overflows range [-128, +127] | | 2 | N | Negative Flag | Set if MSB is 1 (N = R7) | | 1 | Z | Zero Flag | Set if result is zero (R == 0) | | 0 | C | Carry / Borrow Flag | Unsigned carry from MSB (R7) or borrow |

Flags are used for conditional branching and tracking arithmetic results.

Assembly Syntax Notes

  • Comments: Use ; for line comments
  • Labels: Must end with : (e.g., loop:)
  • Registers: Case-insensitive R0-R31 (r16, R16 equivalent)
  • Immediates: Decimal (42), hex (0x2A), binary (0b00101010)
  • Whitespace: Flexible indentation, spaces/tabs interchangeable

Architecture Overview

CPU Components

  • 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

Memory Map

0x0000 - 0x001F    General-Purpose Registers (R0 - R31) / System Reserved
0x0020 - 0x005F    I/O Registers (64 ports: 0x00 - 0x3F via IN/OUT)
0x0060 - 0x01FF    User SRAM (general variables & arrays start at 0x0060)
0x0200 - 0x05FF    32x32 Heatmap VRAM (1,024 bytes, writable visual memory)
0x0600 - 0xFFFF    Extended User SRAM (general data, heap & downward-growing stack)

Testing

pytest                                    # Run all tests
pytest --cov=src/tiny8 --cov-report=html  # With coverage
pytest tests/test_arithmetic.py           # Specific test file

Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

Areas for contribution: New instructions, example programs, documentation, visualizations, performance optimizations.

License

MIT License — see LICENSE for details.

Support


Made for learners, educators, and curious minds

Star the repo if you find it useful!

Chat with me