diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 7de5816..cae1a15 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -26,8 +26,8 @@ set(SRCS ActionReplay.cpp Debugger/Debugger_SymbolMap.cpp Debugger/Dump.cpp Debugger/PPCDebugInterface.cpp - DSP/assemble.cpp - DSP/disassemble.cpp + DSP/DSPAssembler.cpp + DSP/DSPDisassembler.cpp DSP/DSPAccelerator.cpp DSP/DSPIntCCUtil.cpp DSP/DSPIntExtOps.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 63650ff..f91087c 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -60,8 +60,8 @@ - - + + @@ -265,8 +265,8 @@ - - + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index a943f2a..34264a0 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -509,10 +509,7 @@ HW %28Flipper/Hollywood%29 - - DSPCore - - + DSPCore @@ -527,6 +524,9 @@ DSPCore + + DSPCore + DSPCore @@ -1038,10 +1038,7 @@ HW %28Flipper/Hollywood%29 - - DSPCore - - + DSPCore @@ -1062,6 +1059,9 @@ DSPCore + + DSPCore + DSPCore diff --git a/Source/Core/Core/DSP/DSPAssembler.cpp b/Source/Core/Core/DSP/DSPAssembler.cpp new file mode 100644 index 0000000..5778e02 --- /dev/null +++ b/Source/Core/Core/DSP/DSPAssembler.cpp @@ -0,0 +1,1021 @@ +/*==================================================================== + +$Id: assemble.cpp,v 1.3 2008-11-11 01:04:26 wntrmute Exp $ + +project: GameCube DSP Tool (gcdsp) +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. + +Revision 1.4 2008/10/04 10:30:00 Hermes +added function to export the code to .h file +added support for '/ *' '* /' and '//' for comentaries +added some sintax detection when use registers + +$Log: not supported by cvs2svn $ +Revision 1.2 2005/09/14 02:19:29 wntrmute +added header guards +use standard main function + +Revision 1.1 2005/08/24 22:13:34 wntrmute +Initial import + + +====================================================================*/ + +#include +#include +#include +#include +#include + +#include "Common/Common.h" +#include "Common/FileUtil.h" + +#include "Core/DSP/DSPAssembler.h" +#include "Core/DSP/DSPDisassembler.h" +#include "Core/DSP/DSPInterpreter.h" +#include "Core/DSP/DSPTables.h" + +static const char *err_string[] = +{ + "", + "Unknown Error", + "Unknown opcode", + "Not enough parameters", + "Too many parameters", + "Wrong parameter", + "Expected parameter of type 'string'", + "Expected parameter of type 'value'", + "Expected parameter of type 'register'", + "Expected parameter of type 'memory pointer'", + "Expected parameter of type 'immediate'", + "Incorrect binary value", + "Incorrect hexadecimal value", + "Incorrect decimal value", + "Label already exists", + "Label not defined", + "No matching brackets", + "This opcode cannot be extended", + "Given extending params for non extensible opcode", + "Wrong parameter: must be accumulator register", + "Wrong parameter: must be mid accumulator register", + "Invalid register", + "Number out of range" +}; + +DSPAssembler::DSPAssembler(const AssemblerSettings &settings) : + gdg_buffer(NULL), + m_cur_addr(0), + m_cur_pass(0), + m_current_param(0), + settings_(settings) + +{ +} + +DSPAssembler::~DSPAssembler() +{ + if(gdg_buffer) + free(gdg_buffer); +} + +bool DSPAssembler::Assemble(const char *text, std::vector &code, std::vector *line_numbers) +{ + if (line_numbers) + line_numbers->clear(); + const char *fname = "tmp.asm"; + if (!File::WriteStringToFile(text, fname)) + return false; + InitPass(1); + if (!AssembleFile(fname, 1)) + return false; + + // We now have the size of the output buffer + if (m_totalSize > 0) + { + gdg_buffer = (char *)malloc(m_totalSize * sizeof(u16) + 4); + if(!gdg_buffer) + return false; + + memset(gdg_buffer, 0, m_totalSize * sizeof(u16)); + } else + return false; + + InitPass(2); + if (!AssembleFile(fname, 2)) + return false; + + code.resize(m_totalSize); + for (int i = 0; i < m_totalSize; i++) { + code[i] = *(u16 *)(gdg_buffer + i * 2); + } + + if(gdg_buffer) { + free(gdg_buffer); + gdg_buffer = NULL; + } + + last_error_str = "(no errors)"; + last_error = ERR_OK; + + return true; +} + +void DSPAssembler::ShowError(err_t err_code, const char *extra_info) +{ + + if (!settings_.force) + failed = true; + + char error_buffer[1024]; + char *buf_ptr = error_buffer; + buf_ptr += sprintf(buf_ptr, "%i : %s ", code_line, cur_line.c_str()); + if (!extra_info) + extra_info = "-"; + + if (m_current_param == 0) + buf_ptr += sprintf(buf_ptr, "ERROR: %s Line: %d : %s\n", err_string[err_code], code_line, extra_info); + else + buf_ptr += sprintf(buf_ptr, "ERROR: %s Line: %d Param: %d : %s\n", + err_string[err_code], code_line, m_current_param, extra_info); + last_error_str = error_buffer; + last_error = err_code; +} + +char *skip_spaces(char *ptr) +{ + while (*ptr == ' ') + ptr++; + return ptr; +} + +const char *skip_spaces(const char *ptr) +{ + while (*ptr == ' ') + ptr++; + return ptr; +} + +// Parse a standalone value - it can be a number in one of several formats or a label. +s32 DSPAssembler::ParseValue(const char *str) +{ + bool negative = false; + s32 val = 0; + const char *ptr = str; + + if (ptr[0] == '#') + { + ptr++; + negative = true; // Wow! Double # (needed one to get in here) negates??? + } + if (ptr[0] == '-') + { + ptr++; + negative = true; + } + if (ptr[0] == '0') + { + if (ptr[1] >= '0' && ptr[1] <= '9') + { + for (int i = 0; ptr[i] != 0; i++) + { + val *= 10; + if (ptr[i] >= '0' && ptr[i] <= '9') + val += ptr[i] - '0'; + else + ShowError(ERR_INCORRECT_DEC, str); + } + } + else + { + switch (ptr[1]) + { + case 'X': // hex + for (int i = 2 ; ptr[i] != 0 ; i++) + { + val <<= 4; + if (ptr[i] >= 'a' && ptr[i] <= 'f') + val += (ptr[i]-'a'+10); + else if (ptr[i] >= 'A' && ptr[i] <= 'F') + val += (ptr[i]-'A'+10); + else if (ptr[i] >= '0' && ptr[i] <= '9') + val += (ptr[i] - '0'); + else + ShowError(ERR_INCORRECT_HEX, str); + } + break; + case '\'': // binary + for (int i = 2; ptr[i] != 0; i++) + { + val *=2; + if(ptr[i] >= '0' && ptr[i] <= '1') + val += ptr[i] - '0'; + else + ShowError(ERR_INCORRECT_BIN, str); + } + break; + default: + // value is 0 or error + val = 0; + break; + } + } + } + else + { + // Symbol starts with a digit - it's a dec number. + if (ptr[0] >= '0' && ptr[0] <= '9') + { + for (int i = 0; ptr[i] != 0; i++) + { + val *= 10; + if (ptr[i] >= '0' && ptr[i] <= '9') + val += ptr[i] - '0'; + else + ShowError(ERR_INCORRECT_DEC, str); + } + } + else // Everything else is a label. + { + // Lookup label + u16 value; + if (labels.GetLabelValue(ptr, &value)) + return value; + if (m_cur_pass == 2) + ShowError(ERR_UNKNOWN_LABEL, str); + } + } + if (negative) + return -val; + return val; +} + +// Modifies both src and dst! +// What does it do, really?? +char *DSPAssembler::FindBrackets(char *src, char *dst) +{ + s32 len = (s32) strlen(src); + s32 first = -1; + s32 count = 0; + s32 i, j; + j = 0; + for (i = 0 ; i < len ; i++) + { + if (src[i] == '(') + { + if (first < 0) + { + count = 1; + src[i] = 0x0; + first = i; + } + else + { + count++; + dst[j++] = src[i]; + } + } + else if (src[i] == ')') + { + if (--count == 0) + { + dst[j] = 0; + return &src[i+1]; + } + else + { + dst[j++] = src[i]; + } + } + else + { + if (first >= 0) + dst[j++] = src[i]; + } + } + if (count) + ShowError(ERR_NO_MATCHING_BRACKETS); + return NULL; +} + +// Bizarre in-place expression evaluator. +u32 DSPAssembler::ParseExpression(const char *ptr) +{ + char *pbuf; + s32 val = 0; + + char *d_buffer = (char *)malloc(1024); + char *s_buffer = (char *)malloc(1024); + strcpy(s_buffer, ptr); + + while ((pbuf = FindBrackets(s_buffer, d_buffer)) != NULL) + { + val = ParseExpression(d_buffer); + sprintf(d_buffer, "%s%d%s", s_buffer, val, pbuf); + strcpy(s_buffer, d_buffer); + } + + int j = 0; + for (int i = 0; i < ((s32)strlen(s_buffer) + 1) ; i++) + { + char c = s_buffer[i]; + if (c != ' ') + d_buffer[j++] = c; + } + + for (int i = 0; i < ((s32)strlen(d_buffer) + 1) ; i++) + { + char c = d_buffer[i]; + if (c == '-') + { + if (i == 0) + c = '#'; + else + { + switch (d_buffer[i - 1]) + { + case '/': + case '%': + case '*': + c = '#'; + } + } + } + d_buffer[i] = c; + } + + while ((pbuf = strstr(d_buffer, "+")) != NULL) + { + *pbuf = 0x0; + val = ParseExpression(d_buffer) + ParseExpression(pbuf+1); + sprintf(d_buffer, "%d", val); + } + + while ((pbuf = strstr(d_buffer, "-")) != NULL) + { + *pbuf = 0x0; + val = ParseExpression(d_buffer) - ParseExpression(pbuf+1); + if (val < 0) + { + val = 0x10000 + (val & 0xffff); // ATTENTION: avoid a terrible bug!!! number cannot write with '-' in sprintf + fprintf(stderr, "WARNING: Number Underflow at Line: %d \n", code_line); + } + sprintf(d_buffer, "%d", val); + } + + while ((pbuf = strstr(d_buffer, "*")) != NULL) + { + *pbuf = 0x0; + val = ParseExpression(d_buffer) * ParseExpression(pbuf+1); + sprintf(d_buffer, "%d", val); + } + + while ((pbuf = strstr(d_buffer, "/")) != NULL) + { + *pbuf = 0x0; + val = ParseExpression(d_buffer) / ParseExpression(pbuf+1); + sprintf(d_buffer, "%d", val); + } + + while ((pbuf = strstr(d_buffer, "|")) != NULL) + { + *pbuf = 0x0; + val = ParseExpression(d_buffer) | ParseExpression(pbuf+1); + sprintf(d_buffer, "%d", val); + } + + while ((pbuf = strstr(d_buffer, "&")) != NULL) + { + *pbuf = 0x0; + val = ParseExpression(d_buffer) & ParseExpression(pbuf+1); + sprintf(d_buffer, "%d", val); + } + + val = ParseValue(d_buffer); + free(d_buffer); + free(s_buffer); + return val; +} + +// Destroys parstr +u32 DSPAssembler::GetParams(char *parstr, param_t *par) +{ + u32 count = 0; + char *tmpstr = skip_spaces(parstr); + tmpstr = strtok(tmpstr, ",\x00"); + for (int i = 0; i < 10; i++) + { + if (tmpstr == NULL) + break; + tmpstr = skip_spaces(tmpstr); + if (strlen(tmpstr) == 0) + break; + if (tmpstr) + count++; + else + break; + + par[i].type = P_NONE; + switch (tmpstr[0]) + { + case '"': + par[i].str = strtok(tmpstr, "\""); + par[i].type = P_STR; + break; + case '#': + par[i].val = ParseExpression(tmpstr + 1); + par[i].type = P_IMM; + break; + case '@': + if (tmpstr[1] == '$') + { + par[i].val = ParseExpression(tmpstr + 2); + par[i].type = P_PRG; + } + else + { + par[i].val = ParseExpression(tmpstr + 1); + par[i].type = P_MEM; + } + break; + case '$': + par[i].val = ParseExpression(tmpstr + 1); + par[i].type = P_REG; + break; + + default: + par[i].val = ParseExpression(tmpstr); + par[i].type = P_VAL; + break; + } + tmpstr = strtok(NULL, ",\x00"); + } + return count; +} + +const opc_t *DSPAssembler::FindOpcode(const char *opcode, u32 par_count, const opc_t * const opcod, int opcod_size) +{ + if (opcode[0] == 'C' && opcode[1] == 'W') + return &cw; + + AliasMap::const_iterator alias_iter = aliases.find(opcode); + if (alias_iter != aliases.end()) + opcode = alias_iter->second.c_str(); + for (int i = 0; i < opcod_size; i++) + { + const opc_t *opc = &opcod[i]; + if (strcmp(opc->name, opcode) == 0) + { + if (par_count < opc->param_count) + { + ShowError(ERR_NOT_ENOUGH_PARAMETERS); + } + if (par_count > opc->param_count) + { + ShowError(ERR_TOO_MANY_PARAMETERS); + } + return opc; + } + } + ShowError(ERR_UNKNOWN_OPCODE); + return NULL; +} + +// weird... +u16 get_mask_shifted_down(u16 mask) +{ + while (!(mask & 1)) + mask >>= 1; + return mask; +} + +bool DSPAssembler::VerifyParams(const opc_t *opc, param_t *par, int count, bool ext) +{ + for (int i = 0; i < count; i++) + { + const int current_param = i + 1; // just for display. + if (opc->params[i].type != par[i].type || (par[i].type & P_REG)) + { + if (par[i].type == P_VAL && + (opc->params[i].type == P_ADDR_I || opc->params[i].type == P_ADDR_D)) + { + // Data and instruction addresses are valid as VAL values. + continue; + } + + if ((opc->params[i].type & P_REG) && (par[i].type & P_REG)) + { + // Just a temp. Should be replaced with more purposeful vars. + int value; + + // modified by Hermes: test the register range + switch ((unsigned)opc->params[i].type) + { + case P_REG18: + case P_REG19: + case P_REG1A: + value = (opc->params[i].type >> 8) & 31; + if ((int)par[i].val < value || + (int)par[i].val > value + get_mask_shifted_down(opc->params[i].mask)) + { + if (ext) fprintf(stderr, "(ext) "); + fprintf(stderr, "%s (param %i)", cur_line.c_str(), current_param); + ShowError(ERR_INVALID_REGISTER); + } + break; + case P_PRG: + if ((int)par[i].val < 0 || (int)par[i].val > 0x3) + { + if (ext) fprintf(stderr, "(ext) "); + fprintf(stderr, "%s (param %i)", cur_line.c_str(), current_param); + ShowError(ERR_INVALID_REGISTER); + } + break; + case P_ACC: + if ((int)par[i].val < 0x20 || (int)par[i].val > 0x21) + { + if (ext) fprintf(stderr, "(ext) "); + if (par[i].val >= 0x1e && par[i].val <= 0x1f) { + fprintf(stderr, "%i : %s ", code_line, cur_line.c_str()); + fprintf(stderr, "WARNING: $ACM%d register used instead of $ACC%d register Line: %d Param: %d Ext: %d\n", + (par[i].val & 1), (par[i].val & 1), code_line, current_param, ext); + } + else if (par[i].val >= 0x1c && par[i].val <= 0x1d) { + fprintf(stderr, "WARNING: $ACL%d register used instead of $ACC%d register Line: %d Param: %d\n", + (par[i].val & 1), (par[i].val & 1), code_line, current_param); + } + else + ShowError(ERR_WRONG_PARAMETER_ACC); + } + break; + case P_ACCM: + if ((int)par[i].val < 0x1e || (int)par[i].val > 0x1f) + { + if (ext) fprintf(stderr, "(ext) "); + if (par[i].val >= 0x1c && par[i].val <= 0x1d) + fprintf(stderr, "WARNING: $ACL%d register used instead of $ACM%d register Line: %d Param: %d\n", + (par[i].val & 1), (par[i].val & 1), code_line, current_param); + else if (par[i].val >= 0x20 && par[i].val <= 0x21) + fprintf(stderr, "WARNING: $ACC%d register used instead of $ACM%d register Line: %d Param: %d\n", + (par[i].val & 1), (par[i].val & 1), code_line, current_param); + else + ShowError(ERR_WRONG_PARAMETER_ACC); + } + break; + + case P_ACCL: + if ((int)par[i].val < 0x1c || (int)par[i].val > 0x1d) + { + if (ext) fprintf(stderr, "(ext) "); + if (par[i].val >= 0x1e && par[i].val <= 0x1f) + { + fprintf(stderr, "%s ", cur_line.c_str()); + fprintf(stderr, "WARNING: $ACM%d register used instead of $ACL%d register Line: %d Param: %d\n", + (par[i].val & 1), (par[i].val & 1), code_line, current_param); + } + else if (par[i].val >= 0x20 && par[i].val <= 0x21) { + fprintf(stderr, "%s ", cur_line.c_str()); + fprintf(stderr, "WARNING: $ACC%d register used instead of $ACL%d register Line: %d Param: %d\n", + (par[i].val & 1), (par[i].val & 1), code_line, current_param); + } + else + ShowError(ERR_WRONG_PARAMETER_ACC); + } + break; +/* case P_ACCM_D: //P_ACC_MID: + if ((int)par[i].val < 0x1e || (int)par[i].val > 0x1f) + { + ShowError(ERR_WRONG_PARAMETER_MID_ACC); + } + break;*/ + } + continue; + } + + switch (par[i].type & (P_REG | 7)) + { + case P_REG: + if (ext) fprintf(stderr, "(ext) "); + ShowError(ERR_EXPECTED_PARAM_REG); + break; + case P_MEM: + if (ext) fprintf(stderr, "(ext) "); + ShowError(ERR_EXPECTED_PARAM_MEM); + break; + case P_VAL: + if (ext) fprintf(stderr, "(ext) "); + ShowError(ERR_EXPECTED_PARAM_VAL); + break; + case P_IMM: + if (ext) fprintf(stderr, "(ext) "); + ShowError(ERR_EXPECTED_PARAM_IMM); + break; + } + ShowError(ERR_WRONG_PARAMETER); + break; + } + else if ((opc->params[i].type & 3) != 0 && (par[i].type & 3) != 0) + { + // modified by Hermes: test NUMBER range + int value = get_mask_shifted_down(opc->params[i].mask); + unsigned int valueu = 0xffff & ~(value >> 1); + if ((int)par[i].val < 0) + { + if (value == 7) // value 7 por sbclr/sbset + { + fprintf(stderr,"Value must be from 0x0 to 0x%x\n", value); + ShowError(ERR_OUT_RANGE_NUMBER); + } + else if (opc->params[i].type == P_MEM) + { + if (value < 256) + fprintf(stderr, "Address value must be from 0x%x to 0x%x\n",valueu, (value>>1)); + else + fprintf(stderr, "Address value must be from 0x0 to 0x%x\n", value); + + ShowError(ERR_OUT_RANGE_NUMBER); + } + else if ((int)par[i].val < -((value >> 1) + 1)) + { + if (value < 128) + fprintf(stderr, "Value must be from -0x%x to 0x%x, is %i\n", + (value >> 1) + 1, value >> 1, par[i].val); + else + fprintf(stderr, "Value must be from -0x%x to 0x%x or 0x0 to 0x%x, is %i\n", + (value >> 1) + 1, value >> 1, value, par[i].val); + + ShowError(ERR_OUT_RANGE_NUMBER); + } + } + else + { + if (value == 7) // value 7 por sbclr/sbset + { + if (par[i].val > (unsigned)value) + { + fprintf(stderr,"Value must be from 0x%x to 0x%x, is %i\n",valueu, value, par[i].val); + ShowError(ERR_OUT_RANGE_NUMBER); + } + } + else if (opc->params[i].type == P_MEM) + { + if (value < 256) + value >>= 1; // addressing 8 bit with sign + if (par[i].val > (unsigned)value && + (par[i].val < valueu || par[i].val > (unsigned)0xffff)) + { + if (value < 256) + fprintf(stderr,"Address value must be from 0x%x to 0x%x, is %04x\n", valueu, value, par[i].val); + else + fprintf(stderr,"Address value must be minor of 0x%x\n", value+1); + ShowError(ERR_OUT_RANGE_NUMBER); + } + } + else + { + if (value < 128) + value >>= 1; // special case ASL/ASR/LSL/LSR + if (par[i].val > (unsigned)value) + { + if (value < 64) + fprintf(stderr,"Value must be from -0x%x to 0x%x, is %i\n", (value + 1), value, par[i].val); + else + fprintf(stderr,"Value must be minor of 0x%x, is %i\n", value + 1, par[i].val); + ShowError(ERR_OUT_RANGE_NUMBER); + } + } + } + continue; + } + } + m_current_param = 0; + return true; +} + + +// Merge opcode with params. +void DSPAssembler::BuildCode(const opc_t *opc, param_t *par, u32 par_count, u16 *outbuf) +{ + outbuf[m_cur_addr] |= opc->opcode; + for (u32 i = 0; i < par_count; i++) + { + // Ignore the "reverse" parameters since they are implicit. + if (opc->params[i].type != P_ACC_D && opc->params[i].type != P_ACCM_D) + { + u16 t16 = outbuf[m_cur_addr + opc->params[i].loc]; + u16 v16 = par[i].val; + if (opc->params[i].lshift > 0) + v16 <<= opc->params[i].lshift; + else + v16 >>= -opc->params[i].lshift; + v16 &= opc->params[i].mask; + outbuf[m_cur_addr + opc->params[i].loc] = t16 | v16; + } + } +} + +void DSPAssembler::InitPass(int pass) +{ + failed = false; + if (pass == 1) + { + // Reset label table. Pre-populate with hw addresses and registers. + labels.Clear(); + labels.RegisterDefaults(); + aliases.clear(); + aliases["S15"] = "SET15"; + aliases["S16"] = "SET16"; + aliases["S40"] = "SET40"; + } + m_cur_addr = 0; + m_totalSize = 0; + cur_segment = SEGMENT_CODE; + segment_addr[SEGMENT_CODE] = 0; + segment_addr[SEGMENT_DATA] = 0; + segment_addr[SEGMENT_OVERLAY] = 0; +} + +bool DSPAssembler::AssembleFile(const char *fname, int pass) +{ + int disable_text = 0; // modified by Hermes + + std::ifstream fsrc; + OpenFStream(fsrc, fname, std::ios_base::in); + + if (fsrc.fail()) + { + std::cerr << "Cannot open file " << fname << std::endl; + return false; + } + + //printf("%s: Pass %d\n", fname, pass); + code_line = 0; + m_cur_pass = pass; + +#define LINEBUF_SIZE 1024 + char line[LINEBUF_SIZE] = {0}; + while (!failed && !fsrc.fail() && !fsrc.eof()) + { + int opcode_size = 0; + fsrc.getline(line, LINEBUF_SIZE); + if(fsrc.fail()) + break; + + cur_line = line; + //printf("A: %s\n", line); + code_line++; + + param_t params[10] = {{0, P_NONE, NULL}}; + param_t params_ext[10] = {{0, P_NONE, NULL}}; + + bool upper = true; + for (int i = 0; i < LINEBUF_SIZE; i++) + { + char c = line[i]; + // This stuff handles /**/ and // comments. + // modified by Hermes : added // and /* */ for long commentaries + if (c == '/') + { + if (i < 1023) + { + if (line[i+1] == '/') + c = 0x00; + else if (line[i+1] == '*') + { + // toggle comment mode. + disable_text = !disable_text; + } + } + } + else if (c == '*') + { + if (i < 1023 && line[i+1] == '/' && disable_text) + { + disable_text = 0; + c = 32; + line[i + 1] = 32; + } + } + + // turn text into spaces if disable_text is on (in a comment). + if (disable_text && ((unsigned char)c) > 32) c = 32; + + if (c == 0x0a || c == 0x0d || c == ';') + c = 0x00; + if (c == 0x09) // tabs to spaces + c = ' '; + if (c == '"') + upper = !upper; + if (upper && c >= 'a' && c <= 'z') // convert to uppercase + c = c - 'a' + 'A'; + line[i] = c; + if (c == 0) + break; // modified by Hermes + } + char *ptr = line; + + std::string label; + + size_t col_pos = std::string(line).find(":"); + if (col_pos != std::string::npos) + { + bool valid = true; + + for(int j = 0; j < (int)col_pos; j++) + { + if (j == 0) + if (!((ptr[j] >= 'A' && ptr[j] <= 'Z') || (ptr[j] == '_'))) + valid = false; + if (!((ptr[j] >= '0' && ptr[j] <= '9') || (ptr[j] >= 'A' && ptr[j] <= 'Z') || (ptr[j] == '_'))) + valid = false; + } + if (valid) + { + label = std::string(line).substr(0, col_pos); + ptr += col_pos + 1; + } + } + + char *opcode = NULL; + opcode = strtok(ptr, " "); + char *opcode_ext = NULL; + + u32 params_count = 0; + u32 params_count_ext = 0; + if (opcode) + { + if ((opcode_ext = strstr(opcode, "'")) != NULL) + { + opcode_ext[0] = '\0'; + opcode_ext++; + if (strlen(opcode_ext) == 0) + opcode_ext = NULL; + } + // now we have opcode and label + + params_count = 0; + params_count_ext = 0; + + char *paramstr = strtok(NULL, "\0"); + char *paramstr_ext = 0; + // there is valid opcode so probably we have parameters + + if (paramstr) + { + if ((paramstr_ext = strstr(paramstr, ":")) != NULL) + { + paramstr_ext[0] = '\0'; + paramstr_ext++; + } + } + + if (paramstr) + params_count = GetParams(paramstr, params); + if (paramstr_ext) + params_count_ext = GetParams(paramstr_ext, params_ext); + } + + if (!label.empty()) + { + // there is a valid label so lets store it in labels table + u32 lval = m_cur_addr; + if (opcode) + { + if (strcmp(opcode, "EQU") == 0) + { + lval = params[0].val; + opcode = NULL; + } + } + if (pass == 1) + labels.RegisterLabel(label, lval); + } + + if (opcode == NULL) + continue; + + // check if opcode is reserved compiler word + if (strcmp("INCLUDE", opcode) == 0) + { + if (params[0].type == P_STR) + { + char *tmpstr; + u32 thisCodeline = code_line; + + if (include_dir.size()) + { + tmpstr = (char *)malloc(include_dir.size() + strlen(params[0].str) + 2); + sprintf(tmpstr, "%s/%s", include_dir.c_str(), params[0].str); + } + else + { + tmpstr = (char *)malloc(strlen(params[0].str) + 1); + strcpy(tmpstr, params[0].str); + } + + AssembleFile(tmpstr, pass); + + code_line = thisCodeline; + + free(tmpstr); + } + else + ShowError(ERR_EXPECTED_PARAM_STR); + continue; + } + + if (strcmp("INCDIR", opcode) == 0) + { + if (params[0].type == P_STR) + include_dir = params[0].str; + else + ShowError(ERR_EXPECTED_PARAM_STR); + continue; + } + + if (strcmp("ORG", opcode) == 0) + { + if (params[0].type == P_VAL) + m_cur_addr = params[0].val; + else + ShowError(ERR_EXPECTED_PARAM_VAL); + continue; + } + + if (strcmp("SEGMENT", opcode) == 0) + { + if (params[0].type == P_STR) + { + segment_addr[cur_segment] = m_cur_addr; + if (strcmp("DATA", params[0].str) == 0) + cur_segment = SEGMENT_DATA; + if (strcmp("CODE", params[0].str) == 0) + cur_segment = SEGMENT_CODE; + m_cur_addr = segment_addr[cur_segment]; + } + else + ShowError(ERR_EXPECTED_PARAM_STR); + continue; + } + + const opc_t *opc = FindOpcode(opcode, params_count, opcodes, opcodes_size); + if (!opc) + opc = &cw; + + opcode_size = opc->size; + + VerifyParams(opc, params, params_count); + + const opc_t *opc_ext = NULL; + // Check for opcode extensions. + if (opc->extended) + { + if (opcode_ext) + { + opc_ext = FindOpcode(opcode_ext, params_count_ext, opcodes_ext, opcodes_ext_size); + VerifyParams(opc_ext, params_ext, params_count_ext, true); + } + else if (params_count_ext) + ShowError(ERR_EXT_PAR_NOT_EXT); + } + else + { + if (opcode_ext) + ShowError(ERR_EXT_CANT_EXTEND_OPCODE); + if (params_count_ext) + ShowError(ERR_EXT_PAR_NOT_EXT); + } + + if (pass == 2) + { + // generate binary + ((u16 *)gdg_buffer)[m_cur_addr] = 0x0000; + BuildCode(opc, params, params_count, (u16 *)gdg_buffer); + if (opc_ext) + BuildCode(opc_ext, params_ext, params_count_ext, (u16 *)gdg_buffer); + } + + m_cur_addr += opcode_size; + m_totalSize += opcode_size; + }; + + if (!failed) + fsrc.close(); + + return !failed; +} diff --git a/Source/Core/Core/DSP/DSPAssembler.h b/Source/Core/Core/DSP/DSPAssembler.h new file mode 100644 index 0000000..6ee7906 --- /dev/null +++ b/Source/Core/Core/DSP/DSPAssembler.h @@ -0,0 +1,137 @@ +/*==================================================================== + + 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. + + ====================================================================*/ + +#pragma once + +#include +#include + +#include "Common/Common.h" + +#include "Core/DSP/DSPDisassembler.h" +#include "Core/DSP/DSPTables.h" +#include "Core/DSP/LabelMap.h" + +enum err_t +{ + ERR_OK = 0, + ERR_UNKNOWN, + ERR_UNKNOWN_OPCODE, + ERR_NOT_ENOUGH_PARAMETERS, + ERR_TOO_MANY_PARAMETERS, + ERR_WRONG_PARAMETER, + ERR_EXPECTED_PARAM_STR, + ERR_EXPECTED_PARAM_VAL, + ERR_EXPECTED_PARAM_REG, + ERR_EXPECTED_PARAM_MEM, + ERR_EXPECTED_PARAM_IMM, + ERR_INCORRECT_BIN, + ERR_INCORRECT_HEX, + ERR_INCORRECT_DEC, + ERR_LABEL_EXISTS, + ERR_UNKNOWN_LABEL, + ERR_NO_MATCHING_BRACKETS, + ERR_EXT_CANT_EXTEND_OPCODE, + ERR_EXT_PAR_NOT_EXT, + ERR_WRONG_PARAMETER_ACC, + ERR_WRONG_PARAMETER_MID_ACC, + ERR_INVALID_REGISTER, + ERR_OUT_RANGE_NUMBER +}; + + +// Unless you want labels to carry over between files, you probably +// want to create a new DSPAssembler for every file you assemble. +class DSPAssembler +{ +public: + DSPAssembler(const AssemblerSettings &settings); + ~DSPAssembler(); + + // line_numbers is optional (and not yet implemented). It'll receieve a list of ints, + // one for each word of code, indicating the source assembler code line number it came from. + + // If returns false, call GetErrorString to get some text to present to the user. + bool Assemble(const char *text, std::vector &code, std::vector *line_numbers = NULL); + + std::string GetErrorString() const { return last_error_str; } + err_t GetError() const { return last_error; } + +private: + struct param_t + { + u32 val; + partype_t type; + char *str; + }; + + enum segment_t + { + SEGMENT_CODE = 0, + SEGMENT_DATA, + SEGMENT_OVERLAY, + SEGMENT_MAX + }; + + // Utility functions + s32 ParseValue(const char *str); + u32 ParseExpression(const char *ptr); + + u32 GetParams(char *parstr, param_t *par); + + void InitPass(int pass); + bool AssembleFile(const char *fname, int pass); + + void ShowError(err_t err_code, const char *extra_info = NULL); + // void ShowWarning(err_t err_code, const char *extra_info = NULL); + + char *FindBrackets(char *src, char *dst); + const opc_t *FindOpcode(const char *opcode, u32 par_count, const opc_t * const opcod, int opcod_size); + bool VerifyParams(const opc_t *opc, param_t *par, int count, bool ext = false); + void BuildCode(const opc_t *opc, param_t *par, u32 par_count, u16 *outbuf); + + char *gdg_buffer; + + std::string include_dir; + std::string cur_line; + + u32 m_cur_addr; + int m_totalSize; + u8 m_cur_pass; + + LabelMap labels; + + u32 code_line; + bool failed; + std::string last_error_str; + err_t last_error; + + typedef std::map AliasMap; + AliasMap aliases; + + segment_t cur_segment; + u32 segment_addr[SEGMENT_MAX]; + int m_current_param; + const AssemblerSettings settings_; +}; diff --git a/Source/Core/Core/DSP/DSPCodeUtil.cpp b/Source/Core/Core/DSP/DSPCodeUtil.cpp index 6cafd66..60c9b4e 100644 --- a/Source/Core/Core/DSP/DSPCodeUtil.cpp +++ b/Source/Core/Core/DSP/DSPCodeUtil.cpp @@ -9,9 +9,9 @@ #include "Common/FileUtil.h" #include "Common/StringUtil.h" -#include "Core/DSP/assemble.h" -#include "Core/DSP/disassemble.h" +#include "Core/DSP/DSPAssembler.h" #include "Core/DSP/DSPCodeUtil.h" +#include "Core/DSP/DSPDisassembler.h" bool Assemble(const char *text, std::vector &code, bool force) { diff --git a/Source/Core/Core/DSP/DSPDisassembler.cpp b/Source/Core/Core/DSP/DSPDisassembler.cpp new file mode 100644 index 0000000..8df83f3 --- /dev/null +++ b/Source/Core/Core/DSP/DSPDisassembler.cpp @@ -0,0 +1,359 @@ +/*==================================================================== + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + ====================================================================*/ + +#include +#include + +#include "Common/Common.h" +#include "Common/FileUtil.h" + +#include "Core/DSP/DSPDisassembler.h" +#include "Core/DSP/DSPTables.h" + +#ifdef _MSC_VER +#pragma warning(disable:4996) +#endif + +#ifndef MAX_PATH +#include // For MAX_PATH +#endif + +extern void nop(const UDSPInstruction opc); + +DSPDisassembler::DSPDisassembler(const AssemblerSettings &settings) + : settings_(settings) +{ +} + +DSPDisassembler::~DSPDisassembler() +{ + // Some old code for logging unknown ops. + std::string filename = File::GetUserPath(D_DUMPDSP_IDX) + "UnkOps.txt"; + File::IOFile uo(filename, "w"); + if (!uo) + return; + + int count = 0; + for (const auto& entry : unk_opcodes) + { + if (entry.second > 0) + { + count++; + fprintf(uo.GetHandle(), "OP%04x\t%d", entry.first, entry.second); + for (int j = 15; j >= 0; j--) // print op bits + { + if ((j & 0x3) == 3) + fprintf(uo.GetHandle(), "\tb"); + fprintf(uo.GetHandle(), "%d", (entry.first >> j) & 0x1); + } + fprintf(uo.GetHandle(), "\n"); + } + } + fprintf(uo.GetHandle(), "Unknown opcodes count: %d\n", count); +} + +bool DSPDisassembler::Disassemble(int start_pc, const std::vector &code, int base_addr, std::string &text) +{ + const char *tmp1 = "tmp1.bin"; + + // First we have to dump the code to a bin file. + { + File::IOFile f(tmp1, "wb"); + f.WriteArray(&code[0], code.size()); + } + + // Run the two passes. + return DisFile(tmp1, base_addr, 1, text) && DisFile(tmp1, base_addr, 2, text); +} + +char *DSPDisassembler::DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char *strbuf) +{ + char *buf = strbuf; + for (int j = 0; j < opc.param_count; j++) + { + if (j > 0) + buf += sprintf(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) + sprintf(buf, "$%s", pdregname(val)); + else + sprintf(buf, "$%d", val); + break; + + case P_PRG: + if (settings_.decode_registers) + sprintf(buf, "@$%s", pdregname(val)); + else + sprintf(buf, "@$%d", val); + break; + + case P_VAL: + case P_ADDR_I: + case P_ADDR_D: + if (settings_.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 == 0x003f) // LSL, LSR, ASL, ASR + sprintf(buf, "#%d", (val & 0x20) ? (val | 0xFFFFFFC0) : val); // 6-bit sign extension + else + sprintf(buf, "#0x%02x", val); + } + else + { + sprintf(buf, "#0x%04x", val); + } + break; + + case P_MEM: + if (opc.params[j].size != 2) + val = (u16)(s16)(s8)val; + + if (settings_.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); + break; + } + + buf += strlen(buf); + } + + return strbuf; +} + +static void MakeLowerCase(char *ptr) +{ + int i = 0; + while (ptr[i]) + { + ptr[i] = tolower(ptr[i]); + i++; + } +} + +bool DSPDisassembler::DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest) +{ + char buffer[256]; + char *buf = buffer; + + // Start with 8 spaces, if there's no label. + buf[0] = ' '; + buf[1] = '\0'; + buf++; + + if ((*pc & 0x7fff) >= 0x1000) + { + ++pc; + dest.append("; outside memory"); + return false; + } + + const u32 op1 = binbuf[*pc & 0x0fff]; + + const DSPOPCTemplate *opc = NULL; + const DSPOPCTemplate *opc_ext = NULL; + + // find opcode + for (int j = 0; j < opcodes_size; j++) + { + u16 mask = opcodes[j].opcode_mask; + + if ((op1 & mask) == opcodes[j].opcode) + { + opc = &opcodes[j]; + break; + } + } + const DSPOPCTemplate fake_op = {"CW", 0x0000, 0x0000, nop, NULL, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}}, false, false, false, false, false}; + if (!opc) + opc = &fake_op; + + bool extended = false; + bool only7bitext = false; + + if (((opc->opcode >> 12) == 0x3) && (op1 & 0x007f)) { + extended = true; + only7bitext = true; + } + else if (((opc->opcode >> 12) > 0x3) && (op1 & 0x00ff)) + extended = true; + else + extended = false; + + if (extended) + { + // opcode has an extension + // find opcode + for (int j = 0; j < opcodes_ext_size; j++) + { + if (only7bitext) { + if (((op1 & 0x7f) & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode) + { + opc_ext = &opcodes_ext[j]; + break; + } + } + else { + if ((op1 & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode) + { + opc_ext = &opcodes_ext[j]; + break; + } + } + } + } + + // printing + + if (settings_.show_pc) + buf += sprintf(buf, "%04x ", *pc); + + u32 op2; + + // Size 2 - the op has a large immediate. + if (opc->size == 2) + { + op2 = binbuf[(*pc + 1) & 0x0fff]; + if (settings_.show_hex) + buf += sprintf(buf, "%04x %04x ", op1, op2); + } + else + { + op2 = 0; + if (settings_.show_hex) + buf += sprintf(buf, "%04x ", op1); + } + + char opname[20]; + strcpy(opname, opc->name); + if (settings_.lower_case_ops) + MakeLowerCase(opname); + char ext_buf[20]; + if (extended) + sprintf(ext_buf, "%s%c%s", opname, settings_.ext_separator, opc_ext->name); + else + sprintf(ext_buf, "%s", opname); + if (settings_.lower_case_ops) + MakeLowerCase(ext_buf); + + if (settings_.print_tabs) + buf += sprintf(buf, "%s\t", ext_buf); + else + buf += sprintf(buf, "%-12s", ext_buf); + + if (opc->param_count > 0) + DisParams(*opc, op1, op2, buf); + + buf += strlen(buf); + + // Handle opcode extension. + if (extended) + { + if (opc->param_count > 0) + buf += sprintf(buf, " "); + buf += sprintf(buf, ": "); + if (opc_ext->param_count > 0) + DisParams(*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) + *pc += opc_ext->size; + else + *pc += opc->size; + + if (pass == 2) + dest.append(buffer); + return true; +} + +bool DSPDisassembler::DisFile(const char* name, int base_addr, int pass, std::string &output) +{ + File::IOFile in(name, "rb"); + if (!in) + { + printf("gd_dis_file: No input\n"); + return false; + } + + const int size = ((int)in.GetSize() & ~1) / 2; + u16 *const binbuf = new u16[size]; + in.ReadArray(binbuf, size); + in.Close(); + + // Actually do the disassembly. + for (u16 pc = 0; pc < size;) + { + DisOpcode(binbuf, base_addr, pass, &pc, output); + if (pass == 2) + output.append("\n"); + } + delete[] binbuf; + return true; +} diff --git a/Source/Core/Core/DSP/DSPDisassembler.h b/Source/Core/Core/DSP/DSPDisassembler.h new file mode 100644 index 0000000..855ee9a --- /dev/null +++ b/Source/Core/Core/DSP/DSPDisassembler.h @@ -0,0 +1,84 @@ +/*==================================================================== + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + ====================================================================*/ + +#pragma once + +#include +#include + +#include "Common/Common.h" + +#include "Core/DSP/DSPTables.h" +#include "Core/DSP/LabelMap.h" + +struct AssemblerSettings +{ + AssemblerSettings() + : print_tabs(false), + show_hex(false), + show_pc(false), + force(false), + decode_names(true), + decode_registers(true), + ext_separator('\''), + lower_case_ops(true), + pc(0) + { + } + + bool print_tabs; + bool show_hex; + bool show_pc; + bool force; + bool decode_names; + bool decode_registers; + char ext_separator; + bool lower_case_ops; + + u16 pc; +}; + +class DSPDisassembler +{ +public: + DSPDisassembler(const AssemblerSettings &settings); + ~DSPDisassembler(); + + bool Disassemble(int start_pc, const std::vector &code, int base_addr, std::string &text); + + // Warning - this one is trickier to use right. + // Use pass == 2 if you're just using it by itself. + bool DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest); + +private: + // Moves PC forward and writes the result to dest. + bool DisFile(const char* name, int base_addr, int pass, std::string &output); + + char* DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char* strbuf); + std::map unk_opcodes; + + const AssemblerSettings settings_; + + LabelMap labels; +}; diff --git a/Source/Core/Core/DSP/assemble.cpp b/Source/Core/Core/DSP/assemble.cpp deleted file mode 100644 index eecfc06..0000000 --- a/Source/Core/Core/DSP/assemble.cpp +++ /dev/null @@ -1,1021 +0,0 @@ -/*==================================================================== - -$Id: assemble.cpp,v 1.3 2008-11-11 01:04:26 wntrmute Exp $ - -project: GameCube DSP Tool (gcdsp) -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. - -Revision 1.4 2008/10/04 10:30:00 Hermes -added function to export the code to .h file -added support for '/ *' '* /' and '//' for comentaries -added some sintax detection when use registers - -$Log: not supported by cvs2svn $ -Revision 1.2 2005/09/14 02:19:29 wntrmute -added header guards -use standard main function - -Revision 1.1 2005/08/24 22:13:34 wntrmute -Initial import - - -====================================================================*/ - -#include -#include -#include -#include -#include - -#include "Common/Common.h" -#include "Common/FileUtil.h" - -#include "Core/DSP/assemble.h" -#include "Core/DSP/disassemble.h" -#include "Core/DSP/DSPInterpreter.h" -#include "Core/DSP/DSPTables.h" - -static const char *err_string[] = -{ - "", - "Unknown Error", - "Unknown opcode", - "Not enough parameters", - "Too many parameters", - "Wrong parameter", - "Expected parameter of type 'string'", - "Expected parameter of type 'value'", - "Expected parameter of type 'register'", - "Expected parameter of type 'memory pointer'", - "Expected parameter of type 'immediate'", - "Incorrect binary value", - "Incorrect hexadecimal value", - "Incorrect decimal value", - "Label already exists", - "Label not defined", - "No matching brackets", - "This opcode cannot be extended", - "Given extending params for non extensible opcode", - "Wrong parameter: must be accumulator register", - "Wrong parameter: must be mid accumulator register", - "Invalid register", - "Number out of range" -}; - -DSPAssembler::DSPAssembler(const AssemblerSettings &settings) : - gdg_buffer(NULL), - m_cur_addr(0), - m_cur_pass(0), - m_current_param(0), - settings_(settings) - -{ -} - -DSPAssembler::~DSPAssembler() -{ - if(gdg_buffer) - free(gdg_buffer); -} - -bool DSPAssembler::Assemble(const char *text, std::vector &code, std::vector *line_numbers) -{ - if (line_numbers) - line_numbers->clear(); - const char *fname = "tmp.asm"; - if (!File::WriteStringToFile(text, fname)) - return false; - InitPass(1); - if (!AssembleFile(fname, 1)) - return false; - - // We now have the size of the output buffer - if (m_totalSize > 0) - { - gdg_buffer = (char *)malloc(m_totalSize * sizeof(u16) + 4); - if(!gdg_buffer) - return false; - - memset(gdg_buffer, 0, m_totalSize * sizeof(u16)); - } else - return false; - - InitPass(2); - if (!AssembleFile(fname, 2)) - return false; - - code.resize(m_totalSize); - for (int i = 0; i < m_totalSize; i++) { - code[i] = *(u16 *)(gdg_buffer + i * 2); - } - - if(gdg_buffer) { - free(gdg_buffer); - gdg_buffer = NULL; - } - - last_error_str = "(no errors)"; - last_error = ERR_OK; - - return true; -} - -void DSPAssembler::ShowError(err_t err_code, const char *extra_info) -{ - - if (!settings_.force) - failed = true; - - char error_buffer[1024]; - char *buf_ptr = error_buffer; - buf_ptr += sprintf(buf_ptr, "%i : %s ", code_line, cur_line.c_str()); - if (!extra_info) - extra_info = "-"; - - if (m_current_param == 0) - buf_ptr += sprintf(buf_ptr, "ERROR: %s Line: %d : %s\n", err_string[err_code], code_line, extra_info); - else - buf_ptr += sprintf(buf_ptr, "ERROR: %s Line: %d Param: %d : %s\n", - err_string[err_code], code_line, m_current_param, extra_info); - last_error_str = error_buffer; - last_error = err_code; -} - -char *skip_spaces(char *ptr) -{ - while (*ptr == ' ') - ptr++; - return ptr; -} - -const char *skip_spaces(const char *ptr) -{ - while (*ptr == ' ') - ptr++; - return ptr; -} - -// Parse a standalone value - it can be a number in one of several formats or a label. -s32 DSPAssembler::ParseValue(const char *str) -{ - bool negative = false; - s32 val = 0; - const char *ptr = str; - - if (ptr[0] == '#') - { - ptr++; - negative = true; // Wow! Double # (needed one to get in here) negates??? - } - if (ptr[0] == '-') - { - ptr++; - negative = true; - } - if (ptr[0] == '0') - { - if (ptr[1] >= '0' && ptr[1] <= '9') - { - for (int i = 0; ptr[i] != 0; i++) - { - val *= 10; - if (ptr[i] >= '0' && ptr[i] <= '9') - val += ptr[i] - '0'; - else - ShowError(ERR_INCORRECT_DEC, str); - } - } - else - { - switch (ptr[1]) - { - case 'X': // hex - for (int i = 2 ; ptr[i] != 0 ; i++) - { - val <<= 4; - if (ptr[i] >= 'a' && ptr[i] <= 'f') - val += (ptr[i]-'a'+10); - else if (ptr[i] >= 'A' && ptr[i] <= 'F') - val += (ptr[i]-'A'+10); - else if (ptr[i] >= '0' && ptr[i] <= '9') - val += (ptr[i] - '0'); - else - ShowError(ERR_INCORRECT_HEX, str); - } - break; - case '\'': // binary - for (int i = 2; ptr[i] != 0; i++) - { - val *=2; - if(ptr[i] >= '0' && ptr[i] <= '1') - val += ptr[i] - '0'; - else - ShowError(ERR_INCORRECT_BIN, str); - } - break; - default: - // value is 0 or error - val = 0; - break; - } - } - } - else - { - // Symbol starts with a digit - it's a dec number. - if (ptr[0] >= '0' && ptr[0] <= '9') - { - for (int i = 0; ptr[i] != 0; i++) - { - val *= 10; - if (ptr[i] >= '0' && ptr[i] <= '9') - val += ptr[i] - '0'; - else - ShowError(ERR_INCORRECT_DEC, str); - } - } - else // Everything else is a label. - { - // Lookup label - u16 value; - if (labels.GetLabelValue(ptr, &value)) - return value; - if (m_cur_pass == 2) - ShowError(ERR_UNKNOWN_LABEL, str); - } - } - if (negative) - return -val; - return val; -} - -// Modifies both src and dst! -// What does it do, really?? -char *DSPAssembler::FindBrackets(char *src, char *dst) -{ - s32 len = (s32) strlen(src); - s32 first = -1; - s32 count = 0; - s32 i, j; - j = 0; - for (i = 0 ; i < len ; i++) - { - if (src[i] == '(') - { - if (first < 0) - { - count = 1; - src[i] = 0x0; - first = i; - } - else - { - count++; - dst[j++] = src[i]; - } - } - else if (src[i] == ')') - { - if (--count == 0) - { - dst[j] = 0; - return &src[i+1]; - } - else - { - dst[j++] = src[i]; - } - } - else - { - if (first >= 0) - dst[j++] = src[i]; - } - } - if (count) - ShowError(ERR_NO_MATCHING_BRACKETS); - return NULL; -} - -// Bizarre in-place expression evaluator. -u32 DSPAssembler::ParseExpression(const char *ptr) -{ - char *pbuf; - s32 val = 0; - - char *d_buffer = (char *)malloc(1024); - char *s_buffer = (char *)malloc(1024); - strcpy(s_buffer, ptr); - - while ((pbuf = FindBrackets(s_buffer, d_buffer)) != NULL) - { - val = ParseExpression(d_buffer); - sprintf(d_buffer, "%s%d%s", s_buffer, val, pbuf); - strcpy(s_buffer, d_buffer); - } - - int j = 0; - for (int i = 0; i < ((s32)strlen(s_buffer) + 1) ; i++) - { - char c = s_buffer[i]; - if (c != ' ') - d_buffer[j++] = c; - } - - for (int i = 0; i < ((s32)strlen(d_buffer) + 1) ; i++) - { - char c = d_buffer[i]; - if (c == '-') - { - if (i == 0) - c = '#'; - else - { - switch (d_buffer[i - 1]) - { - case '/': - case '%': - case '*': - c = '#'; - } - } - } - d_buffer[i] = c; - } - - while ((pbuf = strstr(d_buffer, "+")) != NULL) - { - *pbuf = 0x0; - val = ParseExpression(d_buffer) + ParseExpression(pbuf+1); - sprintf(d_buffer, "%d", val); - } - - while ((pbuf = strstr(d_buffer, "-")) != NULL) - { - *pbuf = 0x0; - val = ParseExpression(d_buffer) - ParseExpression(pbuf+1); - if (val < 0) - { - val = 0x10000 + (val & 0xffff); // ATTENTION: avoid a terrible bug!!! number cannot write with '-' in sprintf - fprintf(stderr, "WARNING: Number Underflow at Line: %d \n", code_line); - } - sprintf(d_buffer, "%d", val); - } - - while ((pbuf = strstr(d_buffer, "*")) != NULL) - { - *pbuf = 0x0; - val = ParseExpression(d_buffer) * ParseExpression(pbuf+1); - sprintf(d_buffer, "%d", val); - } - - while ((pbuf = strstr(d_buffer, "/")) != NULL) - { - *pbuf = 0x0; - val = ParseExpression(d_buffer) / ParseExpression(pbuf+1); - sprintf(d_buffer, "%d", val); - } - - while ((pbuf = strstr(d_buffer, "|")) != NULL) - { - *pbuf = 0x0; - val = ParseExpression(d_buffer) | ParseExpression(pbuf+1); - sprintf(d_buffer, "%d", val); - } - - while ((pbuf = strstr(d_buffer, "&")) != NULL) - { - *pbuf = 0x0; - val = ParseExpression(d_buffer) & ParseExpression(pbuf+1); - sprintf(d_buffer, "%d", val); - } - - val = ParseValue(d_buffer); - free(d_buffer); - free(s_buffer); - return val; -} - -// Destroys parstr -u32 DSPAssembler::GetParams(char *parstr, param_t *par) -{ - u32 count = 0; - char *tmpstr = skip_spaces(parstr); - tmpstr = strtok(tmpstr, ",\x00"); - for (int i = 0; i < 10; i++) - { - if (tmpstr == NULL) - break; - tmpstr = skip_spaces(tmpstr); - if (strlen(tmpstr) == 0) - break; - if (tmpstr) - count++; - else - break; - - par[i].type = P_NONE; - switch (tmpstr[0]) - { - case '"': - par[i].str = strtok(tmpstr, "\""); - par[i].type = P_STR; - break; - case '#': - par[i].val = ParseExpression(tmpstr + 1); - par[i].type = P_IMM; - break; - case '@': - if (tmpstr[1] == '$') - { - par[i].val = ParseExpression(tmpstr + 2); - par[i].type = P_PRG; - } - else - { - par[i].val = ParseExpression(tmpstr + 1); - par[i].type = P_MEM; - } - break; - case '$': - par[i].val = ParseExpression(tmpstr + 1); - par[i].type = P_REG; - break; - - default: - par[i].val = ParseExpression(tmpstr); - par[i].type = P_VAL; - break; - } - tmpstr = strtok(NULL, ",\x00"); - } - return count; -} - -const opc_t *DSPAssembler::FindOpcode(const char *opcode, u32 par_count, const opc_t * const opcod, int opcod_size) -{ - if (opcode[0] == 'C' && opcode[1] == 'W') - return &cw; - - AliasMap::const_iterator alias_iter = aliases.find(opcode); - if (alias_iter != aliases.end()) - opcode = alias_iter->second.c_str(); - for (int i = 0; i < opcod_size; i++) - { - const opc_t *opc = &opcod[i]; - if (strcmp(opc->name, opcode) == 0) - { - if (par_count < opc->param_count) - { - ShowError(ERR_NOT_ENOUGH_PARAMETERS); - } - if (par_count > opc->param_count) - { - ShowError(ERR_TOO_MANY_PARAMETERS); - } - return opc; - } - } - ShowError(ERR_UNKNOWN_OPCODE); - return NULL; -} - -// weird... -u16 get_mask_shifted_down(u16 mask) -{ - while (!(mask & 1)) - mask >>= 1; - return mask; -} - -bool DSPAssembler::VerifyParams(const opc_t *opc, param_t *par, int count, bool ext) -{ - for (int i = 0; i < count; i++) - { - const int current_param = i + 1; // just for display. - if (opc->params[i].type != par[i].type || (par[i].type & P_REG)) - { - if (par[i].type == P_VAL && - (opc->params[i].type == P_ADDR_I || opc->params[i].type == P_ADDR_D)) - { - // Data and instruction addresses are valid as VAL values. - continue; - } - - if ((opc->params[i].type & P_REG) && (par[i].type & P_REG)) - { - // Just a temp. Should be replaced with more purposeful vars. - int value; - - // modified by Hermes: test the register range - switch ((unsigned)opc->params[i].type) - { - case P_REG18: - case P_REG19: - case P_REG1A: - value = (opc->params[i].type >> 8) & 31; - if ((int)par[i].val < value || - (int)par[i].val > value + get_mask_shifted_down(opc->params[i].mask)) - { - if (ext) fprintf(stderr, "(ext) "); - fprintf(stderr, "%s (param %i)", cur_line.c_str(), current_param); - ShowError(ERR_INVALID_REGISTER); - } - break; - case P_PRG: - if ((int)par[i].val < 0 || (int)par[i].val > 0x3) - { - if (ext) fprintf(stderr, "(ext) "); - fprintf(stderr, "%s (param %i)", cur_line.c_str(), current_param); - ShowError(ERR_INVALID_REGISTER); - } - break; - case P_ACC: - if ((int)par[i].val < 0x20 || (int)par[i].val > 0x21) - { - if (ext) fprintf(stderr, "(ext) "); - if (par[i].val >= 0x1e && par[i].val <= 0x1f) { - fprintf(stderr, "%i : %s ", code_line, cur_line.c_str()); - fprintf(stderr, "WARNING: $ACM%d register used instead of $ACC%d register Line: %d Param: %d Ext: %d\n", - (par[i].val & 1), (par[i].val & 1), code_line, current_param, ext); - } - else if (par[i].val >= 0x1c && par[i].val <= 0x1d) { - fprintf(stderr, "WARNING: $ACL%d register used instead of $ACC%d register Line: %d Param: %d\n", - (par[i].val & 1), (par[i].val & 1), code_line, current_param); - } - else - ShowError(ERR_WRONG_PARAMETER_ACC); - } - break; - case P_ACCM: - if ((int)par[i].val < 0x1e || (int)par[i].val > 0x1f) - { - if (ext) fprintf(stderr, "(ext) "); - if (par[i].val >= 0x1c && par[i].val <= 0x1d) - fprintf(stderr, "WARNING: $ACL%d register used instead of $ACM%d register Line: %d Param: %d\n", - (par[i].val & 1), (par[i].val & 1), code_line, current_param); - else if (par[i].val >= 0x20 && par[i].val <= 0x21) - fprintf(stderr, "WARNING: $ACC%d register used instead of $ACM%d register Line: %d Param: %d\n", - (par[i].val & 1), (par[i].val & 1), code_line, current_param); - else - ShowError(ERR_WRONG_PARAMETER_ACC); - } - break; - - case P_ACCL: - if ((int)par[i].val < 0x1c || (int)par[i].val > 0x1d) - { - if (ext) fprintf(stderr, "(ext) "); - if (par[i].val >= 0x1e && par[i].val <= 0x1f) - { - fprintf(stderr, "%s ", cur_line.c_str()); - fprintf(stderr, "WARNING: $ACM%d register used instead of $ACL%d register Line: %d Param: %d\n", - (par[i].val & 1), (par[i].val & 1), code_line, current_param); - } - else if (par[i].val >= 0x20 && par[i].val <= 0x21) { - fprintf(stderr, "%s ", cur_line.c_str()); - fprintf(stderr, "WARNING: $ACC%d register used instead of $ACL%d register Line: %d Param: %d\n", - (par[i].val & 1), (par[i].val & 1), code_line, current_param); - } - else - ShowError(ERR_WRONG_PARAMETER_ACC); - } - break; -/* case P_ACCM_D: //P_ACC_MID: - if ((int)par[i].val < 0x1e || (int)par[i].val > 0x1f) - { - ShowError(ERR_WRONG_PARAMETER_MID_ACC); - } - break;*/ - } - continue; - } - - switch (par[i].type & (P_REG | 7)) - { - case P_REG: - if (ext) fprintf(stderr, "(ext) "); - ShowError(ERR_EXPECTED_PARAM_REG); - break; - case P_MEM: - if (ext) fprintf(stderr, "(ext) "); - ShowError(ERR_EXPECTED_PARAM_MEM); - break; - case P_VAL: - if (ext) fprintf(stderr, "(ext) "); - ShowError(ERR_EXPECTED_PARAM_VAL); - break; - case P_IMM: - if (ext) fprintf(stderr, "(ext) "); - ShowError(ERR_EXPECTED_PARAM_IMM); - break; - } - ShowError(ERR_WRONG_PARAMETER); - break; - } - else if ((opc->params[i].type & 3) != 0 && (par[i].type & 3) != 0) - { - // modified by Hermes: test NUMBER range - int value = get_mask_shifted_down(opc->params[i].mask); - unsigned int valueu = 0xffff & ~(value >> 1); - if ((int)par[i].val < 0) - { - if (value == 7) // value 7 por sbclr/sbset - { - fprintf(stderr,"Value must be from 0x0 to 0x%x\n", value); - ShowError(ERR_OUT_RANGE_NUMBER); - } - else if (opc->params[i].type == P_MEM) - { - if (value < 256) - fprintf(stderr, "Address value must be from 0x%x to 0x%x\n",valueu, (value>>1)); - else - fprintf(stderr, "Address value must be from 0x0 to 0x%x\n", value); - - ShowError(ERR_OUT_RANGE_NUMBER); - } - else if ((int)par[i].val < -((value >> 1) + 1)) - { - if (value < 128) - fprintf(stderr, "Value must be from -0x%x to 0x%x, is %i\n", - (value >> 1) + 1, value >> 1, par[i].val); - else - fprintf(stderr, "Value must be from -0x%x to 0x%x or 0x0 to 0x%x, is %i\n", - (value >> 1) + 1, value >> 1, value, par[i].val); - - ShowError(ERR_OUT_RANGE_NUMBER); - } - } - else - { - if (value == 7) // value 7 por sbclr/sbset - { - if (par[i].val > (unsigned)value) - { - fprintf(stderr,"Value must be from 0x%x to 0x%x, is %i\n",valueu, value, par[i].val); - ShowError(ERR_OUT_RANGE_NUMBER); - } - } - else if (opc->params[i].type == P_MEM) - { - if (value < 256) - value >>= 1; // addressing 8 bit with sign - if (par[i].val > (unsigned)value && - (par[i].val < valueu || par[i].val > (unsigned)0xffff)) - { - if (value < 256) - fprintf(stderr,"Address value must be from 0x%x to 0x%x, is %04x\n", valueu, value, par[i].val); - else - fprintf(stderr,"Address value must be minor of 0x%x\n", value+1); - ShowError(ERR_OUT_RANGE_NUMBER); - } - } - else - { - if (value < 128) - value >>= 1; // special case ASL/ASR/LSL/LSR - if (par[i].val > (unsigned)value) - { - if (value < 64) - fprintf(stderr,"Value must be from -0x%x to 0x%x, is %i\n", (value + 1), value, par[i].val); - else - fprintf(stderr,"Value must be minor of 0x%x, is %i\n", value + 1, par[i].val); - ShowError(ERR_OUT_RANGE_NUMBER); - } - } - } - continue; - } - } - m_current_param = 0; - return true; -} - - -// Merge opcode with params. -void DSPAssembler::BuildCode(const opc_t *opc, param_t *par, u32 par_count, u16 *outbuf) -{ - outbuf[m_cur_addr] |= opc->opcode; - for (u32 i = 0; i < par_count; i++) - { - // Ignore the "reverse" parameters since they are implicit. - if (opc->params[i].type != P_ACC_D && opc->params[i].type != P_ACCM_D) - { - u16 t16 = outbuf[m_cur_addr + opc->params[i].loc]; - u16 v16 = par[i].val; - if (opc->params[i].lshift > 0) - v16 <<= opc->params[i].lshift; - else - v16 >>= -opc->params[i].lshift; - v16 &= opc->params[i].mask; - outbuf[m_cur_addr + opc->params[i].loc] = t16 | v16; - } - } -} - -void DSPAssembler::InitPass(int pass) -{ - failed = false; - if (pass == 1) - { - // Reset label table. Pre-populate with hw addresses and registers. - labels.Clear(); - labels.RegisterDefaults(); - aliases.clear(); - aliases["S15"] = "SET15"; - aliases["S16"] = "SET16"; - aliases["S40"] = "SET40"; - } - m_cur_addr = 0; - m_totalSize = 0; - cur_segment = SEGMENT_CODE; - segment_addr[SEGMENT_CODE] = 0; - segment_addr[SEGMENT_DATA] = 0; - segment_addr[SEGMENT_OVERLAY] = 0; -} - -bool DSPAssembler::AssembleFile(const char *fname, int pass) -{ - int disable_text = 0; // modified by Hermes - - std::ifstream fsrc; - OpenFStream(fsrc, fname, std::ios_base::in); - - if (fsrc.fail()) - { - std::cerr << "Cannot open file " << fname << std::endl; - return false; - } - - //printf("%s: Pass %d\n", fname, pass); - code_line = 0; - m_cur_pass = pass; - -#define LINEBUF_SIZE 1024 - char line[LINEBUF_SIZE] = {0}; - while (!failed && !fsrc.fail() && !fsrc.eof()) - { - int opcode_size = 0; - fsrc.getline(line, LINEBUF_SIZE); - if(fsrc.fail()) - break; - - cur_line = line; - //printf("A: %s\n", line); - code_line++; - - param_t params[10] = {{0, P_NONE, NULL}}; - param_t params_ext[10] = {{0, P_NONE, NULL}}; - - bool upper = true; - for (int i = 0; i < LINEBUF_SIZE; i++) - { - char c = line[i]; - // This stuff handles /**/ and // comments. - // modified by Hermes : added // and /* */ for long commentaries - if (c == '/') - { - if (i < 1023) - { - if (line[i+1] == '/') - c = 0x00; - else if (line[i+1] == '*') - { - // toggle comment mode. - disable_text = !disable_text; - } - } - } - else if (c == '*') - { - if (i < 1023 && line[i+1] == '/' && disable_text) - { - disable_text = 0; - c = 32; - line[i + 1] = 32; - } - } - - // turn text into spaces if disable_text is on (in a comment). - if (disable_text && ((unsigned char)c) > 32) c = 32; - - if (c == 0x0a || c == 0x0d || c == ';') - c = 0x00; - if (c == 0x09) // tabs to spaces - c = ' '; - if (c == '"') - upper = !upper; - if (upper && c >= 'a' && c <= 'z') // convert to uppercase - c = c - 'a' + 'A'; - line[i] = c; - if (c == 0) - break; // modified by Hermes - } - char *ptr = line; - - std::string label; - - size_t col_pos = std::string(line).find(":"); - if (col_pos != std::string::npos) - { - bool valid = true; - - for(int j = 0; j < (int)col_pos; j++) - { - if (j == 0) - if (!((ptr[j] >= 'A' && ptr[j] <= 'Z') || (ptr[j] == '_'))) - valid = false; - if (!((ptr[j] >= '0' && ptr[j] <= '9') || (ptr[j] >= 'A' && ptr[j] <= 'Z') || (ptr[j] == '_'))) - valid = false; - } - if (valid) - { - label = std::string(line).substr(0, col_pos); - ptr += col_pos + 1; - } - } - - char *opcode = NULL; - opcode = strtok(ptr, " "); - char *opcode_ext = NULL; - - u32 params_count = 0; - u32 params_count_ext = 0; - if (opcode) - { - if ((opcode_ext = strstr(opcode, "'")) != NULL) - { - opcode_ext[0] = '\0'; - opcode_ext++; - if (strlen(opcode_ext) == 0) - opcode_ext = NULL; - } - // now we have opcode and label - - params_count = 0; - params_count_ext = 0; - - char *paramstr = strtok(NULL, "\0"); - char *paramstr_ext = 0; - // there is valid opcode so probably we have parameters - - if (paramstr) - { - if ((paramstr_ext = strstr(paramstr, ":")) != NULL) - { - paramstr_ext[0] = '\0'; - paramstr_ext++; - } - } - - if (paramstr) - params_count = GetParams(paramstr, params); - if (paramstr_ext) - params_count_ext = GetParams(paramstr_ext, params_ext); - } - - if (!label.empty()) - { - // there is a valid label so lets store it in labels table - u32 lval = m_cur_addr; - if (opcode) - { - if (strcmp(opcode, "EQU") == 0) - { - lval = params[0].val; - opcode = NULL; - } - } - if (pass == 1) - labels.RegisterLabel(label, lval); - } - - if (opcode == NULL) - continue; - - // check if opcode is reserved compiler word - if (strcmp("INCLUDE", opcode) == 0) - { - if (params[0].type == P_STR) - { - char *tmpstr; - u32 thisCodeline = code_line; - - if (include_dir.size()) - { - tmpstr = (char *)malloc(include_dir.size() + strlen(params[0].str) + 2); - sprintf(tmpstr, "%s/%s", include_dir.c_str(), params[0].str); - } - else - { - tmpstr = (char *)malloc(strlen(params[0].str) + 1); - strcpy(tmpstr, params[0].str); - } - - AssembleFile(tmpstr, pass); - - code_line = thisCodeline; - - free(tmpstr); - } - else - ShowError(ERR_EXPECTED_PARAM_STR); - continue; - } - - if (strcmp("INCDIR", opcode) == 0) - { - if (params[0].type == P_STR) - include_dir = params[0].str; - else - ShowError(ERR_EXPECTED_PARAM_STR); - continue; - } - - if (strcmp("ORG", opcode) == 0) - { - if (params[0].type == P_VAL) - m_cur_addr = params[0].val; - else - ShowError(ERR_EXPECTED_PARAM_VAL); - continue; - } - - if (strcmp("SEGMENT", opcode) == 0) - { - if (params[0].type == P_STR) - { - segment_addr[cur_segment] = m_cur_addr; - if (strcmp("DATA", params[0].str) == 0) - cur_segment = SEGMENT_DATA; - if (strcmp("CODE", params[0].str) == 0) - cur_segment = SEGMENT_CODE; - m_cur_addr = segment_addr[cur_segment]; - } - else - ShowError(ERR_EXPECTED_PARAM_STR); - continue; - } - - const opc_t *opc = FindOpcode(opcode, params_count, opcodes, opcodes_size); - if (!opc) - opc = &cw; - - opcode_size = opc->size; - - VerifyParams(opc, params, params_count); - - const opc_t *opc_ext = NULL; - // Check for opcode extensions. - if (opc->extended) - { - if (opcode_ext) - { - opc_ext = FindOpcode(opcode_ext, params_count_ext, opcodes_ext, opcodes_ext_size); - VerifyParams(opc_ext, params_ext, params_count_ext, true); - } - else if (params_count_ext) - ShowError(ERR_EXT_PAR_NOT_EXT); - } - else - { - if (opcode_ext) - ShowError(ERR_EXT_CANT_EXTEND_OPCODE); - if (params_count_ext) - ShowError(ERR_EXT_PAR_NOT_EXT); - } - - if (pass == 2) - { - // generate binary - ((u16 *)gdg_buffer)[m_cur_addr] = 0x0000; - BuildCode(opc, params, params_count, (u16 *)gdg_buffer); - if (opc_ext) - BuildCode(opc_ext, params_ext, params_count_ext, (u16 *)gdg_buffer); - } - - m_cur_addr += opcode_size; - m_totalSize += opcode_size; - }; - - if (!failed) - fsrc.close(); - - return !failed; -} diff --git a/Source/Core/Core/DSP/assemble.h b/Source/Core/Core/DSP/assemble.h deleted file mode 100644 index 28a9db4..0000000 --- a/Source/Core/Core/DSP/assemble.h +++ /dev/null @@ -1,137 +0,0 @@ -/*==================================================================== - - 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. - - ====================================================================*/ - -#pragma once - -#include -#include - -#include "Common/Common.h" - -#include "Core/DSP/disassemble.h" -#include "Core/DSP/DSPTables.h" -#include "Core/DSP/LabelMap.h" - -enum err_t -{ - ERR_OK = 0, - ERR_UNKNOWN, - ERR_UNKNOWN_OPCODE, - ERR_NOT_ENOUGH_PARAMETERS, - ERR_TOO_MANY_PARAMETERS, - ERR_WRONG_PARAMETER, - ERR_EXPECTED_PARAM_STR, - ERR_EXPECTED_PARAM_VAL, - ERR_EXPECTED_PARAM_REG, - ERR_EXPECTED_PARAM_MEM, - ERR_EXPECTED_PARAM_IMM, - ERR_INCORRECT_BIN, - ERR_INCORRECT_HEX, - ERR_INCORRECT_DEC, - ERR_LABEL_EXISTS, - ERR_UNKNOWN_LABEL, - ERR_NO_MATCHING_BRACKETS, - ERR_EXT_CANT_EXTEND_OPCODE, - ERR_EXT_PAR_NOT_EXT, - ERR_WRONG_PARAMETER_ACC, - ERR_WRONG_PARAMETER_MID_ACC, - ERR_INVALID_REGISTER, - ERR_OUT_RANGE_NUMBER -}; - - -// Unless you want labels to carry over between files, you probably -// want to create a new DSPAssembler for every file you assemble. -class DSPAssembler -{ -public: - DSPAssembler(const AssemblerSettings &settings); - ~DSPAssembler(); - - // line_numbers is optional (and not yet implemented). It'll receieve a list of ints, - // one for each word of code, indicating the source assembler code line number it came from. - - // If returns false, call GetErrorString to get some text to present to the user. - bool Assemble(const char *text, std::vector &code, std::vector *line_numbers = NULL); - - std::string GetErrorString() const { return last_error_str; } - err_t GetError() const { return last_error; } - -private: - struct param_t - { - u32 val; - partype_t type; - char *str; - }; - - enum segment_t - { - SEGMENT_CODE = 0, - SEGMENT_DATA, - SEGMENT_OVERLAY, - SEGMENT_MAX - }; - - // Utility functions - s32 ParseValue(const char *str); - u32 ParseExpression(const char *ptr); - - u32 GetParams(char *parstr, param_t *par); - - void InitPass(int pass); - bool AssembleFile(const char *fname, int pass); - - void ShowError(err_t err_code, const char *extra_info = NULL); - // void ShowWarning(err_t err_code, const char *extra_info = NULL); - - char *FindBrackets(char *src, char *dst); - const opc_t *FindOpcode(const char *opcode, u32 par_count, const opc_t * const opcod, int opcod_size); - bool VerifyParams(const opc_t *opc, param_t *par, int count, bool ext = false); - void BuildCode(const opc_t *opc, param_t *par, u32 par_count, u16 *outbuf); - - char *gdg_buffer; - - std::string include_dir; - std::string cur_line; - - u32 m_cur_addr; - int m_totalSize; - u8 m_cur_pass; - - LabelMap labels; - - u32 code_line; - bool failed; - std::string last_error_str; - err_t last_error; - - typedef std::map AliasMap; - AliasMap aliases; - - segment_t cur_segment; - u32 segment_addr[SEGMENT_MAX]; - int m_current_param; - const AssemblerSettings settings_; -}; diff --git a/Source/Core/Core/DSP/disassemble.cpp b/Source/Core/Core/DSP/disassemble.cpp deleted file mode 100644 index 462b480..0000000 --- a/Source/Core/Core/DSP/disassemble.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/*==================================================================== - - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - ====================================================================*/ - -#include -#include - -#include "Common/Common.h" -#include "Common/FileUtil.h" - -#include "Core/DSP/disassemble.h" -#include "Core/DSP/DSPTables.h" - -#ifdef _MSC_VER -#pragma warning(disable:4996) -#endif - -#ifndef MAX_PATH -#include // For MAX_PATH -#endif - -extern void nop(const UDSPInstruction opc); - -DSPDisassembler::DSPDisassembler(const AssemblerSettings &settings) - : settings_(settings) -{ -} - -DSPDisassembler::~DSPDisassembler() -{ - // Some old code for logging unknown ops. - std::string filename = File::GetUserPath(D_DUMPDSP_IDX) + "UnkOps.txt"; - File::IOFile uo(filename, "w"); - if (!uo) - return; - - int count = 0; - for (const auto& entry : unk_opcodes) - { - if (entry.second > 0) - { - count++; - fprintf(uo.GetHandle(), "OP%04x\t%d", entry.first, entry.second); - for (int j = 15; j >= 0; j--) // print op bits - { - if ((j & 0x3) == 3) - fprintf(uo.GetHandle(), "\tb"); - fprintf(uo.GetHandle(), "%d", (entry.first >> j) & 0x1); - } - fprintf(uo.GetHandle(), "\n"); - } - } - fprintf(uo.GetHandle(), "Unknown opcodes count: %d\n", count); -} - -bool DSPDisassembler::Disassemble(int start_pc, const std::vector &code, int base_addr, std::string &text) -{ - const char *tmp1 = "tmp1.bin"; - - // First we have to dump the code to a bin file. - { - File::IOFile f(tmp1, "wb"); - f.WriteArray(&code[0], code.size()); - } - - // Run the two passes. - return DisFile(tmp1, base_addr, 1, text) && DisFile(tmp1, base_addr, 2, text); -} - -char *DSPDisassembler::DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char *strbuf) -{ - char *buf = strbuf; - for (int j = 0; j < opc.param_count; j++) - { - if (j > 0) - buf += sprintf(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) - sprintf(buf, "$%s", pdregname(val)); - else - sprintf(buf, "$%d", val); - break; - - case P_PRG: - if (settings_.decode_registers) - sprintf(buf, "@$%s", pdregname(val)); - else - sprintf(buf, "@$%d", val); - break; - - case P_VAL: - case P_ADDR_I: - case P_ADDR_D: - if (settings_.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 == 0x003f) // LSL, LSR, ASL, ASR - sprintf(buf, "#%d", (val & 0x20) ? (val | 0xFFFFFFC0) : val); // 6-bit sign extension - else - sprintf(buf, "#0x%02x", val); - } - else - { - sprintf(buf, "#0x%04x", val); - } - break; - - case P_MEM: - if (opc.params[j].size != 2) - val = (u16)(s16)(s8)val; - - if (settings_.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); - break; - } - - buf += strlen(buf); - } - - return strbuf; -} - -static void MakeLowerCase(char *ptr) -{ - int i = 0; - while (ptr[i]) - { - ptr[i] = tolower(ptr[i]); - i++; - } -} - -bool DSPDisassembler::DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest) -{ - char buffer[256]; - char *buf = buffer; - - // Start with 8 spaces, if there's no label. - buf[0] = ' '; - buf[1] = '\0'; - buf++; - - if ((*pc & 0x7fff) >= 0x1000) - { - ++pc; - dest.append("; outside memory"); - return false; - } - - const u32 op1 = binbuf[*pc & 0x0fff]; - - const DSPOPCTemplate *opc = NULL; - const DSPOPCTemplate *opc_ext = NULL; - - // find opcode - for (int j = 0; j < opcodes_size; j++) - { - u16 mask = opcodes[j].opcode_mask; - - if ((op1 & mask) == opcodes[j].opcode) - { - opc = &opcodes[j]; - break; - } - } - const DSPOPCTemplate fake_op = {"CW", 0x0000, 0x0000, nop, NULL, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}}, false, false, false, false, false}; - if (!opc) - opc = &fake_op; - - bool extended = false; - bool only7bitext = false; - - if (((opc->opcode >> 12) == 0x3) && (op1 & 0x007f)) { - extended = true; - only7bitext = true; - } - else if (((opc->opcode >> 12) > 0x3) && (op1 & 0x00ff)) - extended = true; - else - extended = false; - - if (extended) - { - // opcode has an extension - // find opcode - for (int j = 0; j < opcodes_ext_size; j++) - { - if (only7bitext) { - if (((op1 & 0x7f) & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode) - { - opc_ext = &opcodes_ext[j]; - break; - } - } - else { - if ((op1 & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode) - { - opc_ext = &opcodes_ext[j]; - break; - } - } - } - } - - // printing - - if (settings_.show_pc) - buf += sprintf(buf, "%04x ", *pc); - - u32 op2; - - // Size 2 - the op has a large immediate. - if (opc->size == 2) - { - op2 = binbuf[(*pc + 1) & 0x0fff]; - if (settings_.show_hex) - buf += sprintf(buf, "%04x %04x ", op1, op2); - } - else - { - op2 = 0; - if (settings_.show_hex) - buf += sprintf(buf, "%04x ", op1); - } - - char opname[20]; - strcpy(opname, opc->name); - if (settings_.lower_case_ops) - MakeLowerCase(opname); - char ext_buf[20]; - if (extended) - sprintf(ext_buf, "%s%c%s", opname, settings_.ext_separator, opc_ext->name); - else - sprintf(ext_buf, "%s", opname); - if (settings_.lower_case_ops) - MakeLowerCase(ext_buf); - - if (settings_.print_tabs) - buf += sprintf(buf, "%s\t", ext_buf); - else - buf += sprintf(buf, "%-12s", ext_buf); - - if (opc->param_count > 0) - DisParams(*opc, op1, op2, buf); - - buf += strlen(buf); - - // Handle opcode extension. - if (extended) - { - if (opc->param_count > 0) - buf += sprintf(buf, " "); - buf += sprintf(buf, ": "); - if (opc_ext->param_count > 0) - DisParams(*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) - *pc += opc_ext->size; - else - *pc += opc->size; - - if (pass == 2) - dest.append(buffer); - return true; -} - -bool DSPDisassembler::DisFile(const char* name, int base_addr, int pass, std::string &output) -{ - File::IOFile in(name, "rb"); - if (!in) - { - printf("gd_dis_file: No input\n"); - return false; - } - - const int size = ((int)in.GetSize() & ~1) / 2; - u16 *const binbuf = new u16[size]; - in.ReadArray(binbuf, size); - in.Close(); - - // Actually do the disassembly. - for (u16 pc = 0; pc < size;) - { - DisOpcode(binbuf, base_addr, pass, &pc, output); - if (pass == 2) - output.append("\n"); - } - delete[] binbuf; - return true; -} diff --git a/Source/Core/Core/DSP/disassemble.h b/Source/Core/Core/DSP/disassemble.h deleted file mode 100644 index 855ee9a..0000000 --- a/Source/Core/Core/DSP/disassemble.h +++ /dev/null @@ -1,84 +0,0 @@ -/*==================================================================== - - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - ====================================================================*/ - -#pragma once - -#include -#include - -#include "Common/Common.h" - -#include "Core/DSP/DSPTables.h" -#include "Core/DSP/LabelMap.h" - -struct AssemblerSettings -{ - AssemblerSettings() - : print_tabs(false), - show_hex(false), - show_pc(false), - force(false), - decode_names(true), - decode_registers(true), - ext_separator('\''), - lower_case_ops(true), - pc(0) - { - } - - bool print_tabs; - bool show_hex; - bool show_pc; - bool force; - bool decode_names; - bool decode_registers; - char ext_separator; - bool lower_case_ops; - - u16 pc; -}; - -class DSPDisassembler -{ -public: - DSPDisassembler(const AssemblerSettings &settings); - ~DSPDisassembler(); - - bool Disassemble(int start_pc, const std::vector &code, int base_addr, std::string &text); - - // Warning - this one is trickier to use right. - // Use pass == 2 if you're just using it by itself. - bool DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest); - -private: - // Moves PC forward and writes the result to dest. - bool DisFile(const char* name, int base_addr, int pass, std::string &output); - - char* DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char* strbuf); - std::map unk_opcodes; - - const AssemblerSettings settings_; - - LabelMap labels; -}; diff --git a/Source/Core/Core/HW/DSPLLE/DSPDebugInterface.cpp b/Source/Core/Core/HW/DSPLLE/DSPDebugInterface.cpp index 863daf7..4520ddf 100644 --- a/Source/Core/Core/HW/DSPLLE/DSPDebugInterface.cpp +++ b/Source/Core/Core/HW/DSPLLE/DSPDebugInterface.cpp @@ -2,8 +2,8 @@ // Licensed under GPLv2 // Refer to the license.txt file included. -#include "Core/DSP/disassemble.h" #include "Core/DSP/DSPCore.h" +#include "Core/DSP/DSPDisassembler.h" #include "Core/DSP/DSPMemoryMap.h" #include "Core/HW/DSPLLE/DSPDebugInterface.h" #include "Core/HW/DSPLLE/DSPSymbols.h" diff --git a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp index c0d4b11..ee41902 100644 --- a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp +++ b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp @@ -17,8 +17,8 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Host.h" -#include "Core/DSP/disassemble.h" #include "Core/DSP/DSPCore.h" +#include "Core/DSP/DSPDisassembler.h" #include "Core/DSP/DSPHost.h" #include "Core/DSP/DSPHWInterface.h" #include "Core/DSP/DSPInterpreter.h" diff --git a/Source/Core/Core/HW/DSPLLE/DSPLLETools.cpp b/Source/Core/Core/HW/DSPLLE/DSPLLETools.cpp index 1d9c0e8..3bf4a7e 100644 --- a/Source/Core/Core/HW/DSPLLE/DSPLLETools.cpp +++ b/Source/Core/Core/HW/DSPLLE/DSPLLETools.cpp @@ -12,9 +12,9 @@ #include "Common/Common.h" #include "Common/FileUtil.h" -#include "Core/DSP/disassemble.h" #include "Core/DSP/DSPCodeUtil.h" #include "Core/DSP/DSPCore.h" +#include "Core/DSP/DSPDisassembler.h" #include "Core/DSP/DSPInterpreter.h" #include "Core/HW/DSPLLE/DSPLLEGlobals.h" #include "Core/HW/DSPLLE/DSPLLETools.h" diff --git a/Source/Core/Core/HW/DSPLLE/DSPSymbols.cpp b/Source/Core/Core/HW/DSPLLE/DSPSymbols.cpp index 0933635..d4e5318 100644 --- a/Source/Core/Core/HW/DSPLLE/DSPSymbols.cpp +++ b/Source/Core/Core/HW/DSPLLE/DSPSymbols.cpp @@ -10,8 +10,8 @@ #include "Common/FileUtil.h" #include "Common/StringUtil.h" -#include "Core/DSP/disassemble.h" #include "Core/DSP/DSPCore.h" +#include "Core/DSP/DSPDisassembler.h" #include "Core/HW/DSPLLE/DSPSymbols.h" namespace DSPSymbols {