From 969c402284e622125c1027634a0a1757871e9247 Mon Sep 17 00:00:00 2001
From: lumirth <65358837+lumirth@users.noreply.github.com>
Date: Wed, 14 Oct 2026 02:07:03 -0500
Subject: [PATCH 1/2] Rewrite and consolidate documentation for Stickwalker
users
---
.github/ISSUE_TEMPLATE/bug.yml | 9 +-
.github/ISSUE_TEMPLATE/change.yml | 8 +-
.github/pull_request_template.md | 6 +-
CONTEXT.md | 20 --
CONTRIBUTING.md | 55 +---
README.md | 64 +---
assets/README.md | 130 +-------
config/README.md | 47 ---
.../0001-preserve-native-motion-ownership.md | 14 -
docs/adr/0002-m-only-dark-wake.md | 35 ---
docs/build.md | 243 ++-------------
docs/h8-reconstruction.md | 35 ---
docs/references.md | 281 +-----------------
docs/source.md | 229 --------------
docs/third-party.md | 71 +----
stick/README.md | 66 ----
stick/build_port.py | 4 +-
stick/docs/build.md | 80 -----
stick/docs/controls.md | 53 +---
stick/docs/debugging.md | 57 ----
stick/docs/device-settings-and-display.md | 60 ----
stick/docs/firmware-simulation-research.md | 130 --------
stick/docs/history/README.md | 16 -
stick/docs/history/audio.md | 92 ------
stick/docs/history/ir-readiness-regression.md | 146 ---------
stick/docs/history/power-and-wake.md | 113 -------
stick/docs/history/transfers.md | 92 ------
stick/docs/images/three-key-settings.png | Bin 6688 -> 0 bytes
stick/docs/install.md | 114 ++-----
stick/docs/motion.md | 29 --
stick/docs/native-platform.md | 142 ---------
stick/docs/playing.md | 65 ----
stick/docs/pmic-input.md | 46 ---
stick/docs/power.md | 45 ---
stick/docs/release.md | 117 --------
stick/docs/relink.md | 56 +---
stick/docs/validation-history.md | 47 ---
stick/package_release.py | 5 -
stick/simulation/README.md | 235 ---------------
39 files changed, 109 insertions(+), 2948 deletions(-)
delete mode 100644 CONTEXT.md
delete mode 100644 config/README.md
delete mode 100644 docs/adr/0001-preserve-native-motion-ownership.md
delete mode 100644 docs/adr/0002-m-only-dark-wake.md
delete mode 100644 docs/h8-reconstruction.md
delete mode 100644 docs/source.md
delete mode 100644 stick/README.md
delete mode 100644 stick/docs/build.md
delete mode 100644 stick/docs/debugging.md
delete mode 100644 stick/docs/device-settings-and-display.md
delete mode 100644 stick/docs/firmware-simulation-research.md
delete mode 100644 stick/docs/history/README.md
delete mode 100644 stick/docs/history/audio.md
delete mode 100644 stick/docs/history/ir-readiness-regression.md
delete mode 100644 stick/docs/history/power-and-wake.md
delete mode 100644 stick/docs/history/transfers.md
delete mode 100644 stick/docs/images/three-key-settings.png
delete mode 100644 stick/docs/motion.md
delete mode 100644 stick/docs/native-platform.md
delete mode 100644 stick/docs/playing.md
delete mode 100644 stick/docs/pmic-input.md
delete mode 100644 stick/docs/power.md
delete mode 100644 stick/docs/release.md
delete mode 100644 stick/docs/validation-history.md
delete mode 100644 stick/simulation/README.md
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index bf586a2..fea40d6 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -1,22 +1,19 @@
name: Bug report
-description: Report unexpected behavior in Stickwalker.
+description: Report a Stickwalker problem.
body:
- type: textarea
id: reproduction
attributes:
- label: What happened?
- description: Describe the steps, expected result and observed result.
+ label: Steps and result
validations:
required: true
- type: textarea
id: setup
attributes:
- label: Version and setup
- description: Which release or custom build are you using? Is USB connected? For button problems, include the layout and rotation; for connection problems, include the game, distance and alignment.
+ label: Firmware version
validations:
required: true
- type: textarea
id: evidence
attributes:
label: Screenshots or logs
- description: Attach anything that helps explain the problem, if available. Keep full flash and EEPROM backups private because they contain your game data.
diff --git a/.github/ISSUE_TEMPLATE/change.yml b/.github/ISSUE_TEMPLATE/change.yml
index 84e4fef..2948eaf 100644
--- a/.github/ISSUE_TEMPLATE/change.yml
+++ b/.github/ISSUE_TEMPLATE/change.yml
@@ -4,12 +4,6 @@ body:
- type: textarea
id: need
attributes:
- label: What would this help you do?
- description: Describe the use case and any current obstacle.
+ label: What would you change?
validations:
required: true
- - type: textarea
- id: proposal
- attributes:
- label: Proposed behavior
- description: Include an example or tradeoff if you have one.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 49797e7..c8b9bfb 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,5 +1 @@
-Describe the problem and resulting behavior.
-
-## Validation
-
-Record the checks run and any physical qualification still needed.
+
diff --git a/CONTEXT.md b/CONTEXT.md
deleted file mode 100644
index f786114..0000000
--- a/CONTEXT.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Stickwalker terms
-
-Stickwalker is firmware for recreating a Pokéwalker on the M5Stack Stick S3.
-The reconstructed firmware implements the game and infrared protocol.
-The Stick adapters connect it to the new hardware.
-
-| Term | Meaning |
-| --- | --- |
-| Native firmware | The original Pokéwalker program represented by the matching H8 reconstruction. |
-| Experience fidelity | Recreating gameplay, progression, screen availability, responsiveness, walking and game compatibility on the Stick. Hardware settings can change to produce the same experience. |
-| Native UI cadence | The application's schedule for rendering and visual state. Ordinary refresh opportunities occur every quarter second; individual animations may advance more slowly. |
-| Panel scan rate | The TFT controller's internal rate for driving its stored image, independent of application cadence and processor image transfers. |
-| Stationary carry | Screen-off operation detecting whether meaningful movement has begun. |
-| Moving carry | Screen-off operation collecting and processing acceleration for walking progression. |
-| Interactive use | Viewing or controlling the UI, including idle-on time after the last action. |
-| Native screen availability | The original rules for waking, remaining visible and turning the display off, with foreground-specific exceptions. |
-| Motion processing equivalence | Agreement in native motion/game results from the same chronological samples, external events and initial state. This detects scheduling regressions. |
-| Motion accuracy | Agreement between detected steps and actual walking, measured on hardware and affected by sensor input, carrying position and the estimator. |
-| Native sampling gap | An interval where sound or IR owns foreground execution and shared state, suspending native motion sampling. Ordinary menus and games continue sampling. |
-| IR session | A native protocol exchange with its peer, retaining foreground ownership and timing requirements. |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6625811..3c4a763 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,56 +1,9 @@
# Contributing
-For firmware changes, start with the [Stick development guide](stick/README.md)
-or the [application source guide](docs/source.md).
+Use [Issues](https://github.com/lumirth/stickwalker/issues) for bugs and feature requests. Run the [firmware and formatting checks](docs/build.md#checks) for code changes.
-## Report a problem
+Test through compiled-firmware workflows with physical or simulated inputs and independent expected results. Extend an existing workflow where possible. Do not add unit tests, integration tests or tests that mirror the implementation.
-Include the release version, or commit for a custom build, what you did, and
-what happened. A short sequence that reproduces the problem is especially
-useful. Also include the details relevant to it:
+Changes to shared Pokéwalker code must still match the original H8 binary under both supported compilers.
-- For buttons or wake, the layout, rotation and whether USB was connected.
-- For infrared, the photodiode wiring, other device or game, distance and
- orientation. Include how often the connection succeeds or fails.
-- For a test script, the command and relevant output.
-
-Keep flash backups and EEPROM images private; they contain registration and
-game data. Share the diagnostic output needed to describe the problem.
-
-## Propose a change
-
-Explain the problem and how the patch changes the Stick's behavior. Stick
-hardware adapters live in `stick/`; game rules and the infrared protocol live
-in `src/`. Preserve stored record sizes, byte order and the order in which tasks
-use shared working memory.
-
-Run `uv run python stick/check.py` for code changes. It builds the firmware and
-executes the E2E workflows.
-Describe the device tests you ran. Shared H8 application changes also need the
-format, lint and binary-matching checks in the [H8 build guide](docs/build.md).
-For documentation, check the instructions and links you changed.
-
-## Tests
-
-Run the compiled firmware through complete user workflows. Drive physical
-inputs on the Stick or its simulated board, then check what someone can see,
-hear, receive over infrared or recover from saved flash. Establish expectations
-from the user contract, protocol specification or independent recordings.
-
-The automated [firmware E2E checks](stick/simulation/README.md) boot the production
-image through the ESP32-S3 ROM and its bootloader. Their board models implement
-GPIO and bus devices from the hardware specifications. Keep the application and
-SDK drivers in that execution path.
-
-Review coverage before adding a test for a bug fix. Extend a relevant workflow
-when there is a genuine gap. Remove unit and integration tests,
-tautologies and tests that merely detect implementation changes. Expectations
-must describe useful behavior and come from evidence independent of the code
-being exercised. Apply this review to existing tests too.
-
-## Source and license
-
-Contributions use the project's [MIT license](LICENSE). Credit third-party
-material and keep its notices. Keep ROM dumps, extracted retail artwork,
-proprietary compiler binaries and confidential source out of contributions.
-See [third-party notices](docs/third-party.md) for the included dependencies.
+Contributions use the [MIT license](LICENSE). Retain third-party notices. Do not commit ROMs, extracted retail artwork or proprietary compilers.
diff --git a/README.md b/README.md
index e3e6961..444ebb4 100644
--- a/README.md
+++ b/README.md
@@ -1,67 +1,27 @@
-
+
# Stickwalker
-Stickwalker is firmware that turns an M5Stack Stick S3 into a recreation of the
-Pokéwalker. Take a Pokémon from HeartGold or SoulSilver for a walk, earn Watts,
-catch Pokémon and find items, then send your Pokémon and finds back to the game
-over infrared.
+Stickwalker ports my [matching Pokéwalker firmware reconstruction](https://github.com/lumirth/pw) to the M5Stack Stick S3.
-I built it from my [binary-matching reconstruction of the Pokéwalker firmware](docs/h8-reconstruction.md).
-It runs the original game logic, including step counting, minigames, progression,
-sounds and communication with the games and other walkers.
+## Install
-## Get started
+You need a Stick S3, a BPV10NF photodiode and a USB-C data cable. Connect the photodiode's anode to GND and cathode to G5.
-You'll need:
+Download the firmware ZIP from [Releases](https://github.com/lumirth/stickwalker/releases) and follow the [installation guide](stick/docs/install.md).
-- An M5Stack Stick S3.
-- A BPV10NF photodiode for receiving infrared. The Stick has a built-in transmitter.
-- A USB-C data cable and a computer to install the firmware.
-- An infrared-equipped HeartGold or SoulSilver Game Card and a console that can run it.
+Downloads use placeholder built-in artwork. For the original graphics, [build with artwork from your own ROM](docs/build.md#artwork).
-Download the firmware ZIP from [Releases](https://github.com/lumirth/stickwalker/releases),
-then follow the [installation guide](stick/docs/install.md). It covers backups,
-firmware updates and the two photodiode connections, G5 and GND. Once the Stick
-is ready, [connect to your game and choose a Pokémon to take with you](stick/docs/playing.md).
+## Controls
-The download uses placeholder pictures and a font for the built-in screens.
-Pokémon and route artwork arrive from your game. You can also
-[build the firmware](stick/docs/build.md) with
-[artwork extracted from your own Pokéwalker firmware](assets/README.md).
+L is Left, M is Center and R is Right. The default orientation is left-side-down.
-## The Pokéwalker on the Stick S3
+Hold M for about half a second to wake the screen. Hold L+R for one second to open Stick Settings. See [controls](stick/docs/controls.md) for the other layouts.
-Step counting uses the Pokéwalker's original algorithm. Both devices use Bosch
-accelerometers, the BMA150 in the Pokéwalker and the BMI270 in the Stick.
-Stickwalker converts the readings to the original scale and processes them at
-the original cadence, so step tracking should behave much like an original
-Pokéwalker. [How step counting works](stick/docs/motion.md) explains the details.
+## Development
-The original 96×64 screen appears at twice its size on the Stick's display.
-You can rotate it and choose a light or dark appearance. The speaker plays the
-original tone sequences. Your Pokémon, route and progress stay saved across
-restarts. Return your Pokémon to HGSS before updating pre-release firmware;
-updates can require resetting game data.
+[Build instructions](docs/build.md) · [Contributing](CONTRIBUTING.md) · [Issues](https://github.com/lumirth/stickwalker/issues)
-Walking continues while the screen is off. Hold M for half a second to wake it.
-With the default controls, L moves left, M confirms, and R moves right.
-The screen starts left-side-down. Hold L+R for one second to open Stick Settings.
-See [controls and settings](stick/docs/controls.md)
-for the other button layouts.
-
-## Help and contribute
-
-To report a problem or work on the firmware, start with
-[Contributing](CONTRIBUTING.md). The [development guide](stick/README.md) maps
-out the code. [Release notes](https://github.com/lumirth/stickwalker/releases)
-cover changes and testing for each version; the
-[validation history](stick/docs/validation-history.md) records the device trials.
-
-Stickwalker is an unofficial project by [lumirth](https://github.com/lumirth),
-released under [MIT](LICENSE). Dependencies and marked assets have their own
-[licenses and notices](docs/third-party.md). The
-[references and acknowledgments](docs/references.md) credit the research and
-tools used in the reconstruction and port.
+By [lumirth](https://github.com/lumirth), under [MIT](LICENSE). [Credits](docs/references.md) and [third-party licenses](docs/third-party.md).
diff --git a/assets/README.md b/assets/README.md
index c772be7..a509caf 100644
--- a/assets/README.md
+++ b/assets/README.md
@@ -1,129 +1,19 @@
-# Artwork inputs
+# Edit built-in artwork
-The H8 build reads six NCG images and a BMP font sheet. Each file comes from
-`assets/local/` when present, with original artwork in `assets/placeholders/`
-filling any missing entry. The build reports the placeholders it uses.
+Copy the images and palette you want to edit from `assets/placeholders/` into `assets/local/`, or [extract them from your own ROM](../docs/build.md#artwork).
-Extract the resident artwork and font once:
+Build with `--artwork local` to use your edits.
-```sh
-uv run python -m tools.assets extract /path/to/retail.bin
-```
-
-Extraction checks the ROM's size and SHA-256, then saves the seven images and an
-editor palette in the ignored `assets/local/` directory. Future builds read those
-files, so the ROM can be moved or removed. Existing files are protected; use
-`--force` to replace the extracted images and palette. Other files in the local
-directory are preserved. Keep a copy of edited artwork before extracting again.
-
-| File | Size | Contents |
-| --- | --- | --- |
-| `walker.ncg` | 32 × 32 | Device image used by the connection screens |
-| `face-neutral.ncg` | 16 × 8 | Waiting expression |
-| `face-smile.ncg` | 16 × 8 | Successful connection expression |
-| `face-frown.ncg` | 16 × 8 | Failed connection expression |
-| `button-arrow.ncg` | 8 × 8 | Button prompt |
-| `ir-signal.ncg` | 8 × 8 | Infrared activity indicator |
-| `font.bmp` | 108 × 8 | 36 adjacent 3 × 8 glyphs: `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ` |
-| `grayscale.ncl` | 16 palette entries | Editor palette; the first four entries show the firmware shades |
-
-For Stickwalker, the default build always uses placeholders. After extracting
-or editing local images, select them with `--artwork local`; missing entries
-fall back to placeholders. See the [Stick build guide](../stick/docs/build.md).
-
-## Editing
-
-To create custom artwork without a ROM, copy the desired placeholder files and
-`grayscale.ncl` into `assets/local/` and edit them there.
-
-NCG and NCL are Nitro intermediate graphics formats supported by
-[NitroPaint](https://github.com/Garhoogin/NitroPaint). Keep four-bit tiles,
-palette zero, and pixel indices 0 through 3. Those indices select white, light
-gray, dark gray, and black. The supplied NCL palette displays this mapping in
-an editor.
-
-For an ordinary bitmap editor, convert an image to BMP and back:
+Open NCG images in [NitroPaint](https://github.com/Garhoogin/NitroPaint), or convert them to BMP for another editor.
```sh
-uv run python -m tools.assets convert assets/local/walker.ncg walker.bmp
-# Edit walker.bmp, preserving its dimensions and grayscale colors.
-uv run python -m tools.assets convert walker.bmp assets/local/walker.ncg --force
+uv run -m tools.assets convert assets/local/walker.ncg walker.bmp
+# Edit walker.bmp.
+uv run -m tools.assets convert walker.bmp assets/local/walker.ncg --force
```
-Use opaque grayscale values **255, 170, 85, and 0**. The converter also accepts
-**173 and 82**, the neighboring display values produced by the NCL palette's
-five-bit channels. The font uses opaque **255 and 0**. Disable antialiasing while
-drawing, and use nearest-neighbor scaling when viewing pixel art. Unsupported
-colors produce an error identifying the file and pixel to fix.
-
-The font sheet can be edited directly as a BMP. Every three consecutive columns
-form one glyph, and every row of each cell is preserved. The firmware supplies
-the blank separator column when drawing text.
-
-### Walker pixels and rest timing
-
-The sound engine's 42 note-period constants are defined in
-`src/application/pw_builtin.c`, immediately before the walker image. Rest code
-`0x7D` indexes beyond that table into the image and uses packed byte `0x53` as
-its duration divisor. That byte is **2** in the retail image. Zero creates a
-zero divisor; other values change the duration of rests.
-
-All 256 walker bytes describe image pixels. Byte `0x53` is the low bit plane of
-column **9**, rows **8–15**, in the generated firmware packing. To preserve it,
-use light gray or black at **(9, 9)** and white or dark gray at **(9, 8)** and
-**(9, 10–15)**. Coordinates start at zero.
+Keep the original dimensions and use opaque grayscale values 255, 170, 85 and 0. The converter also accepts 173 and 82. Edit `font.bmp` directly, using only 255 and 0. Turn off antialiasing.
-The placeholder includes a light-gray pixel at (9, 9) for this purpose.
-`config/artwork.json` records the required byte, and conversion checks it for
-both local and placeholder images. An incompatible edit stops conversion with
-the filename, packed offset, and expected value.
-
-## File conversion
-
-[`config/artwork.json`](../config/artwork.json) records each resource's ROM range,
-dimensions, firmware packing, and shared audio-byte requirements.
-[`tools/assets.py`](../tools/assets.py) performs
-extraction and conversion. BMP handling uses
-[Pillow](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#bmp).
-
-The sprite files use the `NCCG` signature and a `CHAR` block containing dimensions
-in tiles, four-bit color depth, and 8 × 8 tiles. Tiles appear from left to right
-and top to bottom; each byte holds the left pixel in its low nibble. The public
-[NitroPaint NCG reader](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroCharacter.c#L642)
-and [NCL writer](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroPalette.c#L513)
-describe these containers.
-
-Extraction reconstructs the sprite pixels from the ROM. Its NCG writer supplies
-fresh palette-zero tile attributes and a link to `grayscale.ncl`. The palette lets an editor display the four firmware shades. The BMP font sheet
-arranges the stored glyph bytes into editable cells.
-
-The generated firmware images consist of eight-row pages, read from top to
-bottom. Each page contains columns from left to right. A column stores two bytes:
-the high bit plane, then the low bit plane. Bit zero describes the upper pixel.
-This is the layout consumed by `DisplayBlit` and `RasterColumn`.
-
-The font stores one byte per column, with bit zero at the top row and a set bit
-for black. `DisplayText` reads the 108 column bytes as 36 adjacent glyphs. All
-eight rows survive extraction and conversion, including pixels outside the
-usual letter height.
-
-Conversion produces the aggregate initializer included by
-`src/application/pw_builtin.c`.
-The font and all six images retain their byte extents and order. Artwork sent
-from the console into serial EEPROM is handled by the firmware's communication
-path and remains separate from these resident images.
-
-## Placeholders
-
-The bundled artwork shows a simple rectangular device, facial expressions, a
-button arrow, a radio symbol, and small alphanumeric glyphs. Its drawing source
-is [`placeholders/draw.py`](placeholders/draw.py). Regenerate the files with:
-
-```sh
-uv run python -m assets.placeholders.draw
-```
+If conversion flags byte `0x53` in `walker.ncg`, keep pixel 9,9 light gray or black, and pixels 9,8 and 9,10 through 9,15 white or dark gray. Coordinates start at zero.
-The source and images are [available under CC0](placeholders/LICENSE.md).
-Placeholder builds retain the resident image dimensions and glyph cells while
-producing a different firmware hash. The firmware continues to obtain its other
-artwork through console-provided EEPROM resources.
+The bundled placeholders are [CC0](placeholders/LICENSE.md).
diff --git a/config/README.md b/config/README.md
deleted file mode 100644
index 4515cd4..0000000
--- a/config/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Build configuration
-
-`artwork.json` is shared by both targets. The remaining files describe the H8
-build and the tools used around it. `configure.py`
-reads them to generate `build.ninja`; the build utilities also record their
-contents with the produced firmware.
-
-| File | Purpose |
-| --- | --- |
-| `build.json` | Shared compiler options, per-module flag overrides, linker sections, and the retail image's size and SHA-256 |
-| `artwork.json` | Resident asset names, ROM extraction ranges, image dimensions, packed pixel formats, and bytes shared with audio timing |
-| `toolchains.json` | Fingerprints of the qualified 6.02.01 and 6.02.02 suites and known updater packages |
-| `host-tools.json` | Managed Wibo releases, download locations, and integrity hashes |
-| `h8.xml` | Cppcheck's target widths and alignment for the H8 C implementation |
-| `lint-suppressions.txt` | Two narrow Cppcheck exceptions for preserved target arithmetic |
-
-Compiler fingerprints describe tested inputs. Other complete installations can
-be imported and built with a warning. Each build records the actual compiler
-files selected on that machine. Downloaded Wibo executables must pass their
-recorded integrity hash before use.
-
-`tools/modules.py` discovers C files recursively under `src/` and sorts their
-unique lowercase basenames. Configuration, compilation, linking, and receipt
-validation use the resulting module list. Directory paths serve navigation;
-basename order determines object order.
-
-`build.json` supplies extra compiler options through `source_flags`, keyed by
-the full lowercase basename including `.c`:
-
-```json
-{
- "source_flags": {
- "h8_rominfo.c": ["-section=const=M"],
- "pw_fourier.c": ["-speed=shift,loop=2"],
- "pw_pedometer.c": ["-speed"]
- }
-}
-```
-
-Other modules use the common compiler options. An override must name an
-existing source. Project `#include` directives name headers relative to
-`include/`, such as `application/pw_pedometer.h` or `support/lib_common.h`.
-
-Source order, integer widths, compiler flags, and linker placement can affect
-retail identity. After changing them, build under both qualified suites and run
-`uv run ninja verify` for each. The [build guide](../docs/build.md) explains the
-commands, and the [artwork guide](../assets/README.md) explains the image formats.
diff --git a/docs/adr/0001-preserve-native-motion-ownership.md b/docs/adr/0001-preserve-native-motion-ownership.md
deleted file mode 100644
index beb131c..0000000
--- a/docs/adr/0001-preserve-native-motion-ownership.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Process motion samples on the original schedule
-
-Stickwalker processes each acceleration sample as it arrives, using the
-Pokéwalker's inactive and walking schedules and its 64-sample FFT window.
-Sound and infrared pause sampling while they use the shared working memory.
-Menus and games continue sampling.
-
-This preserves the order in which the original program receives movement and
-input. Buffering samples for later processing would also require replaying
-button events and task changes in that order. The port therefore keeps
-immediate processing.
-
-See the [screen-off wake policy](0002-m-only-dark-wake.md) for how buttons affect
-this schedule.
diff --git a/docs/adr/0002-m-only-dark-wake.md b/docs/adr/0002-m-only-dark-wake.md
deleted file mode 100644
index 901a46d..0000000
--- a/docs/adr/0002-m-only-dark-wake.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Use M to wake the screen
-
-Holding physical M through native Center qualification wakes the Stick in every
-layout and rotation, roughly half a second. The native firmware also retains
-its social-offer opportunity; the adapter no longer starts interactive mode
-from a separate 500 ms wall-clock threshold.
-L and R resume their configured roles after wake. Releasing M early cancels
-panel preparation. A held L or R still allows M to wake the display.
-
-While the screen is off, EXT1 watches M and the next game deadline sets the
-timer wake. L polling stops. After wake, the adapter clears L's retained event
-and waits for all buttons to be released. A successful PMIC read is required
-to establish that L has been released.
-
-When the game or Settings is visible, the front buttons can wake the CPU and
-required button polls supply a 5 ms deadline. Walking and interactive game work
-retain their native timing instead of accelerating to consume queued input.
-
-## GPIO restoration
-
-EXT1's incremental API appends pins. Clear its old mask before switching
-between M and M/R. After every sleep attempt, return both pads to digital GPIO,
-including when the sleep controller rejects entry. Configuration failures
-keep the CPU awake until a scheduled retry.
-
-## Checks
-
-Production-firmware simulation covers timeout, canceled and qualified M holds,
-held-input suppression, RTC timer/EXT1 wake and digital GPIO restoration. The
-[device test records](../../stick/docs/history/power-and-wake.md) record the
-button, panel and boot trials. USB keeps the CPU awake during bench commands;
-test physical sleep and wake on battery.
-
-The [ESP-IDF sleep API](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/sleep_modes.html)
-describes the wake-source and GPIO functions.
diff --git a/docs/build.md b/docs/build.md
index ed1d73d..4e59bb6 100644
--- a/docs/build.md
+++ b/docs/build.md
@@ -1,246 +1,57 @@
-# Building the H8 reconstruction
+# Build Stickwalker
-For M5StickS3, use the [Stickwalker build guide](../stick/docs/build.md).
-
-Compiler import and artwork extraction are separate, one-time setup operations.
-Compilation converts the extracted files and runs the imported tools. Its final
-check uses the retail image's recorded size and SHA-256, so it works without a
-ROM file.
-
-## Host setup
-
-The supported hosts are macOS Arm64 using Wibo/Rosetta and Windows x86_64
-running the compiler directly.
-
-Install [uv](https://docs.astral.sh/uv/getting-started/installation/) through your
-preferred package manager. For example, macOS with Homebrew:
+Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and [Arduino CLI](https://arduino.github.io/arduino-cli/latest/installation/). Run these commands from the repository root.
```sh
-brew install uv
-softwareupdate --install-rosetta
+git clone https://github.com/lumirth/stickwalker.git
+cd stickwalker
+uv sync --locked
+arduino-cli config add board_manager.additional_urls https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json
+arduino-cli core update-index
+arduino-cli core install m5stack:esp32@3.3.9
+arduino-cli lib install 'M5GFX@0.2.29'
+arduino-cli lib install --no-deps 'M5Unified@0.2.21'
+uv run python stick/build_port.py
```
-On Windows with WinGet:
-
-```powershell
-winget install --id=astral-sh.uv -e
-```
-
-Run the remaining commands from the repository root. `uv run` prepares Python
-and the dependencies recorded in `uv.lock`. The default Python series is 3.12;
-the tools support Python 3.11 or newer. Ninja schedules compilation and Pillow
-reads and writes BMP images. The C compiler remains user-provided.
-
-On macOS, configuration downloads the Wibo version recorded in
-`config/host-tools.json` and validates it before execution. To use an existing
-launcher, pass `--wibo PATH` to `configure.py` or set `PW_WIBO`. To return to the
-managed launcher, use `--managed-wibo`. Windows uses native execution.
+The firmware is in `stick/.build/output/`. Follow [installation](../stick/docs/install.md) to flash it.
-Compiler processes run in separate scratch directories with short ASCII paths.
-A checkout can contain spaces. If the system temporary directory is unsuitable,
-pass `--work-dir PATH` naming an existing writable ASCII directory.
+## Artwork
-## Import a compiler
+Extract the built-in graphics from your own 49,152-byte Pokéwalker ROM, then select them when building.
```sh
-uv run -m tools.toolchain import path/to/compiler
+uv run -m tools.assets extract /path/to/pokewalker.bin
+uv run python stick/build_port.py --artwork local
```
-The input can be a suite directory, its `bin` directory, `ch38.exe`, an enclosing
-HEW installation containing one suite, an H8 updater executable, or a ZIP
-containing one H8 updater. The importer reads archives directly and keeps the
-compiler phases, runtime packs, and headers together in `.local/toolchains/`.
-The original installation or installer is needed only during import.
+The supported ROM has SHA-256 `f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607`.
-These suites reproduce the retail image:
+For custom graphics, see [editing artwork](../assets/README.md).
-| Suite | CH38 | OPTLNK | Known package |
-| --- | --- | --- | --- |
-| 6.02.01 | 6.02.01 | 9.04.01 | `h8v6201u.exe` |
-| 6.02.02 | 6.02.02 | 9.05.00 | `h8v6202u.exe` or `h8v6202u-doc-e.zip` |
+## Checks
-The `h8v6202u-doc-e.zip` archive contains the updater and can be imported directly.
-Importing an updater supplies a complete compiler suite without running the
-installer or having an existing HEW installation.
-
-`config/toolchains.json` records known package and installed-file hashes.
-Unfamiliar fingerprints warn and remain usable. Missing necessary compiler
-components or malformed archives stop import with a diagnostic. The matching
-requirement applies to the produced image when strict verification is requested.
-
-Use `--name LABEL` to give an import a distinct local name, especially when
-trying another bundle of the same compiler revision:
+Install Rust 1.98.1 and Tesseract. On macOS, install Tesseract with `brew install tesseract`. On Debian or Ubuntu, use `sudo apt-get install tesseract-ocr`. Then run the firmware workflows.
```sh
-uv run -m tools.toolchain import path/to/other/bundle --name experiment
-uv run -m tools.toolchain list
-uv run configure.py --toolchain experiment
+uv run python stick/check.py
```
-Reimporting a name replaces that local installation after the new input has been
-parsed and checked. A failed replacement restores the prior installation; if
-restoration also fails, the error identifies the retained recovery directory.
-
-## Extract artwork
+For formatting and static analysis, install Cppcheck 2.21.0 and run:
```sh
-uv run -m tools.assets extract path/to/retail.bin
-```
-
-The supported image is **49,152 bytes**, with SHA-256:
-
-```text
-f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607
+uv run --group dev -m tools.check
```
-Extraction checks this identity because the resource offsets describe that
-image. It saves six NCG sprite files, a BMP font, and an NCL editor palette in
-`assets/local/`. Existing files require `--force` before replacement. See the
-[artwork guide](../assets/README.md) for formats, conversion and editing.
+## Build the original H8 target
-You can build before extraction: any missing image uses its original CC0
-placeholder. An existing image with invalid dimensions or colors produces an
-error identifying what needs correction. Placeholder use appears in the build
-report and normally changes the output hash.
-
-The editable containers preserve the recovered pixels and their packing. The
-NCG writer supplies fresh editor metadata; the BMP font sheet is a reconstructed
-representation of the glyphs.
-
-## Compile and check
-
-```sh
-uv run configure.py
-uv run ninja
-```
-
-Configuration selects the previous compiler, or the only available import. With
-multiple imports, select one explicitly:
+Use Renesas H8 compiler suite 6.02.01 or 6.02.02. Windows x86_64 runs it directly. On macOS Apple Silicon, install Rosetta; configuration supplies Wibo.
```sh
+uv run -m tools.toolchain import /path/to/compiler-or-updater
uv run configure.py --toolchain 6.02.01
uv run ninja
-```
-
-Outputs are written to `build//`:
-
-| Output | Contents |
-| --- | --- |
-| `pw.bin`, `pw.map` | Firmware image and linker map |
-| `rom_assets.h` | Converted resident artwork arrays |
-| `pw_pedometer.obj`, `pw_pedometer.log`, `pw_pedometer.json` | A module's object, compiler output, and input/output receipt; each C module has its own set |
-| `runtime.log`, `link.log` | Runtime-library and linker output |
-| `artwork.json`, `runtime.json`, `link.json`, `build.json` | Actual source, artwork, compiler, recipe and output hashes |
-| `status.json` | Image size, SHA-256, retail-match result and placeholder use |
-
-The object name comes from the C file's basename: for example,
-`src/application/pw_pedometer.c` produces `build//pw_pedometer.obj`.
-The build discovers sources recursively and links their objects in alphabetical
-basename order. Names must be unique and lowercase across the source tree.
-Directory moves preserve that ordering; filename changes can affect the image.
-See the [source guide](source.md#modules-and-reading-paths) for the module families.
-
-A different output hash is a warning. A completed modified build returns success
-and leaves `pw.bin` available. Compiler errors, invalid assets and inconsistent
-build artifacts return failure with an actionable message.
-
-For release verification, use:
-
-```sh
uv run ninja verify
```
-This requires the exact retail size and SHA-256 and writes `verified.json` on
-success. Run it under **both 6.02.01 and 6.02.02** for accepted matching-source
-changes. The build records also check that the image came from the current
-source, assets and compiler files. They catch stale objects even when timestamps
-have been preserved. Firmware E2E checks and formatting complement this check.
-
-## Clean, recover and work offline
-
-Remove generated outputs for the selected compiler:
-
-```sh
-uv run ninja -t clean
-uv run ninja
-```
-
-The clean command keeps `assets/local/`, imported compilers, and cached Wibo.
-To clear another compiler's outputs, configure that compiler and run the same
-clean command. `ninja -t clean` removes the outputs declared in the graph;
-extra files saved manually in `build/` remain there.
-
-Ninja rebuilds a changed C file individually; a header change rebuilds all
-firmware units. Discovery includes nested source and header directories, and
-source additions or removals regenerate the graph. Asset edits rebuild
-conversion, `pw_builtin.c`, and the linked image. Adding or removing a local asset,
-or restoring the local artwork directory, automatically updates its fallback
-selection. Changing imported compiler files refreshes their recorded inventory
-before the next build. A compiler whose required files have been removed needs
-to be reimported.
-
-After moving the checkout, rerun `uv run configure.py` to select the local
-launcher and scratch paths. If a build reports obsolete or replaced artifacts,
-refresh the graph before cleaning and rebuilding:
-
-```sh
-uv run configure.py
-uv run ninja -t clean
-uv run ninja
-```
-
-Compilation errors include the failing tool's log path. Fix the reported input
-and repeat the build.
-
-Once dependencies and Wibo are cached, the setup and build can run offline:
-
-```sh
-uv run --offline configure.py --offline
-uv run --offline ninja
-```
-
-For an offline machine, prepare its Python environment and required host tools
-in advance. The compiler and artwork import commands operate on local files.
-
-## Development checks
-
-The `dev` dependency group supplies clang-format 21.1.8 and Ruff. Install
-Cppcheck 2.21.0 separately, then run:
-
-```sh
-uv run --group dev -m tools.check
-```
-
-This checks C formatting, Python formatting and lint, and Cppcheck with the H8
-target model. Run `uv run python stick/check.py` separately for the production
-firmware workflows. To apply formatting:
-
-```sh
-uv run --group dev -m tools.check --format
-```
-
-Formatting includes nested source and header directories. The accepted HEW
-device header at `include/startup/iodefine.h` keeps its original formatting.
-Cppcheck has two narrow suppressions for arithmetic whose evaluation follows
-the target's 16-bit rules.
-
-`uv` is a convenience for a reproducible host environment. An existing Python
-environment with Ninja and Pillow can also run `python configure.py` and `ninja`;
-the dependency versions are recorded in `uv.lock`.
-
-## Why generate Ninja?
-
-Ninja represents each build output and dependency explicitly. `tools/modules.py`
-resolves the recursively discovered sources into one ordered module list used
-by configuration, compilation, linking, and receipt validation.
-`config/build.json` holds common options, per-module flag overrides, and section
-placement. Common dependency groups keep repeated paths out of each
-compiler command. See [Ninja's design](https://ninja-build.org/manual.html#_philosophical_overview).
-
-Separate extraction and compilation are established decompilation workflows;
-[SM64](https://github.com/n64decomp/sm64/blob/master/extract_assets.py) retains
-extracted assets and [OOT](https://github.com/zeldaret/oot#4-setup-the-rom-and-build-process)
-provides a separate asset-setup step. This project combines that workflow with
-[uv's managed project environment](https://docs.astral.sh/uv/guides/projects/).
-Ninja already supplies incremental builds and cleaning, so those tasks need no
-additional task runner.
+Extract the original artwork first to reproduce the reference ROM. Repeat with `--toolchain 6.02.02` when changing shared application code.
diff --git a/docs/h8-reconstruction.md b/docs/h8-reconstruction.md
deleted file mode 100644
index 126f715..0000000
--- a/docs/h8-reconstruction.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# The H8 firmware reconstruction
-
-The application in `src/` and `include/` reconstructs the Pokéwalker's firmware.
-With retail artwork and a supported Renesas compiler suite, it builds a binary
-identical to the reference image with SHA-256:
-
-```text
-f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607
-```
-
-## Build the original target
-
-Provide a Renesas H8/H8S/H8SX compiler installation. Suites 6.02.01 and 6.02.02
-are supported. The compiler's 32-bit Windows executables run natively on Windows
-x86_64 and through Wibo/Rosetta on macOS arm64.
-
-Follow the [H8 build guide](build.md) to import the compiler, configure and
-build. Placeholder artwork is sufficient for studying or modifying the program.
-To reproduce the reference hash, [extract the resident artwork](../assets/README.md)
-from your own firmware image and run `uv run ninja verify` under each supported
-suite.
-
-For an M5StickS3 build, use the [Stickwalker build guide](../stick/docs/build.md).
-That build uses the M5Stack Arduino toolchain.
-
-## Read and contribute
-
-The [source guide](source.md) follows boot, scheduling, motion, storage and
-infrared across modules. File prefixes preserve the observed link order;
-renaming a file can alter binary placement. [Build configuration](../config/README.md)
-describes the associated flags and sections.
-
-Follow [Contributing](../CONTRIBUTING.md) when changing either target. See the
-[references and acknowledgments](references.md) for research sources and
-community projects.
diff --git a/docs/references.md b/docs/references.md
index 269e6c4..82f54bd 100644
--- a/docs/references.md
+++ b/docs/references.md
@@ -1,280 +1,13 @@
-# References
+# Credits
-This document catalogs the sources consulted for this project and related
-community projects.
+Stickwalker builds on [lumirth/pw](https://github.com/lumirth/pw), my matching reconstruction of the Pokéwalker firmware.
-Source-code links use the consulted revision where it is recorded. License notices for third-party tools appear in the [third-party acknowledgments](third-party.md).
+UnrealPowerz's [pw_firm](https://github.com/UnrealPowerz/pw_firm) inspired the reconstruction. h4lfheart's [PocketWalker](https://github.com/h4lfheart/PocketWalker) inspired the initial Pokéwalker work.
-## Pokéwalker research and game behavior
+Dmitry Grinberg's [Pokéwalker research](https://dmitry.gr/?r=05.Projects&proj=28.%20pokewalker), Martin Korth's [GBATEK](https://problemkaputt.de/gbatek.htm#dscartinfraredpwalkerircommands), [PRET's HGSS reconstruction](https://github.com/pret/pokeheartgold) and mamba2410's [reverse-pokewalker](https://github.com/mamba2410/reverse-pokewalker) supplied research and references.
-- Dmitry Grinberg's investigation of the hardware, firmware, infrared protocol,
- EEPROM contents, and device workflows. The accompanying annotated firmware
- disassembly and DECODEIMG utility helped interpret instructions and packed
- artwork.
- [PokéWalker hacking](https://dmitry.gr/?r=05.Projects&proj=28.%20pokewalker).
-- Martin Korth's technical reference covering Pokéwalker commands, memory
- organization, peripherals, and cartridge communication.
- [GBATEK: Pokéwalker IR commands](https://problemkaputt.de/gbatek.htm#dscartinfraredpwalkerircommands).
-- The memory-map tables used to investigate persistent records, EEPROM regions,
- resource boundaries, and their RAM counterparts.
- [GBATEK: Pokéwalker memory map](https://problemkaputt.de/gbatek.htm#dscartinfraredpwalkermemorymap).
-- The register tables used to identify hardware accesses, including the RTC
- time registers.
- [GBATEK: H8/386 special-function registers](https://problemkaputt.de/gbatek.htm#h8386sfrs).
-- nocash's firsthand account of reconstructing command tables and correcting
- packet lengths and memory-map interpretations, consulted during the
- persistent-record and protocol investigations.
- [NESdev Pokéwalker discussion](https://forums.nesdev.org/viewtopic.php?start=30&t=21140).
-- The public reconstruction of HeartGold and SoulSilver, used to understand
- Pokémon data, saved records, serialization, and the console endpoint.
- [PRET pokeheartgold, consulted revision](https://github.com/pret/pokeheartgold/tree/0985e8718df4f25e64d6507d89c0c97c0d288981).
-- The principal HGSS communication application, used to follow complete
- Pokéwalker transactions and their effects on game state.
- [PRET's Pokéwalker overlay](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/asm/overlay_112.s).
-- Community reverse engineering covering infrared communication, captures,
- compression, and stored data.
- [mamba2410/reverse-pokewalker](https://github.com/mamba2410/reverse-pokewalker).
-- Tools and experiments connecting HGSS under DeSmuME to a software Walker,
- together with EEPROM and artwork inspection utilities.
- [Pusty/PokewalkerUtils](https://github.com/Pusty/PokewalkerUtils).
-- A public implementation useful for understanding how course data is
- assembled and selected for transfer.
- [Lincoln-LM's Pokéwalker course injector](https://github.com/lincoln-lm/pokewalker-course-injector/blob/43dc0617ea34bd4ac14e20ea0e64109a013d7f1b/main.py).
+Thanks to the [Pokéwalker Hacking community](https://discord.gg/ymbTMsS), including mamba2410, zenithknight, mriancamp and porocyon.
-### PRET reading list
+The port uses [M5Stack's Stick S3 hardware and libraries](https://docs.m5stack.com/en/core/StickS3). Firmware tests use [esp32sim](https://github.com/mikroverk/esp32sim), by Joakim Eriksson and Alice.
-These paths were consulted alongside the communication overlay. Each link
-uses revision `0985e8718df4f25e64d6507d89c0c97c0d288981`.
-
-- Pokémon creation, field access, and gender and shininess calculations used
- to interpret transferred Pokémon records.
- [Pokémon operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/pokemon.c).
-- Saved Trainer House records and the operations that manage them, used when
- following exchanged trainer and party data.
- [Trainer House storage](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/save_trainer_house.c).
-- Trainer-profile initialization and accessors, used to follow identity
- values into communication records.
- [Player data](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/player_data.c).
-- Pokémon and course gift processing, used to investigate distribution data
- and course unlocks.
- [Mystery Gift operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/scrcmd_mystery_gift.c).
-- Inventory insertion and its success or failure result, used to establish
- the effect of return-trip item awards.
- [Bag operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/bag.c).
-- Encoded-string copying and termination, used to follow names between
- communication records and game string objects.
- [String operations](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/pm_string.c).
-- String buffering for messages, used to trace displayed text back to the
- values supplied by communication paths.
- [Message formatting](https://github.com/pret/pokeheartgold/blob/0985e8718df4f25e64d6507d89c0c97c0d288981/src/message_format.c).
-
-## Hardware
-
-- The microcontroller family's registers, memory organization, interrupts,
- timers, ADC, and serial peripherals.
- [Renesas H8/38602R Group Hardware Manual](https://www.renesas.com/en/document/mah/h838602r-group-hardware-manual).
-- Device-specific additions needed when applying the family manual to the
- H8/38606.
- [Renesas H8/38606 addition note](https://www.renesas.com/en/document/tcu/addition-h838606-group).
-- Instruction semantics, addressing modes, arithmetic, and processor flags
- used when interpreting the executable.
- [Renesas H8/300H Series Software Manual](https://www.renesas.com/en/document/mah/h8300h-series-software-manual).
-- The older instruction-set reference retained for decoding H8 instructions
- and checking operand semantics: Hitachi document ADE-602-053A.
- [H8/300H Series Programming Manual, hosted by ManualsLib](https://www.manualslib.com/manual/1585252/Hitachi-H8-300h-Series.html).
-- The accelerometer's sample representation, registers, measurement settings,
- and operating modes. The consulted edition is BST-BMA150-DS000-06,
- version 1.6, October 30, 2008.
- [Bosch BMA150 datasheet, hosted by DigiKey](https://media.digikey.com/pdf/Data%20Sheets/Bosch/BMA150.pdf).
-- The serial EEPROM's commands, addressing, page programming, and timing
- constraints. The consulted family datasheet covers M95512-W, M95512-R,
- and M95512-DF: DS4192, Rev.24, September 2021.
- [ST M95512-W/-R/-DF datasheet](https://www.st.com/resource/en/datasheet/m95512-w.pdf).
-- LCD commands, page and column addressing, display RAM, and initial-line
- selection. The consulted edition is V1.0, July 1, 2008. Dmitry's article
- records the August 2026 NT7508 identification and credits Lukas Unguraitis's
- research.
- [Novatek NT7508 datasheet, hosted by Orient Display](https://www.orientdisplay.com/wp-content/uploads/2022/08/NT7508_V1.0.pdf).
-- The earlier LCD comparison reference for four-level grayscale display
- memory and command encodings: Rev.1.0, August 2002. The subsequent LCD
- investigation identified NT7508 as the better match for the firmware's
- command sequence.
- [Solomon Systech SSD1854 datasheet mirror](https://datasheet4u.com/pdf-down/S/S/D/SSD1854-SolomonSystech.pdf).
-
-## Compiler documentation and C behavior
-
-- C representation, calling conventions, compiler options, assembly,
- libraries, and linking. The retained manual is Compiler Package Ver.6.01,
- REJ10B0161-0100, Rev.1.00, January 12, 2005.
- [Renesas H8S/H8/300 compiler-package manual](https://www.renesas.com/en/document/mat/h8s-h8300-series-cc-compiler-assembler-optimizing-linkage-editor-compiler-package-v601-users-manual).
-- HEW project and build behavior. Section 3.10 documents the default
- alphabetical object-link order used as context for this project's module
- naming and organization.
- [HEW V4.02 User's Manual](https://www.renesas.com/en/document/mat/high-performance-embedded-workshop-v402-users-manual).
-- Compiler behavior, extensions, options, and examples relevant to the
- historical H8 toolchain.
- [Renesas H8S/H8/300 compiler application note](https://www.renesas.com/en/document/apn/h8s-h8300-series-cc-compiler-package-application-note).
-- Background on structure padding, alignment, addressing, and optimization,
- consulted during the storage-reconstruction work: AN0403008, Rev.1.00,
- March 2004.
- [Renesas Embedded C Programming III — Optimization](https://www.renesas.com/en/document/apn/embedded-programming-iii-ecprogramiiiopt).
-- C committee discussion of C90 union-member interpretation, consulted when
- reviewing the resident-resource byte view.
- [WG14 N847](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n847.htm).
-- Configuring target integer widths for static analysis, used to establish
- the project's H8 platform configuration.
- [Cppcheck platform documentation](https://cppcheck.sourceforge.io/manual.html#platform).
-- The relationship between compiler-package releases and their constituent
- compiler, assembler, linker, and library tools.
- [Renesas software component list](https://www.renesas.com/en/document/oth/cc-compiler-package-h8sx-h8s-h8-family-software-component-list).
-- Official release information for the first supported compiler suite.
- [V6.02 Release 01 announcement](https://www.renesas.com/en/document/tnr/cc-compiler-package-h8sx-h8s-and-h8-mcu-families-revised-v602-release-01).
-- Official release information for the second supported compiler suite.
- [V6.02 Release 02 announcement](https://www.renesas.com/en/document/tnr/cc-compiler-package-h8sx-h8s-and-h8-mcu-families-revised-v602-release-02).
-
-## Reconstruction methods and tools
-
-- The H8 instruction decoder used during reconstruction. The retained tool
- is GNU Binutils 2.16.1's `h8300-hms-objdump`.
- [GNU Binutils 2.16.1 release](https://ftp.gnu.org/gnu/binutils/binutils-2.16.1.tar.bz2).
-- Background reading on inferring types from machine code, consulted during
- the storage-context investigation: Noonan, Loginov and Cok, 2016.
- [Polymorphic Type Inference for Machine Code](https://arxiv.org/abs/1603.05495v2).
-
-## Artwork and setup tools
-
-- A public implementation of the NCG container and tile representation used
- by the extracted sprite files.
- [NitroPaint's NCG reader](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroCharacter.c#L642).
-- A public implementation of the NCL palette container used for editing
- those sprites.
- [NitroPaint's NCL writer](https://github.com/Garhoogin/NitroPaint/blob/be4d73c4843350665e0547590d7449996c6c33e8/NitroPaint/object/NitroPalette.c#L513).
-- Another public reader documenting the NCCG container through its
- implementation.
- [Tinke's NCCG reader](https://github.com/pleonex/tinke/blob/3263fc2baeea40c847e5267f826730210864fb8c/Plugins/Images/Images/NCCG.cs).
-- The image library used for BMP artwork and font-sheet handling.
- [Pillow's BMP documentation](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#bmp).
-- InstallShield extraction work that informed the project's installer parser
- and decoding approach.
- [ISx](https://github.com/lifenjoiner/ISx).
-- Public InstallShield cabinet extraction code that also informed
- compiler-package extraction.
- [Unshield](https://github.com/twogood/unshield).
-- Installer behavior and extraction options consulted while evaluating
- compiler-input handling.
- [Revenera's InstallShield command-line reference](https://docs.revenera.com/installshield/helplibrary/IHelpSetup_EXECmdLine.htm).
-- The compatibility tool used to execute historical Windows compiler
- programs on supported Unix hosts.
- [Wibo](https://github.com/decompals/wibo).
-
-## Build design
-
-These projects and manuals informed contributor setup and build tooling.
-
-- A decompilation project template consulted for Python configuration,
- Ninja integration, tool provisioning, and executable overrides.
- [DTK template, consulted revision](https://github.com/encounter/dtk-template/tree/95a941f755919ebe50c1725a4ce73524470e7a02).
-- A working example of user-supplied original inputs, generated build
- configuration, and managed compiler wrappers.
- [The Wind Waker decompilation](https://github.com/zeldaret/tww/tree/ff768cc1d9ed10e884e006773a5f9ac6a8d6eff4).
-- An additional comparison for contributor setup, historical compiler
- execution, and development environments.
- [Super Smash Bros. Melee decompilation](https://github.com/doldecomp/melee/tree/35acdca612f5dae409a37cb481fd3230cd42d560).
-- A comparison for manually installed compiler wrappers and ordinary
- host prerequisites.
- [New Super Mario Bros. Wii decompilation](https://github.com/NSMBW-Community/NSMBW-Decomp/tree/5e9c76d50573d5659ba70bb174553e5d2ec3c708).
-- A concrete example of extracting reusable artwork inputs before
- compilation, consulted when designing the separate artwork-extraction step.
- [Super Mario 64 asset extractor](https://github.com/n64decomp/sm64/blob/master/extract_assets.py).
-- Another example of a separate asset-setup stage before normal builds.
- [Ocarina of Time setup documentation](https://github.com/zeldaret/oot#4-setup-the-rom-and-build-process).
-- The reasoning behind generated build graphs and explicit dependencies.
- [Ninja's design documentation](https://ninja-build.org/manual.html#_philosophical_overview).
-- Managed Python environments and dependency locking used by the project.
- [uv project documentation](https://docs.astral.sh/uv/guides/projects/).
-
-## Historical development context
-
-These references informed the reconstruction's coding conventions.
-
-- Contemporary Japanese embedded-development guidance consulted when
- choosing consistent naming, type, and coding policies.
- [IPA/SEC embedded coding guide](https://www.ipa.go.jp/archive/publish/qv6pgp0000000x2q-att/000005106.pdf).
-- A contemporary Renesas firmware example providing context for period
- C conventions.
- [Renesas user-boot application note](https://www.renesas.com/en/document/apn/example-using-user-boot-mode-renesas-018um-flash-devices-xmodem-data-transfer).
-- Another contemporary H8 firmware example consulted during the style
- investigation. This example uses the IAR compiler and project generator.
- [Renesas H8/38024F peripheral examples](https://www.renesas.com/en/document/apn/example-code-h838024f-peripherals).
-- Nintendo's public account of the Pokéwalker's development and intended
- experience.
- [Iwata Asks: HeartGold and SoulSilver, section 5](https://www.nintendo.co.jp/ds/interview/ipkj/vol1/index5.html).
-
-## Related projects
-
-Community projects for Pokéwalker emulation, hardware recreation, artwork,
-and data inspection.
-
-- A community implementation connecting infrared communication, EEPROM
- records, audio, and application behavior.
- [PicoWalker core](https://github.com/mamba2410/picowalker-core).
-- mamba2410's recreation of the Pokéwalker using the Raspberry Pi RP2350
- microcontroller. This repository supplies the device drivers and board
- integration for the application core.
- [PicoWalker](https://github.com/mamba2410/picowalker).
-- PCB designs for the PicoWalker recreation, including RP2350 hardware and
- a Raspberry Pi Pico 2 carrier board.
- [PicoWalker hardware](https://github.com/mamba2410/picowalker-hardware).
-- jpcerrone's experimental Windows emulator for exploring the Pokéwalker's
- menus and minigames with ROM and EEPROM images.
- [PokeStroller](https://github.com/jpcerrone/pokestroller).
-- UnrealPowerz' prototype Pokéwalker emulator, including CPU, EEPROM, LCD,
- and button emulation.
- [Powar](https://github.com/UnrealPowerz/powar).
-- UnrealPowerz' browser application for viewing and editing data and artwork
- in Pokéwalker EEPROM images.
- [Pokéwalker EEPROM Editor](https://unrealpowerz.github.io/pokewalker-eeprom-editor/).
- Its implementation provides readable views of EEPROM contents for format
- exploration.
- [EEPROM editor source](https://github.com/UnrealPowerz/pokewalker-eeprom-editor).
-- PoroCYon's project for dumping Pokéwalker ROM and EEPROM contents.
- [Pokéwalker ROM dumper](https://gitlab.ulyssis.org/pcy/pokewalker-rom-dumper).
-- mamba2410's image viewer and converter, which displays packed Pokéwalker
- images in a terminal and converts grayscale BMP artwork to the device's
- image format.
- [pw-lcd](https://github.com/mamba2410/pw-lcd).
-- An interoperability project illustrating how HGSS's Pokéwalker protocol
- can operate through another infrared transport.
- [RtcPwalker](https://github.com/francesco265/RtcPwalker).
-
-## Acknowledgments
-
-UnrealPowerz's [pw_firm](https://github.com/UnrealPowerz/pw_firm/tree/main/)
-inspired the matching reconstruction. h4lfheart's
-[PocketWalker](https://github.com/h4lfheart/PocketWalker) inspired the initial
-Pokéwalker work. Dmitry Grinberg's hardware and firmware research laid much of
-the groundwork.
-
-The [Pokéwalker Hacking community](https://discord.gg/ymbTMsS), including
-mamba2410, zenithknight, mriancamp, porocyon and its other contributors, provided
-research, tools and a place to compare findings. The projects and authors linked
-above also contributed to the reconstruction's technical foundations.
-
-## Stick hardware and runtime
-
-The port uses M5Unified 0.2.21, M5GFX 0.2.29 and ESP-IDF 5.5 interfaces from
-the pinned M5Stack board core. These references describe their hardware and APIs.
-
-| Subject | Primary sources |
-| --- | --- |
-| Board wiring, supplies and battery | [M5StickS3 specification](https://docs.m5stack.com/en/core/StickS3), [schematic](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/K150_Stick_S3_PRJ_V0.6_20251111_2025_11_17_16_10_24.pdf), [M5PM1 power configuration](https://docs.m5stack.com/en/arduino/m5sticks3/m5pm1) |
-| PMIC register and button behavior | [M5PM1 V1.9 datasheet](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf), [M5Unified PMIC driver](https://github.com/m5stack/M5Unified/blob/0.2.21/src/utility/power/M5PM1_Class.cpp) |
-| Motion sensor | [BMI270 datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf) |
-| Display and buffer interpretation | [StickS3 display specification](https://docs.m5stack.com/en/accessory/display/Display_1.14_For_StickS3), [ST7789P3 datasheet](https://files.waveshare.com/wiki/ESP32-S3-GEEK/ST7789P3.pdf), [M5GFX ST7789 driver](https://github.com/m5stack/M5GFX/blob/0.2.29/src/lgfx/v1/panel/Panel_ST7789.hpp) |
-| Codec startup and word format | [ES8311 datasheet](https://www.lcdwiki.com/res/PublicFile/ES8311_DS.pdf), [Espressif codec initialization](https://github.com/espressif/esp-adf/blob/release/v2.x/components/esp_codec_dev/device/es8311/es8311.c) |
-| Sleep and RTC GPIO ownership | [ESP-IDF sleep modes](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/sleep_modes.html), [sleep errors](https://github.com/espressif/esp-idf/blob/v5.5/components/esp_hw_support/include/esp_sleep.h) |
-| USB transport during sleep | [ESP-IDF USB Serial/JTAG console](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/usb-serial-jtag-console.html) |
-| Debugger effects and failures | [JTAG setup](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/jtag-debugging/index.html), [breakpoints and watchpoints](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/jtag-debugging/tips-and-quirks.html), [watchdogs](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/wdts.html), [panic handling](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/fatal-errors.html) |
-| Crash records | [Reset reasons](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/system/misc_system_api.html#reset-reason), [retention attributes](https://github.com/espressif/esp-idf/blob/v5.5/components/esp_common/include/esp_attr.h), [core dumps](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/core_dump.html) |
-| Failed PMIC transactions | [ESP-IDF I2C probe guidance](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-reference/peripherals/i2c.html#i2c-master-probe), [M5Unified bus wrapper](https://github.com/m5stack/M5Unified/blob/0.2.21/src/utility/I2C_Class.cpp), [M5GFX backend](https://github.com/m5stack/M5GFX/blob/0.2.29/src/lgfx/v1/platforms/esp32/common.cpp) |
-| Current measurement | [Espressif measurement guidance](https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/api-guides/current-consumption-measurement-modules.html) |
+[Third-party licenses](third-party.md).
diff --git a/docs/source.md b/docs/source.md
deleted file mode 100644
index 23fb47b..0000000
--- a/docs/source.md
+++ /dev/null
@@ -1,229 +0,0 @@
-# Reading the application source
-
-The original target runs on the H8/38606 in normal mode: 8-bit bytes, 16-bit integers
-and pointers, and 32-bit longs. It uses C90 with CH38 extensions for interrupts,
-sections, bitfield order and machine instructions.
-
-## Choose the target
-
-For Stickwalker adapter changes, start with the [Stick development guide](../stick/README.md).
-The application paths below are shared, but compiler widths, register access
-and startup are target-specific. `PW_STICK_S3` selects the port's hardware and compiler adaptations; C++ adapters use the platform ABI. Preserve explicit widths and byte order
-in serialized records rather than relying on host structure layout.
-
-H8 link-order and retail-matching requirements below apply to the original
-target. A shared application change needs both matching checks and Stick host
-regressions; an adapter-only change needs the Stick checks and relevant hardware
-qualification.
-
-## Modules and reading paths
-
-The source retains 40 separately compiled C files. Four `h8_` modules in
-`src/startup/` initialize sections, enter interrupts, reset the device, and
-describe the ROM. `src/support/` holds `ir.c` and the two `lib_` helper modules.
-The 32 `pw_` modules in `src/application/` cover activities, storage, and device
-interfaces. `src/globals.c` defines shared storage.
-
-Module headers generally follow the source directories and basenames under
-`include/`. Shared types, records, resources, and view state remain at the
-include root.
-Project includes use paths from that root, such as
-`#include "application/pw_pedometer.h"` and `#include "support/lib_common.h"`.
-The shared `application/pw_eeprom_m95512.h` interface covers the serial bus and
-single-byte programming in `pw_eeprom_m95512_bus.c`, plus bulk transfers and
-mirrored-record handling in `pw_eeprom_m95512_io.c`. Its status-receipt helpers
-are implemented in `lib_common.c`. That module also groups raster operations,
-artwork loading, scratch allocation, step pacing and small runtime helpers.
-`InstallTask` lives in `pw_home.c`; communication completion and staged-walk
-activation are declared in `h8_resetprg.h`.
-
-Each source must have a unique lowercase basename. The build discovers C files
-recursively and links their objects in alphabetical basename order. Directory
-paths organize navigation independently of that order. The `h8_`, `lib_`, and
-`pw_` prefixes are reconstruction conventions that express the observed module
-sequence. Renaming a file can change code and data placement and requires the
-same complete matching checks as an implementation change. Section placement
-and runtime-library handling have their own build rules.
-
-These paths connect the modules by responsibility:
-
-| Start here | Responsibility |
-| --- | --- |
-| [h8_resetprg.c](../src/startup/h8_resetprg.c), [pw_main.c](../src/application/pw_main.c), [pw_power.c](../src/application/pw_power.c) | Boot, foreground tasks, activity scheduling and low-power transitions |
-| [view.h](../include/view.h), [globals.h](../include/globals.h) | View state, interrupt-shared values and workspace ownership |
-| [pw_accel_bma150.c](../src/application/pw_accel_bma150.c), [pw_fourier.c](../src/application/pw_fourier.c), [pw_pedometer.c](../src/application/pw_pedometer.c) | Samples, spectrum analysis and accepted step batches |
-| [lib_common.c](../src/support/lib_common.c), [pw_rtc.c](../src/application/pw_rtc.c), [save.h](../include/save.h) | Step pacing, Watts, time, counters and persistence |
-| [pw_eeprom_m95512_bus.c](../src/application/pw_eeprom_m95512_bus.c), [pw_eeprom_m95512_io.c](../src/application/pw_eeprom_m95512_io.c), [records.h](../include/records.h), [resources.h](../include/resources.h) | Serial storage, repairing reads, records and resource layouts |
-| [ir.c](../src/support/ir.c), [ir_state.h](../include/support/ir_state.h), [protocol.h](../include/protocol.h) | Connection, packets, peer exchange and deferred completion |
-| [pw_home.c](../src/application/pw_home.c), [pw_pictogram_menu.c](../src/application/pw_pictogram_menu.c), [pw_pack.c](../src/application/pw_pack.c), [pw_trainer.c](../src/application/pw_trainer.c), [pw_local_settings.c](../src/application/pw_local_settings.c) | Navigation, inventory, trainer history and settings |
-| [pw_pokeradar.c](../src/application/pw_pokeradar.c), [pw_battle.c](../src/application/pw_battle.c), [pw_dowsing.c](../src/application/pw_dowsing.c), [pw_carry_overflow.c](../src/application/pw_carry_overflow.c) | Search, battle, rewards and full-inventory replacement |
-| [pw_follower_events.c](../src/application/pw_follower_events.c), [pw_follower_prompts.c](../src/application/pw_follower_prompts.c), [pw_friend.c](../src/application/pw_friend.c), [pw_cutscenes.c](../src/application/pw_cutscenes.c), [pw_diary.c](../src/application/pw_diary.c) | Social offers, peer play, presentations and the diary |
-| [pw_nt7508.c](../src/application/pw_nt7508.c), [pw_builtin.c](../src/application/pw_builtin.c), [pw_buzzer.c](../src/application/pw_buzzer.c), [pw_player_input.c](../src/application/pw_player_input.c) | Drawing, resident artwork, sound and buttons |
-| [pw_factory_test.c](../src/application/pw_factory_test.c), [pw_selftest.c](../src/application/pw_selftest.c), [pw_battery.c](../src/application/pw_battery.c) | Fixture operations, diagnostics and battery references |
-
-## Execution and shared state
-
-Boot restores mirrored records, repairs an interrupted staged copy, initializes
-the peripherals and installs the foreground task. Interrupts update counters
-and latch work for that task. The quarter-second RTC interrupt requests a UI
-refresh; the one-second interrupt maintains seconds separately. Several ticks
-can coalesce into one pending refresh, which the foreground handles when it runs.
-
-`MainTick` samples on every wake. A completed motion batch takes priority over
-rendering; a pending refresh takes priority over deferred RTC processing.
-Inactive mode suspends regular sampling and refresh timers, while later wakes
-still sample for activity detection. Activity resumes motion mode for thirty
-seconds. Interactive entry sets sixty seconds for the display and ninety for
-motion; later button edges refresh the display timeout to ninety seconds.
-
-`g_state` contains runtime state, including the RAM copy of `SaveData`.
-`g_ui.view` overlays the active view in an 18-byte bank. Changing `g_state.view`
-preserves that bank. Radar passes its encounter byte to battle; a full inventory
-passes its selection to discard; IR carries its LCD readback result into
-diagnostics. Initializers deliberately preserve those bytes.
-
-`g_work` overlays motion, sound and IR storage. Audio reuses the three motion
-sample arrays for a score and takes over the foreground while it plays. IR
-reuses motion state and part of the scratch area. Task handoffs finish using the
-old interpretation before preparing the next one.
-
-Scratch allocation reserves consecutive bytes; callers provide initialization
-and alignment. Resetting scratch rewinds the cursor. The bytes remain until
-another user writes them. Peer rewards deliberately place retained records
-beyond the course prefix that a nested lookup reloads. Drawing helpers similarly
-reuse known portions of the backing storage. Callers must account for every
-intervening write before using a retained pointer. Ordinary allocation checks
-1,024 bytes and sleeps after an overflow; direct large-artwork transfers use all
-1,536 bytes of the backing storage.
-
-## From motion to steps
-
-The main task collects signed high bytes of the accelerometer's three axes.
-Each 64-sample batch passes through the FFT, accumulating approximate magnitudes
-from all axes.
-
-`MotionEstimateQ9` visits candidates in a defined order, compares relative
-magnitudes and changes its rejection test according to the preceding batch.
-These checks determine which bin is accepted.
-
-The accepted bin position represents steps per batch in Q9 units: one step is
-512. Fractional credit carries into later batches. `StepPacingTick` spreads the
-whole-step budget across subsequent samples, credits at most one step per call
-and updates hourly, daily and lifetime counters. Every twenty credited steps
-can earn a Watt, up to 9,999. The arithmetic, single-credit conditions and
-saturation points are part of the firmware's behavior.
-
-RTC processing handles minutes, hours and the configured daily rollover.
-The elapsed-hours counter runs through periods with an empty Pokemon slot. It
-resets on a new walk or lifetime reset and stops when it or the lifetime step
-count reaches 9,999,999. Rollover shifts daily history and clears the IDs of
-prior peer records while preserving their other fields.
-
-## Views and activities
-
-Activity state includes input, update and rendering. Renderers can advance or
-clamp animation counters, change a presentation stage or finish a view. State
-names therefore appear across those paths. Timers explicitly distinguish input
-polls, rendered calls, hardware counts and completed motion batches.
-
-Battle separates presentation phase, player action, probability row and
-response code. The same response code has different meanings for Attack and
-Evade; the table and dispatch document that relationship. Dowsing separates
-patch selection from the chosen course item. Inventory selectors distinguish
-ordinary slots from held Pokemon and event gifts, including their index bases.
-
-Reward persistence and its presentation can happen at different times. An IR
-reward is already stored when its arrival animation begins. Some local rewards
-commit on acknowledgement, while others are stored before display. Diary
-appending is a separate operation; the device's cursor rotates through 23 slots
-within the 24-entry stored area.
-
-## Stored data and communication
-
-`eeprom_map.h` owns serial-storage addresses. `save.h`, `records.h` and
-`resources.h` describe the corresponding layouts. Resource expressions use
-pointer syntax to calculate EEPROM addresses for the driver. Transfer spans
-can contain more than one logical object or extend beyond the image being
-displayed.
-
-Mirrored reads repair a bad copy, or fill both copies with `FF` when neither
-checksum validates. When both validate, the buffer holds the primary copy.
-The repair decision compares their additive checksums: unequal sums trigger a
-backup rewrite from primary. Status receipt checks and battery-reference checks
-also perform the I/O and repair described by their interfaces.
-
-Native H8 counters and console-produced little-endian fields coexist. Stored
-fields with an `Le` suffix retain little-endian bytes during forwarding and
-equality checks. Numeric consumers decode them where needed. Course item
-lookup returns an encoded ID suitable for copying directly into inventory.
-
-`PersistentReset` clears pairing, session state, return inventory, weekly steps
-and peer history. Its arguments control event and lifetime clearing independently.
-
-The IR layer separates handshake role, peer-status role and bulk phase.
-Both peers eventually send and receive. Packet handlers may queue a foreground
-completion action, which takes priority over a later error. Walk activation
-sets a recovery marker around the staged page copies; diary, counter and
-inventory changes after clearing the marker are outside that recovery span.
-
-## Graphics and sound
-
-Resident artwork is extracted once into six NCG files and a BMP font sheet.
-The build converts these editable files into C arrays; missing images use
-original placeholders. See the [artwork guide](../assets/README.md).
-EEPROM artwork arrives from the console separately.
-A raster page covers eight rows and stores the high then low bit plane for
-each column. Small Pokemon animations contain two 32×24 frames; large animations
-contain two 64×48 frames.
-
-The display has two drawing banks. Helpers distinguish page numbers from pixel
-coordinates. Horizontal animation deliberately extends beyond the visible
-96 columns; the driver transmits the requested width. Vertical page addresses
-must stay within the bank. Name frames and message rules also write fixed
-regions around their artwork, as described in the helper interfaces.
-A blank transfer sprite still follows its arrival/departure animation.
-
-EEPROM score descriptors hold a little-endian offset, byte length and checksum.
-Loading a score replaces the motion samples in shared RAM. A borrowed score's
-storage must stay valid until playback ends and satisfy the command sequencing
-and preceding-record requirements in `score.h` and `pw_buzzer.h`. Repeat commands
-return to the shared RAM score.
-
-Notes hold duration units and timer-table indices. The worker and Timer W
-interrupt cooperate on duration, separation and legato. The rest index reaches
-past the pitch table into adjacent resident artwork; the shared storage
-declaration includes the full accessed span.
-
-## Conventions and matching
-
-Use concise PascalCase functions and types, lowerCamelCase locals and fields,
-`g_` globals, uppercase constants, and two-space indentation. Function braces
-start on the next line; control braces stay on the same line. The project
-selected these conventions using embedded C guidance from the period.
-
-Names express meaning and useful units. Comments explain relationships and
-constraints that the code alone cannot make clear. Keep ordinary coordinates,
-indices and numeric tables simple when their meaning is already clear.
-
-H8 application changes must preserve full retail identity under both supported
-compiler suites. Integer promotions, signed shifts, access widths, volatile reads,
-bitfield order, alignment, expression grouping and local lifetimes matter.
-Call order also preserves shared random-number consumption and workspace
-ownership. Run the [formatter, linter and complete builds](build.md).
-
-One CH38-specific return convention needs care when porting:
-`EepromWriteByte` passes the last sampled event byte through the CH38 return
-register without an ordinary C return expression. Its callers consistently
-forward or inspect the whole event byte, including its clock and UI flags.
-A compiler port must make that return explicit and review the other target
-assumptions. The qualified suites are CH38 6.02.01 and 6.02.02.
-
-## Remaining uncertainties
-
-- The IR-request event has no ordinary setter in this firmware.
-- `pendingStepsQ9` is consumed and cleared without an established nonzero
- producer; the motion batch's `resetOnlyByte` is only cleared.
-- The threshold-test `resetWord` is initialized without an established consumer.
-- Preserved fields keep their observed initialization and transfer behavior
- where their meaning remains unresolved, including the event-item prefix,
- leading walk/peer words and the separately cleared Pokemon appearance bit.
diff --git a/docs/third-party.md b/docs/third-party.md
index d54d208..1520c89 100644
--- a/docs/third-party.md
+++ b/docs/third-party.md
@@ -1,31 +1,8 @@
-# Third-party material and tools
+# Third-party licenses
-The [references](references.md) describe the research and tools that inform this project.
+Stickwalker uses the [MIT license](../LICENSE). Included dependencies and assets keep their own licenses.
-The compiler archive reader in `tools/installshield.py` uses the InstallShield format and
-block-decoding approach documented by [ISx](https://github.com/lifenjoiner/ISx)
-and [Unshield](https://github.com/twogood/unshield). Their MIT notices follow.
-
-[Wibo](https://github.com/decompals/wibo) is downloaded separately from its
-upstream release, or supplied locally. uv, Ninja, Pillow, Ruff, clang-format and Cppcheck retain their own licenses.
-The Python dependencies are recorded in `uv.lock`.
-
-The NCG/NCL converter follows public format descriptions in
-[NitroPaint](https://github.com/Garhoogin/NitroPaint) and
-[Tinke](https://github.com/pleonex/tinke). The [artwork guide](../assets/README.md)
-links the consulted revisions and explains the reconstructed editor metadata.
-
-## Renesas HEW device header
-
-[`include/startup/iodefine.h`](../include/startup/iodefine.h) is the retained
-Renesas HEW device-register template. It identifies itself as **H8/38602 Series
-Include File, Ver 2.1**, with the marker **HEW_2006.10.05**. It provides the
-memory-mapped register declarations used by the firmware.
-
-This vendor template is outside the project's MIT license. Its existing
-identification is retained in the file, and automated formatting and line-ending
-conversion exclude it. The retained file contains no standalone license grant;
-the project adds no license grant over Renesas's material.
+The compiler archive reader follows [ISx](https://github.com/lifenjoiner/ISx) and [Unshield](https://github.com/twogood/unshield). Their notices follow.
## ISx
@@ -73,38 +50,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-## Firmware simulation
-
-The development checks use [esp32sim](https://github.com/mikroverk/esp32sim) at
-`fe3a9c08125cc760107865df3ffa979362952fe9`, by Joakim Eriksson and Alice.
-[`stick/simulation/soc.patch`](../stick/simulation/soc.patch) extends its SPI,
-GPIO, watchdog and RMT models. Its [MIT notice](../stick/simulation/ESP32SIM-LICENSE)
-is retained with the patch. The simulator downloads Espressif's ESP32-S3 mask ROM
-from [esp-rom-elfs](https://github.com/espressif/esp-rom-elfs), under Apache-2.0.
-
-## Stickwalker runtime
-
-The Stick build uses M5Stack's ESP32 board package **3.3.9**,
-[M5Unified 0.2.21](https://github.com/m5stack/M5Unified/tree/0.2.21) and
-[M5GFX 0.2.29](https://github.com/m5stack/M5GFX/tree/0.2.29).
-The two M5 libraries use MIT; their full notices are retained in
-[`stick/licenses/`](../stick/licenses/). The Arduino ESP32 core contains
-LGPL-2.1-or-later material, and its underlying ESP-IDF, precompiled libraries
-and components have their own licenses.
-
-Each firmware release includes a runtime archive with the exact application
-objects, Arduino core archive, linked SDK libraries, linker scripts, dependency
-sources and their original notices. Its relinking recipe reproduces the release
-ELF and supports replacing the Arduino core. See the [relinking guide](../stick/docs/relink.md).
-
-The runtime uses ESP-IDF **v5.5.4** at
-`735507283d5b2f9fb363a1901172dbd9e847945d`, including its pinned submodules, plus
-LittleFS **1.22.1** and ESP Diagnostics **1.2.1**. The source inventory in
-[`stick/runtime-sources.json`](../stick/runtime-sources.json) records the archive
-origins, hashes and submodule revisions. The SDK's linked Espressif radio
-libraries retain their included binary-library licenses. GCC runtime and Newlib
-notices are included with the firmware and runtime archives.
+## Other material
-Earlier versions of the reconstruction remain under their original CC0 terms.
-The [placeholder artwork](../assets/placeholders/LICENSE.md) also retains its
-explicit CC0 dedication.
+- The [Renesas HEW register header](../include/startup/iodefine.h) retains its vendor identification and is outside the project's MIT license.
+- [M5Unified](https://github.com/m5stack/M5Unified) and [M5GFX](https://github.com/m5stack/M5GFX) use MIT. Their notices are in [stick/licenses](../stick/licenses/).
+- [esp32sim](https://github.com/mikroverk/esp32sim) uses MIT. Its [notice](../stick/simulation/ESP32SIM-LICENSE) accompanies the simulator patch. Espressif's mask ROM uses Apache-2.0.
+- The Arduino core uses LGPL-2.1-or-later. Runtime downloads include dependency sources, notices and a [relinking recipe](../stick/docs/relink.md). [runtime-sources.json](../stick/runtime-sources.json) records the dependency versions and origins.
+- The [placeholder artwork](../assets/placeholders/LICENSE.md) uses CC0. Earlier reconstruction releases retain their original CC0 terms.
+- Wibo, uv, Ninja, Pillow, Ruff, clang-format and Cppcheck keep their upstream licenses.
+- The graphics converter follows format descriptions from [NitroPaint](https://github.com/Garhoogin/NitroPaint) and [Tinke](https://github.com/pleonex/tinke).
diff --git a/stick/README.md b/stick/README.md
deleted file mode 100644
index 4e36f2a..0000000
--- a/stick/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Developing Stickwalker
-
-The [build guide](docs/build.md) covers toolchain setup, firmware checks and compiling
-firmware. For installing a release on your Stick, use the
-[installation guide](docs/install.md).
-
-## Find the code
-
-The reconstructed application in `src/` and `include/` implements game rules, records
-and the infrared protocol. The adapters here connect that application to M5StickS3.
-
-| Change | Start here |
-| --- | --- |
-| Native timing and foreground scheduling | [native_runtime.cpp](native_runtime.cpp), [foreground_bridge.c](foreground_bridge.c) |
-| Board coordination and Settings overlay | [board_runtime.cpp](board_runtime.cpp), [board_settings.cpp](board_settings.cpp) |
-| Button acquisition and native control mapping | [input_bridge.cpp](input_bridge.cpp), [controls.cpp](controls.cpp) |
-| Panel, palette, rotation and backlight | [display_panel.cpp](display_panel.cpp), [display_bus.cpp](display_bus.cpp) |
-| Optical capture, receive decoding and transmission | [ir_transport.cpp](ir_transport.cpp), [ir_rx_core.cpp](ir_rx_core.cpp), [ir_gate.h](ir_gate.h) |
-| Motion samples and sensor scaling | [motion_sample.cpp](motion_sample.cpp), [accel_bridge.cpp](accel_bridge.cpp) |
-| Physical audio readiness and codec supply | [sound_bridge.cpp](sound_bridge.cpp) |
-| Game-data persistence and recovery | [eeprom_backend.cpp](eeprom_backend.cpp), [device_identity.c](device_identity.c) |
-| Board supplies, PMIC setup and processor sleep | [board_hal.cpp](board_hal.cpp), [power_sleep.cpp](power_sleep.cpp) |
-
-The [player guide](docs/playing.md) describes the complete walk and transfer flow.
-The [native platform guide](docs/native-platform.md) describes ownership,
-readiness boundaries, input cancellation and storage recovery.
-The [motion guide](docs/motion.md) explains the sensor adaptation.
-The [source guide](../docs/source.md) follows the native application across
-modules. [CONTEXT.md](../CONTEXT.md) defines motion, display and power terms.
-The [power guide](docs/power.md), [settings/display implementation](docs/device-settings-and-display.md)
-and [debugging guide](docs/debugging.md) explain the adapters.
-
-## Change and validate
-
-Keep protocol/session behavior in `src/support/ir.c`; optical acquisition and
-decoding belong in the Stick adapters. Preserve [the original motion schedule](../docs/adr/0001-preserve-native-motion-ownership.md),
-and the [M-only screen-off wake policy](../docs/adr/0002-m-only-dark-wake.md).
-Changes to the optical sampler need the [machine-code timing check](docs/release.md#build-and-check).
-
-`PW_STICK_BENCH_CONTROL` enables state-changing serial diagnostics. Build it with
-`--bench-control` and keep those images separate from production candidates.
-The scripts in `audits/` state whether they use host simulation, saved inputs or
-a physical board. Logs, flash backups and received data belong in ignored
-`stick/.build/`.
-
-The [test history](docs/validation-history.md) records device trials and the
-[release guide](docs/release.md) covers packaging and testing a release. Follow
-[Contributing](../CONTRIBUTING.md) for patches and problem reports.
-
-## Device identity and save repair
-
-Each Stick needs a persistent ID for peer play. `device_identity.c` provisions
-missing IDs from the ESP32's factory MAC, keeping all six MAC bytes in the
-40-byte identity. It preserves existing valid IDs and recovers a damaged master
-copy from its backup or saved status.
-
-At mount, the storage backend replays the save journal, repairs the master and
-status IDs and the outgoing trainer record, then checkpoints the image before
-boot. Repair changes the identity fields and their checksums; the game data
-stays intact. `CommitStagedWalk` also copies the current ID into the trainer
-record, including when boot finishes a transfer interrupted on older firmware.
-
-The production workflows exercise journal recovery, stale-checkpoint records,
-optical status/page exchange and persistence across reset. Run them with
-`uv run python stick/check.py`. Repeat encounters, day rollover and complete
-retail transfers still need the device checks in the release guide.
diff --git a/stick/build_port.py b/stick/build_port.py
index 64d0067..a993012 100644
--- a/stick/build_port.py
+++ b/stick/build_port.py
@@ -111,14 +111,14 @@ def query(*parts):
{},
)
if core.get("installed_version") != CORE:
- raise InputError(f"Install m5stack:esp32@{CORE}; see stick/docs/build.md.")
+ raise InputError(f"Install m5stack:esp32@{CORE}; see docs/build.md.")
installed = {
row["library"]["name"]: row["library"]["version"]
for row in query("lib", "list")["installed_libraries"]
}
for name, expected in LIBRARIES.items():
if installed.get(name) != expected:
- raise InputError(f"Install {name}@{expected}; see stick/docs/build.md.")
+ raise InputError(f"Install {name}@{expected}; see docs/build.md.")
output = args.output_dir.resolve()
# Never leave a successful manifest attached to a failed/replaced build.
output.mkdir(parents=True, exist_ok=True)
diff --git a/stick/docs/build.md b/stick/docs/build.md
deleted file mode 100644
index 3bedefe..0000000
--- a/stick/docs/build.md
+++ /dev/null
@@ -1,80 +0,0 @@
-# Building Stickwalker
-
-This creates the firmware you will install on your Stick S3. Download the source
-from [the repository](https://github.com/lumirth/stickwalker) and unpack it, or
-clone it with Git:
-
-```sh
-git clone https://github.com/lumirth/stickwalker.git
-cd stickwalker
-```
-
-Open a terminal in that folder. Run the following commands on your computer.
-
-Use Python 3.11 or later, [uv](https://docs.astral.sh/uv/), and
-[Arduino CLI](https://arduino.github.io/arduino-cli/latest/installation/).
-The build was tested with Arduino CLI 1.5.1 on macOS arm64.
-
-Install the tested board core and libraries in your Arduino environment:
-
-```sh
-arduino-cli config add board_manager.additional_urls https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json
-arduino-cli core update-index
-arduino-cli core install m5stack:esp32@3.3.9
-arduino-cli lib install 'M5GFX@0.2.29'
-arduino-cli lib install --no-deps 'M5Unified@0.2.21'
-```
-
-Build the firmware:
-
-```sh
-uv sync --locked
-uv run python stick/build_port.py
-```
-
-The builder checks those core and library versions and selects
-`m5stack:esp32:m5stack_sticks3:PSRAM=opi`. It generates resident artwork directly
-from the included placeholders and compiles production firmware without an
-H8 compiler.
-
-Output is `stick/.build/output/`: `PwStick.ino.bin` is the application;
-`PwStick.ino.elf` retains symbols for debugging. Bootloader and partition binaries
-are generated for first installation. `build-manifest.json` records the board,
-tool versions, production/diagnostic mode, artwork, source inputs and output
-SHA-256 hashes. Follow the [install guide](install.md) before writing any image.
-
-For an isolated Arduino installation, pass the executable and configuration:
-
-```sh
-uv run python stick/build_port.py --arduino-cli /path/to/arduino-cli --config-file /path/to/arduino-cli.yaml
-```
-
-For resident retail or edited artwork, follow the [artwork guide](../../assets/README.md),
-then add `--artwork local`. Missing local images fall back to placeholders. Keep
-that build for personal use. The default build always uses placeholders, even
-when `assets/local/` exists.
-
-Use `--output-dir` to keep builds in separate directories. Add `--bench-control`
-for a diagnostic build with serial commands and tracing. Release builds use the
-default production mode.
-
-## Firmware E2E checks
-
-Install Rust 1.98.1 and Tesseract, then run:
-
-```sh
-uv run python stick/check.py
-```
-
-This builds production firmware and boots it on a simulated Stick S3. It checks
-Settings navigation, rotation and appearance changes, persistence after restart,
-and Connect recovery and retry with the receiver held low or high. The runner
-reads the rendered display and decodes the transmitted infrared waveform.
-
-Each run saves screenshots, console output, signal recordings, flash and a
-result record under `stick/.build/e2e-results/`. The record identifies the
-application and simulator used. See the [simulation guide](../simulation/README.md)
-for setup, retained builds and scenario commands.
-
-For distributable archives and optical timing verification, follow
-[release preparation](release.md).
diff --git a/stick/docs/controls.md b/stick/docs/controls.md
index e58c4ef..18544d4 100644
--- a/stick/docs/controls.md
+++ b/stick/docs/controls.md
@@ -1,53 +1,16 @@
-# Controls and settings
+# Stick controls
-Use the Stick's buttons to navigate the Pokéwalker screens. The default layout
-uses L for Left, M for Center and R for Right, with the screen left-side-down.
+The default screen orientation is left-side-down.
-In a two-key layout, if Center is hard to trigger, increase the M+R window in Stick Settings. This
-gives you more time to press the second button. Keep both pressed together for
-at least 20 ms.
+Hold M for about half a second to wake the screen.
-| Layout | Left | Center | Right | Open Stick Settings |
+| Layout | Left | Center | Right | Stick Settings |
| --- | --- | --- | --- | --- |
-| 2-key M/R | M | M+R | R | L |
-| 2-key R/M | R | M+R | M | L |
| 3-key L/R, default | L | M | R | Hold L+R for one second |
| 3-key R/L | R | M | L | Hold L+R for one second |
+| 2-key M/R | M | M+R | R | L |
+| 2-key R/M | R | M+R | M | L |
-## Wake the screen
-
-With the screen off, hold M until the screen wakes, roughly half a second in any layout or rotation.
-Release all buttons before navigating. L and R work again once the screen is
-awake.
-
-Wake uses native Center-hold qualification and its social-offer opportunity,
-rather than a separate wall-clock shortcut. An abandoned hold cancels
-preparation. IR entry also consumes its gesture through release, preventing
-held input from being replayed into the result screen.
-
-## Change settings
-
-In Stick Settings, M selects the next row, R changes its value, and L closes
-the menu. Input layout and screen rotation are independent.
-
-Choose Light or Dark appearance and use Test speaker to check audio.
-In two-key layouts, M+R window selects 80, 120 or 160 ms between the two presses;
-80 ms is the default. Three-key layouts show Center: M instead.
-The game's Contrast setting adjusts backlight brightness.
-
-Your layout, rotation and appearance are remembered across restarts, separately
-from your Pokémon and game progress. Settings also shows battery voltage and an
-estimated charge percentage.
-
-In three-key layouts, hold physical L and R together for one second to open
-Stick Settings. Gameplay receives both directions while the hold is pending.
-Release all buttons after the overlay opens before using its controls. Native
-Volume and Contrast retain their original two-choice selector. Native time and
-background work continue while Stick Settings captures buttons.
-
-Rapid taps are buffered, but stale input is not played into later screens. If
-pending transitions exceed 250 ms or fill the bounded queue, the gesture is
-canceled until release. Diagnostic builds report these cancellations.
+For two-key layouts, M+R window sets how much time you have to press the second button.
-Unplug USB to carry the Stick. It sleeps between game tasks while continuing
-to track walking; hold M for half a second when you want to check your Pokémon.
+Contrast controls the Stick's backlight.
diff --git a/stick/docs/debugging.md b/stick/docs/debugging.md
deleted file mode 100644
index db1aed3..0000000
--- a/stick/docs/debugging.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Debugging Stickwalker
-
-Use the ELF from the firmware you are testing; release ZIPs include it alongside
-the application binary. [Back up the flash](install.md#back-up-before-writing)
-before installing a diagnostic build so you can restore the current game data.
-
-## Startup or a disappearing USB connection
-
-USB power keeps the ESP32 awake. Use it to inspect startup, PMIC registers and
-normal execution. Opening a serial port or changing DTR/RTS can reset the board,
-and ESP32 Light-sleep interrupts USB Serial/JTAG. Record resets and port changes
-alongside the firmware's reset reason.
-
-After flashing, check the application descriptor, ready state and advancing
-RTC. These establish that the application has booted. A flash verification
-checks the stored bytes.
-
-## Battery sleep and wake
-
-Arm a finite diagnostic trial over USB, disconnect it, then reconnect after the
-trial. Retained records survive the USB reset and contain sample counts, sleep
-returns, elapsed time, wake sources and entry errors. Save incomplete records
-too; they help locate where execution stopped.
-
-A pending wake source or a sleep interval shorter than entry overhead can
-cancel a sleep attempt. Record the API stage and error code to distinguish
-these cases from a configuration failure. For power consumption, measure
-current at the battery connection using the [power guide](power.md).
-
-## Breakpoints and register inspection
-
-Use Espressif OpenOCD with `board/esp32s3-builtin.cfg` and the matching ELF.
-Keep USB connected and place hardware breakpoints before or after infrared
-acquisition. Halting inside the optical sampler changes the signal timing.
-Watchpoint 1 is reserved for stack-end detection.
-
-Set `ESP_FLASH_SIZE 0` before loading the board configuration to disable flash
-breakpoints. If CPU1 inspection fails with an invalid OCD ID, try
-`set ESP_ONLYCPU 1` before loading the configuration. This allowed CPU0 reads
-in the [recorded board investigation](history/power-and-wake.md).
-Restart the board without the debugger when testing watchdogs or panic behavior.
-
-## Crash dumps and PMIC failures
-
-The coredump partition starts at `0x7F0000` and occupies `0x10000` bytes. Decode
-a saved dump with its matching ELF and check its integrity and application
-identity. Record when you collected it, since flash can retain an older dump.
-If no dump was written, inspect the reset reason and power supply, then check
-whether execution is stuck or just the USB connection has dropped.
-
-For a failed PMIC read, capture the I2C address, register, attempt number,
-duration, error code and reset reason before retrying. Use M5Unified's existing
-I2C bus and bounded reinitialization. Measuring SDA and SCL after the failure
-can show a stuck bus; a trace during the transfer is needed to locate the fault.
-
-The [hardware references](../../docs/references.md#stick-hardware-and-runtime)
-link the vendor's debugging, sleep and peripheral documentation.
diff --git a/stick/docs/device-settings-and-display.md b/stick/docs/device-settings-and-display.md
deleted file mode 100644
index 0c8e472..0000000
--- a/stick/docs/device-settings-and-display.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Settings and display implementation
-
-For button instructions, see [controls and settings](controls.md). This guide
-covers persistence and pixel presentation for changes to the adapters.
-
-## Persist settings
-
-`pw-controls/layout-v2` stores layout, rotation and the two-key window.
-The display adapter applies rotation. Button mapping uses the selected layout.
-Older configuration keys are ignored.
-Without a saved setting, the layout is three-key L/M/R and rotation is left-side-down.
-Game progress is stored in the EEPROM image.
-
-Three-key Settings access observes a one-second physical L+R hold in `Controls`;
-normal directional transitions continue until recognition. The input bridge
-then requests the overlay, clears pending Center activity and requires release
-at entry and closure. The native Settings renderer keeps its two original
-choices.
-
-`pw-display/dark` stores appearance. Unsaved installations default to Dark.
-A failed persistence write leaves the previous appearance active. A successful
-change repaints the overlay immediately and invalidates the game image so it
-receives a complete repaint when the overlay closes.
-
-## Present native shades
-
-The port scales native pixels 2× with nearest-neighbor sampling and preserves
-native rendering cadence. Its RGB565 palette is:
-
-| Native shade | Light | Dark |
-| --- | --- | --- |
-| 0, background | `FFFF`, white | `0000`, black |
-| 1 | `AD75`, light gray | `528A`, dark gray |
-| 2 | `528A`, dark gray | `AD75`, light gray |
-| 3, foreground | `0000`, black | `FFFF`, white |
-
-RGB565 gives green six bits and red/blue five. The intermediate channels expand
-to (173,174,173) and (82,81,82) with M5GFX, within one 8-bit level of neutral.
-
-Call `setSwapBytes(true)` after panel reinitialization. M5GFX's
-`create_pc(uint16_t*, bool)` path otherwise interprets intermediate buffer words
-in the wrong byte order. Black and white still look correct, which can conceal
-the mistake. Light/Dark selects the software palette. Panel inversion, gamma
-and RGB order remain driver settings.
-
-The selected theme also applies to Settings, battery text and the game border.
-Wake clears the controller with the current background. Native Contrast maps
-to backlight brightness.
-
-## Check changes
-
-The presenter consumes an immutable native LCD snapshot and revision. A call
-transfers at most eight native rows and finishes with SPI idle, retaining no
-caller pointer. Remaining strips are scheduled independently of native refresh;
-appearance, rotation and overlay changes also invalidate presentation.
-
-`uv run python stick/check.py` exercises production input gestures, layout and
-appearance persistence, and returning from Settings to the native screen.
-After a display change, check the four shades and Settings on the board in both
-appearances and after wake.
diff --git a/stick/docs/firmware-simulation-research.md b/stick/docs/firmware-simulation-research.md
deleted file mode 100644
index 47b16e0..0000000
--- a/stick/docs/firmware-simulation-research.md
+++ /dev/null
@@ -1,130 +0,0 @@
-# Simulating the Stick S3 firmware
-
-The useful test target is the compiled Stickwalker application running on an
-emulated ESP32-S3 and a model of the Stick S3. Drive its buttons, acceleration and
-infrared receiver, then observe the screen, transmitted packets and persistent
-flash. This exercises the application, FreeRTOS, SDK drivers and board adapters
-together.
-
-Research checked on 10 October 2026. The selected bring-up target is
-[esp32sim](https://github.com/mikroverk/esp32sim), pinned to
-`fe3a9c08125cc760107865df3ffa979362952fe9`. It provides editable CPU and peripheral
-models, runs locally, and exposes the board events needed by Stickwalker. The local
-bring-up now executes Settings and Connect workflows on production firmware.
-The runner and setup instructions are in the [simulation guide](../simulation/README.md).
-
-## Simulator choices
-
-| Simulator | Relevant support | Work needed for Stickwalker |
-| --- | --- | --- |
-| [esp32sim](https://github.com/mikroverk/esp32sim) | Dual-core Xtensa S3; real ROM, bootloader and app; flash/PSRAM; I2C, SPI2, I2S, RMT; editable board models and scripted inputs | Stick S3 board devices, SPI3, dedicated GPIO input and timer-group watchdog behavior |
-| [Wokwi](https://docs.wokwi.com/guides/esp32) | S3 CPU, GPIO/IOMUX, flash/PSRAM, I2C, SPI, RMT, LEDC and watchdogs; loads compiled images | Custom Stick S3 devices; S3 I2S is unsupported; dedicated GPIO instruction support needs a runtime probe |
-| [Espressif QEMU](https://github.com/espressif/esp-toolchain-docs/blob/main/qemu/README.md) | S3 CPU, ROM, flash/PSRAM, interrupts and timers | Its support table lists I2C, GP-SPI, I2S, RMT and GPIO/IOMUX as unsupported; the existing framebuffer device uses a separate virtual interface |
-| [PastorCatto ESP32-Emulator](https://github.com/PastorCatto/ESP32-Emulator/tree/main/qemu) | QEMU patches add S3 SPI2/SPI3, I2C and native USB serial | Board devices and the remaining receiver/audio peripherals; inspect patches because the top-level README predates them |
-| [Velxio](https://github.com/davidmonterocrespo24/velxio/blob/master/docs/BUILD-QEMU.md) | Its own QEMU variant adds S3 peripheral callbacks and GP-SPI | Obtain its matching source/runtime archive through a free personal key, then inspect S3 I2S, dedicated GPIO and watchdog behavior |
-| [Espressif esp-emu](https://github.com/espressif/esp-emulator) | S3 compiled images, octal PSRAM, GP-SPI, I2C, RMT, GPIO, LEDC and watchdogs; native macOS arm64 binary | Establish how to attach Stick S3 bus devices; the public repository distributes binaries, documentation and tools |
-
-Espressif's [Arduino CI](https://github.com/espressif/arduino-esp32/blob/master/.github/CI_README.md)
-uses Wokwi and physical boards for runtime checks. Its current configuration
-disables QEMU by default because Wokwi covers more peripherals. M5Stack's
-[Stick S3 documentation](https://docs.m5stack.com/en/core/StickS3) supplies the
-board wiring, chip identities and firmware flashing routes; the documented
-development routes run on the board.
-
-### Wokwi automation
-
-Load a merged image containing the production bootloader, partition table and
-application. Set the board attributes to 8 MB flash and 8 MB octal PSRAM, and
-`cpuFrequency` to `max`: the default instruction-rate cap is about 8 MHz, while
-the optical sampler uses the 240 MHz cycle counter.
-
-[Automation scenarios](https://docs.wokwi.com/wokwi-ci/automation-scenarios)
-control buttons and sensors. The [CLI](https://docs.wokwi.com/wokwi-ci/cli-usage)
-records serial output, screenshots and logic-analyzer traces; it requires a
-Wokwi CI token. The [Python client](https://github.com/wokwi/wokwi-python-client)
-also reads display framebuffers and controls simulation start, pause and restart.
-
-[Custom chips](https://docs.wokwi.com/chips-api/getting-started) run as WebAssembly
-and can implement I2C/SPI devices. The [time API](https://docs.wokwi.com/chips-api/time)
-provides nanosecond deadlines. For the photodiode, model the voltage threshold
-crossing after the firmware releases GPIO5; Wokwi's
-[analog support](https://docs.wokwi.com/chips-api/analog) is limited, so a generic
-photoresistor or 38 kHz remote receiver would model a different circuit.
-
-## Stick S3 model
-
-The production board uses the following devices and routes:
-
-| Device | Connection | Behavior the model must provide |
-| --- | --- | --- |
-| M5PM1 | I2C1, SDA47/SCL48, address `0x6e` | Register access, supply state, millivolt readings, L-button state and read-cleared press latch |
-| BMI270 | Same bus, address `0x68` | Reset/configuration upload, initialization status, data-ready flags and timed acceleration samples |
-| ST7789P3 | SPI3; MOSI39, SCLK40, DC45, CS41, reset21 | Display commands, RGB565 RAM, orientation, visible window and status reads |
-| ES8311 | I2C address `0x18`; I2S0 MCLK18/BCLK17/WS15/data14 | Codec register state and I2S sample capture, with amplifier enable from M5PM1 GPIO3 |
-| M/S buttons | GPIO11/12 | Active-low contacts and timed presses/releases |
-| Optical link | Transmitter GPIO46; external photodiode GPIO5 | RMT pulse output and charge/discharge threshold input, including disconnected and loose-contact states |
-| Saved data | Production flash partitions | Real erase/write operations, retained data across reset and application updates |
-
-These routes come from `board_hal.cpp`, `sound_bridge.cpp`, `input_bridge.cpp` and
-[M5Stack's pin map](https://docs.m5stack.com/en/core/StickS3#pinmap).
-Use the [M5PM1 manual](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf)
-for defaults and latch semantics, and the
-[Bosch BMI270 datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf)
-for sensor configuration and readiness. The Stick S3 has a BMI270; the driver's
-optional BMM150 probe does not establish that a magnetometer is fitted.
-
-In the pinned esp32sim source, `DcsPanel::st7789()` already interprets display
-writes into GRAM. Add the Stick's visible crop and P3 status replies. Its ES8311
-device is currently register memory, so codec power behavior needs its own model.
-`BoardModel` exposes cycle-stamped output-enable changes and scheduled input
-edges, which fit the photodiode measurement.
-
-The pinned upstream needs three SoC additions for this firmware. SPI3 is named
-but unmounted; `ee.get_gpio_in` is an unimplemented Xtensa instruction;
-timer-group watchdog registers retain writes without executing watchdog stages.
-The repository's `soc.patch` implements these and records RMT pulse durations. CPU timing uses an approximate instruction
-cost, with 64-instruction scheduling rounds by default. Functional scenarios can
-use this scheduler; optical measurements need comparison with retained hardware
-traces before interpreting timing results.
-
-## Extending coverage
-
-Load a retained installation save and update only the application partition.
-Read the Pokémon, route and progress through the UI before and after updating.
-Inspect the final saved bytes with an independent save decoder, allowing only
-the documented identity and checksum changes.
-
-For Connect, drive disconnected, continuously active and intermittent receiver
-states. Observe the connection result and continued navigation, then restore a
-healthy optical link and complete an exchange. The bring-up comparison below exercises watchdog resets through the compiled
-receiver.
-
-Peer scenarios should use separate device identities and persistent saves:
-first meeting, repeat meeting, next-day meeting, reboot between encounters and
-interrupted walk transfer. Check the visible outcome and independently decoded
-transmitted packets/save contents. Retained Pokéwalker recordings and documented
-protocol behavior provide expectations; a second copy of Stickwalker supplies a
-peer but does not by itself establish interoperability with an original walker.
-
-## Bring-up results
-
-The production Connect candidate boots through the ESP32-S3 ROM and bootloader.
-Physical button actions open Settings, change rotation and appearance, restart
-the chip and reopen Settings with both choices retained. OCR checks the rendered
-screen against the behavior described in the controls guide.
-
-A second workflow holds GPIO5 low during Connect, opens Settings after the
-attempt, closes it, holds GPIO5 high and retries. It decodes emitted GPIO46
-pulses against [Dmitry's recorded protocol](https://dmitry.gr/?proj=28.+pokewalker&r=05.Projects).
-It checks 115200-baud 8N1 SIR framing, the transport XOR and the `0xFC`
-advertisement, observes the no-reply sequence and
-opens Settings afterward. Both workflows pass on the current candidate with
-application SHA-256
-`24ef7b4c95c8ccfd31369957cbfe2c98ec1b12c1017bbc32efa49fe0bda67661`.
-
-As a comparison, the released `candidate-2026-10-10` application with SHA-256
-`26fa8a557a05083a9df8f9e527cd169c95796e423d4a7267da990a9f005a54c9`
-resets during the held-low scenario. This comparison uses the same simulator
-models and compiled application images. The low/high inputs exercise electrical
-fault and no-reply behavior; retained optical measurements will establish the
-charge/discharge model for peer and Game Card exchanges.
diff --git a/stick/docs/history/README.md b/stick/docs/history/README.md
deleted file mode 100644
index de79689..0000000
--- a/stick/docs/history/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Device test records
-
-These records collect the transfer, power, input and audio trials used to develop
-Stickwalker. Each names the firmware, setup and result. Raw traces and private
-saves are kept locally under `stick/.build/`.
-
-- [Transfer trials](transfers.md) cover a retail HeartGold Game Card, the 3DS
- test peer, EEPROM readback and packet failures.
-- [Power and wake trials](power-and-wake.md) cover battery sleep, button handling,
- PMIC faults, display wake and save preservation.
-- [Audio trials](audio.md) cover score timing, codec startup and acoustic checks.
-
-For current instructions, see [development](../../README.md),
-[power management](../power.md), [debugging](../debugging.md) or
-[release preparation](../release.md). Each release's qualification record lists
-which checks ran on that version.
diff --git a/stick/docs/history/audio.md b/stick/docs/history/audio.md
deleted file mode 100644
index 743a9be..0000000
--- a/stick/docs/history/audio.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Audio trials, September 26-27, 2026
-
-These tests measured score timing and acoustic output. Raw
-artifacts remain in `stick/.build/sound-scheduling-2026-09-26/` and
-`stick/.build/codec-startup-2026-09-27/`.
-
-These are historical results, not qualification of the current
-[native-platform redesign](../native-platform.md). It removes score reanchoring
-and the fixed cold-start delay, preparing physical output separately from native
-score timing. Its acoustic onset and settling need fresh device measurements.
-
-## Score scheduling
-
-The native score walker and Stick adapter initially failed 102 of 176 host
-timing cases. An 80 ms foreground stall reduced a warm move cue's only tone
-to zero duration; even an 8 ms stall clipped its first note. Anchoring new
-notes to the actual output transition and selecting speaker channel 0 fixed
-the tested scheduling and overlapping-voice behavior.
-
-All 176 final cases passed across sixteen HGSS scores, including cold starts,
-warm repeats, initial stalls of 0/8/20/80/200 ms and a stall during playback.
-Tone/separator lifetimes agreed with unstalled playback within 200 µs.
-These are host command-lifetime observations.
-
-A USB bench recorded 20/20 complete native move cues after an 80 ms stall,
-with zero tone/begin/power errors and minimum command lifetime 52,667 µs.
-One I2S startup served the batch; codec/I2S flags returned to zero after the
-hold expired.
-
-The production app was
-`d9e3c352cf217a292557c099923fd2c7c3fa75c28a5db3be5059a2336d90aef3`.
-Its app-only flash and complete storage preservation were verified. The logical
-EEPROM hash stayed
-`5ccd615bca4bd063098e94bbab297c38b22475b64093869eefad74f2d421aac4`;
-boot checkpointed the existing journal from generation 567 to 568.
-
-## Cold output path
-
-The proposed missing-clock cause was falsified: all six observations found
-168 to 175 BCLK transitions per 400 µs before the first note.
-
-The codec retained power-sequence register `0x0C=0x20` and 24-bit input while
-output was 16-bit stereo. The adapter now verifies stage setup before activation,
-configures DAC bias/format and handles an acknowledged first-write loss.
-Cold preparation uses 200 ms, with no preparation delay for warm repeats.
-At 65 ms the diagnostic ADC transient obscured the 52 ms cue; 200 ms is a
-the preparation interval used by the passing tests.
-
-The board self-test uses codec digital feedback and microphone energy, requiring
-both the native cue near FFT bin 16/17 and its observed fifth harmonic near
-bin 82/83. It reports the spectral measurements and discards the samples.
-
-## Results
-
-- BCLK-before-tone: 6/6 present.
-- Physical outgoing PCM: cold and warm native cues both present.
-- Original ADC startup: samples stayed zero until approximately 730 ms during
- a sustained calibration tone. Selecting fast VMID charge alone did not
- remove that delay in the tested configuration.
-- Clearing the stage timing removed the long ADC gate, but cold 65 ms captures
- still had a large analog transient. In the retained eight-case replay, all
- four cold captures failed the spectral criterion; all four warm cues passed.
-- Final 200 ms implementation: **24/24 acoustic cases**, 12 cold rail cycles
- and 12 warm repeats on the same boot. Minimum qualifying sample count was
- 1,367. All 24 had complete 512-frame spectra and the expected spectral bins.
- Twelve I2S starts, no begin/power/tone errors, and final codec/I2S flags zero.
-- Amplifier-disabled control: the digital peak stayed at bin 17, while the
- microphone peak moved from bin 83 to bin 6. Maximum note-band amplitude
- fell from 293 to 8. This distinguishes acoustic pickup from digital leakage.
-- Native score timing: **176/176** cases over the 16 HGSS scores, initial
- foreground stalls and mid-note stalls. Pitch and note/separator durations
- remain within the existing 200 µs comparison tolerance.
-- Register-order/fault regression: the previous adapter fails every case;
- the final adapter passes normal startup, first-write loss, retained resume,
- full rail loss, and safe failure after persistent write loss.
-- All 16 existing lifecycle/fault cases pass with the updated cold deadline.
-
-The microphone capture includes the ADC's startup transient, which obscured
-the tone in the failed cold captures. The final 200 ms setup produced the
-expected signal in both cold and warm tests. These measurements describe the
-recorded signal spectrum; listening checks assess the resulting sound.
-
-One intermediate spectral diagnostic overflowed loopTask's stack while printing.
-That batch is invalid and remains in the evidence directory. Diagnostic buffers
-were moved to bounded static storage; the final 24-case batch had no resets.
-
-The production sampler functions retained their complete bytes and addresses.
-
-The installed codec-startup production app was
-`edcf812717e01e48ac78c1aa9bd2d6e2f930608778bf68cca3afd394deebee94`.
-App readback, the complete storage partition and all logical EEPROM bytes
-matched their backups; CPU0 readback found ready=1 and advancing RTC after resume.
diff --git a/stick/docs/history/ir-readiness-regression.md b/stick/docs/history/ir-readiness-regression.md
deleted file mode 100644
index 0943eb7..0000000
--- a/stick/docs/history/ir-readiness-regression.md
+++ /dev/null
@@ -1,146 +0,0 @@
-# IR readiness and DS reply regression
-
-PR #5 moved receive handoff and durable EEPROM work into the compiled Stick
-port. The user subsequently reported both immediate DS connection failures and
-failures after the transfer sound began. Hardware was unavailable for this
-investigation.
-
-The references are the matching [Pokéwalker firmware](https://github.com/lumirth/pw/tree/6dc7bc09950078fa3fe0dffa4dae34e9549a99da)
-and the user's HGSS `src/phc/irc.c`, `phc_lib.c` and PHC record definitions.
-The pre-PR comparison is `b3ef7a2`; its existing Watts byte-order defect was
-accepted only by the diagnostic comparison peer, so it could exercise the
-same acquisition and transfer sequence.
-
-## Reproduction
-
-The original ideal, short status/read exchange passed. A longer synthetic
-uncompressed course transfer at the captured retail-card baud, with local
-transmitter echo and separate CPU instruction-cost scheduling, failed on the
-PR firmware. The pre-PR firmware completed the same exchange. One retained
-failure changed a data bit in a 136-byte page, causing the native checksum to
-reject it and the DS peer to reach its unchanged 100 ms timeout.
-
-The course-only comparisons used the same 10,430-byte public payload
-(`b5dcc0c82d28b8f9270aa4add67523011952a0c5aeb63253126188ee5574afa8`),
-115760 baud, 5 ms peer turnaround and live local transmitter echo:
-
-| Firmware | Result |
-| --- | --- |
-| Pre-PR `b3ef7a2` | All 82 course pages complete; legacy Watts accepted only for comparison |
-| PR's original IR implementation at `68ae1fe` | Corrupted page; timeout after eight native transmissions |
-| Sampling/handoff repair alone | Valid pages, then a late reply after 27 native transmissions |
-| Sampling, bounded readback and exact decoder optimization | All 82 course pages complete |
-
-The producer had acquired additional request/ack fences and handoff bookkeeping
-inside its timing-sensitive gate function. The worker now stops at a complete
-burst and performs notification and release waiting outside that function.
-Readiness observes fresh progress in the existing published sample counter;
-it still waits for physical samples before initial transmission and replies.
-Receive-to-reply storage remains protected by a consumed-event ticket.
-
-After removing the acquisition failure, the broader payload exposed late
-replies during journal growth. The write path retains checked open, size, write,
-sync and close operations. Its independent readback uses a fresh POSIX
-descriptor to read only the appended header and payload, avoiding buffered
-seek/read prefix work. Reader closure is checked too.
-
-The decoder's complete candidate set, framing-first/phase-second ranking,
-original tie order, baud range, phase range, strong/weak thresholds and recovery
-rules are retained. It visits likely baud candidates first and rejects a
-candidate only when its already-observed framing errors and nonnegative phase
-sum prove it cannot win. Winning data-cell occupancy is built once. A
-sanitizer-backed comparison against the original decoder matched all output
-bytes, geometry, cached-baud behavior and recovery diagnostics on 2,048
-generated waveforms. This creates reply-time headroom without accepting a
-packet the previous search would reject.
-
-## Permanent coverage
-
-`peer-transfer` follows HGSS's uncompressed put choreography with public data:
-status/Watts, 82 course pages, five trainer-record pages, PHC and distribution
-reads, the checksum/ping exchange, commit and one-way shutdown. It checks the
-native committed course and record independently after reset.
-
-`peer-play` uses a synthetic eligible Walker and exchanges status, animation,
-name, trainer-house records, peer information and start. It checks both bulk
-transfer directions and the received resources and native peer-history entry
-after reset. This exercises one handshake role, not all collision/backoff cases.
-
-Run the exact production build through:
-
-```sh
-uv run python stick/check.py --scenario peer-transfer --scenario peer-play
-```
-
-These workflows execute the ROM, bootloader, FreeRTOS, SDK and application and
-use optical/button/bus inputs. They do not inject native game state. Their
-instruction-cost mode schedules the CPUs independently in one-instruction
-quanta using the simulator's opcode, branch, call and load-use costs. It is a
-software timing regression test; analog acquisition, cache contention, flash
-part latency and retail hardware interoperability remain outside its model.
-The frozen machine-byte timing reference remains a separate diagnostic.
-
-## Initial software candidate (`22769ce`)
-
-All 13 production workflows pass on application
-`05ed911a060fca5c3e023a7994e351ed553974e68789a5caff5e6283f31730e5`.
-The full put completes 135 native transmissions, then independently verified
-course/record persistence after reset. Its maximum modeled DS send-to-complete-
-response interval is 91.99 ms against the unchanged 100 ms deadline. The same
-exchange also completes on the fresh bench build. Walker resources and native
-peer history survive reset.
-
-Both builds' input and output hashes match their manifests. Static checks pass,
-and Renesas 6.02.01 and 6.02.02 each reproduce the canonical 49,152-byte image
-(`f9e210a3b74afbbd12c5a66a51cc05cb9fbac986805ff0a3bfb4be6074d15607`).
-The frozen machine-byte comparison differs for all three tracked functions;
-its reference has not been updated. These new images were not flashed.
-
-## Retail put failure and flash busy timing
-
-The initial local-artwork candidate was installed and app/non-app flash verified.
-The user then reported successful return from stroll, failed start of stroll, and
-occasional black screens. A single subsequent diagnostic attempt decoded all five
-incoming frames with no checksum or malformed-page rejection. They were response,
-status, put request, and compressed course pages at `0xD700` and `0xD780`.
-The last acknowledgment began 108.039 ms after the peer's final pulse; the journal
-append maximum was 98.022 ms. The earlier production capture retained a 103.471 ms
-append. No crash was captured; the user could not reproduce that symptom.
-
-HGSS `compress`/`sendData` independently compress 128-byte course and trainer pages
-using Nintendo LZ10, falling back to raw pages when compression does not help.
-The last partial page is sent in a padded 128-byte form. The original full-put
-workflow covered only uncompressed writes and therefore omitted this path.
-
-The SPI flash model also completed programming and erases instantly. Optional WIP
-busy periods now exercise the real SDK's polling: 300 microseconds per program
-command and 20 milliseconds per sector erase are explicit stress parameters, not
-claimed measurements of the installed flash chip. With those periods and the
-compressed put sequence, the installed filesystem-backed implementation fails
-unattended: a 116.078 ms append produces a 132.544 ms acknowledgment and the peer's
-unchanged 100 ms timeout.
-
-The backend now uses a single flash-record format in the existing game-data
-partition. Two checkpoints cover a bounded log. Headers and bodies are programmed
-and independently read back before a final publication word. Log space is erased
-before acquisition; append operations cannot erase during an IR session. Checkpoint
-replacement and journal clearing run after publication outside that session.
-The filesystem and old save/configuration conversions are removed.
-
-The first raw-log build completes the full uncompressed put and Walker exchange
-with the same flash-busy profile; its maximum modeled DS response is 39.04 ms.
-Recovery checks cover current records, stale records and an unpublished truncated
-append. Permanent compressed-put coverage and a bus-observed zero-erase invariant
-protect the timing window. Unsupported filesystem data and a corrupt published
-checkpoint are rejected without conversion or erasure.
-
-This storage format requires initializing the game-data region. Return the Pokémon
-to HGSS before pre-release updates; see the [install guide](../install.md).
-
-The final public and local-artwork production images each pass all 14 workflows,
-including compressed put and the zero-erase reply-window check. Public application
-SHA-256: `a8aa2659834cfed7deeb800a95bca5a57ff360ee415897d78377dd4aaa01591b`.
-Local application SHA-256: `a5b890cad28ec7f800d98944243e12cb35c987aad983046717734faff57c7ab4`.
-Independent negative boots also reject unsupported data, a corrupt published
-checkpoint, and a corrupt newer checkpoint even when an older valid save exists;
-all leave the data partition unchanged. Source/output hashes and static checks pass.
diff --git a/stick/docs/history/power-and-wake.md b/stick/docs/history/power-and-wake.md
deleted file mode 100644
index b1a65ec..0000000
--- a/stick/docs/history/power-and-wake.md
+++ /dev/null
@@ -1,113 +0,0 @@
-# Power and wake trials, September 26-27, 2026
-
-This record preserves measured outcomes and failed observations from successive
-images. The [current power guide](../power.md) defines the implemented policy.
-The early trials used an M/R wake mask and a dark-screen L polling cap; the later
-M-only policy superseded both.
-
-## Initial sleep and wake tests
-
-Raw artifacts are local under `stick/.build/power-hardware-2026-09-26/`.
-
-| Artifact / attempt | Outcome and meaning |
-| --- | --- |
-| `sleep.json`, `sleep-reconnect.json` | USB-connected sleep commands lost subsequent observation. The trials ended without a retrieved sleep count. |
-| `startup.json`, startup/boot diagnostic logs | Awake startup/configuration observations; stage reporting introduced after PMIC startup failures. |
-| `dark-focused.json` → `dark-recovered.json` | First retained dark observation: 56 returns, 4.956 s in sleep calls. Recovered from the retained trial record. |
-| `dark-final.json` | Harness could not retrieve its expected previous record; failed observation. |
-| `dark-bus-diagnostic.json` | PMIC startup failure after warm reset; SDA and SCL were both high after the failure. |
-| `dark-recovery.json` | Completed approximately 5.093 s dark trial: 56 successful sleep returns, five accelerometer reads, native clock +5 s, zero recorded failures. |
-| `lit-recovery.json` | Completed sampling trial but four sleep entry errors and only 17 successful returns / 0.912 s in sleep calls. Failed power-scheduling result; original error codes unavailable. |
-| `lit-reject-cause.json` | Completed approximately 5.025 s visible trial: 82 returns, 80 accelerometer reads, clock +5 s, zero recorded failures. |
-| `lit-reject-cause-2.json` | USB device disappeared; ROM recovery could not open the missing port. Failed observation, required physical replug. |
-| `audio-reconnect.json` | Incomplete initial audio observation; later `audio-final.json` is the completed check. |
-| `audio-final.json` | Two cold-supply playback cycles. LCD/audio supply and amp 00→0c→00, codec/I2S stopped after hold, zero begin/power errors. |
-| `rx-final.json` | Two-second receiver worker start/stop, 240 MHz startup prerequisite, return to 80 MHz and IR boost off. PMIC first read failed and recovered on retry 1 during receiver startup. The trial exercised start and stop with no peer. |
-| `jtag-availability.log` | Espressif OpenOCD examined both cores using built-in USB JTAG; no halt, breakpoint, reset or flash command in that probe. |
-| `pre-flash-coredump-analysis.log` | Initial backup already contained a CRC-valid historical dump. Official decoder rejected a mismatched ELF; matching historical ELF not found. |
-| `rejection-host-before-fix.log` / `rejection-host-after-fix.log` | Regression exposed the one-second veto; revised scheduler permits ordinary rejection without resetting wake configuration. |
-| `vbus-regression-before.log` / `vbus-regression-after.json` | Persistent failed power reads incorrectly allowed sleep before fix. Fifteen host lifecycle cases now pass, including unknown VBUS veto and transient read recovery. |
-| `final-host-ir-regression.log` | Three TX lifecycle/symbol tests pass. |
-| `usb-veto-final.json` | Final diagnostic build kept sleep count/error count zero during six seconds after Y arm, with measured VBUS 5218 mV. |
-| `production-jtag-*.log` | Initial running-memory read was rejected (target not halted). Subsequent dual-core reads were inconsistent with the ELF while CPU1 could not be examined. The installed supported ESP_ONLYCPU=1 configuration returned ready=1, USB veto=1 and the expected application descriptor magic. Brief halt/resume was followed by a fresh standalone reset. |
-| `flash-interim-production.log` | A previous production output was flashed while a diagnostic rebuild was still running. The interim image received no tests. Later trials used completed builds, and each flash preserved the storage partition. |
-
-Sleep-call durations include entry and wake overhead. The PMIC recovered on
-retry after a failed read; the records do not locate the electrical fault.
-
-## Panel and physical-input findings
-
-The display review established that a command without chip-select ownership
-was ignored. RDDPM remained `98` after an unselected display-on command and
-changed to `9c` after the selected command. Following restoration, MADCTL was
-`b8` and pixel format `05`.
-
-The wake diagnostic found a pending gesture could inherit the inactive
-one-second deadline. Before correction, a generated Center gesture still had
-the panel/supply off near 923 ms. After correction, all nine generated cold-wake
-cycles had panel, PWM and supply ready near the 0.9-second observation.
-A subsequent physical held-M failure showed that generated inputs had not
-qualified switch acquisition.
-
-EXT1 preparation left M/R in RTC IO while polling used digital reads. The
-corrected handoff restores digital ownership after every sleep return. The
-hardware mux probe reported `before=3 restored=1 after=0` twice. Two timed
-physical-button windows captured no M edge.
-
-`rtc-mux-production-liveness.log` had invalid descriptor `0xbad00bad` and no
-clock progression, so its RAM values cannot identify a crash cause. A fresh
-USB-UART reset using DTR/RTS false before opening the connection was followed
-by valid descriptor `0xabcd5432`, ready=1 and RTC progression over 1.5 seconds.
-That reset-capable recovery and its post-reset verification are both retained.
-Wake artifacts are under `stick/.build/wake-diagnostic-2026-09-26/`.
-
-## M-only policy verification
-
-The September 27 host checks covered eight input configurations, sixteen
-runtime/fault cases, fifteen abandoned-wake cases and three codec cases.
-The dark-mask/cadence regressions failed on the preceding code and passed
-after the M-only change.
-
-Eight USB-awake generated-input trials passed: two abandoned M taps, two
-accepted M holds, two ignored dark L events and two Settings opens after wake.
-Accepted wakes reported LCD status `9c`, pixel format `05`, PWM ready and PMIC
-output `04`; rejected gestures left output `00` and the panel asleep.
-
-The first probe failed its obsolete native-Center injection expectation at
-0.9 seconds. It was changed to exercise M wake; `hardware-wake-attempt1.json`
-and its log remain under `stick/.build/m-only-wake-2026-09-27/`.
-
-## Installation identities and storage checks
-
-Each listed image had app-only flash verification and retained sampler machine
-bytes. Each storage comparison used that trial's pre-flash backup.
-
-| Milestone | Production app SHA-256 | Local evidence directory |
-| --- | --- | --- |
-| Lifecycle | `b94ddf991fb495b0ac01849f855bd4426e69632700b19fd01a9f998723465b68` | `power-hardware-2026-09-26/` |
-| Wake deadline | `7778124e9eb41f2a59a766a1360f1a6e1575055340c98b6bcdcd2e39aae2f7a7` | `wake-diagnostic-2026-09-26/` |
-| RTC GPIO handoff | `12fa6823643b6d62d60242b2cb8fbc04deb877d41c0c274593c2348e8ed2f98b` | `wake-diagnostic-2026-09-26/` |
-| Panel restoration | `a782fd21b5aa5380481b4412406948faabbba09808b0ef44137c6bb859cf3024` | `wake-diagnostic-2026-09-26/` |
-| Abandoned wake cleanup | `4fb5efcec62ace583f9b19ec46e35b0f677c2f39314f27a437725de8cde56398` | `abandoned-wake-2026-09-27/` |
-| M-only policy | `5e29d017a468958a07f74da98e4d60602f1e8a28530a742ee5f1bd81631b6c43` | `m-only-wake-2026-09-27/` |
-
-The lifecycle trial's independently reconstructed 65,536-byte EEPROM hash
-remained `324c0e5d46de68c861195b71132179cc1fed7eea94c02918d6aa7717d9f11885`.
-Boot checkpointed six valid journal records into snapshot generation 566;
-physical filesystem bytes changed but logical EEPROM did not.
-
-The panel-restoration block retained an initial failed whole-filesystem equality
-check in `display-production-storage-verify.log`. Independent reconstruction
-found course resources, status/device IDs, paired Pokémon and save checksums
-preserved. Logical EEPROM did change: the clock advanced 23 seconds, diary
-cursor moved from 21 to 22, and entry 21 recorded native boredom action `0x15`
-after a generated Center gesture accepted it. Changes were confined to that
-136-byte diary entry and the two 24-byte save payloads/checksums. Steps, Watts
-and Pokémon-minute counters were unchanged. The resulting filesystem then
-matched a fresh snapshot. `display-save-comparison.json` preserves both results.
-
-The later abandoned-wake and M-only trials preserved logical EEPROM hash
-`671d392987cf62c9dd845c62e4c2ae13cc679dc828f788c5af44e047701120d5`.
-Checkpointing moved generation 568 to 569 with zero changed logical addresses.
-The M-only production flash also matched its complete pre-production storage
-partition; CPU0 readback found ready=1 and advancing RTC after resume.
diff --git a/stick/docs/history/transfers.md b/stick/docs/history/transfers.md
deleted file mode 100644
index 6aac8bc..0000000
--- a/stick/docs/history/transfers.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Transfer trials
-
-These trials used a retail HeartGold Game Card and a 3DS test peer running the
-HGSS communication protocol with generated course data. Raw traces and EEPROM
-readbacks are kept under `stick/.build/trials/`.
-
-## Recorded operations
-
-- A source-faithful 3DS entry completed all 368 pages with 374 valid Stick
- receives and no invalid packets in
- `stick/.build/trials/port-hgss-real-image-atomic-complete/`. Independent
- EEPROM readback matched the HGSS source-built image and the transmitted
- course byte for byte. The course and record in that trial were sealed random
- payloads used to check transport and storage. A frozen `.137`
- app image remains at `../bench/experiments/g5-terminal-start-137/app.bin`;
- its full pre-port flash backup is also retained under `stick/.build/`.
-- A real HeartGold Game Card completed a walk-start transfer with the Stick.
- Its optical UART clock measured about 2.9855 gates/cell, outside the old
- 3.0000-3.0080 fit range learned from the 3DS bench transmitter. A wider
- full fit recovered its packets but took 132-149 ms, exceeding both the
- game's 100 ms reply timeout and the port's roughly 98 ms inactivity check.
- The receiver now searches the full start-phase range around the current
- burst's or a previous credible long burst's baud estimate, and computes
- the expensive phase score only for competitive fits. This uses UART framing
- only; the original protocol still checks commands, tokens, and checksums.
- The successful real-card trace recorded 374/374 valid optical bursts,
- no rejected burst or checksum failure, and a normal completion. Its first
- 32 retained burst diagnostics all used the fast fit; maximum recorded decode time
- was 30.9 ms and maximum recorded reply-start time was 31.7 ms. A 65,536-byte
- EEPROM readback after transfer had matching status and save mirrors and
- clear commit markers. After reboot, the received course and staged-course
- regions remained exact; only mirrored save counters changed. The validated
- bench app hash is `56b27c350da65bde2efcd855709cfcbb87519a31f482f08a6ff4f69482f9a7c2`.
- The cleaned bench app had the same sampler machine bytes; its hash is
- `6e38abbed86906bb4d98d2ec3ecbe954f159a366d006929bff53df1b707bbc35`.
- The same source was then compiled without `PW_STICK_BENCH_CONTROL` and
- flashed as an app-only update. The diagnostic-free image hash is
- `317943ba93b54810ea5be57fc936574f70bf403b63a1196458b4b174b59129d9`.
- Flash verification passed, and the user reported that a normal HeartGold
- connection on this production image worked. This second real-card success
- is user-observed; the production image has no per-burst serial trace.
-- A source-faithful registered-walker `back` transaction on the integrated
- firmware received 90 valid bursts, ended with the original HGSS DONE result,
- and cleared the saved Pokémon flag while retaining registration. An `entry`
- request on that already registered walker was correctly rejected by HGSS.
- The bench artifacts are under `stick/.build/trials/journal-hgss-005-back/`.
-- Two subsequent source-faithful `put` transactions and an intervening `back`
- completed on the same Stick boot (`journal-hgss-006-put` through
- `journal-hgss-008-put`). The two `put` runs each received 133 valid bursts
- without an invalid packet, and the intervening `back` received 90 valid
- bursts without an invalid packet. Each operation reached the original PHC
- DONE state. Independent 64 KiB EEPROM readback after both `put` runs matched
- every byte of the 10,430-byte course fixture, with both status copies and
- checksums valid and the Pokémon flag set. The fixture comes from HGSS's
- first-course table, compressed artwork, and course background. It chooses a
- plausible starter and renders English labels because no trainer save or DS
- message renderer is running on this bench; `hgss_course_fixture.py` records
- these choices. The 3DS still executes the original PHC protocol engine.
-- A further same-boot diagnostic series completed ten `back`/`put` cycles:
- 20/20 original PHC operations reached DONE, with 90/90 valid packets in
- each `back` and 133/133 in each `put`. The ledger and independent final
- EEPROM verification are in `stick/.build/trials/opening-repeat-ledger.json`
- and `opening-repeat-final-verification.json`. The first attempted repeat
- before this series failed on `put` page 59: the source 3DS transmitted wire
- `AA` in the page's first byte, the Stick decoded `AB`, the source checksum
- rejected it, no ACK was sent, and both ends timed out. That trial is retained
- as `journal-repeat-01-put`.
- A bench-only 96-gate opening snapshot now prints when a checksum fails so
- the next occurrence can distinguish weak/missing optical observation from
- phase assignment. The flashed diagnostic app SHA-256 is
- `b8ec2c57cf16cf8834e9f1816adb95d2666ba6542ee4dad7ada4b724a9325dd9`.
-- A longer frozen-image diagnostic recorded 44 successful whole operations
- before `cycle-023-back` failed. The source 3DS sent a page control packet
- beginning `A6`; the Stick decoded `A7`, the source checksum rejected it,
- and the peer timed out. Two release-late gate sentinels occur near that
- packet's opening UART cells. The artifact retains the full trace and the
- opening gate snapshot under `stick/.build/trials/opening-long-001/`.
- The sentinel encoding overwrites any measured crossing on those gates,
- so the capture does not distinguish a missed optical pulse from a late
- sample. All 45 attempts remain in the ledger.
-
-## After initial power changes
-
-The first power candidate completed 20 same-boot operations with exact course
-readback. After wake corrections, a later candidate completed a fresh `back`
-and `put`, then 10/10 further operations with valid status mirrors and exact
-course readback. Artifacts are under `power-001-repeat/` and `power-004-repeat/`.
-The initial Light-sleep diagnostic also completed a `back` and `put` with
-90/90 and 133/133 valid receives and all 10,430 course bytes matching.
-
-These runs qualify their own images. See the [validation summary](../validation-history.md)
-and [release checklist](../release.md) for the remaining production trials.
diff --git a/stick/docs/images/three-key-settings.png b/stick/docs/images/three-key-settings.png
deleted file mode 100644
index 2590f0d05379003150c9895e0ec089e6f79827e0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 6688
zcmds5c~nzZ8h?nmgLd3yvmHCpIH^knj3Qx1MQtq@O$ZH3swmXNh!BGU%_6N*7o=7}
z!I)SmLIMdQJTL+Qi%XF}Y&D6p1!YN10s&))BtYh6&YU@?=ZwCLT~F^H_nwoR_pQJC
zZO69-Z*rZoULu3I*5*bV?LmjS>TIcXg9ixm^^4gm8tTQ>Xyf0*0X-nxI$k~uE-
zr!{)7rZmM?kc??7GDOd~2|KseJ-(`pZkQjDy6A)OFO11f(8qs@Rd7A(NWYb*R5Bvt
znE^@qJWI9iI>{12bBi59d@t5r&B-ctCwd;IDgtbDh4eE-$$AbUAPbxZX}|mnjx%(8
zf#`&G;OXSm(9StL&J8*i@*KTlN>4}()=D-#QboNxR_-5*XOnp7d$RcST&%TeXD)>v
zM#FC1&F7=B2pg+U*y5i-N;}?ljd$c8w-e&@rhI=QZHGqdhCdWZ6lhLu7h!kTlvt%*
z7MVP$;3H#~`6RhCO*PCSl=&NtqU4Lul0Xyr#@gC3~=BFQPDm
zce@?A7c@GD>;InBzb-8J=UekX>Kcp$86&cG^rBTt&a~8X&5Gw-oS|_|F**&wo){99
zhQ6zK+RZq8?UuSElF&N+kmq^Eq;Ke{KOe{c#*-Zsf55d+W}0$PZT?adp#pC{;=A9L
zRct;Fz4FAbr14GripIi&BT!ZSS`^8G)*h1No3bi`qj09$Mw9Amlk=ClXoekt0PFo%
z1KeN)UHpdYx5^1yALUUvl)w7du!N#|903v2j}i{=ZlbH{BeofxtKePjE682BqtVll
zx4(}i#-dxbDb0llb2<|rp3>HhrG(BDiJsN5EZTsF>UU#W7zI7lj=2B|J{HinZ|so>
zJ(zrLm53SBbNUwYZKgbXSg>xMm7BZ*p}O*SED9wF$nLvT
zt>0~GS4u;{wVLo=jf8~B)0g&!BPW_hU9EypbdTiH=Z5C$qzRoEc+0-CY??QLv5VGp
z5;QlI(2C;Rto;ok-1J-Sy)%mn{VUy66`;~fmqbj_*D&$8;o*^o>gh$J(_J~k@aC2L
zZNV{2!S)GIJoJGT&N%P^+*Wm1{H+@h=mP7-K{sJ;Zcv@7
z?JR%4YIcB-)YS6$!f;}Y@??@&L@ySBAwoWYF0oRgvOw2S$2$*aqlYPrI*
zb1BpBMwMb@Yrjpi3YBtc^r~CREMQ<2yy=961C1o-y#2QgDG7}QL7*KeUR_#hQHe>4
zoX4br+v2-4=ulBk(C9kK73rD&(oG8WbX7~V+orxJdB|1o_WuQV25au8g52#bUViCv
zy}U|8D)ANLOmD@A(?Nc+#@C85`fE{!!AMqfO8~JoFGz7OThhyqc3Uc^jMlhZUg?Mu
z=adOkYg#L6eYG8|-p$2-KX5Huz0CS#CR%Cco{jD5R-e-#yJY1R6n960e8GwLFPzm)
zDnC^o=d0{+W;eSR%Vif!Yib*rqXMjWPTtV)trzTrP6eJ{$Oya3uBMyGOjNf)nVCCjXgG^lM{2;
zwn4*Q+xZL6x^0!Gp0Chvu@gTz>D6JFDeecbHO}gF!*!D_{j)3Aj{~^%u;Gk{L>_(7
zKBOiR^9^w%65Fp#l%)*!S1IY43zf}KdH?>tqI?rBq(dR5N{=eW0o@6(1!%#EDcBpB
z+LnT#b{kfh+f<0iNN#TE%6VK5mJJ3sokPSFkL9q%41km4*h{gF4&izA*|etOD>!xp
zTEVZ+QtISJl1WvavjhMPj!}^%yBcVqf9%6i8@iPgo}J6u!;QQPCbE>FC>*+nswp1M
zs!>s#fCTuSV#mg#F9{o3Z!wZYX0s31G
zxxk;erF-{r@Z*?(B$|$@8u6k=6O|)EOnImiCjvIPy&iYJ@@-vZq}n#g*k*1Y6zHCl
zYW<30^yBk7s!ULy;=?TNx3jMc`AG}=(}vp)T=tG0Cn2-Tf}9WarA9~Wzh}K6b0O&k
z;p8WKsd&)|X$7g6R@36qT>P>uw9!KdnI2f;2rd!|afIa-p0TZfN3Ch0$FQ`KyP9SJ
zz{hd0W*@}ko}4%N@XDyJr!Dl=l-^B*Xh{5dw;gf0ySC`S*HeyR_EqXAj~NjFaA2H6
zfnD4=5H&sDfT6Yuuk6wH&Vc3@CuhR6_P;U$-jw`|UQ)Z$Sfbjz{y}_X)C;0|>CDOtaPtrI^nvx?`V^`8a}`{4dSPi_!cC#FRZT;V6-L
zoup?+UPtp{wB~9Y&m5M9R;M(|%65dhXbo@xa0}
zaNZ8|C*Z@jsVXf1e>*C5ei0>s(7daQEY^`;dR#kfP+#H)AlMg+
zR|H2l$+H!Q5QcJKpufvD;toj_IO}yKw`zsCGDGO$U0g~}o}Oy!W{
zVapKvs3X#(@P1Zwa3F$k6z8*_l)So3*oYarew5XlL$(SiV7ev1N86wnT7OM{!&e#V
zm$F}9C0z!zI21|BvnvzLgnK`h4P`N;jaEytuch~Z%JAGPrul8eBh$r|+PU8tFsM(@
zI|X;azO=YjBod(x>xiXgrcoGc@Qh&+X!h9wo*6O~u=X60-kVC*`2^mRmMROA7o+5u
zB%V`qw?kAP3VfDpdyTlt`}_!}Zj33Gp|UK)})BBRZ!azM9^ZA)Sv9tNyv}p
zpsBtD_qmP)Q~HOi04Mx<0O+K`=E~m=*M9;|144amvXsK$()zC0)!DK!c!T(#AE*8c
D(!)xZ
diff --git a/stick/docs/install.md b/stick/docs/install.md
index 8a662ed..d8ee6ba 100644
--- a/stick/docs/install.md
+++ b/stick/docs/install.md
@@ -1,120 +1,42 @@
-# Installing and updating Stickwalker
+# Install Stickwalker
-Installing Stickwalker replaces the firmware currently on your Stick S3.
-Back it up first so you can restore it if needed.
+Download and unpack the firmware ZIP from [Releases](https://github.com/lumirth/stickwalker/releases). Run the commands below from the unpacked folder. They use [uv](https://docs.astral.sh/uv/getting-started/installation/) to run esptool.
-Download the firmware ZIP from the
-[Releases](https://github.com/lumirth/stickwalker/releases)
-and unpack it. Check its SHA-256 against the release's `SHA256SUMS`, using
-`shasum -a 256` on macOS, `sha256sum` on Linux or `Get-FileHash` in PowerShell.
-You can also [build the firmware yourself](build.md).
-Use the M5StickS3 USB-C port with a data cable. On macOS, the serial port usually
-appears as `/dev/cu.usbmodem*`; on Linux, `/dev/ttyACM*`; on Windows, check the
-COM port in Device Manager. Compare the device list before and after connecting
-if several ports are present.
+Connect the Stick S3 with a USB-C data cable. Replace `PORT` with its serial port, usually `/dev/cu.usbmodem*` on macOS, `/dev/ttyACM*` on Linux or a `COM` port on Windows.
-Replace `PORT` in the commands below with that port. Run them from the firmware
-output directory. [uv](https://docs.astral.sh/uv/) runs the pinned esptool version;
-esptool connects to and resets the board.
+## Photodiode
-## Back up before writing
+The Stick's built-in IR LED transmits. Add a BPV10NF to receive.
-Save the full 8 MiB flash somewhere outside the firmware output directory:
-
-```sh
-uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT read-flash 0 0x800000 /path/to/stickwalker-before.bin
-```
-
-Keep this backup private: it contains registration, Pokémon, game data and device
-settings. Check that it is exactly 8,388,608 bytes before proceeding. If the
-board won't connect, enter its USB download mode using M5Stack's
-[StickS3 instructions](https://docs.m5stack.com/en/core/StickS3).
-
-## Update a pre-release installation
+| BPV10NF | Stick S3 |
+| --- | --- |
+| Anode | GND |
+| Cathode | G5 |
-Return your Pokémon to HGSS before updating. Pre-release builds do not promise
-save compatibility. Keep the flash backup, then initialize the game-data region
-for the new firmware. This resets registration, steps, Watts, items and other
-walker progress; the Pokémon is already safe in your game. Device settings in
-NVS and resident artwork are separate from this region.
+Use the [package drawing](https://www.vishay.com/docs/81503/bpv10nf.pdf#page=4) to identify the leads.
-Check that the installed partition layout matches the new build:
+## Back up
```sh
-uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT read-flash 0x8000 0xC00 /path/to/installed-partitions.bin
+uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT read-flash 0 0x800000 stickwalker-backup.bin
```
-Compare those bytes with `PwStick.ino.partitions.bin`, using `cmp` on macOS/Linux
-or `fc /b` on Windows. For the matching app0 layout used by this guide:
+## Update
+
+Return your Pokémon to HGSS first. Pre-release updates reset registration and walker progress. Button layout, rotation and appearance stay saved.
```sh
uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT --after no-reset erase-region 0x670000 0x180000
uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT write-flash 0x10000 PwStick.ino.bin
-uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT verify-flash 0x10000 PwStick.ino.bin
```
-The backend supports only the current flash-record format. It starts a new
-walker on blank storage; unsupported or damaged storage reports a fault rather
-than converting an old save. After installation, connect HGSS to start a stroll.
-
-Use these commands for updates. Arduino's Upload command also rewrites the
-bootloader, partition table and boot selection. If the installed layout differs
-or app1 is active, inspect that installation before choosing an application
-address. These instructions use app0 at `0x10000`.
-
## First installation
-After making a backup, initialize the region where Stickwalker will store its
-game data, then install the firmware. This starts a new walker.
-
-```sh
-uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT erase-region 0x670000 0x180000
-```
-
-Install the bootloader, partition table, boot selection and application:
-
```sh
+uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT --after no-reset erase-region 0x670000 0x180000
uvx --from esptool==5.1.0 esptool --chip esp32s3 --port PORT write-flash 0x0 PwStick.ino.bootloader.bin 0x8000 PwStick.ino.partitions.bin 0xE000 boot_app0.bin 0x10000 PwStick.ino.bin
```
-The selected layout has app0 at `0x10000` (size `0x330000`), app1 at `0x340000`,
-game-data storage at `0x670000` (size `0x180000`), and coredump at `0x7F0000`.
-NVS starts at `0x9000`. On first boot, Stickwalker publishes its initial
-checkpoint in the erased game-data region.
-
-After boot, check the display and controls, open Settings and test sound.
-Confirm the device settings, then connect HGSS to register the new walker.
-Unplug USB, let the screen go dark, and hold M until it wakes, roughly half a
-second. You can now
-[connect to your game and take a Pokémon for a walk](playing.md). The
-[controls guide](controls.md) covers button layouts and device settings.
-
-## Infrared receiver
-
-Stickwalker uses the Stick S3's built-in infrared LED to transmit and a
-**BPV10NF photodiode** on the expansion header to receive. Connect the photodiode
-before trying a Game Card transfer or peer play.
-
-Turn the Stick off and disconnect USB while fitting it:
-
-| BPV10NF lead | Stick S3 header pin |
-| --- | --- |
-| Anode (A) | **GND** |
-| Cathode (C) | **G5** (GPIO5) |
-
-G5 and GND are adjacent on the header. Identify the leads using the
-[BPV10NF package drawing](https://www.vishay.com/docs/81503/bpv10nf.pdf#page=4),
-especially if they have already been trimmed. Connect the bare photodiode
-directly between those two pins. Keep the leads from touching each other or
-neighboring pins. Use a connector or mount that holds both contacts firmly;
-the connection needs to stay steady while you carry the Stick and during transfers.
-
-Point the photodiode's rounded face and the Stick's infrared transmitter toward
-the Game Card's infrared window or the other walker's infrared window. Both
-need a clear line of sight. If a transfer times out, check the two connections
-and polarity, then adjust alignment and separation while keeping the devices
-still.
+[Stick controls](controls.md).
-If transfers work intermittently or report a disrupted connection, turn the
-Stick off, disconnect USB and reseat the photodiode. Once both contacts are
-secure, turn it on and retry the transfer.
+If esptool cannot connect, use M5Stack's [USB download-mode instructions](https://docs.m5stack.com/en/core/StickS3).
diff --git a/stick/docs/motion.md b/stick/docs/motion.md
deleted file mode 100644
index 704ddbc..0000000
--- a/stick/docs/motion.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# How Stickwalker counts steps
-
-Stickwalker runs the Pokéwalker's original step-counting algorithm. The Stick's
-accelerometer measures movement, and the game code decides which movement
-counts as walking and how many steps to award.
-
-The Pokéwalker uses a Bosch BMA150 accelerometer. The Stick S3 uses a
-[Bosch BMI270](https://www.bosch-sensortec.com/en/products/motion-sensors/imus/bmi270),
-a newer sensor designed for wearables. Stickwalker reads all three axes through
-M5Unified and converts them to the signed eight-bit values used by the original
-firmware. One g becomes 64 counts, matching the BMA150's ±2 g output after the
-original firmware takes its high byte. Values beyond that range saturate.
-Between fresh sensor readings, the adapter keeps the previous sample.
-
-The game collects 64 samples at its original cadence, then uses a fixed-point
-Fourier transform to find repeated movement across the three axes. It selects
-a walking frequency and carries fractional steps into the next batch. The
-original pacing logic adds steps to the daily and lifetime totals and awards
-Watts.
-
-Matching the input scale and timing lets the original algorithm work with the
-new sensor, so Stickwalker should count steps much like a Pokéwalker. As with
-the original device, how you carry it affects the motion it measures.
-
-For the sensor conversion, see [`motion_sample.cpp`](../motion_sample.cpp) and
-[`accel_bridge.cpp`](../accel_bridge.cpp). The step algorithm is in
-[`pw_fourier.c`](../../src/application/pw_fourier.c) and
-[`pw_pedometer.c`](../../src/application/pw_pedometer.c).
-[`pw_power.c`](../../src/application/pw_power.c) controls the sampling schedule.
diff --git a/stick/docs/native-platform.md b/stick/docs/native-platform.md
deleted file mode 100644
index 5ef3a9c..0000000
--- a/stick/docs/native-platform.md
+++ /dev/null
@@ -1,142 +0,0 @@
-# Native execution and Stick services
-
-The behavioral reference is the [matching Pokéwalker reconstruction](https://github.com/lumirth/pw/tree/6dc7bc09950078fa3fe0dffa4dae34e9549a99da).
-Both supported H8 compiler suites must continue producing the reference image.
-Stick-specific branches adapt hardware and interaction; they do not replace
-native game rules, rendering, score interpretation, or motion estimation.
-DS-side byte order and completion behavior were also checked against locally
-supplied HGSS source. That source and its artwork are not distributed here.
-
-## Execution ownership
-
-`native_runtime.cpp` owns Timer B1, Timer W, RTC events, Center IRQ, interrupt
-masks, and foreground wake eligibility. Timer W deadlines retain 32768 Hz
-watch-clock precision. The native score interpreter runs from compare events,
-not the physical speaker service or the former fixed 8 ms BeepTick scheduler.
-
-RTC time-of-day advances independently of interrupt delivery and the native
-saved-seconds counter. Pending RTC sources coalesce while masked; flag state is
-separate from interrupt enable. Interrupt effects delivered during foreground
-execution do not themselves request another foreground run. Timer B1 follows
-native motion transitions, not an input queue's desire for faster consumption.
-
-The application task owns native state. Interrupt service occurs at runtime and
-adapter boundaries, including LCD writes; output workers do not call game
-updates or mutate the motion/sound/IR workspace. This is a source-level native
-platform implementation, not instruction-accurate H8 CPU emulation.
-
-`board_runtime.cpp` composes services and chooses the next deadline. Settings
-owns its overlay in `board_settings.cpp`; diagnostic commands live in
-`board_diagnostics.cpp` and compile out of production. Native renderers still
-advance only when the firmware consumes a refresh request.
-
-## Inputs and interaction
-
-Physical acquisition and Settings share debounce state. PMIC L supplies a
-current level and retained press; configuration/read failures are retried.
-The four current layout encodings remain unchanged. Older configuration keys are ignored.
-
-The delivery queue timestamps transitions. Disjoint buttons may omit a redundant
-neutral state, but repeated presses of one button require a delivered release.
-Center IRQ becomes eligible only at the delivery front, so an older direction
-cannot merge with a later confirmation. Pending transitions older than 250 ms
-or a full queue cancel the gesture and require physical release. Diagnostic
-counters expose both cases; native scans are not manufactured to drain a queue.
-
-While dark, M presents native Center activity. Native hold qualification starts
-interactive mode and requests the original social-offer check. Panel and audio
-preparation start earlier without claiming the native score workspace. Wake,
-overlay, and IR transitions consume their physical gesture through release.
-Opening Settings cancels an undelivered Center latch. Closing it resumes sampling
-and visibility without pretending another native Center wake occurred.
-
-Two-key layouts retain dedicated L access to Stick Settings. Three-key layouts
-observe a one-second physical L+R hold alongside normal directional delivery.
-Recognition opens the overlay and consumes its gesture through release; pending
-holds reserve no input. The native Settings selector retains Volume and Contrast.
-Settings captures buttons, but native time, motion and eligible foreground work
-continue; it is not a game pause.
-
-## Output and power
-
-The LCD bus publishes a revision for visible RAM, start-line and power changes.
-The presenter consumes an immutable 96-by-64 snapshot, not `uiFrame` or live game
-state. Each call transfers at most eight native rows, returns with SPI idle, and
-retains no caller pointer. A newer snapshot can supersede remaining strips.
-Appearance, rotation and overlay invalidation also require presentation even
-without a new native LCD revision.
-
-Codec/I2S preparation is independent of Timer W. Output remains prepared during
-interactive use and is suspended for screen-off carry and IR. Readiness means
-configuration succeeded, not that acoustic settling was measured. The existing
-single-voice waveform, codec readback, rail-generation handling and teardown
-protections remain. Digital PCM checks do not establish analog onset or power.
-
-Sleep deadlines include native events, required input polling, Settings
-inactivity, and display/audio/supply recovery. Quiet dark M-only input uses GPIO
-wake; when CPU sleep is unavailable, polling still detects M. Digital GPIO
-ownership is restored after every sleep attempt. Powered I2S is an actual sleep
-constraint, distinct from native score ownership.
-
-## Infrared handoff
-
-Native framing, tokens, checksums, commands and completion actions are retained.
-Optical acquisition retains its dedicated implementation and 240 MHz requirement.
-Audio is quiesced and the prepared Connect image presented before acquisition.
-
-A completed non-self burst hands execution to the protocol with the receiver
-core's interrupts restored. The worker waits while decoding and bounded storage
-work complete. Consumed-event tickets prevent lost or premature release. Startup
-and transmission wait for fresh acquisition progress before emitting a bit;
-that preparation is not tagged as self-echo. The gate producer ends at a complete
-non-self burst. Notification and release waiting run in the worker with
-interrupts restored, outside the gate function; readiness uses its existing
-published sample count. Ignored or rejected input releases
-reception when no native packet remains. Stop wakes a waiting receiver and waits
-for completion and suspension before deleting its task.
-
-Native-requested bank changes can be presented in bounded strips in the
-receive-to-reply window. Acquisition coordination changed and must be physically
-requalified: simulator success is not proof of unchanged optical timing.
-
-## Persistence
-
-The native 64 KiB EEPROM address space, mixed-endian records, mirrors and staging
-regions remain intact. The status path explicitly writes big-endian Watts, as
-required by the DS reader.
-
-The backend has one flash-record format, with two alternating 64 KiB
-checkpoints and a bounded append log in the existing game-data partition.
-Generation and sequence numbers prevent a covered journal from overriding a
-published checkpoint. Native records retain their mixed byte order and mirrors.
-
-A record programs its body and checked header into pre-erased space, reads both
-back independently, and programs its publication word last. Recovery applies only
-published records with valid CRCs and consecutive sequences. An unpublished tail
-is ignored. Published corruption fails closed. Inactive checkpoints are retired
-before replacement; journal erasure follows publication of the covering checkpoint.
-
-Journal capacity is prepared before acquisition. During IR, append operations
-perform only bounded programming and readback; flash erasure is prohibited by the
-storage module. Checkpoint replacement and clearing dirty journal sectors happen
-outside the session. The decoder's exact search pruning retains reply-time
-headroom; see the [IR regression investigation](history/ir-readiness-regression.md).
-
-The filesystem, legacy save readers, migration and old control-layout conversion
-are removed. Unsupported game data is not interpreted or erased automatically.
-Pre-release upgrades require returning the Pokémon to HGSS, backing up, and
-initializing the game-data partition as described in the [install guide](install.md).
-Board settings remain separately stored in NVS.
-
-The DS's Back and present-collection paths do not wait for a final deletion
-acknowledgment. No new handshake or claim of two-device atomicity is introduced.
-Native staging and completion order retain their existing recovery limits.
-
-## Qualification
-
-Run the [production firmware workflows](../simulation/README.md), native-source
-formatting/static analysis, and exact H8 matching under both supported compilers.
-Public EEPROM fixtures exercise normal startup and persisted flash, not game
-memory hooks. Physical controls, acoustics, walking comparisons, current draw,
-flash interruption, and real peer/Game Card exchanges need hardware qualification
-on the exact candidate.
diff --git a/stick/docs/playing.md b/stick/docs/playing.md
deleted file mode 100644
index 10a065e..0000000
--- a/stick/docs/playing.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# Take your Pokémon for a walk
-
-Choose a Pokémon and a route in HeartGold or SoulSilver, transfer them to the
-Stick, and take it with you. Walking earns Watts, which you can spend searching
-for Pokémon and items along the route.
-
-## Connect to your game
-
-Start with [Stickwalker installed and the photodiode connected](install.md).
-Use an infrared-equipped HeartGold or SoulSilver Game Card and a console that
-can run it. Have a Pokémon in a PC Box and at least five free PC Box spaces.
-If a Pokémon from this save is out on another walker, return it first.
-
-1. Start the game and choose **Connect to Pokéwalker** from the main menu, then
- **Go for a Stroll**.
-2. Choose a Pokémon from your PC Boxes and a route.
-3. On the Stick, choose **Connect**. With the default buttons, L moves left,
- R moves right, and M confirms.
-4. Aim the Stick's infrared transmitter and photodiode at the Game Card's
- infrared window. Hold them still until both sides finish.
-
-Your Pokémon will appear on the Stick with the chosen route. The transfer also
-sets its clock and registers it with your game. Nintendo's
-[connection instructions](https://en-americas-support.nintendo.com/app/answers/detail/a_id/7374/p/50/c/120)
-show the game-side prompts.
-
-## Walk and play
-
-Carry the Stick while you walk. It counts steps with the screen off; hold M for
-half a second to wake it, then release the buttons to navigate. Open **Poké
-Radar** to search for Pokémon or **Dowsing Machine** to search for items. Both
-use the same rules and Watts costs as the original Pokéwalker.
-
-The [controls guide](controls.md) covers button layouts, screen rotation,
-appearance and sound settings. Your Pokémon and progress are saved across
-restarts.
-
-## Bring your Pokémon and finds back
-
-Open **Connect to Pokéwalker** in the game again. Choose **Return from a Stroll**
-to bring your walking Pokémon back, or **Receive a gift** to collect your finds
-while leaving that Pokémon on the Stick. Choose **Connect** on the Stick and
-align its transmitter and photodiode with the Game Card again.
-
-Return your Pokémon to the same game it came from. The
-[HeartGold manual](https://csassets.nintendo.com/noaext/image/private/t_KA_PDF/DS_Pokemon_HeartGold)
-explains both transfer options.
-
-## Connect with another walker
-
-With a Pokémon on each walker, choose **Connect** on both and aim their infrared
-transmitters and receivers at each other. Peer play records the encounter and
-awards a gift. A repeat connection on the same day reports that you have already
-connected. You can connect again after the walker's next day rollover.
-
-## Connection trouble
-
-For a timeout, check the [photodiode wiring](install.md#infrared-receiver), then
-adjust alignment and distance. Keep both the Stick's transmitter and its
-photodiode pointed at the other device throughout the transfer.
-
-If an older Stickwalker says "cannot connect again" with a walker it has never
-met, return its Pokémon to HGSS, then
-[update its firmware and initialize game data](install.md#update-a-pre-release-installation).
-Update both Sticks if both run older firmware.
diff --git a/stick/docs/pmic-input.md b/stick/docs/pmic-input.md
deleted file mode 100644
index 5d4f170..0000000
--- a/stick/docs/pmic-input.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Reading the L button
-
-The Stick's L button connects to the M5PM1 power-management chip. Stickwalker
-reads the chip's button status over I2C. M and R connect directly to ESP32 GPIOs.
-
-## Status and click timing
-
-| Register | Field | Use |
-| --- | --- | --- |
-| `BTN_Status`, `0x48` | Bit 0 | Current pressed or released state |
-| `BTN_Status`, `0x48` | Bit 7 | A press occurred; reading the register clears it |
-| `BTN_CFG_1`, `0x49` | Bits 2:1, SINGLE | Classified single-click timing: 125, 250, 500 or 1000 ms |
-
-Stickwalker reads both status bits together. The press latch catches taps that
-end between polls; the current-state bit supports holding L. SINGLE controls
-the PMIC's classified-click behavior, which is separate from these raw reads.
-The documented register default is `0x2A`, selecting 250 ms. See the
-[M5PM1 datasheet, pp. 23–24](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf#page=25)
-and [register definitions](https://github.com/m5stack/M5PM1/blob/be9a5456c007c333e7ac963f33bfde1ffa5d82ee/src/M5PM1.h).
-
-## Polling and release
-
-Awake input polls run every 5 ms. During visible operation, sleep lasts at most
-100 ms, and the native 62.5 ms work deadline usually wakes the CPU sooner. After
-an L gesture, 50 ms of released state rearms it. This prevents one press from
-opening and immediately closing Settings.
-
-With the screen off, M wakes the Stick and L polling stops. After wake, the
-adapter clears any retained L press and waits for button release before
-resuming navigation. See [`input_bridge.cpp`](../input_bridge.cpp) and
-[`power_sleep.cpp`](../power_sleep.cpp).
-
-## Why the port polls
-
-The PMIC's IRQ output connects to GPIO13. Its documented button events are
-classified single-click, double-click and power-on events. Polling `0x48` gives
-the port the pressed state and short-tap latch it needs for navigation and held-L
-Settings access.
-
-The IRQ function also flashes the PMIC's status LED when button reset or
-power-off is disabled, as they are in Stickwalker. The current setup keeps that
-IRQ output disabled. An IRQ-based implementation would need to preserve both
-press/release handling and LED behavior. See the
-[board wiring](https://docs.m5stack.com/en/arduino/m5sticks3/m5pm1),
-[datasheet, pp. 21–27](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf#page=23)
-and [`board_hal.cpp`](../board_hal.cpp).
diff --git a/stick/docs/power.md b/stick/docs/power.md
deleted file mode 100644
index 840dbaa..0000000
--- a/stick/docs/power.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Power management
-
-Stickwalker sleeps the ESP32-S3 between game tasks when running on battery.
-USB power keeps the CPU awake so the serial and debugger connections remain
-available.
-
-## Sampling and buttons
-
-The original game checks for activity at 1 Hz and samples walking at 16 Hz.
-Stickwalker processes each sample as it arrives. Sound and infrared pause motion
-sampling while they use the shared working memory; menus and games continue to
-sample. The [motion scheduling decision](../../docs/adr/0001-preserve-native-motion-ownership.md)
-explains why.
-
-Required awake button polls run every 5 ms. While the game or Settings is visible,
-that acquisition deadline participates in CPU sleep decisions, including L's
-retained PMIC press. Delivery does not change the native sampling clock.
-
-With the screen off, holding M through native Center qualification wakes it. Releasing M early cancels
-panel preparation. After wake, the adapter clears L's retained press and waits
-for all buttons to be released before navigation resumes. See the
-[wake policy](../../docs/adr/0002-m-only-dark-wake.md) and
-[L button reference](pmic-input.md).
-
-## Peripherals
-
-`board_hal.cpp` configures the PMIC. The display, sound and infrared adapters
-request their supplies through `peripheral_power.cpp`, which switches a shared
-supply off after its last user releases it.
-
-`power_sleep.cpp` sets the wake sources and restores M and R to digital GPIO
-after each sleep attempt, including rejected entries. Configuration failures
-keep the CPU awake until a scheduled retry succeeds.
-
-## Measuring power use
-
-Settings estimates charge percentage from battery voltage. To measure battery
-life, record current at the battery connection during stationary carry,
-walking and screen-on use. Include time spent with the screen lit, sound,
-infrared transfers and transient peaks. This measures the whole device,
-including the PMIC, sensors and peripheral supplies.
-
-The [device test records](history/power-and-wake.md) contain sleep and wake
-results. The [release checklist](release.md#device-checks-for-a-stable-release)
-lists the battery tests for a release.
diff --git a/stick/docs/release.md b/stick/docs/release.md
deleted file mode 100644
index 4283605..0000000
--- a/stick/docs/release.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# Preparing a release
-
-A release contains installable firmware, source, runtime dependencies and a test
-record identifying the firmware tested. Use a prerelease for early testing;
-reserve a stable release for an image that completes the device checks below.
-
-## Build and check
-
-From the intended checkout, run:
-
-```sh
-uv sync --locked
-uv run python stick/build_port.py --output-dir stick/.build/candidate --retain-build
-uv run python stick/check.py --use-build --build-dir stick/.build/candidate
-uv run python stick/check_timing.py --elf stick/.build/candidate/PwStick.ino.elf --tool-prefix /path/to/xtensa-esp32s3-elf- --output stick/.build/candidate/ir-timing.json
-```
-
-The [build guide](build.md) covers Arduino setup and pinned versions. The
-`--retain-build` option saves the objects and link command used by the candidate.
-Find `xtensa-esp32s3-elf-` under the Arduino data directory at
-`packages/m5stack/tools/esp-x32/2601/bin/`.
-
-The timing check compares `measure_ir_gate`, `aligned_next` and `run_gate_segment`
-with the production reference. The report records the new ELF hash and whether
-each function's instruction bytes match.
-
-The native-platform redesign changes the receiver handoff and its gate-loop
-machine code. The frozen reference is intentionally unchanged. A differing
-report is a physical requalification requirement, not permission to replace the
-reference with the candidate's hashes. Simulator protocol checks alone cannot
-qualify this candidate for a stable release.
-
-## Assemble the runtime kit
-
-Use the source archives and `sources.json` from the previous release's runtime
-kit, or obtain the pinned archives recorded in
-[`runtime-sources.json`](../runtime-sources.json). Keep their hashes and original
-notices. The ESP-IDF archive contains commit
-`735507283d5b2f9fb363a1901172dbd9e847945d` and the recursive submodule revisions
-recorded in that inventory, with Git metadata excluded.
-
-Pass the installed SDK, compiler and dependency source directory:
-
-```sh
-uv run python stick/package_runtime.py --build-dir stick/.build/candidate --sdk /path/to/packages/m5stack/tools/esp32s3-libs/3.3.9 --toolchain /path/to/packages/m5stack/tools/esp-x32/2601 --sources-dir /path/to/dependency-sources --output-dir stick/.build/runtime
-```
-
-This retains application/library objects, the Arduino core archive, referenced
-SDK archives, linker scripts, build configuration, dependency sources and
-notices. It creates a portable relinking recipe and verifies that it reproduces
-the candidate ELF byte for byte. The [relinking guide](relink.md) explains how
-to use it with a modified Arduino runtime.
-
-## Package the release
-
-Commit the final source and documentation, then run from that clean checkout:
-
-```sh
-uv run python stick/package_release.py --build-dir stick/.build/candidate --runtime-dir stick/.build/runtime --output-dir stick/.build/release --allow-ir-timing-change
-```
-
-Packaging verifies the source inventory, firmware hashes, production mode,
-placeholder artwork, absence of diagnostic markers, infrared timing report and
-runtime kit. It produces three archives and `SHA256SUMS`:
-
-`--allow-ir-timing-change` permits a prerelease with changed IR instruction bytes;
-the original timing report is bundled and the manifest remains unqualified.
-Omit the flag to require the frozen machine-byte reference. Neither mode replaces
-physical qualification for a stable release.
-
-- The firmware ZIP contains the binaries, symbols, installation and player
- guides, manifests, timing result and dependency notices.
-- The source archive contains the committed code and documentation.
-- The runtime archive contains dependency sources and the relinking kit.
-
-The firmware manifest records `hardware_qualified: false`. A separate
-qualification record identifies the commit, app and ELF hashes and the tests
-completed for that image. [Validation history](validation-history.md) provides
-the previous device results.
-
-## Device checks for a stable release
-
-Run these checks on the firmware you intend to publish. Record the setup,
-observations and failures.
-
-1. [Back up and install](install.md) the candidate. Read back the app and compare
- its hash. Return the Pokémon to HGSS before pre-release updates, initialize
- game data, and confirm device settings, new registration and valid EEPROM
- mirrors.
-2. Disconnect USB. Exercise native M-hold wake in every layout and rotation,
- ignored dark L/R, abandoned short M taps, navigation after release, Settings,
- display restoration, short sounds and clock progression.
-3. Repeat retail walk-start and return transfers on that image. Record
- whole-operation successes and failures, game/version, distance, orientation,
- elapsed time and EEPROM readback. Test both HeartGold and SoulSilver.
-4. Compare walking and stationary behavior with actual steps and, where
- available, an original Pokéwalker. Measure whole-device battery current with
- USB disconnected. Record the time spent walking, idle and using the screen.
-5. Interrupt EEPROM journal/image writes on hardware and verify recovery
- without losing the last completed save.
-6. With the [photodiode connected](install.md#infrared-receiver), peer-connect
- with an original Pokéwalker and with another Stickwalker. Check first contact,
- same-day repeat rejection, a different peer and reconnection after day
- rollover. Record each device's firmware and game language.
-
-Use the [debugging guide](debugging.md) for retained diagnostics and USB/JTAG
-procedures.
-
-## Publish on GitHub
-
-Run the Linux and macOS CI jobs for the final commit. Check unpacked archives,
-installation links and checksums, including a relink from the unpacked runtime
-archive. Attach all three archives, checksums and the qualification record.
-
-Write release notes around what changed and how to update. Link the installation
-and controls guides. List the tests completed on this build and the device
-checks still to run. Publish as a prerelease until the stable-release checks pass.
diff --git a/stick/docs/relink.md b/stick/docs/relink.md
index 75e6f9b..d67f4d8 100644
--- a/stick/docs/relink.md
+++ b/stick/docs/relink.md
@@ -1,63 +1,17 @@
-# Relinking the firmware
+# Relink a runtime kit
-Use the runtime kit to rebuild a release ELF or replace the libraries linked
-into it. For ordinary installation, download the firmware ZIP and follow the
-[install guide](install.md).
-
-The kit contains the release's application and library objects, Arduino core
-archive, linked ESP32-S3 SDK archives, linker scripts and dependency source
-archives. `sources.json` records their origins and hashes. Licenses and notices
-are retained under `licenses/` and in the source archives.
-
-## Reproduce the release ELF
-
-Install the **esp-x32 2601** toolchain through M5Stack's **3.3.9** Arduino board
-package. Pass its directory containing `bin/` to the relinker:
+Unpack the runtime archive and run this command from its directory. Use the esp-x32 2601 toolchain supplied by M5Stack's 3.3.9 board package.
```sh
python3 relink.py --toolchain /path/to/packages/m5stack/tools/esp-x32/2601
```
-Run this command from the unpacked kit, using Python 3.12 or later. It writes
-`output/PwStick.ino.elf` and a linker map. Compare the ELF's SHA-256 with
-`runtime-manifest.json`. The release packager verifies that the supplied objects
-reproduce the original ELF byte for byte.
-
-## Use a modified Arduino runtime
-
-Unpack `sources/m5stack-3.3.9.zip`. It contains the board configuration, Arduino
-core, bundled libraries and build recipes used by the release. Modify that
-source in your Arduino package installation, keeping the release's board and
-library versions. Build the accompanying Stickwalker source with
-`stick/build_port.py --retain-build`, following its build guide.
-
-To link the original application objects with your rebuilt core:
+This writes `output/PwStick.ino.elf`. Its SHA-256 should match `runtime-manifest.json`.
-```sh
-python3 relink.py --toolchain /path/to/esp-x32/2601 --core /path/to/new-build/relink-build/core/core.a
-```
-
-For changes to Arduino's bundled FS, LittleFS or Preferences libraries, replace
-the corresponding files in `objects/libraries/` with those from the new build.
-The same approach applies to M5 library objects. `link.json` records the ordered
-link arguments; SDK archives and scripts are under `sdk/`.
+To use a rebuilt Arduino core, add `--core /path/to/core.a`.
-Convert a relinked ELF into an ESP32-S3 flash image with the pinned esptool:
+To create a flash image:
```sh
uvx --from esptool==5.3.0 esptool --chip esp32s3 elf2image --flash-mode dio --flash-freq 80m --flash-size 8MB --elf-sha256-offset 0xb0 -o output/PwStick.ino.bin output/PwStick.ino.elf
```
-
-After changing the runtime, run the [infrared timing check and device tests](release.md)
-on the resulting firmware.
-
-## ESP-IDF sources
-
-The SDK identifies ESP-IDF **v5.5.4**, commit
-`735507283d5b2f9fb363a1901172dbd9e847945d`. Its source archive includes the pinned
-recursive submodules; `sources.json` records each revision. The Arduino library
-builder source and SDK's `versions.txt`, `sdkconfig`, include files and flags are
-also included. The SDK supplies managed components LittleFS **1.22.1** and
-ESP Diagnostics **1.2.1**; Stickwalker's EEPROM backend does not use LittleFS. Their component source archives retain their own
-notices and build files. Espressif's radio libraries are supplied as upstream
-binary archives under their included licenses.
diff --git a/stick/docs/validation-history.md b/stick/docs/validation-history.md
deleted file mode 100644
index 183cb71..0000000
--- a/stick/docs/validation-history.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Device and software validation
-
-These records cover device trials and host tests during development. Each
-release also includes a qualification record listing its firmware hashes and
-completed tests.
-
-| Area | Evidence and results |
-| --- | --- |
-| Game connection | A real HeartGold Game Card completed a walk-start transfer, with 374/374 valid optical bursts and independently checked EEPROM mirrors. A subsequent production connection was reported successful. |
-| Repeated transfers and storage | A source-faithful 3DS peer completed repeated `back`/`put` operations, including a 20/20 series and exact course readback. The [transfer record](history/transfers.md) identifies the fixtures and retained failures. |
-| Sound | Sixteen native scores passed 176 host timing cases. A board trial passed 24/24 acoustic cases across cold starts and warm repeats. See [audio trials](history/audio.md). |
-| Power and wake | Battery-only trials exercised sleep returns, advancing RTC, panel restoration and RTC GPIO handoff. Generated gestures exercised the M-only wake policy. See [power and wake trials](history/power-and-wake.md). |
-| Production installation | The September 27 readback verified the installed application, preserved paired storage and advancing RTC after resume. |
-| Host regressions | Checks cover utilities, controls, wire formats, EEPROM recovery, identity repair, peer encounters and day rollover, RTC events, display commands, decoding and peripheral failures. |
-| October 10 identity repair | Host tests repaired three retained device saves and preserved every byte outside the identity fields and their checksums. Simulated checkpoint failures recovered on reboot; peer lifecycle tests passed for both status roles. |
-
-## Optical failures under repeated testing
-
-One `put` trial decoded page 59's first byte as `AB` where the peer sent `AA`;
-checksum rejection led to timeout. A subsequent series completed 20/20
-operations. A longer diagnostic run completed 44 operations before
-`cycle-023-back` decoded `A7` where the peer sent `A6`. Its gate sentinel encoding
-does not distinguish a missed pulse from a late sample. The
-[transfer record](history/transfers.md#recorded-operations) retains all attempts.
-
-## Reference production image
-
-The saved M-only-wake production application has SHA-256:
-
-```text
-5e29d017a468958a07f74da98e4d60602f1e8a28530a742ee5f1bd81631b6c43
-```
-
-Its [dated verification](history/power-and-wake.md) records that installation.
-The candidate timing check compares the optical sampler with the machine code
-from this image. This establishes continuity of acquisition code across builds.
-
-## Completing device qualification
-
-The [release checklist](release.md#device-checks-for-a-stable-release) calls for
-physical controls, repeated retail transfers with both games, walking
-comparisons, battery measurements and interrupted-write recovery on the exact
-candidate.
-
-The [evidence archive](history/README.md) retains image identities, measurements,
-failed probes and test methods. Raw traces and private flash snapshots are kept
-under ignored `stick/.build/`.
diff --git a/stick/package_release.py b/stick/package_release.py
index 6720b22..89ad176 100644
--- a/stick/package_release.py
+++ b/stick/package_release.py
@@ -161,7 +161,6 @@ def git(*parts):
"git_commit": commit,
"hardware_qualified": False,
"ir_baseline_identical": read_json(args.build_dir / "ir-timing.json")["passed"],
- "qualification": "See RELEASE.md; qualify these exact output hashes.",
"runtime_archive": runtime.name,
"runtime_sha256": digest(runtime),
}
@@ -170,8 +169,6 @@ def git(*parts):
for original, name in (
("LICENSE", "LICENSE"),
("stick/docs/install.md", "INSTALL.md"),
- ("stick/docs/release.md", "RELEASE.md"),
- ("stick/docs/playing.md", "PLAYING.md"),
("stick/docs/controls.md", "CONTROLS.md"),
("docs/third-party.md", "THIRD-PARTY.md"),
):
@@ -186,8 +183,6 @@ def link(match):
rel = source_target.resolve().relative_to(ROOT).as_posix()
bundled = {
"stick/docs/install.md": "INSTALL.md",
- "stick/docs/release.md": "RELEASE.md",
- "stick/docs/playing.md": "PLAYING.md",
"stick/docs/controls.md": "CONTROLS.md",
"docs/third-party.md": "THIRD-PARTY.md",
"LICENSE": "LICENSE",
diff --git a/stick/simulation/README.md b/stick/simulation/README.md
deleted file mode 100644
index 5541ce9..0000000
--- a/stick/simulation/README.md
+++ /dev/null
@@ -1,235 +0,0 @@
-# Firmware E2E checks
-
-Run the production Stickwalker image on a simulated Stick S3, press its buttons,
-and check the screen and infrared output. The ESP32-S3 ROM, bootloader,
-FreeRTOS, SDK drivers and application execute together. Flash writes use the
-firmware's NVS and flash-record code.
-
-## Run the checks
-
-Set up the [Arduino build tools](../docs/build.md), Rust 1.98.1 and Tesseract.
-On macOS, `brew install tesseract` installs the screen reader. On Debian or
-Ubuntu, use `sudo apt-get install tesseract-ocr`.
-
-From the repository root:
-
-```sh
-uv sync --locked
-uv run python stick/check.py
-```
-
-The first run downloads the pinned simulator and Espressif mask ROM, verifies
-them, applies `soc.patch`, builds the simulator and builds production firmware.
-Later runs reuse the simulator checkout and Cargo output in `stick/.build/simulator/`.
-
-To check a retained firmware build:
-
-```sh
-uv run python stick/check.py --use-build --build-dir stick/.build/candidate
-```
-
-The runner checks that its source and binary hashes match the build record.
-`--arduino-cli` and `--config-file` select a separate Arduino installation when
-building through `check.py`.
-
-Repeat `--scenario` to run selected workflows, for example
-`--scenario controls --scenario wake`. Fixtures are generated directly in the
-current flash-record format; no filesystem packer is needed.
-
-## Workflows and expectations
-
-`settings.actions` opens Settings with L, uses M and R to change rotation and
-appearance, closes the menu and restarts the chip. OCR reads the display in the selected physical orientation before and after
-restart. The background brightness also checks the visible appearance. The [controls guide](../docs/controls.md) defines the button
-behavior and persistence expected by this workflow.
-
-`connect.actions` selects Connect while GPIO5 is held low. After the attempt
-finishes, L opens Settings. It closes the menu, holds GPIO5 high and retries
-Connect through M+R. The firmware completes its no-reply sequence and Settings
-opens again. A separate decoder checks the emitted light pulses as 115200-baud
-8N1 SIR, removes the transport XOR and checks the `0xFC` advertisement on both
-attempts. These expectations come from [Dmitry's captured Pokéwalker protocol](https://dmitry.gr/?proj=28.+pokewalker&r=05.Projects). This exercises receiver
-sampling, interrupt masking, watchdog service, session cleanup and reuse through
-the real firmware.
-
-`controls.actions` boots a public, synthetic registered walker. Thirty-millisecond
-contacts navigate Settings, Inventory and Trainer with repeated and alternating
-direction taps. A ten-millisecond overlap must remain two directions rather than
-become Center. A real M+R chord enters native Settings. Opening the board overlay
-while holding R must not change its layout until R is released and pressed again.
-The navigation expectations follow `HomeUpdate`, `MainMenuUpdate` and the documented
-Stick control mapping, not an injected foreground callback.
-
-`layouts.actions` selects every layout, resets the chip and reopens Settings to
-verify persistence. Both three-key mappings deliver physical L+R to gameplay
-before a one-second hold opens the board overlay. The pending-hold capture must
-show the native menu; the overlay's input layout must remain unchanged while
-both keys, then R alone, remain held. A fresh R changes the layout only after
-release. Native Volume and Contrast remain the only selector choices: a second
-Right press stays on Contrast. The workflow changes volume 2 to 0 and contrast
-4 to 5, saves, resets, and checks both checksummed EEPROM mirrors in the
-resulting flash records. Editor cursor positions are checked independently.
-After cycling from three-key back to two-key, a fresh dedicated L press must
-reopen the overlay with the same two-key layout.
-
-`wake.actions` waits for the native 60-second display timeout with USB present.
-R and L cannot wake the sleeping panel. A short M press cancels preparation and
-releases the shared supply; a qualified M hold wakes the screen and must be consumed
-through release. The following two presses navigate normally. This exercises native
-eight-scan Center acceptance, not a diagnostic wake function. It does **not** exercise
-CPU Light-sleep: USB keeps the CPU awake throughout this workflow.
-
-`battery` runs the same contacts with VBUS at zero. It additionally requires a
-retained RTC timer-wake cause, an M-only EXT1 mask while dark, an EXT1 wake for
-the accepted M hold, and both front-button pads restored to digital GPIO. These
-are hardware-register observations from the model, not firmware diagnostic hooks.
-The timer cause is observed at wake rather than an arbitrary later instant: the
-RTC model clears that status when the CPU enters its next sleep.
-
-The controls and wake workflows capture actual I2S DMA output from the simulator.
-Their generated score contains tempo 120, a six-unit note at pitch index 20 and an
-end marker. The matching firmware's compare table gives 77, so its fundamental is
-32768/78 Hz. Native duration arithmetic and final-period muting give 48 periods,
-or 114.26 ms. Frequency is measured between the first and last complete positive
-zero crossing, with two samples of endpoint quantization tolerance. Counting
-crossings over the entire amplitude envelope would bias a short note's estimate
-downward. Duration tolerance conservatively budgets both edges of the configured
-four-by-64-frame DMA queue, one 1 ms service tick and two PCM samples (24.31 ms at
-22050 Hz). M5Unified's stop request is consumed by its mixer; it does not discard
-already queued samples. This is an acceptance tolerance, not a hardware latency
-guarantee. Five navigation notes and the
-first cold-wake note plus a warm repeat must all be present separately. These are
-digital-output checks, not measurements of acoustic onset, loudness or codec analog
-settling.
-
-### Public registered fixture
-
-`fixture.py` generates all 64 KiB from public record layouts and original synthetic
-labels rendered with Pillow's default font. It supplies the `nintendo` signature,
-valid mirrored identity/status/save records, a registered flag, 100 Watts, no
-companion, volume 2 and contrast 4. Its score and simple cursor rasters are generated
-locally. It imports no private saves, Nintendo artwork, DS assets or retail scores.
-
-The runner wraps this image in the current committed checkpoint format and
-loads it into the game-data partition before boot. Later reads, writes, record
-publication and NVS changes go through the real firmware. It never patches game
-RAM or replaces native callbacks. Fixture hashes are retained with the evidence.
-
-Three recovery cases reuse `recovery.actions`, navigate the native menu and reset
-again. `recovery-stale` starts from 200 Watts with an older published journal
-containing 100; the checkpoint must win. `recovery-journal` publishes a valid next
-record changing both save mirrors from 200 to 100; replay must survive reset.
-`recovery-truncated` leaves an incomplete, unpublished append after a 200-Watt
-checkpoint; it must not partially apply. Both native mirrors must retain their
-expected big-endian Watts and checksums. These are current-format recovery states,
-not compatibility fixtures or exhaustive physical power-loss coverage.
-
-`peer.actions` uses a minimal DS-side protocol peer, not a second copy of the
-firmware. It observes GPIO46 SIR, validates checksums and session tokens, sends
-status/read requests through GPIO5, and requires the 100-Watt value as `00 64`.
-`check.py` separately decodes that reply from the transmitted optical waveform.
-`peer-storage.actions` additionally writes one public 128-byte pattern at `0xFF80`,
-reads it back, resets the chip and reads it again in a new session. The final
-committed checkpoint must contain the same pattern. Both workflows finish by
-opening Settings through physical inputs. The peer's ideal charge/discharge
-model drives the compiled photodiode receiver; it does not replace decoding or
-native protocol callbacks. It is a functional status/page exchange, not a complete
-retail DS transfer or an analog interoperability qualification.
-
-`peer-transfer.actions` follows the supplied HGSS put sequence with synthetic
-uncompressed data: status/Watts, 82 course pages, five trainer-record pages,
-PHC/distribution reads, checksum/ping, commit and one-way shutdown. After reset,
-independent flash extraction checks the committed course and trainer identity.
-
-`peer-transfer-compressed.actions` follows the same sequence using independently
-encoded Nintendo LZ10 pages, as HGSS does for starting a stroll. Pages that grow
-are sent raw, and the final partial course/record page uses HGSS's padded
-128-byte form. Native commit and reset must retain the same expected course and
-trainer record.
-
-`peer-play.actions` uses an eligible public Walker fixture and exchanges status,
-animation, name, trainer-house records, peer information and start. Both bulk
-transfer directions are checked. After reset, the native peer-history entry
-and received animation/name must remain in flash. It covers one handshake role;
-it does not yet cover all opening collisions or both native role branches.
-
-All three full exchanges enable local transmitter echo and use 115760 baud, matching
-the recorded retail-card clock. `STICK_E2E_OPCODE_TIMING=1` uses independently
-scheduled CPUs in one-instruction quanta with opcode/branch/call/load-use costs.
-Full exchanges also hold the NOR WIP bit for 300 microseconds per program
-command and 20 milliseconds per sector erase. These are explicit regression
-stress parameters, not a claim about the installed flash chip. An independent
-bus observer requires zero flash erases while a protocol reply is pending.
-The default short workflows retain their original timing mode. This is a
-software timing sensitivity check, not an analog or cache-accurate hardware
-model. The [regression investigation](../docs/history/ir-readiness-regression.md)
-records the before/after evidence and decoder-equivalence check.
-
-Each run creates a fresh directory under `stick/.build/e2e-results/` with screen
-images, OCR text, console output, optical waveforms and flash. `results.json`
-records the completed workflows and hashes of the application, merged image and
-simulator patch. CI retains these files for review, including failed runs.
-
-The suite does not establish retail hardware transfers, exhaustive power-loss
-recovery, walking accuracy, social-offer acceptance, battery life or
-physical optical timing. The stationary fixture has no eligible social offer;
-observing the wake alone does not prove the offer consequence. An exploratory
-battery-only run (`vbus 0`) originally reached an RTC-watchdog reset with cause16
-on both unchanged main and the redesign. Guest stacks established that the pinned
-RTC model left SLEEP_EN set without generating wake/reject status, trapping the SDK
-in `rtc_sleep_start`. The local simulator patch now supplies timer/EXT1 wake,
-sleep rejection and watchdog pause during modeled sleep. This enables the battery
-workflow but does not measure current consumption or establish physical battery
-life.
-
-## Board model and simulator
-
-`board.rs` implements the Stick's I2C1 devices on GPIO47/48, the M5PM1 button
-latch, selectable input voltage, stationary BMI270 samples and the ST7789 display on SPI3. Its visible
-window crops the controller's GRAM to the Stick's 135-by-240 panel. Chip resets
-retain the external devices; their reset commands reset their own state.
-
-The models follow the [M5PM1 manual](https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf),
-[BMI270 datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf)
-and [Stick S3 wiring](https://docs.m5stack.com/en/core/StickS3#pinmap).
-`soc.patch` adds SPI3 using the existing GP-SPI engine, dedicated GPIO matrix
-reads, timer-group watchdog stages and RMT waveform capture to
-[esp32sim](https://github.com/mikroverk/esp32sim). The
-[simulation research](../docs/firmware-simulation-research.md) compares the
-available ESP32 testing tools and describes the additional devices and evidence
-needed for walking, peer exchange and sleep scenarios.
-
-## Scenario commands
-
-Commands run in order. `run` advances both cores and peripherals for the given
-number of milliseconds. Every run stops on an unexpected chip reset or panic.
-
-| Command | Action |
-| --- | --- |
-| `run 100` | Run firmware for 100 ms |
-| `run-until-timer-wake 1000` | Run until an RTC timer wake is observed, failing after the given maximum milliseconds |
-| `press M`, `release M` | Change a physical M, R or L contact |
-| `gpio 5 0` | Hold a GPIO input low or high |
-| `serial d` | Send USB serial bytes; used for bench-image investigation, never by production acceptance workflows |
-| `screen name.png` | Capture the visible display |
-| `optical name.optical` | Save emitted GPIO46 light pulses with durations in CPU cycles |
-| `flash name.bin` | Save the flash image |
-| `vbus 0` | Set the PMIC input-voltage register; 5000 restores simulated USB power |
-| `board name.txt` | Record panel sleep/on state and the shared-supply output latch |
-| `audio-clear` | Clear only the host's captured PCM samples, without changing firmware or I2S registers |
-| `audio name.pcm` | Save mono signed little-endian 16-bit digital PCM and its `.rate` sample-rate file |
-| `peer start` | Enable the ideal optical DS status/Watts peer |
-| `peer write-page 65408` | Also write/read a page containing byte `index XOR 0x5A` at the given address |
-| `peer read-page 65408` | Also read and verify that page without writing it |
-| `peer report name.txt` | Save peer phase, packet trace, observed Watts and any protocol error |
-| `peer put path baud turnaround_us` | Run the HGSS put sequence with a public 10430-byte course |
-| `peer put-compressed path baud turnaround_us` | Run HGSS put with per-page LZ10 compression |
-| `peer walker path` | Run a Walker exchange using a public 64 KiB fixture |
-| `peer echo on` | Illuminate the receiver with live local RMT pulses during transmission |
-| `run-until-peer 15000` | Advance until peer completion/error, bounded by the given milliseconds |
-| `peer stop` | Disconnect the test peer before restarting the board |
-| `reset` | Restart the chip while retaining flash and board devices |
-
-The simulator executable also accepts an optional initial flash offset and image
-after its normal arguments. `check.py` uses this only for the generated flash-record
-fixture, before the ROM boots.
From f7ea741873d6e2ab8f4f9ba66f95f26d3dedaa36 Mon Sep 17 00:00:00 2001
From: lumirth <65358837+lumirth@users.noreply.github.com>
Date: Wed, 14 Oct 2026 02:10:19 -0500
Subject: [PATCH 2/2] Cut release-packager disclaimer copy
---
stick/package_release.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/stick/package_release.py b/stick/package_release.py
index 89ad176..46532da 100644
--- a/stick/package_release.py
+++ b/stick/package_release.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Package a committed production build without saves, retail artwork or diagnostics."""
+"""Package Stickwalker firmware, source and runtime archives."""
from __future__ import annotations
@@ -112,7 +112,7 @@ def validate_build(folder: Path, allow_ir_timing_change: bool = False) -> dict:
if row.get("bytes") != expected["bytes"] or row.get("sha256") != expected["sha256"]:
if not allow_ir_timing_change:
raise InputError(
- f"IR timing changed: {name}; use --allow-ir-timing-change for an unqualified prerelease."
+ f"IR timing changed: {name}; use --allow-ir-timing-change for a prerelease."
)
if not allow_ir_timing_change and not timing.get("passed"):
raise InputError("IR timing verification did not pass.")
@@ -224,7 +224,7 @@ def link(match):
(output / "SHA256SUMS").write_text(
"".join(f"{digest(p)} {p.name}\n" for p in (source, firmware, runtime))
)
- print(f"Candidate prepared at {output}; hardware qualification remains open.")
+ print(f"Release archives: {output}")
def main() -> int: