tiny8 package¶
Submodules¶
Module contents¶
tiny8 - a tiny AVR-like 8-bit CPU simulator package.
Provides a compact CPU implementation, assembler and visualization helpers for experimentation and teaching.
- class tiny8.CPU(memory: Memory | None = None)[source]¶
Bases:
objectIn-memory 8-bit AVR-like CPU model.
The CPU implements a compact instruction-dispatch model. Handlers are methods named
op_<mnemonic>and are invoked bystep()for the currently-loaded program.- Attributes:
regs (list[int]): 32 8-bit general purpose registers (R0..R31). pc (int): Program counter (index into
program). sp (int): Stack pointer (index into RAM in the associated- sreg (int): Status register bits stored in a single integer (I, T,
H, S, V, N, Z, C).
cycle (int): Instruction execution counter. reg_trace (list[tuple[int, int, int]]): Per-cycle register change
trace entries of the form
(cycle, reg, new_value).- mem_trace (list[tuple[int, int, int]]): Per-cycle memory change
trace entries of the form
(cycle, addr, new_value).- step_trace (list[dict]): Full per-step snapshots useful for
visualization and debugging.
- Note:
This implementation simplifies many AVR specifics (flag semantics, exact cycle counts, IO mapping) in favor of clarity. Extend or replace individual
op_handlers to increase fidelity.
- get_flag(bit: int) bool[source]¶
Return the boolean value of a specific SREG flag bit.
- Args:
bit: Integer bit index (0..7).
- Returns:
True if the bit is set, False otherwise.
- load_program(program: list[tuple[str, tuple]], labels: dict[str, int])[source]¶
Load an assembled program into the CPU.
- Args:
- program: list of
(mnemonic, operands)tuples returned by the assembler.
labels: Mapping of label strings to instruction indices.
- program: list of
- Note:
After loading the program, the program counter is reset to zero.
- op_add(rd: int, rr: int)[source]¶
Add register
rrtord(Rd := Rd + Rr) and update flags.Sets C, H, N, V, S, Z per AVR semantics.
- op_adiw(rd_word_low: int, imm_word: int)[source]¶
Add immediate to word register pair (Rd:Rd+1) - simplified.
rd_word_low is the low register of the pair (even register index). imm_word is a 16-bit immediate to add.
- op_brcc(label: str)[source]¶
Branch to a label if the carry flag is clear.
- Args:
label (str): Destination label to jump to if the carry flag is clear.
- op_brcs(label: str)[source]¶
Branch to a label if the carry flag is set.
- Args:
label (str): Destination label to jump to if the carry flag is set.
- op_breq(label: str)[source]¶
Branch to label if Zero flag is set (BREQ).
- Args:
label: Destination label to jump to if Z flag is set.
- op_brge(label: str | int)[source]¶
BRGE - Branch if Greater or Equal (Signed)
- Args:
label: Destination label or address to jump to if the condition is met.
- op_brlt(label: str | int)[source]¶
BRLT - Branch if Less Than (Signed).
- Args:
label: Destination label or address to jump to if the condition is met.
- op_brne(label: str)[source]¶
Branch to label if Zero flag is not set (BRNE).
- Args:
label: Destination label to jump to if Z flag is not set.
- op_call(label: str)[source]¶
Call a subroutine by pushing the return address and jumping to label.
The return address (pc+1) is pushed as two bytes (high then low) onto the stack, decrementing the stack pointer after each write. Control then jumps to
label.- Args:
label (str): Label to call.
- op_cbi(io_addr: int, bit: int)[source]¶
Clear a bit in an I/O/memory-mapped address (use RAM area).
- Args:
io_addr: I/O or RAM address to modify. bit: Bit index to clear (0..7).
- op_cpse(rd: int, rr: int)[source]¶
Compare and Skip if Equal: compare Rd,Rr; if equal, skip next instruction.
- op_div(rd: int, rr: int)[source]¶
Unsigned divide convenience instruction: quotient -> Rd, remainder -> Rd+1.
- op_jmp(label: str | int)[source]¶
Jump to a given label or numeric address by updating the program counter.
This operation sets the CPU’s program counter (self.pc) to the target address minus one. The subtraction of one accounts for the fact that the instruction dispatcher will typically increment the program counter after the current instruction completes.
- Args:
- label (str | int): The jump target. If a string, it is treated as a symbolic label
and looked up in self.labels to obtain its numeric address. If an int (or any value convertible to int), it is used directly as the numeric address.
- op_ld(rd: int, addr_reg: int)[source]¶
Load from RAM at address contained in register
addr_regintord.- Args:
rd: Destination register index. addr_reg: Register index containing the RAM address to load from.
- op_ldi(reg_idx: int, imm: int)[source]¶
Load an immediate value into a register.
- Args:
reg_idx: Destination register index. imm: Immediate value to load.
- Note:
The AVR LDI instruction is normally restricted to R16..R31; this simplified implementation accepts any register index.
- op_mov(rd: int, rr: int)[source]¶
Copy the value from register
rrintord.- Args:
rd: Destination register index. rr: Source register index.
- op_mul(rd: int, rr: int)[source]¶
Multiply 8x8 -> 16: store low in Rd, high in Rd+1. Update Z and C conservatively.
- op_pop(rd: int)[source]¶
Pop a value from the stack into a register.
The stack pointer is incremented, the byte at the new stack pointer is read from RAM, and the value is written into register
rd.- Args:
rd (int): Destination register index to receive the popped value.
- op_push(rr: int)[source]¶
Push a register value onto the stack.
The value of register
rris written to the RAM at the current stack pointer, and the stack pointer is then decremented.- Args:
rr (int): Source register index to push.
- op_ret()[source]¶
Return from subroutine by popping the return address and setting PC.
Two bytes are popped from the stack (low then high) to reconstruct the return address, which is then loaded into the program counter (adjusted because step() will increment PC after execution).
- op_sbi(io_addr: int, bit: int)[source]¶
Set a bit in an I/O/memory-mapped address (use RAM area).
- Args:
io_addr: I/O or RAM address to modify. bit: Bit index to set (0..7).
- op_sbiw(rd_word_low: int, imm_word: int)[source]¶
Subtract immediate from word register pair (Rd:Rd+1) — simplified.
rd_word_low is the low register of the pair (even register index). imm_word is a 16-bit immediate to subtract.
- op_st(addr_reg: int, rr: int)[source]¶
Store register
rrinto RAM at address contained in registeraddr_reg.- Args:
addr_reg: Register index containing the RAM address to write to. rr: Source register index to store.
- read_ram(addr: int) int[source]¶
Read a byte from RAM at the given address.
- Args:
addr: RAM address to read.
- Returns:
Byte value stored at
addr(0..255).
- read_reg(r: int) int[source]¶
Return the 8-bit value from register
r.- Args:
r: Register index (0..31).
- Returns:
The 8-bit value (0..255) stored in the register.
- run(max_cycles: int = 100000) None[source]¶
Run instructions until program end or
max_cyclesis reached.- Args:
- max_cycles: Maximum number of instruction cycles to execute
(default 100000).
- Note:
This repeatedly calls
step()until it returns False or the maximum cycle count is reached.
- set_flag(bit: int, value: bool) None[source]¶
Set or clear a specific SREG flag bit.
- Args:
bit: Integer bit index (0..7) representing the flag position. value: True to set the bit, False to clear it.
- step() bool[source]¶
Execute a single instruction at the current program counter.
Performs one fetch-decode-execute cycle. A pre-step snapshot of registers and non-zero RAM is recorded, the instruction handler (
op_<mnemonic>) is invoked, and a post-step trace entry is appended tostep_trace.- Returns:
True if an instruction was executed; False if the PC is out of range and execution should stop.
- trigger_interrupt(vector_addr: int)[source]¶
Trigger an interrupt vector if it is enabled.
If the interrupt vector is enabled in
self.interrupts, the current PC+1 is pushed onto the stack (high then low byte) and control jumps tovector_addr.- Args:
vector_addr (int): Interrupt vector address to jump to.
- Returns:
None
- write_ram(addr: int, val: int) None[source]¶
Write an 8-bit value to RAM at
addrand record the trace.- Args:
addr: RAM address to write. val: Value to write; will be truncated to 8 bits.
- Note:
The underlying
Memoryobject stores the value; a(cycle, addr, val)tuple is appended tomem_tracefor visualizers/tests.
- class tiny8.Visualizer(cpu)[source]¶
Bases:
object- animate_combined(mem_addr_start: int = 0, mem_addr_end: int = 31, filename: str | None = None, interval: int = 200, fps: int = 30, fontsize=9, cmap='inferno', plot_every: int = 1)[source]¶
Animate SREG bits, registers (R0..R31), and a memory range as three stacked subplots.
- Args:
mem_addr_start: Start memory address for memory subplot (default 0). mem_addr_end: End memory address for memory subplot (default 31). filename: Optional output filename for saving an animation (GIF). If
not provided, show the plot interactively.
interval: Milliseconds between frames in the animation. fps: Frames per second for saving output. fontsize: Font size for labels and ticks. cmap: Matplotlib colormap name for the heatmaps. plot_every: Plot every N cycles (downsampling of frames).
- Notes:
Requires numpy and matplotlib. If Matplotlib’s animation or ffmpeg is not available, the method will attempt to show a static figure.
- tiny8.assemble(text: str) Tuple[List[Tuple[str, Tuple]], Dict[str, int]][source]¶
Parse assembly source text and return parsed instructions and label map.
- Args:
- text (str): Assembly source code as a single string. May contain
multiple lines, labels and comments.
- Returns:
- Tuple[List[Tuple[str, Tuple]], Dict[str, int]]: A pair (instructions, labels):
instructions: list of parsed instructions in source order. Each instruction is a tuple (mnemonic, operands) where mnemonic is a string and operands is a tuple of operand values as produced by the assembler.
labels: mapping from label names (str) to integer addresses (instruction indices).
- Raises:
- Exception: Propagates parsing errors from the underlying parser
(for example, syntax or operand errors).
- Notes:
This function is a thin wrapper around parse_asm(…) and forwards any exceptions raised by the parser.
- Example:
>>> src = "start: MOV R1, 5\nJMP start" >>> instructions, labels = assemble(src) >>> labels {'start': 0}
- tiny8.assemble_file(path: str)[source]¶
Assemble the contents of a source file.
Reads the entire file at path and passes its contents to assemble(…).
- Args:
path (str): Path to the source file to assemble.
- Returns:
Any: The result produced by calling assemble(source_text). The exact type depends on the implementation of assemble(…).
- Raises:
FileNotFoundError: If the specified file does not exist. OSError: For other I/O related errors when opening or reading the file. Exception: Any exception raised by assemble(…) will be propagated.
- Notes:
The file is opened in text mode and read entirely into memory; for very large files this may be inefficient.
- Example:
>>> result = assemble_file("program.asm") >>> # result now holds the assembled output produced by assemble(...)