diff --git a/CMakeLists.txt b/CMakeLists.txt index 62aefa3d71..a8fecb0392 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1022,6 +1022,8 @@ if(WIN32) Windows/Debugger/Debugger_SymbolMap.h Windows/Debugger/Debugger_VFPUDlg.cpp Windows/Debugger/Debugger_VFPUDlg.h + Windows/Debugger/ExpressionParser.cpp + Windows/Debugger/ExpressionParser.h Windows/Debugger/SimpleELF.h # Windows/DlgDynaView.cpp # Windows/DlgDynaView.h diff --git a/Core/Debugger/DebugInterface.h b/Core/Debugger/DebugInterface.h index e3647b887d..3b857b3597 100644 --- a/Core/Debugger/DebugInterface.h +++ b/Core/Debugger/DebugInterface.h @@ -41,6 +41,7 @@ public: virtual int getColor(unsigned int address){return 0xFFFFFFFF;} virtual const char *getDescription(unsigned int address) {return "";} virtual const char *findSymbolForAddress(unsigned int address) { return NULL; }; + virtual bool getSymbolValue(char* symbol, u32& dest) { return false; }; virtual const char *GetName() = 0; virtual int GetGPRSize() = 0; //32 or 64 diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index 2fbae9c69d..930710b960 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -237,6 +237,20 @@ const char* SymbolMap::getDirectSymbol(u32 address) return NULL; } +bool SymbolMap::getSymbolValue(char* symbol, u32& dest) +{ + for (size_t i = 0, n = entries.size(); i < n; i++) + { + MapEntry &entry = entries[i]; + if (stricmp(entry.name,symbol) == 0) + { + dest = entries[i].address; + return true; + } + } + return false; +} + char descriptionTemp[256]; char *SymbolMap::GetDescription(unsigned int address) diff --git a/Core/Debugger/SymbolMap.h b/Core/Debugger/SymbolMap.h index 20d572c3e0..3e061bc018 100644 --- a/Core/Debugger/SymbolMap.h +++ b/Core/Debugger/SymbolMap.h @@ -59,6 +59,7 @@ public: unsigned int GetRunCount(int num); void SortSymbols(); const char* getDirectSymbol(u32 address); + bool getSymbolValue(char* symbol, u32& dest); void UseFuncSignaturesFile(const char *filename, u32 maxAddress); void CompileFuncSignaturesFile(const char *filename); diff --git a/Core/MIPS/MIPSDebugInterface.cpp b/Core/MIPS/MIPSDebugInterface.cpp index 3a64260f1e..035f851500 100644 --- a/Core/MIPS/MIPSDebugInterface.cpp +++ b/Core/MIPS/MIPSDebugInterface.cpp @@ -86,6 +86,12 @@ const char *MIPSDebugInterface::findSymbolForAddress(unsigned int address) return symbolMap.getDirectSymbol(address); } +bool MIPSDebugInterface::getSymbolValue(char* symbol, u32& dest) +{ + return symbolMap.getSymbolValue(symbol,dest); +} + + void MIPSDebugInterface::runToBreakpoint() { diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index 9dececca16..11b552d687 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -41,6 +41,7 @@ public: virtual int getColor(unsigned int address); virtual const char *getDescription(unsigned int address); virtual const char *findSymbolForAddress(unsigned int address); + virtual bool getSymbolValue(char* symbol, u32& dest); //overridden functions const char *GetName(); diff --git a/Windows/Debugger/CtrlRegisterList.cpp b/Windows/Debugger/CtrlRegisterList.cpp index 718cc53190..35d6d450b7 100644 --- a/Windows/Debugger/CtrlRegisterList.cpp +++ b/Windows/Debugger/CtrlRegisterList.cpp @@ -13,6 +13,7 @@ #include "../../globals.h" #include "Debugger_Disasm.h" +#include "ExpressionParser.h" #include "../main.h" @@ -360,10 +361,18 @@ void CtrlRegisterList::editRegisterValue() return; u32 val = cpu->GetRegValue(cat,reg); - if (InputBox_GetHex(GetModuleHandle(NULL),wnd,"Set new value",val,val)) + + char temp[256]; + sprintf(temp,"%08X",val); + if (InputBox_GetString(GetModuleHandle(NULL),wnd,"Set new value",temp,temp)) { - cpu->SetRegValue(cat,reg,val); - redraw(); + if (parseExpression(temp,cpu,val) == false) + { + displayExpressionError(wnd); + } else { + cpu->SetRegValue(cat,reg,val); + redraw(); + } } } diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index c453fb9e9b..880cdfc97e 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -10,6 +10,7 @@ #include "Debugger_MemoryDlg.h" #include "Debugger_Disasm.h" #include "Debugger_VFPUDlg.h" +#include "ExpressionParser.h" #include "../main.h" #include "CtrlRegisterList.h" @@ -66,6 +67,24 @@ static LRESULT CALLBACK BreakpointListProc(HWND hDlg, UINT message, WPARAM wPara +FAR WNDPROC DefGotoEditProc; + +LRESULT CALLBACK GotoEditProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_KEYUP: + if( wParam == VK_RETURN ) + { + SendMessage(GetParent(hDlg),WM_USER+6,0,0); + return 0; + } + default: + return (LRESULT)CallWindowProc((WNDPROC)DefGotoEditProc,hDlg,message,wParam,lParam); + }; + + return 0; +} CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Dialog((LPCSTR)IDD_DISASM, _hInstance, _hParent) @@ -124,7 +143,11 @@ CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Di ShowWindow(GetDlgItem(m_hDlg, IDC_REGLIST), SW_NORMAL); ShowWindow(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), SW_HIDE); SetTimer(m_hDlg,1,1000,0); - + + // subclass the goto edit box + HWND editWnd = GetDlgItem(m_hDlg,IDC_ADDRESS); + DefGotoEditProc = (WNDPROC)GetWindowLongPtr(editWnd,GWLP_WNDPROC); + SetWindowLongPtr(editWnd,GWLP_WNDPROC,(LONG_PTR)DefGotoEditProc); // subclass the breakpoint list HWND breakpointHwnd = GetDlgItem(m_hDlg, IDC_BREAKPOINTLIST); @@ -147,7 +170,6 @@ CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Di ListView_InsertColumn(breakpointHwnd, i, &lvc); } - // Actually resize the window to the proper size (after the above setup.) if (w != -1 && h != -1) { @@ -584,7 +606,8 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) } MemCheck check; - if (InputBox_GetHex(GetModuleHandle(NULL), m_hDlg, "JIT (and not HLE) only for now, no delete", 0, check.iStartAddress)) + + if (executeExpressionWindow(m_hDlg,cpu,check.iStartAddress)) { check.bBreak = true; check.bLog = true; @@ -603,19 +626,6 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) } } break; - - case IDC_ADDRESS: - { - if (HIWORD(wParam) == EN_CHANGE ) - { - char szBuffer[32]; - GetWindowText ((HWND)lParam, szBuffer, 32); - ptr->gotoAddr(parseHex(szBuffer)); - UpdateDialog(); - } - } - break; - case IDC_UPDATECALLSTACK: { HWND hDlg = m_hDlg; @@ -700,6 +710,22 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) case WM_USER+5: removeBreakpoint(wParam); break; + case WM_USER+6: // goto edit + { + char szBuffer[256]; + CtrlDisAsmView *ptr = CtrlDisAsmView::getFrom(GetDlgItem(m_hDlg,IDC_DISASMVIEW)); + GetWindowText(GetDlgItem(m_hDlg,IDC_ADDRESS),szBuffer,256); + + u32 addr; + if (parseExpression(szBuffer,cpu,addr) == false) + { + displayExpressionError(m_hDlg); + } else { + ptr->gotoAddr(addr); + } + UpdateDialog(); + } + break; case WM_SIZE: { UpdateSize(LOWORD(lParam), HIWORD(lParam)); diff --git a/Windows/Debugger/Debugger_MemoryDlg.cpp b/Windows/Debugger/Debugger_MemoryDlg.cpp index 60b59baa33..6ea1985578 100644 --- a/Windows/Debugger/Debugger_MemoryDlg.cpp +++ b/Windows/Debugger/Debugger_MemoryDlg.cpp @@ -8,6 +8,7 @@ #include "Debugger_MemoryDlg.h" #include "CtrlMemView.h" +#include "ExpressionParser.h" #include "../../Core/MIPS/MIPSDebugInterface.h" // BAD @@ -156,8 +157,13 @@ BOOL CMemoryDlg::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) char temp[256]; u32 addr; GetWindowText(GetDlgItem(m_hDlg,IDC_ADDRESS),temp,255); - sscanf(temp,"%08x",&addr); - mv->gotoAddr(addr); + + if (parseExpression(temp,cpu,addr) == false) + { + displayExpressionError(m_hDlg); + } else { + mv->gotoAddr(addr); + } break; } case WM_INITDIALOG: diff --git a/Windows/Debugger/ExpressionParser.cpp b/Windows/Debugger/ExpressionParser.cpp new file mode 100644 index 0000000000..f0eaa50fdb --- /dev/null +++ b/Windows/Debugger/ExpressionParser.cpp @@ -0,0 +1,474 @@ +#include "ExpressionParser.h" +#include "../../Core/MIPS/MIPSDebugInterface.h" +#include "../InputBox.h" + +typedef enum { + EXOP_BRACKETL, EXOP_BRACKETR, EXOP_SIGNPLUS, EXOP_SIGNMINUS, + EXOP_BITNOT, EXOP_LOGNOT, EXOP_MUL, EXOP_DIV, EXOP_MOD, EXOP_ADD, EXOP_SUB, + EXOP_SHL, EXOP_SHR, EXOP_GREATEREQUAL, EXOP_GREATER, EXOP_LOWEREQUAL, EXOP_LOWER, + EXOP_EQUAL, EXOP_NOTEQUAL, EXOP_BITAND, EXOP_XOR, EXOP_BITOR, EXOP_LOGAND, + EXOP_LOGOR, EXOP_TERTIF, EXOP_TERTELSE, EXOP_NUMBER, EXOP_NONE, EXOP_COUNT +} ExpressionOpcodeType; + +typedef enum { EXCOMM_CONST, EXCOMM_OP } ExpressionCommand; + +typedef std::pair ExpressionPair; + +static char expressionError[256]; + +typedef struct { + char Name[4]; + unsigned char Priority; + unsigned char len; + unsigned char args; + bool sign; +} ExpressionOpcode; + +const ExpressionOpcode ExpressionOpcodes[] = { + { "(", 15, 1, 0, false }, // EXOP_BRACKETL + { ")", 15, 1, 0, false }, // EXOP_BRACKETR + { "+", 12, 1, 1, true }, // EXOP_SIGNPLUS + { "-", 12, 1, 1, true }, // EXOP_SIGNMINUS + { "~", 12, 1, 1, false }, // EXOP_BITNOT + { "!", 12, 1, 1, false }, // EXOP_LOGNOT + { "*", 11, 1, 2, false }, // EXOP_MUL + { "/", 11, 1, 2, false }, // EXOP_DIV + { "%", 11, 1, 2, false }, // EXOP_MOD + { "+", 10, 1, 2, false }, // EXOP_ADD + { "-", 10, 1, 2, false }, // EXOP_SUB + { "<<", 9, 2, 2, false }, // EXOP_SHL + { ">>", 9, 2, 2, false }, // EXOP_SHR + { ">=", 8, 2, 2, false }, // EXOP_GREATEREQUAL + { ">", 8, 1, 2, false }, // EXOP_GREATER + { "<=", 8, 2, 2, false }, // EXOP_LOWEREQUAL + { "<", 8, 1, 2, false }, // EXOP_LOWER + { "==", 7, 2, 2, false }, // EXOP_EQUAL + { "!=", 7, 2, 2, false }, // EXOP_NOTEQUAL + { "&", 6, 1, 2, false }, // EXOP_BITAND + { "^", 5, 1, 2, false }, // EXOP_XOR + { "|", 4, 1, 2, false }, // EXOP_BITOR + { "&&", 3, 2, 2, false }, // EXOP_LOGAND + { "||", 2, 2, 2, false }, // EXOP_LOGOR + { "?", 0, 1, 0, false }, // EXOP_TERTIF + { ":", 1, 1, 3, false }, // EXOP_TERTELSE + { "", 0, 0, 0, false }, // EXOP_NUMBER + { "", 0, 0, 0, false } // EXOP_NONE +}; + +bool parseNumber(char* str, int defaultrad, int len, u32& result) +{ + int val = 0; + int r = 0; + if (len == 0) len = (int) strlen(str); + + if (str[0] == '0' && tolower(str[1]) == 'x') + { + r = 16; + str+=2; + len-=2; + } else if (str[0] == '$') + { + r = 16; + str++; + len--; + } else if (str[0] == '0' && tolower(str[1]) == 'o') + { + r = 8; + str+=2; + len-=2; + } else { + if (!(str[0] >= '0' && str[0] <= '9')) return false; + + if (tolower(str[len-1]) == 'b') + { + r = 2; + len--; + } else if (tolower(str[len-1]) == 'o') + { + r = 8; + len--; + } else if (tolower(str[len-1]) == 'h') + { + r = 16; + len--; + } else { + r = defaultrad; + } + } + + switch (r) + { + case 2: // bin + while (len--) + { + if (*str != '0' && *str != '1') return false; + val = val << 1; + if (*str++ == '1') + { + val++; + } + } + break; + case 8: // oct + while (len--) + { + if (*str < '0' || *str > '7') return false; + val = val << 3; + val+=(*str++-'0'); + } + break; + case 10: // dec + while (len--) + { + if (*str < '0' || *str > '9') return false; + val = val * 10; + val += (*str++ - '0'); + } + break; + case 16: // hex + while (len--) + { + char c = tolower(*str++); + if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) return false; + val = val << 4; + + if (c >= 'a') val += c-'a'+10; + else val += c-'0'; + } + break; + default: + return false; + } + + result = val; + return true; +} + +bool parseLabel(char* label, DebugInterface* debug, u32& dest) +{ + // check if it's a register first + for (int i = 0; i < 32; i++) + { + char reg[8]; + sprintf(reg,"r%d",i); + + if (stricmp(label,reg) == 0 + || stricmp(label,debug->GetRegName(0,i)) == 0) + { + dest = debug->GetRegValue(0,i); + return true; + } + } + + // now check labels + return debug->getSymbolValue(label,dest); +} + +ExpressionOpcodeType getExpressionOpcode(char* str, int& ReturnLen, ExpressionOpcodeType LastOpcode) +{ + int longestlen = 0; + ExpressionOpcodeType result = EXOP_NONE; + + for (int i = 0; i < EXOP_NUMBER; i++) + { + if (ExpressionOpcodes[i].sign == true && + (LastOpcode == EXOP_NUMBER || LastOpcode == EXOP_BRACKETR)) continue; + + int len = ExpressionOpcodes[i].len; + if (len > longestlen) + { + if (strncmp(ExpressionOpcodes[i].Name,str,len) == 0) + { + result = (ExpressionOpcodeType) i; + longestlen = len; + } + } + } + + ReturnLen = longestlen; + return result; +} + +bool isAlphaNum(char c) +{ + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + c == '@' || c == '_' || c == '$') + { + return true; + } else { + return false; + } +} + +bool parseExpression(char* infix, DebugInterface* cpu, u32& dest) +{ + expressionError[0] = 0; + + int infixPos = 0; + int infixLen = strlen(infix); + ExpressionOpcodeType lastOpcode = EXOP_NONE; + + std::vector postfixStack; + std::vector opcodeStack; + + while (infixPos < infixLen) + { + char first = tolower(infix[infixPos]); + char subStr[12]; + int subPos = 0; + + if (first == ' ' || first == '\t') + { + infixPos++; + continue; + } + + if (first >= '0' && first <= '9') + { + while (isAlphaNum(infix[infixPos])) + { + subStr[subPos++] = infix[infixPos++]; + } + subStr[subPos] = 0; + + u32 value; + if (parseNumber(subStr,16,subPos,value) == false) + { + sprintf(expressionError,"Invalid number \"%s\"",subStr); + return false; + } + + postfixStack.push_back(ExpressionPair(EXCOMM_CONST,value)); + lastOpcode = EXOP_NUMBER; + } else if ((first >= 'a' && first <= 'z') || first == '@') + { + while (isAlphaNum(infix[infixPos])) + { + subStr[subPos++] = infix[infixPos++]; + } + subStr[subPos] = 0; + + u32 value; + if (parseLabel(subStr,cpu,value) == false) + { + sprintf(expressionError,"Invalid label \"%s\"",subStr); + return false; + } + + postfixStack.push_back(ExpressionPair(EXCOMM_CONST,value)); + lastOpcode = EXOP_NUMBER; + } else { + int len; + ExpressionOpcodeType type = getExpressionOpcode(&infix[infixPos],len,lastOpcode); + if (type == EXOP_NONE) + { + sprintf(expressionError,"Invalid operator at \"%s\"",&infix[infixPos]); + return false; + } + + switch (type) + { + case EXOP_BRACKETL: + opcodeStack.push_back(type); + break; + case EXOP_BRACKETR: + while (true) + { + if (opcodeStack.empty()) + { + sprintf(expressionError,"Closing parenthesis without opening one"); + return false; + } + ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; + opcodeStack.pop_back(); + if (t == EXOP_BRACKETL) break; + postfixStack.push_back(ExpressionPair(EXCOMM_OP,t)); + } + break; + default: + if (opcodeStack.empty() == false) + { + int CurrentPriority = ExpressionOpcodes[type].Priority; + while (!opcodeStack.empty()) + { + ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; + opcodeStack.pop_back(); + + if (t == EXOP_BRACKETL) + { + opcodeStack.push_back(t); + break; + } + + if (ExpressionOpcodes[t].Priority >= CurrentPriority) + { + postfixStack.push_back(ExpressionPair(EXCOMM_OP,t)); + } else { + opcodeStack.push_back(t); + break; + } + } + } + opcodeStack.push_back(type); + break; + } + infixPos += len; + lastOpcode = type; + } + } + + while (!opcodeStack.empty()) + { + ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; + opcodeStack.pop_back(); + + if (t == EXOP_BRACKETL) // opening bracket without closing one + { + sprintf(expressionError,"Parenthesis not closed"); + return false; + } + postfixStack.push_back(ExpressionPair(EXCOMM_OP,t)); + } + + + // parse postfix now + int num = 0; + u32 opcode; + std::vector valueStack; + unsigned int arg[5]; + + while (num < postfixStack.size()) + { + switch (postfixStack[num].first) + { + case EXCOMM_CONST: // konstante zahl + valueStack.push_back(postfixStack[num++].second); + break; + case EXCOMM_OP: // opcode + opcode = postfixStack[num++].second; + if (valueStack.size() < ExpressionOpcodes[opcode].args) + { + sprintf(expressionError,"Not enough arguments"); + return false; + } + for (int l = 0; l < ExpressionOpcodes[opcode].args; l++) + { + arg[l] = valueStack[valueStack.size()-1]; + valueStack.pop_back(); + } + + switch (opcode) + { + case EXOP_SIGNPLUS: // keine aktion nötig + break; + case EXOP_SIGNMINUS: // -0 + valueStack.push_back(0-arg[0]); + break; + case EXOP_BITNOT: // ~b + valueStack.push_back(~arg[0]); + break; + case EXOP_LOGNOT: // !b + valueStack.push_back(!arg[0]); + break; + case EXOP_MUL: // a*b + valueStack.push_back(arg[1]*arg[0]); + break; + case EXOP_DIV: // a/b + valueStack.push_back(arg[1]/arg[0]); + break; + case EXOP_MOD: // a%b + valueStack.push_back(arg[1]%arg[0]); + break; + case EXOP_ADD: // a+b + valueStack.push_back(arg[1]+arg[0]); + break; + case EXOP_SUB: // a-b + valueStack.push_back(arg[1]-arg[0]); + break; + case EXOP_SHL: // a<>b + valueStack.push_back(arg[1]>>arg[0]); + break; + case EXOP_GREATEREQUAL: // a >= b + valueStack.push_back(arg[1]>=arg[0]); + break; + case EXOP_GREATER: // a > b + valueStack.push_back(arg[1]>arg[0]); + break; + case EXOP_LOWEREQUAL: // a <= b + valueStack.push_back(arg[1]<=arg[0]); + break; + case EXOP_LOWER: // a < b + valueStack.push_back(arg[1] + @@ -304,6 +305,7 @@ + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 1ca00ab3a8..2476f41936 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -101,6 +101,9 @@ Windows\Input + + Windows\Debugger + @@ -179,6 +182,9 @@ Windows\Input + + Windows\Debugger +