287 lines
5.3 KiB
C++
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 */
|