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.
; 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.
; 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
.byte 0x8f, 0xb6, 0x00, 0x00 ; sta long $00:00B6 (user ID)
sta >$0000B6 ; sta long $00:00B6 (user ID)
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
.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
; mitigation as crt0Gsos.s.
@ -49,7 +50,7 @@ __start:
; GS/OS calls (see __gnoSaveDP / gsosWrite-under-GNO path).
rep #0x30
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.
lda #0

View file

@ -93,14 +93,22 @@ class W65816Operand : public MCParsedAsmOperand {
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:
W65816Operand(StringRef T, SMLoc S)
: Kind(k_Tok), Tok({T.data(), (unsigned)T.size()}), Start(S),
End(SMLoc::getFromPointer(T.data() + T.size())) {}
W65816Operand(unsigned R, SMLoc S, SMLoc E)
: Kind(k_Reg), RegNum(R), Start(S), End(E) {}
W65816Operand(KindTy K, const MCExpr *E, SMLoc S, SMLoc E2)
: Kind(K), Start(S), End(E2) {
W65816Operand(KindTy K, const MCExpr *E, SMLoc S, SMLoc E2,
bool ForceLong = false)
: Kind(K), Start(S), End(E2), ForceLong(ForceLong) {
if (K == k_Imm)
Imm = E;
else
@ -124,8 +132,9 @@ public:
}
static std::unique_ptr<W65816Operand> createAddr(const MCExpr *Val, SMLoc S,
SMLoc E) {
return std::make_unique<W65816Operand>(k_Addr, Val, S, E);
SMLoc E,
bool ForceLong = false) {
return std::make_unique<W65816Operand>(k_Addr, Val, S, E, ForceLong);
}
// --- MCParsedAsmOperand hooks ------------------------------------------
@ -189,15 +198,21 @@ public:
}
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 {
return Kind == k_Addr &&
return Kind == k_Addr && !ForceLong &&
(!isConstant(Addr) || constFitsUnsigned(Addr, 16));
}
bool isAddrLong() const {
if (Kind != k_Addr)
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))
// Symbolic — accept; if a same-mnemonic narrower form exists (Abs)
// the matcher will prefer it.
@ -590,6 +605,18 @@ bool W65816AsmParser::parseOperand(OperandVector &Operands) {
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
// rely on the standard LLVM asm expression parser, which accepts
// identifiers (labels), decimal integers, hex ($... via MCAsmInfo
@ -599,7 +626,7 @@ bool W65816AsmParser::parseOperand(OperandVector &Operands) {
if (Parser.parseExpression(Val))
return Error(S, "expected expression");
SMLoc E = getLexer().getLoc();
Operands.push_back(W65816Operand::createAddr(Val, S, E));
Operands.push_back(W65816Operand::createAddr(Val, S, E, ForceLong));
return false;
}