2015-05-25 13:11:41 +02:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
|
|
|
// Copyright 2005 Duddie
|
|
|
|
// Licensed under GPLv2+
|
|
|
|
// Refer to the license.txt file included.
|
2009-03-23 09:10:32 +00:00
|
|
|
|
2016-12-21 15:03:52 -05:00
|
|
|
#include "Core/DSP/DSPDisassembler.h"
|
|
|
|
|
2014-07-02 19:45:30 -04:00
|
|
|
#include <algorithm>
|
2014-02-17 05:18:15 -05:00
|
|
|
#include <cstdlib>
|
2014-07-02 19:45:30 -04:00
|
|
|
#include <fstream>
|
2014-03-12 15:33:41 -04:00
|
|
|
#include <string>
|
2017-06-06 23:36:16 -04:00
|
|
|
#include <vector>
|
2009-03-23 09:10:32 +00:00
|
|
|
|
2014-09-07 20:06:58 -05:00
|
|
|
#include "Common/CommonTypes.h"
|
2017-01-15 21:46:32 +01:00
|
|
|
#include "Common/File.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Common/FileUtil.h"
|
2014-07-02 19:45:30 -04:00
|
|
|
#include "Common/StringUtil.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
|
|
|
|
#include "Core/DSP/DSPTables.h"
|
2016-12-22 14:01:12 -05:00
|
|
|
#include "Core/DSP/Interpreter/DSPInterpreter.h"
|
2009-03-23 09:10:32 +00:00
|
|
|
|
2016-12-30 13:25:40 -05:00
|
|
|
namespace DSP
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
DSPDisassembler::DSPDisassembler(const AssemblerSettings& settings) : settings_(settings)
|
2009-04-12 14:48:55 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-05-15 17:12:15 -07:00
|
|
|
bool DSPDisassembler::Disassemble(const std::vector<u16>& code, int base_addr, std::string& text)
|
2009-04-14 22:30:31 +00:00
|
|
|
{
|
2017-05-15 17:12:15 -07:00
|
|
|
for (u16 pc = 0; pc < code.size();)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2017-05-15 17:12:15 -07:00
|
|
|
if (!DisassembleOpcode(code.data(), base_addr, &pc, text))
|
|
|
|
return false;
|
|
|
|
text.append("\n");
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
2017-05-15 17:12:15 -07:00
|
|
|
return true;
|
2009-04-14 22:30:31 +00:00
|
|
|
}
|
|
|
|
|
2014-07-02 19:45:30 -04:00
|
|
|
std::string DSPDisassembler::DisassembleParameters(const DSPOPCTemplate& opc, u16 op1, u16 op2)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
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 += StringFromFormat("$%s", pdregname(val));
|
|
|
|
else
|
|
|
|
buf += StringFromFormat("$%d", val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case P_PRG:
|
|
|
|
if (settings_.decode_registers)
|
|
|
|
buf += StringFromFormat("@$%s", pdregname(val));
|
|
|
|
else
|
|
|
|
buf += StringFromFormat("@$%d", val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case P_VAL:
|
|
|
|
case P_ADDR_I:
|
|
|
|
case P_ADDR_D:
|
|
|
|
if (settings_.decode_names)
|
|
|
|
{
|
|
|
|
buf += pdname(val);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf += StringFromFormat("0x%04x", val);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case P_IMM:
|
|
|
|
if (opc.params[j].size != 2)
|
|
|
|
{
|
|
|
|
if (opc.params[j].mask == 0x003f) // LSL, LSR, ASL, ASR
|
|
|
|
buf += StringFromFormat("#%d",
|
|
|
|
(val & 0x20) ? (val | 0xFFFFFFC0) : val); // 6-bit sign extension
|
|
|
|
else
|
|
|
|
buf += StringFromFormat("#0x%02x", val);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf += StringFromFormat("#0x%04x", val);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case P_MEM:
|
|
|
|
if (opc.params[j].size != 2)
|
|
|
|
val = (u16)(s16)(s8)val;
|
|
|
|
|
|
|
|
if (settings_.decode_names)
|
|
|
|
buf += StringFromFormat("@%s", pdname(val));
|
|
|
|
else
|
|
|
|
buf += StringFromFormat("@0x%04x", val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ERROR_LOG(DSPLLE, "Unknown parameter type: %x", opc.params[j].type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
2017-05-15 17:12:15 -07:00
|
|
|
bool DSPDisassembler::DisassembleOpcode(const u16* binbuf, int base_addr, u16* pc,
|
2016-06-24 10:43:46 +02:00
|
|
|
std::string& dest)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
if ((*pc & 0x7fff) >= 0x1000)
|
|
|
|
{
|
|
|
|
++pc;
|
|
|
|
dest.append("; outside memory");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-25 19:56:19 -05:00
|
|
|
const u16 op1 = binbuf[*pc & 0x0fff];
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-01-25 19:56:19 -05:00
|
|
|
// Find main opcode
|
|
|
|
const DSPOPCTemplate* opc = FindOpInfoByOpcode(op1);
|
2017-05-15 17:30:21 -07:00
|
|
|
const DSPOPCTemplate fake_op = {
|
|
|
|
"CW", 0x0000, 0x0000, nullptr, nullptr, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}},
|
|
|
|
false, false, false, false, false};
|
2016-06-24 10:43:46 +02:00
|
|
|
if (!opc)
|
|
|
|
opc = &fake_op;
|
|
|
|
|
2017-01-25 19:56:19 -05:00
|
|
|
bool is_extended = false;
|
|
|
|
bool is_only_7_bit_ext = false;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
if (((opc->opcode >> 12) == 0x3) && (op1 & 0x007f))
|
|
|
|
{
|
2017-01-25 19:56:19 -05:00
|
|
|
is_extended = true;
|
|
|
|
is_only_7_bit_ext = true;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
else if (((opc->opcode >> 12) > 0x3) && (op1 & 0x00ff))
|
|
|
|
{
|
2017-01-25 19:56:19 -05:00
|
|
|
is_extended = true;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2017-01-25 19:56:19 -05:00
|
|
|
const DSPOPCTemplate* opc_ext = nullptr;
|
|
|
|
if (is_extended)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
|
|
|
// opcode has an extension
|
2017-01-25 19:56:19 -05:00
|
|
|
const u16 extended_opcode = is_only_7_bit_ext ? op1 & 0x7F : op1;
|
|
|
|
opc_ext = FindExtOpInfoByOpcode(extended_opcode);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// printing
|
|
|
|
|
|
|
|
if (settings_.show_pc)
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += StringFromFormat("%04x ", *pc);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-01-25 19:56:19 -05:00
|
|
|
u16 op2;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
// Size 2 - the op has a large immediate.
|
|
|
|
if (opc->size == 2)
|
|
|
|
{
|
|
|
|
op2 = binbuf[(*pc + 1) & 0x0fff];
|
|
|
|
if (settings_.show_hex)
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += StringFromFormat("%04x %04x ", op1, op2);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
op2 = 0;
|
|
|
|
if (settings_.show_hex)
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += StringFromFormat("%04x ", op1);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string opname = opc->name;
|
2017-01-25 19:56:19 -05:00
|
|
|
if (is_extended)
|
2017-05-15 17:30:21 -07:00
|
|
|
opname += StringFromFormat("%c%s", settings_.ext_separator, opc_ext->name);
|
2016-06-24 10:43:46 +02:00
|
|
|
if (settings_.lower_case_ops)
|
2017-05-15 17:30:21 -07:00
|
|
|
std::transform(opname.begin(), opname.end(), opname.begin(), ::tolower);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
if (settings_.print_tabs)
|
2017-05-15 17:30:21 -07:00
|
|
|
dest += StringFromFormat("%s\t", opname.c_str());
|
2016-06-24 10:43:46 +02:00
|
|
|
else
|
2017-05-15 17:30:21 -07:00
|
|
|
dest += StringFromFormat("%-12s", opname.c_str());
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
if (opc->param_count > 0)
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += DisassembleParameters(*opc, op1, op2);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
// Handle opcode extension.
|
2017-01-25 19:56:19 -05:00
|
|
|
if (is_extended)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
|
|
|
if (opc->param_count > 0)
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += " ";
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += ": ";
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
if (opc_ext->param_count > 0)
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += DisassembleParameters(*opc_ext, op1, op2);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (opc->opcode_mask == 0)
|
|
|
|
{
|
|
|
|
// unknown opcode
|
2017-05-15 17:12:15 -07:00
|
|
|
dest += "\t\t; *** UNKNOWN OPCODE ***";
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2017-05-15 17:30:21 -07:00
|
|
|
*pc += is_extended ? opc_ext->size : opc->size;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
return true;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
2016-12-30 13:25:40 -05:00
|
|
|
} // namespace DSP
|