Repository navigation
Add multi-CPU-variant support (68010/CPU32/68020/68030/68040/68060) - #2
Conversation
m68kdasm previously decoded plain 68000 opcodes only. This adds a CPU selection mechanism (DecodeOptions.CPU, default M68000 preserves prior behavior byte-for-byte) and gates every opcode/addressing-mode pattern by an explicit per-CPU bitset, since the 68k family isn't a strict newer-implies-older chain (CPU32 branches off 68010 with its own extensions; 68040 drops CALLM/RTM that 68020/68030 have). New capability by CPU: - 68010/CPU32: MOVEC, MOVES, RTD, BGND - 68020+: full extension-word addressing (memory indirect, scaled/ suppressed index, 0/16/32-bit base and outer displacements), BFxxx bitfield family, CAS, CHK2/CMP2, 32x32 MULU.L/MULS.L/DIVU.L/DIVS.L, PACK/UNPK, CALLM/RTM, TRAPcc, LINK.L, EXTB.L, CHK.L - 68030/68040/68060 inherit the 68020 additions automatically via the CPU bitset tagging (68040/68060 correctly exclude CALLM/RTM) Deliberately not implemented (documented in docs/design-cpu-variants.md): CAS2, CPU32's TBLS/TBLU family, 68040's MOVE16/CINV/CPUSH, and FPU/PMMU coprocessor instructions — each carries enough encoding uncertainty or scope to warrant its own follow-up rather than a guessed implementation. While auditing the opcode table for this work, also found and fixed several pre-existing plain-68000 gaps unrelated to CPU variants: LINK, UNLK, EXT, CHK, EXG, RESET, RTE, RTR, ILLEGAL, NBCD, MOVEP, and all of ADDQ/SUBQ/Scc/DBcc were missing entirely, and TAS/ADDX/SUBX/EXG were silently mis-decoding as other instructions (e.g. EXG as AND, TAS as TST) due to opcode-space collisions the original table didn't account for. Every canonical 68000 mnemonic now has a decoder. Also reorganized internal/decoders/*.go: split the overloaded types.go into types.go (data model), cpu.go (CPU/cpuSet), and opcodetable.go (the single opcode-dispatch table), folded four session-specific files back into the existing per-instruction-family files, and deduplicated several near-identical decoder functions behind small shared factories. Covered by 65 new/updated tests across the decoder package and the top-level package, including a full-table regression test asserting no two opcode patterns silently collide. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8e89194c30
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| valSUBXMemW = 0x9148 | ||
| valSUBXMemL = 0x9188 | ||
|
|
||
| valBFTST = 0xE0C0 |
There was a problem hiding this comment.
Register bitfield opcodes at E8C0–EFC0
The bitfield opcode family is offset by 0x0800: the design document identifies it as 0xE8C0–0xEFC0, while these constants register 0xE0C0–0xE7C0. Consequently, real 68020+ bitfield instructions fall through to the shift/rotate decoder, while matching memory shift encodings are claimed as BFxxx instructions and incorrectly consume a bitfield extension word, desynchronizing range disassembly.
Useful? React with 👍 / 👎.
| if ext&0x8000 != 0 { | ||
| mnemonic = mnemonicSigned | ||
| } | ||
| dlMeta := registerOperand(RegisterKindData, uint8(ext&0x7)) |
There was a problem hiding this comment.
Decode long MUL/DIV extension fields correctly
The long multiply/divide extension word does not use bit 15 for signedness or its low three bits as the sole 32-bit destination register. For example, the valid MULS.L D0,D2 encoding 4C 00 28 00 is decoded here as MULU.L D0,D0; unsigned operations targeting any register other than D0 are likewise rendered with D0. This shared parser also misdecodes the corresponding DIV.L forms.
Useful? React with 👍 / 👎.
| dcMeta := registerOperand(RegisterKindData, uint8((ext>>3)&0x7)) | ||
| duMeta := registerOperand(RegisterKindData, uint8(ext&0x7)) |
There was a problem hiding this comment.
Extract CAS register operands from their encoded fields
CAS stores its compare and update data-register numbers in different extension-word fields than these shifts use. For the valid CAS.W D1,D2,(A3) bytes 0C D3 00 81, these lines produce CAS.W D0,D1,(A3) instead of preserving D1 and D2, so nearly every nonzero CAS register combination is reported incorrectly.
Useful? React with 👍 / 👎.
| disp := int32(int16(binary.BigEndian.Uint16(data[2:4]))) | ||
| // Matches decodeBxx's existing displacement-base convention for the | ||
| // 16-bit form (offset counted after consuming the displacement word). | ||
| target := uint32(int32(inst.Address) + 4 + disp) |
There was a problem hiding this comment.
Base DBcc targets on the extension-word PC
DBcc's signed displacement is relative to the PC immediately after the opcode word, not after its displacement extension. Thus DBF D0,* (51 C8 FF FE) at $1000 should target $1000, but this calculation returns $1002; the incorrect target also propagates into branch metadata and symbolization for every DBcc instruction.
Useful? React with 👍 / 👎.
Summary
m68kdasmpreviously decoded plain 68000 opcodes only. This PR adds:DecodeOptions.CPU, defaulting toM68000which preserves prior decode output byte-for-byte.CALLM/RTMthat 68020/68030 have.MOVEC,MOVES,RTD,BGND.BFxxxbitfield family,CAS,CHK2/CMP2, 32×32MULU.L/MULS.L/DIVU.L/DIVS.L,PACK/UNPK,CALLM/RTM,TRAPcc,LINK.L,EXTB.L,CHK.L.CALLM/RTM).Deliberately not implemented, documented in
docs/design-cpu-variants.md:CAS2, CPU32'sTBLS/TBLUfamily, 68040'sMOVE16/CINV/CPUSH, and FPU/PMMU coprocessor instructions. Each carries enough encoding uncertainty or scope that a guessed implementation felt worse than a documented gap.Bonus: plain-68000 baseline fixes
Auditing the opcode table for this work turned up several pre-existing gaps unrelated to CPU variants — some silent:
LINK,UNLK,EXT,CHK,EXG,RESET,RTE,RTR,ILLEGAL,NBCD,MOVEP, and all ofADDQ/SUBQ/Scc/DBcc.TASfell through toTST,ADDX/SUBXfell through toADD/SUBwith a garbled operand,EXGfell through toAND.Every canonical 68000 mnemonic now has a decoder.
Reorganization
types.gointotypes.go(data model),cpu.go(CPU/cpuSet), andopcodetable.go(the single opcode-dispatch table).move.go,arithmetic.go,compare.go,bcd.go,multiply_divide.go,single_op.go,special.go).noOperand,singleRegisterOperand).Notes for reviewers
go.sumwas previously out of sync withgo.mod(missing thev1.4.0entry it required) — fixed viago mod tidy.m68kasmtest dependency (PC-relative literal displacements stopped being adjusted for the extension-word base starting in v1.3.2, still present in v1.4.0) while chasing a flaky round-trip test; this repo's own decoder now consistently uses the raw encoded value for PC-relative display (matching the currentm68kasmbehavior), documented inline where relevant.TestFindDecoderMatchesOpcodeTable) that asserts no two opcode patterns in the dispatch table silently collide.🤖 Generated with Claude Code