fxos/lib/lang.cpp

303 lines
6.5 KiB
C++

//---------------------------------------------------------------------------//
// 1100101 |_ mov #0, r4 __ //
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
#include <fxos/lang.h>
#include <fxos/util/format.h>
#include <string>
#include <cstring>
namespace FxOS {
//---
// CPU registers
//---
// clang-format off
char const *regnames[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r0_bank", "r1_bank", "r2_bank", "r3_bank",
"r4_bank", "r5_bank", "r6_bank", "r7_bank",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"mach", "macl", "pr", "pc",
"sr", "ssr", "spc", "gbr", "vbr", "dbr", "sgr"
};
// clang-format on
/* Construction from string */
CpuRegister::CpuRegister(std::string name)
{
int regcount = (sizeof regnames / sizeof regnames[0]);
char const *name_c = name.c_str();
for(int i = 0; i < regcount; i++) {
if(!strcmp(regnames[i], name_c)) {
m_name = CpuRegisterName(i + 1);
return;
}
}
m_name = CpuRegister::UNDEFINED;
}
/* Conversion to string */
std::string CpuRegister::str() const noexcept
{
int regcount = (sizeof regnames / sizeof regnames[0]);
int i = m_name - 1;
if(i < 0 || i >= regcount)
return format("<Register%d>", i + 1);
return regnames[i];
}
//---
// Instruction arguments
//---
/* External constructors */
AsmArgument AsmArgument_Reg(CpuRegister base)
{
AsmArgument arg;
arg.kind = AsmArgument::Reg;
arg.base = base;
return arg;
}
AsmArgument AsmArgument_Deref(CpuRegister base)
{
AsmArgument arg;
arg.kind = AsmArgument::Deref;
arg.base = base;
return arg;
}
AsmArgument AsmArgument_PostInc(CpuRegister base)
{
AsmArgument arg;
arg.kind = AsmArgument::PostInc;
arg.base = base;
return arg;
}
AsmArgument AsmArgument_PreDec(CpuRegister base)
{
AsmArgument arg;
arg.kind = AsmArgument::PreDec;
arg.base = base;
return arg;
}
AsmArgument AsmArgument_StructDeref(int disp, int opsize, CpuRegister base)
{
AsmArgument arg;
arg.kind = AsmArgument::StructDeref;
arg.base = base;
arg.disp = disp;
arg.opsize = opsize;
return arg;
}
AsmArgument AsmArgument_ArrayDeref(CpuRegister index, CpuRegister base)
{
AsmArgument arg;
arg.kind = AsmArgument::ArrayDeref;
arg.base = base;
arg.index = index;
return arg;
}
AsmArgument AsmArgument_PcRel(int disp, int opsize)
{
AsmArgument arg;
arg.kind = AsmArgument::PcRel;
arg.disp = disp;
arg.opsize = opsize;
return arg;
}
AsmArgument AsmArgument_PcJump(int disp)
{
AsmArgument arg;
arg.kind = AsmArgument::PcJump;
arg.disp = disp;
return arg;
}
AsmArgument AsmArgument_PcAddr(int disp)
{
AsmArgument arg;
arg.kind = AsmArgument::PcAddr;
arg.disp = disp;
return arg;
}
AsmArgument AsmArgument_Imm(int imm)
{
AsmArgument arg;
arg.kind = AsmArgument::Imm;
arg.imm = imm;
return arg;
}
/* String representation */
std::string AsmArgument::str() const
{
switch(kind) {
case AsmArgument::Reg:
return base.str();
case AsmArgument::Deref:
return format("@%s", base.str());
case AsmArgument::PostInc:
return format("@%s+", base.str());
case AsmArgument::PreDec:
return format("@-%s", base.str());
case AsmArgument::StructDeref:
return format("@(%d,%s)", disp, base.str().c_str());
case AsmArgument::ArrayDeref:
return format("@(%s,%s)", index.str().c_str(), base.str().c_str());
case AsmArgument::PcRel:
return format("@(%d,pc)", disp);
case AsmArgument::PcJump:
return format("pc+%d", disp);
case AsmArgument::PcAddr:
return format("pc+%u", disp);
case AsmArgument::Imm:
return format("#%d", imm);
default:
return "(invalid)";
}
}
//---
// Instruction management
//---
AsmInstruction::AsmInstruction(char const *mn):
opcode {0}, opsize {0}, arg_count {0}
{
int len = strlen(mn);
int pos = std::max(0, len - 2);
if(!strncmp(mn + pos, ".b", 2)) {
opsize = 1;
len -= 2;
}
else if(!strncmp(mn + pos, ".w", 2)) {
opsize = 2;
len -= 2;
}
else if(!strncmp(mn + pos, ".l", 2)) {
opsize = 4;
len -= 2;
}
len = std::min(len, 11);
strncpy(mnemonic, mn, len);
mnemonic[len] = 0;
}
AsmInstruction::AsmInstruction(char const *mn, AsmArgument arg):
AsmInstruction(mn)
{
args[0] = arg;
arg_count = 1;
}
AsmInstruction::AsmInstruction(
char const *mn, AsmArgument arg1, AsmArgument arg2):
AsmInstruction(mn)
{
args[0] = arg1;
args[1] = arg2;
arg_count = 2;
}
//---
// Instruction classes
//---
bool AsmInstruction::isterminal() const noexcept
{
if(!strcmp(mnemonic, "rte") || !strcmp(mnemonic, "rts"))
return true;
/* Also jmp @rn which is regarded as a terminal call */
if(!strcmp(mnemonic, "jmp") && args[0].kind == AsmArgument::Deref)
return true;
/* Same for braf because we can't analyse further */
if(!strcmp(mnemonic, "braf"))
return true;
return false;
}
bool AsmInstruction::isjump() const noexcept
{
return !strcmp(mnemonic, "bra");
}
bool AsmInstruction::iscondjump() const noexcept
{
char const *v[] = {
"bf",
"bf.s",
"bf/s",
"bt",
"bt.s",
"bt/s",
NULL,
};
for(int i = 0; v[i]; i++) {
if(!strcmp(mnemonic, v[i]))
return true;
}
return false;
}
bool AsmInstruction::iscall() const noexcept
{
return !strcmp(mnemonic, "jsr") || !strcmp(mnemonic, "bsr")
|| !strcmp(mnemonic, "bsrf");
}
bool AsmInstruction::isdelayed() const noexcept
{
char const *v[] = {
"rte",
"rts",
"jmp",
"jsr",
"bra",
"braf",
"bsr",
"bsrf",
"bf.s",
"bf/s",
"bt.s",
"bt/s",
NULL,
};
for(int i = 0; v[i]; i++) {
if(!strcmp(mnemonic, v[i]))
return true;
}
return false;
}
bool AsmInstruction::isvaliddelayslot() const noexcept
{
// TODO: PC-relative move is a valid delay slot but it doesn't work
return !isdelayed() && !isterminal() && !isjump() && !iscondjump()
&& strcmp(this->mnemonic, "mova") != 0;
}
} /* namespace FxOS */