/*==================================================================== 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 #include #include #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, "R00", "Register 00",}, {0x01, "R01", "Register 01",}, {0x02, "R02", "Register 02",}, {0x03, "R03", "Register 03",}, {0x04, "R04", "Register 04",}, {0x05, "R05", "Register 05",}, {0x06, "R06", "Register 06",}, {0x07, "R07", "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, "ACH0", "Accu High 0",}, {0x11, "ACH1", "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; } gd_globals_t* gd_init() { gd_globals_t* gdg = (gd_globals_t*)malloc(sizeof(gd_globals_t)); memset(gdg, 0, sizeof(gd_globals_t)); return(gdg); } 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; }