GAS now takes > modifier to force long addressing.

This commit is contained in:
Scott Duensing 2026-06-29 01:43:27 -05:00
parent b6a6a5cdcc
commit c9129f2a81
2 changed files with 40 additions and 12 deletions

View file

@ -29,13 +29,14 @@ __start:
; NUL-terminated C string of the invocation command line. ; NUL-terminated C string of the invocation command line.
; Stash the cmdline pointer (Y:X) to bank-0 scratch $00:00B0..B3 ; Stash the cmdline pointer (Y:X) to bank-0 scratch $00:00B0..B3
; and the user ID (A) to $00:00B6, BEFORE we switch to DP=0. ; and the user ID (A) to $00:00B6, BEFORE we switch to DP=0.
; Bank-explicit `sta long` so we don't depend on DP/DBR here. ; Bank-explicit `sta long` (force-long `>` prefix) so we don't depend
; on DP/DBR here.
rep #0x30 rep #0x30
.byte 0x8f, 0xb6, 0x00, 0x00 ; sta long $00:00B6 (user ID) sta >$0000B6 ; sta long $00:00B6 (user ID)
tya tya
.byte 0x8f, 0xb0, 0x00, 0x00 ; sta long $00:00B0 (cmdline low word = Y) sta >$0000B0 ; sta long $00:00B0 (cmdline low word = Y)
txa txa
.byte 0x8f, 0xb2, 0x00, 0x00 ; sta long $00:00B2 (cmdline high word = X) sta >$0000B2 ; sta long $00:00B2 (cmdline high word = X)
; Set DBR := PBR (our entry-segment bank) — same Loader-contract ; Set DBR := PBR (our entry-segment bank) — same Loader-contract
; mitigation as crt0Gsos.s. ; mitigation as crt0Gsos.s.
@ -49,7 +50,7 @@ __start:
; GS/OS calls (see __gnoSaveDP / gsosWrite-under-GNO path). ; GS/OS calls (see __gnoSaveDP / gsosWrite-under-GNO path).
rep #0x30 rep #0x30
tdc ; A = current (GNO) DP tdc ; A = current (GNO) DP
.byte 0x8f, 0xb4, 0x00, 0x00 ; sta long $00:00B4 (stash GNO DP word) sta >$0000B4 ; sta long $00:00B4 (stash GNO DP word)
; Set DP=0 — backend assumes DP=0 for `sta dp` / `[dp],y` etc. ; Set DP=0 — backend assumes DP=0 for `sta dp` / `[dp],y` etc.
lda #0 lda #0

View file

@ -93,14 +93,22 @@ class W65816Operand : public MCParsedAsmOperand {
SMLoc Start, End; SMLoc Start, End;
// Set only for k_Addr operands carrying an explicit force-long prefix
// (`>expr`). When true the operand matches ONLY the 24-bit absolute-long
// addressing class (AddrLong), regardless of how small the value is, so a
// bank-explicit `sta >$000071` reaches STA_Long (0x8F) instead of being
// narrowed to STA_DP/STA_Abs by value fit.
bool ForceLong = false;
public: public:
W65816Operand(StringRef T, SMLoc S) W65816Operand(StringRef T, SMLoc S)
: Kind(k_Tok), Tok({T.data(), (unsigned)T.size()}), Start(S), : Kind(k_Tok), Tok({T.data(), (unsigned)T.size()}), Start(S),
End(SMLoc::getFromPointer(T.data() + T.size())) {} End(SMLoc::getFromPointer(T.data() + T.size())) {}
W65816Operand(unsigned R, SMLoc S, SMLoc E) W65816Operand(unsigned R, SMLoc S, SMLoc E)
: Kind(k_Reg), RegNum(R), Start(S), End(E) {} : Kind(k_Reg), RegNum(R), Start(S), End(E) {}
W65816Operand(KindTy K, const MCExpr *E, SMLoc S, SMLoc E2) W65816Operand(KindTy K, const MCExpr *E, SMLoc S, SMLoc E2,
: Kind(K), Start(S), End(E2) { bool ForceLong = false)
: Kind(K), Start(S), End(E2), ForceLong(ForceLong) {
if (K == k_Imm) if (K == k_Imm)
Imm = E; Imm = E;
else else
@ -124,8 +132,9 @@ public:
} }
static std::unique_ptr<W65816Operand> createAddr(const MCExpr *Val, SMLoc S, static std::unique_ptr<W65816Operand> createAddr(const MCExpr *Val, SMLoc S,
SMLoc E) { SMLoc E,
return std::make_unique<W65816Operand>(k_Addr, Val, S, E); bool ForceLong = false) {
return std::make_unique<W65816Operand>(k_Addr, Val, S, E, ForceLong);
} }
// --- MCParsedAsmOperand hooks ------------------------------------------ // --- MCParsedAsmOperand hooks ------------------------------------------
@ -189,15 +198,21 @@ public:
} }
bool isAddrDP() const { bool isAddrDP() const {
return Kind == k_Addr && isConstant(Addr) && constFitsUnsigned(Addr, 8); return Kind == k_Addr && !ForceLong &&
isConstant(Addr) && constFitsUnsigned(Addr, 8);
} }
bool isAddrAbs() const { bool isAddrAbs() const {
return Kind == k_Addr && return Kind == k_Addr && !ForceLong &&
(!isConstant(Addr) || constFitsUnsigned(Addr, 16)); (!isConstant(Addr) || constFitsUnsigned(Addr, 16));
} }
bool isAddrLong() const { bool isAddrLong() const {
if (Kind != k_Addr) if (Kind != k_Addr)
return false; return false;
// Explicit `>expr` prefix forces the long form for any value that fits
// in 24 bits (or any symbol), and the narrower AddrDP/AddrAbs predicates
// above bail, so the matcher has only the long variant to pick.
if (ForceLong)
return !isConstant(Addr) || constFitsUnsigned(Addr, 24);
if (!isConstant(Addr)) if (!isConstant(Addr))
// Symbolic — accept; if a same-mnemonic narrower form exists (Abs) // Symbolic — accept; if a same-mnemonic narrower form exists (Abs)
// the matcher will prefer it. // the matcher will prefer it.
@ -590,6 +605,18 @@ bool W65816AsmParser::parseOperand(OperandVector &Operands) {
return false; return false;
} }
// Force-long prefix: `>expr` selects the 24-bit absolute-long addressing
// form even when the value would otherwise fit in DP/abs. This is the only
// way to spell, e.g., a bank-explicit `sta >$000071` (opcode 0x8F) — the
// 65816 encodes width in the opcode, so without the prefix the matcher
// always narrows by value fit. ('>' lexes as AsmToken::Greater; it cannot
// begin an address expression, so consuming it here is unambiguous.)
bool ForceLong = false;
if (getLexer().is(AsmToken::Greater)) {
Parser.Lex(); // eat '>'
ForceLong = true;
}
// Anything else is an address / branch-target / block-move byte. We // Anything else is an address / branch-target / block-move byte. We
// rely on the standard LLVM asm expression parser, which accepts // rely on the standard LLVM asm expression parser, which accepts
// identifiers (labels), decimal integers, hex ($... via MCAsmInfo // identifiers (labels), decimal integers, hex ($... via MCAsmInfo
@ -599,7 +626,7 @@ bool W65816AsmParser::parseOperand(OperandVector &Operands) {
if (Parser.parseExpression(Val)) if (Parser.parseExpression(Val))
return Error(S, "expected expression"); return Error(S, "expected expression");
SMLoc E = getLexer().getLoc(); SMLoc E = getLexer().getLoc();
Operands.push_back(W65816Operand::createAddr(Val, S, E)); Operands.push_back(W65816Operand::createAddr(Val, S, E, ForceLong));
return false; return false;
} }