//===-- W65816InstrFormats.td - W65816 Instruction Formats -*- tablegen -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Instruction format definitions for the W65816. Every WDC 65816 instruction // is 1-4 bytes: a single-byte opcode followed by 0-3 bytes of immediate data, // direct-page offset, absolute address, long address, or PC-relative // displacement. // // TSFlags encode which processor-mode bits (M, X) an instruction depends on. // Bit 0: instruction assumes M clear (16-bit accumulator). // Bit 1: instruction assumes M set (8-bit accumulator). // Bit 2: instruction assumes X clear (16-bit index). // Bit 3: instruction assumes X set (8-bit index). // Instructions whose behavior does not depend on M/X leave all four bits 0. // The REP/SEP scheduling pass consumes these bits to decide where to insert // mode transitions. // //===----------------------------------------------------------------------===// // Base instruction class. Subclasses set Size and fill in the opcode byte // and operand fields. class W65816Inst pattern = []> : Instruction { let Namespace = "W65816"; // The disassembler scaffold reads from the default "W65816" decoder table. // Mode-ambiguous variants (LDA_Imm8, LDX_Imm8, ...) override this to // "W65816MHigh" or "W65816XHigh" so they end up in separate tables that // the scaffold does NOT consult, eliminating same-opcode conflicts in the // default table. See comment in W65816InstrInfo.td. let DecoderNamespace = "W65816"; let OutOperandList = outs; let InOperandList = ins; let AsmString = asmstr; let Pattern = pattern; // MC-layer-only instructions conservatively mark side effects so the // scheduler can't reorder them. Lowering targets override as needed. let mayLoad = true; let mayStore = true; let hasSideEffects = true; // Mode-dependence bits consumed by the REP/SEP pass. bit MLow = 0; bit MHigh = 0; bit XLow = 0; bit XHigh = 0; let TSFlags{0} = MLow; let TSFlags{1} = MHigh; let TSFlags{2} = XLow; let TSFlags{3} = XHigh; } // Pseudo-instruction: expands away before emission. class W65816Pseudo pattern> : W65816Inst { let isPseudo = 1; let Size = 0; let mayLoad = false; let mayStore = false; let hasSideEffects = false; } //===----------------------------------------------------------------------===// // Operand classes. //===----------------------------------------------------------------------===// // Custom DiagnosticType values would require matching Match_ enum // values in the parser; we rely on the generic Match_InvalidOperand // message for now. class W65816AsmOperand : AsmOperandClass { let Name = name; } class W65816ImmOp : W65816AsmOperand { let RenderMethod = "addImmOperands"; } class W65816AddrOp : W65816AsmOperand; class W65816PCRelOp : W65816AsmOperand; def imm8 : Operand { let ParserMatchClass = W65816ImmOp<"Imm8">; let OperandType = "OPERAND_IMMEDIATE"; let Type = i8; let EncoderMethod = "encodeImm8"; let DecoderMethod = "decodeImm8"; } def imm16 : Operand { let ParserMatchClass = W65816ImmOp<"Imm16">; let OperandType = "OPERAND_IMMEDIATE"; let Type = i16; let EncoderMethod = "encodeImm16"; let DecoderMethod = "decodeImm16"; } def addrDP : Operand { let ParserMatchClass = W65816AddrOp<"AddrDP">; let OperandType = "OPERAND_MEMORY"; let Type = i8; let PrintMethod = "printAddrDP"; let EncoderMethod = "encodeAddr8"; let DecoderMethod = "decodeAddr8"; } def addrAbs : Operand { let ParserMatchClass = W65816AddrOp<"AddrAbs">; let OperandType = "OPERAND_MEMORY"; let Type = i16; let PrintMethod = "printAddrAbs"; let EncoderMethod = "encodeAddr16"; let DecoderMethod = "decodeAddr16"; } def addrLong : Operand { let ParserMatchClass = W65816AddrOp<"AddrLong">; let OperandType = "OPERAND_MEMORY"; let Type = i32; let PrintMethod = "printAddrLong"; let EncoderMethod = "encodeAddr24"; let DecoderMethod = "decodeAddr24"; } def pcrel8 : Operand { let ParserMatchClass = W65816PCRelOp<"PCRel8">; let OperandType = "OPERAND_PCREL"; let PrintMethod = "printPCRel8"; let EncoderMethod = "encodePCRel8"; let DecoderMethod = "decodePCRel8"; } def pcrel16 : Operand { let ParserMatchClass = W65816PCRelOp<"PCRel16">; let OperandType = "OPERAND_PCREL"; let PrintMethod = "printPCRel16"; let EncoderMethod = "encodePCRel16"; let DecoderMethod = "decodePCRel16"; } //===----------------------------------------------------------------------===// // Size-classified base instructions. Size is in bytes including opcode. //===----------------------------------------------------------------------===// // Single-byte implied-operand instruction. class Inst1 opcode, string asmstr> : W65816Inst<(outs), (ins), asmstr> { let Size = 1; bits<8> Inst; let Inst{7-0} = opcode; } //===----------------------------------------------------------------------===// // Addressing-mode shorthand classes. // // Each class declares a concrete 16/24/32-bit instruction encoding and wires // the named operand(s) into the bit positions following the opcode byte. // Without the explicit `let Inst{...} = name;` assignments, tablegen emits an // encoder that writes the opcode but leaves the operand bytes as zero. //===----------------------------------------------------------------------===// class InstImplied op, string mnem> : Inst1; class InstImm8 op, string mnem> : W65816Inst<(outs), (ins imm8:$imm), !strconcat(mnem, "\t#$imm")> { let Size = 2; bits<8> imm; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = imm; } class InstImm16 op, string mnem> : W65816Inst<(outs), (ins imm16:$imm), !strconcat(mnem, "\t#$imm")> { let Size = 3; bits<16> imm; bits<24> Inst; let Inst{7-0} = op; let Inst{23-8} = imm; } class InstDP op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t$addr")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } class InstAbs op, string mnem> : W65816Inst<(outs), (ins addrAbs:$addr), !strconcat(mnem, "\t$addr")> { let Size = 3; bits<16> addr; bits<24> Inst; let Inst{7-0} = op; let Inst{23-8} = addr; } class InstAbsLong op, string mnem> : W65816Inst<(outs), (ins addrLong:$addr), !strconcat(mnem, "\t$addr")> { let Size = 4; bits<24> addr; bits<32> Inst; let Inst{7-0} = op; let Inst{31-8} = addr; } class InstAbsX op, string mnem> : W65816Inst<(outs), (ins addrAbs:$addr), !strconcat(mnem, "\t$addr, x")> { let Size = 3; bits<16> addr; bits<24> Inst; let Inst{7-0} = op; let Inst{23-8} = addr; } class InstAbsY op, string mnem> : W65816Inst<(outs), (ins addrAbs:$addr), !strconcat(mnem, "\t$addr, y")> { let Size = 3; bits<16> addr; bits<24> Inst; let Inst{7-0} = op; let Inst{23-8} = addr; } class InstDPX op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t$addr, x")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } class InstDPY op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t$addr, y")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } // DP indirect addressing modes (parens around the DP byte mean // "the 16-bit pointer at $00:dp+0..1, used to form the effective // address"). Without these, the asm parser falls through to the // absolute opcode and silently corrupts memory at $00:dp. // // `(dp)` reads/writes through the pointer at DP+offset. 0x92/0xb2 // `(dp), y` same, then adds Y to form the data address. 0x91/0xb1 // `(dp, x)` adds X to dp first, then dereferences. 0x81/0xa1 class InstDPInd op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t($addr )")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } class InstDPIndY op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t($addr ), y")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } class InstDPIndX op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t($addr , x)")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } // Absolute indirect: `JMP (addr)` (opcode 0x6C). Reads a 16-bit // pointer from absolute address `addr` and jumps there (bank-local). // Used by the indirect-call trampoline in crt0/libgcc to dispatch // through a function-pointer slot. class InstAbsInd op, string mnem> : W65816Inst<(outs), (ins addrAbs:$addr), !strconcat(mnem, "\t($addr )")> { let Size = 3; bits<16> addr; bits<24> Inst; let Inst{7-0} = op; let Inst{23-8} = addr; } // DP indirect long: `[dp]` reads a 24-bit pointer at DP+offset and // uses it to form the data address (bank-crossing). `[dp], y` adds // Y to that 24-bit pointer. Only available with square-bracket // syntax — round parens are reserved for 16-bit indirection. class InstDPIndLong op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t[$addr ]")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } class InstDPIndLongY op, string mnem> : W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t[$addr ], y")> { let Size = 2; bits<8> addr; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = addr; } // Stack-relative addressing. Operand is an unsigned 8-bit offset added // to S; reads / writes the byte (or word, in 16-bit M mode) at that // stack address. Shares the addrDP operand class for parsing. class InstStackRel op, string mnem> : W65816Inst<(outs), (ins addrDP:$off), !strconcat(mnem, "\t$off, s")> { let Size = 2; bits<8> off; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = off; } // Stack-relative indirect indexed-Y: `LDA (off,S),Y`. Reads the 16-bit // pointer stored at S+off, adds Y, then loads from that address. Used // to dereference pointers spilled to a stack scratch slot — the only // way the 65816 can deref a pointer not already in zero page. class InstStackRelIndY op, string mnem> : W65816Inst<(outs), (ins addrDP:$off), !strconcat(mnem, "\t($off, s), y")> { let Size = 2; bits<8> off; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = off; } class InstPCRel8 op, string mnem> : W65816Inst<(outs), (ins pcrel8:$dest), !strconcat(mnem, "\t$dest")> { let Size = 2; bits<8> dest; bits<16> Inst; let Inst{7-0} = op; let Inst{15-8} = dest; } class InstPCRel16 op, string mnem> : W65816Inst<(outs), (ins pcrel16:$dest), !strconcat(mnem, "\t$dest")> { let Size = 3; bits<16> dest; bits<24> Inst; let Inst{7-0} = op; let Inst{23-8} = dest; } // Branch / return / call flags are applied at the def site via `let` // blocks (idiomatic LLVM approach) rather than through mixin classes, since // TableGen's multi-inheritance can't override fields from unrelated bases.