fxos/lib/lang.cpp

287 lines
5.3 KiB
C++

#include <fxos/lang.h>
#include <fxos/util.h>
#include <stdexcept>
#include <string>
#include <cstring>
#include <map>
namespace FxOS {
//---
// CPU registers
//---
using Reg = CpuRegister::CpuRegisterName;
static std::map<Reg,std::string> regnames = {
{ Reg::R0, "r0" },
{ Reg::R1, "r1" },
{ Reg::R2, "r2" },
{ Reg::R3, "r3" },
{ Reg::R4, "r4" },
{ Reg::R5, "r5" },
{ Reg::R6, "r6" },
{ Reg::R7, "r7" },
{ Reg::R0B, "r0_bank" },
{ Reg::R1B, "r1_bank" },
{ Reg::R2B, "r2_bank" },
{ Reg::R3B, "r3_bank" },
{ Reg::R4B, "r4_bank" },
{ Reg::R5B, "r5_bank" },
{ Reg::R6B, "r6_bank" },
{ Reg::R7B, "r7_bank" },
{ Reg::R8, "r8" },
{ Reg::R9, "r9" },
{ Reg::R10, "r10" },
{ Reg::R11, "r11" },
{ Reg::R12, "r12" },
{ Reg::R13, "r13" },
{ Reg::R14, "r14" },
{ Reg::R15, "r15" },
{ Reg::MACH, "mach" },
{ Reg::MACL, "macl" },
{ Reg::PR, "pr" },
{ Reg::PC, "pc" },
{ Reg::SR, "sr" },
{ Reg::SSR, "ssr" },
{ Reg::SPC, "spc" },
{ Reg::GBR, "gbr" },
{ Reg::VBR, "vbr" },
{ Reg::DBR, "dbr" },
{ Reg::SGR, "sgr" },
};
/* Construction from string - pretty slow */
CpuRegister::CpuRegister(std::string name)
{
for(auto &it: regnames) if(it.second == name)
{
m_name = it.first;
return;
}
throw std::invalid_argument("invalid CpuRegister name");
}
/* Conversion to string */
std::string CpuRegister::str() const noexcept
{
return regnames.at(m_name);
}
//---
// Instruction arguments
//---
/* External constructors */
Argument Argument_Reg(CpuRegister base)
{
Argument arg;
arg.kind = Argument::Reg;
arg.base = base;
return arg;
}
Argument Argument_Deref(CpuRegister base)
{
Argument arg;
arg.kind = Argument::Deref;
arg.base = base;
return arg;
}
Argument Argument_PostInc(CpuRegister base)
{
Argument arg;
arg.kind = Argument::PostInc;
arg.base = base;
return arg;
}
Argument Argument_PreDec(CpuRegister base)
{
Argument arg;
arg.kind = Argument::PreDec;
arg.base = base;
return arg;
}
Argument Argument_StructDeref(int disp, int opsize, CpuRegister base)
{
Argument arg;
arg.kind = Argument::StructDeref;
arg.base = base;
arg.disp = disp;
arg.opsize = opsize;
return arg;
}
Argument Argument_ArrayDeref(CpuRegister index, CpuRegister base)
{
Argument arg;
arg.kind = Argument::ArrayDeref;
arg.base = base;
arg.index = index;
return arg;
}
Argument Argument_PcRel(int disp, int opsize)
{
Argument arg;
arg.kind = Argument::PcRel;
arg.disp = disp;
arg.opsize = opsize;
return arg;
}
Argument Argument_PcJump(int disp)
{
Argument arg;
arg.kind = Argument::PcJump;
arg.disp = disp;
return arg;
}
Argument Argument_PcAddr(int disp)
{
Argument arg;
arg.kind = Argument::PcAddr;
arg.disp = disp;
return arg;
}
Argument Argument_Imm(int imm)
{
Argument arg;
arg.kind = Argument::Imm;
arg.imm = imm;
return arg;
}
/* String representation */
std::string Argument::str() const
{
switch(kind)
{
case Argument::Reg:
return base.str();
case Argument::Deref:
return format("@%s", base.str());
case Argument::PostInc:
return format("@%s+", base.str());
case Argument::PreDec:
return format("@-%s", base.str());
case Argument::StructDeref:
return format("@(%d,%s)", disp, base.str().c_str());
case Argument::ArrayDeref:
return format("@(%s,%s)", index.str().c_str(),
base.str().c_str());
case Argument::PcRel:
return format("@(%d,pc)", disp);
case Argument::PcJump:
return format("pc+%d", disp);
case Argument::PcAddr:
return format("pc+%u", disp);
case Argument::Imm:
return format("#%d", imm);
default:
return "(invalid)";
}
}
//---
// Instruction management
//---
Instruction::Instruction(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;
}
Instruction::Instruction(char const *mn, Argument arg):
Instruction(mn)
{
args[0] = arg;
arg_count = 1;
}
Instruction::Instruction(char const *mn, Argument arg1, Argument arg2):
Instruction(mn)
{
args[0] = arg1;
args[1] = arg2;
arg_count = 2;
}
//---
// Instruction classes
//---
bool Instruction::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 == Argument::Deref) return true;
/* Same for braf because we can't analyse further */
if(!strcmp(mnemonic, "braf")) return true;
return false;
}
bool Instruction::isjump() const noexcept
{
return !strcmp(mnemonic, "bra");
}
bool Instruction::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 Instruction::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 Instruction::isvaliddelayslot() const noexcept
{
return !isdelayed() && !isterminal() && !isjump() && !iscondjump();
}
} /* namespace FxOS */