fxos/include/fxos/lang.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

189 lines
6.1 KiB
C
Raw Normal View History

//---------------------------------------------------------------------------//
// 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 <string>
#include <cstdint>
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 */