fxos/lib/lang.cpp

181 lines
4.7 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>
#include <cassert>
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 operands
//---
AsmOperand::AsmOperand(Kind kind, CpuRegister base):
m_kind {kind}, m_base {base}
{
}
AsmOperand::AsmOperand(int disp, i8 opsize, CpuRegister base):
m_kind {StructDeref}, m_base {base}, m_opsize {opsize}, m_disp_imm {disp}
{
}
AsmOperand::AsmOperand(CpuRegister index, CpuRegister base):
m_kind {ArrayDeref}, m_base {base}, m_index {index}
{
}
AsmOperand::AsmOperand(Kind kind, int disp_imm, i8 opsize):
m_kind {kind}, m_opsize {opsize}, m_disp_imm {disp_imm}
{
}
std::string AsmOperand::str() const
{
switch(m_kind) {
case Reg:
return m_base.str();
case Deref:
return format("@%s", m_base.str());
case PostInc:
return format("@%s+", m_base.str());
case PreDec:
return format("@-%s", m_base.str());
case StructDeref:
return format("@(%d,%s)", m_disp_imm, m_base.str().c_str());
case ArrayDeref:
return format("@(%s,%s)", m_index.str().c_str(), m_base.str().c_str());
case PcRel:
return format("@(%d,pc)", m_disp_imm);
case PcJump:
return format("pc+%d", m_disp_imm);
case PcAddr:
return format("pc+%u", m_disp_imm);
case Imm:
return format("#%d", m_disp_imm);
default:
return "(invalid)";
}
}
u32 AsmOperand::getPCRelativeTarget(u32 pc, int size) const
{
size = size + (size == 0);
if(m_kind == AsmOperand::PcRel)
return (pc & -size) + 4 + m_disp_imm;
if(m_kind == AsmOperand::PcJump)
return pc + 4 + m_disp_imm;
if(m_kind == AsmOperand::PcAddr)
return (pc & -4) + 4 + m_disp_imm;
/* SH3 manual says that mova uses the target address of the jump when
in a delay slot. SH4AL-DSP makes it invalid. Supporting this would
be very tricky since the target PC is often dynamic (eg. rts). */
return -1;
}
//---
// Instruction management
//---
static char const *instructionMnemonics[] = {
#define GENDEFS_INSN(NAME, STR) [AsmInstruction::SH_##NAME] = STR,
#include "gendefs/insn.h"
#undef GENDEFS_INSN
};
AsmInstruction::AsmInstruction(u32 encoding, char const *mnemonic, int tags,
int opCount, AsmOperand op1, AsmOperand op2):
m_encoding {encoding},
m_opsize {0}, m_opCount {(u8)opCount}, m_tags {(u16)tags}, m_ops {op1, op2}
{
std::string mn {mnemonic};
if(mn.ends_with(".b"))
m_opsize = 1;
else if(mn.ends_with(".w"))
m_opsize = 2;
else if(mn.ends_with(".l"))
m_opsize = 4;
if(m_opsize != 0)
mn = mn.substr(0, mn.size() - 2);
int i;
for(i = 0; i < SH_MAX; i++) {
if(mn == instructionMnemonics[i]) {
m_opcode = i;
break;
}
}
assert(i < SH_MAX && "AsmInstruction with unknown opcode string");
}
char const *AsmInstruction::mnemonic() const
{
assert(m_opcode < SH_MAX);
return instructionMnemonics[m_opcode];
}
u32 AsmInstruction::getPCRelativeTarget(u32 pc) const
{
for(AsmOperand const &op: operands()) {
if(op.usesPCRelativeAddressing())
return op.getPCRelativeTarget(pc, m_opsize);
}
return -1;
}
} /* namespace FxOS */