summaryrefslogtreecommitdiff
path: root/Source/Core/Common
diff options
context:
space:
mode:
authorTilka <tilkax@gmail.com>2023-12-16 21:15:31 +0000
committerGitHub <noreply@github.com>2023-12-16 21:15:31 +0000
commit773ffd04b8ce3695e2676db66ac2a2bf2ba73494 (patch)
tree64d79a5c4e238cee05128e0c61b7e2e4e9ca71bb /Source/Core/Common
parent190c4e8cda7a78dd7b1ae184425b49d363734c7e (diff)
parentc8c9928eb11cea81808127bae9894fc6cfbac770 (diff)
Merge pull request #11497 from vyuuui/debugger_assembler_ui
Built-in assembler for debugger interface
Diffstat (limited to 'Source/Core/Common')
-rw-r--r--Source/Core/Common/Assembler/AssemblerShared.cpp26
-rw-r--r--Source/Core/Common/Assembler/AssemblerShared.h545
-rw-r--r--Source/Core/Common/Assembler/AssemblerTables.cpp1482
-rw-r--r--Source/Core/Common/Assembler/AssemblerTables.h152
-rw-r--r--Source/Core/Common/Assembler/CaseInsensitiveDict.h126
-rw-r--r--Source/Core/Common/Assembler/GekkoAssembler.cpp189
-rw-r--r--Source/Core/Common/Assembler/GekkoAssembler.h29
-rw-r--r--Source/Core/Common/Assembler/GekkoIRGen.cpp832
-rw-r--r--Source/Core/Common/Assembler/GekkoIRGen.h50
-rw-r--r--Source/Core/Common/Assembler/GekkoLexer.cpp794
-rw-r--r--Source/Core/Common/Assembler/GekkoLexer.h188
-rw-r--r--Source/Core/Common/Assembler/GekkoParser.cpp885
-rw-r--r--Source/Core/Common/Assembler/GekkoParser.h124
-rw-r--r--Source/Core/Common/CMakeLists.txt12
-rw-r--r--Source/Core/Common/CommonPaths.h1
-rw-r--r--Source/Core/Common/FileUtil.cpp2
-rw-r--r--Source/Core/Common/FileUtil.h1
17 files changed, 5438 insertions, 0 deletions
diff --git a/Source/Core/Common/Assembler/AssemblerShared.cpp b/Source/Core/Common/Assembler/AssemblerShared.cpp
new file mode 100644
index 0000000000..77d7e737f9
--- /dev/null
+++ b/Source/Core/Common/Assembler/AssemblerShared.cpp
@@ -0,0 +1,26 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Common/Assembler/AssemblerShared.h"
+
+#include <fmt/format.h>
+
+namespace Common::GekkoAssembler
+{
+std::string AssemblerError::FormatError() const
+{
+ const char* space_char = col == 0 ? "" : " ";
+
+ std::string_view line_str = error_line;
+ if (line_str.back() == '\n')
+ {
+ line_str = line_str.substr(0, line_str.length() - 1);
+ }
+
+ return fmt::format("Error on line {0} col {1}:\n"
+ " {2}\n"
+ " {3:{4}}{5:^^{6}}\n"
+ "{7}",
+ line + 1, col + 1, line_str, space_char, col, '^', len, message);
+}
+} // namespace Common::GekkoAssembler
diff --git a/Source/Core/Common/Assembler/AssemblerShared.h b/Source/Core/Common/Assembler/AssemblerShared.h
new file mode 100644
index 0000000000..23bf5be0ce
--- /dev/null
+++ b/Source/Core/Common/Assembler/AssemblerShared.h
@@ -0,0 +1,545 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <string_view>
+#include <variant>
+
+namespace Common::GekkoAssembler
+{
+struct Interval
+{
+ size_t begin;
+ size_t len;
+ constexpr size_t End() const { return begin + len; }
+};
+
+struct AssemblerError
+{
+ std::string message;
+ std::string_view error_line;
+ size_t line;
+ size_t col;
+ size_t len;
+
+ std::string FormatError() const;
+};
+
+template <typename Tag, typename T>
+using Tagged = std::pair<Tag, T>;
+template <typename Tag, typename T>
+constexpr const Tag& TagOf(const Tagged<Tag, T>& val)
+{
+ return std::get<0>(val);
+}
+template <typename Tag, typename T>
+constexpr Tag& TagOf(Tagged<Tag, T>& val)
+{
+ return std::get<0>(val);
+}
+template <typename Tag, typename T>
+constexpr const T& ValueOf(const Tagged<Tag, T>& val)
+{
+ return std::get<1>(val);
+}
+template <typename Tag, typename T>
+constexpr T& ValueOf(Tagged<Tag, T>& val)
+{
+ return std::get<1>(val);
+}
+
+template <typename T>
+using FailureOr = std::variant<AssemblerError, T>;
+template <typename T>
+constexpr bool IsFailure(const FailureOr<T>& var)
+{
+ return std::holds_alternative<AssemblerError>(var);
+}
+template <typename T>
+constexpr AssemblerError& GetFailure(FailureOr<T>& var)
+{
+ return std::get<AssemblerError>(var);
+}
+template <typename T>
+constexpr const AssemblerError& GetFailure(const FailureOr<T>& var)
+{
+ return std::get<AssemblerError>(var);
+}
+template <typename T>
+constexpr const T& GetT(const FailureOr<T>& var)
+{
+ return std::get<T>(var);
+}
+template <typename T>
+constexpr T& GetT(FailureOr<T>& var)
+{
+ return std::get<T>(var);
+}
+
+enum class GekkoDirective
+{
+ Byte,
+ _2byte,
+ _4byte,
+ _8byte,
+ Float,
+ Double,
+ Locate,
+ PadAlign,
+ Align,
+ Zeros,
+ Skip,
+ DefVar,
+ Ascii,
+ Asciz
+};
+
+enum class GekkoMnemonic : size_t
+{
+ Add,
+ Addc,
+ Adde,
+ Addi,
+ Addic,
+ AddicDot,
+ Addis,
+ Addme,
+ Addze,
+ Divw,
+ Divwu,
+ Mulhw,
+ Mulhwu,
+ Mulli,
+ Mullw,
+ Neg,
+ Subf,
+ Subfc,
+ Subfe,
+ Subfic,
+ Subfme,
+ Subfze,
+ Cmp,
+ Cmpi,
+ Cmpl,
+ Cmpli,
+ And,
+ Andc,
+ AndiDot,
+ AndisDot,
+ Cntlzw,
+ Eqv,
+ Extsb,
+ Extsh,
+ Nand,
+ Nor,
+ Or,
+ Orc,
+ Ori,
+ Oris,
+ Xor,
+ Xori,
+ Xoris,
+ Rlwimi,
+ Rlwinm,
+ Rlwnm,
+ Slw,
+ Sraw,
+ Srawi,
+ Srw,
+ Fadd,
+ Fadds,
+ Fdiv,
+ Fdivs,
+ Fmul,
+ Fmuls,
+ Fres,
+ Frsqrte,
+ Fsub,
+ Fsubs,
+ Fsel,
+ Fmadd,
+ Fmadds,
+ Fmsub,
+ Fmsubs,
+ Fnmadd,
+ Fnmadds,
+ Fnmsub,
+ Fnmsubs,
+ Fctiw,
+ Fctiwz,
+ Frsp,
+ Fcmpo,
+ Fcmpu,
+ Mcrfs,
+ Mffs,
+ Mtfsb0,
+ Mtfsb1,
+ Mtfsf,
+ Mtfsfi,
+ Lbz,
+ Lbzu,
+ Lbzux,
+ Lbzx,
+ Lha,
+ Lhau,
+ Lhaux,
+ Lhax,
+ Lhz,
+ Lhzu,
+ Lhzux,
+ Lhzx,
+ Lwz,
+ Lwzu,
+ Lwzux,
+ Lwzx,
+ Stb,
+ Stbu,
+ Stbux,
+ Stbx,
+ Sth,
+ Sthu,
+ Sthux,
+ Sthx,
+ Stw,
+ Stwu,
+ Stwux,
+ Stwx,
+ Lhbrx,
+ Lwbrx,
+ Sthbrx,
+ Stwbrx,
+ Lmw,
+ Stmw,
+ Lswi,
+ Lswx,
+ Stswi,
+ Stswx,
+ Eieio,
+ Isync,
+ Lwarx,
+ StwcxDot,
+ Sync,
+ Lfd,
+ Lfdu,
+ Lfdux,
+ Lfdx,
+ Lfs,
+ Lfsu,
+ Lfsux,
+ Lfsx,
+ Stfd,
+ Stfdu,
+ Stfdux,
+ Stfdx,
+ Stfiwx,
+ Stfs,
+ Stfsu,
+ Stfsux,
+ Stfsx,
+ Fabs,
+ Fmr,
+ Fnabs,
+ Fneg,
+ B,
+ Bc,
+ Bcctr,
+ Bclr,
+ Crand,
+ Crandc,
+ Creqv,
+ Crnand,
+ Crnor,
+ Cror,
+ Crorc,
+ Crxor,
+ Mcrf,
+ Rfi,
+ Sc,
+ Tw,
+ Twi,
+ Mcrxr,
+ Mfcr,
+ Mfmsr,
+ Mfspr_nobitswap,
+ Mftb_nobitswap,
+ Mtcrf,
+ Mtmsr,
+ Mtspr_nobitswap,
+ Dcbf,
+ Dcbi,
+ Dcbst,
+ Dcbt,
+ Dcbtst,
+ Dcbz,
+ Icbi,
+ Mfsr,
+ Mfsrin,
+ Mtsr,
+ Mtsrin,
+ Tlbie,
+ Tlbsync,
+ Eciwx,
+ Ecowx,
+ Psq_lx,
+ Psq_stx,
+ Psq_lux,
+ Psq_stux,
+ Psq_l,
+ Psq_lu,
+ Psq_st,
+ Psq_stu,
+ Ps_div,
+ Ps_sub,
+ Ps_add,
+ Ps_sel,
+ Ps_res,
+ Ps_mul,
+ Ps_rsqrte,
+ Ps_msub,
+ Ps_madd,
+ Ps_nmsub,
+ Ps_nmadd,
+ Ps_neg,
+ Ps_mr,
+ Ps_nabs,
+ Ps_abs,
+ Ps_sum0,
+ Ps_sum1,
+ Ps_muls0,
+ Ps_muls1,
+ Ps_madds0,
+ Ps_madds1,
+ Ps_cmpu0,
+ Ps_cmpo0,
+ Ps_cmpu1,
+ Ps_cmpo1,
+ Ps_merge00,
+ Ps_merge01,
+ Ps_merge10,
+ Ps_merge11,
+ Dcbz_l,
+ LastMnemonic = Dcbz_l,
+ InvalidMnemonic,
+};
+
+enum class ExtendedGekkoMnemonic : size_t
+{
+ Subi,
+ Subis,
+ Subic,
+ SubicDot,
+ Sub,
+ Subc,
+ Cmpwi,
+ Cmpw,
+ Cmplwi,
+ Cmplw,
+ Extlwi,
+ Extrwi,
+ Inslwi,
+ Insrwi,
+ Rotlwi,
+ Rotrwi,
+ Rotlw,
+ Slwi,
+ Srwi,
+ Clrlwi,
+ Clrrwi,
+ Clrlslwi,
+ Bt,
+ Bf,
+ Bdnz,
+ Bdnzt,
+ Bdnzf,
+ Bdz,
+ Bdzt,
+ Bdzf,
+ BtPredict,
+ BfPredict,
+ BdnzPredict,
+ BdnztPredict,
+ BdnzfPredict,
+ BdzPredict,
+ BdztPredict,
+ BdzfPredict,
+ Blr,
+ Btlr,
+ Bflr,
+ Bdnzlr,
+ Bdnztlr,
+ Bdnzflr,
+ Bdzlr,
+ Bdztlr,
+ Bdzflr,
+ BtlrPredict,
+ BflrPredict,
+ BdnzlrPredict,
+ BdnztlrPredict,
+ BdnzflrPredict,
+ BdzlrPredict,
+ BdztlrPredict,
+ BdzflrPredict,
+ Bctr,
+ Btctr,
+ Bfctr,
+ BtctrPredict,
+ BfctrPredict,
+ Blt,
+ Ble,
+ Beq,
+ Bge,
+ Bgt,
+ Bnl,
+ Bne,
+ Bng,
+ Bso,
+ Bns,
+ Bun,
+ Bnu,
+ BltPredict,
+ BlePredict,
+ BeqPredict,
+ BgePredict,
+ BgtPredict,
+ BnlPredict,
+ BnePredict,
+ BngPredict,
+ BsoPredict,
+ BnsPredict,
+ BunPredict,
+ BnuPredict,
+ Bltlr,
+ Blelr,
+ Beqlr,
+ Bgelr,
+ Bgtlr,
+ Bnllr,
+ Bnelr,
+ Bnglr,
+ Bsolr,
+ Bnslr,
+ Bunlr,
+ Bnulr,
+ BltlrPredict,
+ BlelrPredict,
+ BeqlrPredict,
+ BgelrPredict,
+ BgtlrPredict,
+ BnllrPredict,
+ BnelrPredict,
+ BnglrPredict,
+ BsolrPredict,
+ BnslrPredict,
+ BunlrPredict,
+ BnulrPredict,
+ Bltctr,
+ Blectr,
+ Beqctr,
+ Bgectr,
+ Bgtctr,
+ Bnlctr,
+ Bnectr,
+ Bngctr,
+ Bsoctr,
+ Bnsctr,
+ Bunctr,
+ Bnuctr,
+ BltctrPredict,
+ BlectrPredict,
+ BeqctrPredict,
+ BgectrPredict,
+ BgtctrPredict,
+ BnlctrPredict,
+ BnectrPredict,
+ BngctrPredict,
+ BsoctrPredict,
+ BnsctrPredict,
+ BunctrPredict,
+ BnuctrPredict,
+ Crset,
+ Crclr,
+ Crmove,
+ Crnot,
+ Twlt,
+ Twlti,
+ Twle,
+ Twlei,
+ Tweq,
+ Tweqi,
+ Twge,
+ Twgei,
+ Twgt,
+ Twgti,
+ Twnl,
+ Twnli,
+ Twne,
+ Twnei,
+ Twng,
+ Twngi,
+ Twllt,
+ Twllti,
+ Twlle,
+ Twllei,
+ Twlge,
+ Twlgei,
+ Twlgt,
+ Twlgti,
+ Twlnl,
+ Twlnli,
+ Twlng,
+ Twlngi,
+ Trap,
+ Mtxer,
+ Mfxer,
+ Mtlr,
+ Mflr,
+ Mtctr,
+ Mfctr,
+ Mtdsisr,
+ Mfdsisr,
+ Mtdar,
+ Mfdar,
+ Mtdec,
+ Mfdec,
+ Mtsdr1,
+ Mfsdr1,
+ Mtsrr0,
+ Mfsrr0,
+ Mtsrr1,
+ Mfsrr1,
+ Mtasr,
+ Mfasr,
+ Mtear,
+ Mfear,
+ Mttbl,
+ Mftbl,
+ Mttbu,
+ Mftbu,
+ Mtsprg,
+ Mfsprg,
+ Mtibatu,
+ Mfibatu,
+ Mtibatl,
+ Mfibatl,
+ Mtdbatu,
+ Mfdbatu,
+ Mtdbatl,
+ Mfdbatl,
+ Nop,
+ Li,
+ Lis,
+ La,
+ Mr,
+ Not,
+ Mtcr,
+ Mfspr,
+ Mftb,
+ Mtspr,
+ LastMnemonic = Mtspr,
+ InvalidMnemonic
+};
+} // namespace Common::GekkoAssembler
diff --git a/Source/Core/Common/Assembler/AssemblerTables.cpp b/Source/Core/Common/Assembler/AssemblerTables.cpp
new file mode 100644
index 0000000000..ba6c8ad5d4
--- /dev/null
+++ b/Source/Core/Common/Assembler/AssemblerTables.cpp
@@ -0,0 +1,1482 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Common/Assembler/AssemblerTables.h"
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/CaseInsensitiveDict.h"
+#include "Common/CommonTypes.h"
+
+namespace Common::GekkoAssembler::detail
+{
+namespace
+{
+constexpr size_t PLAIN_MNEMONIC = 0x0;
+constexpr size_t RECORD_BIT = 0x1;
+constexpr size_t OVERFLOW_EXCEPTION = 0x2;
+// Since RC/OE are mutually exclusive from LK/AA, they can occupy the same slot
+constexpr size_t LINK_BIT = 0x1;
+constexpr size_t ABSOLUTE_ADDRESS_BIT = 0x2;
+
+// Compile-time helpers for mnemonic generation
+// Generate inclusive mask [left, right] -- MSB=0 LSB=31
+constexpr u32 Mask(u32 left, u32 right)
+{
+ return static_cast<u32>(((u64{1} << (32 - left)) - 1) & ~((u64{1} << (31 - right)) - 1));
+}
+constexpr u32 InsertVal(u32 val, u32 left, u32 right)
+{
+ return val << (31 - right) & Mask(left, right);
+}
+constexpr u32 InsertOpcode(u32 opcode)
+{
+ return InsertVal(opcode, 0, 5);
+}
+constexpr u32 SprBitswap(u32 spr)
+{
+ return ((spr & 0b0000011111) << 5) | ((spr & 0b1111100000) >> 5);
+}
+
+constexpr MnemonicDesc INVALID_MNEMONIC = {0, 0, {}};
+constexpr ExtendedMnemonicDesc INVALID_EXT_MNEMONIC = {0, nullptr};
+
+// All operands as referenced by the Gekko/Broadway user manual
+// See section 12.1.2 under Chapter 12
+constexpr OperandDesc _A = OperandDesc{Mask(11, 15), {16, false}};
+constexpr OperandDesc _B = OperandDesc{Mask(16, 20), {11, false}};
+constexpr OperandDesc _BD = OperandDesc{Mask(16, 29), {0, true}};
+constexpr OperandDesc _BI = OperandDesc{Mask(11, 15), {16, false}};
+constexpr OperandDesc _BO = OperandDesc{Mask(6, 10), {21, false}};
+constexpr OperandDesc _C = OperandDesc{Mask(21, 25), {6, false}};
+constexpr OperandDesc _Crba = OperandDesc{Mask(11, 15), {16, false}};
+constexpr OperandDesc _Crbb = OperandDesc{Mask(16, 20), {11, false}};
+constexpr OperandDesc _Crbd = OperandDesc{Mask(6, 10), {21, false}};
+constexpr OperandDesc _Crfd = OperandDesc{Mask(6, 8), {23, false}};
+constexpr OperandDesc _Crfs = OperandDesc{Mask(11, 13), {18, false}};
+constexpr OperandDesc _CRM = OperandDesc{Mask(12, 19), {12, false}};
+constexpr OperandDesc _D = OperandDesc{Mask(6, 10), {21, false}};
+constexpr OperandDesc _FM = OperandDesc{Mask(7, 14), {17, false}};
+constexpr OperandDesc _W1 = OperandDesc{Mask(16, 16), {15, false}};
+constexpr OperandDesc _W2 = OperandDesc{Mask(21, 21), {10, false}};
+constexpr OperandDesc _IMM = OperandDesc{Mask(16, 19), {12, false}};
+constexpr OperandDesc _L = OperandDesc{Mask(10, 10), {21, false}};
+constexpr OperandDesc _LI = OperandDesc{Mask(6, 29), {0, true}};
+constexpr OperandDesc _MB = OperandDesc{Mask(21, 25), {6, false}};
+constexpr OperandDesc _ME = OperandDesc{Mask(26, 30), {1, false}};
+constexpr OperandDesc _NB = OperandDesc{Mask(16, 20), {11, false}};
+constexpr OperandDesc _Offd = OperandDesc{Mask(16, 31), {0, true}};
+constexpr OperandDesc _OffdPs = OperandDesc{Mask(20, 31), {0, true}};
+constexpr OperandDesc _S = OperandDesc{Mask(6, 10), {21, false}};
+constexpr OperandDesc _SH = OperandDesc{Mask(16, 20), {11, false}};
+constexpr OperandDesc _SIMM = OperandDesc{Mask(16, 31), {0, true}};
+constexpr OperandDesc _SPR = OperandDesc{Mask(11, 20), {11, false}};
+constexpr OperandDesc _SR = OperandDesc{Mask(12, 15), {16, false}};
+constexpr OperandDesc _TO = OperandDesc{Mask(6, 10), {21, false}};
+constexpr OperandDesc _TPR = OperandDesc{Mask(11, 20), {11, false}};
+constexpr OperandDesc _UIMM = OperandDesc{Mask(16, 31), {0, false}};
+constexpr OperandDesc _I1 = OperandDesc{Mask(17, 19), {12, false}};
+constexpr OperandDesc _I2 = OperandDesc{Mask(22, 24), {7, false}};
+} // namespace
+
+void OperandList::Insert(size_t before, u32 val)
+{
+ overfill = count == MAX_OPERANDS;
+ for (size_t i = before + 1; i <= count && i < MAX_OPERANDS; i++)
+ {
+ std::swap(list[i], list[before]);
+ }
+
+ list[before] = Tagged<Interval, u32>({0, 0}, val);
+ if (!overfill)
+ {
+ count++;
+ }
+}
+
+// OperandDesc holds the shift position for an operand, as well as the mask
+// Whether the user provided a valid input for an operand can be determined by the mask
+u32 OperandDesc::MaxVal() const
+{
+ const u32 mask_sh = mask >> shift;
+ if (is_signed)
+ {
+ const u32 mask_hibit = (mask_sh & (mask_sh ^ (mask_sh >> 1)));
+ return mask_hibit - 1;
+ }
+ return mask_sh;
+}
+
+u32 OperandDesc::MinVal() const
+{
+ if (is_signed)
+ {
+ return ~MaxVal();
+ }
+ return 0;
+}
+
+u32 OperandDesc::TruncBits() const
+{
+ const u32 mask_sh = mask >> shift;
+ const u32 mask_lobit = mask_sh & (mask_sh ^ (mask_sh << 1));
+ return mask_lobit - 1;
+}
+
+bool OperandDesc::Fits(u32 val) const
+{
+ const u32 mask_sh = mask >> shift;
+ if (is_signed)
+ {
+ // Get high bit and low bit from a range mask
+ const u32 mask_hibit = mask_sh & (mask_sh ^ (mask_sh >> 1));
+ const u32 mask_lobit = mask_sh & (mask_sh ^ (mask_sh << 1));
+ // Positive max is (signbit - 1)
+ // Negative min is ~(Positive Max)
+ const u32 positive_max = mask_hibit - 1;
+ const u32 negative_max = ~positive_max;
+ // Truncated bits are any bits right of the mask that are 0 after shifting
+ const u32 truncate_bits = mask_lobit - 1;
+ return (val <= positive_max || val >= negative_max) && !(val & truncate_bits);
+ }
+ return (mask_sh & val) == val;
+}
+
+u32 OperandDesc::Fit(u32 val) const
+{
+ return (val << shift) & mask;
+}
+
+///////////////////
+// PARSER TABLES //
+///////////////////
+
+extern const CaseInsensitiveDict<u32, '_'> sprg_map = {
+ {"xer", 1}, {"lr", 8}, {"ctr", 9}, {"dsisr", 18}, {"dar", 19},
+ {"dec", 22}, {"sdr1", 25}, {"srr0", 26}, {"srr1", 27}, {"sprg0", 272},
+ {"sprg1", 273}, {"sprg2", 274}, {"sprg3", 275}, {"ear", 282}, {"tbl", 284},
+ {"tbu", 285}, {"ibat0u", 528}, {"ibat0l", 529}, {"ibat1u", 530}, {"ibat1l", 531},
+ {"ibat2u", 532}, {"ibat2l", 533}, {"ibat3u", 534}, {"ibat3l", 535}, {"dbat0u", 536},
+ {"dbat0l", 537}, {"dbat1u", 538}, {"dbat1l", 539}, {"dbat2u", 540}, {"dbat2l", 541},
+ {"dbat3u", 542}, {"dbat3l", 543}, {"gqr0", 912}, {"gqr1", 913}, {"gqr2", 914},
+ {"gqr3", 915}, {"gqr4", 916}, {"gqr5", 917}, {"gqr6", 918}, {"gqr7", 919},
+ {"hid2", 920}, {"wpar", 921}, {"dma_u", 922}, {"dma_l", 923}, {"ummcr0", 936},
+ {"upmc1", 937}, {"upmc2", 938}, {"usia", 939}, {"ummcr1", 940}, {"upmc3", 941},
+ {"upmc4", 942}, {"usda", 943}, {"mmcr0", 952}, {"pmc1", 953}, {"pmc2", 954},
+ {"sia", 955}, {"mmcr1", 956}, {"pmc3", 957}, {"pmc4", 958}, {"sda", 959},
+ {"hid0", 1008}, {"hid1", 1009}, {"iabr", 1010}, {"dabr", 1013}, {"l2cr", 1017},
+ {"ictc", 1019}, {"thrm1", 1020}, {"thrm2", 1021}, {"thrm3", 1022}};
+
+extern const CaseInsensitiveDict<GekkoDirective> directives_map = {
+ {"byte", GekkoDirective::Byte}, {"2byte", GekkoDirective::_2byte},
+ {"4byte", GekkoDirective::_4byte}, {"8byte", GekkoDirective::_8byte},
+ {"float", GekkoDirective::Float}, {"double", GekkoDirective::Double},
+ {"locate", GekkoDirective::Locate}, {"padalign", GekkoDirective::PadAlign},
+ {"align", GekkoDirective::Align}, {"zeros", GekkoDirective::Zeros},
+ {"skip", GekkoDirective::Skip}, {"defvar", GekkoDirective::DefVar},
+ {"ascii", GekkoDirective::Ascii}, {"asciz", GekkoDirective::Asciz},
+};
+
+#define MNEMONIC(mnemonic_str, mnemonic_enum, variant_bits, alg) \
+ { \
+ mnemonic_str, \
+ { \
+ static_cast<size_t>(mnemonic_enum) * VARIANT_PERMUTATIONS + (variant_bits), alg \
+ } \
+ }
+#define PLAIN_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \
+ MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg)
+#define RC_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \
+ MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \
+ MNEMONIC(mnemonic_str ".", mnemonic_enum, RECORD_BIT, alg)
+#define OERC_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \
+ MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \
+ MNEMONIC(mnemonic_str ".", mnemonic_enum, RECORD_BIT, alg), \
+ MNEMONIC(mnemonic_str "o", mnemonic_enum, OVERFLOW_EXCEPTION, alg), \
+ MNEMONIC(mnemonic_str "o.", mnemonic_enum, (RECORD_BIT | OVERFLOW_EXCEPTION), alg)
+#define LK_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \
+ MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \
+ MNEMONIC(mnemonic_str "l", mnemonic_enum, LINK_BIT, alg)
+#define AALK_MNEMONIC(mnemonic_str, mnemonic_enum, alg) \
+ MNEMONIC(mnemonic_str, mnemonic_enum, PLAIN_MNEMONIC, alg), \
+ MNEMONIC(mnemonic_str "l", mnemonic_enum, LINK_BIT, alg), \
+ MNEMONIC(mnemonic_str "a", mnemonic_enum, ABSOLUTE_ADDRESS_BIT, alg), \
+ MNEMONIC(mnemonic_str "la", mnemonic_enum, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg)
+
+extern const CaseInsensitiveDict<ParseInfo, '.', '_'> mnemonic_tokens = {
+ OERC_MNEMONIC("add", GekkoMnemonic::Add, ParseAlg::Op3),
+ OERC_MNEMONIC("addc", GekkoMnemonic::Addc, ParseAlg::Op3),
+ OERC_MNEMONIC("adde", GekkoMnemonic::Adde, ParseAlg::Op3),
+ PLAIN_MNEMONIC("addi", GekkoMnemonic::Addi, ParseAlg::Op3),
+ PLAIN_MNEMONIC("addic", GekkoMnemonic::Addic, ParseAlg::Op3),
+ PLAIN_MNEMONIC("addic.", GekkoMnemonic::AddicDot, ParseAlg::Op3),
+ PLAIN_MNEMONIC("addis", GekkoMnemonic::Addis, ParseAlg::Op3),
+ OERC_MNEMONIC("addme", GekkoMnemonic::Addme, ParseAlg::Op2),
+ OERC_MNEMONIC("addze", GekkoMnemonic::Addze, ParseAlg::Op2),
+ RC_MNEMONIC("and", GekkoMnemonic::And, ParseAlg::Op3),
+ RC_MNEMONIC("andc", GekkoMnemonic::Andc, ParseAlg::Op3),
+ PLAIN_MNEMONIC("andi.", GekkoMnemonic::AndiDot, ParseAlg::Op3),
+ PLAIN_MNEMONIC("andis.", GekkoMnemonic::AndisDot, ParseAlg::Op3),
+ AALK_MNEMONIC("b", GekkoMnemonic::B, ParseAlg::Op1),
+ AALK_MNEMONIC("bc", GekkoMnemonic::Bc, ParseAlg::Op3),
+ LK_MNEMONIC("bcctr", GekkoMnemonic::Bcctr, ParseAlg::Op2),
+ LK_MNEMONIC("bclr", GekkoMnemonic::Bclr, ParseAlg::Op2),
+ PLAIN_MNEMONIC("cmp", GekkoMnemonic::Cmp, ParseAlg::Op4),
+ PLAIN_MNEMONIC("cmpi", GekkoMnemonic::Cmpi, ParseAlg::Op4),
+ PLAIN_MNEMONIC("cmpl", GekkoMnemonic::Cmpl, ParseAlg::Op4),
+ PLAIN_MNEMONIC("cmpli", GekkoMnemonic::Cmpli, ParseAlg::Op4),
+ RC_MNEMONIC("cntlzw", GekkoMnemonic::Cntlzw, ParseAlg::Op2),
+ PLAIN_MNEMONIC("crand", GekkoMnemonic::Crand, ParseAlg::Op3),
+ PLAIN_MNEMONIC("crandc", GekkoMnemonic::Crandc, ParseAlg::Op3),
+ PLAIN_MNEMONIC("creqv", GekkoMnemonic::Creqv, ParseAlg::Op3),
+ PLAIN_MNEMONIC("crnand", GekkoMnemonic::Crnand, ParseAlg::Op3),
+ PLAIN_MNEMONIC("crnor", GekkoMnemonic::Crnor, ParseAlg::Op3),
+ PLAIN_MNEMONIC("cror", GekkoMnemonic::Cror, ParseAlg::Op3),
+ PLAIN_MNEMONIC("crorc", GekkoMnemonic::Crorc, ParseAlg::Op3),
+ PLAIN_MNEMONIC("crxor", GekkoMnemonic::Crxor, ParseAlg::Op3),
+ PLAIN_MNEMONIC("dcbf", GekkoMnemonic::Dcbf, ParseAlg::Op2),
+ PLAIN_MNEMONIC("dcbi", GekkoMnemonic::Dcbi, ParseAlg::Op2),
+ PLAIN_MNEMONIC("dcbst", GekkoMnemonic::Dcbst, ParseAlg::Op2),
+ PLAIN_MNEMONIC("dcbt", GekkoMnemonic::Dcbt, ParseAlg::Op2),
+ PLAIN_MNEMONIC("dcbtst", GekkoMnemonic::Dcbtst, ParseAlg::Op2),
+ PLAIN_MNEMONIC("dcbz", GekkoMnemonic::Dcbz, ParseAlg::Op2),
+ PLAIN_MNEMONIC("dcbz_l", GekkoMnemonic::Dcbz_l, ParseAlg::Op2),
+ OERC_MNEMONIC("divw", GekkoMnemonic::Divw, ParseAlg::Op3),
+ OERC_MNEMONIC("divwu", GekkoMnemonic::Divwu, ParseAlg::Op3),
+ PLAIN_MNEMONIC("eciwx", GekkoMnemonic::Eciwx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("ecowx", GekkoMnemonic::Ecowx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("eieio", GekkoMnemonic::Eieio, ParseAlg::None),
+ RC_MNEMONIC("eqv", GekkoMnemonic::Eqv, ParseAlg::Op3),
+ RC_MNEMONIC("extsb", GekkoMnemonic::Extsb, ParseAlg::Op2),
+ RC_MNEMONIC("extsh", GekkoMnemonic::Extsh, ParseAlg::Op2),
+ RC_MNEMONIC("fabs", GekkoMnemonic::Fabs, ParseAlg::Op2),
+ RC_MNEMONIC("fadd", GekkoMnemonic::Fadd, ParseAlg::Op3),
+ RC_MNEMONIC("fadds", GekkoMnemonic::Fadds, ParseAlg::Op3),
+ PLAIN_MNEMONIC("fcmpo", GekkoMnemonic::Fcmpo, ParseAlg::Op3),
+ PLAIN_MNEMONIC("fcmpu", GekkoMnemonic::Fcmpu, ParseAlg::Op3),
+ RC_MNEMONIC("fctiw", GekkoMnemonic::Fctiw, ParseAlg::Op2),
+ RC_MNEMONIC("fctiwz", GekkoMnemonic::Fctiwz, ParseAlg::Op2),
+ RC_MNEMONIC("fdiv", GekkoMnemonic::Fdiv, ParseAlg::Op3),
+ RC_MNEMONIC("fdivs", GekkoMnemonic::Fdivs, ParseAlg::Op3),
+ RC_MNEMONIC("fmadd", GekkoMnemonic::Fmadd, ParseAlg::Op4),
+ RC_MNEMONIC("fmadds", GekkoMnemonic::Fmadds, ParseAlg::Op4),
+ RC_MNEMONIC("fmr", GekkoMnemonic::Fmr, ParseAlg::Op2),
+ RC_MNEMONIC("fmsub", GekkoMnemonic::Fmsub, ParseAlg::Op4),
+ RC_MNEMONIC("fmsubs", GekkoMnemonic::Fmsubs, ParseAlg::Op4),
+ RC_MNEMONIC("fmul", GekkoMnemonic::Fmul, ParseAlg::Op3),
+ RC_MNEMONIC("fmuls", GekkoMnemonic::Fmuls, ParseAlg::Op3),
+ RC_MNEMONIC("fnabs", GekkoMnemonic::Fnabs, ParseAlg::Op2),
+ RC_MNEMONIC("fneg", GekkoMnemonic::Fneg, ParseAlg::Op2),
+ RC_MNEMONIC("fnmadd", GekkoMnemonic::Fnmadd, ParseAlg::Op4),
+ RC_MNEMONIC("fnmadds", GekkoMnemonic::Fnmadds, ParseAlg::Op4),
+ RC_MNEMONIC("fnmsub", GekkoMnemonic::Fnmsub, ParseAlg::Op4),
+ RC_MNEMONIC("fnmsubs", GekkoMnemonic::Fnmsubs, ParseAlg::Op4),
+ RC_MNEMONIC("fres", GekkoMnemonic::Fres, ParseAlg::Op2),
+ RC_MNEMONIC("frsp", GekkoMnemonic::Frsp, ParseAlg::Op2),
+ RC_MNEMONIC("frsqrte", GekkoMnemonic::Frsqrte, ParseAlg::Op2),
+ RC_MNEMONIC("fsel", GekkoMnemonic::Fsel, ParseAlg::Op4),
+ RC_MNEMONIC("fsub", GekkoMnemonic::Fsub, ParseAlg::Op3),
+ RC_MNEMONIC("fsubs", GekkoMnemonic::Fsubs, ParseAlg::Op3),
+ PLAIN_MNEMONIC("icbi", GekkoMnemonic::Icbi, ParseAlg::Op2),
+ PLAIN_MNEMONIC("isync", GekkoMnemonic::Isync, ParseAlg::None),
+ PLAIN_MNEMONIC("lbz", GekkoMnemonic::Lbz, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lbzu", GekkoMnemonic::Lbzu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lbzux", GekkoMnemonic::Lbzux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lbzx", GekkoMnemonic::Lbzx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lfd", GekkoMnemonic::Lfd, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lfdu", GekkoMnemonic::Lfdu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lfdux", GekkoMnemonic::Lfdux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lfdx", GekkoMnemonic::Lfdx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lfs", GekkoMnemonic::Lfs, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lfsu", GekkoMnemonic::Lfsu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lfsux", GekkoMnemonic::Lfsux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lfsx", GekkoMnemonic::Lfsx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lha", GekkoMnemonic::Lha, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lhau", GekkoMnemonic::Lhau, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lhaux", GekkoMnemonic::Lhaux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lhax", GekkoMnemonic::Lhax, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lhbrx", GekkoMnemonic::Lhbrx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lhz", GekkoMnemonic::Lhz, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lhzu", GekkoMnemonic::Lhzu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lhzux", GekkoMnemonic::Lhzux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lhzx", GekkoMnemonic::Lhzx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lmw", GekkoMnemonic::Lmw, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lswi", GekkoMnemonic::Lswi, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lswx", GekkoMnemonic::Lswx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lwarx", GekkoMnemonic::Lwarx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lwbrx", GekkoMnemonic::Lwbrx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lwz", GekkoMnemonic::Lwz, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lwzu", GekkoMnemonic::Lwzu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("lwzux", GekkoMnemonic::Lwzux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("lwzx", GekkoMnemonic::Lwzx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("mcrf", GekkoMnemonic::Mcrf, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mcrfs", GekkoMnemonic::Mcrfs, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mcrxr", GekkoMnemonic::Mcrxr, ParseAlg::Op1),
+ PLAIN_MNEMONIC("mfcr", GekkoMnemonic::Mfcr, ParseAlg::Op1),
+ RC_MNEMONIC("mffs", GekkoMnemonic::Mffs, ParseAlg::Op1),
+ PLAIN_MNEMONIC("mfmsr", GekkoMnemonic::Mfmsr, ParseAlg::Op1),
+ PLAIN_MNEMONIC("mfspr_nobitswap", GekkoMnemonic::Mfspr_nobitswap, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mfsr", GekkoMnemonic::Mfsr, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mfsrin", GekkoMnemonic::Mfsrin, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mftb_nobitswap", GekkoMnemonic::Mftb_nobitswap, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mtcrf", GekkoMnemonic::Mtcrf, ParseAlg::Op2),
+ RC_MNEMONIC("mtfsb0", GekkoMnemonic::Mtfsb0, ParseAlg::Op1),
+ RC_MNEMONIC("mtfsb1", GekkoMnemonic::Mtfsb1, ParseAlg::Op1),
+ RC_MNEMONIC("mtfsf", GekkoMnemonic::Mtfsf, ParseAlg::Op2),
+ RC_MNEMONIC("mtfsfi", GekkoMnemonic::Mtfsfi, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mtmsr", GekkoMnemonic::Mtmsr, ParseAlg::Op1),
+ PLAIN_MNEMONIC("mtspr_nobitswap", GekkoMnemonic::Mtspr_nobitswap, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mtsr", GekkoMnemonic::Mtsr, ParseAlg::Op2),
+ PLAIN_MNEMONIC("mtsrin", GekkoMnemonic::Mtsrin, ParseAlg::Op2),
+ RC_MNEMONIC("mulhw", GekkoMnemonic::Mulhw, ParseAlg::Op3),
+ RC_MNEMONIC("mulhwu", GekkoMnemonic::Mulhwu, ParseAlg::Op3),
+ PLAIN_MNEMONIC("mulli", GekkoMnemonic::Mulli, ParseAlg::Op3),
+ OERC_MNEMONIC("mullw", GekkoMnemonic::Mullw, ParseAlg::Op3),
+ RC_MNEMONIC("nand", GekkoMnemonic::Nand, ParseAlg::Op3),
+ OERC_MNEMONIC("neg", GekkoMnemonic::Neg, ParseAlg::Op2),
+ RC_MNEMONIC("nor", GekkoMnemonic::Nor, ParseAlg::Op3),
+ RC_MNEMONIC("or", GekkoMnemonic::Or, ParseAlg::Op3),
+ RC_MNEMONIC("orc", GekkoMnemonic::Orc, ParseAlg::Op3),
+ PLAIN_MNEMONIC("ori", GekkoMnemonic::Ori, ParseAlg::Op3),
+ PLAIN_MNEMONIC("oris", GekkoMnemonic::Oris, ParseAlg::Op3),
+ PLAIN_MNEMONIC("psq_l", GekkoMnemonic::Psq_l, ParseAlg::Op1Off1Op2),
+ PLAIN_MNEMONIC("psq_lu", GekkoMnemonic::Psq_lu, ParseAlg::Op1Off1Op2),
+ PLAIN_MNEMONIC("psq_lux", GekkoMnemonic::Psq_lux, ParseAlg::Op5),
+ PLAIN_MNEMONIC("psq_lx", GekkoMnemonic::Psq_lx, ParseAlg::Op5),
+ PLAIN_MNEMONIC("psq_st", GekkoMnemonic::Psq_st, ParseAlg::Op1Off1Op2),
+ PLAIN_MNEMONIC("psq_stu", GekkoMnemonic::Psq_stu, ParseAlg::Op1Off1Op2),
+ PLAIN_MNEMONIC("psq_stux", GekkoMnemonic::Psq_stux, ParseAlg::Op5),
+ PLAIN_MNEMONIC("psq_stx", GekkoMnemonic::Psq_stx, ParseAlg::Op5),
+ RC_MNEMONIC("ps_abs", GekkoMnemonic::Ps_abs, ParseAlg::Op2),
+ RC_MNEMONIC("ps_add", GekkoMnemonic::Ps_add, ParseAlg::Op3),
+ PLAIN_MNEMONIC("ps_cmpo0", GekkoMnemonic::Ps_cmpo0, ParseAlg::Op3),
+ PLAIN_MNEMONIC("ps_cmpo1", GekkoMnemonic::Ps_cmpo1, ParseAlg::Op3),
+ PLAIN_MNEMONIC("ps_cmpu0", GekkoMnemonic::Ps_cmpu0, ParseAlg::Op3),
+ PLAIN_MNEMONIC("ps_cmpu1", GekkoMnemonic::Ps_cmpu1, ParseAlg::Op3),
+ RC_MNEMONIC("ps_div", GekkoMnemonic::Ps_div, ParseAlg::Op3),
+ RC_MNEMONIC("ps_madd", GekkoMnemonic::Ps_madd, ParseAlg::Op4),
+ RC_MNEMONIC("ps_madds0", GekkoMnemonic::Ps_madds0, ParseAlg::Op4),
+ RC_MNEMONIC("ps_madds1", GekkoMnemonic::Ps_madds1, ParseAlg::Op4),
+ RC_MNEMONIC("ps_merge00", GekkoMnemonic::Ps_merge00, ParseAlg::Op3),
+ RC_MNEMONIC("ps_merge01", GekkoMnemonic::Ps_merge01, ParseAlg::Op3),
+ RC_MNEMONIC("ps_merge10", GekkoMnemonic::Ps_merge10, ParseAlg::Op3),
+ RC_MNEMONIC("ps_merge11", GekkoMnemonic::Ps_merge11, ParseAlg::Op3),
+ RC_MNEMONIC("ps_mr", GekkoMnemonic::Ps_mr, ParseAlg::Op2),
+ RC_MNEMONIC("ps_msub", GekkoMnemonic::Ps_msub, ParseAlg::Op4),
+ RC_MNEMONIC("ps_mul", GekkoMnemonic::Ps_mul, ParseAlg::Op3),
+ RC_MNEMONIC("ps_muls0", GekkoMnemonic::Ps_muls0, ParseAlg::Op3),
+ RC_MNEMONIC("ps_muls1", GekkoMnemonic::Ps_muls1, ParseAlg::Op3),
+ RC_MNEMONIC("ps_nabs", GekkoMnemonic::Ps_nabs, ParseAlg::Op2),
+ RC_MNEMONIC("ps_neg", GekkoMnemonic::Ps_neg, ParseAlg::Op2),
+ RC_MNEMONIC("ps_nmadd", GekkoMnemonic::Ps_nmadd, ParseAlg::Op4),
+ RC_MNEMONIC("ps_nmsub", GekkoMnemonic::Ps_nmsub, ParseAlg::Op4),
+ RC_MNEMONIC("ps_res", GekkoMnemonic::Ps_res, ParseAlg::Op2),
+ RC_MNEMONIC("ps_rsqrte", GekkoMnemonic::Ps_rsqrte, ParseAlg::Op2),
+ RC_MNEMONIC("ps_sel", GekkoMnemonic::Ps_sel, ParseAlg::Op4),
+ RC_MNEMONIC("ps_sub", GekkoMnemonic::Ps_sub, ParseAlg::Op3),
+ RC_MNEMONIC("ps_sum0", GekkoMnemonic::Ps_sum0, ParseAlg::Op4),
+ RC_MNEMONIC("ps_sum1", GekkoMnemonic::Ps_sum1, ParseAlg::Op4),
+ PLAIN_MNEMONIC("rfi", GekkoMnemonic::Rfi, ParseAlg::None),
+ RC_MNEMONIC("rlwimi", GekkoMnemonic::Rlwimi, ParseAlg::Op5),
+ RC_MNEMONIC("rlwinm", GekkoMnemonic::Rlwinm, ParseAlg::Op5),
+ RC_MNEMONIC("rlwnm", GekkoMnemonic::Rlwnm, ParseAlg::Op5),
+ PLAIN_MNEMONIC("sc", GekkoMnemonic::Sc, ParseAlg::None),
+ RC_MNEMONIC("slw", GekkoMnemonic::Slw, ParseAlg::Op3),
+ RC_MNEMONIC("sraw", GekkoMnemonic::Sraw, ParseAlg::Op3),
+ RC_MNEMONIC("srawi", GekkoMnemonic::Srawi, ParseAlg::Op3),
+ RC_MNEMONIC("srw", GekkoMnemonic::Srw, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stb", GekkoMnemonic::Stb, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stbu", GekkoMnemonic::Stbu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stbux", GekkoMnemonic::Stbux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stbx", GekkoMnemonic::Stbx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stfd", GekkoMnemonic::Stfd, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stfdu", GekkoMnemonic::Stfdu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stfdux", GekkoMnemonic::Stfdux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stfdx", GekkoMnemonic::Stfdx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stfiwx", GekkoMnemonic::Stfiwx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stfs", GekkoMnemonic::Stfs, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stfsu", GekkoMnemonic::Stfsu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stfsux", GekkoMnemonic::Stfsux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stfsx", GekkoMnemonic::Stfsx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("sth", GekkoMnemonic::Sth, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("sthbrx", GekkoMnemonic::Sthbrx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("sthu", GekkoMnemonic::Sthu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("sthux", GekkoMnemonic::Sthux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("sthx", GekkoMnemonic::Sthx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stmw", GekkoMnemonic::Stmw, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stswi", GekkoMnemonic::Stswi, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stswx", GekkoMnemonic::Stswx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stw", GekkoMnemonic::Stw, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stwbrx", GekkoMnemonic::Stwbrx, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stwcx.", GekkoMnemonic::StwcxDot, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stwu", GekkoMnemonic::Stwu, ParseAlg::Op1Off1),
+ PLAIN_MNEMONIC("stwux", GekkoMnemonic::Stwux, ParseAlg::Op3),
+ PLAIN_MNEMONIC("stwx", GekkoMnemonic::Stwx, ParseAlg::Op3),
+ OERC_MNEMONIC("subf", GekkoMnemonic::Subf, ParseAlg::Op3),
+ OERC_MNEMONIC("subfc", GekkoMnemonic::Subfc, ParseAlg::Op3),
+ OERC_MNEMONIC("subfe", GekkoMnemonic::Subfe, ParseAlg::Op3),
+ PLAIN_MNEMONIC("subfic", GekkoMnemonic::Subfic, ParseAlg::Op3),
+ OERC_MNEMONIC("subfme", GekkoMnemonic::Subfme, ParseAlg::Op2),
+ OERC_MNEMONIC("subfze", GekkoMnemonic::Subfze, ParseAlg::Op2),
+ PLAIN_MNEMONIC("sync", GekkoMnemonic::Sync, ParseAlg::None),
+ PLAIN_MNEMONIC("tlbie", GekkoMnemonic::Tlbie, ParseAlg::Op1),
+ PLAIN_MNEMONIC("tlbsync", GekkoMnemonic::Tlbsync, ParseAlg::None),
+ PLAIN_MNEMONIC("tw", GekkoMnemonic::Tw, ParseAlg::Op3),
+ PLAIN_MNEMONIC("twi", GekkoMnemonic::Twi, ParseAlg::Op3),
+ RC_MNEMONIC("xor", GekkoMnemonic::Xor, ParseAlg::Op3),
+ PLAIN_MNEMONIC("xori", GekkoMnemonic::Xori, ParseAlg::Op3),
+ PLAIN_MNEMONIC("xoris", GekkoMnemonic::Xoris, ParseAlg::Op3),
+};
+
+#define PSEUDO(mnemonic, base, variant_bits, alg) \
+ { \
+ mnemonic, { static_cast<size_t>(base) * VARIANT_PERMUTATIONS + (variant_bits), alg } \
+ }
+#define PLAIN_PSEUDO(mnemonic, base, alg) PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg)
+#define RC_PSEUDO(mnemonic, base, alg) \
+ PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic ".", base, RECORD_BIT, alg)
+#define OERC_PSEUDO(mnemonic, base, alg) \
+ PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic ".", base, RECORD_BIT, alg), \
+ PSEUDO(mnemonic "o", base, OVERFLOW_EXCEPTION, alg), \
+ PSEUDO(mnemonic "o.", base, (RECORD_BIT | OVERFLOW_EXCEPTION), alg)
+#define LK_PSEUDO(mnemonic, base, alg) \
+ PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg)
+#define LKAA_PSEUDO(mnemonic, base, alg) \
+ PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg), \
+ PSEUDO(mnemonic "a", base, ABSOLUTE_ADDRESS_BIT, alg), \
+ PSEUDO(mnemonic "la", base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg)
+#define LKPRED_PSEUDO(mnemonic, base, alg) \
+ PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg), \
+ PSEUDO(mnemonic "-", base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l-", base, LINK_BIT, alg), \
+ PSEUDO(mnemonic "+", base##Predict, PLAIN_MNEMONIC, alg), \
+ PSEUDO(mnemonic "l+", base##Predict, LINK_BIT, alg)
+#define LKAAPRED_PSEUDO(mnemonic, base, alg) \
+ PSEUDO(mnemonic, base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l", base, LINK_BIT, alg), \
+ PSEUDO(mnemonic "a", base, ABSOLUTE_ADDRESS_BIT, alg), \
+ PSEUDO(mnemonic "la", base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg), \
+ PSEUDO(mnemonic "-", base, PLAIN_MNEMONIC, alg), PSEUDO(mnemonic "l-", base, LINK_BIT, alg), \
+ PSEUDO(mnemonic "a-", base, ABSOLUTE_ADDRESS_BIT, alg), \
+ PSEUDO(mnemonic "la-", base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg), \
+ PSEUDO(mnemonic "+", base##Predict, PLAIN_MNEMONIC, alg), \
+ PSEUDO(mnemonic "l+", base##Predict, LINK_BIT, alg), \
+ PSEUDO(mnemonic "a+", base##Predict, ABSOLUTE_ADDRESS_BIT, alg), \
+ PSEUDO(mnemonic "la+", base##Predict, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), alg)
+
+extern const CaseInsensitiveDict<ParseInfo, '.', '_', '+', '-'> extended_mnemonic_tokens = {
+ PLAIN_PSEUDO("subi", ExtendedGekkoMnemonic::Subi, ParseAlg::Op3),
+ PLAIN_PSEUDO("subis", ExtendedGekkoMnemonic::Subis, ParseAlg::Op3),
+ PLAIN_PSEUDO("subic", ExtendedGekkoMnemonic::Subic, ParseAlg::Op3),
+ PLAIN_PSEUDO("subic.", ExtendedGekkoMnemonic::SubicDot, ParseAlg::Op3),
+ OERC_PSEUDO("sub", ExtendedGekkoMnemonic::Sub, ParseAlg::Op3),
+ OERC_PSEUDO("subc", ExtendedGekkoMnemonic::Subc, ParseAlg::Op3),
+ PLAIN_PSEUDO("cmpwi", ExtendedGekkoMnemonic::Cmpwi, ParseAlg::Op2Or3),
+ PLAIN_PSEUDO("cmpw", ExtendedGekkoMnemonic::Cmpw, ParseAlg::Op2Or3),
+ PLAIN_PSEUDO("cmplwi", ExtendedGekkoMnemonic::Cmplwi, ParseAlg::Op2Or3),
+ PLAIN_PSEUDO("cmplw", ExtendedGekkoMnemonic::Cmplw, ParseAlg::Op2Or3),
+ RC_PSEUDO("extlwi", ExtendedGekkoMnemonic::Extlwi, ParseAlg::Op4),
+ RC_PSEUDO("extrwi", ExtendedGekkoMnemonic::Extrwi, ParseAlg::Op4),
+ RC_PSEUDO("inslwi", ExtendedGekkoMnemonic::Inslwi, ParseAlg::Op4),
+ RC_PSEUDO("insrwi", ExtendedGekkoMnemonic::Insrwi, ParseAlg::Op4),
+ RC_PSEUDO("rotlwi", ExtendedGekkoMnemonic::Rotlwi, ParseAlg::Op3),
+ RC_PSEUDO("rotrwi", ExtendedGekkoMnemonic::Rotrwi, ParseAlg::Op3),
+ RC_PSEUDO("rotlw", ExtendedGekkoMnemonic::Rotlw, ParseAlg::Op3),
+ RC_PSEUDO("slwi", ExtendedGekkoMnemonic::Slwi, ParseAlg::Op3),
+ RC_PSEUDO("srwi", ExtendedGekkoMnemonic::Srwi, ParseAlg::Op3),
+ RC_PSEUDO("clrlwi", ExtendedGekkoMnemonic::Clrlwi, ParseAlg::Op3),
+ RC_PSEUDO("clrrwi", ExtendedGekkoMnemonic::Clrrwi, ParseAlg::Op3),
+ RC_PSEUDO("clrlslwi", ExtendedGekkoMnemonic::Clrlslwi, ParseAlg::Op4),
+ LKAAPRED_PSEUDO("bt", ExtendedGekkoMnemonic::Bt, ParseAlg::Op2),
+ LKAAPRED_PSEUDO("bf", ExtendedGekkoMnemonic::Bf, ParseAlg::Op2),
+ LKAAPRED_PSEUDO("bdnz", ExtendedGekkoMnemonic::Bdnz, ParseAlg::Op1),
+ LKAAPRED_PSEUDO("bdnzt", ExtendedGekkoMnemonic::Bdnzt, ParseAlg::Op2),
+ LKAAPRED_PSEUDO("bdnzf", ExtendedGekkoMnemonic::Bdnzf, ParseAlg::Op2),
+ LKAAPRED_PSEUDO("bdz", ExtendedGekkoMnemonic::Bdz, ParseAlg::Op1),
+ LKAAPRED_PSEUDO("bdzt", ExtendedGekkoMnemonic::Bdzt, ParseAlg::Op2),
+ LKAAPRED_PSEUDO("bdzf", ExtendedGekkoMnemonic::Bdzf, ParseAlg::Op2),
+ LK_PSEUDO("blr", ExtendedGekkoMnemonic::Blr, ParseAlg::None),
+ LK_PSEUDO("bctr", ExtendedGekkoMnemonic::Bctr, ParseAlg::None),
+ LKPRED_PSEUDO("btlr", ExtendedGekkoMnemonic::Btlr, ParseAlg::Op1),
+ LKPRED_PSEUDO("btctr", ExtendedGekkoMnemonic::Btctr, ParseAlg::Op1),
+ LKPRED_PSEUDO("bflr", ExtendedGekkoMnemonic::Bflr, ParseAlg::Op1),
+ LKPRED_PSEUDO("bfctr", ExtendedGekkoMnemonic::Bfctr, ParseAlg::Op1),
+ LKPRED_PSEUDO("bdnzlr", ExtendedGekkoMnemonic::Bdnzlr, ParseAlg::None),
+ LKPRED_PSEUDO("bdnztlr", ExtendedGekkoMnemonic::Bdnztlr, ParseAlg::Op1),
+ LKPRED_PSEUDO("bdnzflr", ExtendedGekkoMnemonic::Bdnzflr, ParseAlg::Op1),
+ LKPRED_PSEUDO("bdzlr", ExtendedGekkoMnemonic::Bdzlr, ParseAlg::None),
+ LKPRED_PSEUDO("bdztlr", ExtendedGekkoMnemonic::Bdztlr, ParseAlg::Op1),
+ LKPRED_PSEUDO("bdzflr", ExtendedGekkoMnemonic::Bdzflr, ParseAlg::Op1),
+ LKAAPRED_PSEUDO("blt", ExtendedGekkoMnemonic::Blt, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("ble", ExtendedGekkoMnemonic::Ble, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("beq", ExtendedGekkoMnemonic::Beq, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bge", ExtendedGekkoMnemonic::Bge, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bgt", ExtendedGekkoMnemonic::Bgt, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bnl", ExtendedGekkoMnemonic::Bnl, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bne", ExtendedGekkoMnemonic::Bne, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bng", ExtendedGekkoMnemonic::Bng, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bso", ExtendedGekkoMnemonic::Bso, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bns", ExtendedGekkoMnemonic::Bns, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bun", ExtendedGekkoMnemonic::Bun, ParseAlg::Op1Or2),
+ LKAAPRED_PSEUDO("bnu", ExtendedGekkoMnemonic::Bnu, ParseAlg::Op1Or2),
+ LKPRED_PSEUDO("bltlr", ExtendedGekkoMnemonic::Bltlr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bltctr", ExtendedGekkoMnemonic::Bltctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("blelr", ExtendedGekkoMnemonic::Blelr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("blectr", ExtendedGekkoMnemonic::Blectr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("beqlr", ExtendedGekkoMnemonic::Beqlr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("beqctr", ExtendedGekkoMnemonic::Beqctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bgelr", ExtendedGekkoMnemonic::Bgelr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bgectr", ExtendedGekkoMnemonic::Bgectr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bgtlr", ExtendedGekkoMnemonic::Bgtlr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bgtctr", ExtendedGekkoMnemonic::Bgtctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnllr", ExtendedGekkoMnemonic::Bnllr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnlctr", ExtendedGekkoMnemonic::Bnlctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnelr", ExtendedGekkoMnemonic::Bnelr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnectr", ExtendedGekkoMnemonic::Bnectr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnglr", ExtendedGekkoMnemonic::Bnglr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bngctr", ExtendedGekkoMnemonic::Bngctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bsolr", ExtendedGekkoMnemonic::Bsolr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bsoctr", ExtendedGekkoMnemonic::Bsoctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnslr", ExtendedGekkoMnemonic::Bnslr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnsctr", ExtendedGekkoMnemonic::Bnsctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bunlr", ExtendedGekkoMnemonic::Bunlr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bunctr", ExtendedGekkoMnemonic::Bunctr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnulr", ExtendedGekkoMnemonic::Bnulr, ParseAlg::NoneOrOp1),
+ LKPRED_PSEUDO("bnuctr", ExtendedGekkoMnemonic::Bnuctr, ParseAlg::NoneOrOp1),
+ PLAIN_PSEUDO("crset", ExtendedGekkoMnemonic::Crset, ParseAlg::Op1),
+ PLAIN_PSEUDO("crclr", ExtendedGekkoMnemonic::Crclr, ParseAlg::Op1),
+ PLAIN_PSEUDO("crmove", ExtendedGekkoMnemonic::Crmove, ParseAlg::Op2),
+ PLAIN_PSEUDO("crnot", ExtendedGekkoMnemonic::Crnot, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlt", ExtendedGekkoMnemonic::Twlt, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlti", ExtendedGekkoMnemonic::Twlti, ParseAlg::Op2),
+ PLAIN_PSEUDO("twle", ExtendedGekkoMnemonic::Twle, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlei", ExtendedGekkoMnemonic::Twlei, ParseAlg::Op2),
+ PLAIN_PSEUDO("tweq", ExtendedGekkoMnemonic::Tweq, ParseAlg::Op2),
+ PLAIN_PSEUDO("tweqi", ExtendedGekkoMnemonic::Tweqi, ParseAlg::Op2),
+ PLAIN_PSEUDO("twge", ExtendedGekkoMnemonic::Twge, ParseAlg::Op2),
+ PLAIN_PSEUDO("twgei", ExtendedGekkoMnemonic::Twgei, ParseAlg::Op2),
+ PLAIN_PSEUDO("twgt", ExtendedGekkoMnemonic::Twgt, ParseAlg::Op2),
+ PLAIN_PSEUDO("twgti", ExtendedGekkoMnemonic::Twgti, ParseAlg::Op2),
+ PLAIN_PSEUDO("twnl", ExtendedGekkoMnemonic::Twnl, ParseAlg::Op2),
+ PLAIN_PSEUDO("twnli", ExtendedGekkoMnemonic::Twnli, ParseAlg::Op2),
+ PLAIN_PSEUDO("twne", ExtendedGekkoMnemonic::Twne, ParseAlg::Op2),
+ PLAIN_PSEUDO("twnei", ExtendedGekkoMnemonic::Twnei, ParseAlg::Op2),
+ PLAIN_PSEUDO("twng", ExtendedGekkoMnemonic::Twng, ParseAlg::Op2),
+ PLAIN_PSEUDO("twngi", ExtendedGekkoMnemonic::Twngi, ParseAlg::Op2),
+ PLAIN_PSEUDO("twllt", ExtendedGekkoMnemonic::Twllt, ParseAlg::Op2),
+ PLAIN_PSEUDO("twllti", ExtendedGekkoMnemonic::Twllti, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlle", ExtendedGekkoMnemonic::Twlle, ParseAlg::Op2),
+ PLAIN_PSEUDO("twllei", ExtendedGekkoMnemonic::Twllei, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlge", ExtendedGekkoMnemonic::Twlge, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlgei", ExtendedGekkoMnemonic::Twlgei, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlgt", ExtendedGekkoMnemonic::Twlgt, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlgti", ExtendedGekkoMnemonic::Twlgti, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlnl", ExtendedGekkoMnemonic::Twlnl, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlnli", ExtendedGekkoMnemonic::Twlnli, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlng", ExtendedGekkoMnemonic::Twlng, ParseAlg::Op2),
+ PLAIN_PSEUDO("twlngi", ExtendedGekkoMnemonic::Twlngi, ParseAlg::Op2),
+ PLAIN_PSEUDO("trap", ExtendedGekkoMnemonic::Trap, ParseAlg::None),
+ PLAIN_PSEUDO("mtxer", ExtendedGekkoMnemonic::Mtxer, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfxer", ExtendedGekkoMnemonic::Mfxer, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtlr", ExtendedGekkoMnemonic::Mtlr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mflr", ExtendedGekkoMnemonic::Mflr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtctr", ExtendedGekkoMnemonic::Mtctr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfctr", ExtendedGekkoMnemonic::Mfctr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtdsisr", ExtendedGekkoMnemonic::Mtdsisr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfdsisr", ExtendedGekkoMnemonic::Mfdsisr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtdar", ExtendedGekkoMnemonic::Mtdar, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfdar", ExtendedGekkoMnemonic::Mfdar, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtdec", ExtendedGekkoMnemonic::Mtdec, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfdec", ExtendedGekkoMnemonic::Mfdec, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtsdr1", ExtendedGekkoMnemonic::Mtsdr1, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfsdr1", ExtendedGekkoMnemonic::Mfsdr1, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtsrr0", ExtendedGekkoMnemonic::Mtsrr0, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfsrr0", ExtendedGekkoMnemonic::Mfsrr0, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtsrr1", ExtendedGekkoMnemonic::Mtsrr1, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfsrr1", ExtendedGekkoMnemonic::Mfsrr1, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtasr", ExtendedGekkoMnemonic::Mtasr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfasr", ExtendedGekkoMnemonic::Mfasr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtear", ExtendedGekkoMnemonic::Mtear, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfear", ExtendedGekkoMnemonic::Mfear, ParseAlg::Op1),
+ PLAIN_PSEUDO("mttbl", ExtendedGekkoMnemonic::Mttbl, ParseAlg::Op1),
+ PLAIN_PSEUDO("mftbl", ExtendedGekkoMnemonic::Mftbl, ParseAlg::Op1),
+ PLAIN_PSEUDO("mttbu", ExtendedGekkoMnemonic::Mttbu, ParseAlg::Op1),
+ PLAIN_PSEUDO("mftbu", ExtendedGekkoMnemonic::Mftbu, ParseAlg::Op1),
+ PLAIN_PSEUDO("mtsprg", ExtendedGekkoMnemonic::Mtsprg, ParseAlg::Op2),
+ PLAIN_PSEUDO("mfsprg", ExtendedGekkoMnemonic::Mfsprg, ParseAlg::Op2),
+ PLAIN_PSEUDO("mtibatu", ExtendedGekkoMnemonic::Mtibatu, ParseAlg::Op2),
+ PLAIN_PSEUDO("mfibatu", ExtendedGekkoMnemonic::Mfibatu, ParseAlg::Op2),
+ PLAIN_PSEUDO("mtibatl", ExtendedGekkoMnemonic::Mtibatl, ParseAlg::Op2),
+ PLAIN_PSEUDO("mfibatl", ExtendedGekkoMnemonic::Mfibatl, ParseAlg::Op2),
+ PLAIN_PSEUDO("mtdbatu", ExtendedGekkoMnemonic::Mtdbatu, ParseAlg::Op2),
+ PLAIN_PSEUDO("mfdbatu", ExtendedGekkoMnemonic::Mfdbatu, ParseAlg::Op2),
+ PLAIN_PSEUDO("mtdbatl", ExtendedGekkoMnemonic::Mtdbatl, ParseAlg::Op2),
+ PLAIN_PSEUDO("mfdbatl", ExtendedGekkoMnemonic::Mfdbatl, ParseAlg::Op2),
+ PLAIN_PSEUDO("nop", ExtendedGekkoMnemonic::Nop, ParseAlg::None),
+ PLAIN_PSEUDO("li", ExtendedGekkoMnemonic::Li, ParseAlg::Op2),
+ PLAIN_PSEUDO("lis", ExtendedGekkoMnemonic::Lis, ParseAlg::Op2),
+ PLAIN_PSEUDO("la", ExtendedGekkoMnemonic::La, ParseAlg::Op1Off1),
+ RC_PSEUDO("mr", ExtendedGekkoMnemonic::Mr, ParseAlg::Op2),
+ RC_PSEUDO("not", ExtendedGekkoMnemonic::Not, ParseAlg::Op2),
+ PLAIN_PSEUDO("mtcr", ExtendedGekkoMnemonic::Mtcr, ParseAlg::Op1),
+ PLAIN_PSEUDO("mfspr", ExtendedGekkoMnemonic::Mfspr, ParseAlg::Op2),
+ PLAIN_PSEUDO("mftb", ExtendedGekkoMnemonic::Mftb, ParseAlg::Op2),
+ PLAIN_PSEUDO("mtspr", ExtendedGekkoMnemonic::Mtspr, ParseAlg::Op2),
+};
+
+#undef MNEMONIC
+#undef PLAIN_MNEMONIC
+#undef RC_MNEMONIC
+#undef OERC_MNEMONIC
+#undef LK_MNEMONIC
+#undef AALK_MNEMONIC
+#undef PSEUDO
+#undef PLAIN_PSEUDO
+#undef RC_PSEUDO
+#undef OERC_PSEUDO
+#undef LK_PSEUDO
+#undef LKAA_PSEUDO
+#undef LKPRED_PSEUDO
+#undef LKAAPRED_PSEUDO
+
+//////////////////////
+// ASSEMBLER TABLES //
+//////////////////////
+#define EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, ...) \
+ MnemonicDesc \
+ { \
+ InsertOpcode(opcode_val) | (extra_bits), \
+ static_cast<u32>(std::initializer_list<OperandDesc>{__VA_ARGS__}.size()), \
+ { \
+ __VA_ARGS__ \
+ } \
+ }
+#define MNEMONIC(opcode_val, extra_bits, ...) \
+ EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), INVALID_MNEMONIC, INVALID_MNEMONIC, \
+ INVALID_MNEMONIC
+#define BASIC_MNEMONIC(opcode_val, ...) MNEMONIC(opcode_val, 0, __VA_ARGS__)
+#define RC_MNEMONIC(opcode_val, extra_bits, ...) \
+ EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 31, 31)), __VA_ARGS__), \
+ INVALID_MNEMONIC, INVALID_MNEMONIC
+#define OERC_MNEMONIC(opcode_val, extra_bits, ...) \
+ EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 31, 31)), __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 21, 21)), __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY( \
+ opcode_val, ((extra_bits) | InsertVal(1, 31, 31) | InsertVal(1, 21, 21)), __VA_ARGS__)
+#define LK_MNEMONIC(opcode_val, extra_bits, ...) \
+ EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(1, 31, 31)), __VA_ARGS__), \
+ INVALID_MNEMONIC, INVALID_MNEMONIC
+#define AALK_MNEMONIC(opcode_val, extra_bits, ...) \
+ EMIT_MNEMONIC_ENTRY(opcode_val, extra_bits, __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(0b01, 30, 31)), __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(0b10, 30, 31)), __VA_ARGS__), \
+ EMIT_MNEMONIC_ENTRY(opcode_val, ((extra_bits) | InsertVal(0b11, 30, 31)), __VA_ARGS__)
+
+// Defines all basic mnemonics that Broadway/Gekko supports
+extern const std::array<MnemonicDesc, NUM_MNEMONICS* VARIANT_PERMUTATIONS> mnemonics = {
+ // A-2
+ OERC_MNEMONIC(31, InsertVal(266, 22, 30), _D, _A, _B), // add
+ OERC_MNEMONIC(31, InsertVal(10, 22, 30), _D, _A, _B), // addc
+ OERC_MNEMONIC(31, InsertVal(138, 22, 30), _D, _A, _B), // adde
+ BASIC_MNEMONIC(14, _D, _A, _SIMM), // addi
+ BASIC_MNEMONIC(12, _D, _A, _SIMM), // addic
+ BASIC_MNEMONIC(13, _D, _A, _SIMM), // addic.
+ BASIC_MNEMONIC(15, _D, _A, _SIMM), // addis
+ OERC_MNEMONIC(31, InsertVal(234, 22, 30), _D, _A), // addme
+ OERC_MNEMONIC(31, InsertVal(202, 22, 30), _D, _A), // addze
+ OERC_MNEMONIC(31, InsertVal(491, 22, 30), _D, _A, _B), // divw
+ OERC_MNEMONIC(31, InsertVal(459, 22, 30), _D, _A, _B), // divwu
+ RC_MNEMONIC(31, InsertVal(75, 22, 30), _D, _A, _B), // mulhw
+ RC_MNEMONIC(31, InsertVal(11, 22, 30), _D, _A, _B), // mulhwu
+ BASIC_MNEMONIC(7, _D, _A, _SIMM), // mulli
+ OERC_MNEMONIC(31, InsertVal(235, 22, 30), _D, _A, _B), // mullw
+ OERC_MNEMONIC(31, InsertVal(104, 22, 30), _D, _A), // neg
+ OERC_MNEMONIC(31, InsertVal(40, 22, 30), _D, _A, _B), // subf
+ OERC_MNEMONIC(31, InsertVal(8, 22, 30), _D, _A, _B), // subfc
+ OERC_MNEMONIC(31, InsertVal(136, 22, 30), _D, _A, _B), // subfe
+ BASIC_MNEMONIC(8, _D, _A, _SIMM), // subfic
+ OERC_MNEMONIC(31, InsertVal(232, 22, 30), _D, _A), // subfme
+ OERC_MNEMONIC(31, InsertVal(200, 22, 30), _D, _A), // subfze
+
+ // A-3
+ MNEMONIC(31, InsertVal(0, 21, 30), _Crfd, _L, _A, _B), // cmp
+ BASIC_MNEMONIC(11, _Crfd, _L, _A, _SIMM), // cmpi
+ MNEMONIC(31, InsertVal(32, 21, 30), _Crfd, _L, _A, _B), // cmpl
+ BASIC_MNEMONIC(10, _Crfd, _L, _A, _UIMM), // cmpli
+
+ // A-4
+ RC_MNEMONIC(31, InsertVal(28, 21, 30), _A, _S, _B), // and
+ RC_MNEMONIC(31, InsertVal(60, 21, 30), _A, _S, _B), // andc
+ BASIC_MNEMONIC(28, _A, _S, _UIMM), // andi.
+ BASIC_MNEMONIC(29, _A, _S, _UIMM), // andis.
+ RC_MNEMONIC(31, InsertVal(26, 21, 30), _A, _S), // cntlzw
+ RC_MNEMONIC(31, InsertVal(284, 21, 30), _A, _S, _B), // eqv
+ RC_MNEMONIC(31, InsertVal(954, 21, 30), _A, _S), // extsb
+ RC_MNEMONIC(31, InsertVal(922, 21, 30), _A, _S), // extsh
+ RC_MNEMONIC(31, InsertVal(476, 21, 30), _A, _S, _B), // nand
+ RC_MNEMONIC(31, InsertVal(124, 21, 30), _A, _S, _B), // nor
+ RC_MNEMONIC(31, InsertVal(444, 21, 30), _A, _S, _B), // or
+ RC_MNEMONIC(31, InsertVal(412, 21, 30), _A, _S, _B), // orc
+ BASIC_MNEMONIC(24, _A, _S, _UIMM), // ori
+ BASIC_MNEMONIC(25, _A, _S, _UIMM), // oris
+ RC_MNEMONIC(31, InsertVal(316, 21, 30), _A, _S, _B), // xor
+ BASIC_MNEMONIC(26, _A, _S, _UIMM), // xori
+ BASIC_MNEMONIC(27, _A, _S, _UIMM), // xoris
+
+ // A-5
+ RC_MNEMONIC(20, 0, _A, _S, _SH, _MB, _ME), // rlwimi
+ RC_MNEMONIC(21, 0, _A, _S, _SH, _MB, _ME), // rlwinm
+ RC_MNEMONIC(23, 0, _A, _S, _B, _MB, _ME), // rlwnm
+
+ // A-6
+ RC_MNEMONIC(31, InsertVal(24, 21, 30), _A, _S, _B), // slw
+ RC_MNEMONIC(31, InsertVal(792, 21, 30), _A, _S, _B), // sraw
+ RC_MNEMONIC(31, InsertVal(824, 21, 30), _A, _S, _SH), // srawi
+ RC_MNEMONIC(31, InsertVal(536, 21, 30), _A, _S, _B), // srw
+
+ // A-7
+ RC_MNEMONIC(63, InsertVal(21, 26, 30), _D, _A, _B), // fadd
+ RC_MNEMONIC(59, InsertVal(21, 26, 30), _D, _A, _B), // fadds
+ RC_MNEMONIC(63, InsertVal(18, 26, 30), _D, _A, _B), // fdiv
+ RC_MNEMONIC(59, InsertVal(18, 26, 30), _D, _A, _B), // fdivs
+ RC_MNEMONIC(63, InsertVal(25, 26, 30), _D, _A, _C), // fmul
+ RC_MNEMONIC(59, InsertVal(25, 26, 30), _D, _A, _C), // fmuls
+ RC_MNEMONIC(59, InsertVal(24, 26, 30), _D, _B), // fres
+ RC_MNEMONIC(63, InsertVal(26, 26, 30), _D, _B), // frsqrte
+ RC_MNEMONIC(63, InsertVal(20, 26, 30), _D, _A, _B), // fsub
+ RC_MNEMONIC(59, InsertVal(20, 26, 30), _D, _A, _B), // fsubs
+ RC_MNEMONIC(63, InsertVal(23, 26, 30), _D, _A, _C, _B), // fsel
+
+ // A-8
+ RC_MNEMONIC(63, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadd
+ RC_MNEMONIC(59, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadds
+ RC_MNEMONIC(63, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsub
+ RC_MNEMONIC(59, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsubs
+ RC_MNEMONIC(63, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadd
+ RC_MNEMONIC(59, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadds
+ RC_MNEMONIC(63, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsub
+ RC_MNEMONIC(59, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsubs
+
+ // A-9
+ RC_MNEMONIC(63, InsertVal(14, 21, 30), _D, _B), // fctiw
+ RC_MNEMONIC(63, InsertVal(15, 21, 30), _D, _B), // fctiwz
+ RC_MNEMONIC(63, InsertVal(12, 21, 30), _D, _B), // frsp
+
+ // A-10
+ MNEMONIC(63, InsertVal(32, 21, 30), _Crfd, _A, _B), // fcmpo
+ MNEMONIC(63, InsertVal(0, 21, 30), _Crfd, _A, _B), // fcmpu
+
+ // A-11
+ MNEMONIC(63, InsertVal(64, 21, 30), _Crfd, _Crfs), // mcrfs
+ RC_MNEMONIC(63, InsertVal(583, 21, 30), _D), // mffs
+ RC_MNEMONIC(63, InsertVal(70, 21, 30), _Crbd), // mtfsb0
+ RC_MNEMONIC(63, InsertVal(38, 21, 30), _Crbd), // mtfsb1
+ RC_MNEMONIC(63, InsertVal(711, 21, 30), _FM, _B), // mtfsf
+ RC_MNEMONIC(63, InsertVal(134, 21, 30), _Crfd, _IMM), // mtfsfi
+
+ // A-12
+ BASIC_MNEMONIC(34, _D, _Offd, _A), // lbz
+ BASIC_MNEMONIC(35, _D, _Offd, _A), // lbzu
+ MNEMONIC(31, InsertVal(119, 21, 30), _D, _A, _B), // lbzux
+ MNEMONIC(31, InsertVal(87, 21, 30), _D, _A, _B), // lbzx
+ BASIC_MNEMONIC(42, _D, _Offd, _A), // lha
+ BASIC_MNEMONIC(43, _D, _Offd, _A), // lhau
+ MNEMONIC(31, InsertVal(375, 21, 30), _D, _A, _B), // lhaux
+ MNEMONIC(31, InsertVal(343, 21, 30), _D, _A, _B), // lhax
+ BASIC_MNEMONIC(40, _D, _Offd, _A), // lhz
+ BASIC_MNEMONIC(41, _D, _Offd, _A), // lhzu
+ MNEMONIC(31, InsertVal(311, 21, 30), _D, _A, _B), // lhzux
+ MNEMONIC(31, InsertVal(279, 21, 30), _D, _A, _B), // lhzx
+ BASIC_MNEMONIC(32, _D, _Offd, _A), // lwz
+ BASIC_MNEMONIC(33, _D, _Offd, _A), // lwzu
+ MNEMONIC(31, InsertVal(55, 21, 30), _D, _A, _B), // lwzux
+ MNEMONIC(31, InsertVal(23, 21, 30), _D, _A, _B), // lwzx
+
+ // A-13
+ BASIC_MNEMONIC(38, _S, _Offd, _A), // stb
+ BASIC_MNEMONIC(39, _S, _Offd, _A), // stbu
+ MNEMONIC(31, InsertVal(247, 21, 30), _S, _A, _B), // stbux
+ MNEMONIC(31, InsertVal(215, 21, 30), _S, _A, _B), // stbx
+ BASIC_MNEMONIC(44, _S, _Offd, _A), // sth
+ BASIC_MNEMONIC(45, _S, _Offd, _A), // sthu
+ MNEMONIC(31, InsertVal(439, 21, 30), _S, _A, _B), // sthux
+ MNEMONIC(31, InsertVal(407, 21, 30), _S, _A, _B), // sthx
+ BASIC_MNEMONIC(36, _S, _Offd, _A), // stw
+ BASIC_MNEMONIC(37, _S, _Offd, _A), // stwu
+ MNEMONIC(31, InsertVal(183, 21, 30), _S, _A, _B), // stwux
+ MNEMONIC(31, InsertVal(151, 21, 30), _S, _A, _B), // stwx
+
+ // A-14
+ MNEMONIC(31, InsertVal(790, 21, 30), _D, _A, _B), // lhbrx
+ MNEMONIC(31, InsertVal(534, 21, 30), _D, _A, _B), // lwbrx
+ MNEMONIC(31, InsertVal(918, 21, 30), _S, _A, _B), // sthbrx
+ MNEMONIC(31, InsertVal(662, 21, 30), _S, _A, _B), // stwbrx
+
+ // A-15
+ BASIC_MNEMONIC(46, _D, _Offd, _A), // lmw
+ BASIC_MNEMONIC(47, _S, _Offd, _A), // stmw
+
+ // A-16
+ MNEMONIC(31, InsertVal(597, 21, 30), _D, _A, _NB), // lswi
+ MNEMONIC(31, InsertVal(533, 21, 30), _D, _A, _B), // lswx
+ MNEMONIC(31, InsertVal(725, 21, 30), _S, _A, _NB), // stswi
+ MNEMONIC(31, InsertVal(661, 21, 30), _S, _A, _B), // stswx
+
+ // A-17
+ MNEMONIC(31, InsertVal(854, 21, 30)), // eieio
+ MNEMONIC(19, InsertVal(150, 21, 30)), // isync
+ MNEMONIC(31, InsertVal(20, 21, 30), _D, _A, _B), // lwarx
+ MNEMONIC(31, InsertVal(150, 21, 30) | InsertVal(1, 31, 31), _S, _A, _B), // stwcx.
+ MNEMONIC(31, InsertVal(598, 21, 30)), // sync
+
+ // A-18
+ BASIC_MNEMONIC(50, _D, _Offd, _A), // lfd
+ BASIC_MNEMONIC(51, _D, _Offd, _A), // lfdu
+ MNEMONIC(31, InsertVal(631, 21, 30), _D, _A, _B), // lfdux
+ MNEMONIC(31, InsertVal(599, 21, 30), _D, _A, _B), // lfdx
+ BASIC_MNEMONIC(48, _D, _Offd, _A), // lfs
+ BASIC_MNEMONIC(49, _D, _Offd, _A), // lfsu
+ MNEMONIC(31, InsertVal(567, 21, 30), _D, _A, _B), // lfsux
+ MNEMONIC(31, InsertVal(535, 21, 30), _D, _A, _B), // lfsx
+
+ // A-19
+ BASIC_MNEMONIC(54, _S, _Offd, _A), // stfd
+ BASIC_MNEMONIC(55, _S, _Offd, _A), // stfdu
+ MNEMONIC(31, InsertVal(759, 21, 30), _S, _A, _B), // stfdux
+ MNEMONIC(31, InsertVal(727, 21, 30), _S, _A, _B), // stfdx
+ MNEMONIC(31, InsertVal(983, 21, 30), _S, _A, _B), // stfiwx
+ BASIC_MNEMONIC(52, _S, _Offd, _A), // stfs
+ BASIC_MNEMONIC(53, _S, _Offd, _A), // stfsu
+ MNEMONIC(31, InsertVal(695, 21, 30), _S, _A, _B), // stfsux
+ MNEMONIC(31, InsertVal(663, 21, 30), _S, _A, _B), // stfsx
+
+ // A-20
+ RC_MNEMONIC(63, InsertVal(264, 21, 30), _D, _B), // fabs
+ RC_MNEMONIC(63, InsertVal(72, 21, 30), _D, _B), // fmr
+ RC_MNEMONIC(63, InsertVal(136, 21, 30), _D, _B), // fnabs
+ RC_MNEMONIC(63, InsertVal(40, 21, 30), _D, _B), // fneg
+
+ // A-21
+ AALK_MNEMONIC(18, 0, _LI), // b
+ AALK_MNEMONIC(16, 0, _BO, _BI, _BD), // bc
+ LK_MNEMONIC(19, InsertVal(528, 21, 30), _BO, _BI), // bcctr
+ LK_MNEMONIC(19, InsertVal(16, 21, 30), _BO, _BI), // bclr
+
+ // A-22
+ MNEMONIC(19, InsertVal(257, 21, 30), _Crbd, _Crba, _Crbb), // crand
+ MNEMONIC(19, InsertVal(129, 21, 30), _Crbd, _Crba, _Crbb), // crandc
+ MNEMONIC(19, InsertVal(289, 21, 30), _Crbd, _Crba, _Crbb), // creqv
+ MNEMONIC(19, InsertVal(225, 21, 30), _Crbd, _Crba, _Crbb), // crnand
+ MNEMONIC(19, InsertVal(33, 21, 30), _Crbd, _Crba, _Crbb), // crnor
+ MNEMONIC(19, InsertVal(449, 21, 30), _Crbd, _Crba, _Crbb), // cror
+ MNEMONIC(19, InsertVal(417, 21, 30), _Crbd, _Crba, _Crbb), // crorc
+ MNEMONIC(19, InsertVal(193, 21, 30), _Crbd, _Crba, _Crbb), // crxor
+ MNEMONIC(19, InsertVal(0, 21, 30), _Crfd, _Crfs), // mcrf
+
+ // A-23
+ MNEMONIC(19, InsertVal(50, 21, 30)), // rfi
+ MNEMONIC(17, InsertVal(1, 30, 30)), // sc
+
+ // A-24
+ MNEMONIC(31, InsertVal(4, 21, 30), _TO, _A, _B), // tw
+ BASIC_MNEMONIC(3, _TO, _A, _SIMM), // twi
+
+ // A-25
+ MNEMONIC(31, InsertVal(512, 21, 30), _Crfd), // mcrxr
+ MNEMONIC(31, InsertVal(19, 21, 30), _D), // mfcr
+ MNEMONIC(31, InsertVal(83, 21, 30), _D), // mfmsr
+ MNEMONIC(31, InsertVal(339, 21, 30), _D, _SPR), // mfspr
+ MNEMONIC(31, InsertVal(371, 21, 30), _D, _TPR), // mftb
+ MNEMONIC(31, InsertVal(144, 21, 30), _CRM, _S), // mtcrf
+ MNEMONIC(31, InsertVal(146, 21, 30), _S), // mtmsr
+ MNEMONIC(31, InsertVal(467, 21, 30), _SPR, _D), // mtspr
+
+ // A-26
+ MNEMONIC(31, InsertVal(86, 21, 30), _A, _B), // dcbf
+ MNEMONIC(31, InsertVal(470, 21, 30), _A, _B), // dcbi
+ MNEMONIC(31, InsertVal(54, 21, 30), _A, _B), // dcbst
+ MNEMONIC(31, InsertVal(278, 21, 30), _A, _B), // dcbt
+ MNEMONIC(31, InsertVal(246, 21, 30), _A, _B), // dcbtst
+ MNEMONIC(31, InsertVal(1014, 21, 30), _A, _B), // dcbz
+ MNEMONIC(31, InsertVal(982, 21, 30), _A, _B), // icbi
+
+ // A-27
+ MNEMONIC(31, InsertVal(595, 21, 30), _D, _SR), // mfsr
+ MNEMONIC(31, InsertVal(659, 21, 30), _D, _B), // mfsrin
+ MNEMONIC(31, InsertVal(210, 21, 30), _SR, _S), // mtsr
+ MNEMONIC(31, InsertVal(242, 21, 30), _S, _B), // mtsrin
+
+ // A-28
+ MNEMONIC(31, InsertVal(306, 21, 30), _B), // tlbie
+ MNEMONIC(31, InsertVal(566, 21, 30)), // tlbsync
+
+ // A-29
+ MNEMONIC(31, InsertVal(310, 21, 30), _D, _A, _B), // eciwx
+ MNEMONIC(31, InsertVal(438, 21, 30), _S, _A, _B), // ecowx
+
+ // A-30
+ MNEMONIC(4, InsertVal(6, 25, 30), _D, _A, _B, _W2, _I2), // psq_lx
+ MNEMONIC(4, InsertVal(7, 25, 30), _S, _A, _B, _W2, _I2), // psq_stx
+ MNEMONIC(4, InsertVal(38, 25, 30), _D, _A, _B, _W2, _I2), // psq_lux
+ MNEMONIC(4, InsertVal(39, 25, 30), _S, _A, _B, _W2, _I2), // psq_stux
+ BASIC_MNEMONIC(56, _D, _OffdPs, _A, _W1, _I1), // psq_l
+ BASIC_MNEMONIC(57, _D, _OffdPs, _A, _W1, _I1), // psq_lu
+ BASIC_MNEMONIC(60, _S, _OffdPs, _A, _W1, _I1), // psq_st
+ BASIC_MNEMONIC(61, _S, _OffdPs, _A, _W1, _I1), // psq_stu
+
+ // A-31
+ RC_MNEMONIC(4, InsertVal(18, 26, 30), _D, _A, _B), // ps_div
+ RC_MNEMONIC(4, InsertVal(20, 26, 30), _D, _A, _B), // ps_sub
+ RC_MNEMONIC(4, InsertVal(21, 26, 30), _D, _A, _B), // ps_add
+ RC_MNEMONIC(4, InsertVal(23, 26, 30), _D, _A, _C, _B), // ps_sel
+ RC_MNEMONIC(4, InsertVal(24, 26, 30), _D, _B), // ps_res
+ RC_MNEMONIC(4, InsertVal(25, 26, 30), _D, _A, _C), // ps_mul
+ RC_MNEMONIC(4, InsertVal(26, 26, 30), _D, _B), // ps_rsqrte
+ RC_MNEMONIC(4, InsertVal(28, 26, 30), _D, _A, _C, _B), // ps_msub
+ RC_MNEMONIC(4, InsertVal(29, 26, 30), _D, _A, _C, _B), // ps_madd
+ RC_MNEMONIC(4, InsertVal(30, 26, 30), _D, _A, _C, _B), // ps_nmsub
+ RC_MNEMONIC(4, InsertVal(31, 26, 30), _D, _A, _C, _B), // ps_nmadd
+ RC_MNEMONIC(4, InsertVal(40, 21, 30), _D, _B), // ps_neg
+ RC_MNEMONIC(4, InsertVal(72, 21, 30), _D, _B), // ps_mr
+ RC_MNEMONIC(4, InsertVal(136, 21, 30), _D, _B), // ps_nabs
+ RC_MNEMONIC(4, InsertVal(264, 21, 30), _D, _B), // ps_abs
+
+ // A-32
+ RC_MNEMONIC(4, InsertVal(10, 26, 30), _D, _A, _C, _B), // ps_sum0
+ RC_MNEMONIC(4, InsertVal(11, 26, 30), _D, _A, _C, _B), // ps_sum1
+ RC_MNEMONIC(4, InsertVal(12, 26, 30), _D, _A, _C), // ps_muls0
+ RC_MNEMONIC(4, InsertVal(13, 26, 30), _D, _A, _C), // ps_muls1
+ RC_MNEMONIC(4, InsertVal(14, 26, 30), _D, _A, _C, _B), // ps_madds0
+ RC_MNEMONIC(4, InsertVal(15, 26, 30), _D, _A, _C, _B), // ps_madds1
+ MNEMONIC(4, InsertVal(0, 21, 30), _Crfd, _A, _B), // ps_cmpu0
+ MNEMONIC(4, InsertVal(32, 21, 30), _Crfd, _A, _B), // ps_cmpo0
+ MNEMONIC(4, InsertVal(64, 21, 30), _Crfd, _A, _B), // ps_cmpu1
+ MNEMONIC(4, InsertVal(96, 21, 30), _Crfd, _A, _B), // ps_cmpo1
+ RC_MNEMONIC(4, InsertVal(528, 21, 30), _D, _A, _B), // ps_merge00
+ RC_MNEMONIC(4, InsertVal(560, 21, 30), _D, _A, _B), // ps_merge01
+ RC_MNEMONIC(4, InsertVal(592, 21, 30), _D, _A, _B), // ps_merge10
+ RC_MNEMONIC(4, InsertVal(624, 21, 30), _D, _A, _B), // ps_merge11
+ MNEMONIC(4, InsertVal(1014, 21, 30), _A, _B), // dcbz_l
+};
+
+namespace
+{
+// Reused operand translators for extended mnemonics
+void NegateSIMM(OperandList& operands)
+{
+ operands[2] = static_cast<u32>(-static_cast<s32>(operands[2]));
+}
+
+void SwapOps1And2(OperandList& operands)
+{
+ std::swap(operands[1], operands[2]);
+}
+
+void SetCompareWordMode(OperandList& operands)
+{
+ if (operands.count == 2)
+ {
+ operands.Insert(0, 0);
+ }
+ operands.Insert(1, 0);
+}
+
+template <u32 BO, u32 BI>
+void FillBOBI(OperandList& operands)
+{
+ operands.Insert(0, BO);
+ operands.Insert(1, BI);
+}
+
+template <size_t Idx>
+void BitswapIdx(OperandList& operands)
+{
+ operands[Idx] = SprBitswap(operands[Idx]);
+}
+
+template <u32 BO, u32 Cond, u32 ParamCount>
+void FillBOBICond(OperandList& operands)
+{
+ if (operands.count < ParamCount)
+ {
+ operands.Insert(0, 0);
+ }
+ operands[0] = (operands[0] << 2) | Cond;
+ operands.Insert(0, BO);
+}
+
+template <u32 BO>
+void FillBO(OperandList& operands)
+{
+ operands.Insert(0, BO);
+}
+
+template <u32 TO>
+void TrapSetTO(OperandList& operands)
+{
+ operands.Insert(0, TO);
+}
+
+template <u32 SPRG>
+void FillMtspr(OperandList& operands)
+{
+ operands.Insert(0, SPRG);
+}
+
+template <u32 SPRG>
+void FillMfspr(OperandList& operands)
+{
+ operands.Insert(1, SPRG);
+}
+
+template <u32 SPRG>
+void FillMtsprBatAndBitswap(OperandList& operands)
+{
+ operands[0] = SprBitswap(2 * operands[0] + SPRG);
+}
+
+template <u32 SPRG>
+void FillMfsprBatAndBitswap(OperandList& operands)
+{
+ operands[1] = SprBitswap(2 * operands[1] + SPRG);
+}
+} // namespace
+
+#define PSEUDO(base, variant_bits, cb) \
+ ExtendedMnemonicDesc { static_cast<size_t>(base) * VARIANT_PERMUTATIONS + variant_bits, cb }
+#define PLAIN_PSEUDO(base, cb) \
+ PSEUDO(base, PLAIN_MNEMONIC, cb), INVALID_EXT_MNEMONIC, INVALID_EXT_MNEMONIC, INVALID_EXT_MNEMONIC
+#define RC_PSEUDO(base, cb) \
+ PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, RECORD_BIT, cb), INVALID_EXT_MNEMONIC, \
+ INVALID_EXT_MNEMONIC
+#define OERC_PSEUDO(base, cb) \
+ PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, RECORD_BIT, cb), \
+ PSEUDO(base, OVERFLOW_EXCEPTION, cb), PSEUDO(base, (RECORD_BIT | OVERFLOW_EXCEPTION), cb)
+#define LK_PSEUDO(base, cb) \
+ PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, LINK_BIT, cb), INVALID_EXT_MNEMONIC, \
+ INVALID_EXT_MNEMONIC
+#define LKAA_PSEUDO(base, cb) \
+ PSEUDO(base, PLAIN_MNEMONIC, cb), PSEUDO(base, LINK_BIT, cb), \
+ PSEUDO(base, ABSOLUTE_ADDRESS_BIT, cb), PSEUDO(base, (LINK_BIT | ABSOLUTE_ADDRESS_BIT), cb)
+
+extern const std::array<ExtendedMnemonicDesc, NUM_EXT_MNEMONICS* VARIANT_PERMUTATIONS>
+ extended_mnemonics = {
+ // E.2.1
+ PLAIN_PSEUDO(GekkoMnemonic::Addi, NegateSIMM), // subi
+ PLAIN_PSEUDO(GekkoMnemonic::Addis, NegateSIMM), // subis
+ PLAIN_PSEUDO(GekkoMnemonic::Addic, NegateSIMM), // subic
+ PLAIN_PSEUDO(GekkoMnemonic::AddicDot, NegateSIMM), // subic.
+
+ // E.2.2
+ OERC_PSEUDO(GekkoMnemonic::Subf, SwapOps1And2), // sub
+ OERC_PSEUDO(GekkoMnemonic::Subfc, SwapOps1And2), // subc
+
+ // E.3.2
+ PLAIN_PSEUDO(GekkoMnemonic::Cmpi, SetCompareWordMode), // cmpwi
+ PLAIN_PSEUDO(GekkoMnemonic::Cmp, SetCompareWordMode), // cmpw
+ PLAIN_PSEUDO(GekkoMnemonic::Cmpli, SetCompareWordMode), // cmplwi
+ PLAIN_PSEUDO(GekkoMnemonic::Cmpl, SetCompareWordMode), // cmplw
+
+ // E.4.2
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 n = operands[2], b = operands[3];
+ operands[2] = b;
+ operands[3] = 0;
+ operands.Insert(4, n - 1);
+ })), // extlwi
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 n = operands[2], b = operands[3];
+ operands[2] = b + n;
+ operands[3] = 32 - n;
+ operands.Insert(4, 31);
+ })), // extrwi
+ RC_PSEUDO(GekkoMnemonic::Rlwimi, ([](OperandList& operands) {
+ const u32 n = operands[2], b = operands[3];
+ operands[2] = 32 - b;
+ operands[3] = b;
+ operands.Insert(4, b + n - 1);
+ })), // inslwi
+ RC_PSEUDO(GekkoMnemonic::Rlwimi, ([](OperandList& operands) {
+ const u32 n = operands[2], b = operands[3];
+ operands[2] = 32 - (b + n);
+ operands[3] = b;
+ operands.Insert(4, b + n - 1);
+ })), // insrwi
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ operands.Insert(3, 0);
+ operands.Insert(4, 31);
+ })), // rotlwi
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 n = operands[2];
+ operands[2] = 32 - n;
+ operands.Insert(3, 0);
+ operands.Insert(4, 31);
+ })), // rotrwi
+ RC_PSEUDO(GekkoMnemonic::Rlwnm, ([](OperandList& operands) {
+ operands.Insert(3, 0);
+ operands.Insert(4, 31);
+ })), // rotlw
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 n = operands[2];
+ operands.Insert(3, 0);
+ operands.Insert(4, 31 - n);
+ })), // slwi
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 n = operands[2];
+ operands[2] = 32 - n;
+ operands.Insert(3, n);
+ operands.Insert(4, 31);
+ })), // srwi
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 n = operands[2];
+ operands[2] = 0;
+ operands.Insert(3, n);
+ operands.Insert(4, 31);
+ })), // clrlwi
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 n = operands[2];
+ operands[2] = 0;
+ operands.Insert(3, 0);
+ operands.Insert(4, 31 - n);
+ })), // clrrwi
+ RC_PSEUDO(GekkoMnemonic::Rlwinm, ([](OperandList& operands) {
+ const u32 b = operands[2], n = operands[3];
+ operands[2] = n;
+ operands[3] = b - n;
+ operands.Insert(4, 31 - n);
+ })), // clrlslwi
+
+ // E.5.2
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<12>)), // bt
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<4>)), // bf
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<16, 0>)), // bdnz
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<8>)), // bdnzt
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<0>)), // bdnzf
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<18, 0>)), // bdz
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<10>)), // bdzt
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<2>)), // bdzf
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<13>)), // bt+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<5>)), // bf+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<17, 0>)), // bdnz+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<9>)), // bdnzt+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<1>)), // bdnzf+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBI<19, 0>)), // bdz+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<11>)), // bdzt+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBO<3>)), // bdzf+
+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<20, 0>)), // blr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<12>)), // btlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<4>)), // bflr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<16, 0>)), // bdnzlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<8>)), // bdnztlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<0>)), // bdnzflr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<18, 0>)), // bdzlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<10>)), // bdztlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<2>)), // bdzflr
+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<13>)), // btlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<5>)), // bflr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<17, 0>)), // bdnzlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<9>)), // bdnztlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<1>)), // bdnzflr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBI<19, 0>)), // bdzlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<11>)), // bdztlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBO<3>)), // bdzflr+
+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBI<20, 0>)), // bctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<12>)), // btctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<4>)), // bfctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<13>)), // btctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBO<5>)), // bfctr+
+
+ // E.5.3
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 0, 2>)), // blt
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 1, 2>)), // ble
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 2, 2>)), // beq
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 0, 2>)), // bge
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 1, 2>)), // bgt
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 0, 2>)), // bnl
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 2, 2>)), // bne
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 1, 2>)), // bng
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 3, 2>)), // bso
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 3, 2>)), // bns
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<12, 3, 2>)), // bun
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<4, 3, 2>)), // bnu
+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 0, 2>)), // blt+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 1, 2>)), // ble+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 2, 2>)), // beq+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 0, 2>)), // bge+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 1, 2>)), // bgt+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 0, 2>)), // bnl+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 2, 2>)), // bne+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 1, 2>)), // bng+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 3, 2>)), // bso+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 3, 2>)), // bns+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<13, 3, 2>)), // bun+
+ LKAA_PSEUDO(GekkoMnemonic::Bc, (FillBOBICond<5, 3, 2>)), // bnu+
+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 0, 1>)), // bltlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 1, 1>)), // blelr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 2, 1>)), // beqlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 0, 1>)), // bgelr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 1, 1>)), // bgtlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 0, 1>)), // bnllr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 2, 1>)), // bnelr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 1, 1>)), // bnglr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 3, 1>)), // bsolr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 3, 1>)), // bnslr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<12, 3, 1>)), // bunlr
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<4, 3, 1>)), // bnulr
+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 0, 1>)), // bltlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 1, 1>)), // blelr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 2, 1>)), // beqlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 0, 1>)), // bgelr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 1, 1>)), // bgtlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 0, 1>)), // bnllr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 2, 1>)), // bnelr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 1, 1>)), // bnglr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 3, 1>)), // bsolr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 3, 1>)), // bnslr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<13, 3, 1>)), // bunlr+
+ LK_PSEUDO(GekkoMnemonic::Bclr, (FillBOBICond<5, 3, 1>)), // bnulr+
+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 0, 1>)), // bltctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 1, 1>)), // blectr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 2, 1>)), // beqctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 0, 1>)), // bgectr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 1, 1>)), // bgtctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 0, 1>)), // bnlctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 2, 1>)), // bnectr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 1, 1>)), // bngctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 3, 1>)), // bsoctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 3, 1>)), // bnsctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<12, 3, 1>)), // bunctr
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<4, 3, 1>)), // bnuctr
+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 0, 1>)), // bltctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 1, 1>)), // blectr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 2, 1>)), // beqctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 0, 1>)), // bgectr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 1, 1>)), // bgtctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 0, 1>)), // bnlctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 2, 1>)), // bnectr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 1, 1>)), // bngctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 3, 1>)), // bsoctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 3, 1>)), // bnsctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<13, 3, 1>)), // bunctr+
+ LK_PSEUDO(GekkoMnemonic::Bcctr, (FillBOBICond<5, 3, 1>)), // bnuctr+
+
+ // E.6
+ PLAIN_PSEUDO(GekkoMnemonic::Creqv,
+ [](OperandList& operands) {
+ operands.Insert(1, operands[0]);
+ operands.Insert(2, operands[0]);
+ }), // crset
+ PLAIN_PSEUDO(GekkoMnemonic::Crxor,
+ [](OperandList& operands) {
+ operands.Insert(1, operands[0]);
+ operands.Insert(2, operands[0]);
+ }), // crclr
+ PLAIN_PSEUDO(GekkoMnemonic::Cror,
+ [](OperandList& operands) { operands.Insert(2, operands[1]); }), // crmove
+ PLAIN_PSEUDO(GekkoMnemonic::Crnor,
+ [](OperandList& operands) { operands.Insert(2, operands[1]); }), // crnot
+
+ // E.7
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<16>), // twlt
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<16>), // twlti
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<20>), // twle
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<20>), // twlei
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<4>), // tweq
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<4>), // tweqi
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<12>), // twge
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<12>), // twgei
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<8>), // twgt
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<8>), // twgti
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<12>), // twnl
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<12>), // twnli
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<24>), // twne
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<24>), // twnei
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<20>), // twng
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<20>), // twngi
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<2>), // twllt
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<2>), // twllti
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<6>), // twlle
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<6>), // twllei
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<5>), // twlge
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<5>), // twlgei
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<1>), // twlgt
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<1>), // twlgti
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<5>), // twlnl
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<5>), // twlnli
+ PLAIN_PSEUDO(GekkoMnemonic::Tw, TrapSetTO<6>), // twlng
+ PLAIN_PSEUDO(GekkoMnemonic::Twi, TrapSetTO<6>), // twlngi
+ PLAIN_PSEUDO(GekkoMnemonic::Tw,
+ [](OperandList& operands) {
+ operands.Insert(0, 31);
+ operands.Insert(1, 0);
+ operands.Insert(2, 0);
+ }), // trap
+
+ // E.8
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(1)>), // mtxer
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(1)>), // mfxer
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(8)>), // mtlr
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(8)>), // mflr
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(9)>), // mtctr
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(9)>), // mfctr
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(18)>), // mtdsisr
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(18)>), // mfdsisr
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(19)>), // mtdar
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(19)>), // mfdar
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(22)>), // mtdec
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(22)>), // mfdec
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(25)>), // mtsdr1
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(25)>), // mfsdr1
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(26)>), // mtsrr0
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(26)>), // mfsrr0
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(27)>), // mtsrr1
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(27)>), // mfsrr1
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(280)>), // mtasr
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(280)>), // mfasr
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(282)>), // mtear
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfspr<SprBitswap(282)>), // mfear
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(284)>), // mttbl
+ PLAIN_PSEUDO(GekkoMnemonic::Mftb_nobitswap, FillMfspr<SprBitswap(268)>), // mftbl
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtspr<SprBitswap(285)>), // mttbu
+ PLAIN_PSEUDO(GekkoMnemonic::Mftb_nobitswap, FillMfspr<SprBitswap(269)>), // mftbu
+ PLAIN_PSEUDO(
+ GekkoMnemonic::Mtspr_nobitswap,
+ [](OperandList& operands) { operands[0] = SprBitswap(operands[0] + 272); }), // mtsprg
+ PLAIN_PSEUDO(
+ GekkoMnemonic::Mfspr_nobitswap,
+ [](OperandList& operands) { operands[1] = SprBitswap(operands[1] + 272); }), // mfsprg
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<528>), // mtibatu
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<528>), // mfibatu
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<529>), // mtibatl
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<529>), // mfibatl
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<536>), // mtdbatu
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<536>), // mfdbatu
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, FillMtsprBatAndBitswap<537>), // mtdbatl
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, FillMfsprBatAndBitswap<537>), // mfdbatl
+
+ // E.9
+ PLAIN_PSEUDO(GekkoMnemonic::Ori,
+ [](OperandList& operands) {
+ operands.Insert(0, 0);
+ operands.Insert(1, 0);
+ operands.Insert(2, 0);
+ }), // nop
+ PLAIN_PSEUDO(GekkoMnemonic::Addi,
+ [](OperandList& operands) { operands.Insert(1, 0); }), // li
+ PLAIN_PSEUDO(GekkoMnemonic::Addis,
+ [](OperandList& operands) { operands.Insert(1, 0); }), // lis
+ PLAIN_PSEUDO(GekkoMnemonic::Addi, SwapOps1And2), // la
+ RC_PSEUDO(GekkoMnemonic::Or,
+ ([](OperandList& operands) { operands.Insert(2, operands[1]); })), // mr
+ RC_PSEUDO(GekkoMnemonic::Nor,
+ ([](OperandList& operands) { operands.Insert(2, operands[1]); })), // not
+ PLAIN_PSEUDO(GekkoMnemonic::Mtcrf,
+ [](OperandList& operands) { operands.Insert(0, 0xff); }), // mtcr
+
+ // Additional mnemonics
+ PLAIN_PSEUDO(GekkoMnemonic::Mfspr_nobitswap, BitswapIdx<1>), // mfspr
+ PLAIN_PSEUDO(GekkoMnemonic::Mftb_nobitswap, BitswapIdx<1>), // mfspr
+ PLAIN_PSEUDO(GekkoMnemonic::Mtspr_nobitswap, BitswapIdx<0>), // mtspr
+};
+
+#undef EMIT_MNEMONIC_ENTRY
+#undef MNEMONIC
+#undef BASIC_MNEMONIC
+#undef RC_MNEMONIC
+#undef OERC_MNEMONIC
+#undef LK_MNEMONIC
+#undef AALK_MNEMONIC
+#undef PSEUDO
+#undef PLAIN_PSEUDO
+#undef RC_PSEUDO
+#undef OERC_PSEUDO
+#undef LK_PSEUDO
+#undef LKAA_PSEUDO
+
+//////////////////
+// LEXER TABLES //
+//////////////////
+
+namespace
+{
+constexpr TransitionF HasPlusOrMinus = [](char c) { return c == '+' || c == '-'; };
+constexpr TransitionF HasDigit = [](char c) -> bool { return std::isdigit(c); };
+constexpr TransitionF HasE = [](char c) { return c == 'e'; };
+constexpr TransitionF HasDot = [](char c) { return c == '.'; };
+
+// Normal string characters
+constexpr TransitionF HasNormal = [](char c) { return c != '\n' && c != '"' && c != '\\'; };
+// Invalid characters in string
+constexpr TransitionF HasInvalid = [](char c) { return c == '\n'; };
+// Octal digits
+constexpr TransitionF HasOctal = [](char c) { return c >= '0' && c <= '7'; };
+// Hex digits
+constexpr TransitionF HasHex = [](char c) -> bool { return std::isxdigit(c); };
+// Normal - octal
+constexpr TransitionF HasNormalMinusOctal = [](char c) { return HasNormal(c) && !HasOctal(c); };
+// Normal - hex
+constexpr TransitionF HasNormalMinusHex = [](char c) { return HasNormal(c) && !HasHex(c); };
+// Escape start
+constexpr TransitionF HasEscape = [](char c) { return c == '\\'; };
+// All single-character escapes
+constexpr TransitionF HasSCE = [](char c) { return !HasOctal(c) && c != 'x' && c != '\n'; };
+// Hex escape
+constexpr TransitionF HasHexStart = [](char c) { return c == 'x'; };
+constexpr TransitionF HasQuote = [](char c) { return c == '"'; };
+} // namespace
+
+extern const std::vector<DfaNode> float_dfa = {
+ {{DfaEdge(HasPlusOrMinus, 1), DfaEdge(HasDigit, 2), DfaEdge(HasDot, 5)},
+ "Invalid float: No numeric value"},
+
+ {{DfaEdge(HasDigit, 2), DfaEdge(HasDot, 5)}, "Invalid float: No numeric value"},
+
+ {{DfaEdge(HasDigit, 2), DfaEdge(HasDot, 3), DfaEdge(HasE, 7)}, std::nullopt},
+ {{DfaEdge(HasDigit, 4)}, "Invalid float: No numeric value after decimal point"},
+ {{DfaEdge(HasDigit, 4), DfaEdge(HasE, 7)}, std::nullopt},
+
+ {{DfaEdge(HasDigit, 6)}, "Invalid float: No numeric value after decimal point"},
+ {{DfaEdge(HasDigit, 6), DfaEdge(HasE, 7)}, std::nullopt},
+
+ {{DfaEdge(HasDigit, 9), DfaEdge(HasPlusOrMinus, 8)},
+ "Invalid float: No numeric value following exponent signifier"},
+ {{DfaEdge(HasDigit, 9)}, "Invalid float: No numeric value following exponent signifier"},
+ {{DfaEdge(HasDigit, 9)}, std::nullopt},
+};
+
+extern const std::vector<DfaNode> string_dfa = {
+ // Base character check
+ {{DfaEdge(HasNormal, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2), DfaEdge(HasEscape, 3)},
+ "Invalid string: No terminating \""},
+
+ // Invalid (unescaped newline)
+ {{}, "Invalid string: No terminating \""},
+ // String end
+ {{}, std::nullopt},
+
+ // Escape character breakout
+ {{DfaEdge(HasSCE, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasOctal, 4), DfaEdge(HasHexStart, 6)},
+ "Invalid string: No terminating \""},
+
+ // Octal characters, at most 3
+ {{DfaEdge(HasNormalMinusOctal, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2),
+ DfaEdge(HasEscape, 3), DfaEdge(HasOctal, 5)},
+ "Invalid string: No terminating \""},
+ {{DfaEdge(HasNormal, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2), DfaEdge(HasEscape, 3)},
+ "Invalid string: No terminating \""},
+
+ // Hex characters, 1 or more
+ {{DfaEdge(HasHex, 7)}, "Invalid string: bad hex escape"},
+ {{DfaEdge(HasNormalMinusHex, 0), DfaEdge(HasInvalid, 1), DfaEdge(HasQuote, 2),
+ DfaEdge(HasEscape, 3), DfaEdge(HasHex, 7)},
+ "Invalid string: No terminating \""},
+};
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/AssemblerTables.h b/Source/Core/Common/Assembler/AssemblerTables.h
new file mode 100644
index 0000000000..ed084208e4
--- /dev/null
+++ b/Source/Core/Common/Assembler/AssemblerTables.h
@@ -0,0 +1,152 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <optional>
+#include <string_view>
+#include <vector>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/CaseInsensitiveDict.h"
+#include "Common/CommonTypes.h"
+
+namespace Common::GekkoAssembler::detail
+{
+///////////////////
+// PARSER TABLES //
+///////////////////
+enum class ParseAlg
+{
+ None,
+ Op1,
+ NoneOrOp1,
+ Op1Off1,
+ Op2,
+ Op1Or2,
+ Op3,
+ Op2Or3,
+ Op4,
+ Op5,
+ Op1Off1Op2,
+};
+
+struct ParseInfo
+{
+ size_t mnemonic_index;
+ ParseAlg parse_algorithm;
+};
+
+// Mapping of SPRG names to values
+extern const CaseInsensitiveDict<u32, '_'> sprg_map;
+// Mapping of directive names to an enumeration
+extern const CaseInsensitiveDict<GekkoDirective> directives_map;
+// Mapping of normal Gekko mnemonics to their index and argument form
+extern const CaseInsensitiveDict<ParseInfo, '.', '_'> mnemonic_tokens;
+// Mapping of extended Gekko mnemonics to their index and argument form
+extern const CaseInsensitiveDict<ParseInfo, '.', '_', '+', '-'> extended_mnemonic_tokens;
+
+//////////////////////
+// ASSEMBLER TABLES //
+//////////////////////
+constexpr size_t MAX_OPERANDS = 5;
+
+struct OperandList
+{
+ std::array<Tagged<Interval, u32>, MAX_OPERANDS> list;
+ u32 count;
+ bool overfill;
+
+ constexpr u32 operator[](size_t index) const { return ValueOf(list[index]); }
+ constexpr u32& operator[](size_t index) { return ValueOf(list[index]); }
+
+ void Insert(size_t before, u32 val);
+
+ template <typename It>
+ void Copy(It begin, It end)
+ {
+ count = 0;
+ for (auto& i : list)
+ {
+ if (begin == end)
+ {
+ break;
+ }
+ i = *begin;
+ begin++;
+ count++;
+ }
+ overfill = begin != end;
+ }
+};
+
+struct OperandDesc
+{
+ u32 mask;
+ struct
+ {
+ u32 shift : 31;
+ bool is_signed : 1;
+ };
+ u32 MaxVal() const;
+ u32 MinVal() const;
+ u32 TruncBits() const;
+
+ bool Fits(u32 val) const;
+ u32 Fit(u32 val) const;
+};
+
+// MnemonicDesc holds the machine-code template for mnemonics
+struct MnemonicDesc
+{
+ // Initial value for a given mnemonic (opcode, func code, LK, AA, OE)
+ const u32 initial_value;
+ const u32 operand_count;
+ // Masks for operands
+ std::array<OperandDesc, MAX_OPERANDS> operand_masks;
+};
+
+// ExtendedMnemonicDesc holds the name of the mnemonic it transforms to as well as a
+// transformer callback to translate the operands into the correct form for the base mnemonic
+struct ExtendedMnemonicDesc
+{
+ size_t mnemonic_index;
+ void (*transform_operands)(OperandList&);
+};
+
+static constexpr size_t NUM_MNEMONICS = static_cast<size_t>(GekkoMnemonic::LastMnemonic) + 1;
+static constexpr size_t NUM_EXT_MNEMONICS =
+ static_cast<size_t>(ExtendedGekkoMnemonic::LastMnemonic) + 1;
+static constexpr size_t VARIANT_PERMUTATIONS = 4;
+
+// Table for mapping mnemonic+variants to their descriptors
+extern const std::array<MnemonicDesc, NUM_MNEMONICS * VARIANT_PERMUTATIONS> mnemonics;
+// Table for mapping extended mnemonic+variants to their descriptors
+extern const std::array<ExtendedMnemonicDesc, NUM_EXT_MNEMONICS * VARIANT_PERMUTATIONS>
+ extended_mnemonics;
+
+//////////////////
+// LEXER TABLES //
+//////////////////
+
+// In place of the reliace on std::regex, DFAs will be defined for matching sufficiently complex
+// tokens This gives an extra benefit of providing reasons for match failures
+using TransitionF = bool (*)(char c);
+using DfaEdge = std::pair<TransitionF, size_t>;
+struct DfaNode
+{
+ std::vector<DfaEdge> edges;
+ // If nullopt: this is a final node
+ // If string: invalid reason
+ std::optional<std::string_view> match_failure_reason;
+};
+
+// Floating point strings that will be accepted by std::stof/std::stod
+// regex: [\+-]?(\d+(\.\d+)?|\.\d+)(e[\+-]?\d+)?
+extern const std::vector<DfaNode> float_dfa;
+// C-style strings
+// regex: "([^\\\n]|\\([0-7]{1,3}|x[0-9a-fA-F]+|[^x0-7\n]))*"
+extern const std::vector<DfaNode> string_dfa;
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/CaseInsensitiveDict.h b/Source/Core/Common/Assembler/CaseInsensitiveDict.h
new file mode 100644
index 0000000000..8f6acccc8f
--- /dev/null
+++ b/Source/Core/Common/Assembler/CaseInsensitiveDict.h
@@ -0,0 +1,126 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace Common::GekkoAssembler::detail
+{
+// Hacky implementation of a case insensitive alphanumeric trie supporting extended entries
+// Standing in for std::map to support case-insensitive lookups while allowing string_views in
+// lookups
+template <typename V, char... ExtraMatches>
+class CaseInsensitiveDict
+{
+public:
+ CaseInsensitiveDict(const std::initializer_list<std::pair<std::string_view, V>>& il)
+ {
+ for (auto&& [k, v] : il)
+ {
+ Add(k, v);
+ }
+ }
+
+ template <typename T>
+ V const* Find(const T& key) const
+ {
+ auto&& [last_e, it] = TryFind(key);
+ if (it == key.cend() && last_e->_val)
+ {
+ return &*last_e->_val;
+ }
+ return nullptr;
+ }
+ static constexpr size_t NUM_CONNS = 36 + sizeof...(ExtraMatches);
+ static constexpr uint32_t INVALID_CONN = static_cast<uint32_t>(-1);
+
+private:
+ struct TrieEntry
+ {
+ std::array<uint32_t, 36 + sizeof...(ExtraMatches)> _conns;
+ std::optional<V> _val;
+
+ TrieEntry() { std::fill(_conns.begin(), _conns.end(), INVALID_CONN); }
+ };
+
+ constexpr size_t IndexOf(char c) const
+ {
+ size_t idx;
+ if (std::isalpha(c))
+ {
+ idx = std::tolower(c) - 'a';
+ }
+ else if (std::isdigit(c))
+ {
+ idx = c - '0' + 26;
+ }
+ else
+ {
+ idx = 36;
+ // Expands to an equivalent for loop over ExtraMatches
+ if constexpr (sizeof...(ExtraMatches) > 0)
+ {
+ (void)((c != ExtraMatches ? ++idx, true : false) && ...);
+ }
+ }
+ return idx;
+ }
+
+ template <typename T>
+ auto TryFind(const T& key) const -> std::pair<TrieEntry const*, decltype(key.cbegin())>
+ {
+ std::pair<TrieEntry const*, decltype(key.cbegin())> ret(&m_root_entry, key.cbegin());
+ const auto k_end = key.cend();
+
+ for (; ret.second != k_end; ret.second++)
+ {
+ const size_t idx = IndexOf(*ret.second);
+ if (idx >= NUM_CONNS || ret.first->_conns[idx] == INVALID_CONN)
+ {
+ break;
+ }
+
+ ret.first = &m_entry_pool[ret.first->_conns[idx]];
+ }
+
+ return ret;
+ }
+
+ template <typename T>
+ auto TryFind(const T& key) -> std::pair<TrieEntry*, decltype(key.cbegin())>
+ {
+ auto&& [e_const, it] =
+ const_cast<CaseInsensitiveDict<V, ExtraMatches...> const*>(this)->TryFind(key);
+ return {const_cast<TrieEntry*>(e_const), it};
+ }
+
+ void Add(std::string_view key, const V& val)
+ {
+ auto&& [last_e, it] = TryFind(key);
+ if (it != key.cend())
+ {
+ for (; it != key.cend(); it++)
+ {
+ const size_t idx = IndexOf(*it);
+ if (idx >= NUM_CONNS)
+ {
+ break;
+ }
+ last_e->_conns[idx] = static_cast<uint32_t>(m_entry_pool.size());
+ last_e = &m_entry_pool.emplace_back();
+ }
+ }
+ last_e->_val = val;
+ }
+
+ TrieEntry m_root_entry;
+ std::vector<TrieEntry> m_entry_pool;
+};
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/GekkoAssembler.cpp b/Source/Core/Common/Assembler/GekkoAssembler.cpp
new file mode 100644
index 0000000000..4ee97c59d9
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoAssembler.cpp
@@ -0,0 +1,189 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Common/Assembler/GekkoAssembler.h"
+
+#include <algorithm>
+#include <array>
+#include <string>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/AssemblerTables.h"
+#include "Common/Assembler/GekkoIRGen.h"
+#include "Common/Assert.h"
+#include "Common/CommonTypes.h"
+
+namespace Common::GekkoAssembler
+{
+namespace
+{
+using namespace Common::GekkoAssembler::detail;
+
+FailureOr<u32> FillInstruction(const MnemonicDesc& desc, const OperandList& operands,
+ std::string_view inst_line)
+{
+ // Parser shouldn't allow this to pass
+ ASSERT_MSG(COMMON, desc.operand_count == operands.count && !operands.overfill,
+ "Unexpected operand count mismatch for instruction {}. Expected {} but found {}",
+ inst_line, desc.operand_count, operands.overfill ? 6 : operands.count);
+
+ u32 instruction = desc.initial_value;
+ for (u32 i = 0; i < operands.count; i++)
+ {
+ if (!desc.operand_masks[i].Fits(operands[i]))
+ {
+ std::string message;
+ const u32 trunc_bits = desc.operand_masks[i].TruncBits();
+ if (trunc_bits == 0)
+ {
+ if (desc.operand_masks[i].is_signed)
+ {
+ message = fmt::format("{:#x} not between {:#x} and {:#x}", static_cast<s32>(operands[i]),
+ static_cast<s32>(desc.operand_masks[i].MinVal()),
+ static_cast<s32>(desc.operand_masks[i].MaxVal()));
+ }
+ else
+ {
+ message = fmt::format("{:#x} not between {:#x} and {:#x}", operands[i],
+ desc.operand_masks[i].MinVal(), desc.operand_masks[i].MaxVal());
+ }
+ }
+ else
+ {
+ if (desc.operand_masks[i].is_signed)
+ {
+ message = fmt::format("{:#x} not between {:#x} and {:#x} or not aligned to {}",
+ static_cast<s32>(operands[i]),
+ static_cast<s32>(desc.operand_masks[i].MinVal()),
+ static_cast<s32>(desc.operand_masks[i].MaxVal()), trunc_bits + 1);
+ }
+ else
+ {
+ message = fmt::format("{:#x} not between {:#x} and {:#x} or not aligned to {}",
+ operands[i], desc.operand_masks[i].MinVal(),
+ desc.operand_masks[i].MaxVal(), trunc_bits + 1);
+ }
+ }
+ return AssemblerError{std::move(message), "", 0, TagOf(operands.list[i]).begin,
+ TagOf(operands.list[i]).len};
+ }
+ instruction |= desc.operand_masks[i].Fit(operands[i]);
+ }
+ return instruction;
+}
+
+void AdjustOperandsForGas(GekkoMnemonic mnemonic, OperandList& ops_list)
+{
+ switch (mnemonic)
+ {
+ case GekkoMnemonic::Cmp:
+ case GekkoMnemonic::Cmpl:
+ case GekkoMnemonic::Cmpi:
+ case GekkoMnemonic::Cmpli:
+ if (ops_list.count < 4)
+ {
+ ops_list.Insert(0, 0);
+ }
+ break;
+
+ case GekkoMnemonic::Addis:
+ // Because GAS wants to allow for addis and lis to work nice with absolute addresses, the
+ // immediate operand should also "fit" into the _UIMM field, so just turn a valid UIMM into a
+ // SIMM
+ if (ops_list[2] >= 0x8000 && ops_list[2] <= 0xffff)
+ {
+ ops_list[2] = ops_list[2] - 0x10000;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // namespace
+
+void CodeBlock::PushBigEndian(u32 val)
+{
+ instructions.push_back((val >> 24) & 0xff);
+ instructions.push_back((val >> 16) & 0xff);
+ instructions.push_back((val >> 8) & 0xff);
+ instructions.push_back(val & 0xff);
+}
+
+FailureOr<std::vector<CodeBlock>> Assemble(std::string_view instruction,
+ u32 current_instruction_address)
+{
+ FailureOr<detail::GekkoIR> parse_result =
+ detail::ParseToIR(instruction, current_instruction_address);
+ if (IsFailure(parse_result))
+ {
+ return GetFailure(parse_result);
+ }
+
+ const auto& parsed_blocks = GetT(parse_result).blocks;
+ const auto& operands = GetT(parse_result).operand_pool;
+ std::vector<CodeBlock> out_blocks;
+
+ for (const detail::IRBlock& parsed_block : parsed_blocks)
+ {
+ CodeBlock new_block(parsed_block.block_address);
+ for (const detail::ChunkVariant& chunk : parsed_block.chunks)
+ {
+ if (std::holds_alternative<detail::InstChunk>(chunk))
+ {
+ for (const detail::GekkoInstruction& parsed_inst : std::get<detail::InstChunk>(chunk))
+ {
+ OperandList adjusted_ops;
+ ASSERT(parsed_inst.op_interval.len <= MAX_OPERANDS);
+ adjusted_ops.Copy(operands.begin() + parsed_inst.op_interval.begin,
+ operands.begin() + parsed_inst.op_interval.End());
+
+ size_t idx = parsed_inst.mnemonic_index;
+ if (parsed_inst.is_extended)
+ {
+ extended_mnemonics[idx].transform_operands(adjusted_ops);
+ idx = extended_mnemonics[idx].mnemonic_index;
+ }
+
+ AdjustOperandsForGas(static_cast<GekkoMnemonic>(idx >> 2), adjusted_ops);
+
+ FailureOr<u32> inst = FillInstruction(mnemonics[idx], adjusted_ops, parsed_inst.raw_text);
+ if (IsFailure(inst))
+ {
+ GetFailure(inst).error_line = parsed_inst.raw_text;
+ GetFailure(inst).line = parsed_inst.line_number;
+ return GetFailure(inst);
+ }
+
+ new_block.PushBigEndian(GetT(inst));
+ }
+ }
+ else if (std::holds_alternative<detail::ByteChunk>(chunk))
+ {
+ detail::ByteChunk byte_arr = std::get<detail::ByteChunk>(chunk);
+ new_block.instructions.insert(new_block.instructions.end(), byte_arr.begin(),
+ byte_arr.end());
+ }
+ else if (std::holds_alternative<detail::PadChunk>(chunk))
+ {
+ detail::PadChunk pad_len = std::get<detail::PadChunk>(chunk);
+ new_block.instructions.insert(new_block.instructions.end(), pad_len, 0);
+ }
+ else
+ {
+ ASSERT(false);
+ }
+ }
+
+ if (!new_block.instructions.empty())
+ {
+ out_blocks.emplace_back(std::move(new_block));
+ }
+ }
+ return out_blocks;
+}
+} // namespace Common::GekkoAssembler
diff --git a/Source/Core/Common/Assembler/GekkoAssembler.h b/Source/Core/Common/Assembler/GekkoAssembler.h
new file mode 100644
index 0000000000..ede99532f3
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoAssembler.h
@@ -0,0 +1,29 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string_view>
+#include <vector>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/CommonTypes.h"
+
+namespace Common::GekkoAssembler
+{
+struct CodeBlock
+{
+ CodeBlock(u32 address) : block_address(address) {}
+
+ void PushBigEndian(u32 val);
+
+ u32 block_address;
+ std::vector<u8> instructions;
+};
+
+// Common::GekkoAssember::Assemble - Core routine for assembling Gekko/Broadway instructions
+// Supports the full Gekko ISA, as well as the extended mnemonics defined by the book "PowerPC
+// Microprocessor Family: The Programming Environments" The input assembly is fully parsed and
+// assembled with a base address specified by the base_virtual_address
+FailureOr<std::vector<CodeBlock>> Assemble(std::string_view assembly, u32 base_virtual_address);
+} // namespace Common::GekkoAssembler
diff --git a/Source/Core/Common/Assembler/GekkoIRGen.cpp b/Source/Core/Common/Assembler/GekkoIRGen.cpp
new file mode 100644
index 0000000000..72c284a15e
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoIRGen.cpp
@@ -0,0 +1,832 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Common/Assembler/GekkoIRGen.h"
+
+#include <functional>
+#include <map>
+#include <numeric>
+#include <set>
+#include <stack>
+#include <variant>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/GekkoParser.h"
+#include "Common/Assert.h"
+#include "Common/BitUtils.h"
+
+namespace Common::GekkoAssembler::detail
+{
+namespace
+{
+class GekkoIRPlugin : public ParsePlugin
+{
+public:
+ GekkoIRPlugin(GekkoIR& result, u32 base_addr)
+ : m_output_result(result), m_active_var(nullptr), m_operand_scan_begin(0)
+ {
+ m_active_block = &m_output_result.blocks.emplace_back(base_addr);
+ }
+ virtual ~GekkoIRPlugin() = default;
+
+ void OnDirectivePre(GekkoDirective directive) override;
+ void OnDirectivePost(GekkoDirective directive) override;
+ void OnInstructionPre(const ParseInfo& mnemonic_info, bool extended) override;
+ void OnInstructionPost(const ParseInfo& mnemonic_info, bool extended) override;
+ void OnOperandPre() override;
+ void OnOperandPost() override;
+ void OnResolvedExprPost() override;
+ void OnOperator(AsmOp operation) override;
+ void OnTerminal(Terminal type, const AssemblerToken& val) override;
+ void OnHiaddr(std::string_view id) override;
+ void OnLoaddr(std::string_view id) override;
+ void OnCloseParen(ParenType type) override;
+ void OnLabelDecl(std::string_view name) override;
+ void OnVarDecl(std::string_view name) override;
+ void PostParseAction() override;
+
+ u32 CurrentAddress() const;
+ std::optional<u64> LookupVar(std::string_view lab);
+ std::optional<u32> LookupLabel(std::string_view lab);
+
+ template <typename T>
+ T& GetChunk();
+
+ template <typename T>
+ void AddBytes(T val);
+
+ void AddStringBytes(std::string_view str, bool null_term);
+
+ void PadAlign(u32 bits);
+ void PadSpace(size_t space);
+
+ void StartBlock(u32 address);
+ void StartBlockAlign(u32 bits);
+ void StartInstruction(size_t mnemonic_index, bool extended);
+ void FinishInstruction();
+ void SaveOperandFixup(size_t str_left, size_t str_right);
+
+ void AddBinaryEvaluator(u32 (*evaluator)(u32, u32));
+ void AddUnaryEvaluator(u32 (*evaluator)(u32));
+ void AddAbsoluteAddressConv();
+ void AddLiteral(u32 lit);
+ void AddSymbolResolve(std::string_view sym, bool absolute);
+
+ void RunFixups();
+
+ void EvalOperatorRel(AsmOp operation);
+ void EvalOperatorAbs(AsmOp operation);
+ void EvalTerminalRel(Terminal type, const AssemblerToken& tok);
+ void EvalTerminalAbs(Terminal type, const AssemblerToken& tok);
+
+private:
+ enum class EvalMode
+ {
+ RelAddrDoublePass,
+ AbsAddrSinglePass,
+ };
+
+ GekkoIR& m_output_result;
+
+ IRBlock* m_active_block;
+ GekkoInstruction m_build_inst;
+ u64* m_active_var;
+ size_t m_operand_scan_begin;
+
+ std::map<std::string, u32, std::less<>> m_labels;
+ std::map<std::string, u64, std::less<>> m_constants;
+ std::set<std::string> m_symset;
+
+ EvalMode m_evaluation_mode;
+
+ // For operand parsing
+ std::stack<std::function<u32()>> m_fixup_stack;
+ std::vector<std::function<u32()>> m_operand_fixups;
+ size_t m_operand_str_start;
+
+ // For directive parsing
+ std::vector<u64> m_eval_stack;
+ std::variant<std::vector<float>, std::vector<double>> m_floats_list;
+ std::string_view m_string_lit;
+ GekkoDirective m_active_directive;
+};
+
+///////////////
+// OVERRIDES //
+///////////////
+
+void GekkoIRPlugin::OnDirectivePre(GekkoDirective directive)
+{
+ m_evaluation_mode = EvalMode::AbsAddrSinglePass;
+ m_active_directive = directive;
+ m_eval_stack = std::vector<u64>{};
+
+ switch (directive)
+ {
+ case GekkoDirective::Float:
+ m_floats_list = std::vector<float>{};
+ break;
+ case GekkoDirective::Double:
+ m_floats_list = std::vector<double>{};
+ break;
+ default:
+ break;
+ }
+}
+
+void GekkoIRPlugin::OnDirectivePost(GekkoDirective directive)
+{
+ switch (directive)
+ {
+ // .nbyte directives are handled by OnResolvedExprPost
+ default:
+ break;
+
+ case GekkoDirective::Float:
+ case GekkoDirective::Double:
+ std::visit(
+ [this](auto&& vec) {
+ for (auto&& val : vec)
+ {
+ AddBytes(val);
+ }
+ },
+ m_floats_list);
+ break;
+
+ case GekkoDirective::DefVar:
+ ASSERT(m_active_var != nullptr);
+ *m_active_var = m_eval_stack.back();
+ m_active_var = nullptr;
+ break;
+
+ case GekkoDirective::Locate:
+ StartBlock(static_cast<u32>(m_eval_stack.back()));
+ break;
+
+ case GekkoDirective::Zeros:
+ PadSpace(static_cast<u32>(m_eval_stack.back()));
+ break;
+
+ case GekkoDirective::Skip:
+ {
+ const u32 skip_len = static_cast<u32>(m_eval_stack.back());
+ if (skip_len > 0)
+ {
+ StartBlock(CurrentAddress() + skip_len);
+ }
+ break;
+ }
+
+ case GekkoDirective::PadAlign:
+ PadAlign(static_cast<u32>(m_eval_stack.back()));
+ break;
+
+ case GekkoDirective::Align:
+ StartBlockAlign(static_cast<u32>(m_eval_stack.back()));
+ break;
+
+ case GekkoDirective::Ascii:
+ AddStringBytes(m_string_lit, false);
+ break;
+
+ case GekkoDirective::Asciz:
+ AddStringBytes(m_string_lit, true);
+ break;
+ }
+ m_eval_stack = {};
+}
+
+void GekkoIRPlugin::OnInstructionPre(const ParseInfo& mnemonic_info, bool extended)
+{
+ m_evaluation_mode = EvalMode::RelAddrDoublePass;
+ StartInstruction(mnemonic_info.mnemonic_index, extended);
+}
+
+void GekkoIRPlugin::OnInstructionPost(const ParseInfo&, bool)
+{
+ FinishInstruction();
+}
+
+void GekkoIRPlugin::OnOperandPre()
+{
+ m_operand_str_start = m_owner->lexer.ColNumber();
+}
+
+void GekkoIRPlugin::OnOperandPost()
+{
+ SaveOperandFixup(m_operand_str_start, m_owner->lexer.ColNumber());
+}
+
+void GekkoIRPlugin::OnResolvedExprPost()
+{
+ switch (m_active_directive)
+ {
+ case GekkoDirective::Byte:
+ AddBytes<u8>(static_cast<u8>(m_eval_stack.back()));
+ break;
+ case GekkoDirective::_2byte:
+ AddBytes<u16>(static_cast<u16>(m_eval_stack.back()));
+ break;
+ case GekkoDirective::_4byte:
+ AddBytes<u32>(static_cast<u32>(m_eval_stack.back()));
+ break;
+ case GekkoDirective::_8byte:
+ AddBytes<u64>(static_cast<u64>(m_eval_stack.back()));
+ break;
+ default:
+ return;
+ }
+ m_eval_stack.clear();
+}
+
+void GekkoIRPlugin::OnOperator(AsmOp operation)
+{
+ if (m_evaluation_mode == EvalMode::RelAddrDoublePass)
+ {
+ EvalOperatorRel(operation);
+ }
+ else
+ {
+ EvalOperatorAbs(operation);
+ }
+}
+
+void GekkoIRPlugin::OnTerminal(Terminal type, const AssemblerToken& val)
+{
+ if (type == Terminal::Str)
+ {
+ m_string_lit = val.token_val;
+ }
+ else if (m_evaluation_mode == EvalMode::RelAddrDoublePass)
+ {
+ EvalTerminalRel(type, val);
+ }
+ else
+ {
+ EvalTerminalAbs(type, val);
+ }
+}
+
+void GekkoIRPlugin::OnHiaddr(std::string_view id)
+{
+ if (m_evaluation_mode == EvalMode::RelAddrDoublePass)
+ {
+ AddSymbolResolve(id, true);
+ AddLiteral(16);
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs >> rhs; });
+ AddLiteral(0xffff);
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs & rhs; });
+ }
+ else
+ {
+ u32 base;
+ if (auto lbl = LookupLabel(id); lbl)
+ {
+ base = *lbl;
+ }
+ else if (auto var = LookupVar(id); var)
+ {
+ base = *var;
+ }
+ else
+ {
+ m_owner->EmitErrorHere(fmt::format("Undefined reference to Label/Constant '{}'", id));
+ return;
+ }
+ m_eval_stack.push_back((base >> 16) & 0xffff);
+ }
+}
+
+void GekkoIRPlugin::OnLoaddr(std::string_view id)
+{
+ if (m_evaluation_mode == EvalMode::RelAddrDoublePass)
+ {
+ AddSymbolResolve(id, true);
+ AddLiteral(0xffff);
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs & rhs; });
+ }
+ else
+ {
+ u32 base;
+ if (auto lbl = LookupLabel(id); lbl)
+ {
+ base = *lbl;
+ }
+ else if (auto var = LookupVar(id); var)
+ {
+ base = *var;
+ }
+ else
+ {
+ m_owner->EmitErrorHere(fmt::format("Undefined reference to Label/Constant '{}'", id));
+ return;
+ }
+
+ m_eval_stack.push_back(base & 0xffff);
+ }
+}
+
+void GekkoIRPlugin::OnCloseParen(ParenType type)
+{
+ if (type != ParenType::RelConv)
+ {
+ return;
+ }
+
+ if (m_evaluation_mode == EvalMode::RelAddrDoublePass)
+ {
+ AddAbsoluteAddressConv();
+ }
+ else
+ {
+ m_eval_stack.push_back(CurrentAddress());
+ EvalOperatorAbs(AsmOp::Sub);
+ }
+}
+
+void GekkoIRPlugin::OnLabelDecl(std::string_view name)
+{
+ const std::string name_str(name);
+ if (m_symset.contains(name_str))
+ {
+ m_owner->EmitErrorHere(fmt::format("Label/Constant {} is already defined", name));
+ return;
+ }
+
+ m_labels[name_str] = m_active_block->BlockEndAddress();
+ m_symset.insert(name_str);
+}
+
+void GekkoIRPlugin::OnVarDecl(std::string_view name)
+{
+ const std::string name_str(name);
+ if (m_symset.contains(name_str))
+ {
+ m_owner->EmitErrorHere(fmt::format("Label/Constant {} is already defined", name));
+ return;
+ }
+
+ m_active_var = &m_constants[name_str];
+ m_symset.insert(name_str);
+}
+
+void GekkoIRPlugin::PostParseAction()
+{
+ RunFixups();
+}
+
+//////////////////////
+// HELPER FUNCTIONS //
+//////////////////////
+
+u32 GekkoIRPlugin::CurrentAddress() const
+{
+ return m_active_block->BlockEndAddress();
+}
+
+std::optional<u64> GekkoIRPlugin::LookupVar(std::string_view var)
+{
+ auto var_it = m_constants.find(var);
+ return var_it == m_constants.end() ? std::nullopt : std::optional(var_it->second);
+}
+
+std::optional<u32> GekkoIRPlugin::LookupLabel(std::string_view lab)
+{
+ auto label_it = m_labels.find(lab);
+ return label_it == m_labels.end() ? std::nullopt : std::optional(label_it->second);
+}
+
+void GekkoIRPlugin::AddStringBytes(std::string_view str, bool null_term)
+{
+ ByteChunk& bytes = GetChunk<ByteChunk>();
+ ConvertStringLiteral(str, &bytes);
+ if (null_term)
+ {
+ bytes.push_back('\0');
+ }
+}
+
+template <typename T>
+T& GekkoIRPlugin::GetChunk()
+{
+ if (!m_active_block->chunks.empty() && std::holds_alternative<T>(m_active_block->chunks.back()))
+ {
+ return std::get<T>(m_active_block->chunks.back());
+ }
+
+ return std::get<T>(m_active_block->chunks.emplace_back(T{}));
+}
+
+template <typename T>
+void GekkoIRPlugin::AddBytes(T val)
+{
+ if constexpr (std::is_integral_v<T>)
+ {
+ ByteChunk& bytes = GetChunk<ByteChunk>();
+ for (size_t i = sizeof(T) - 1; i > 0; i--)
+ {
+ bytes.push_back((val >> (8 * i)) & 0xff);
+ }
+ bytes.push_back(val & 0xff);
+ }
+ else if constexpr (std::is_same_v<T, float>)
+ {
+ static_assert(sizeof(double) == sizeof(u64));
+ AddBytes(BitCast<u32>(val));
+ }
+ else
+ {
+ // std::is_same_v<T, double>
+ static_assert(sizeof(double) == sizeof(u64));
+ AddBytes(BitCast<u64>(val));
+ }
+}
+
+void GekkoIRPlugin::PadAlign(u32 bits)
+{
+ const u32 align_mask = (1 << bits) - 1;
+ const u32 current_addr = m_active_block->BlockEndAddress();
+ if (current_addr & align_mask)
+ {
+ PadChunk& current_pad = GetChunk<PadChunk>();
+ current_pad += (1 << bits) - (current_addr & align_mask);
+ }
+}
+
+void GekkoIRPlugin::PadSpace(size_t space)
+{
+ GetChunk<PadChunk>() += space;
+}
+
+void GekkoIRPlugin::StartBlock(u32 address)
+{
+ m_active_block = &m_output_result.blocks.emplace_back(address);
+}
+
+void GekkoIRPlugin::StartBlockAlign(u32 bits)
+{
+ const u32 align_mask = (1 << bits) - 1;
+ const u32 current_addr = m_active_block->BlockEndAddress();
+ if (current_addr & align_mask)
+ {
+ StartBlock((1 << bits) + (current_addr & ~align_mask));
+ }
+}
+
+void GekkoIRPlugin::StartInstruction(size_t mnemonic_index, bool extended)
+{
+ m_build_inst = GekkoInstruction{
+ .mnemonic_index = mnemonic_index,
+ .raw_text = m_owner->lexer.CurrentLine(),
+ .line_number = m_owner->lexer.LineNumber(),
+ .is_extended = extended,
+ };
+ m_operand_scan_begin = m_output_result.operand_pool.size();
+}
+
+void GekkoIRPlugin::AddBinaryEvaluator(u32 (*evaluator)(u32, u32))
+{
+ std::function<u32()> rhs = std::move(m_fixup_stack.top());
+ m_fixup_stack.pop();
+ std::function<u32()> lhs = std::move(m_fixup_stack.top());
+ m_fixup_stack.pop();
+ m_fixup_stack.emplace([evaluator, lhs = std::move(lhs), rhs = std::move(rhs)]() {
+ return evaluator(lhs(), rhs());
+ });
+}
+
+void GekkoIRPlugin::AddUnaryEvaluator(u32 (*evaluator)(u32))
+{
+ std::function<u32()> sub = std::move(m_fixup_stack.top());
+ m_fixup_stack.pop();
+ m_fixup_stack.emplace([evaluator, sub = std::move(sub)]() { return evaluator(sub()); });
+}
+
+void GekkoIRPlugin::AddAbsoluteAddressConv()
+{
+ const u32 inst_address = m_active_block->BlockEndAddress();
+ std::function<u32()> sub = std::move(m_fixup_stack.top());
+ m_fixup_stack.pop();
+ m_fixup_stack.emplace([inst_address, sub = std::move(sub)] { return sub() - inst_address; });
+}
+
+void GekkoIRPlugin::AddLiteral(u32 lit)
+{
+ m_fixup_stack.emplace([lit] { return lit; });
+}
+
+void GekkoIRPlugin::AddSymbolResolve(std::string_view sym, bool absolute)
+{
+ const u32 source_address = m_active_block->BlockEndAddress();
+ AssemblerError err_on_fail = AssemblerError{
+ fmt::format("Unresolved symbol '{}'", sym),
+ m_owner->lexer.CurrentLine(),
+ m_owner->lexer.LineNumber(),
+ // Lexer should currently point to the label, as it hasn't been eaten yet
+ m_owner->lexer.ColNumber(),
+ sym.size(),
+ };
+
+ m_fixup_stack.emplace(
+ [this, sym, absolute, source_address, err_on_fail = std::move(err_on_fail)] {
+ auto label_it = m_labels.find(sym);
+ if (label_it != m_labels.end())
+ {
+ if (absolute)
+ {
+ return label_it->second;
+ }
+ return label_it->second - source_address;
+ }
+
+ auto var_it = m_constants.find(sym);
+ if (var_it != m_constants.end())
+ {
+ return static_cast<u32>(var_it->second);
+ }
+
+ m_owner->error = std::move(err_on_fail);
+ return u32{0};
+ });
+}
+
+void GekkoIRPlugin::SaveOperandFixup(size_t str_left, size_t str_right)
+{
+ m_operand_fixups.emplace_back(std::move(m_fixup_stack.top()));
+ m_fixup_stack.pop();
+ m_output_result.operand_pool.emplace_back(Interval{str_left, str_right - str_left}, 0);
+}
+
+void GekkoIRPlugin::RunFixups()
+{
+ for (size_t i = 0; i < m_operand_fixups.size(); i++)
+ {
+ ValueOf(m_output_result.operand_pool[i]) = m_operand_fixups[i]();
+ if (m_owner->error)
+ {
+ return;
+ }
+ }
+}
+
+void GekkoIRPlugin::FinishInstruction()
+{
+ m_build_inst.op_interval.begin = m_operand_scan_begin;
+ m_build_inst.op_interval.len = m_output_result.operand_pool.size() - m_operand_scan_begin;
+ GetChunk<InstChunk>().emplace_back(m_build_inst);
+ m_operand_scan_begin = 0;
+}
+
+void GekkoIRPlugin::EvalOperatorAbs(AsmOp operation)
+{
+#define EVAL_BINARY_OP(OPERATOR) \
+ { \
+ u64 rhs = m_eval_stack.back(); \
+ m_eval_stack.pop_back(); \
+ m_eval_stack.back() = m_eval_stack.back() OPERATOR rhs; \
+ }
+
+ switch (operation)
+ {
+ case AsmOp::Or:
+ EVAL_BINARY_OP(|);
+ break;
+ case AsmOp::Xor:
+ EVAL_BINARY_OP(^);
+ break;
+ case AsmOp::And:
+ EVAL_BINARY_OP(&);
+ break;
+ case AsmOp::Lsh:
+ EVAL_BINARY_OP(<<);
+ break;
+ case AsmOp::Rsh:
+ EVAL_BINARY_OP(>>);
+ break;
+ case AsmOp::Add:
+ EVAL_BINARY_OP(+);
+ break;
+ case AsmOp::Sub:
+ EVAL_BINARY_OP(-);
+ break;
+ case AsmOp::Mul:
+ EVAL_BINARY_OP(*);
+ break;
+ case AsmOp::Div:
+ EVAL_BINARY_OP(/);
+ break;
+ case AsmOp::Neg:
+ m_eval_stack.back() = static_cast<u32>(-static_cast<s32>(m_eval_stack.back()));
+ break;
+ case AsmOp::Not:
+ m_eval_stack.back() = ~m_eval_stack.back();
+ break;
+ }
+#undef EVAL_BINARY_OP
+#undef EVAL_UNARY_OP
+}
+
+void GekkoIRPlugin::EvalOperatorRel(AsmOp operation)
+{
+ switch (operation)
+ {
+ case AsmOp::Or:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs | rhs; });
+ break;
+ case AsmOp::Xor:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs ^ rhs; });
+ break;
+ case AsmOp::And:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs & rhs; });
+ break;
+ case AsmOp::Lsh:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs << rhs; });
+ break;
+ case AsmOp::Rsh:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs >> rhs; });
+ break;
+ case AsmOp::Add:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs + rhs; });
+ break;
+ case AsmOp::Sub:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs - rhs; });
+ break;
+ case AsmOp::Mul:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs * rhs; });
+ break;
+ case AsmOp::Div:
+ AddBinaryEvaluator([](u32 lhs, u32 rhs) { return lhs / rhs; });
+ break;
+ case AsmOp::Neg:
+ AddUnaryEvaluator([](u32 val) { return static_cast<u32>(-static_cast<s32>(val)); });
+ break;
+ case AsmOp::Not:
+ AddUnaryEvaluator([](u32 val) { return ~val; });
+ break;
+ }
+}
+
+void GekkoIRPlugin::EvalTerminalRel(Terminal type, const AssemblerToken& tok)
+{
+ switch (type)
+ {
+ case Terminal::Hex:
+ case Terminal::Dec:
+ case Terminal::Oct:
+ case Terminal::Bin:
+ case Terminal::GPR:
+ case Terminal::FPR:
+ case Terminal::SPR:
+ case Terminal::CRField:
+ case Terminal::Lt:
+ case Terminal::Gt:
+ case Terminal::Eq:
+ case Terminal::So:
+ {
+ std::optional<u32> val = tok.EvalToken<u32>();
+ ASSERT(val.has_value());
+ AddLiteral(*val);
+ break;
+ }
+
+ case Terminal::Dot:
+ AddLiteral(CurrentAddress());
+ break;
+
+ case Terminal::Id:
+ {
+ if (auto label_it = m_labels.find(tok.token_val); label_it != m_labels.end())
+ {
+ AddLiteral(label_it->second - CurrentAddress());
+ }
+ else if (auto var_it = m_constants.find(tok.token_val); var_it != m_constants.end())
+ {
+ AddLiteral(var_it->second);
+ }
+ else
+ {
+ AddSymbolResolve(tok.token_val, false);
+ }
+ break;
+ }
+
+ // Parser should disallow this from happening
+ default:
+ ASSERT(false);
+ break;
+ }
+}
+
+void GekkoIRPlugin::EvalTerminalAbs(Terminal type, const AssemblerToken& tok)
+{
+ switch (type)
+ {
+ case Terminal::Hex:
+ case Terminal::Dec:
+ case Terminal::Oct:
+ case Terminal::Bin:
+ case Terminal::GPR:
+ case Terminal::FPR:
+ case Terminal::SPR:
+ case Terminal::CRField:
+ case Terminal::Lt:
+ case Terminal::Gt:
+ case Terminal::Eq:
+ case Terminal::So:
+ {
+ std::optional<u64> val = tok.EvalToken<u64>();
+ ASSERT(val.has_value());
+ m_eval_stack.push_back(*val);
+ break;
+ }
+
+ case Terminal::Flt:
+ {
+ std::visit(
+ [&tok](auto&& vec) {
+ auto opt = tok.EvalToken<typename std::decay_t<decltype(vec)>::value_type>();
+ ASSERT(opt.has_value());
+ vec.push_back(*opt);
+ },
+ m_floats_list);
+ break;
+ }
+
+ case Terminal::Dot:
+ m_eval_stack.push_back(static_cast<u64>(CurrentAddress()));
+ break;
+
+ case Terminal::Id:
+ {
+ if (auto label_it = m_labels.find(tok.token_val); label_it != m_labels.end())
+ {
+ m_eval_stack.push_back(label_it->second);
+ }
+ else if (auto var_it = m_constants.find(tok.token_val); var_it != m_constants.end())
+ {
+ m_eval_stack.push_back(var_it->second);
+ }
+ else
+ {
+ m_owner->EmitErrorHere(
+ fmt::format("Undefined reference to Label/Constant '{}'", tok.ValStr()));
+ return;
+ }
+ break;
+ }
+
+ // Parser should disallow this from happening
+ default:
+ ASSERT(false);
+ break;
+ }
+}
+} // namespace
+
+u32 IRBlock::BlockEndAddress() const
+{
+ return std::accumulate(chunks.begin(), chunks.end(), block_address,
+ [](u32 acc, const ChunkVariant& chunk) {
+ size_t size;
+ if (std::holds_alternative<InstChunk>(chunk))
+ {
+ size = std::get<InstChunk>(chunk).size() * 4;
+ }
+ else if (std::holds_alternative<ByteChunk>(chunk))
+ {
+ size = std::get<ByteChunk>(chunk).size();
+ }
+ else if (std::holds_alternative<PadChunk>(chunk))
+ {
+ size = std::get<PadChunk>(chunk);
+ }
+ else
+ {
+ ASSERT(false);
+ size = 0;
+ }
+
+ return acc + static_cast<u32>(size);
+ });
+}
+
+FailureOr<GekkoIR> ParseToIR(std::string_view assembly, u32 base_virtual_address)
+{
+ GekkoIR ret;
+ GekkoIRPlugin plugin(ret, base_virtual_address);
+
+ ParseWithPlugin(&plugin, assembly);
+
+ if (plugin.Error())
+ {
+ return FailureOr<GekkoIR>(std::move(*plugin.Error()));
+ }
+
+ return std::move(ret);
+}
+
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/GekkoIRGen.h b/Source/Core/Common/Assembler/GekkoIRGen.h
new file mode 100644
index 0000000000..4a1a56fa66
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoIRGen.h
@@ -0,0 +1,50 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string_view>
+#include <vector>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/GekkoLexer.h"
+#include "Common/CommonTypes.h"
+
+namespace Common::GekkoAssembler::detail
+{
+struct GekkoInstruction
+{
+ // Combination of a mnemonic index and variant:
+ // (<GekkoMnemonic> << 2) | (<variant bits>)
+ size_t mnemonic_index = 0;
+ // Below refers to GekkoParseResult::operand_pool
+ Interval op_interval = Interval{0, 0};
+ // Literal text of this instruction
+ std::string_view raw_text;
+ size_t line_number = 0;
+ bool is_extended = false;
+};
+
+using InstChunk = std::vector<GekkoInstruction>;
+using ByteChunk = std::vector<u8>;
+using PadChunk = size_t;
+using ChunkVariant = std::variant<InstChunk, ByteChunk, PadChunk>;
+
+struct IRBlock
+{
+ explicit IRBlock(u32 address) : block_address(address) {}
+
+ u32 BlockEndAddress() const;
+
+ std::vector<ChunkVariant> chunks;
+ u32 block_address;
+};
+
+struct GekkoIR
+{
+ std::vector<IRBlock> blocks;
+ std::vector<Tagged<Interval, u32>> operand_pool;
+};
+
+FailureOr<GekkoIR> ParseToIR(std::string_view assembly, u32 base_virtual_address);
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/GekkoLexer.cpp b/Source/Core/Common/Assembler/GekkoLexer.cpp
new file mode 100644
index 0000000000..8947802ecb
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoLexer.cpp
@@ -0,0 +1,794 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Common/Assembler/GekkoLexer.h"
+
+#include "Common/Assert.h"
+
+#include <iterator>
+#include <numeric>
+
+namespace Common::GekkoAssembler::detail
+{
+namespace
+{
+constexpr bool IsOctal(char c)
+{
+ return c >= '0' && c <= '7';
+}
+
+constexpr bool IsBinary(char c)
+{
+ return c == '0' || c == '1';
+}
+
+template <typename T>
+constexpr T ConvertNib(char c)
+{
+ if (c >= 'a' && c <= 'f')
+ {
+ return static_cast<T>(c - 'a' + 10);
+ }
+ if (c >= 'A' && c <= 'F')
+ {
+ return static_cast<T>(c - 'A' + 10);
+ }
+ return static_cast<T>(c - '0');
+}
+
+constexpr TokenType SingleCharToken(char ch)
+{
+ switch (ch)
+ {
+ case ',':
+ return TokenType::Comma;
+ case '(':
+ return TokenType::Lparen;
+ case ')':
+ return TokenType::Rparen;
+ case '|':
+ return TokenType::Pipe;
+ case '^':
+ return TokenType::Caret;
+ case '&':
+ return TokenType::Ampersand;
+ case '+':
+ return TokenType::Plus;
+ case '-':
+ return TokenType::Minus;
+ case '*':
+ return TokenType::Star;
+ case '/':
+ return TokenType::Slash;
+ case '~':
+ return TokenType::Tilde;
+ case '@':
+ return TokenType::At;
+ case ':':
+ return TokenType::Colon;
+ case '`':
+ return TokenType::Grave;
+ case '.':
+ return TokenType::Dot;
+ case '\0':
+ return TokenType::Eof;
+ case '\n':
+ return TokenType::Eol;
+ default:
+ return TokenType::Invalid;
+ }
+}
+
+// Convert a string literal into its raw-data form
+template <typename Cont>
+void ConvertStringLiteral(std::string_view literal, std::back_insert_iterator<Cont> out_it)
+{
+ for (size_t i = 1; i < literal.size() - 1;)
+ {
+ if (literal[i] == '\\')
+ {
+ ++i;
+ if (IsOctal(literal[i]))
+ {
+ // Octal escape
+ char octal_escape = 0;
+ for (char c = literal[i]; IsOctal(c); c = literal[++i])
+ {
+ octal_escape = (octal_escape << 3) + (c - '0');
+ }
+ out_it = static_cast<u8>(octal_escape);
+ }
+ else if (literal[i] == 'x')
+ {
+ // Hex escape
+ char hex_escape = 0;
+ for (char c = literal[++i]; std::isxdigit(c); c = literal[++i])
+ {
+ hex_escape = (hex_escape << 4) + ConvertNib<char>(c);
+ }
+ out_it = static_cast<u8>(hex_escape);
+ }
+ else
+ {
+ char simple_escape;
+ switch (literal[i])
+ {
+ case '\'':
+ simple_escape = '\x27';
+ break;
+ case '"':
+ simple_escape = '\x22';
+ break;
+ case '?':
+ simple_escape = '\x3f';
+ break;
+ case '\\':
+ simple_escape = '\x5c';
+ break;
+ case 'a':
+ simple_escape = '\x07';
+ break;
+ case 'b':
+ simple_escape = '\x08';
+ break;
+ case 'f':
+ simple_escape = '\x0c';
+ break;
+ case 'n':
+ simple_escape = '\x0a';
+ break;
+ case 'r':
+ simple_escape = '\x0d';
+ break;
+ case 't':
+ simple_escape = '\x09';
+ break;
+ case 'v':
+ simple_escape = '\x0b';
+ break;
+ default:
+ simple_escape = literal[i];
+ break;
+ }
+ out_it = static_cast<u8>(simple_escape);
+ ++i;
+ }
+ }
+ else
+ {
+ out_it = static_cast<u8>(literal[i]);
+ ++i;
+ }
+ }
+}
+
+template <typename T>
+std::optional<T> EvalIntegral(TokenType tp, std::string_view val)
+{
+ constexpr auto hex_step = [](T acc, char c) { return acc << 4 | ConvertNib<T>(c); };
+ constexpr auto dec_step = [](T acc, char c) { return acc * 10 + (c - '0'); };
+ constexpr auto oct_step = [](T acc, char c) { return acc << 3 | (c - '0'); };
+ constexpr auto bin_step = [](T acc, char c) { return acc << 1 | (c - '0'); };
+
+ switch (tp)
+ {
+ case TokenType::HexadecimalLit:
+ return std::accumulate(val.begin() + 2, val.end(), T{0}, hex_step);
+ case TokenType::DecimalLit:
+ return std::accumulate(val.begin(), val.end(), T{0}, dec_step);
+ case TokenType::OctalLit:
+ return std::accumulate(val.begin() + 1, val.end(), T{0}, oct_step);
+ case TokenType::BinaryLit:
+ return std::accumulate(val.begin() + 2, val.end(), T{0}, bin_step);
+ case TokenType::GPR:
+ case TokenType::FPR:
+ return std::accumulate(val.begin() + 1, val.end(), T{0}, dec_step);
+ case TokenType::CRField:
+ return std::accumulate(val.begin() + 2, val.end(), T{0}, dec_step);
+ case TokenType::SPR:
+ return static_cast<T>(*sprg_map.Find(val));
+ case TokenType::Lt:
+ return T{0};
+ case TokenType::Gt:
+ return T{1};
+ case TokenType::Eq:
+ return T{2};
+ case TokenType::So:
+ return T{3};
+ default:
+ return std::nullopt;
+ }
+}
+} // namespace
+
+void ConvertStringLiteral(std::string_view literal, std::vector<u8>* out_vec)
+{
+ ConvertStringLiteral(literal, std::back_inserter(*out_vec));
+}
+
+std::string_view TokenTypeToStr(TokenType tp)
+{
+ switch (tp)
+ {
+ case TokenType::GPR:
+ return "GPR";
+ case TokenType::FPR:
+ return "FPR";
+ case TokenType::SPR:
+ return "SPR";
+ case TokenType::CRField:
+ return "CR Field";
+ case TokenType::Lt:
+ case TokenType::Gt:
+ case TokenType::Eq:
+ case TokenType::So:
+ return "CR Bit";
+ case TokenType::Identifier:
+ return "Identifier";
+ case TokenType::StringLit:
+ return "String Literal";
+ case TokenType::DecimalLit:
+ return "Decimal Literal";
+ case TokenType::BinaryLit:
+ return "Binary Literal";
+ case TokenType::HexadecimalLit:
+ return "Hexadecimal Literal";
+ case TokenType::OctalLit:
+ return "Octal Literal";
+ case TokenType::FloatLit:
+ return "Float Literal";
+ case TokenType::Invalid:
+ return "Invalid";
+ case TokenType::Lsh:
+ return "<<";
+ case TokenType::Rsh:
+ return ">>";
+ case TokenType::Comma:
+ return ",";
+ case TokenType::Lparen:
+ return "(";
+ case TokenType::Rparen:
+ return ")";
+ case TokenType::Pipe:
+ return "|";
+ case TokenType::Caret:
+ return "^";
+ case TokenType::Ampersand:
+ return "&";
+ case TokenType::Plus:
+ return "+";
+ case TokenType::Minus:
+ return "-";
+ case TokenType::Star:
+ return "*";
+ case TokenType::Slash:
+ return "/";
+ case TokenType::Tilde:
+ return "~";
+ case TokenType::At:
+ return "@";
+ case TokenType::Colon:
+ return ":";
+ case TokenType::Grave:
+ return "`";
+ case TokenType::Dot:
+ return ".";
+ case TokenType::Eof:
+ return "End of File";
+ case TokenType::Eol:
+ return "End of Line";
+ default:
+ return "";
+ }
+}
+
+std::string_view AssemblerToken::TypeStr() const
+{
+ return TokenTypeToStr(token_type);
+}
+
+std::string_view AssemblerToken::ValStr() const
+{
+ switch (token_type)
+ {
+ case TokenType::Eol:
+ return "<EOL>";
+ case TokenType::Eof:
+ return "<EOF>";
+ default:
+ return token_val;
+ }
+}
+
+template <>
+std::optional<float> AssemblerToken::EvalToken() const
+{
+ if (token_type == TokenType::FloatLit)
+ {
+ return std::stof(std::string(token_val));
+ }
+ return std::nullopt;
+}
+
+template <>
+std::optional<double> AssemblerToken::EvalToken() const
+{
+ if (token_type == TokenType::FloatLit)
+ {
+ return std::stod(std::string(token_val));
+ }
+ return std::nullopt;
+}
+
+template <>
+std::optional<u8> AssemblerToken::EvalToken() const
+{
+ return EvalIntegral<u8>(token_type, token_val);
+}
+
+template <>
+std::optional<u16> AssemblerToken::EvalToken() const
+{
+ return EvalIntegral<u16>(token_type, token_val);
+}
+
+template <>
+std::optional<u32> AssemblerToken::EvalToken() const
+{
+ return EvalIntegral<u32>(token_type, token_val);
+}
+
+template <>
+std::optional<u64> AssemblerToken::EvalToken() const
+{
+ return EvalIntegral<u64>(token_type, token_val);
+}
+
+size_t Lexer::LineNumber() const
+{
+ return m_lexed_tokens.empty() ? m_pos.line : TagOf(m_lexed_tokens.front()).line;
+}
+
+size_t Lexer::ColNumber() const
+{
+ return m_lexed_tokens.empty() ? m_pos.col : TagOf(m_lexed_tokens.front()).col;
+}
+
+std::string_view Lexer::CurrentLine() const
+{
+ const size_t line_index =
+ m_lexed_tokens.empty() ? m_pos.index : TagOf(m_lexed_tokens.front()).index;
+ size_t begin_index = line_index == 0 ? 0 : line_index - 1;
+ for (; begin_index > 0; begin_index--)
+ {
+ if (m_lex_string[begin_index] == '\n')
+ {
+ begin_index++;
+ break;
+ }
+ }
+ size_t end_index = begin_index;
+ for (; end_index < m_lex_string.size(); end_index++)
+ {
+ if (m_lex_string[end_index] == '\n')
+ {
+ end_index++;
+ break;
+ }
+ }
+ return m_lex_string.substr(begin_index, end_index - begin_index);
+}
+
+void Lexer::SetIdentifierMatchRule(IdentifierMatchRule set)
+{
+ FeedbackTokens();
+ m_match_rule = set;
+}
+
+const Tagged<CursorPosition, AssemblerToken>& Lexer::LookaheadTagRef(size_t num_fwd) const
+{
+ while (m_lexed_tokens.size() < num_fwd)
+ {
+ LookaheadRef();
+ }
+ return m_lexed_tokens[num_fwd];
+}
+
+AssemblerToken Lexer::Lookahead() const
+{
+ if (m_lexed_tokens.empty())
+ {
+ CursorPosition pos_pre = m_pos;
+ m_lexed_tokens.emplace_back(pos_pre, LexSingle());
+ }
+ return ValueOf(m_lexed_tokens.front());
+}
+
+const AssemblerToken& Lexer::LookaheadRef() const
+{
+ if (m_lexed_tokens.empty())
+ {
+ CursorPosition pos_pre = m_pos;
+ m_lexed_tokens.emplace_back(pos_pre, LexSingle());
+ }
+ return ValueOf(m_lexed_tokens.front());
+}
+
+TokenType Lexer::LookaheadType() const
+{
+ return LookaheadRef().token_type;
+}
+
+AssemblerToken Lexer::LookaheadFloat() const
+{
+ FeedbackTokens();
+ SkipWs();
+
+ CursorPosition pos_pre = m_pos;
+ ScanStart();
+
+ std::optional<std::string_view> failure_reason = RunDfa(float_dfa);
+
+ // Special case: lex at least a single char for no matches for errors to make sense
+ if (m_scan_pos.index == pos_pre.index)
+ {
+ Step();
+ }
+
+ std::string_view tok_str = ScanFinishOut();
+ AssemblerToken tok;
+ if (!failure_reason)
+ {
+ tok = AssemblerToken{
+ TokenType::FloatLit,
+ tok_str,
+ "",
+ Interval{0, 0},
+ };
+ }
+ else
+ {
+ tok = AssemblerToken{
+ TokenType::Invalid,
+ tok_str,
+ *failure_reason,
+ Interval{0, tok_str.length()},
+ };
+ }
+
+ m_lexed_tokens.emplace_back(pos_pre, tok);
+ return tok;
+}
+
+void Lexer::Eat()
+{
+ if (m_lexed_tokens.empty())
+ {
+ LexSingle();
+ }
+ else
+ {
+ m_lexed_tokens.pop_front();
+ }
+}
+
+void Lexer::EatAndReset()
+{
+ Eat();
+ SetIdentifierMatchRule(IdentifierMatchRule::Typical);
+}
+
+std::optional<std::string_view> Lexer::RunDfa(const std::vector<DfaNode>& dfa) const
+{
+ size_t dfa_index = 0;
+ bool transition_found;
+ do
+ {
+ transition_found = false;
+ if (Peek() == '\0')
+ {
+ break;
+ }
+
+ const DfaNode& n = dfa[dfa_index];
+ for (auto&& edge : n.edges)
+ {
+ if (edge.first(Peek()))
+ {
+ transition_found = true;
+ dfa_index = edge.second;
+ break;
+ }
+ }
+
+ if (transition_found)
+ {
+ Step();
+ }
+ } while (transition_found);
+
+ return dfa[dfa_index].match_failure_reason;
+}
+
+void Lexer::SkipWs() const
+{
+ ScanStart();
+ for (char c = Peek(); std::isspace(c) && c != '\n'; c = Step().Peek())
+ {
+ }
+ if (Peek() == '#')
+ {
+ while (Peek() != '\n' && Peek() != '\0')
+ {
+ Step();
+ }
+ }
+ ScanFinish();
+}
+
+void Lexer::FeedbackTokens() const
+{
+ if (m_lexed_tokens.empty())
+ {
+ return;
+ }
+ m_pos = m_scan_pos = TagOf(m_lexed_tokens.front());
+ m_lexed_tokens.clear();
+}
+
+bool Lexer::IdentifierHeadExtra(char h) const
+{
+ switch (m_match_rule)
+ {
+ case IdentifierMatchRule::Typical:
+ case IdentifierMatchRule::Mnemonic:
+ return false;
+ case IdentifierMatchRule::Directive:
+ return std::isdigit(h);
+ }
+ return false;
+}
+
+bool Lexer::IdentifierExtra(char c) const
+{
+ switch (m_match_rule)
+ {
+ case IdentifierMatchRule::Typical:
+ case IdentifierMatchRule::Directive:
+ return false;
+ case IdentifierMatchRule::Mnemonic:
+ return c == '+' || c == '-' || c == '.';
+ }
+ return false;
+}
+
+void Lexer::ScanStart() const
+{
+ m_scan_pos = m_pos;
+}
+
+void Lexer::ScanFinish() const
+{
+ m_pos = m_scan_pos;
+}
+
+std::string_view Lexer::ScanFinishOut() const
+{
+ const size_t start = m_pos.index;
+ m_pos = m_scan_pos;
+ return m_lex_string.substr(start, m_scan_pos.index - start);
+}
+
+char Lexer::Peek() const
+{
+ if (m_scan_pos.index >= m_lex_string.length())
+ {
+ return 0;
+ }
+ return m_lex_string[m_scan_pos.index];
+}
+
+const Lexer& Lexer::Step() const
+{
+ if (m_scan_pos.index >= m_lex_string.length())
+ {
+ return *this;
+ }
+
+ if (Peek() == '\n')
+ {
+ m_scan_pos.line++;
+ m_scan_pos.col = 0;
+ }
+ else
+ {
+ m_scan_pos.col++;
+ }
+ m_scan_pos.index++;
+ return *this;
+}
+
+TokenType Lexer::LexStringLit(std::string_view& invalid_reason, Interval& invalid_region) const
+{
+ // The open quote has alread been matched
+ const size_t string_start = m_scan_pos.index - 1;
+ TokenType token_type = TokenType::StringLit;
+
+ std::optional<std::string_view> failure_reason = RunDfa(string_dfa);
+
+ if (failure_reason)
+ {
+ token_type = TokenType::Invalid;
+ invalid_reason = *failure_reason;
+ invalid_region = Interval{0, m_scan_pos.index - string_start};
+ }
+
+ return token_type;
+}
+
+TokenType Lexer::ClassifyAlnum() const
+{
+ const std::string_view alnum = m_lex_string.substr(m_pos.index, m_scan_pos.index - m_pos.index);
+ constexpr auto valid_regnum = [](std::string_view rn) {
+ if (rn.length() == 1 && std::isdigit(rn[0]))
+ {
+ return true;
+ }
+ else if (rn.length() == 2 && std::isdigit(rn[0]) && std::isdigit(rn[1]))
+ {
+ if (rn[0] == '1' || rn[0] == '2')
+ {
+ return true;
+ }
+
+ if (rn[0] == '3')
+ {
+ return rn[1] <= '2';
+ }
+ }
+
+ return false;
+ };
+ constexpr auto eq_nocase = [](std::string_view str, std::string_view lwr) {
+ auto it_l = str.cbegin(), it_r = lwr.cbegin();
+ for (; it_l != str.cend() && it_r != lwr.cend(); it_l++, it_r++)
+ {
+ if (std::tolower(*it_l) != *it_r)
+ {
+ return false;
+ }
+ }
+ return it_l == str.end() && it_r == lwr.end();
+ };
+
+ if (std::tolower(alnum[0]) == 'r' && valid_regnum(alnum.substr(1)))
+ {
+ return TokenType::GPR;
+ }
+ else if (std::tolower(alnum[0]) == 'f' && valid_regnum(alnum.substr(1)))
+ {
+ return TokenType::FPR;
+ }
+ else if (alnum.length() == 3 && eq_nocase(alnum.substr(0, 2), "cr") && alnum[2] >= '0' &&
+ alnum[2] <= '7')
+ {
+ return TokenType::CRField;
+ }
+ else if (eq_nocase(alnum, "lt"))
+ {
+ return TokenType::Lt;
+ }
+ else if (eq_nocase(alnum, "gt"))
+ {
+ return TokenType::Gt;
+ }
+ else if (eq_nocase(alnum, "eq"))
+ {
+ return TokenType::Eq;
+ }
+ else if (eq_nocase(alnum, "so"))
+ {
+ return TokenType::So;
+ }
+ else if (sprg_map.Find(alnum) != nullptr)
+ {
+ return TokenType::SPR;
+ }
+ return TokenType::Identifier;
+}
+
+AssemblerToken Lexer::LexSingle() const
+{
+ SkipWs();
+
+ ScanStart();
+ const char h = Peek();
+
+ TokenType token_type;
+ std::string_view invalid_reason = "";
+ Interval invalid_region = Interval{0, 0};
+
+ Step();
+
+ if (std::isalpha(h) || h == '_' || IdentifierHeadExtra(h))
+ {
+ for (char c = Peek(); std::isalnum(c) || c == '_' || IdentifierExtra(c); c = Step().Peek())
+ {
+ }
+
+ token_type = ClassifyAlnum();
+ }
+ else if (h == '"')
+ {
+ token_type = LexStringLit(invalid_reason, invalid_region);
+ }
+ else if (h == '0')
+ {
+ const char imm_type = Peek();
+
+ if (imm_type == 'x')
+ {
+ token_type = TokenType::HexadecimalLit;
+ Step();
+ for (char c = Peek(); std::isxdigit(c); c = Step().Peek())
+ {
+ }
+ }
+ else if (imm_type == 'b')
+ {
+ token_type = TokenType::BinaryLit;
+ Step();
+ for (char c = Peek(); IsBinary(c); c = Step().Peek())
+ {
+ }
+ }
+ else if (IsOctal(imm_type))
+ {
+ token_type = TokenType::OctalLit;
+ for (char c = Peek(); IsOctal(c); c = Step().Peek())
+ {
+ }
+ }
+ else
+ {
+ token_type = TokenType::DecimalLit;
+ }
+ }
+ else if (std::isdigit(h))
+ {
+ for (char c = Peek(); std::isdigit(c); c = Step().Peek())
+ {
+ }
+ token_type = TokenType::DecimalLit;
+ }
+ else if (h == '<' || h == '>')
+ {
+ // Special case for two-character operators
+ const char second_ch = Peek();
+ if (second_ch == h)
+ {
+ Step();
+ token_type = second_ch == '<' ? TokenType::Lsh : TokenType::Rsh;
+ }
+ else
+ {
+ token_type = TokenType::Invalid;
+ invalid_reason = "Unrecognized character";
+ invalid_region = Interval{0, 1};
+ }
+ }
+ else
+ {
+ token_type = SingleCharToken(h);
+ if (token_type == TokenType::Invalid)
+ {
+ invalid_reason = "Unrecognized character";
+ invalid_region = Interval{0, 1};
+ }
+ }
+
+ AssemblerToken new_tok = {token_type, ScanFinishOut(), invalid_reason, invalid_region};
+ SkipWs();
+ return new_tok;
+}
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/GekkoLexer.h b/Source/Core/Common/Assembler/GekkoLexer.h
new file mode 100644
index 0000000000..9ff78c04d0
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoLexer.h
@@ -0,0 +1,188 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <deque>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+#include <vector>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/AssemblerTables.h"
+#include "Common/CommonTypes.h"
+
+namespace Common::GekkoAssembler::detail
+{
+void ConvertStringLiteral(std::string_view literal, std::vector<u8>* out_vec);
+
+enum class TokenType
+{
+ Invalid,
+ Identifier,
+ StringLit,
+ HexadecimalLit,
+ DecimalLit,
+ OctalLit,
+ BinaryLit,
+ FloatLit,
+ GPR,
+ FPR,
+ CRField,
+ SPR,
+ Lt,
+ Gt,
+ Eq,
+ So,
+ // EOL signifies boundaries between instructions, a la ';'
+ Eol,
+ Eof,
+
+ Dot,
+ Colon,
+ Comma,
+ Lparen,
+ Rparen,
+ Pipe,
+ Caret,
+ Ampersand,
+ Lsh,
+ Rsh,
+ Plus,
+ Minus,
+ Star,
+ Slash,
+ Tilde,
+ Grave,
+ At,
+
+ OperatorBegin = Dot,
+ LastToken = At,
+};
+
+std::string_view TokenTypeToStr(TokenType);
+
+struct AssemblerToken
+{
+ TokenType token_type;
+ std::string_view token_val;
+ std::string_view invalid_reason;
+ // Within an invalid token, specifies the erroneous region
+ Interval invalid_region;
+
+ std::string_view TypeStr() const;
+ std::string_view ValStr() const;
+
+ // Supported Templates:
+ // u8, u16, u32, u64, float, double
+ template <typename T>
+ std::optional<T> EvalToken() const;
+};
+
+struct CursorPosition
+{
+ size_t index = 0;
+ size_t line = 0;
+ size_t col = 0;
+};
+
+class Lexer
+{
+public:
+ enum class IdentifierMatchRule
+ {
+ Typical,
+ Mnemonic, // Mnemonics can contain +, -, or . to specify branch prediction rules and link bit
+ Directive, // Directives can start with a digit
+ };
+
+public:
+ explicit Lexer(std::string_view str)
+ : m_lex_string(str), m_match_rule(IdentifierMatchRule::Typical)
+ {
+ }
+
+ size_t LineNumber() const;
+ size_t ColNumber() const;
+ std::string_view CurrentLine() const;
+
+ // Since there's only one place floats get lexed, it's 'okay' to have an explicit
+ // "lex a float token" function
+ void SetIdentifierMatchRule(IdentifierMatchRule set);
+ const Tagged<CursorPosition, AssemblerToken>& LookaheadTagRef(size_t num_fwd) const;
+ AssemblerToken Lookahead() const;
+ const AssemblerToken& LookaheadRef() const;
+ TokenType LookaheadType() const;
+ // Since there's only one place floats get lexed, it's 'okay' to have an explicit
+ // "lex a float token" function
+ AssemblerToken LookaheadFloat() const;
+ void Eat();
+ void EatAndReset();
+
+ template <size_t N>
+ void LookaheadTaggedN(std::array<Tagged<CursorPosition, AssemblerToken>, N>* tokens_out) const
+ {
+ const size_t filled_amt = std::min(m_lexed_tokens.size(), N);
+
+ std::copy_n(m_lexed_tokens.begin(), filled_amt, tokens_out->begin());
+
+ std::generate_n(tokens_out->begin() + filled_amt, N - filled_amt, [this] {
+ CursorPosition p = m_pos;
+ return m_lexed_tokens.emplace_back(p, LexSingle());
+ });
+ }
+
+ template <size_t N>
+ void LookaheadN(std::array<AssemblerToken, N>* tokens_out) const
+ {
+ const size_t filled_amt = std::min(m_lexed_tokens.size(), N);
+
+ auto _it = m_lexed_tokens.begin();
+ std::generate_n(tokens_out->begin(), filled_amt, [&_it] { return ValueOf(*_it++); });
+
+ std::generate_n(tokens_out->begin() + filled_amt, N - filled_amt, [this] {
+ CursorPosition p = m_pos;
+ return ValueOf(m_lexed_tokens.emplace_back(p, LexSingle()));
+ });
+ }
+
+ template <size_t N>
+ void EatN()
+ {
+ size_t consumed = 0;
+ while (m_lexed_tokens.size() > 0 && consumed < N)
+ {
+ m_lexed_tokens.pop_front();
+ consumed++;
+ }
+ for (size_t i = consumed; i < N; i++)
+ {
+ LexSingle();
+ }
+ }
+
+private:
+ std::optional<std::string_view> RunDfa(const std::vector<DfaNode>& dfa) const;
+ void SkipWs() const;
+ void FeedbackTokens() const;
+ bool IdentifierHeadExtra(char h) const;
+ bool IdentifierExtra(char c) const;
+ void ScanStart() const;
+ void ScanFinish() const;
+ std::string_view ScanFinishOut() const;
+ char Peek() const;
+ const Lexer& Step() const;
+ TokenType LexStringLit(std::string_view& invalid_reason, Interval& invalid_region) const;
+ TokenType ClassifyAlnum() const;
+ AssemblerToken LexSingle() const;
+
+ std::string_view m_lex_string;
+ mutable CursorPosition m_pos;
+ mutable CursorPosition m_scan_pos;
+ mutable std::deque<Tagged<CursorPosition, AssemblerToken>> m_lexed_tokens;
+ IdentifierMatchRule m_match_rule;
+};
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/GekkoParser.cpp b/Source/Core/Common/Assembler/GekkoParser.cpp
new file mode 100644
index 0000000000..26b5bb1082
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoParser.cpp
@@ -0,0 +1,885 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Common/Assembler/GekkoParser.h"
+
+#include <algorithm>
+#include <array>
+#include <functional>
+
+#include <fmt/format.h>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/AssemblerTables.h"
+#include "Common/Assembler/GekkoLexer.h"
+#include "Common/Assert.h"
+
+namespace Common::GekkoAssembler::detail
+{
+namespace
+{
+bool MatchOperandFirst(const AssemblerToken& tok)
+{
+ switch (tok.token_type)
+ {
+ case TokenType::Minus:
+ case TokenType::Tilde:
+ case TokenType::Lparen:
+ case TokenType::Grave:
+ case TokenType::Identifier:
+ case TokenType::DecimalLit:
+ case TokenType::OctalLit:
+ case TokenType::HexadecimalLit:
+ case TokenType::BinaryLit:
+ case TokenType::Dot:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void ParseImm(ParseState* state)
+{
+ AssemblerToken tok = state->lexer.Lookahead();
+ switch (tok.token_type)
+ {
+ case TokenType::HexadecimalLit:
+ state->plugin.OnTerminal(Terminal::Hex, tok);
+ break;
+ case TokenType::DecimalLit:
+ state->plugin.OnTerminal(Terminal::Dec, tok);
+ break;
+ case TokenType::OctalLit:
+ state->plugin.OnTerminal(Terminal::Oct, tok);
+ break;
+ case TokenType::BinaryLit:
+ state->plugin.OnTerminal(Terminal::Bin, tok);
+ break;
+ default:
+ state->EmitErrorHere(fmt::format("Invalid {} with value '{}'", tok.TypeStr(), tok.ValStr()));
+ return;
+ }
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.Eat();
+}
+
+void ParseId(ParseState* state)
+{
+ AssemblerToken tok = state->lexer.Lookahead();
+ if (tok.token_type == TokenType::Identifier)
+ {
+ state->plugin.OnTerminal(Terminal::Id, tok);
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.Eat();
+ }
+ else
+ {
+ state->EmitErrorHere(fmt::format("Expected an identifier, but found '{}'", tok.ValStr()));
+ }
+}
+
+void ParseIdLocation(ParseState* state)
+{
+ std::array<AssemblerToken, 3> toks;
+ state->lexer.LookaheadN(&toks);
+
+ if (toks[1].token_type == TokenType::At)
+ {
+ if (toks[2].token_val == "ha")
+ {
+ state->plugin.OnHiaddr(toks[0].token_val);
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.EatN<3>();
+ return;
+ }
+ else if (toks[2].token_val == "l")
+ {
+ state->plugin.OnLoaddr(toks[0].token_val);
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.EatN<3>();
+ return;
+ }
+ }
+
+ ParseId(state);
+}
+
+void ParsePpcBuiltin(ParseState* state)
+{
+ AssemblerToken tok = state->lexer.Lookahead();
+ switch (tok.token_type)
+ {
+ case TokenType::GPR:
+ state->plugin.OnTerminal(Terminal::GPR, tok);
+ break;
+ case TokenType::FPR:
+ state->plugin.OnTerminal(Terminal::FPR, tok);
+ break;
+ case TokenType::SPR:
+ state->plugin.OnTerminal(Terminal::SPR, tok);
+ break;
+ case TokenType::CRField:
+ state->plugin.OnTerminal(Terminal::CRField, tok);
+ break;
+ case TokenType::Lt:
+ state->plugin.OnTerminal(Terminal::Lt, tok);
+ break;
+ case TokenType::Gt:
+ state->plugin.OnTerminal(Terminal::Gt, tok);
+ break;
+ case TokenType::Eq:
+ state->plugin.OnTerminal(Terminal::Eq, tok);
+ break;
+ case TokenType::So:
+ state->plugin.OnTerminal(Terminal::So, tok);
+ break;
+ default:
+ state->EmitErrorHere(
+ fmt::format("Unexpected token '{}' in ppc builtin", state->lexer.LookaheadRef().ValStr()));
+ break;
+ }
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.Eat();
+}
+
+void ParseBaseexpr(ParseState* state)
+{
+ TokenType tok = state->lexer.LookaheadType();
+ switch (tok)
+ {
+ case TokenType::HexadecimalLit:
+ case TokenType::DecimalLit:
+ case TokenType::OctalLit:
+ case TokenType::BinaryLit:
+ ParseImm(state);
+ break;
+
+ case TokenType::Identifier:
+ ParseIdLocation(state);
+ break;
+
+ case TokenType::GPR:
+ case TokenType::FPR:
+ case TokenType::SPR:
+ case TokenType::CRField:
+ case TokenType::Lt:
+ case TokenType::Gt:
+ case TokenType::Eq:
+ case TokenType::So:
+ ParsePpcBuiltin(state);
+ break;
+
+ case TokenType::Dot:
+ state->plugin.OnTerminal(Terminal::Dot, state->lexer.Lookahead());
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.Eat();
+ break;
+
+ default:
+ state->EmitErrorHere(
+ fmt::format("Unexpected token '{}' in expression", state->lexer.LookaheadRef().ValStr()));
+ break;
+ }
+}
+
+void ParseBitor(ParseState* state);
+void ParseParen(ParseState* state)
+{
+ if (state->HasToken(TokenType::Lparen))
+ {
+ state->plugin.OnOpenParen(ParenType::Normal);
+ if (state->error)
+ {
+ return;
+ }
+
+ state->lexer.Eat();
+ ParseBitor(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ if (state->HasToken(TokenType::Rparen))
+ {
+ state->plugin.OnCloseParen(ParenType::Normal);
+ }
+ state->ParseToken(TokenType::Rparen);
+ }
+ else if (state->HasToken(TokenType::Grave))
+ {
+ state->plugin.OnOpenParen(ParenType::RelConv);
+
+ state->lexer.Eat();
+ ParseBitor(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ if (state->HasToken(TokenType::Grave))
+ {
+ state->plugin.OnCloseParen(ParenType::RelConv);
+ }
+ state->ParseToken(TokenType::Grave);
+ }
+ else
+ {
+ ParseBaseexpr(state);
+ }
+}
+
+void ParseUnary(ParseState* state)
+{
+ TokenType tok = state->lexer.LookaheadType();
+ if (tok == TokenType::Minus || tok == TokenType::Tilde)
+ {
+ state->lexer.Eat();
+ ParseUnary(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ if (tok == TokenType::Minus)
+ {
+ state->plugin.OnOperator(AsmOp::Neg);
+ }
+ else
+ {
+ state->plugin.OnOperator(AsmOp::Not);
+ }
+ }
+ else
+ {
+ ParseParen(state);
+ }
+}
+
+void ParseMultiplication(ParseState* state)
+{
+ ParseUnary(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ TokenType tok = state->lexer.LookaheadType();
+ while (tok == TokenType::Star || tok == TokenType::Slash)
+ {
+ state->lexer.Eat();
+ ParseUnary(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ if (tok == TokenType::Star)
+ {
+ state->plugin.OnOperator(AsmOp::Mul);
+ }
+ else
+ {
+ state->plugin.OnOperator(AsmOp::Div);
+ }
+ tok = state->lexer.LookaheadType();
+ }
+}
+
+void ParseAddition(ParseState* state)
+{
+ ParseMultiplication(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ TokenType tok = state->lexer.LookaheadType();
+ while (tok == TokenType::Plus || tok == TokenType::Minus)
+ {
+ state->lexer.Eat();
+ ParseMultiplication(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ if (tok == TokenType::Plus)
+ {
+ state->plugin.OnOperator(AsmOp::Add);
+ }
+ else
+ {
+ state->plugin.OnOperator(AsmOp::Sub);
+ }
+ tok = state->lexer.LookaheadType();
+ }
+}
+
+void ParseShift(ParseState* state)
+{
+ ParseAddition(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ TokenType tok = state->lexer.LookaheadType();
+ while (tok == TokenType::Lsh || tok == TokenType::Rsh)
+ {
+ state->lexer.Eat();
+ ParseAddition(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ if (tok == TokenType::Lsh)
+ {
+ state->plugin.OnOperator(AsmOp::Lsh);
+ }
+ else
+ {
+ state->plugin.OnOperator(AsmOp::Rsh);
+ }
+ tok = state->lexer.LookaheadType();
+ }
+}
+
+void ParseBitand(ParseState* state)
+{
+ ParseShift(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ while (state->HasToken(TokenType::Ampersand))
+ {
+ state->lexer.Eat();
+ ParseShift(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ state->plugin.OnOperator(AsmOp::And);
+ }
+}
+
+void ParseBitxor(ParseState* state)
+{
+ ParseBitand(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ while (state->HasToken(TokenType::Caret))
+ {
+ state->lexer.Eat();
+ ParseBitand(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ state->plugin.OnOperator(AsmOp::Xor);
+ }
+}
+
+void ParseBitor(ParseState* state)
+{
+ ParseBitxor(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ while (state->HasToken(TokenType::Pipe))
+ {
+ state->lexer.Eat();
+ ParseBitxor(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ state->plugin.OnOperator(AsmOp::Or);
+ }
+}
+
+void ParseOperand(ParseState* state)
+{
+ state->plugin.OnOperandPre();
+ ParseBitor(state);
+ if (state->error)
+ {
+ return;
+ }
+ state->plugin.OnOperandPost();
+}
+
+void ParseOperandList(ParseState* state, ParseAlg alg)
+{
+ if (alg == ParseAlg::None)
+ {
+ return;
+ }
+ if (alg == ParseAlg::NoneOrOp1)
+ {
+ if (MatchOperandFirst(state->lexer.Lookahead()))
+ {
+ ParseOperand(state);
+ }
+ return;
+ }
+
+ enum ParseStep
+ {
+ _Operand,
+ _Comma,
+ _Lparen,
+ _Rparen,
+ _OptComma
+ };
+ std::vector<ParseStep> steps;
+
+ switch (alg)
+ {
+ case ParseAlg::Op1:
+ steps = {_Operand};
+ break;
+ case ParseAlg::Op1Or2:
+ steps = {_Operand, _OptComma, _Operand};
+ break;
+ case ParseAlg::Op2Or3:
+ steps = {_Operand, _Comma, _Operand, _OptComma, _Operand};
+ break;
+ case ParseAlg::Op1Off1:
+ steps = {_Operand, _Comma, _Operand, _Lparen, _Operand, _Rparen};
+ break;
+ case ParseAlg::Op2:
+ steps = {_Operand, _Comma, _Operand};
+ break;
+ case ParseAlg::Op3:
+ steps = {_Operand, _Comma, _Operand, _Comma, _Operand};
+ break;
+ case ParseAlg::Op4:
+ steps = {_Operand, _Comma, _Operand, _Comma, _Operand, _Comma, _Operand};
+ break;
+ case ParseAlg::Op5:
+ steps = {_Operand, _Comma, _Operand, _Comma, _Operand, _Comma, _Operand, _Comma, _Operand};
+ break;
+ case ParseAlg::Op1Off1Op2:
+ steps = {_Operand, _Comma, _Operand, _Lparen, _Operand,
+ _Rparen, _Comma, _Operand, _Comma, _Operand};
+ break;
+ default:
+ ASSERT(false);
+ return;
+ }
+
+ for (ParseStep step : steps)
+ {
+ bool stop_parse = false;
+ switch (step)
+ {
+ case _Operand:
+ ParseOperand(state);
+ break;
+ case _Comma:
+ state->ParseToken(TokenType::Comma);
+ break;
+ case _Lparen:
+ state->ParseToken(TokenType::Lparen);
+ break;
+ case _Rparen:
+ state->ParseToken(TokenType::Rparen);
+ break;
+ case _OptComma:
+ if (state->HasToken(TokenType::Comma))
+ {
+ state->ParseToken(TokenType::Comma);
+ }
+ else
+ {
+ stop_parse = true;
+ }
+ break;
+ }
+ if (state->error)
+ {
+ return;
+ }
+ if (stop_parse)
+ {
+ break;
+ }
+ }
+}
+
+void ParseInstruction(ParseState* state)
+{
+ state->lexer.SetIdentifierMatchRule(Lexer::IdentifierMatchRule::Mnemonic);
+
+ AssemblerToken mnemonic_token = state->lexer.Lookahead();
+ if (mnemonic_token.token_type != TokenType::Identifier)
+ {
+ state->lexer.SetIdentifierMatchRule(Lexer::IdentifierMatchRule::Typical);
+ return;
+ }
+
+ ParseInfo const* parse_info = mnemonic_tokens.Find(mnemonic_token.token_val);
+ bool is_extended = false;
+ if (parse_info == nullptr)
+ {
+ parse_info = extended_mnemonic_tokens.Find(mnemonic_token.token_val);
+ if (parse_info == nullptr)
+ {
+ state->EmitErrorHere(
+ fmt::format("Unknown or unsupported mnemonic '{}'", mnemonic_token.ValStr()));
+ return;
+ }
+ is_extended = true;
+ }
+
+ state->plugin.OnInstructionPre(*parse_info, is_extended);
+
+ state->lexer.EatAndReset();
+
+ ParseOperandList(state, parse_info->parse_algorithm);
+ if (state->error)
+ {
+ return;
+ }
+
+ state->plugin.OnInstructionPost(*parse_info, is_extended);
+}
+
+void ParseLabel(ParseState* state)
+{
+ std::array<AssemblerToken, 2> tokens;
+ state->lexer.LookaheadN(&tokens);
+
+ if (tokens[0].token_type == TokenType::Identifier && tokens[1].token_type == TokenType::Colon)
+ {
+ state->plugin.OnLabelDecl(tokens[0].token_val);
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.EatN<2>();
+ }
+}
+
+void ParseResolvedExpr(ParseState* state)
+{
+ state->plugin.OnResolvedExprPre();
+ ParseBitor(state);
+ if (state->error)
+ {
+ return;
+ }
+ state->plugin.OnResolvedExprPost();
+}
+
+void ParseExpressionList(ParseState* state)
+{
+ ParseResolvedExpr(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ while (state->HasToken(TokenType::Comma))
+ {
+ state->lexer.Eat();
+ ParseResolvedExpr(state);
+ if (state->error)
+ {
+ return;
+ }
+ }
+}
+
+void ParseFloat(ParseState* state)
+{
+ AssemblerToken flt_token = state->lexer.LookaheadFloat();
+ if (flt_token.token_type != TokenType::FloatLit)
+ {
+ state->EmitErrorHere("Invalid floating point literal");
+ return;
+ }
+ state->plugin.OnTerminal(Terminal::Flt, flt_token);
+ state->lexer.Eat();
+}
+
+void ParseFloatList(ParseState* state)
+{
+ ParseFloat(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ while (state->HasToken(TokenType::Comma))
+ {
+ state->lexer.Eat();
+ ParseFloat(state);
+ if (state->error)
+ {
+ return;
+ }
+ }
+}
+
+void ParseDefvar(ParseState* state)
+{
+ AssemblerToken tok = state->lexer.Lookahead();
+ if (tok.token_type == TokenType::Identifier)
+ {
+ state->plugin.OnVarDecl(tok.token_val);
+ if (state->error)
+ {
+ return;
+ }
+ state->lexer.Eat();
+
+ state->ParseToken(TokenType::Comma);
+ if (state->error)
+ {
+ return;
+ }
+
+ ParseResolvedExpr(state);
+ }
+ else
+ {
+ state->EmitErrorHere(fmt::format("Expected an identifier, but found '{}'", tok.ValStr()));
+ }
+}
+
+void ParseString(ParseState* state)
+{
+ AssemblerToken tok = state->lexer.Lookahead();
+ if (tok.token_type == TokenType::StringLit)
+ {
+ state->plugin.OnTerminal(Terminal::Str, tok);
+ state->lexer.Eat();
+ }
+ else
+ {
+ state->EmitErrorHere(fmt::format("Expected a string literal, but found '{}'", tok.ValStr()));
+ }
+}
+
+void ParseDirective(ParseState* state)
+{
+ // TODO: test directives
+ state->lexer.SetIdentifierMatchRule(Lexer::IdentifierMatchRule::Directive);
+ AssemblerToken tok = state->lexer.Lookahead();
+ if (tok.token_type != TokenType::Identifier)
+ {
+ state->EmitErrorHere(fmt::format("Unexpected token '{}' in directive type", tok.ValStr()));
+ return;
+ }
+
+ GekkoDirective const* directive_enum = directives_map.Find(tok.token_val);
+ if (directive_enum == nullptr)
+ {
+ state->EmitErrorHere(fmt::format("Unknown assembler directive '{}'", tok.ValStr()));
+ return;
+ }
+
+ state->plugin.OnDirectivePre(*directive_enum);
+
+ state->lexer.EatAndReset();
+ switch (*directive_enum)
+ {
+ case GekkoDirective::Byte:
+ case GekkoDirective::_2byte:
+ case GekkoDirective::_4byte:
+ case GekkoDirective::_8byte:
+ ParseExpressionList(state);
+ break;
+
+ case GekkoDirective::Float:
+ case GekkoDirective::Double:
+ ParseFloatList(state);
+ break;
+
+ case GekkoDirective::Locate:
+ case GekkoDirective::Zeros:
+ case GekkoDirective::Skip:
+ ParseResolvedExpr(state);
+ break;
+
+ case GekkoDirective::PadAlign:
+ case GekkoDirective::Align:
+ ParseImm(state);
+ break;
+
+ case GekkoDirective::DefVar:
+ ParseDefvar(state);
+ break;
+
+ case GekkoDirective::Ascii:
+ case GekkoDirective::Asciz:
+ ParseString(state);
+ break;
+ }
+
+ if (state->error)
+ {
+ return;
+ }
+
+ state->plugin.OnDirectivePost(*directive_enum);
+}
+
+void ParseLine(ParseState* state)
+{
+ if (state->HasToken(TokenType::Dot))
+ {
+ state->ParseToken(TokenType::Dot);
+ ParseDirective(state);
+ }
+ else
+ {
+ ParseInstruction(state);
+ }
+}
+
+void ParseProgram(ParseState* state)
+{
+ AssemblerToken tok = state->lexer.Lookahead();
+ if (tok.token_type == TokenType::Eof)
+ {
+ state->eof = true;
+ return;
+ }
+ ParseLabel(state);
+ if (state->error)
+ {
+ return;
+ }
+ ParseLine(state);
+ if (state->error)
+ {
+ return;
+ }
+
+ while (!state->eof && !state->error)
+ {
+ tok = state->lexer.Lookahead();
+ if (tok.token_type == TokenType::Eof)
+ {
+ state->eof = true;
+ }
+ else if (tok.token_type == TokenType::Eol)
+ {
+ state->lexer.Eat();
+ ParseLabel(state);
+ if (state->error)
+ {
+ return;
+ }
+ ParseLine(state);
+ }
+ else
+ {
+ state->EmitErrorHere(
+ fmt::format("Unexpected token '{}' where line should have ended", tok.ValStr()));
+ }
+ }
+}
+} // namespace
+
+ParseState::ParseState(std::string_view input_str, ParsePlugin& p)
+ : lexer(input_str), plugin(p), eof(false)
+{
+}
+
+bool ParseState::HasToken(TokenType tp) const
+{
+ return lexer.LookaheadType() == tp;
+}
+
+void ParseState::ParseToken(TokenType tp)
+{
+ AssemblerToken tok = lexer.LookaheadRef();
+ if (tok.token_type == tp)
+ {
+ lexer.Eat();
+ }
+ else
+ {
+ EmitErrorHere(fmt::format("Expected '{}' but found '{}'", TokenTypeToStr(tp), tok.ValStr()));
+ }
+}
+
+void ParseState::EmitErrorHere(std::string&& message)
+{
+ AssemblerToken cur_token = lexer.Lookahead();
+ if (cur_token.token_type == TokenType::Invalid)
+ {
+ error = AssemblerError{
+ std::string(cur_token.invalid_reason),
+ lexer.CurrentLine(),
+ lexer.LineNumber(),
+ lexer.ColNumber() + cur_token.invalid_region.begin,
+ cur_token.invalid_region.len,
+ };
+ }
+ else
+ {
+ error = AssemblerError{
+ std::move(message), lexer.CurrentLine(), lexer.LineNumber(),
+ lexer.ColNumber(), cur_token.token_val.size(),
+ };
+ }
+}
+
+void ParseWithPlugin(ParsePlugin* plugin, std::string_view input)
+{
+ ParseState parse_state = ParseState(input, *plugin);
+ plugin->SetOwner(&parse_state);
+ ParseProgram(&parse_state);
+
+ if (parse_state.error)
+ {
+ plugin->OnError();
+ plugin->ForwardError(std::move(*parse_state.error));
+ }
+ else
+ {
+ plugin->PostParseAction();
+ if (parse_state.error)
+ {
+ plugin->OnError();
+ plugin->ForwardError(std::move(*parse_state.error));
+ }
+ }
+
+ plugin->SetOwner(nullptr);
+}
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/Assembler/GekkoParser.h b/Source/Core/Common/Assembler/GekkoParser.h
new file mode 100644
index 0000000000..4258b5d582
--- /dev/null
+++ b/Source/Core/Common/Assembler/GekkoParser.h
@@ -0,0 +1,124 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <string_view>
+
+#include "Common/Assembler/AssemblerShared.h"
+#include "Common/Assembler/GekkoLexer.h"
+#include "Common/CommonTypes.h"
+
+namespace Common::GekkoAssembler::detail
+{
+class ParsePlugin;
+
+struct ParseState
+{
+ ParseState(std::string_view input_str, ParsePlugin& plugin);
+
+ bool HasToken(TokenType tp) const;
+ void ParseToken(TokenType tp);
+ void EmitErrorHere(std::string&& message);
+
+ Lexer lexer;
+ ParsePlugin& plugin;
+
+ std::optional<AssemblerError> error;
+ bool eof;
+};
+
+enum class AsmOp
+{
+ Or,
+ Xor,
+ And,
+ Lsh,
+ Rsh,
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Neg,
+ Not
+};
+
+enum class Terminal
+{
+ Hex,
+ Dec,
+ Oct,
+ Bin,
+ Flt,
+ Str,
+ Id,
+ GPR,
+ FPR,
+ SPR,
+ CRField,
+ Lt,
+ Gt,
+ Eq,
+ So,
+ Dot,
+};
+
+enum class ParenType
+{
+ Normal,
+ RelConv,
+};
+
+// Overridable plugin class supporting a series of skeleton functions which get called when
+// the parser parses a given point of interest
+class ParsePlugin
+{
+public:
+ ParsePlugin() : m_owner(nullptr) {}
+ virtual ~ParsePlugin() = default;
+
+ void SetOwner(ParseState* o) { m_owner = o; }
+ void ForwardError(AssemblerError&& err) { m_owner_error = std::move(err); }
+ std::optional<AssemblerError>& Error() { return m_owner_error; }
+
+ virtual void PostParseAction() {}
+
+ // Nonterminal callouts
+ // Pre occurs prior to the head nonterminal being parsed
+ // Post occurs after the nonterminal has been fully parsed
+ virtual void OnDirectivePre(GekkoDirective directive) {}
+ virtual void OnDirectivePost(GekkoDirective directive) {}
+ virtual void OnInstructionPre(const ParseInfo& mnemonic_info, bool extended) {}
+ virtual void OnInstructionPost(const ParseInfo& mnemonic_info, bool extended) {}
+ virtual void OnOperandPre() {}
+ virtual void OnOperandPost() {}
+ virtual void OnResolvedExprPre() {}
+ virtual void OnResolvedExprPost() {}
+
+ // Operator callouts
+ // All occur after the relevant operands have been parsed
+ virtual void OnOperator(AsmOp operation) {}
+
+ // Individual token callouts
+ // All occur prior to the token being parsed
+ // Due to ambiguity of some tokens, an explicit operation is provided
+ virtual void OnTerminal(Terminal type, const AssemblerToken& val) {}
+ virtual void OnHiaddr(std::string_view id) {}
+ virtual void OnLoaddr(std::string_view id) {}
+ virtual void OnOpenParen(ParenType type) {}
+ virtual void OnCloseParen(ParenType type) {}
+ virtual void OnError() {}
+ virtual void OnLabelDecl(std::string_view name) {}
+ virtual void OnVarDecl(std::string_view name) {}
+
+protected:
+ ParseState* m_owner;
+ std::optional<AssemblerError> m_owner_error;
+};
+
+// Parse the provided input with a plugin to handle what to do with certain points of interest
+// e.g. Convert to an IR for generating final machine code, picking up syntactical information
+void ParseWithPlugin(ParsePlugin* plugin, std::string_view input);
+} // namespace Common::GekkoAssembler::detail
diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index 2c4ec7a6f1..a4a761276b 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -1,6 +1,18 @@
add_library(common
Analytics.cpp
Analytics.h
+ Assembler/AssemblerShared.cpp
+ Assembler/AssemblerShared.h
+ Assembler/AssemblerTables.cpp
+ Assembler/AssemblerTables.h
+ Assembler/GekkoAssembler.cpp
+ Assembler/GekkoAssembler.h
+ Assembler/GekkoIRGen.cpp
+ Assembler/GekkoIRGen.h
+ Assembler/GekkoLexer.cpp
+ Assembler/GekkoLexer.h
+ Assembler/GekkoParser.cpp
+ Assembler/GekkoParser.h
Assert.h
BitField.h
BitSet.h
diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h
index 4510842089..bf30fb98a9 100644
--- a/Source/Core/Common/CommonPaths.h
+++ b/Source/Core/Common/CommonPaths.h
@@ -94,6 +94,7 @@
#define DYNAMICINPUT_DIR "DynamicInputTextures"
#define GRAPHICSMOD_DIR "GraphicMods"
#define WIISDSYNC_DIR "WiiSDSync"
+#define ASSEMBLY_DIR "SavedAssembly"
// This one is only used to remove it if it was present
#define SHADERCACHE_LEGACY_DIR "ShaderCache"
diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp
index 4b211fec5e..562e66ff56 100644
--- a/Source/Core/Common/FileUtil.cpp
+++ b/Source/Core/Common/FileUtil.cpp
@@ -896,6 +896,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_GBASAVES_IDX] = s_user_paths[D_GBAUSER_IDX] + GBASAVES_DIR DIR_SEP;
s_user_paths[F_GBABIOS_IDX] = s_user_paths[D_GBAUSER_IDX] + GBA_BIOS;
+ s_user_paths[D_ASM_ROOT_IDX] = s_user_paths[D_USER_IDX] + ASSEMBLY_DIR DIR_SEP;
+
// The shader cache has moved to the cache directory, so remove the old one.
// TODO: remove that someday.
File::DeleteDirRecursively(s_user_paths[D_USER_IDX] + SHADERCACHE_LEGACY_DIR DIR_SEP);
diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h
index 276dd6caa8..94491cecfd 100644
--- a/Source/Core/Common/FileUtil.h
+++ b/Source/Core/Common/FileUtil.h
@@ -71,6 +71,7 @@ enum
D_GPU_DRIVERS_TMP,
D_GPU_DRIVERS_HOOKS,
D_GPU_DRIVERS_FILE_REDIRECT,
+ D_ASM_ROOT_IDX,
FIRST_FILE_USER_PATH_IDX,
F_DOLPHINCONFIG_IDX = FIRST_FILE_USER_PATH_IDX,
F_GCPADCONFIG_IDX,