You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The emulator is fundamentally a set of devices. Restoring it from a saved state should mean recreating the same set of devices and loading each device's state into it — constructing the emulator from the config, not loading state into a pre-existing instance. This is the foundation under #37 (settings are one slice of per-device state) and the long-term session vision. Full design notes are kept locally; summary below.
Container and boundary
A top-level Emulator owns the device set, its construction, the Dispatcher, and the run loop (lifted out of Spectrum, which shrinks to a pure core device).
Emulator and Dispatcher are distinct: devices are handed the Dispatcher (already the on_event argument), never the Emulator, so device independence is structural — a device cannot reach up, only notify/query peers.
Identity and registration
Device identity = type tag + a container-assigned ordinal / optional name; uniqueness is the container's responsibility. id(self) is never persisted.
DataRecord/Device auto-registration via __init_subclass__, keyed on the bare class name, uniqueness asserted at declaration (so plugins/custom code are restorable).
loading -> config -> run
The config is a SessionDataRecord holding the device set as (type, name, state) plus rollback history.
File loaders (.z80/.rzx/.sna) become config transformers contributing device slices; a reset/model-switch is just amending the config; a rollback is loading a past config.
UnifiedSnapshot becomes SpectrumState, one device slice among many.
Settings and persistence
Settings are the first slice of per-device state: a GetSettings combining query + a SetOption apply, one address (device, option) for live read / live write / persisted key, each option carrying a default and a scope (content vs host).
Environment coupling (global prefs, session save/load, filesystem, raw sound output) becomes opt-in devices, so an API-built emulator is hermetic and reproducible.
Models
A uniform device set across all models; a present device need not actively participate (AY present-but-inactive on a 48K).
The model is broadcast as a descriptor (a DataRecord of facts/properties) via an extended EmulatorReset(model); each device self-configures by reading the properties it cares about, never the model name — so adding e.g. a 16K model touches no device.
Build order — demand-driven, demo-paired (each fundamental landed with the small use case that demonstrates it):
Arc 1: expose SoundDevice's produced samples -> a test that reads them (separate ticket).
The emulator is fundamentally a set of devices. Restoring it from a saved state should mean recreating the same set of devices and loading each device's state into it — constructing the emulator from the config, not loading state into a pre-existing instance. This is the foundation under #37 (settings are one slice of per-device state) and the long-term session vision. Full design notes are kept locally; summary below.
Container and boundary
Emulatorowns the device set, its construction, theDispatcher, and the run loop (lifted out ofSpectrum, which shrinks to a pure core device).EmulatorandDispatcherare distinct: devices are handed theDispatcher(already theon_eventargument), never theEmulator, so device independence is structural — a device cannot reach up, only notify/query peers.Identity and registration
id(self)is never persisted.DataRecord/Deviceauto-registration via__init_subclass__, keyed on the bare class name, uniqueness asserted at declaration (so plugins/custom code are restorable).loading -> config -> run
SessionDataRecordholding the device set as(type, name, state)plus rollback history..z80/.rzx/.sna) become config transformers contributing device slices; a reset/model-switch is just amending the config; a rollback is loading a past config.UnifiedSnapshotbecomesSpectrumState, one device slice among many.Settings and persistence
GetSettingscombining query + aSetOptionapply, one address(device, option)for live read / live write / persisted key, each option carrying adefaultand ascope(content vs host).Models
DataRecordof facts/properties) via an extendedEmulatorReset(model); each device self-configures by reading the properties it cares about, never the model name — so adding e.g. a 16K model touches no device.Build order — demand-driven, demo-paired (each fundamental landed with the small use case that demonstrates it):
SoundDevice's produced samples -> a test that reads them (separate ticket).GetSettings/SetOption(broadcast by option name) -> a Settings menu item for speed (feeds Settings UI: surface the speed, fps and latency knobs (GUI panel, CLI, persistence) #37).DataRecord-> speed survives a restart (seed ofsession.zx).Emulator/Dispatchersplit, and the model descriptor arrive as concrete use cases pull them in.Related: #19 (split into devices), #18 (public API), #37 (settings UI), #23 (playback as a device).