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 step-accurate emulation. Add instruction handlers by defining methods named op_<mnemonic> on CPU.

class tiny8.cpu.CPU(memory=None)[source]

Bases: object

In-memory 8-bit AVR-like CPU model.

The CPU implements a compact instruction-dispatch model. Handlers are methods named op_<mnemonic> and are invoked by step() for the currently-loaded program.

Parameters:

memory (Memory | None)

regs

32 8-bit general purpose registers (R0..R31).

Type:

list[int]

pc

Program counter (index into program).

Type:

int

sp

Stack pointer (index into RAM in the associated tiny8.memory.Memory).

Type:

int

sreg

Status register bits stored in a single integer (I, T, H, S, V, N, Z, C).

Type:

int

step_count

Instruction execution counter.

Type:

int

reg_trace

Per-step register change trace entries of the form (step, reg, new_value).

Type:

list[tuple[int, int, int]]

mem_trace

Per-step memory change trace entries of the form (step, addr, new_value).

Type:

list[tuple[int, int, int]]

step_trace

Full per-step snapshots useful for visualization and debugging.

Type:

list[dict]

Note

This implementation simplifies many AVR specifics (flag semantics, exact step counts, IO mapping) in favor of clarity. Extend or replace individual op_ handlers to increase fidelity.

__init__(memory=None)[source]
Parameters:

memory (Memory | None)

set_flag(bit, value)[source]

Set or clear a specific SREG flag bit.

Parameters:
  • bit (int) – Integer bit index (0..7) representing the flag position.

  • value (bool) – True to set the bit, False to clear it.

Return type:

None

get_flag(bit)[source]

Return the boolean value of a specific SREG flag bit.

Parameters:

bit (int) – Integer bit index (0..7).

Returns:

True if the bit is set, False otherwise.

Return type:

bool

read_reg(r)[source]

Return the 8-bit value from register r.

Parameters:

r (int) – Register index (0..31).

Returns:

The 8-bit value (0..255) stored in the register.

Return type:

int

write_reg(r, val)[source]

Write an 8-bit value to register r and record the change.

Parameters:
  • r (int) – Register index (0..31).

  • val (int) – Value to write; will be truncated to 8 bits.

Return type:

None

Note

A trace entry is appended only when the register value actually changes to avoid noisy traces.

read_ram(addr)[source]

Read a byte from RAM at the given address.

Parameters:

addr (int) – RAM address to read.

Returns:

Byte value stored at addr (0..255).

Return type:

int

write_ram(addr, val)[source]

Write an 8-bit value to RAM at addr and record the trace.

Parameters:
  • addr (int) – RAM address to write.

  • val (int) – Value to write; will be truncated to 8 bits.

Return type:

None

Note

The underlying Memory object stores the value; a (step, addr, val) tuple is appended to mem_trace for visualizers/tests.

load_program(program, labels=None, pc_to_line=None, source_lines=None)[source]

Load an assembled program into the CPU.

Parameters:
  • program (list[tuple[str, tuple]] | AsmResult) – Either a list of (mnemonic, operands) tuples or an AsmResult object. If AsmResult, other params are ignored.

  • labels (dict[str, int] | None) – Mapping of label strings to instruction indices (ignored if program is AsmResult).

  • pc_to_line (dict[int, int] | None) – Optional mapping from PC to source line number for tracing (ignored if program is AsmResult).

  • source_lines (list[str] | None) – Optional original assembly source lines for display (ignored if program is AsmResult).

Note

After loading the program, the program counter is reset to zero.

step()[source]

Execute a single instruction at the current program counter.

Performs one fetch-decode-execute step. 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 to step_trace.

Returns:

True if an instruction was executed; False if the PC is out of range and execution should stop.

Return type:

bool

run(max_steps=100000, show_progress=True)[source]

Run instructions until program end or max_steps is reached.

Parameters:
  • max_steps (int) – Maximum number of instruction steps to execute (default 100000).

  • show_progress (bool) – If True, display a progress bar during execution (default True).

Return type:

None

Note

This repeatedly calls step() until it returns False or the maximum step count is reached.

op_nop()[source]

No-operation: does nothing for one step.

op_ldi(reg_idx, imm)[source]

Load an immediate value into a register.

Parameters:
  • reg_idx (int) – Destination register index.

  • imm (int) – 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, rr)[source]

Copy the value from register rr into rd.

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

op_add(rd, rr)[source]

Add register rr to rd (Rd := Rd + Rr) and update flags.

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

Note

Sets C, H, N, V, S, Z per AVR semantics.

op_and(rd, rr)[source]

Logical AND (Rd := Rd & Rr) — updates N, Z, V=0, C=0, H=0, S.

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

op_or(rd, rr)[source]

Logical OR (Rd := Rd | Rr) — updates N, Z, V=0, C=0, H=0, S.

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

op_eor(rd, rr)[source]

Exclusive OR (Rd := Rd ^ Rr) — updates N, Z, V=0, C=0, H=0, S.

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

op_sub(rd, rr)[source]

Subtract (Rd := Rd - Rr) and set flags C,H,N,V,S,Z.

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

op_inc(rd)[source]

Increment (Rd := Rd + 1) — updates V,N,S,Z; does not change C/H.

Parameters:

rd (int) – Destination register index.

op_dec(rd)[source]

Decrement (Rd := Rd - 1) — updates V,N,S,Z; does not change C/H.

Parameters:

rd (int) – Destination register index.

op_mul(rd, rr)[source]

Multiply 8x8 -> 16: store low in Rd, high in Rd+1.

Parameters:
  • rd (int) – Destination register index for low byte.

  • rr (int) – Source register index.

Note

Updates Z and C flags. Z set if product == 0; C set if high != 0.

op_adc(rd, rr)[source]

Add with carry (Rd := Rd + Rr + C) and update flags.

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

op_clr(rd)[source]

Clear register (Rd := 0). Behaves like EOR Rd,Rd for flags.

Parameters:

rd (int) – Destination register index.

op_ser(rd)[source]

Set register all ones (Rd := 0xFF). Update flags conservatively.

Parameters:

rd (int) – Destination register index.

op_div(rd, rr)[source]

Unsigned divide convenience instruction: quotient -> Rd, remainder -> Rd+1.

Parameters:
  • rd (int) – Destination register index for quotient.

  • rr (int) – Divisor register index.

Note

If divisor is zero, sets C and Z flags to indicate error.

op_in(rd, port)[source]

Read from I/O port into register.

Parameters:
  • rd (int) – Destination register index.

  • port (int) – Port address to read from.

op_out(port, rr)[source]

Write register value to I/O port.

Parameters:
  • port (int) – Port address to write to.

  • rr (int) – Source register index.

op_jmp(label)[source]

Jump to a given label or numeric address by updating the program counter.

Parameters:

label (str | int) – The jump target. If a string, it is treated as a symbolic label and looked up in self.labels. If an int, it is used directly as the numeric address.

Note

Sets PC to target - 1 because the instruction dispatcher will increment PC after the current instruction completes.

op_cpi(rd, imm)[source]

Compare register with immediate (sets flags but doesn’t modify register).

Parameters:
  • rd (int) – Register index to compare.

  • imm (int) – Immediate value to compare against.

op_cp(rd, rr)[source]

Compare two registers (sets flags but doesn’t modify registers).

Parameters:
  • rd (int) – First register index.

  • rr (int) – Second register index.

op_lsl(rd)[source]

Logical shift left (Rd := Rd << 1).

Parameters:

rd (int) – Destination register index.

op_lsr(rd)[source]

Logical shift right (Rd := Rd >> 1).

Parameters:

rd (int) – Destination register index.

op_rol(rd)[source]

Rotate left through carry.

Parameters:

rd (int) – Destination register index.

op_ror(rd)[source]

Rotate right through carry.

Parameters:

rd (int) – Destination register index.

op_com(rd)[source]

One’s complement: Rd := ~Rd. Updates N,V,S,Z,C per AVR-ish semantics.

Parameters:

rd (int) – Destination register index.

op_neg(rd)[source]

Two’s complement (negate): Rd := 0 - Rd. Flags as subtraction from 0.

Parameters:

rd (int) – Destination register index.

op_swap(rd)[source]

Swap nibbles in register: Rd[7:4] <-> Rd[3:0]. Does not affect SREG.

Parameters:

rd (int) – Destination register index.

op_tst(rd)[source]

Test: perform AND Rd,Rd and update flags but do not store result.

Parameters:

rd (int) – Register index to test.

op_andi(rd, imm)[source]

Logical AND with immediate (Rd := Rd & K).

Parameters:
  • rd (int) – Destination register index.

  • imm (int) – Immediate value.

op_ori(rd, imm)[source]

Logical OR with immediate (Rd := Rd | K).

Parameters:
  • rd (int) – Destination register index.

  • imm (int) – Immediate value.

op_eori(rd, imm)[source]

Logical EOR with immediate (Rd := Rd ^ K).

Parameters:
  • rd (int) – Destination register index.

  • imm (int) – Immediate value.

op_subi(rd, imm)[source]

Subtract immediate (Rd := Rd - K).

Parameters:
  • rd (int) – Destination register index.

  • imm (int) – Immediate value.

op_sbc(rd, rr)[source]

Subtract with carry (Rd := Rd - Rr - C).

Parameters:
  • rd (int) – Destination register index.

  • rr (int) – Source register index.

op_sbci(rd, imm)[source]

Subtract immediate with carry: Rd := Rd - K - C.

Parameters:
  • rd (int) – Destination register index.

  • imm (int) – Immediate value.

op_sei()[source]

Set Global Interrupt Enable (I bit).

op_cli()[source]

Clear Global Interrupt Enable (I bit).

op_cpse(rd, rr)[source]

Compare and Skip if Equal: compare Rd,Rr; if equal, skip next instruction.

Parameters:
  • rd (int) – First register index.

  • rr (int) – Second register index.

op_sbrs(rd, bit)[source]

Skip next if bit in register is set.

Parameters:
  • rd (int) – Register index.

  • bit (int) – Bit position to test.

op_sbrc(rd, bit)[source]

Skip next if bit in register is clear.

Parameters:
  • rd (int) – Register index.

  • bit (int) – Bit position to test.

op_sbis(io_addr, bit)[source]

Skip if bit in IO/RAM-mapped address is set.

Parameters:
  • io_addr (int) – I/O or RAM address.

  • bit (int) – Bit position to test.

op_sbic(io_addr, bit)[source]

Skip if bit in IO/RAM-mapped address is clear.

Parameters:
  • io_addr (int) – I/O or RAM address.

  • bit (int) – Bit position to test.

op_sbiw(rd_word_low, imm_word)[source]

Subtract immediate from word register pair (Rd:Rd+1) — simplified.

Parameters:
  • rd_word_low (int) – Low register of the pair (even register index).

  • imm_word (int) – 16-bit immediate to subtract.

op_adiw(rd_word_low, imm_word)[source]

Add immediate to word register pair (Rd:Rd+1) - simplified.

Parameters:
  • rd_word_low (int) – Low register of the pair (even register index).

  • imm_word (int) – 16-bit immediate to add.

op_rjmp(label)[source]

Relative jump — label may be an int or string label.

Parameters:

label (str) – Jump target (label name or relative offset).

op_rcall(label)[source]

Relative call — push return address and jump relatively or to label.

Parameters:

label (str) – Call target (label name or relative offset).

op_sbi(io_addr, bit)[source]

Set a bit in an I/O/memory-mapped address (use RAM area).

Parameters:
  • io_addr (int) – I/O or RAM address to modify.

  • bit (int) – Bit index to set (0..7).

op_cbi(io_addr, bit)[source]

Clear a bit in an I/O/memory-mapped address (use RAM area).

Parameters:
  • io_addr (int) – I/O or RAM address to modify.

  • bit (int) – Bit index to clear (0..7).

op_ld(rd, addr_reg)[source]

Load from RAM at address contained in register addr_reg into rd.

Parameters:
  • rd (int) – Destination register index.

  • addr_reg (int) – Register index containing the RAM address to load from.

op_st(addr_reg, rr)[source]

Store register rr into RAM at address contained in register addr_reg.

Parameters:
  • addr_reg (int) – Register index containing the RAM address to write to.

  • rr (int) – Source register index to store.

op_brne(label)[source]

Branch to label if Zero flag is not set (BRNE).

Parameters:

label (str) – Destination label to jump to if Z flag is not set.

op_breq(label)[source]

Branch to label if Zero flag is set (BREQ).

Parameters:

label (str) – Destination label to jump to if Z flag is set.

op_brcs(label)[source]

Branch to a label if the carry flag is set.

Parameters:

label (str) – Destination label to jump to if the carry flag is set.

op_brcc(label)[source]

Branch to a label if the carry flag is clear.

Parameters:

label (str) – Destination label to jump to if the carry flag is clear.

op_brge(label)[source]

Branch if Greater or Equal (Signed).

Parameters:

label (str | int) – Destination label or address to jump to if the condition is met.

op_brlt(label)[source]

Branch if Less Than (Signed).

Parameters:

label (str | int) – Destination label or address to jump to if the condition is met.

op_brmi(label)[source]

Branch if Minus (Negative flag set).

Parameters:

label (str | int) – Destination label or address to jump to if the condition is met.

op_brpl(label)[source]

Branch if Plus (Negative flag clear).

Parameters:

label (str | int) – Destination label or address to jump to if the condition is met.

op_push(rr)[source]

Push a register value onto the stack.

Parameters:

rr (int) – Source register index to push.

Note

The value of register rr is written to RAM at the current stack pointer, and the stack pointer is then decremented.

op_pop(rd)[source]

Pop a value from the stack into a register.

Parameters:

rd (int) – Destination register index to receive the popped value.

Note

The stack pointer is incremented, the byte at the new stack pointer is read from RAM, and the value is written into register rd.

op_call(label)[source]

Call a subroutine by pushing the return address and jumping to label.

Parameters:

label (str) – Label to call.

Note

The return address (pc+1) is pushed as two bytes (high then low) onto the stack, decrementing the stack pointer after each write.

op_ret()[source]

Return from subroutine by popping the return address and setting PC.

Note

Two bytes are popped from the stack (low then high) to reconstruct the return address, which is then loaded into the program counter.

op_reti()[source]

Return from interrupt: pop return address and set I flag.

trigger_interrupt(vector_addr)[source]

Trigger an interrupt vector if it is enabled.

Parameters:

vector_addr (int) – Interrupt vector address to jump to.

Note

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 to vector_addr.