Add pure Jerboa jasm plan

ober

3286d83acf96bd5372c868f7381c45fe413f7bc3

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..571f2de
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+*.so
+*.wpo
+*.wp.so
+*-main.c
+petite_boot.h
+scheme_boot.h
+program_boot.h
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c3decd7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,6 @@
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+Copyright 2026 jasm contributors
+
+Licensed under the Apache License, Version 2.0 with LLVM Exceptions. See
+https://llvm.org/LICENSE.txt for the license text.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ed4a005
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,30 @@
+JERBOA_HOME ?= /Users/user/mine/jerboa
+SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme
+LIBDIRS := $(CURDIR)/lib
+BINARY := jasm
+
+.PHONY: test check run binary test-binary clean
+
+test:
+	$(SCHEME) --libdirs $(LIBDIRS) --script tests/test-jasm.ss
+
+check: test
+
+run:
+	$(SCHEME) --libdirs $(LIBDIRS) --script main.ss --help
+
+binary: test
+	JERBOA_HOME=$(JERBOA_HOME) \
+	SCHEME=$(SCHEME) \
+	BINARY_LIBDIRS=$(LIBDIRS) \
+	$(JERBOA_HOME)/support/build-binary.sh main.ss $(BINARY)
+
+test-binary: binary
+	test -x ./$(BINARY)
+	file ./$(BINARY)
+	./$(BINARY) raw --arch x86-64 --syntax intel --hex '55 48 89 e5 c3'
+	./$(BINARY) raw --arch aarch64 --hex '20 00 80 d2 c0 03 5f d6'
+
+clean:
+	rm -f $(BINARY) $(BINARY).wp.so $(BINARY)-main.c petite_boot.h scheme_boot.h program_boot.h
+	find . \( -name '*.so' -o -name '*.wpo' \) -print0 | xargs -0 rm -f
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..44f8913
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+# jasm
+
+`jasm` is a pure Jerboa binary-to-assembly library and CLI.
+
+The goal is to grow toward `llvm-objdump`-style decoding and output parity
+without linking to LLVM, shelling out to LLVM tools, using FFI, or requiring
+external libraries at runtime.
+
+## Current Scope
+
+- Pure Jerboa decoder core.
+- Seed x86-64 decoder for common prologue/control-flow instructions.
+- Seed AArch64 decoder for common integer/load/store/return instructions.
+- A Jerboa CLI for raw byte disassembly.
+
+Object-file readers and broader ISA tables should be added in Jerboa modules.
+
+## Run
+
+```sh
+make test
+make binary
+./jasm raw --arch x86-64 --syntax intel --hex '55 48 89 e5 c3'
+./jasm raw --arch aarch64 --hex '20 00 80 d2 c0 03 5f d6'
+```
+
+## Library API
+
+```scheme
+(import (jasm disasm))
+
+(disassemble->string 'x86-64 (hex-string->bytevector "55 48 89 e5")
+  '((syntax . intel) (address . 0)))
+```
diff --git a/docs/plan.md b/docs/plan.md
new file mode 100644
index 0000000..1d2486b
--- /dev/null
+++ b/docs/plan.md
@@ -0,0 +1,711 @@
+# jasm Implementation Plan
+
+`jasm` is a pure Jerboa binary-to-assembly library and executable. It must not
+use FFI, dynamic libraries, external disassembler libraries, or shell out to
+installed tools for decoding. LLVM source and documentation may be used only as
+reference material and parity targets.
+
+## Goals
+
+- Produce a real `jasm` binary, not a script.
+- Provide a reusable Jerboa library API for Jerboa's future `disassemble`
+  facility.
+- Decode raw bytevectors into structured instruction records.
+- Format decoded instructions as assembly text.
+- Read object files and disassemble executable sections.
+- Grow toward `llvm-objdump` output and option parity where practical.
+- Keep all decoding tables, parsers, and formatters implemented in Jerboa.
+
+## Non-Goals
+
+- No FFI.
+- No linking to LLVM, binutils, Capstone, or any other external decoder.
+- No runtime dependency on installed tools.
+- No copying GPL implementation code or tables.
+- No guessing instruction bytes from live procedures. Jerboa must provide exact
+  bytes before this library can disassemble procedure bodies.
+
+## Current State
+
+- `./jasm` builds as a native binary.
+- `(jasm disasm)` exposes:
+  - `hex-string->bytevector`
+  - `decode-instruction`
+  - `disassemble-bytevector`
+  - `disassemble->string`
+- Seed raw-byte support exists for:
+  - `x86-64`: common prologue/control-flow instructions.
+  - `aarch64`: common move, add/sub, load/store, return instructions.
+- MIPS and MIPS64 are planned as first-class architecture targets, including
+  both raw-byte decoding and object-file architecture mapping.
+- Tests cover the initial decoder slice.
+
+## Core Architecture
+
+### Instruction Model
+
+Define a stable instruction record with fields for:
+
+- architecture
+- address
+- size
+- raw bytes
+- mnemonic
+- operands
+- formatted text
+- instruction class
+- branch target, when known
+- memory reference metadata, when known
+- validity status: `valid`, `unknown`, `reserved`, `unallocated`, `incomplete`
+- annotations/comments
+
+The current alist shape is acceptable for the seed version, but should become a
+proper record once field usage stabilizes.
+
+### Decode Pipeline
+
+The pipeline should be:
+
+1. Input source: bytevector or object-file section.
+2. Architecture selection.
+3. Decoder dispatch by architecture.
+4. Instruction record output.
+5. Formatter output.
+6. CLI/object-file presentation.
+
+Decoder functions should return structured data first. Text formatting should be
+separate so Jerboa callers can consume instructions without reparsing strings.
+
+### Architecture Registry
+
+Add a registry module:
+
+- `(jasm arch registry)`
+- `register-architecture!`
+- `lookup-architecture`
+- `supported-architectures`
+- architecture aliases, for example `x86_64 -> x86-64`, `arm64 -> aarch64`
+- default syntax and feature profiles per architecture
+
+Each architecture should provide:
+
+- decoder entry
+- formatter defaults
+- valid feature flags
+- endianness behavior
+- minimum and maximum instruction length
+- object-file architecture identifiers
+
+## Raw Byte Disassembly
+
+Raw mode is the Jerboa `disassemble` integration point.
+
+Required API:
+
+```scheme
+(disassemble-bytevector arch bytes options)
+(disassemble->string arch bytes options)
+(decode-instruction arch bytes offset address options)
+```
+
+Options should include:
+
+- `address`
+- `syntax`
+- `features`
+- `cpu`
+- `max-bytes`
+- `stop-address`
+- `show-bytes?`
+- `show-addresses?`
+- `unknown-policy`: emit `.byte`, error, or skip
+- `symbol-resolver`
+- `relocation-resolver`
+
+## CLI Surface
+
+The binary should eventually support:
+
+```sh
+jasm raw --arch x86-64 --syntax intel --hex '55 48 89 e5'
+jasm raw --arch aarch64 --file code.bin --address 0x1000
+jasm -d file.o
+jasm -D file.o
+jasm --headers file.o
+jasm --section=.text file.o
+jasm --syms file.o
+jasm --reloc file.o
+```
+
+Initial commands:
+
+- `raw`
+- `object`
+- `headers`
+- `sections`
+- `symbols`
+- `relocs`
+
+Common options:
+
+- `--arch`
+- `--triple`
+- `--syntax`
+- `--cpu`
+- `--features`
+- `--address`
+- `--start-address`
+- `--stop-address`
+- `--section`
+- `--show-bytes`
+- `--no-show-bytes`
+- `--show-addresses`
+- `--no-addresses`
+- `--print-imm-hex`
+- `--demangle`, later
+- `--wide`
+- `--json`, for tooling
+
+## Object File Support
+
+### Object Model
+
+Introduce `(jasm object)` with records for:
+
+- object file
+- architecture
+- format
+- endian
+- sections
+- symbols
+- relocations
+- segments/program headers
+- entry point
+- source filename
+
+### ELF
+
+Implement ELF first because it is regular and common.
+
+Required:
+
+- ELF32 and ELF64 headers.
+- Little-endian first, then big-endian.
+- Section header table.
+- Program header table.
+- String tables.
+- Symbol tables: `.symtab`, `.dynsym`.
+- Relocations:
+  - `SHT_REL`
+  - `SHT_RELA`
+- Executable section discovery.
+- Section filtering.
+- Address range filtering.
+
+Architectures to map early:
+
+- x86-64
+- i386
+- AArch64
+- ARM
+- MIPS 32/64, big-endian and little-endian
+- RISC-V 32/64
+- PowerPC 32/64
+- WebAssembly, if using ELF containers where relevant
+
+### Mach-O
+
+Implement after ELF.
+
+Required:
+
+- 64-bit Mach-O first.
+- Universal/fat binaries.
+- Load commands.
+- Segments and sections.
+- Symbol table.
+- String table.
+- Dysymtab.
+- Relocations.
+- ARM64 and x86-64 architecture mapping.
+
+### COFF/PE
+
+Implement after ELF/Mach-O.
+
+Required:
+
+- COFF file header.
+- Optional header.
+- Section table.
+- Symbol table.
+- String table.
+- Relocations.
+- PE image sections.
+
+### Archives
+
+Support Unix archives:
+
+- global header
+- member headers
+- long filenames
+- symbol table members
+- nested object iteration
+
+## x86/x86-64 Decoder Plan
+
+### Scope
+
+Grow from seed instructions to a table-driven decoder. Avoid a giant hand-coded
+`cond` for the full ISA.
+
+### Required Decode Layers
+
+- Prefix parsing:
+  - legacy prefixes
+  - segment overrides
+  - operand-size override
+  - address-size override
+  - REP/REPE/REPNE
+  - LOCK
+- REX prefix.
+- VEX prefix.
+- EVEX prefix.
+- XOP prefix, if needed.
+- Opcode maps:
+  - one-byte map
+  - `0F`
+  - `0F 38`
+  - `0F 3A`
+  - VEX maps
+  - EVEX maps
+- ModR/M.
+- SIB.
+- displacement decoding.
+- immediate decoding.
+- relative branch target decoding.
+- operand-size resolution.
+- address-size resolution.
+
+### x86 Output Syntax
+
+Support:
+
+- AT&T syntax.
+- Intel syntax.
+- register naming.
+- immediate formatting.
+- memory operand formatting.
+- branch target formatting.
+
+### x86 Milestones
+
+1. Integer baseline:
+   - `mov`, `lea`, `push`, `pop`
+   - `add`, `sub`, `cmp`, `test`
+   - `and`, `or`, `xor`
+   - `inc`, `dec`, `neg`, `not`
+   - `call`, `ret`, `jmp`, `jcc`
+   - `nop`, `int3`, `syscall`
+2. Common addressing:
+   - ModR/M all modes.
+   - SIB all modes.
+   - RIP-relative addressing.
+3. Stack/frame patterns.
+4. SSE baseline.
+5. AVX/VEX.
+6. AVX-512/EVEX.
+7. privileged/system instructions.
+
+## AArch64 Decoder Plan
+
+### Required Decode Groups
+
+- Data processing immediate.
+- Data processing register.
+- Loads/stores.
+- Branches.
+- Exception generation.
+- System instructions.
+- Floating point and SIMD.
+- Atomics.
+- SVE/SME later.
+
+### AArch64 Milestones
+
+1. Baseline scalar:
+   - `mov`, `movz`, `movn`, `movk`
+   - `add`, `sub`, `cmp`, `cmn`
+   - logical immediates
+   - `ldr`, `str`, `ldp`, `stp`
+   - `b`, `bl`, `br`, `blr`, `ret`
+   - `cbz`, `cbnz`, `tbz`, `tbnz`
+2. PC-relative:
+   - `adr`
+   - `adrp`
+   - literal loads
+3. Bitfield/extract aliases.
+4. SIMD/floating point baseline.
+5. atomics.
+6. SVE and SME.
+
+## ARM/Thumb Plan
+
+Add after AArch64 baseline is stable.
+
+Required:
+
+- ARM state decoder.
+- Thumb and Thumb-2 decoder.
+- IT blocks.
+- condition codes.
+- ARM register aliases.
+- mixed ARM/Thumb object-file mapping symbols.
+
+## RISC-V Plan
+
+RISC-V is a good third architecture because its encoding is regular.
+
+Required:
+
+- RV32 and RV64.
+- Base integer ISA.
+- compressed `C` extension.
+- `M`, `A`, `F`, `D`.
+- vector extension later.
+- feature-driven decode by extension set.
+
+## MIPS and MIPS64 Plan
+
+MIPS and MIPS64 should be first-class pure Jerboa decoders. They need explicit
+endianness handling because MIPS object files and raw byte streams appear in
+both big-endian and little-endian forms.
+
+### Architecture Names
+
+Support aliases:
+
+- `mips`
+- `mipsel`
+- `mips64`
+- `mips64el`
+- `mips32`
+- `mips32el`
+
+Canonical architecture names:
+
+- `mips`
+- `mipsel`
+- `mips64`
+- `mips64el`
+
+### Required Decode Layers
+
+- 32-bit fixed-width instruction fetch.
+- big-endian and little-endian word readers.
+- opcode field dispatch.
+- R-type function dispatch.
+- REGIMM dispatch.
+- coprocessor dispatch.
+- SPECIAL2 and SPECIAL3 dispatch.
+- immediate sign-extension.
+- branch target calculation.
+- jump target calculation.
+- load/store offset formatting.
+- register-name profiles:
+  - numeric: `$0` ...
+  - ABI: `$zero`, `$at`, `$v0`, `$a0`, `$t0`, `$s0`, etc.
+- MIPS64-specific GPR operations.
+- delay-slot awareness metadata.
+
+### MIPS32 Baseline
+
+Initial decoder coverage:
+
+- `nop`
+- `addu`, `subu`
+- `addiu`
+- `and`, `or`, `xor`, `nor`
+- `andi`, `ori`, `xori`, `lui`
+- `sll`, `srl`, `sra`
+- `slt`, `sltu`
+- `lw`, `sw`, `lb`, `lbu`, `lh`, `lhu`, `sb`, `sh`
+- `beq`, `bne`, `blez`, `bgtz`
+- `j`, `jal`, `jr`, `jalr`
+- `syscall`, `break`
+
+### MIPS64 Baseline
+
+MIPS64 builds on MIPS32 and adds:
+
+- 64-bit register operation formatting.
+- `daddu`, `dsubu`
+- `daddiu`
+- `ld`, `sd`
+- `dsll`, `dsrl`, `dsra`
+- `dsll32`, `dsrl32`, `dsra32`
+- 64-bit load/store and branch output tests.
+
+### MIPS Object Integration
+
+ELF support must map:
+
+- `EM_MIPS`
+- ELF class 32/64
+- data encoding big/little endian
+- ABI flags where available
+- `.MIPS.abiflags`
+- MIPS relocation records
+- microMIPS/MIPS16 markers later
+
+### MIPS Milestones
+
+1. Raw big-endian MIPS32 seed decoder.
+2. Raw little-endian MIPS32 seed decoder.
+3. Raw big-endian MIPS64 seed decoder.
+4. Raw little-endian MIPS64 seed decoder.
+5. ABI register-name formatter.
+6. ELF `EM_MIPS` detection and `.text` disassembly.
+7. relocation and symbol annotations.
+8. microMIPS and MIPS16 investigation.
+
+## Other Architecture Roadmap
+
+Add architecture modules in this rough order:
+
+1. x86-64
+2. AArch64
+3. MIPS/MIPS64
+4. RISC-V
+5. ARM/Thumb
+6. WebAssembly
+7. PowerPC
+8. SystemZ
+9. BPF
+10. LoongArch
+11. SPARC
+12. MSP430/AVR and other embedded targets
+
+Each addition needs:
+
+- architecture alias registration
+- raw decode tests
+- object-file mapping
+- formatter tests
+- parity samples against LLVM output where licensing permits comparison by
+  generated output, not copied source.
+
+## Formatting
+
+Create `(jasm format)`:
+
+- instruction line formatting
+- address column
+- bytes column
+- labels/symbols
+- reloc annotations
+- comments
+- architecture-specific syntax hooks
+- JSON output
+
+Formatter options:
+
+- `show-addresses?`
+- `show-bytes?`
+- `bytes-width`
+- `symbolic?`
+- `print-imm-hex?`
+- `syntax`
+- `color?`
+
+## Symbol and Relocation Handling
+
+A disassembler without symbols is useful for raw procedure bytes, but object
+output needs symbolic context.
+
+Required:
+
+- symbol lookup by address.
+- nearest symbol before address.
+- local/global symbol visibility.
+- function boundary detection.
+- relocation lookup by section offset.
+- inline relocation comments.
+- branch target labels.
+
+## Jerboa Integration
+
+Jerboa's future `(std debug disassemble)` should call this library once it has
+exact code bytes.
+
+Required exported API shape:
+
+```scheme
+(procedure-bytes->disassembly arch bytes metadata options)
+```
+
+Expected metadata:
+
+- procedure name
+- source location
+- machine type
+- entry address
+- exact byte length
+- relocation information, if available
+
+`jasm` should not read live process memory. Jerboa owns byte extraction.
+
+## Testing Strategy
+
+### Unit Tests
+
+- one instruction per opcode pattern.
+- invalid/truncated instruction cases.
+- immediate sign-extension cases.
+- branch target calculation.
+- memory operand formatting.
+- syntax variants.
+
+### Golden Tests
+
+Maintain fixture files:
+
+- raw byte fixtures
+- expected instruction records
+- expected formatted output
+
+### Parity Tests
+
+Where an LLVM tool is available on the developer machine, use it only as an
+optional test oracle:
+
+- generate comparison output
+- normalize addresses/spacing
+- compare text
+
+These tests must be optional and not required for normal `make test`.
+
+### Object Fixtures
+
+Add small checked-in binary fixtures only when licensing/provenance is clear.
+Prefer generating fixtures from Jerboa or tiny assembly sources during tests.
+
+## Performance Plan
+
+- Keep byte access allocation-free in inner decode loops.
+- Decode to records first; format only when requested.
+- Use vectors for opcode tables.
+- Avoid string building during decode except for early seed implementation.
+- Add benchmarks for large `.text` sections.
+
+## Error Handling
+
+Unknown bytes should not crash disassembly.
+
+Policies:
+
+- raw mode default: emit `.byte`.
+- strict mode: raise decode error.
+- object mode default: emit `.byte` or `.word` and continue.
+- truncated input: emit incomplete record and stop or strict error.
+
+## Repository Layout
+
+Planned layout:
+
+```text
+main.ss
+lib/jasm/disasm.ss
+lib/jasm/arch/registry.ss
+lib/jasm/arch/x86.ss
+lib/jasm/arch/aarch64.ss
+lib/jasm/object/elf.ss
+lib/jasm/object/macho.ss
+lib/jasm/object/coff.ss
+lib/jasm/format.ss
+tests/test-jasm.ss
+tests/fixtures/
+docs/plan.md
+```
+
+## Milestones
+
+### Milestone 1: Correct Project Shape
+
+- Real `jasm` binary.
+- Pure Jerboa raw-byte disassembly.
+- Seed x86-64 and AArch64 support.
+- Basic tests.
+
+Status: started.
+
+### Milestone 2: Decoder Records and Formatting Split
+
+- Replace text-first instruction records with structured records.
+- Add formatter module.
+- Keep current string API as compatibility wrapper.
+
+### Milestone 3: x86-64 Baseline Decoder
+
+- Prefix parser.
+- ModR/M and SIB parser.
+- Integer baseline.
+- AT&T and Intel syntax.
+- Common compiler output decodes cleanly.
+
+### Milestone 4: AArch64 Baseline Decoder
+
+- Table/group decoder.
+- Scalar baseline.
+- branch/load/store coverage.
+- common compiler output decodes cleanly.
+
+### Milestone 5: ELF Reader
+
+- ELF64 little-endian first.
+- section table and symbols.
+- disassemble `.text`.
+- `jasm -d file.o`.
+
+### Milestone 6: Mach-O Reader
+
+- Mach-O 64-bit.
+- arm64 and x86-64 objects.
+- universal binaries.
+
+### Milestone 7: RISC-V Baseline
+
+- RV32/RV64 base integer.
+- compressed extension.
+- ELF integration.
+
+### Milestone 8: MIPS/MIPS64 Baseline
+
+- MIPS32 and MIPS64 canonical architecture aliases.
+- big-endian and little-endian raw-byte decoders.
+- baseline integer/load/store/branch/jump coverage.
+- ABI register-name output.
+- ELF `EM_MIPS` integration.
+
+### Milestone 9: CLI Parity Pass
+
+- `llvm-objdump`-like option names.
+- headers/sections/symbols/relocations.
+- address ranges and section filters.
+
+### Milestone 10: Jerboa Disassemble Integration
+
+- stable bytevector API.
+- metadata-aware report formatting.
+- tests with synthetic procedure byte fixtures.
+
+## Immediate Next Steps
+
+1. Split current `(jasm disasm)` into architecture modules.
+2. Replace instruction alists with records.
+3. Add x86 prefix, ModR/M, and SIB parsers.
+4. Add AArch64 decode-group dispatch.
+5. Add MIPS/MIPS64 raw decoders for both endian modes.
+6. Add an ELF64 little-endian reader skeleton.
+7. Expand tests before adding more opcodes.
diff --git a/jasm b/jasm
new file mode 100755
index 0000000..b1a7f14
Binary files /dev/null and b/jasm differ
diff --git a/lib/jasm/disasm.ss b/lib/jasm/disasm.ss
new file mode 100644
index 0000000..3281936
--- /dev/null
+++ b/lib/jasm/disasm.ss
@@ -0,0 +1,368 @@
+#!chezscheme
+
+(library (jasm disasm)
+  (export
+    supported-architectures
+    hex-string->bytevector
+    disassemble-bytevector
+    disassemble->string
+    decode-instruction
+    instruction?
+    instruction-arch
+    instruction-address
+    instruction-size
+    instruction-text
+    instruction-bytes)
+
+  (import (chezscheme))
+
+  (define supported-architectures '(x86-64 aarch64))
+
+  (define (instruction? x)
+    (and (pair? x) (eq? (car x) 'instruction)))
+
+  (define (make-instruction arch address size text bytes)
+    (list 'instruction
+          (cons 'arch arch)
+          (cons 'address address)
+          (cons 'size size)
+          (cons 'text text)
+          (cons 'bytes bytes)))
+
+  (define (instruction-ref insn key)
+    (let ([cell (assq key (cdr insn))])
+      (if cell (cdr cell) #f)))
+
+  (define (instruction-arch insn) (instruction-ref insn 'arch))
+  (define (instruction-address insn) (instruction-ref insn 'address))
+  (define (instruction-size insn) (instruction-ref insn 'size))
+  (define (instruction-text insn) (instruction-ref insn 'text))
+  (define (instruction-bytes insn) (instruction-ref insn 'bytes))
+
+  (define (option-ref options key default)
+    (let ([cell (assq key options)])
+      (if cell (cdr cell) default)))
+
+  (define (hex-digit c)
+    (cond
+      [(and (char>=? c #\0) (char<=? c #\9))
+       (- (char->integer c) (char->integer #\0))]
+      [(and (char>=? c #\a) (char<=? c #\f))
+       (+ 10 (- (char->integer c) (char->integer #\a)))]
+      [(and (char>=? c #\A) (char<=? c #\F))
+       (+ 10 (- (char->integer c) (char->integer #\A)))]
+      [else #f]))
+
+  (define (hex-skip? c)
+    (or (char-whitespace? c)
+        (char=? c #\,)
+        (char=? c #\:)
+        (char=? c #\_)))
+
+  (define (clean-hex-string s)
+    (let ([n (string-length s)])
+      (let loop ([i 0] [out '()])
+        (cond
+          [(= i n) (list->string (reverse out))]
+          [(and (< (+ i 1) n)
+                (char=? (string-ref s i) #\0)
+                (or (char=? (string-ref s (+ i 1)) #\x)
+                    (char=? (string-ref s (+ i 1)) #\X)))
+           (loop (+ i 2) out)]
+          [(hex-skip? (string-ref s i))
+           (loop (+ i 1) out)]
+          [else
+           (loop (+ i 1) (cons (string-ref s i) out))]))))
+
+  (define (hex-string->bytevector s)
+    (let* ([clean (clean-hex-string s)]
+           [n (string-length clean)])
+      (when (or (= n 0) (not (= (modulo n 2) 0)))
+        (error 'hex-string->bytevector "hex string must contain an even number of digits" s))
+      (let ([bv (make-bytevector (quotient n 2) 0)])
+        (let loop ([i 0] [j 0])
+          (if (= i n)
+              bv
+              (let ([hi (hex-digit (string-ref clean i))]
+                    [lo (hex-digit (string-ref clean (+ i 1)))])
+                (unless (and hi lo)
+                  (error 'hex-string->bytevector "invalid hex digit" s))
+                (bytevector-u8-set! bv j (+ (* hi 16) lo))
+                (loop (+ i 2) (+ j 1))))))))
+
+  (define (bv-slice bv start len)
+    (let ([out (make-bytevector len 0)])
+      (bytevector-copy! bv start out 0 len)
+      out))
+
+  (define (byte->hex2 b)
+    (let ([digits "0123456789abcdef"])
+      (string (string-ref digits (quotient b 16))
+              (string-ref digits (modulo b 16)))))
+
+  (define (ascii-downcase c)
+    (if (and (char>=? c #\A) (char<=? c #\Z))
+        (integer->char (+ (char->integer #\a)
+                          (- (char->integer c) (char->integer #\A))))
+        c))
+
+  (define (number->hex n)
+    (list->string (map ascii-downcase
+                       (string->list (number->string n 16)))))
+
+  (define (hex-number n)
+    (string-append "0x" (number->hex n)))
+
+  (define (signed8 b)
+    (if (>= b 128) (- b 256) b))
+
+  (define (signed32/u32 x)
+    (if (>= x #x80000000) (- x #x100000000) x))
+
+  (define (bv-u8 bv offset)
+    (bytevector-u8-ref bv offset))
+
+  (define (bv-u32-le bv offset)
+    (+ (bv-u8 bv offset)
+       (* (bv-u8 bv (+ offset 1)) #x100)
+       (* (bv-u8 bv (+ offset 2)) #x10000)
+       (* (bv-u8 bv (+ offset 3)) #x1000000)))
+
+  (define (bits n hi lo)
+    (modulo (quotient n (expt 2 lo)) (expt 2 (+ 1 (- hi lo)))))
+
+  (define x86-reg64
+    '#("rax" "rcx" "rdx" "rbx" "rsp" "rbp" "rsi" "rdi"
+       "r8" "r9" "r10" "r11" "r12" "r13" "r14" "r15"))
+
+  (define x86-reg32
+    '#("eax" "ecx" "edx" "ebx" "esp" "ebp" "esi" "edi"
+       "r8d" "r9d" "r10d" "r11d" "r12d" "r13d" "r14d" "r15d"))
+
+  (define (x86-reg n bits)
+    (vector-ref (if (= bits 64) x86-reg64 x86-reg32) n))
+
+  (define (x86-att-reg name)
+    (string-append "%" name))
+
+  ;; Chez rejects accidental comma identifiers less clearly than a reader
+  ;; error here would. Keep this tiny helper separate so formatting is obvious.
+  (define (x86-intel-binop mnemonic dst src)
+    (string-append mnemonic "\t" dst ", " src))
+
+  (define (x86-att-binop mnemonic suffix dst src)
+    (string-append mnemonic suffix "\t" (x86-att-reg src) ", " (x86-att-reg dst)))
+
+  (define (x86-format-binop mnemonic suffix syntax dst src)
+    (if (eq? syntax 'att)
+        (x86-att-binop mnemonic suffix dst src)
+        (x86-intel-binop mnemonic dst src)))
+
+  (define (x86-format-immop mnemonic suffix syntax dst imm)
+    (let ([imm-text (if (>= imm 10) (hex-number imm) (number->string imm))])
+      (if (eq? syntax 'att)
+          (string-append mnemonic suffix "\t$" imm-text ", " (x86-att-reg dst))
+          (string-append mnemonic "\t" dst ", " imm-text))))
+
+  (define (x86-unknown bv offset address)
+    (make-instruction 'x86-64 address 1
+      (string-append ".byte\t0x" (byte->hex2 (bv-u8 bv offset)))
+      (bv-slice bv offset 1)))
+
+  (define (decode-x86-64 bv offset address options)
+    (let* ([len (bytevector-length bv)]
+           [syntax (option-ref options 'syntax 'att)])
+      (if (>= offset len)
+          #f
+          (let* ([b0 (bv-u8 bv offset)]
+                 [has-rex? (and (>= b0 #x40) (<= b0 #x4f))]
+                 [rex (if has-rex? b0 0)]
+                 [rex-w? (not (= (modulo (quotient rex 8) 2) 0))]
+                 [rex-r (if (= (modulo (quotient rex 4) 2) 0) 0 8)]
+                 [rex-b (if (= (modulo rex 2) 0) 0 8)]
+                 [opoff (if has-rex? (+ offset 1) offset)])
+            (if (>= opoff len)
+                (x86-unknown bv offset address)
+                (let ([op (bv-u8 bv opoff)])
+                  (cond
+                    [(and (>= op #x50) (<= op #x57))
+                     (let* ([reg (x86-reg (+ (- op #x50) rex-b) 64)]
+                            [text (if (eq? syntax 'att)
+                                      (string-append "pushq\t" (x86-att-reg reg))
+                                      (string-append "push\t" reg))]
+                            [size (+ 1 (if has-rex? 1 0))])
+                       (make-instruction 'x86-64 address size text
+                         (bv-slice bv offset size)))]
+                    [(= op #xc3)
+                     (let ([size (+ 1 (if has-rex? 1 0))])