For agents, on top of README.md, which they read first.
run_frameloopstick()until the core reportsFrameRendered. One call is one video frame, never one CPU step; callers count frames with it.- Player-one input is
inputs.p1.set_field(button, pressed). The method is generated by upstream'sdefine_controller_inputs!macro, so it does not appear in a grep forfn set_. wram()andwram_mut()depend on two patches this repo carries on the vendored core (third-party/rust/jgenesis, commits "jgenesis: expose work RAM from snes-core" and "jgenesis: let a host write work RAM"). Upstream keepsMemoryprivate. Re-apply them after any subtree pull.- Index work RAM by offset from
$7E0000. The RAM map in../../research/writes addresses as$7Exxxx; subtract0x7E0000. - The core is deterministic: same ROM, same inputs, same frame count gives a byte-identical picture, optimised or not (checked 2026-09-20 at 1500 frames). A test that compares frames is therefore sound.
snapshotandsnapshot_fixedare different formats on purpose. Every state on disk (resume,home, the MCP tools' named states) issnapshot; onlypackages/replay's keyframes aresnapshot_fixed. Switching either one breaks every file already written in it.