Releases: PyMCU/AVR8Sharp
v1.1.0-beta1
Changelog
All notable changes to this project will be documented in this file.
Packages
| Package | NuGet |
|---|---|
Avr8Sharp |
Core AVR simulator |
Avr8Sharp.TestKit |
FluentAssertions-based test harness (new) |
Breaking Changes
Namespace restructure (from v1.0.2)
All types have been reorganised under AVR8Sharp.Core.*. The CPU sub-namespace has been eliminated — Cpu and all related types live directly in AVR8Sharp.Core:
| Before (v1.0.2) | After (v1.1.0-beta1) |
|---|---|
using AVR8Sharp; |
using AVR8Sharp.Core; |
using AVR8Sharp.Cpu; |
using AVR8Sharp.Core; |
using AVR8Sharp.Peripherals; |
using AVR8Sharp.Core.Peripherals; |
using AVR8Sharp.Utils; |
using AVR8Sharp.Core.Utils; |
new AVR8Sharp.Cpu.Cpu(...) |
new AVR8Sharp.Core.Cpu(...) |
Affected types now in AVR8Sharp.Core: Cpu, AvrInterruptConfig, ClockEventEntry, AvrInterrupt, Opcodes.
Target framework
Multi-targeting (netstandard1.2, netstandard2.0, net6.0, net8.0) is dropped. The library now targets net10.0 exclusively.
CPU API
Cpu.Data and Cpu.DataView are replaced by Cpu.Mmio. Direct SRAM reads and writes go through Cpu.Mmio.Data[]; I/O register hooks are registered via Cpu.Mmio.RegisterWrite().
New: Avr8Sharp.TestKit
Avr8Sharp.TestKit is a new companion package for integration-testing compiled AVR firmware. It wraps Avr8Sharp and provides a fluent, FluentAssertions-compatible API.
using Avr8Sharp.TestKit;
using Avr8Sharp.TestKit.Boards;
var sim = new ArduinoUnoSimulation()
.WithHex(File.ReadAllText("blink.hex"))
.AddUsart(AvrUsart.Usart0Config, out var serial);
sim.RunMilliseconds(500);
serial.Should().Contain("Hello, world!");
sim.Cpu.Should().HaveRegister(16, 0x01);Board presets
| Board | MCU | Clock | Peripherals |
|---|---|---|---|
ArduinoUnoSimulation |
ATmega328P | 16 MHz | Timers 0–2, USART0, SPI, TWI, ADC, GPIO B/C/D |
ArduinoMegaSimulation |
ATmega2560 | 16 MHz | Timers 0–5, USART0–3, SPI, TWI, ADC, GPIO A–L |
ATtiny85Simulation |
ATtiny85 | 8 MHz | Timers 0/1, USI, GPIO B |
Assertions
| Subject | Examples |
|---|---|
Cpu |
.HaveRegister(n, v), .HavePC(addr), .HaveSP(addr), .HaveCycles(n), .HaveZeroFlag(), .HaveInterruptsEnabled() |
AvrIoPort |
.HavePinHigh(n), .HavePinLow(n), .HavePinState(n, state), .HaveOutputValue(v) |
AvrMemoryView |
.HaveByteAt(addr, v), .HaveWordAt(addr, v), .HaveBytesAt(addr, bytes) |
SerialProbe |
.Contain(str), .StartWith(str), .Be(str), .HaveLineCount(n), .ContainByte(b) |
Execution control
AvrTestSimulation exposes: RunCycles, RunMilliseconds, RunInstructions, RunToBreak, RunToAddress, RunUntil, RunUntilSerial.
New Features
ATmega2560 Timer5 and USART3
ArduinoMegaSimulation now includes Timer5 (registers at 0x120–0x12C) and USART3 (registers at 0x130–0x136). Register-address fields in AvrTimerConfig and AvrUsartConfig were promoted from byte to ushort, and the internal interrupt-vector table was raised from 128 to 256 entries.
Pluggable instruction decoders
Three decoders are available, selectable via AvrBuilder:
| Decoder | Builder method | Notes |
|---|---|---|
NativeLutDecoder |
.UseNativeDecoder() |
Unsafe function-pointer LUT — default |
LutDecoder |
.UseLutDecoder() |
Delegate-based LUT |
SwitchDecoder |
.UseSwitchDecoder() |
Switch/case |
SLEEP instruction
Executing SLEEP now invokes AvrInterrupt.OnSleep (if set) with the SM2:SM1:SM0 mode bits from SMCR. Previously it was a silent NOP.
SPI slave mode
AvrSpi.SimulateIncomingMasterByte(byte) lets external code drive a byte as an SPI master when the device is in slave mode (MSTR=0). The OnSlaveTransfer callback fires on each received byte.
TWI (I²C) slave mode
New methods on AvrTwi:
SimulateIncomingAddress(byte address, bool isWrite)— matches TWAR (respecting the TWAMR address mask and general-call flag); sets TWSR to0x60(SLA+W),0xA8(SLA+R), or0x70(general call), and raises TWINT.SimulateIncomingData(byte data)— delivers a data byte in slave-receive mode; sets TWSR to0x80/0x88per TWEA.ReadSlaveTransmitByte()— reads the byte firmware placed in TWDR for slave-transmit.
ADC temperature sensor configurability
AvrAdc.TemperatureVoltage (default 0.378125 V ≈ 25 °C) replaces the previously hardcoded value returned by the internal temperature sensor channel (mux 8 on ATmega328P).
USI start/stop condition callbacks
AvrUsi exposes OnStartCondition and OnStopCondition callbacks that fire when the two-wire mode port listener detects an I²C start or stop event on the correct SCL/SDA edge transitions.
Bug Fixes
Nine datasheet accuracy corrections
Verified against ATmega328P, ATmega2560, and ATtiny85 datasheets:
- Interrupt latency — vector-fetch cycle was missing; ISR entry now takes the spec-required 4 cycles.
- SREG on Reset — the Global Interrupt Enable bit could survive a mid-execution reset;
SREGis now cleared inReset(). - BREAK instruction — previously a silent NOP; now invokes
AvrInterrupt.OnBreakpoint. - USART 9-bit RX — data mask was
0xFFinstead of0x1FF, silently discarding the 9th bit. - PCMSK write scope — a PCMSK write was re-evaluating all port groups instead of only the owning one.
- Timer OC-C — OCFC flag-clear and OCIE-C enable were not wired in the TIFR/TIMSK write hooks.
- Timer input capture — ICF/ICIE fields added to
AvrTimerConfig; new publicTriggerCapture()method latches TCNT into ICR and raises the capture interrupt. - ADC free-running mode —
CompleteAdcRead()now automatically restarts conversion whenADATE=1andADTS=000. - MmioController hook chaining —
RegisterWrite()now chains hooks instead of overwriting, allowing multiple peripherals to share the same I/O register address.
EEPROM: realistic timing and register latching
The EEPROM peripheral now models the multi-cycle write timing specified in the datasheet. Register values are latched at the start of a write operation and held until it completes, preventing mid-write corruption from concurrent register access.
EEPROM: EEPM=11 (reserved mode)
Writing with EEPM0=1 and EEPM1=1 simultaneously is undefined per the datasheet. It is now treated as a no-op.
USI: clockSrc/mode bit extraction
DelegateWriteHookUsicr had an operator-precedence bug (value & (MASK >> N) instead of (value & MASK) >> N) that caused the wrong bits to be read for USICS and USIWM, making software-clocked shifts non-functional.
USI: pin masking
The output-pin bit mask in UpdateOutput was computed incorrectly, causing writes to the wrong DATA port bit on some pin configurations.
USART: 9-bit mode data mask
The RX data mask for 9-bit frames was using the wrong bit width after the initial datasheet fix, potentially still producing incorrect values when the 9th bit was set.
ATtiny85 Timer1 (TC1)
ATtiny85Simulation now includes Timer1: 8-bit, single TCCR1 register at 0x30, prescalers /1–/64, sharing TIFR/TIMSK with Timer0 via hook chaining.
MmioController: respects write bit-mask
MmioController.Write() now honours the register's bit mask before committing the value, preventing reserved bits from being set by peripheral write hooks.
Hex loader: variable flash sizes and bounds checks
AvrRunner.LoadHex() now correctly handles programs targeting devices with flash sizes other than the default, and skips record data that falls outside the allocated flash buffer.
Watchdog: no duplicate timeout events
Reloading the watchdog timer while a timeout event was already queued could schedule a second event. The redundant event is now cancelled before rescheduling.
SPI: ignore incoming bytes when disabled or in master mode
AvrSpi no longer processes SimulateIncomingMasterByte calls when the peripheral is disabled or configured as a master, matching hardware behaviour.
TWI: interrupt gating for slave mode simulation
SimulateIncomingAddress and SimulateIncomingData no longer raise TWINT when the TWI interrupt enable bit (TWIE) is clear, preventing unexpected handler invocations in polling-mode firmware.
TWI: NACK when TWEA is cleared in slave-receive mode
Clearing TWEA mid-transfer now correctly causes the slave to NACK the next incoming byte (TWSR → 0x88), consistent with the datasheet slave-receive state machine.
Performance
MmioControllerreplaces dictionary-based hook lookups with a flat array indexed by address, eliminating per-access allocation and hash overhead.- Direct register access — all peripherals now read/write
Cpu.Mmio.Data[]directly instead of routing through the hook dispatch path for non-hooked registers. [AggressiveInlining]applied toCpu.Tick(), hot-path peripheral methods, andNativeLutDecoder.- Clock event queue —
AvrClockEventEntrylinked list replaced with a compact array-based queue. NativeLutDecoderuses unsafe function pointers (delegate*<ref Cpu, ref ushort, void>) for maximum dispatch throughput.- ADC peripheral caches reference voltage and sample cycles to avoid recomputing them on every conversion.
Tests
- Added unit tests for
MmioControllerwrite masking and hook-chaining behaviour. - Added TWI slave NACK test (TWEA cleared during receive).
- Added TWI slave interrupt-gating tests for
SimulateIncomingAddress/Data. - Restructured test base classes to reduce per-fixture boilerplate.
Full diff: [v1.0.2...v1.1.0-beta1](https://github.com/bege...
v1.0.2 Release
Release Notes
I am thrilled to announce the official release of AVR8Sharp, an AVR-8 simulator library for .NET, written in C#. With this project, I’ve brought the capabilities of the popular avr8js library to the .NET ecosystem, enabling seamless simulation of machine code for Arduino and other AVR microcontrollers.
Key Features:
- AVR-8 Architecture Simulation: I’ve brought the accurate simulation of the AVR-8 architecture from avr8js to the .NET ecosystem
- Builder Pattern: I designed the library with ease of use in mind, leveraging the Builder pattern for intuitive and quick configuration.
- Cross-Platform Compatibility: As a .NET Standard library, it works across various .NET environments, including console applications and GUI-based tools.
- Extensible and Modular: The library is highly configurable and modular, allowing for the simulation of a wide range of AVR microcontrollers.