|
| 1 | +"""Architecture configuration registry for diff-test. |
| 2 | +
|
| 3 | +Defines ArchConfig dataclass and a complete registry of all m68k sub-architecture |
| 4 | +variants, mapping each to its Capstone mode and objdump machine string. |
| 5 | +
|
| 6 | +No capstone imports at module level — cs_arch and cs_mode are stored as plain |
| 7 | +integers so this module can be imported without capstone installed. |
| 8 | +""" |
| 9 | + |
| 10 | +from __future__ import annotations |
| 11 | + |
| 12 | +from dataclasses import dataclass, field |
| 13 | + |
| 14 | +# --------------------------------------------------------------------------- |
| 15 | +# Capstone mode constants (integer literals — no imports needed) |
| 16 | +# --------------------------------------------------------------------------- |
| 17 | +_BE = 1 << 31 # CS_MODE_BIG_ENDIAN = 0x80000000 |
| 18 | +_M000 = 1 << 1 # CS_MODE_M68K_000 |
| 19 | +_M010 = 1 << 2 # CS_MODE_M68K_010 |
| 20 | +_M020 = 1 << 3 # CS_MODE_M68K_020 |
| 21 | +_M030 = 1 << 4 # CS_MODE_M68K_030 |
| 22 | +_M040 = 1 << 5 # CS_MODE_M68K_040 |
| 23 | +_M060 = 1 << 6 # CS_MODE_M68K_060 |
| 24 | +_MCPU32 = 1 << 7 # CS_MODE_M68K_CPU32 |
| 25 | + |
| 26 | +_CS_ARCH_M68K = 8 # CS_ARCH_M68K |
| 27 | + |
| 28 | + |
| 29 | +# --------------------------------------------------------------------------- |
| 30 | +# ArchConfig dataclass |
| 31 | +# --------------------------------------------------------------------------- |
| 32 | +@dataclass(frozen=True) |
| 33 | +class ArchConfig: |
| 34 | + """Configuration for a single architecture variant.""" |
| 35 | + |
| 36 | + name: str |
| 37 | + cs_arch: int |
| 38 | + cs_mode: int |
| 39 | + objdump_machine: str |
| 40 | + objdump_mflags: list[str] = field(default_factory=lambda: ["motorola"]) |
| 41 | + display_name: str = "" |
| 42 | + capstone_note: str = "" |
| 43 | + |
| 44 | + def __post_init__(self) -> None: |
| 45 | + # Default display_name to name if not provided. |
| 46 | + if not self.display_name: |
| 47 | + object.__setattr__(self, "display_name", self.name) |
| 48 | + |
| 49 | + |
| 50 | +# --------------------------------------------------------------------------- |
| 51 | +# Helpers for building the registry |
| 52 | +# --------------------------------------------------------------------------- |
| 53 | +def _cfg( |
| 54 | + name: str, |
| 55 | + cs_mode: int, |
| 56 | + *, |
| 57 | + objdump_machine: str | None = None, |
| 58 | + display_name: str = "", |
| 59 | + capstone_note: str = "", |
| 60 | +) -> ArchConfig: |
| 61 | + """Shorthand factory — all entries share cs_arch=8, mflags=["motorola"].""" |
| 62 | + return ArchConfig( |
| 63 | + name=name, |
| 64 | + cs_arch=_CS_ARCH_M68K, |
| 65 | + cs_mode=cs_mode, |
| 66 | + objdump_machine=objdump_machine if objdump_machine is not None else name, |
| 67 | + display_name=display_name or name, |
| 68 | + capstone_note=capstone_note, |
| 69 | + ) |
| 70 | + |
| 71 | + |
| 72 | +_CF_NOTE_040 = "ColdFire: using CS_MODE_M68K_040 as closest match" |
| 73 | +_CF_NOTE_000 = "ColdFire ISA-A: using CS_MODE_M68K_000 as closest match" |
| 74 | +_CF_NOTE_020 = "ColdFire ISA-A+: using CS_MODE_M68K_020 as closest match" |
| 75 | + |
| 76 | + |
| 77 | +# --------------------------------------------------------------------------- |
| 78 | +# Architecture registry |
| 79 | +# --------------------------------------------------------------------------- |
| 80 | +ARCH_REGISTRY: dict[str, ArchConfig] = {} |
| 81 | + |
| 82 | + |
| 83 | +def _register(*configs: ArchConfig) -> None: |
| 84 | + for c in configs: |
| 85 | + ARCH_REGISTRY[c.name] = c |
| 86 | + |
| 87 | + |
| 88 | +# --- Classic 68k family (exact matches) ----------------------------------- |
| 89 | +_register( |
| 90 | + _cfg("m68k", _BE | _M040, display_name="M68K (default/68040)"), |
| 91 | + _cfg("m68k:68000", _BE | _M000, display_name="M68K 68000"), |
| 92 | + _cfg("m68k:68008", _BE | _M000, display_name="M68K 68008", |
| 93 | + capstone_note="68008 is 68000-class"), |
| 94 | + _cfg("m68k:68010", _BE | _M010, display_name="M68K 68010"), |
| 95 | + _cfg("m68k:68020", _BE | _M020, display_name="M68K 68020"), |
| 96 | + _cfg("m68k:68030", _BE | _M030, display_name="M68K 68030"), |
| 97 | + _cfg("m68k:68040", _BE | _M040, display_name="M68K 68040"), |
| 98 | + _cfg("m68k:68060", _BE | _M060, display_name="M68K 68060"), |
| 99 | + _cfg("m68k:cpu32", _BE | _MCPU32, display_name="M68K CPU32"), |
| 100 | + _cfg("m68k:fido", _BE | _MCPU32, display_name="M68K Fido", |
| 101 | + capstone_note="Fido: using CS_MODE_M68K_CPU32 as closest match"), |
| 102 | +) |
| 103 | + |
| 104 | +# --- ISA-A variants (68000-class ISA A) ----------------------------------- |
| 105 | +_register( |
| 106 | + _cfg("m68k:isa-a:nodiv", _BE | _M000, capstone_note=_CF_NOTE_000), |
| 107 | + _cfg("m68k:isa-a", _BE | _M000, capstone_note=_CF_NOTE_000), |
| 108 | + _cfg("m68k:isa-a:mac", _BE | _M000, capstone_note=_CF_NOTE_000), |
| 109 | + _cfg("m68k:isa-a:emac", _BE | _M000, capstone_note=_CF_NOTE_000), |
| 110 | +) |
| 111 | + |
| 112 | +# --- ISA-A+ variants (some 020 features) ---------------------------------- |
| 113 | +_register( |
| 114 | + _cfg("m68k:isa-aplus", _BE | _M020, capstone_note=_CF_NOTE_020), |
| 115 | + _cfg("m68k:isa-aplus:mac", _BE | _M020, capstone_note=_CF_NOTE_020), |
| 116 | + _cfg("m68k:isa-aplus:emac", _BE | _M020, capstone_note=_CF_NOTE_020), |
| 117 | +) |
| 118 | + |
| 119 | +# --- ISA-B variants (ColdFire ISA B, use 040 as closest) ------------------ |
| 120 | +_register( |
| 121 | + _cfg("m68k:isa-b:nousp", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 122 | + _cfg("m68k:isa-b:nousp:mac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 123 | + _cfg("m68k:isa-b:nousp:emac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 124 | + _cfg("m68k:isa-b", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 125 | + _cfg("m68k:isa-b:mac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 126 | + _cfg("m68k:isa-b:emac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 127 | + _cfg("m68k:isa-b:float", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 128 | + _cfg("m68k:isa-b:float:mac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 129 | + _cfg("m68k:isa-b:float:emac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 130 | +) |
| 131 | + |
| 132 | +# --- ISA-C variants (ColdFire ISA C, use 040 as closest) ------------------ |
| 133 | +_register( |
| 134 | + _cfg("m68k:isa-c", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 135 | + _cfg("m68k:isa-c:mac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 136 | + _cfg("m68k:isa-c:emac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 137 | + _cfg("m68k:isa-c:nodiv", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 138 | + _cfg("m68k:isa-c:nodiv:mac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 139 | + _cfg("m68k:isa-c:nodiv:emac", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 140 | +) |
| 141 | + |
| 142 | +# --- ColdFire numeric variants (all use 040 as closest) ------------------- |
| 143 | +_register( |
| 144 | + _cfg("m68k:5200", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 145 | + _cfg("m68k:5206e", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 146 | + _cfg("m68k:5307", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 147 | + _cfg("m68k:5407", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 148 | + _cfg("m68k:528x", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 149 | + _cfg("m68k:521x", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 150 | + _cfg("m68k:5249", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 151 | + _cfg("m68k:547x", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 152 | + _cfg("m68k:548x", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 153 | + _cfg("m68k:cfv4e", _BE | _M040, capstone_note=_CF_NOTE_040), |
| 154 | +) |
| 155 | + |
| 156 | + |
| 157 | +# --------------------------------------------------------------------------- |
| 158 | +# Default architecture |
| 159 | +# --------------------------------------------------------------------------- |
| 160 | +DEFAULT_ARCH = "m68k" |
| 161 | + |
| 162 | + |
| 163 | +# --------------------------------------------------------------------------- |
| 164 | +# Public API |
| 165 | +# --------------------------------------------------------------------------- |
| 166 | +def get_arch(name: str) -> ArchConfig: |
| 167 | + """Look up an architecture by name. |
| 168 | +
|
| 169 | + Raises ``ValueError`` if *name* is not in the registry. |
| 170 | + """ |
| 171 | + try: |
| 172 | + return ARCH_REGISTRY[name] |
| 173 | + except KeyError: |
| 174 | + raise ValueError( |
| 175 | + f"Unknown architecture: {name!r}. " |
| 176 | + "Use list_archs() to see supported architectures." |
| 177 | + ) from None |
| 178 | + |
| 179 | + |
| 180 | +def list_archs() -> list[str]: |
| 181 | + """Return all supported architecture names, sorted.""" |
| 182 | + return sorted(ARCH_REGISTRY) |
| 183 | + |
| 184 | + |
| 185 | +# --------------------------------------------------------------------------- |
| 186 | +# CLI: print a nice table when run directly |
| 187 | +# --------------------------------------------------------------------------- |
| 188 | +if __name__ == "__main__": |
| 189 | + hdr_name = "Architecture" |
| 190 | + hdr_mode = "cs_mode" |
| 191 | + hdr_mach = "objdump -m" |
| 192 | + hdr_note = "Note" |
| 193 | + |
| 194 | + entries = [ARCH_REGISTRY[k] for k in list_archs()] |
| 195 | + |
| 196 | + w_name = max(len(hdr_name), *(len(e.name) for e in entries)) |
| 197 | + w_mode = max(len(hdr_mode), 12) # "0x80000020" is 10 chars, pad a bit |
| 198 | + w_mach = max(len(hdr_mach), *(len(e.objdump_machine) for e in entries)) |
| 199 | + w_note = max(len(hdr_note), *(len(e.capstone_note) for e in entries)) |
| 200 | + |
| 201 | + fmt = f" {{:<{w_name}}} {{:<{w_mode}}} {{:<{w_mach}}} {{}}" |
| 202 | + sep = fmt.format("-" * w_name, "-" * w_mode, "-" * w_mach, "-" * w_note) |
| 203 | + |
| 204 | + print(f"\nRegistered architectures ({len(entries)}):\n") |
| 205 | + print(fmt.format(hdr_name, hdr_mode, hdr_mach, hdr_note)) |
| 206 | + print(sep) |
| 207 | + for e in entries: |
| 208 | + print(fmt.format(e.name, f"0x{e.cs_mode:08x}", e.objdump_machine, e.capstone_note)) |
| 209 | + print() |
0 commit comments