dolphin/Source/Plugins/Plugin_DSP_LLE/Src/disassemble.cpp

563 lines
11 KiB
C++
Raw Normal View History

/*====================================================================
filename: disassemble.cpp
project: GameCube DSP Tool (gcdsp)
created: 2005.03.04
mail: duddie@walla.com
Copyright (c) 2005 Duddie
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
====================================================================*/
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include "Globals.h"
#include "disassemble.h"
#include "DSPTables.h"
// #include "gdsp_tool.h"
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
u32 unk_opcodes[0x10000];
// predefined labels
typedef struct pdlabel_t
{
u16 addr;
const char* name;
const char* description;
} pdlabels_t;
const pdlabel_t pdlabels[] =
{
{0xffa0, "COEF_A1_0", "COEF_A1_0",},
{0xffa1, "COEF_A2_0", "COEF_A2_0",},
{0xffa2, "COEF_A1_1", "COEF_A1_1",},
{0xffa3, "COEF_A2_1", "COEF_A2_1",},
{0xffa4, "COEF_A1_2", "COEF_A1_2",},
{0xffa5, "COEF_A2_2", "COEF_A2_2",},
{0xffa6, "COEF_A1_3", "COEF_A1_3",},
{0xffa7, "COEF_A2_3", "COEF_A2_3",},
{0xffa8, "COEF_A1_4", "COEF_A1_4",},
{0xffa9, "COEF_A2_4", "COEF_A2_4",},
{0xffaa, "COEF_A1_5", "COEF_A1_5",},
{0xffab, "COEF_A2_5", "COEF_A2_5",},
{0xffac, "COEF_A1_6", "COEF_A1_6",},
{0xffad, "COEF_A2_6", "COEF_A2_6",},
{0xffae, "COEF_A1_7", "COEF_A1_7",},
{0xffaf, "COEF_A2_7", "COEF_A2_7",},
{0xffc9, "DSCR", "DSP DMA Control Reg",},
{0xffcb, "DSBL", "DSP DMA Block Length",},
{0xffcd, "DSPA", "DSP DMA DMEM Address",},
{0xffce, "DSMAH", "DSP DMA Mem Address H",},
{0xffcf, "DSMAL", "DSP DMA Mem Address L",},
{0xffd1, "SampleFormat", "SampleFormat",},
{0xffd3, "Unk Zelda", "Unk Zelda writes to it",},
{0xffd4, "ACSAH", "Accelerator start address H",},
{0xffd5, "ACSAL", "Accelerator start address L",},
{0xffd6, "ACEAH", "Accelerator end address H",},
{0xffd7, "ACEAL", "Accelerator end address L",},
{0xffd8, "ACCAH", "Accelerator current address H",},
{0xffd9, "ACCAL", "Accelerator current address L",},
{0xffda, "pred_scale", "pred_scale",},
{0xffdb, "yn1", "yn1",},
{0xffdc, "yn2", "yn2",},
{0xffdd, "ARAM", "Direct Read from ARAM (uses ADPCM)",},
{0xffde, "GAIN", "Gain",},
{0xffef, "AMDM", "ARAM DMA Request Mask",},
{0xfffb, "DIRQ", "DSP IRQ Request",},
{0xfffc, "DMBH", "DSP Mailbox H",},
{0xfffd, "DMBL", "DSP Mailbox L",},
{0xfffe, "CMBH", "CPU Mailbox H",},
{0xffff, "CMBL", "CPU Mailbox L",},
};
pdlabel_t regnames[] =
{
{0x00, "AR0", "Register 00",},
{0x01, "AR1", "Register 01",},
{0x02, "AR2", "Register 02",},
{0x03, "AR3", "Register 03",},
{0x04, "IX0", "Register 04",},
{0x05, "IX1", "Register 05",},
{0x06, "IX2", "Register 06",},
{0x07, "IX3", "Register 07",},
{0x08, "R08", "Register 08",},
{0x09, "R09", "Register 09",},
{0x0a, "R10", "Register 10",},
{0x0b, "R11", "Register 11",},
{0x0c, "ST0", "Call stack",},
{0x0d, "ST1", "Data stack",},
{0x0e, "ST2", "Loop addr stack",},
{0x0f, "ST3", "Loop counter",},
{0x00, "AC0.H", "Accu High 0",},
{0x11, "AC1.H", "Accu High 1",},
{0x12, "CR", "Config Register",},
{0x13, "SR", "Special Register",},
{0x14, "PROD.L", "Prod L",},
{0x15, "PROD.M1", "Prod M1",},
{0x16, "PROD.H", "Prod H",},
{0x17, "PROD.M2", "Prod M2",},
{0x18, "AX0.L", "Extra Accu L 0",},
{0x19, "AX1.L", "Extra Accu L 1",},
{0x1a, "AX0.H", "Extra Accu H 0",},
{0x1b, "AX1.H", "Extra Accu H 1",},
{0x1c, "AC0.L", "Register 28",},
{0x1d, "AC1.L", "Register 29",},
{0x1e, "AC0.M", "Register 00",},
{0x1f, "AC1.M", "Register 00",},
// To resolve special names.
{0x20, "ACC0", "Accu Full 0",},
{0x21, "ACC1", "Accu Full 1",},
{0x22, "AX0", "Extra Accu 0",},
{0x23, "AX1", "Extra Accu 1",},
};
const char* pdname(u16 val)
{
static char tmpstr[12]; // nasty
for (int i = 0; i < (int)(sizeof(pdlabels) / sizeof(pdlabel_t)); i++)
{
if (pdlabels[i].addr == val)
return pdlabels[i].name;
}
sprintf(tmpstr, "0x%04x", val);
return tmpstr;
}
extern void nop(const UDSPInstruction& opc);
char* gd_dis_params(gd_globals_t* gdg, const DSPOPCTemplate* opc, u16 op1, u16 op2, char* strbuf)
{
char* buf = strbuf;
u32 val;
for (int j = 0; j < opc->param_count; j++)
{
if (j > 0)
{
sprintf(buf, ", ");
buf += strlen(buf);
}
if (opc->params[j].loc >= 1)
val = op2;
else
val = 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;
type = opc->params[j].type;
if ((type & 0xff) == 0x10)
type &= 0xff00;
if (type & P_REG)
{
if (type == P_ACCD) // 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 (gdg->decode_registers)
sprintf(buf, "$%s", regnames[val].name);
else
sprintf(buf, "$%d", val);
break;
case P_PRG:
if (gdg->decode_registers)
sprintf(buf, "@$%s", regnames[val].name);
else
sprintf(buf, "@$%d", val);
break;
case P_VAL:
if (gdg->decode_names)
sprintf(buf, "%s", pdname(val));
else
sprintf(buf, "0x%04x", val);
break;
case P_IMM:
if (opc->params[j].size != 2)
{
if (opc->params[j].mask == 0x007f) // LSL, LSR, ASL, ASR
sprintf(buf, "#%d", val < 64 ? val : -(0x80 - (s32)val));
else
sprintf(buf, "#0x%02x", val);
}
else
sprintf(buf, "#0x%04x", val);
break;
case P_MEM:
if (opc->params[j].size != 2)
val = (u16)(s8)val;
if (gdg->decode_names)
sprintf(buf, "@%s", pdname(val));
else
sprintf(buf, "@0x%04x", val);
break;
default:
ERROR_LOG(DSPLLE, "Unknown parameter type: %x", opc->params[j].type);
// exit(-1);
break;
}
buf += strlen(buf);
}
return strbuf;
}
u16 gd_dis_get_opcode_size(gd_globals_t* gdg)
{
DSPOPCTemplate* opc = 0;
DSPOPCTemplate* opc_ext = 0;
bool extended;
if ((gdg->pc & 0x7fff) >= 0x1000)
return 1;
u32 op1 = gdg->binbuf[gdg->pc & 0x0fff];
for (u32 j = 0; j < opcodes_size; j++)
{
u16 mask;
if (opcodes[j].size & P_EXT)
mask = opcodes[j].opcode_mask & 0xff00;
else
mask = opcodes[j].opcode_mask;
if ((op1 & mask) == opcodes[j].opcode)
{
opc = &opcodes[j];
break;
}
}
if (!opc)
{
ERROR_LOG(DSPLLE, "get_opcode_size ARGH");
exit(0);
}
if (opc->size & P_EXT && op1 & 0x00ff)
extended = true;
else
extended = false;
if (extended)
{
// opcode has an extension
// find opcode
for (u32 j = 0; j < opcodes_ext_size; j++)
{
if ((op1 & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode)
{
opc_ext = &opcodes_ext[j];
break;
}
}
if (!opc_ext)
{
ERROR_LOG(DSPLLE, "get_opcode_size ext ARGH");
}
return opc_ext->size;
}
return(opc->size & ~P_EXT);
}
char* gd_dis_opcode(gd_globals_t* gdg)
{
u32 j;
u32 op1, op2;
const DSPOPCTemplate *opc = NULL;
const DSPOPCTemplate *opc_ext = NULL;
u16 pc;
char* buf = gdg->buffer;
bool extended;
pc = gdg->pc;
*buf = '\0';
if ((pc & 0x7fff) >= 0x1000)
{
gdg->pc++;
return gdg->buffer;
}
pc &= 0x0fff;
op1 = gdg->binbuf[pc];
// find opcode
for (j = 0; j < opcodes_size; j++)
{
u16 mask;
if (opcodes[j].size & P_EXT)
mask = opcodes[j].opcode_mask & 0xff00;
else
mask = opcodes[j].opcode_mask;
if ((op1 & mask) == opcodes[j].opcode)
{
opc = &opcodes[j];
break;
}
}
const DSPOPCTemplate fake_op = {"CW", 0x0000, 0x0000, nop, nop, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}}, NULL, NULL,};
if (!opc)
opc = &fake_op;
if (opc->size & P_EXT && op1 & 0x00ff)
extended = true;
else
extended = false;
if (extended)
{
// opcode has an extension
// find opcode
for (j = 0; j < opcodes_ext_size; j++)
{
if ((op1 & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode)
{
opc_ext = &opcodes_ext[j];
break;
}
}
}
// printing
if (gdg->show_pc)
sprintf(buf, "%04x ", gdg->pc);
buf += strlen(buf);
if ((opc->size & ~P_EXT) == 2)
{
op2 = gdg->binbuf[pc + 1];
if (gdg->show_hex)
sprintf(buf, "%04x %04x ", op1, op2);
}
else
{
op2 = 0;
if (gdg->show_hex)
sprintf(buf, "%04x ", op1);
}
buf += strlen(buf);
char tmpbuf[20];
if (extended)
sprintf(tmpbuf, "%s%c%s", opc->name, gdg->ext_separator, opc_ext->name);
else
sprintf(tmpbuf, "%s", opc->name);
if (gdg->print_tabs)
sprintf(buf, "%s\t", tmpbuf);
else
sprintf(buf, "%-12s", tmpbuf);
buf += strlen(buf);
if (opc->param_count > 0)
gd_dis_params(gdg, opc, op1, op2, buf);
buf += strlen(buf);
if (extended)
{
if (opc->param_count > 0)
sprintf(buf, " ");
buf += strlen(buf);
sprintf(buf, ": ");
buf += strlen(buf);
if (opc_ext->param_count > 0)
gd_dis_params(gdg, opc_ext, op1, op2, buf);
buf += strlen(buf);
}
if (opc->opcode_mask == 0)
{
// unknown opcode
unk_opcodes[op1]++;
sprintf(buf, "\t\t; *** UNKNOWN OPCODE ***");
}
if (extended)
gdg->pc += opc_ext->size;
else
gdg->pc += opc->size & ~P_EXT;
return(gdg->buffer);
}
bool gd_dis_file(gd_globals_t* gdg, char* name, FILE* output)
{
gd_dis_open_unkop();
FILE* in;
u32 size;
in = fopen(name, "rb");
if (in == NULL)
return false;
fseek(in, 0, SEEK_END);
size = (int)ftell(in);
fseek(in, 0, SEEK_SET);
gdg->binbuf = (u16*)malloc(size);
fread(gdg->binbuf, 1, size, in);
gdg->buffer = (char*)malloc(256);
gdg->buffer_size = 256;
for (gdg->pc = 0; gdg->pc < (size / 2);)
fprintf(output, "%s\n", gd_dis_opcode(gdg));
fclose(in);
free(gdg->binbuf);
gdg->binbuf = NULL;
free(gdg->buffer);
gdg->buffer = NULL;
gdg->buffer_size = 0;
gd_dis_close_unkop();
return true;
}
void gd_dis_close_unkop()
{
FILE* uo;
int i, j;
u32 count = 0;
char filename[MAX_PATH];
sprintf(filename, "%sUnkOps.bin", FULL_DSP_DUMP_DIR);
uo = fopen(filename, "wb");
if (uo)
{
fwrite(unk_opcodes, 1, sizeof(unk_opcodes), uo);
fclose(uo);
}
sprintf(filename, "%sUnkOps.txt", FULL_DSP_DUMP_DIR);
uo = fopen(filename, "w");
if (uo)
{
for (i = 0; i < 0x10000; i++)
{
if (unk_opcodes[i])
{
count++;
fprintf(uo, "OP%04x\t%d", i, unk_opcodes[i]);
for (j = 15; j >= 0; j--)
{
if ((j & 0x3) == 3)
fprintf(uo, "\tb");
fprintf(uo, "%d", (i >> j) & 0x1);
}
fprintf(uo, "\n");
}
}
fprintf(uo, "Unknown opcodes count: %d\n", count);
fclose(uo);
}
}
void gd_dis_open_unkop()
{
FILE* uo;
char filename[MAX_PATH];
sprintf(filename, "%sUnkOps.bin", FULL_DSP_DUMP_DIR);
uo = fopen(filename, "rb");
if (uo)
{
fread(unk_opcodes, 1, sizeof(unk_opcodes), uo);
fclose(uo);
}
else
{
int i;
for (i = 0; i < 0x10000; i++)
{
unk_opcodes[i] = 0;
}
}
}
const char *gd_dis_get_reg_name(u16 reg)
{
return regnames[reg].name;
}