Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ghidra-oki66207

build

A Ghidra processor module for the OKI 66201 / 66207 / 66301 (nX-8/200) 16-bit MCU family used in pre-1996 Honda OBD1 ECUs.

Disassembles raw ECU ROM images, tracks the word/byte (DD) accumulator mode, and carries full instruction semantics, so you get cross-references, a function graph and readable decompiled C with named peripheral registers:

P2 = 0xff;
TCON0 = TCON0 & 0xfb | 4;
ADSCAN = 0;
bVar5 = *pbVar18 & (&DAT_6cd2)[idx];      /* ROM map lookup */

The SLEIGH spec is auto-generated from the authoritative src/66207.op opcode table (the same table that drives the dasm662 disassembler in VIRUXE/asm662), so the two decoders stay in sync — 2606 constructors, full instruction coverage.

Install

# copy the processor module into your Ghidra install
cp -r Ghidra/Processors/OKI66207 "$GHIDRA_INSTALL_DIR/Ghidra/Processors/"

# (re)compile the SLEIGH spec
"$GHIDRA_INSTALL_DIR/support/sleigh" \
  "$GHIDRA_INSTALL_DIR/Ghidra/Processors/OKI66207/data/languages/oki66207.slaspec"
"$GHIDRA_INSTALL_DIR/support/sleigh" \
  "$GHIDRA_INSTALL_DIR/Ghidra/Processors/OKI66207/data/languages/oki66207_p28.slaspec"

Restart Ghidra and import the raw ECU .bin. For the stock P28 ROM, choose OKI:66207:LE:16:p28; use OKI:66207:LE:16:default when the ROM's VCAL ABI is unknown.

Seed disassembly from the reset/interrupt/VCAL vector table at 0x0000 (28 little-endian pointers) with the bundled script — that gets the whole ROM walking:

"$GHIDRA_INSTALL_DIR/support/analyzeHeadless" /tmp/proj ecu \
  -import P28.bin -processor "OKI:66207:LE:16:p28" \
  -scriptPath tools/ghidra_scripts \
  -preScript SeedVectors.java \
  -postScript ExportC.java out/          # optional: dump all functions to C

ExportC.java writes out/all.c (every function decompiled) and out/index.txt.

Regenerating the specs

Both specs are generated from the tables in src/; CI fails if the committed output is stale.

LANG_DIR=Ghidra/Processors/OKI66207/data/languages
python3 tools/gen_sleigh.py src/66207.op     "$LANG_DIR/oki66207.slaspec"
python3 tools/gen_pspec.py  src/66207_regs.h "$LANG_DIR/oki66207.pspec"

Design notes

  • Two address spaces — the OKI has separate program and data memory, both based at 0. The module models this: RAM (the default space) holds the loaded ROM image — code, vectors and ROM tables — while DATA holds the SFRs, RAM and the stacks. Collapsing them into one space is wrong and actively harmful: SFR TM3 at data 0x3c would otherwise land on top of the NMI handler at code 0x3c. LC/LCB/CMPC/CMPCB read program memory; every other access is DATA.
  • Named peripherals — the pspec carries the 91 special-function-register symbols (P0P5, TCON0-3, TM0-3, ADSCAN, IRQ, IE, WDT, PWCON0/1 …) parsed straight out of asm662's 66207_regs.h, plus the reset/interrupt/VCAL vector table, so decompiled output reads TCON0 = TCON0 & 0xfb | 4; rather than bRAM0040 = ....
  • DD context — the word(1)/byte(0) accumulator mode is a tracked SLEIGH context variable, because the same opcode changes length by mode (0x86 is a 3-byte ADD in word mode, a 2-byte ADDB in byte mode). L/word ops set DD=1, LB/byte ops set DD=0. Ghidra cannot propagate a callee's DD change back to its caller, so the p28 variant models the stock P28 ABI guarantee that VCAL 1 returns in word mode. The generic variant makes no VCAL assumption.
  • Postfix opcodes — the disambiguating operation byte can follow the operand (e.g. ADC A,N16[X1] = B0,NL,NH,92); modeled with ;-separated multi-token patterns.
  • Mnemonics — SLEIGH only allows bound operands after the mnemonic, so the fixed register/addressing signature is folded into one token (L_A_DP, MOV_LRB_imm) and only the variable operands (immediates, addresses, offsets, branch targets) are shown separately.
  • Semantics / decompilation — data transfer, arithmetic (ADD/ADC/SUB/SBC), logic, compare, increment/decrement, shifts/rotates (incl. SRA), bit ops (SB/RB/MB), stack (PUSHS/POPS/PUSHU/POPU) and all control flow carry real p-code, so the Ghidra decompiler produces readable C, not just a disassembly. Effective addresses model every mode — current-page ((LRB[12:5]<<8)|N8), zero-page, [DP], N16[Xn], ±N8[USP]. Flag behaviour is taken from the MSM66201 instruction manual (Chapter 3): ZF, CF and the JC condition codes are exact — importantly CF is borrow on subtract/compare (JC LT = C=1, JC GE = C=0), and JRNZ decrements the low byte DPL.
  • Multiply/divide/BCDMUL/MULB ((er1,A)=A×er0), DIV/DIVB ((er0,A)÷er2, remainder in er1), EXTND (sign-extend ALA) and DAA/DAS (BCD adjust, using a computed half-carry HC) all emit p-code, so the decompiler renders them as real * / / / % arithmetic.
  • ROM table referenceLC/LCB/CMPC/CMPCB model every addressing form (direct N16, [reg], [[DP]], [off N8], N16[X1], N16[off N8] …), so Honda's fuel/ignition map lookups decompile as ordinary C array indexing, e.g. (&DAT_6cd2)[idx].
  • Register-indirect bit opsSBR/RBR/TBR/MBR take their bit index from A[2:0] (manual Table 3-32) and are modeled as such.
  • Indirect J/CAL — the .op table tags indirect CAL [X] as a plain operation, so these are decoded specially: one bracket is stripped and the target is the value of the inner operand. That makes indirect calls/jumps produce real references instead of a bogus edge to address 0.
  • Flag accuracy — flags follow the manual's per-instruction tables exactly: shifts/rotates set only CF; INC/DEC set ZF+HC (never CF); CLR/CLRB set ZF=1 only for the accumulator; logic ops set only ZF; MOV/L/ST and the stack ops set none. The system stack pushes store-then-decrement and pop increments-then-reads (Table 3-7), so SSP points at the next free slot.
  • Known limitsSMOVI (opcode 0x04, absent from the manual's instruction classification) decodes but has no p-code; it appears in neither the OEM P28 nor the D16Z6 ROM. rN/erN are modeled as fixed registers (the LRB-banked register file is not tracked), so byte ops on the accumulator can surface extraout_AH artifacts; callee-driven DD changes outside a selected ROM-specific variant cannot be recovered statically. Good enough for reading and reasoning about the code, not a cycle-exact emulator.

Validation

Cross-checked against dasm662 on the OEM P28 ROM: identical decode address-for-address (DD mode + every branch target); whole-ROM auto-analysis yields ~10,858 instructions / ~426 functions / ~699 call + ~2755 jump refs, and the decompiler recovers the boot handler's bit-tests, JRNZ loops and call-stack pushes, and ROM map lookups as C array indexing. See docs/ghidra-validation-p28.md.

Related

  • VIRUXE/asm662asm662/dasm662 assembler & disassembler and the 66207.op opcode table this module is built from
  • VIRUXE/oki66k-transpiler — Oki 66k assembly ↔ C transpiler / lifter

License

The Unlicense.

About

Ghidra processor module for the OKI 66201/66207/66301 (Honda OBD1 ECU) MCU family

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Contributors

Languages