Skip to content

Replace keystone-engine with a minimal in-house Thumb-2 assembler#385

Open
BrianPugh wants to merge 1 commit into
mainfrom
remove-keystone-engine
Open

Replace keystone-engine with a minimal in-house Thumb-2 assembler#385
BrianPugh wants to merge 1 commit into
mainfrom
remove-keystone-engine

Conversation

@BrianPugh

Copy link
Copy Markdown
Owner

keystone-engine is unmaintained and painful to build (no macOS-arm64 wheel), and the patcher only ever assembles a small, fixed set of Thumb-2 instructions. Replace it with a purpose-built, dependency-free assembler.

  • Add gnw_patch/thumb_asm.py: encodes movw (T3), mov.w/add.w/sub.w (T2/T3 modified immediate, incl. conditional forms), mov (T1), sub sp (T2), ldr.w [pc, #imm] (T2 literal), narrow/wide b (T2/T4), and IT blocks. Includes the ARM ThumbExpandImm encoder. Rejects operands keystone rejects.
  • Wire it into FirmwarePatchMixin.asm(); drop CachedKeystone, the lazy keystone import, the keystone_cache.json hack, compact_json_encoder.py, and the dead InvalidAsmError.
  • Move keystone-engine from the optional [patch] group to dev (Linux/Windows only via a platform marker) where it serves purely as the test oracle.
  • Add tests/test_thumb_asm.py: compares assemble() against keystone-engine exhaustively (every movw immediate, every modified immediate, branch sweeps, IT blocks, register guards) plus the exact instruction strings the Mario/Zelda patches emit. Skips gracefully where keystone is unavailable.

Also covers the assembly forms introduced by PR #381 (ldr.w pc-literal with signed offsets and conditional add.w), verified byte-for-byte against keystone.

keystone-engine is unmaintained and painful to build (no macOS-arm64 wheel),
and the patcher only ever assembles a small, fixed set of Thumb-2 instructions.
Replace it with a purpose-built, dependency-free assembler.

- Add gnw_patch/thumb_asm.py: encodes movw (T3), mov.w/add.w/sub.w (T2/T3
  modified immediate, incl. conditional forms), mov (T1), sub sp (T2),
  ldr.w [pc, #imm] (T2 literal), narrow/wide b (T2/T4), and IT blocks. Includes
  the ARM ThumbExpandImm encoder. Rejects operands keystone rejects.
- Wire it into FirmwarePatchMixin.asm(); drop CachedKeystone, the lazy keystone
  import, the keystone_cache.json hack, compact_json_encoder.py, and the dead
  InvalidAsmError.
- Move keystone-engine from the optional [patch] group to dev (Linux/Windows
  only via a platform marker) where it serves purely as the test oracle.
- Add tests/test_thumb_asm.py: compares assemble() against keystone-engine
  exhaustively (every movw immediate, every modified immediate, branch sweeps,
  IT blocks, register guards) plus the exact instruction strings the Mario/Zelda
  patches emit. Skips gracefully where keystone is unavailable.

Also covers the assembly forms introduced by PR #381 (ldr.w pc-literal with
signed offsets and conditional add.w), verified byte-for-byte against keystone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant