tiny8.cpu module¶
A simplified AVR-like 8-bit CPU simulator.
This module provides a lightweight CPU model inspired by the ATmega family.
The CPU class is the primary export and implements a small, extensible instruction-dispatch model.
The implementation favors readability over cycle-accurate emulation. Add
instruction handlers by defining methods named op_<mnemonic> on CPU.
- class tiny8.cpu.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_brmi(label: str | int)[source]¶
BRMI - Branch if Minus (Negative flag set).
Branches when the N flag is set (negative result).
- 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_brpl(label: str | int)[source]¶
BRPL - Branch if Plus (Negative flag clear).
Branches when the N flag is clear (non-negative result).
- Args:
label: Destination label or address to jump to if the condition is met.
- 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.