//---------------------------------------------------------------------------// // 1100101 |_ mov #0, r4 __ // // 11 |_ <0xb380 %5c4> / _|_ _____ ___ // // 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< // // |_ base# + offset |_| /_\_\___/__/ // //---------------------------------------------------------------------------// // fxos/lang: Assembler language syntax // // This file defines the syntactic tools needed to read and manipulate // assembler instructions. // // The CpuRegister class is a glorified type-safe enumeration. Registers can be // named, fi. CpuRegister::R0; they can be constructed from their lowercase // name as a string, fi. CpuRegister("r0"); and they can be printed with the // .str() method. // // The Argument struct represents an argument to an instruction. This is // syntactic only; for instance Deref (@rn) does not mean that memory is // accessed, since [jmp @rn] or [ocbwb @rn] do not actually access @rn. // Constructor functions such as Argument_Deref() are provided. // // Finally, the Instruction struct represents an abstract instruction out of // context. Each Instruction object only models one particular instance of one // particular instruction, for instance [mov #14, r2] and not [mov #imm, rn]. // The rationale for this is disassembly speed and a number of simplifications // for passes; and there are less than 65'000 non-DSP instructions anyway. //--- #ifndef FXOS_LANG_H #define FXOS_LANG_H #include #include namespace FxOS { /* CPU register names, with a little meat for conversion to and from string */ class CpuRegister { public: // clang-format off enum CpuRegisterName: int8_t { /* Value 0 is reserved for special purposes such as "no register" */ UNDEFINED = 0, /* Caller-saved general-purpose registers */ R0, R1, R2, R3, R4, R5, R6, R7, /* Banked general-purpose registers. fxos does not account for banking identities, these are just for naming and output. */ R0B, R1B, R2B, R3B, R4B, R5B, R6B, R7B, /* Callee-saved general-purpose registers */ R8, R9, R10, R11, R12, R13, R14, R15, /* System registers */ MACH, MACL, PR, PC, /* Control registers */ SR, SSR, SPC, GBR, VBR, DBR, SGR, }; // clang-format on CpuRegister() = default; /* Construction from CpuRegisterName */ constexpr CpuRegister(CpuRegisterName name): m_name(name) { } /* Construction from string */ CpuRegister(std::string register_name); /* Conversion to string */ std::string str() const noexcept; /* Conversion to CpuRegisterName for switch statements */ constexpr operator CpuRegisterName() noexcept { return m_name; } /* Comparison operators */ constexpr bool operator==(CpuRegister r) const { return m_name == r.m_name; } constexpr bool operator!=(CpuRegister r) const { return m_name != r.m_name; } private: CpuRegisterName m_name; }; /* Addressing modes for arguments */ struct AsmArgument { /* Various addressing modes in the language */ enum Kind : int8_t { Reg, /* rn */ Deref, /* @rn */ PostInc, /* @rn+ */ PreDec, /* @-rn */ StructDeref, /* @(disp,rn) or @(disp,gbr) */ ArrayDeref, /* @(r0,rn) or @(r0,gbr) */ PcRel, /* @(disp,pc) with 4-alignment correction */ PcJump, /* pc+disp */ PcAddr, /* pc+disp (the address itself, for mova) */ Imm, /* #imm */ }; AsmArgument() = default; /* String representation */ std::string str() const; /* Addressing mode */ Kind kind; /* Base register. Valid for all modes except Imm */ CpuRegister base; /* Index register. Valid for ArrayDeref */ CpuRegister index; /* Operation size (0, 1, 2 or 4). Generally a multiplier for disp */ int8_t opsize; union { /* Displacement in bytes. For StructDeref, PcRel, PcJump, and PcAddr */ int disp; /* Immediate value. Valid for Imm */ int imm; }; }; /* AsmArgument constructors */ AsmArgument AsmArgument_Reg(CpuRegister base); AsmArgument AsmArgument_Deref(CpuRegister base); AsmArgument AsmArgument_PostInc(CpuRegister base); AsmArgument AsmArgument_PreDec(CpuRegister base); AsmArgument AsmArgument_StructDeref(int disp, int opsize, CpuRegister base); AsmArgument AsmArgument_ArrayDeref(CpuRegister index, CpuRegister base); AsmArgument AsmArgument_PcRel(int disp, int opsize); AsmArgument AsmArgument_PcJump(int disp); AsmArgument AsmArgument_PcAddr(int disp); AsmArgument AsmArgument_Imm(int imm); /* Assembler instruction */ struct AsmInstruction { AsmInstruction() = default; /* Construct with one or several arguments */ AsmInstruction(char const *mnemonic); AsmInstruction(char const *mnemonic, AsmArgument arg); AsmInstruction(char const *mnemonic, AsmArgument arg1, AsmArgument arg2); /* Original opcode. Initialized to 0 when unset, which is an invalid instruction by design. */ uint16_t opcode; /* Operation size (0, 1, 2 or 4) */ int8_t opsize; /* Number of arguments */ uint8_t arg_count; /* Mnemonic **without the size indicator** */ char mnemonic[12]; /* Arguments (up to 2) */ AsmArgument args[2]; //--- // Instruction classes //--- /* Check whether instruction terminates the function */ bool isterminal() const noexcept; /* Check whether instruction is an unconditional jump */ bool isjump() const noexcept; /* Check whether it's a conditional jump */ bool iscondjump() const noexcept; /* Check whether instruction is a function call */ bool iscall() const noexcept; /* Check whether instruction has a delay slot */ bool isdelayed() const noexcept; /* Check whether instruction can be used in a delay slot */ bool isvaliddelayslot() const noexcept; }; } /* namespace FxOS */ #endif /* FXOS_LANG_H */