mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-02 18:23:35 +00:00
The arm64 pass turned up the same class of bug elsewhere: mnemonics named
after an encoding rather than after what an assembler accepts, and forms
that no caller can reach because the thing that tells them apart is not
checked.
mips
* The printer mapped every `_` to `.`, but MSA spells the sign qualifier
with an underscore and only the element size with a dot: `adds_s.b`,
`max_s.h`, `copy_u.w`. `adds.s.b` is rejected by an assembler. 91
mnemonics were printing text that would not reassemble. The name alone
cannot decide it -- MSA's ADDS_S_D and the FP convert CVT_S_D have the
same shape and want opposite treatment -- so the family is read off the
form's feature.
* `encode` now takes `features: Feature_Set = FEATURES_ALL` and skips
forms outside it, mirroring `decode`, which has had that parameter all
along. That asymmetry was the reason 12 mnemonics carried an ISA-variant
suffix: with no way to say which MIPS you were targeting, the pre-R6 and
R6 encodings of `mul` had to be two enum members. They are now one
mnemonic with two forms. Eight of the twelve did not even need the
feature filter -- pre-R6 MADD takes rs,rt while the PS2 MMI MADD takes
rd,rs,rt, so operand matching alone separates them. Verified against
llvm-mc: pre-R6 `mul` 712a4002, R6 `mul` 012a4098, `madd $t1,$t2`
712a0000. The printer's hand-written override table is gone.
arm32
* 20 `*_LANE` mnemonics folded into their base. The lane form differs from
the base in an operand TYPE already (DPR_ELEM vs DPR), so the matcher
could always tell them apart; the split only cost us the printed name,
which was the enum name verbatim -- `vqdmulh_lane`, which no assembler
takes. VMOV/VLD1-4/VST1-4 are left alone: their lane forms collide with
the base because register lists and lane indices are not modelled.
riscv
* ZEXT_H and REV8 each carry an RV32 and an RV64 encoding with identical
operands, and the forms were already tagged rv32_only / rv64_only -- the
encoder just never looked. `encode` now takes `xlen: XLEN = .RV64` and
filters, so the RV64 encodings are reachable at all: zext.h 0805c53b and
rev8 6b85d513, both confirmed against llvm-mc.
mos6502
* SAX_NMOS folded into SAX. The undocumented NMOS store-A&X and the
HuC6280 register swap share the mnemonic `sax`; one takes a memory
operand and the other takes none, so they are just two form sets.
Verified: every rexcode suite matches HEAD exactly, all 13 packages build,
and MIPS mnemonics llvm-mc does not recognise drop from 448 to 354.
Still open: arm32 has 201 form signatures no caller can select, because the
NEON data type (.i8/.i16/.f32) is not an operand -- `inst_vadd(d0,d1,d2)`
always yields the first form, and only a decoder-supplied form_id hint can
pick another. 38 arm32 mnemonics still carry encoding-shaped names
(VPADD_F, VCEQ_Z, VLDRB_GATHER, VMOV_Q_R, ...).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
130 lines
4.5 KiB
Odin
130 lines
4.5 KiB
Odin
// rexcode · Brendan Punsky (dotbmp@github), original author
|
|
|
|
package rexcode_mos6502
|
|
|
|
// =============================================================================
|
|
// MOS 6502 family mnemonics
|
|
// =============================================================================
|
|
//
|
|
// Covers the four CPU tiers we target:
|
|
// - NMOS official 6502 (56 mnemonics)
|
|
// - NMOS undocumented opcodes (~24, widely used on NES & Apple II)
|
|
// - 65C02 additions (Rockwell/WDC), incl. RMB/SMB/BBR/BBS bit ops
|
|
// - HuC6280 (PC Engine) additions: block xfer, swap regs, MMR ops
|
|
//
|
|
// The undocumented NMOS SAX (store A&X) and the HuC6280 SAX (swap A,X)
|
|
// share the assembler mnemonic `sax`. They are one Mnemonic with two sets
|
|
// of forms: the NMOS one takes a memory operand and the HuC6280 one takes
|
|
// none, so the operand shape alone picks the right encoding.
|
|
|
|
Mnemonic :: enum u16 {
|
|
INVALID = 0,
|
|
|
|
// -------------------------------------------------------------------------
|
|
// NMOS official
|
|
// -------------------------------------------------------------------------
|
|
|
|
// Arithmetic / logical
|
|
ADC, AND, ASL,
|
|
BIT,
|
|
CMP, CPX, CPY,
|
|
DEC, DEX, DEY,
|
|
EOR,
|
|
INC, INX, INY,
|
|
LSR,
|
|
ORA,
|
|
ROL, ROR,
|
|
SBC,
|
|
|
|
// Branches
|
|
BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS,
|
|
|
|
// Jumps / subroutines / interrupts
|
|
JMP, JSR, RTI, RTS,
|
|
BRK,
|
|
|
|
// Flag ops
|
|
CLC, CLD, CLI, CLV,
|
|
SEC, SED, SEI,
|
|
|
|
// Loads / stores
|
|
LDA, LDX, LDY,
|
|
STA, STX, STY,
|
|
|
|
// Stack / transfer
|
|
PHA, PHP, PLA, PLP,
|
|
TAX, TAY, TSX, TXA, TXS, TYA,
|
|
|
|
// NOP
|
|
NOP,
|
|
|
|
// -------------------------------------------------------------------------
|
|
// NMOS undocumented (common subset used on NES & Apple II)
|
|
// -------------------------------------------------------------------------
|
|
|
|
LAX, // LDA + LDX (load A and X from memory)
|
|
DCP, // DEC + CMP (memory)
|
|
ISC, // INC + SBC (also ISB)
|
|
RLA, // ROL + AND
|
|
RRA, // ROR + ADC
|
|
SLO, // ASL + ORA
|
|
SRE, // LSR + EOR
|
|
ALR, // AND #imm + LSR A
|
|
ANC, // AND #imm with carry from N
|
|
ARR, // AND #imm + ROR A
|
|
AXS, // X = (A AND X) - imm (also SBX)
|
|
LAS, // LDA / TSX / AND with stack pointer
|
|
ANE, // A = (A | $EE) AND X AND imm -- unstable (also XAA)
|
|
LXA, // A = X = (A | $EE) AND imm -- unstable
|
|
SHA, // store A AND X AND high+1
|
|
SHX, // store X AND high+1
|
|
SHY, // store Y AND high+1
|
|
TAS, // S = A AND X; store S AND high+1
|
|
JAM, // halt the CPU (also KIL / HLT)
|
|
USBC, // same as SBC #imm but at $EB (an undocumented alias)
|
|
DOP, // double NOP (skips one operand byte)
|
|
TOP, // triple NOP (skips two operand bytes)
|
|
|
|
// -------------------------------------------------------------------------
|
|
// 65C02 additions (Rockwell + WDC)
|
|
// -------------------------------------------------------------------------
|
|
|
|
BRA, // branch always
|
|
INA, // INC A
|
|
DEA, // DEC A
|
|
PHX, PHY,
|
|
PLX, PLY,
|
|
STZ, // store zero
|
|
TRB, TSB, // test-and-reset / test-and-set bits
|
|
STP, WAI, // WDC: stop, wait-for-interrupt
|
|
|
|
// 65C02 Rockwell bit ops -- 32 distinct opcodes
|
|
RMB0, RMB1, RMB2, RMB3, RMB4, RMB5, RMB6, RMB7,
|
|
SMB0, SMB1, SMB2, SMB3, SMB4, SMB5, SMB6, SMB7,
|
|
BBR0, BBR1, BBR2, BBR3, BBR4, BBR5, BBR6, BBR7,
|
|
BBS0, BBS1, BBS2, BBS3, BBS4, BBS5, BBS6, BBS7,
|
|
|
|
// -------------------------------------------------------------------------
|
|
// HuC6280 (PC Engine / TurboGrafx-16)
|
|
// -------------------------------------------------------------------------
|
|
|
|
SXY, // swap X, Y
|
|
SAX, // swap A,X (HuC6280) / store A AND X (NMOS undocumented)
|
|
SAY, // swap A, Y
|
|
CLA, CLX, CLY, // clear A / X / Y
|
|
CSH, CSL, // CPU speed high / low (7.16 MHz vs 1.79 MHz)
|
|
SET, // set T flag (next op uses zp address as accumulator)
|
|
ST0, ST1, ST2, // store immediate to MMR0/1/2
|
|
TAM, TMA, // transfer A to/from MMR (immediate-selected)
|
|
TST, // bit test of immediate against memory
|
|
BSR, // branch to subroutine (PC-relative)
|
|
|
|
// HuC6280 block transfer instructions (7-byte encoding)
|
|
// src(2) -> dst(2), len(2) bytes
|
|
TII, // transfer increment-increment (memcpy ascending)
|
|
TDD, // transfer decrement-decrement (memcpy descending)
|
|
TIN, // transfer increment-no-change (fill from src)
|
|
TIA, // transfer increment-alternate (interleaved)
|
|
TAI, // transfer alternate-increment (interleaved)
|
|
}
|