dolphin/Source/Core/Core/DSP/DSPDisassembler.cpp

254 lines
5.6 KiB
C++

// Copyright 2008 Dolphin Emulator Project
// Copyright 2005 Duddie
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/DSP/DSPDisassembler.h"
#include <algorithm>
#include <limits>
#include <string>
#include <vector>
#include <fmt/format.h>
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/DSP/DSPTables.h"
namespace DSP
{
DSPDisassembler::DSPDisassembler(const AssemblerSettings& settings) : settings_(settings)
{
}
bool DSPDisassembler::Disassemble(const std::vector<u16>& code, std::string& text)
{
if (code.size() > std::numeric_limits<u16>::max())
{
text.append("; code too large for 16-bit addressing\n");
return false;
}
for (u16 pc = 0; pc < code.size();)
{
bool failed = !DisassembleOpcode(code, &pc, text);
text.append("\n");
if (failed)
return false;
}
return true;
}
std::string DSPDisassembler::DisassembleParameters(const DSPOPCTemplate& opc, u16 op1, u16 op2)
{
std::string buf;
for (int j = 0; j < opc.param_count; j++)
{
if (j > 0)
buf += ", ";
u32 val = (opc.params[j].loc >= 1) ? op2 : op1;
val &= opc.params[j].mask;
if (opc.params[j].lshift < 0)
val = val << (-opc.params[j].lshift);
else
val = val >> opc.params[j].lshift;
u32 type = opc.params[j].type;
if ((type & 0xff) == 0x10)
type &= 0xff00;
if (type & P_REG)
{
// Check for _D parameter - if so flip.
if ((type == P_ACC_D) || (type == P_ACCM_D)) // Used to be P_ACCM_D TODO verify
val = (~val & 0x1) | ((type & P_REGS_MASK) >> 8);
else
val |= (type & P_REGS_MASK) >> 8;
type &= ~P_REGS_MASK;
}
switch (type)
{
case P_REG:
if (settings_.decode_registers)
buf += fmt::format("${}", pdregname(val));
else
buf += fmt::format("${}", val);
break;
case P_PRG:
if (settings_.decode_registers)
buf += fmt::format("@${}", pdregname(val));
else
buf += fmt::format("@${}", val);
break;
case P_VAL:
case P_ADDR_I:
case P_ADDR_D:
if (settings_.decode_names)
{
buf += pdname(val);
}
else
{
buf += fmt::format("0x{:04x}", val);
}
break;
case P_IMM:
if (opc.params[j].size != 2)
{
// LSL, LSR, ASL, ASR
if (opc.params[j].mask == 0x003f)
{
// Left and right shifts function essentially as a single shift by a 7-bit signed value,
// but are split into two intructions for clarity.
buf += fmt::format("#{}", (val & 0x20) != 0 ? (int(val) - 64) : int(val));
}
else
{
buf += fmt::format("#0x{:02x}", val);
}
}
else
{
buf += fmt::format("#0x{:04x}", val);
}
break;
case P_MEM:
if (opc.params[j].size != 2)
val = (u16)(s16)(s8)val;
if (settings_.decode_names)
buf += fmt::format("@{}", pdname(val));
else
buf += fmt::format("@0x{:04x}", val);
break;
default:
ERROR_LOG_FMT(DSPLLE, "Unknown parameter type: {:x}", opc.params[j].type);
break;
}
}
return buf;
}
bool DSPDisassembler::DisassembleOpcode(const std::vector<u16>& code, u16* pc, std::string& dest)
{
return DisassembleOpcode(code.data(), code.size(), pc, dest);
}
bool DSPDisassembler::DisassembleOpcode(const u16* binbuf, size_t binbuf_size, u16* pc,
std::string& dest)
{
const u16 wrapped_pc = (*pc & 0x7fff);
if (wrapped_pc >= binbuf_size)
{
++pc;
dest.append("; outside memory");
return false;
}
const u16 op1 = binbuf[wrapped_pc];
// Find main opcode
const DSPOPCTemplate* opc = FindOpInfoByOpcode(op1);
if (!opc)
opc = &cw;
bool is_extended = false;
bool is_only_7_bit_ext = false;
if (((opc->opcode >> 12) == 0x3) && (op1 & 0x007f))
{
is_extended = true;
is_only_7_bit_ext = true;
}
else if (((opc->opcode >> 12) > 0x3) && (op1 & 0x00ff))
{
is_extended = true;
}
const DSPOPCTemplate* opc_ext = nullptr;
if (is_extended)
{
// opcode has an extension
const u16 extended_opcode = is_only_7_bit_ext ? op1 & 0x7F : op1;
opc_ext = FindExtOpInfoByOpcode(extended_opcode);
}
// printing
if (settings_.show_pc)
dest += fmt::format("{:04x} ", wrapped_pc);
u16 op2;
// Size 2 - the op has a large immediate.
if (opc->size == 2)
{
if (wrapped_pc + 1 >= binbuf_size)
{
if (settings_.show_hex)
dest += fmt::format("{:04x} ???? ", op1);
dest += fmt::format("; Insufficient data for large immediate");
*pc += opc->size;
return false;
}
op2 = binbuf[wrapped_pc + 1];
if (settings_.show_hex)
dest += fmt::format("{:04x} {:04x} ", op1, op2);
}
else
{
op2 = 0;
if (settings_.show_hex)
dest += fmt::format("{:04x} ", op1);
}
std::string opname = opc->name;
if (is_extended)
opname += fmt::format("{}{}", settings_.ext_separator, opc_ext->name);
if (settings_.lower_case_ops)
Common::ToLower(&opname);
if (settings_.print_tabs)
dest += fmt::format("{}\t", opname);
else
dest += fmt::format("{:<12}", opname);
if (opc->param_count > 0)
dest += DisassembleParameters(*opc, op1, op2);
// Handle opcode extension.
if (is_extended)
{
if (opc->param_count > 0)
dest += " ";
dest += ": ";
if (opc_ext->param_count > 0)
dest += DisassembleParameters(*opc_ext, op1, op2);
}
if (opc->opcode_mask == 0)
{
// unknown opcode
dest += "\t\t; *** UNKNOWN OPCODE ***";
}
*pc += is_extended ? opc_ext->size : opc->size;
return true;
}
} // namespace DSP