diff --git a/runtime/src/crt0Gno.s b/runtime/src/crt0Gno.s index 5c64f32..c8d46ec 100644 --- a/runtime/src/crt0Gno.s +++ b/runtime/src/crt0Gno.s @@ -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 diff --git a/src/llvm/lib/Target/W65816/AsmParser/W65816AsmParser.cpp b/src/llvm/lib/Target/W65816/AsmParser/W65816AsmParser.cpp index ee1b4da..e664425 100644 --- a/src/llvm/lib/Target/W65816/AsmParser/W65816AsmParser.cpp +++ b/src/llvm/lib/Target/W65816/AsmParser/W65816AsmParser.cpp @@ -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 createAddr(const MCExpr *Val, SMLoc S, - SMLoc E) { - return std::make_unique(k_Addr, Val, S, E); + SMLoc E, + bool ForceLong = false) { + return std::make_unique(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; }