diff --git a/Images.qrc b/Images.qrc index 035a85d2..0ec72d62 100644 --- a/Images.qrc +++ b/Images.qrc @@ -19,5 +19,6 @@ images/stepinto.png images/stepover.png images/undo.png + images/debugPause.png diff --git a/SASM.pro b/SASM.pro index 5e8a3c4b..4133c9d9 100644 --- a/SASM.pro +++ b/SASM.pro @@ -49,7 +49,8 @@ SOURCES += main.cpp\ nasm.cpp \ gas.cpp \ common.cpp \ - fasm.cpp + fasm.cpp \ + signallocker.cpp HEADERS += mainwindow.h \ tab.h \ @@ -67,7 +68,8 @@ HEADERS += mainwindow.h \ nasm.h \ gas.h \ common.h \ - fasm.h + fasm.h \ + signallocker.h FORMS += settings.ui diff --git a/Windows/MinGW/bin/objdump.exe b/Windows/MinGW/bin/objdump.exe new file mode 100644 index 00000000..c30b9a0d Binary files /dev/null and b/Windows/MinGW/bin/objdump.exe differ diff --git a/Windows/MinGW64/bin/objdump.exe b/Windows/MinGW64/bin/objdump.exe new file mode 100644 index 00000000..01a91671 Binary files /dev/null and b/Windows/MinGW64/bin/objdump.exe differ diff --git a/assembler.h b/assembler.h index ea8030ea..4f4c4f2f 100644 --- a/assembler.h +++ b/assembler.h @@ -74,7 +74,7 @@ class Assembler : public QObject //Abstract class bool x86; explicit Assembler(bool x86, QObject *parent = 0); virtual QString getAssemblerPath() = 0; - virtual quint64 getMainOffset(QFile &lst) = 0; + virtual quint64 getMainOffset(QFile &lst, QString entryLabel) = 0; virtual void parseLstFile(QFile &lst, QVector &lines, bool ioIncIncluded, quint64 ioIncSize, quint64 offset) = 0; virtual void fillHighligherRules(QVector &highlightingRules, QList &formats, diff --git a/codeeditor.cpp b/codeeditor.cpp index 6a7412a5..d56bea96 100644 --- a/codeeditor.cpp +++ b/codeeditor.cpp @@ -42,7 +42,8 @@ CodeEditor::CodeEditor(QWidget *parent, bool withBeakpoints) : RuQPlainTextEdit(parent), debugImage(":/images/debugLine.png"), - breakpointImage(":/images/breakpoint.png") + breakpointImage(":/images/breakpoint.png"), + settings("SASM Project", "SASM") { hasBreakpoints = withBeakpoints; prevBlockCount = -1; @@ -105,7 +106,6 @@ void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) { //paint on line number area QPainter painter(lineNumberArea); - QSettings settings("SASM Project", "SASM"); painter.fillRect(event->rect(), settings.value("linenumberpanelcolor", palette().color(QPalette::Window)).value()); QTextBlock block = firstVisibleBlock(); @@ -113,10 +113,6 @@ void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); int bottom = top + (int) blockBoundingRect(block).height(); - //set debugAreaWidth before drawing - debugAreaWidth = 3 + debugImage.width() + 1; //left margin + arrow width + right margin - updateLineNumberAreaWidth(0); - while (block.isValid() && top <= event->rect().bottom()) { if (block.isVisible() && bottom >= event->rect().top()) { QString number = QString::number(blockNumber + 1); @@ -213,7 +209,6 @@ QList *CodeEditor::getBreakpoints() void CodeEditor::highlightCurrentLine() { - QSettings settings("SASM Project", "SASM"); if (!debugMode) { if (settings.value("currentlinemode", true).toBool()) { QList extraSelections; @@ -243,10 +238,9 @@ void CodeEditor::highlightDebugLine(int lineNumber) if (debugMode) { QList extraSelections; - if (!isReadOnly()) { + if (!isReadOnly() && lineNumber > 0) { QTextEdit::ExtraSelection selection; - QSettings settings("SASM Project", "SASM"); QColor lineColor = settings.value("debuglinecolor", QColor(235, 200, 40)).value(); selection.format.setBackground(lineColor); @@ -264,11 +258,16 @@ void CodeEditor::highlightDebugLine(int lineNumber) void CodeEditor::updateDebugLine(int number) { + //number > 0 => highlight line + //number == -1 => exit from debug mode + //number == -2 => does not highlight any line, but does not exit from debug mode + //last case for waiting program stops on next instruction or breakpoint if (number == -1) setDebugMode(false); else setDebugMode(true); - currentDebugLine = number; + if (number != -2) + currentDebugLine = number; //create rectangle of line number area and highlight debug line throw updateRequest() QRect lineNumberAreaRect(lineNumberArea->x(), lineNumberArea->y(), diff --git a/codeeditor.h b/codeeditor.h index 5ae3ea83..9c53eb38 100644 --- a/codeeditor.h +++ b/codeeditor.h @@ -99,6 +99,7 @@ private slots: int firstTopMargin; bool hasBreakpoints; int prevBlockCount; + QSettings settings; signals: void breakpointsChanged(quint64 lineNumber, bool isAdded); diff --git a/debugger.cpp b/debugger.cpp index ef3e8635..120c2031 100644 --- a/debugger.cpp +++ b/debugger.cpp @@ -67,23 +67,45 @@ Debugger::Debugger(QTextEdit *tEdit, const QString &path, bool ioInc, QString tm #endif #ifdef Q_OS_WIN32 QString gdb; + QString objdump; if (settings.value("mode", QString("x86")).toString() == "x86") { gdb = QCoreApplication::applicationDirPath() + "/MinGW/bin/gdb.exe"; + objdump = QCoreApplication::applicationDirPath() + "/MinGW/bin/objdump.exe"; if (! QFile::exists(gdb)) gdb = QCoreApplication::applicationDirPath() + "/Windows/MinGW/bin/gdb.exe"; + if (! QFile::exists(objdump)) + objdump = QCoreApplication::applicationDirPath() + "/Windows/MinGW/bin/objdump.exe"; exitMessage = "mingw_CRTStartup"; } else { gdb = QCoreApplication::applicationDirPath() + "/MinGW64/bin/gdb.exe"; + objdump = QCoreApplication::applicationDirPath() + "/MinGW/bin/objdump.exe"; if (! QFile::exists(gdb)) gdb = QCoreApplication::applicationDirPath() + "/Windows/MinGW64/bin/gdb.exe"; + if (! QFile::exists(objdump)) + objdump = QCoreApplication::applicationDirPath() + "/Windows/MinGW/bin/objdump.exe"; exitMessage = "__fu0__set_invalid_parameter_handler"; } #else QString gdb = "gdb"; + QString objdump = "objdump"; exitMessage = "libc_start_main"; #endif cExitMessage = QRegExp("\\[Inferior .* exited"); + connect(this, SIGNAL(wasStopped()), this, SLOT(emitStarted())); + + //determine entry point + QProcess objdumpProcess; + QStringList objdumpArguments; + objdumpArguments << "-f" << path; + objdumpProcess.start(objdump, objdumpArguments); + objdumpProcess.waitForFinished(); + QString objdumpResult = QString(objdumpProcess.readAllStandardOutput()); + QString startAddress("start address "); + int index = objdumpResult.indexOf(startAddress); + objdumpResult = objdumpResult.mid(index + startAddress.length()); + entryPoint = objdumpResult.toLongLong(0, 16); + QStringList arguments; arguments << path; @@ -98,6 +120,12 @@ Debugger::Debugger(QTextEdit *tEdit, const QString &path, bool ioInc, QString tm bufferTimer->start(10); } +void Debugger::emitStarted() +{ + disconnect(this, SIGNAL(wasStopped()), this, SLOT(emitStarted())); + emit started(); +} + void Debugger::readOutputToBuffer() { if (!process) @@ -112,15 +140,22 @@ void Debugger::processOutput() int index = buffer.indexOf(QString("(gdb)")); int linefeedIndex = errorBuffer.indexOf("\n"); if (index != -1) { //if whole message ready to processing (end of whole message is "(gdb)") - processMessage(buffer.left(index), errorBuffer.left(linefeedIndex)); + QString output = buffer.left(index); + QString error = errorBuffer.left(linefeedIndex); buffer.remove(0, index + 5); //remove processed message errorBuffer.remove(0, linefeedIndex + 1); + processMessage(output, error); } bufferTimer->start(10); } void Debugger::processMessage(QString output, QString error) { + if (error.indexOf("PC register is not available") != -1) { + emit printLog(tr("GDB error\n"), Qt::red); + emit finished(); + return; + } if (c == 0) { //in start wait for printing of start gdb text like this: /*GNU gdb (GDB) 7.4 Copyright (C) 2012 Free Software Foundation, Inc. @@ -134,47 +169,85 @@ void Debugger::processMessage(QString output, QString error) Reading symbols from C:\Users\Dmitri\Dropbox\Projects\SASMstatic\release\Program\SASMprog.exe... done. (gdb)*/ - - doInput(QString("disas main\n"), none); - c++; - return; + if (output.indexOf("no debugging symbols found") != -1) { + dbgSymbols = false; + c++; + } else { + dbgSymbols = true; + doInput(QString("disas main\n"), none); + c++; + return; + } } - if (c == 1 && output != " ") { - //next disassembly top part of code for setting according with program in memory and program in file - /*Dump of assembler code for function sasmStartL: - 0x00401390 <+0>: xor %eax,%eax - 0x00401392 <+2>: ret - 0x00401393 <+3>: add %dl,-0x77(%ebp) - End of assembler dump.*/ - //we need first number (0x00401390) + if (dbgSymbols) { //debug symbols exists + if (c == 1 && output != " ") { + //next disassembly top part of code for setting according with program in memory and program in file + /*Dump of assembler code for function sasmStartL: + 0x00401390 <+0>: xor %eax,%eax + 0x00401392 <+2>: ret + 0x00401393 <+3>: add %dl,-0x77(%ebp) + End of assembler dump.*/ + //we need first number (0x00401390) + + //count offset + QRegExp r = QRegExp("0x[0-9a-fA-F]{8,16}"); + int index = r.indexIn(output); + offset = output.mid(index, r.matchedLength()).toULongLong(0, 16); + //take offset in hexadecimal representation (10 symbols) from string and convert it to int + c++; + processLst(); //count accordance + run(); //perform Debugger::run(), that run program and open I/O files + return; + } - //count offset - QRegExp r = QRegExp("0x[0-9a-fA-F]{8,16}"); - int index = r.indexIn(output); - offset = output.mid(index, r.matchedLength()).toULongLong(0, 16); - //take offset in hexadecimal representation (10 symbols) from string and convert it to int - c++; - processLst(); //count accordance - run(); //perform Debugger::run(), that run program and open I/O files - return; + //determine run of program + //wait for message like this: Breakpoint 1, 0x00401390 in sasmStartL () + if (c == 2 && output.indexOf(QString(" in ")) != -1) { + c++; + actionTypeQueue.enqueue(ni); + } + + //if an error with the wrong name of the section has occurred + if ((c == 2 && output.indexOf(QString("Make breakpoint pending on future shared library load")) != -1) + || (c == 1 && output == " ")) { + actionTypeQueue.enqueue(anyAction); + processAction(tr("An error has occurred in the debugger. Please check the names of the sections.")); + QObject::disconnect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutputToBuffer())); + emit finished(); + } + } else { //debug symbols does not exists (for example, non-gcc linker) + if (c == 1) { + offset = 0x401000; //!!!!!!!!!!!!! - start of text or code section + c++; + processLst(); //count accordance + run(); //perform Debugger::run(), that run program and open I/O files + return; + } + + //determine run of program + //wait for message like this: Breakpoint 1, 0x00401390 in sasmStartL () + if (c == 2 && output.indexOf(QString(" in ")) != -1) { + c++; + actionTypeQueue.enqueue(ni); + } } - //determine run of program - //wait for message like this: Breakpoint 1, 0x00401390 in sasmStartL () - if (c == 2 && output.indexOf(QString(" in ")) != -1) { - c++; - actionTypeQueue.enqueue(ni); - emit started(); //emit start signal + QString sigTrap("SIGTRAP"); //was stopped + if (output.indexOf(sigTrap) != -1) { + stopped = true; + emit wasStopped(); + return; } - //if an error with the wrong name of the section has occurred - if ((c == 2 && output.indexOf(QString("Make breakpoint pending on future shared library load")) != -1) - || (c == 1 && output == " ")) { - actionTypeQueue.enqueue(anyAction); - processAction(tr("An error has occurred in the debugger. Please check the names of the sections.")); - QObject::disconnect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutputToBuffer())); - emit finished(); + QString s("New Thread"); + int index = output.indexOf(s); + if (index != -1) { + QString temp = output; + temp = temp.mid(index + s.length() + 1); + temp = temp.split(QChar('.')).at(0); + pid = temp.toULongLong(0, 10); + output.remove(QRegExp("\\[New Thread[^\\]]*\\]\r?\n")); } //process all actions after start @@ -218,187 +291,186 @@ void Debugger::processAction(QString output, QString error) emit finished(); return; } - if (actionTypeQueue.isEmpty()) { return; } DebugActionType actionType = actionTypeQueue.dequeue(); - int failIndex = output.indexOf(QString("Program received signal")); //program was completed incorrectly - if (failIndex == -1) { //if program works normally - if (actionType == breakpoint) - return; - - if (actionType == si || actionType == ni || actionType == showLine) { - //message is: line number + data - //print line number and other data if si or ni and print line number only if showLine - //scan line number in memory - QRegExp r = QRegExp("0x[0-9a-fA-F]{8,16}"); - int index = r.indexIn(output); + if (actionType == breakpoint) + return; + if (actionType == si || actionType == ni || actionType == showLine) { + //message is: line number + data + //print line number and other data if si or ni and print line number only if showLine + //scan line number in memory + QRegExp r = QRegExp("0x[0-9a-fA-F]{8,16}"); + int index = r.indexIn(output); - //print output - if (index > 1 && !firstAction) { - QString msg = output.left(index); //left part - probably ouput of program; - msg.remove(0, 1); //remove first whitespace - QRegExp continuingMsg("Continuing.\r?\n"); - QRegExp breakpointMsg("\r?\nBreakpoint \\d+, "); - msg.remove(continuingMsg); - msg.remove(breakpointMsg); - emit printOutput(msg); - } - firstAction = false; - - quint64 lineNumber = output.mid(index, r.matchedLength()).toULongLong(0, 16); - //take line number in hexadecimal representation - //(10 symbols) in memory from string and convert it to int - - //find line number in accordance array and get number line in file with code - bool found = false; - for (int i = lines.count() - 1; i >= 0; i--) { - if (lineNumber == lines[i].numInMem) { - lineNumber = lines[i].numInCode; - found = true; - break; - } + //print output + if (index > 1 && !firstAction) { + QString msg = output.left(index); //left part - probably output of program; + msg.remove(0, 1); //remove first whitespace + QRegExp continuingMsg("Continuing.\r?\n"); + QRegExp breakpointMsg("\r?\nBreakpoint \\d+, "); + QRegExp threadMsg("\\[Switching to Thread [^\\]]*\\]\r?\n"); + msg.remove(continuingMsg); + msg.remove(breakpointMsg); + msg.remove(threadMsg); + emit printOutput(msg); + } + firstAction = false; + + quint64 lineNumber = output.mid(index, r.matchedLength()).toULongLong(0, 16); + //take line number in hexadecimal representation + //(10 symbols) in memory from string and convert it to int + + //find line number in accordance array and get number line in file with code + bool found = false; + for (int i = lines.count() - 1; i >= 0; i--) { + if (lineNumber == lines[i].numInMem) { + lineNumber = lines[i].numInCode; + found = true; + break; } + } - if (!found) { - //output = tr("Inside the macro or outside the program.") + '\n'; - emit inMacro(); + if (!found) { + //output = tr("Inside the macro or outside the program.") + '\n'; + emit inMacro(); + return; + } else { //if found highlight and print it + //highlight line number + emit highlightLine(lineNumber); + stopped = true; + emit wasStopped(); + + //print string number and all after it + //output = QString::number(lineNumber) + tr(" line") + output.mid(output.indexOf("()") + 2); + if (actionType == showLine) return; - } else { //if found highlight and print it - //highlight line number - emit highlightLine(lineNumber); - - //print string number and all after it - //output = QString::number(lineNumber) + tr(" line") + output.mid(output.indexOf("()") + 2); - if (actionType == showLine) - return; - output.remove(0, output.indexOf("()") + 2); - } + output.remove(0, output.indexOf("()") + 2); } + } - if (actionType == anyAction) { - if (output[output.length() - 1] != '\n') - output += QChar('\n'); - //process as ni or si - if (output.indexOf(QRegExp("0x[0-9a-fA-F]{8,16} in ")) != -1 - && !backtrace) { - actionTypeQueue.enqueue(showLine); - processAction(output); - } - if (!error.isEmpty()) { - if (output != " \n") - output = error + output; - else - output = error; - } + if (actionType == anyAction) { + if (output[output.length() - 1] != '\n') + output += QChar('\n'); + //process as ni or si + if (output.indexOf(QRegExp("0x[0-9a-fA-F]{8,16} in ")) != -1 + && !backtrace) { + actionTypeQueue.enqueue(showLine); + processAction(output); + } + if (!error.isEmpty()) { + if (output != " \n") + output = error + output; + else + output = error; } + } - if (actionType == infoMemory) { - bool isValid = false; - if (output.indexOf(QString("No symbol")) == -1 && - output.indexOf(QString("no debug info")) == -1 && output != QString(" ")) { - //if variable exists (isValid = true) - isValid = true; - int index = output.indexOf(QRegExp("\\$\\d+ = .*")); - if (index == -1) - isValid = false; - else { - output = output.right(output.length() - index); - output = output.right(output.length() - output.indexOf(QChar('=')) - 1); - for (int i = output.size() - 1; i >= 0; i--) { - if (output[i].isSpace()) - output.remove(i, 1); - } + if (actionType == infoMemory) { + bool isValid = false; + if (output.indexOf(QString("No symbol")) == -1 && + output.indexOf(QString("no debug info")) == -1 && output != QString(" ")) { + //if variable exists (isValid = true) + isValid = true; + int index = output.indexOf(QRegExp("\\$\\d+ = .*")); + if (index == -1) + isValid = false; + else { + output = output.right(output.length() - index); + output = output.right(output.length() - output.indexOf(QChar('=')) - 1); + for (int i = output.size() - 1; i >= 0; i--) { + if (output[i].isSpace()) + output.remove(i, 1); } } - memoryInfo info; - if (isValid) - info.value = output; - info.isValid = isValid; - watches.append(info); - if (watchesCount == watches.size()) { - emit printMemory(watches); - watches.clear(); - } - return; } + memoryInfo info; + if (isValid) + info.value = output; + info.isValid = isValid; + watches.append(info); + if (watchesCount == watches.size()) { + emit printMemory(watches); + watches.clear(); + } + return; + } - if (actionType == infoRegisters) { - QTextStream registersStream(&output); - QList registers; - registersInfo info; - QSettings settings("SASM Project", "SASM"); - if (settings.value("mode", QString("x86")).toString() == "x86") { - for (int i = 0; i < 16; i++) { - if (i == 9) { - registersStream >> info.name >> info.hexValue; - registersStream.skipWhiteSpace(); - info.decValue = registersStream.readLine(); - } else if (i == 8) { - registersStream >> info.name >> info.hexValue; - registersStream.skipWhiteSpace(); - char c; - registersStream >> c; - while (c != ' ') - registersStream >> c; - info.decValue = registersStream.readLine(); - } else { - registersStream >> info.name >> info.hexValue >> info.decValue; - } - registers.append(info); - if (i == 0 && info.name != "eax" && registersOk) { - doInput(QString("info registers\n"), infoRegisters); - registersOk = false; - return; - } + if (actionType == infoRegisters) { + QTextStream registersStream(&output); + QList registers; + registersInfo info; + QSettings settings("SASM Project", "SASM"); + if (settings.value("mode", QString("x86")).toString() == "x86") { + for (int i = 0; i < 16; i++) { + if (i == 8 || i == 9) { + registersStream >> info.name >> info.hexValue; + registersStream.skipWhiteSpace(); + info.decValue = registersStream.readLine(); + } else { + registersStream >> info.name >> info.hexValue >> info.decValue; + } + registers.append(info); + if (i == 0 && info.name != "eax" && registersOk) { + doInput(QString("info registers\n"), infoRegisters); + registersOk = false; + return; } - } else { //x64 - for (int i = 0; i < 24; i++) { - if (i == 17) { - registersStream >> info.name >> info.hexValue; - registersStream.skipWhiteSpace(); - info.decValue = registersStream.readLine(); - } else if (i == 16) { - registersStream >> info.name >> info.hexValue; - registersStream.skipWhiteSpace(); - char c; + } + } else { //x64 + for (int i = 0; i < 24; i++) { + if (i == 17) { + registersStream >> info.name >> info.hexValue; + registersStream.skipWhiteSpace(); + info.decValue = registersStream.readLine(); + } else if (i == 16) { + registersStream >> info.name >> info.hexValue; + registersStream.skipWhiteSpace(); + char c; + registersStream >> c; + while (c != ' ') registersStream >> c; - while (c != ' ') - registersStream >> c; - info.decValue = registersStream.readLine(); - } else { - registersStream >> info.name >> info.hexValue >> info.decValue; - } - registers.append(info); - if (i == 0 && info.name != "rax" && registersOk) { - doInput(QString("info registers\n"), infoRegisters); - registersOk = false; - return; - } + info.decValue = registersStream.readLine(); + } else { + registersStream >> info.name >> info.hexValue >> info.decValue; + } + registers.append(info); + if (i == 0 && info.name != "rax" && registersOk) { + doInput(QString("info registers\n"), infoRegisters); + registersOk = false; + return; } } - emit printRegisters(registers); - return; } - - if (output == QString("\r\n") || output == QString("\n") || - output == QString("\r\n\n") || output == QString("\n\n")) //if empty - return; - } else { //if program fail - output = output.mid(failIndex); + emit printRegisters(registers); + return; } + if (output == QString("\r\n") || output == QString("\n") || + output == QString("\r\n\n") || output == QString("\n\n")) //if empty + return; + //print information to log field emit printLog(output); +} - if (failIndex != -1) { - QObject::disconnect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutputToBuffer())); - emit finished(); - return; - } +bool Debugger::isStopped() +{ + return stopped; +} + +void Debugger::pause() +{ + #ifdef Q_OS_WIN32 //!!!!!!!!!!!!!! - now Windows only + HANDLE proc; + proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD) pid); + DebugBreakProcess(proc); + CloseHandle(proc); + actionTypeQueue.clear(); + #endif } void Debugger::doInput(QString command, DebugActionType actionType) @@ -406,6 +478,10 @@ void Debugger::doInput(QString command, DebugActionType actionType) if (actionType != none) actionTypeQueue.enqueue(actionType); //put \n after commands! + if (actionType == ni || actionType == si || actionType == showLine) { + emit highlightLine(-2); //does not turn off debug mode + stopped = false; + } if (process) process->write(command.toLatin1()); } @@ -421,7 +497,13 @@ void Debugger::processLst() QFile lst; lst.setFileName(tmpPath + "program.lst"); if (lst.open(QIODevice::ReadOnly)) { - offset -= assembler->getMainOffset(lst); + //heuristic: + //if debug symbols exists - offset is difference between main labels in listing and in executable + //otherwise - offset is difference between beginning of text sections in listing and in executable + if (dbgSymbols) + offset -= assembler->getMainOffset(lst, "main"); + else + offset -= assembler->getMainOffset(lst, "start"); lst.close(); lst.open(QIODevice::ReadOnly); assembler->parseLstFile(lst, lines, ioIncIncluded, ioIncSize, offset); @@ -434,7 +516,11 @@ void Debugger::run() //set breakpoint on main, run program amd open output and input files //put \n after commands! //b main and run before others! - doInput(QString("b main\n"), none); + if (dbgSymbols) + doInput(QString("b main\n"), none); + else { + doInput("b *0x" + QString::number(entryPoint, 16) + "\n", none); + } doInput(QString("cd " + tmpPath + "\n"), none); doInput(QString("run\n"), none); doInput(QString("p dup2(open(\"input.txt\",0),0)\n"), none); @@ -442,6 +528,12 @@ void Debugger::run() void Debugger::changeBreakpoint(quint64 lineNumber, bool isAdded) { + if (!isStopped()) { + QEventLoop eventLoop; + connect(this, SIGNAL(wasStopped()), &eventLoop, SLOT(quit())); + pause(); + eventLoop.exec(); + } quint64 numInMem = 0; LineNum brkpoint; for (int i = 0; i < lines.count(); i++) //find address of line diff --git a/debugger.h b/debugger.h index 9d5ee492..c21ab71b 100644 --- a/debugger.h +++ b/debugger.h @@ -54,6 +54,12 @@ #include #include "assembler.h" +#ifdef Q_OS_WIN32 + #include + #include + #include +#endif + enum DebugActionType {ni, si, showLine, infoRegisters, infoMemory, anyAction, none, breakpoint}; class Debugger : public QObject @@ -78,6 +84,9 @@ class Debugger : public QObject typedef Assembler::LineNum LineNum; + bool isStopped(); + void pause(); + private: void processLst(); void run(); @@ -109,6 +118,12 @@ class Debugger : public QObject Assembler *assembler; + bool stopped; + quint64 pid; + bool dbgSymbols; + + quint64 entryPoint; + public slots: void readOutputToBuffer(); void processOutput(); @@ -116,6 +131,7 @@ public slots: void processAction(QString output, QString error = QString()); void doInput(QString command, DebugActionType actionType); void changeBreakpoint(quint64 lineNumber, bool isAdded); + void emitStarted(); signals: void highlightLine(int); //highlight current debug line @@ -126,6 +142,7 @@ public slots: void printLog(QString msg, QColor color = QColor(Qt::black)); void printOutput(QString msg); void inMacro(); + void wasStopped(); }; #endif // DEBUGGER_H diff --git a/fasm.cpp b/fasm.cpp index 904c10a8..19f9ce3a 100644 --- a/fasm.cpp +++ b/fasm.cpp @@ -61,6 +61,7 @@ QString FASM::getListingFilePath(QFile &lstOut) QProcess getLst; QStringList getLstArguments; getLstArguments << lstOut.fileName() << listingPath; + getLst.setWorkingDirectory(Common::applicationDataPath() + "/include"); #ifdef Q_OS_WIN32 getLst.start(Common::applicationDataPath() + "/FASM/listing.exe", getLstArguments); #else @@ -70,12 +71,16 @@ QString FASM::getListingFilePath(QFile &lstOut) return listingPath; } -quint64 FASM::getMainOffset(QFile &lstOut) { +quint64 FASM::getMainOffset(QFile &lstOut, QString entryLabel) +{ QFile lst(getListingFilePath(lstOut)); lst.open(QFile::ReadOnly); - QTextStream lstStream(&lst); - QRegExp mainLabel("main:"); + QRegExp mainLabel; + if (entryLabel == "start") + mainLabel = QRegExp("\\.text"); + else + mainLabel = QRegExp(entryLabel + ":"); bool flag = false; while (!lstStream.atEnd()) { QString line = lstStream.readLine(); @@ -99,31 +104,48 @@ void FASM::parseLstFile(QFile &lstOut, QVector &lines, bool, QFile lst(getListingFilePath(lstOut)); lst.open(QFile::ReadOnly); - bool inTextSection = false; - QRegExp sectionTextRegExp("\\.text"); - QRegExp sectionBssRegExp("\\.bss"); - QRegExp sectionDataRegExp("\\.data"); QTextStream lstStream(&lst); quint64 currentLine = 0; + QList > instrList; while (!lstStream.atEnd()) { QString line = lstStream.readLine(); currentLine++; - if (line.indexOf(sectionTextRegExp) != -1) { - inTextSection = true; - } else if (line.indexOf(sectionDataRegExp) != -1 || line.indexOf(sectionBssRegExp) != -1) { - inTextSection = false; - } - if (inTextSection) { - line = line.split(QChar(':')).at(0); - if (line.length() > 16) - continue; + int index = line.indexOf(QChar(':')); + if (index == -1 || index > 16) + continue; + QString first = line.left(index); + QString second = line.mid(index + 1); + QStringList l = line.split(QChar(':')); + int k = 0; + while (k < second.length() && second[k].isSpace()) + k++; + second.remove(0, k); + instrList.append(QPair(first.toULongLong(0, 16) + offset, second)); + } + lst.close(); + QFile programFile(Common::pathInTemp("program.asm")); + programFile.open(QFile::ReadOnly); + QTextStream programStream(&programFile); + int i = 0; //offset in list + int numInCode = 0; + while (!programStream.atEnd()) { + QString line = programStream.readLine(); + numInCode++; + int k = 0; + while (k < line.length() && line[k].isSpace()) + k++; + line.remove(0, k); + if (line == instrList[i].second) { LineNum l; - l.numInCode = currentLine; - l.numInMem = line.toULongLong(0, 16) + offset; + l.numInCode = numInCode; + l.numInMem = instrList[i].first; lines.append(l); + i++; + if (i >= instrList.size()) + break; } } - lst.close(); + programFile.close(); } QString FASM::getStartText() diff --git a/fasm.h b/fasm.h index 0b0e70a6..bb71b899 100644 --- a/fasm.h +++ b/fasm.h @@ -42,6 +42,7 @@ #define FASM_H #include +#include #include "assembler.h" class FASM : public Assembler @@ -50,7 +51,7 @@ class FASM : public Assembler public: explicit FASM(bool x86, QObject *parent = 0); QString getAssemblerPath(); - quint64 getMainOffset(QFile &lstOut); + quint64 getMainOffset(QFile &lstOut, QString entryLabel); void parseLstFile(QFile &lstOut, QVector &lines, bool, quint64, quint64 offset); void fillHighligherRules(QVector &highlightingRules, QList &formats, diff --git a/gas.cpp b/gas.cpp index ed421526..38e28c48 100644 --- a/gas.cpp +++ b/gas.cpp @@ -57,9 +57,12 @@ QString GAS::getAssemblerPath() #endif } -quint64 GAS::getMainOffset(QFile &lst) { +quint64 GAS::getMainOffset(QFile &lst, QString entryLabel) +{ + if (entryLabel == "start") + return 0; QTextStream lstStream(&lst); - QRegExp mainLabel("main:"); + QRegExp mainLabel(entryLabel + ":"); bool flag = false; while (!lstStream.atEnd()) { QString line = lstStream.readLine(); @@ -69,7 +72,8 @@ quint64 GAS::getMainOffset(QFile &lst) { //omit this string continue; } - char *s = line.toLocal8Bit().data(); + QByteArray lineArr = line.toLocal8Bit(); + const char *s = lineArr.constData(); quint64 a, b, c; if (sscanf(s, "%llu %llx %llx", &a, &b, &c) == 3) { if (!(b == 0 && c == 0)) { //exclude 0 0 @@ -104,7 +108,8 @@ void GAS::parseLstFile(QFile &lst, QVector &lines, bool ioIn continue; } if (inTextSection) { - char *s = line.toLocal8Bit().data(); + QByteArray lineArr = line.toLocal8Bit(); + const char *s = lineArr.constData(); quint64 a, b, c; if (sscanf(s, "%llu %llx %llx", &a, &b, &c) == 3){ if (!(b == 0 && c == 0)) { //exclude 0 0 diff --git a/gas.h b/gas.h index ca432b4b..f8dc59d2 100644 --- a/gas.h +++ b/gas.h @@ -49,7 +49,7 @@ class GAS : public Assembler public: explicit GAS(bool x86, QObject *parent = 0); QString getAssemblerPath(); - quint64 getMainOffset(QFile &lst); + quint64 getMainOffset(QFile &lst, QString entryLabel); void parseLstFile(QFile &lst, QVector &lines, bool ioIncIncluded, quint64 ioIncSize, quint64 offset); void fillHighligherRules(QVector &highlightingRules, QList &formats, diff --git a/images/debugPause.png b/images/debugPause.png new file mode 100644 index 00000000..7b74a586 Binary files /dev/null and b/images/debugPause.png differ diff --git a/language_ru.qm b/language_ru.qm index 9d08ec3b..552a8d76 100644 Binary files a/language_ru.qm and b/language_ru.qm differ diff --git a/language_ru.ts b/language_ru.ts index 2106ea0a..2aac7e93 100644 --- a/language_ru.ts +++ b/language_ru.ts @@ -1,6 +1,6 @@ - + CommandDebugWindow @@ -105,7 +105,14 @@ Debugger - + + GDB error + + Ошибка GDB + + + + An error has occurred in the debugger. Please check the names of the sections. Во время отладки произошла ошибка. Проверьте названия секций. @@ -204,145 +211,156 @@ - + File Файл - + Edit Правка - + Build Построение - - + + + Debug Отладка - - + + Settings Настройки - - - + + + Help Помощь - - + + New Новый - + Open Открыть - + Close file Закрыть файл - + Save Сохранить - + Save as Сохранить как - + Save .exe Сохранить .exe - + Exit Выход - + Find and replace Поиск и замена - + Undo Отменить - + Redo Повторить - + Cut Вырезать - + Copy Копировать - + Paste Вставить - + Delete Удалить - + Select all Выделить всё - + Comment Закомментировать - + Remove comment Раскомментировать - + Indent Отступ - + Remove indent Убрать отступ - + Build this Построить - + + Error! Program directory does not exist. Please reinstall the program. + Ошибка! Каталога программы не существует. Пожалуйста, переустановите программу. + + + + Pause + Приостановить + + + SASM (SimpleASM) 2.3 - simple Open Source IDE for NASM. SASM (SimpleASM) 2.3 - простая Open Source среда разработки на языке ассемблера NASM. @@ -351,7 +369,7 @@ SASM (SimpleASM) 2.2 - простая Open Source среда разработки на языке ассемблера NASM. - + Yandex.Money - 410012181834380 Яндекс.Деньги - 410012181834380 @@ -360,47 +378,49 @@ Запустить - + Run in new window Запустить в отдельном окне - + Stop Остановить - + + + Continue Продолжить - + Step into Шаг с заходом - + Step over Шаг без захода - + Build and run Построить и запустить - + Toggle breakpoint Точка останова - + Show registers Показать регистры - + Show memory Показать память @@ -409,18 +429,18 @@ Программа - + Execution files (*.exe);;All files (*.*) Исполняемые файлы (*.exe);;Все файлы (*.*) - + The program finished normally. Execution time: %1 s The program finished normally. Execution time: %1 ms Программа выполнена успешно. Время выполнения: %1 с - + The program crashed! Execution time: %1 s The program crashed! Execution time: %1 ms Программа завершена аварийно. Время выполнения: %1 с @@ -438,30 +458,30 @@ Команда gdb - + About О программе - + Open file Открыть файл - - + + Assembler source files (*.asm);;All files (*.*) Assembler source file (*.asm) All files (*.*) Исходные коды ассемблера (*.asm);;Все файлы (*.*) - + Save file Сохранить файл - + Save .exe file Сохранить .exe @@ -472,52 +492,51 @@ All files (*.*) Все файлы (*.*) - - + + Yes Да - + No Нет - - + + Cancel Отмена - - + + Save changes? Сохранить изменения? - + Build started... Build started... Построение начато... - Error! Directory NASM does not exist. Please reinstall the program. - Ошибка! Каталога NASM не существует. Пожалуйста, переустановите программу. + Ошибка! Каталога NASM не существует. Пожалуйста, переустановите программу. - + Directory NASM does not exist. Please reinstall the program. Каталога NASM не существует. Пожалуйста, переустановите программу. - + The program is already running. Программа уже запущена. - + The program is executing... Программа выполняется... @@ -531,7 +550,7 @@ All files (*.*) Программа завершена аварийно! - + Development and idea - Dmitriy Manushin Идея и разработка - Дмитрий Манушин @@ -548,7 +567,7 @@ All files (*.*) Integer - + Registers Регистры @@ -561,7 +580,7 @@ All files (*.*) Ошибка! Каталога NASM не существует. Пожалуйста, переустановите программу. - + Error! Ошибка! @@ -570,14 +589,14 @@ All files (*.*) Каталога NASM не существует. Пожалуйста, переустановите программу. - + Warning! Errors have occurred in the build: Warning! Errors have occurred in the build: Внимание! В ходе построения обнаружены ошибки: - + Warning! Внимание! @@ -586,7 +605,7 @@ All files (*.*) При построении программы обнаружены ошибки! - + Built successfully. Built successfully. @@ -605,55 +624,55 @@ All files (*.*) Программа выполнена. - + The program stopped. The program stopped. Программа остановлена. - + The program is not running. Программа не была запущена. - + Before debugging you need to build the program. Before debugging you need to build the program. Перед отладкой следует построить программу. - + Debugging started... Debugging started... Отладка началась... - + Memory Память - + Debugging finished. Debugging finished. Отладка завершена. - + In the previous session was not open any of the saved tabs! В предыдущей сессии не было открыто ни одной сохранённой вкладки! - + All settings will be deleted and all unsaved data will be lost. Are you sure? Все настройки будут удалены, а несохранённые данные потеряны! Вы уверены? - + About SASM О программе @@ -662,17 +681,17 @@ All files (*.*) SASM (SimpleASM) 2.0 Beta - простая Open Source среда разработки на языке ассемблера NASM. - + Licensed under the GNU GPL v3.0 Распространяется по лицензии GNU GPL v3.0 - + Based on the Qt. Основана на Qt. - + Copyright © 2013 Dmitriy Manushin Copyright © 2013 Dmitriy Manushin Copyright © 2013 Дмитрий Манушин @@ -682,12 +701,12 @@ All files (*.*) Разработка - Dmitriy Manushin - + Icon and advices - Alick Gaybullaev Иконка и советы - Алик Гайбуллаев - + Wishes and error messages are sent to the e-mail: Dman1095@gmail.com Wishes and error messages are sent to the e-mail: Dman1095@gmail.com @@ -695,27 +714,27 @@ All files (*.*) Пожелания и сообщения об ошибках отправляйте на адрес Dman1095@gmail.com - + More information on the site: http://dman95.github.io/SASM/ Сайт программы: http://dman95.github.io/SASM/ - + Donate: Поддержите проект: - + PayPal - Dman1095@gmail.com PayPal - Dman1095@gmail.com - + WMZ - Z282016332582 WMZ - Z282016332582 - + WMR - R331674303467 WMR - R331674303467 @@ -1064,6 +1083,16 @@ WMR - R331674303467 Linking options: Опции компоновщика: + + + Assembler path: + Ассемблер + + + + Linker path: + Компоновщик + Enable highlighting diff --git a/mainwindow.cpp b/mainwindow.cpp index 1112ac4c..d5094b83 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -213,7 +213,6 @@ void MainWindow::createMenus() debugMenu = menuBar()->addMenu(tr("Debug")); debugMenu->addAction(debugAction); debugMenu->addSeparator(); - debugMenu->addAction(debugContinueAction); debugMenu->addAction(debugNextNiAction); debugMenu->addAction(debugNextAction); debugMenu->addSeparator(); @@ -402,16 +401,9 @@ void MainWindow::createActions() if (key == "default") key = stdKey.toString(); debugAction->setShortcut(key); + debugKey = key; connect(debugAction, SIGNAL(triggered()), this, SLOT(debug())); - debugContinueAction = new QAction(QIcon(":/images/continue.png"), tr("Continue"), this); - key = keySettings.value("continue", "default").toString(); - stdKey = QKeySequence(QString("F5")); - if (key == "default") - key = stdKey.toString(); - debugContinueAction->setShortcut(key); - connect(debugContinueAction, SIGNAL(triggered()), this, SLOT(debugContinue())); - debugNextNiAction = new QAction(QIcon(":/images/stepover.png"), tr("Step over"), this); key = keySettings.value("stepOver", "default").toString(); stdKey = QKeySequence(QString("F10")); @@ -500,7 +492,6 @@ void MainWindow::createToolBars() debugToolBar = addToolBar(tr("Debug")); debugToolBar->addAction(debugAction); - debugToolBar->addAction(debugContinueAction); debugToolBar->addAction(debugNextNiAction); debugToolBar->addAction(debugNextAction); debugToolBar->addAction(stopAction); @@ -816,6 +807,8 @@ void MainWindow::buildProgram(bool debugMode) //assembler QString assemblerPath = assembler->getAssemblerPath(); + if (settings.contains("assemblerpath")) + assemblerPath = settings.value("assemblerpath").toString(); #ifdef Q_OS_WIN32 QString assemblerOptions = "-g -f win32 $SOURCE$ -l $LSTOUTPUT$ -o $PROGRAM.OBJ$"; #else @@ -838,23 +831,22 @@ void MainWindow::buildProgram(bool debugMode) assemblerProcess.waitForFinished(); //GCC - QString gccOptions = "$PROGRAM.OBJ$ $MACRO.OBJ$ -g -o $PROGRAM$ -m32"; + QString linkerOptions = "$PROGRAM.OBJ$ $MACRO.OBJ$ -g -o $PROGRAM$ -m32"; if (settings.contains("linkingoptions")) - gccOptions = settings.value("linkingoptions").toString(); + linkerOptions = settings.value("linkingoptions").toString(); //macro.c compilation/copying QFile macro; #ifdef Q_OS_WIN32 - QString gcc; + QString linker = settings.value("linkerpath", applicationDataPath() + "/MinGW/bin/gcc.exe").toString(); if (settings.value("mode", QString("x86")).toString() == "x86") { macro.setFileName(applicationDataPath() + "/NASM/macro.o"); - gcc = applicationDataPath() + "/MinGW/bin/gcc.exe"; } else { macro.setFileName(applicationDataPath() + "/NASM/macro64.o"); - gcc = applicationDataPath() + "/MinGW64/bin/gcc.exe"; } macro.copy(stdioMacros); #else QString gcc = "gcc"; + QString linker = settings.value("linkerpath", "gcc").toString(); macro.setFileName(applicationDataPath() + "/NASM/macro.c"); macro.copy(Common::pathInTemp("macro.c")); @@ -870,18 +862,18 @@ void MainWindow::buildProgram(bool debugMode) gccMProcess.waitForFinished(); #endif //final linking - gccOptions.replace("$PROGRAM.OBJ$", Common::pathInTemp("program.o")); - gccOptions.replace("$MACRO.OBJ$", stdioMacros); - gccOptions.replace("$PROGRAM$", Common::pathInTemp("SASMprog.exe")); - gccOptions.replace("$SOURCE$", Common::pathInTemp("program.asm")); - gccOptions.replace("$LSTOUTPUT$", Common::pathInTemp("program.lst")); - QStringList gccArguments = gccOptions.split(QChar(' ')); - QProcess gccProcess; - QString gccOutput = Common::pathInTemp("linkererror.txt"); - gccProcess.setStandardOutputFile(gccOutput); - gccProcess.setStandardErrorFile(gccOutput, QIODevice::Append); - gccProcess.start(gcc, gccArguments); - gccProcess.waitForFinished(); + linkerOptions.replace("$PROGRAM.OBJ$", Common::pathInTemp("program.o")); + linkerOptions.replace("$MACRO.OBJ$", stdioMacros); + linkerOptions.replace("$PROGRAM$", Common::pathInTemp("SASMprog.exe")); + linkerOptions.replace("$SOURCE$", Common::pathInTemp("program.asm")); + linkerOptions.replace("$LSTOUTPUT$", Common::pathInTemp("program.lst")); + QStringList linkerArguments = linkerOptions.split(QChar(' ')); + QProcess linkerProcess; + QString linkerOutput = Common::pathInTemp("linkererror.txt"); + linkerProcess.setStandardOutputFile(linkerOutput); + linkerProcess.setStandardErrorFile(linkerOutput, QIODevice::Append); + linkerProcess.start(linker, linkerArguments); + linkerProcess.waitForFinished(); QFile logFile; logFile.setFileName(assemblerOutput); @@ -903,7 +895,7 @@ void MainWindow::buildProgram(bool debugMode) //print errors printLog(logText, Qt::red); - logFile.setFileName(gccOutput); + logFile.setFileName(linkerOutput); logFile.open(QIODevice::ReadOnly); QTextStream logLinker(&logFile); logText = logLinker.readAll(); @@ -916,7 +908,7 @@ void MainWindow::buildProgram(bool debugMode) printLogWithTime(tr("Built successfully.") + '\n', Qt::darkGreen); //print warnings printLog(logText, Qt::red); - logFile.setFileName(gccOutput); + logFile.setFileName(linkerOutput); logFile.open(QIODevice::ReadOnly); QTextStream logLinker(&logFile); logText = logLinker.readAll(); @@ -1062,31 +1054,65 @@ void MainWindow::printOutput(QString msg, int index) void MainWindow::debug() { - buildProgram(true); - if (!programIsBuilded) { - printLogWithTime(tr("Before debugging you need to build the program.") + '\n', Qt::red); - return; + if (!debugger) { //start debugger + debuggerWasStarted = false; + buildProgram(true); + if (!programIsBuilded) { + printLogWithTime(tr("Before debugging you need to build the program.") + '\n', Qt::red); + return; + } + ((Tab *) tabs->currentWidget())->clearOutput(); + printLogWithTime(tr("Debugging started...") + '\n', Qt::darkGreen); + QString path = Common::pathInTemp("SASMprog.exe"); + CodeEditor *code = ((Tab *) tabs->currentWidget())->code; + debugger = new Debugger(compilerOut, path, ioIncIncluded, Common::pathInTemp(QString()), assembler); + connect(debugger, SIGNAL(highlightLine(int)), code, SLOT(updateDebugLine(int))); + connect(debugger, SIGNAL(finished()), this, SLOT(debugExit()), Qt::QueuedConnection); + connect(debugger, SIGNAL(started()), this, SLOT(enableDebugActions())); + connect(debugger, SIGNAL(started()), this, SLOT(showAnyCommandWidget())); + connect(code, SIGNAL(breakpointsChanged(quint64,bool)), debugger, SLOT(changeBreakpoint(quint64,bool))); + connect(code, SIGNAL(addWatchSignal(const RuQPlainTextEdit::Watch &)), + this, SLOT(setShowMemoryToChecked(RuQPlainTextEdit::Watch))); + connect(debugger, SIGNAL(printLog(QString,QColor)), this, SLOT(printLog(QString,QColor))); + connect(debugger, SIGNAL(printOutput(QString)), this, SLOT(printOutput(QString))); + connect(debugger, SIGNAL(inMacro()), this, SLOT(debugNextNi()), Qt::QueuedConnection); + connect(debugger, SIGNAL(wasStopped()), this, SLOT(changeDebugActionToStart())); + code->setDebugEnabled(); + } else { //pause or continue debugger + debugAction->setEnabled(false); + if (debugger->isStopped()) { + debugAction->setText(tr("Pause")); + debugAction->setIcon(QIcon(":/images/debugPause.png")); + debugger->doInput(QString("c\n"), ni); + debugAction->setShortcut(QString()); + } else { + debugAction->setText(tr("Continue")); + debugAction->setIcon(QIcon(":/images/continue.png")); + debugAction->setShortcut(debugKey); + debugger->pause(); + } + debugAction->setEnabled(true); + } +} + +void MainWindow::changeDebugActionToStart() +{ + debugAction->setText(tr("Continue")); + debugAction->setIcon(QIcon(":/images/continue.png")); + debugAction->setShortcut(debugKey); + if (!debuggerWasStarted) + debuggerWasStarted = true; + else { + debugShowRegisters(); + debugShowMemory(); } - ((Tab *) tabs->currentWidget())->clearOutput(); - printLogWithTime(tr("Debugging started...") + '\n', Qt::darkGreen); - QString path = Common::pathInTemp("SASMprog.exe"); - CodeEditor *code = ((Tab *) tabs->currentWidget())->code; - debugger = new Debugger(compilerOut, path, ioIncIncluded, Common::pathInTemp(QString()), assembler); - connect(debugger, SIGNAL(highlightLine(int)), code, SLOT(updateDebugLine(int))); - connect(debugger, SIGNAL(finished()), this, SLOT(debugExit()), Qt::QueuedConnection); - connect(debugger, SIGNAL(started()), this, SLOT(enableDebugActions())); - connect(debugger, SIGNAL(started()), this, SLOT(showAnyCommandWidget())); - connect(code, SIGNAL(breakpointsChanged(quint64,bool)), debugger, SLOT(changeBreakpoint(quint64,bool))); - connect(code, SIGNAL(addWatchSignal(const RuQPlainTextEdit::Watch &)), - this, SLOT(setShowMemoryToChecked(RuQPlainTextEdit::Watch))); - connect(debugger, SIGNAL(printLog(QString,QColor)), this, SLOT(printLog(QString,QColor))); - connect(debugger, SIGNAL(printOutput(QString)), this, SLOT(printOutput(QString))); - connect(debugger, SIGNAL(inMacro()), this, SLOT(debugNextNi()), Qt::QueuedConnection); - code->setDebugEnabled(); } void MainWindow::enableDebugActions() { + debugAction->setText(tr("Continue")); + debugAction->setIcon(QIcon(":/images/continue.png")); + //set all user's breakpoints CodeEditor *code = ((Tab *) tabs->currentWidget())->code; int lineNumber; @@ -1095,8 +1121,6 @@ void MainWindow::enableDebugActions() } //enable all actions - debugAction->setEnabled(false); - debugContinueAction->setEnabled(true); debugNextAction->setEnabled(true); debugNextNiAction->setEnabled(true); debugShowRegistersAction->setEnabled(true); @@ -1118,8 +1142,9 @@ void MainWindow::enableDebugActions() void MainWindow::disableDebugActions(bool start) { - debugAction->setEnabled(true); - debugContinueAction->setEnabled(false); + debugAction->setText(tr("Debug")); + debugAction->setIcon(QIcon(":/images/debug.png")); + debugNextAction->setEnabled(false); debugNextNiAction->setEnabled(false); debugShowRegistersAction->setEnabled(false); @@ -1137,15 +1162,6 @@ void MainWindow::disableDebugActions(bool start) runAction->setEnabled(true); } -void MainWindow::debugContinue() -{ - debugContinueAction->setEnabled(false); - debugger->doInput(QString("c\n"), ni); - debugShowRegisters(); - debugShowMemory(); - debugContinueAction->setEnabled(true); -} - void MainWindow::debugNext() { CodeEditor *code = ((Tab *) tabs->currentWidget())->code; @@ -1154,8 +1170,6 @@ void MainWindow::debugNext() } else { debugNextAction->setEnabled(false); debugger->doInput(QString("si\n"), si); - debugShowRegisters(); - debugShowMemory(); debugNextAction->setEnabled(true); } } @@ -1170,8 +1184,6 @@ void MainWindow::debugNextNi() { debugNextNiAction->setEnabled(false); debugger->doInput(QString("ni\n"), ni); - debugShowRegisters(); - debugShowMemory(); debugNextNiAction->setEnabled(true); } @@ -1206,46 +1218,47 @@ void MainWindow::debugShowMemory() //fill table memoryWindow->initializeMemoryWindow(watches); } - debugger->setWatchesCount(memoryWindow->rowCount() - 1); - for (int i = 0; i < memoryWindow->rowCount() - 1; i++) { - if (memoryWindow->cellWidget(i, 2)) { - WatchSettinsWidget *settings = (WatchSettinsWidget *) memoryWindow->cellWidget(i, 2); - - int arraySize = 0; - - int size = settings->sizeComboBox->currentIndex(); - QStringList sizeFormat; - sizeFormat << "int" << "short" << "char" << "long long"; - - bool ok; - arraySize = settings->arraySizeEdit->text().toInt(&ok); - if (!ok && sizeFormat[size] == "long long") { - arraySize = 1; - ok = true; - } - QString watchAsArray; - if (ok && arraySize > 0) - watchAsArray = "[" + QString::number(arraySize) + "]"; - - int type = settings->typeComboBox->currentIndex(); - QStringList printFormat; - printFormat << "p" << "p/x" << "p/t" << "p/c" << "p/d" << "p/u" << "p/f"; - - if (! settings->addressCheckbox->isChecked()) { //watch as variable - debugger->doInput(printFormat[type] + " (" + sizeFormat[size] + watchAsArray + ")" + - memoryWindow->item(i, 0)->text() + "\n", infoMemory); - } else { //watch as random address - debugger->doInput(printFormat[type] + " (" + sizeFormat[size] + watchAsArray + ")" + - "*((" + sizeFormat[size] + "*) " + memoryWindow->item(i, 0)->text() + ")" + - "\n", infoMemory); + if (debugger->isStopped()) { + debugger->setWatchesCount(memoryWindow->rowCount() - 1); + for (int i = 0; i < memoryWindow->rowCount() - 1; i++) { + if (memoryWindow->cellWidget(i, 2)) { + WatchSettinsWidget *settings = (WatchSettinsWidget *) memoryWindow->cellWidget(i, 2); + + int arraySize = 0; + + int size = settings->sizeComboBox->currentIndex(); + QStringList sizeFormat; + sizeFormat << "int" << "short" << "char" << "long long"; + + bool ok; + arraySize = settings->arraySizeEdit->text().toInt(&ok); + if (!ok && sizeFormat[size] == "long long") { + arraySize = 1; + ok = true; + } + QString watchAsArray; + if (ok && arraySize > 0) + watchAsArray = "[" + QString::number(arraySize) + "]"; + + int type = settings->typeComboBox->currentIndex(); + QStringList printFormat; + printFormat << "p" << "p/x" << "p/t" << "p/c" << "p/d" << "p/u" << "p/f"; + + if (! settings->addressCheckbox->isChecked()) { //watch as variable + debugger->doInput(printFormat[type] + " (" + sizeFormat[size] + watchAsArray + ")" + + memoryWindow->item(i, 0)->text() + "\n", infoMemory); + } else { //watch as random address + debugger->doInput(printFormat[type] + " (" + sizeFormat[size] + watchAsArray + ")" + + "*((" + sizeFormat[size] + "*) " + memoryWindow->item(i, 0)->text() + ")" + + "\n", infoMemory); + } } } - } - if (memoryWindow->rowCount() > 1) { - QEventLoop eventLoop; - connect(debugger, SIGNAL(printMemory(QList)), &eventLoop, SLOT(quit())); - connect(debugger, SIGNAL(finished()), &eventLoop, SLOT(quit())); - eventLoop.exec(); + static SignalLocker locker; + if (memoryWindow->rowCount() > 1 && locker.tryLock()) { + connect(debugger, SIGNAL(printMemory(QList)), &locker, SLOT(unlock()), Qt::UniqueConnection); + connect(debugger, SIGNAL(finished()), &locker, SLOT(unlock()), Qt::UniqueConnection); + } } } else if (memoryWindow) { @@ -1320,11 +1333,12 @@ void MainWindow::debugShowRegisters() if (memoryDock) memoryDock->show(); } - debugger->doInput(QString("info registers\n"), infoRegisters); - QEventLoop eventLoop; - connect(debugger, SIGNAL(printRegisters(QList)), &eventLoop, SLOT(quit())); - connect(debugger, SIGNAL(finished()), &eventLoop, SLOT(quit())); - eventLoop.exec(); + static SignalLocker locker; + if (debugger->isStopped() && locker.tryLock()) { + connect(debugger, SIGNAL(printRegisters(QList)), &locker, SLOT(unlock()), Qt::UniqueConnection); + connect(debugger, SIGNAL(finished()), &locker, SLOT(unlock()), Qt::UniqueConnection); + debugger->doInput(QString("info registers\n"), infoRegisters); + } } else if (registersWindow) { registersWindow->close(); @@ -1379,16 +1393,16 @@ void MainWindow::closeAnyCommandWidget() void MainWindow::debugRunCommand(QString command, bool print) { - printLog("> " + command + "\n", QColor(32, 71, 247)); - QEventLoop eventLoop; - connect(debugger, SIGNAL(printLog(QString,QColor)), &eventLoop, SLOT(quit())); - if (print) - debugger->doInput("p " + command + "\n", anyAction); - else - debugger->doInput(command + "\n", anyAction); - eventLoop.exec(); - debugShowRegisters(); - debugShowMemory(); + static SignalLocker locker; + if (debugger->isStopped() && locker.tryLock()) { + connect(debugger, SIGNAL(printLog(QString,QColor)), &locker, SLOT(unlock()), Qt::UniqueConnection); + connect(debugger, SIGNAL(finished()), &locker, SLOT(unlock()), Qt::UniqueConnection); + printLog("> " + command + "\n", QColor(32, 71, 247)); + if (print) + debugger->doInput("p " + command + "\n", anyAction); + else + debugger->doInput(command + "\n", anyAction); + } } void MainWindow::find() @@ -1677,6 +1691,15 @@ void MainWindow::initAssemblerSettings(bool firstOpening) QString linkerOptions = assembler->getLinkerOptions(); settingsUi.linkingOptionsEdit->setText(settings.value("linkingoptions", linkerOptions).toString()); + QString assemblerPath = assembler->getAssemblerPath(); + settingsUi.assemblerPathEdit->setText(settings.value("assemblerpath", assemblerPath).toString()); + +#ifdef Q_OS_WIN32 + settingsUi.linkerPathEdit->setText(settings.value("linkerpath", Common::applicationDataPath() + "/MinGW/bin/gcc.exe").toString()); +#else + settingsUi.linkerPathEdit->setText(settings.value("linkerpath", "gcc").toString()); +#endif + disconnect(settingsUi.x86RadioButton, SIGNAL(toggled(bool)), this, SLOT(changeMode(bool))); //mode if (assembler->isx86()) @@ -1755,8 +1778,10 @@ void MainWindow::recreateAssembler(bool start) if (!start) { settingsUi.assemblyOptionsEdit->setText(assembler->getAssemblerOptions()); settingsUi.linkingOptionsEdit->setText(assembler->getLinkerOptions()); + settingsUi.assemblerPathEdit->setText(assembler->getAssemblerPath()); settings.setValue("assemblyoptions", assembler->getAssemblerOptions()); settings.setValue("linkingoptions", assembler->getLinkerOptions()); + settings.setValue("assemblerpath", assembler->getAssemblerPath()); changeStartText(); recreateHighlighter(); } @@ -1773,6 +1798,7 @@ void MainWindow::backupSettings() backupAssembler = settings.value("assembler", QString("NASM")).toString(); backupMode = settings.value("mode", QString("x86")).toString(); backupAssemblerOptions = settings.value("assemblyoptions", assembler->getAssemblerOptions()).toString(); + backupAssemblerPath = settings.value("assemblerpath", assembler->getAssemblerPath()).toString(); backupLinkerOptions = settings.value("linkingoptions", assembler->getLinkerOptions()).toString(); backupStartText = settings.value("starttext", assembler->getStartText()).toString(); } @@ -1783,6 +1809,7 @@ void MainWindow::restoreSettingsAndExit() settings.setValue("mode", backupMode); recreateAssembler(); settings.setValue("assemblyoptions", backupAssemblerOptions); + settings.setValue("assemblerpath", backupAssemblerPath); settings.setValue("linkingoptions", backupLinkerOptions); settings.setValue("starttext", backupStartText); settingsWindow->close(); @@ -1822,6 +1849,8 @@ void MainWindow::saveSettings() settings.setValue("assemblyoptions", settingsUi.assemblyOptionsEdit->text()); settings.setValue("linkingoptions", settingsUi.linkingOptionsEdit->text()); + settings.setValue("assemblerpath", settingsUi.assemblerPathEdit->text()); + settings.setValue("linkerpath", settingsUi.linkerPathEdit->text()); settings.setValue("starttext", settingsStartTextEditor->document()->toPlainText()); backupSettings(); @@ -1936,7 +1965,6 @@ void MainWindow::changeActionsState(int widgetIndex) runExeAction->setEnabled(false); stopAction->setEnabled(false); debugAction->setEnabled(false); - debugContinueAction->setEnabled(false); debugNextAction->setEnabled(false); debugNextNiAction->setEnabled(false); debugShowRegistersAction->setEnabled(false); diff --git a/mainwindow.h b/mainwindow.h index 5c495f0a..9a10abeb 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -55,6 +55,7 @@ #include #include #include +#include #include "tab.h" #include "highlighter.h" #include "debugger.h" @@ -70,6 +71,7 @@ #include "gas.h" #include "common.h" #include "fasm.h" +#include "signallocker.h" class MainWindow : public QMainWindow { @@ -131,7 +133,6 @@ class MainWindow : public QMainWindow QAction *runExeAction; QAction *stopAction; QAction *debugAction; - QAction *debugContinueAction; QAction *debugNextAction; QAction *debugNextNiAction; QAction *debugToggleBreakpointAction; @@ -164,6 +165,8 @@ class MainWindow : public QMainWindow bool programStopped; int outputIndex; Assembler *assembler; + bool debuggerWasStarted; + QString debugKey; //highlighters Highlighter *highlighter; @@ -191,6 +194,7 @@ class MainWindow : public QMainWindow QString backupMode; QString backupAssemblerOptions; QString backupLinkerOptions; + QString backupAssemblerPath; QString backupStartText; //about close @@ -224,7 +228,6 @@ public slots: //debug void debug(); - void debugContinue(); void enableDebugActions(); void disableDebugActions(bool start = false); void debugNext(); @@ -242,6 +245,7 @@ public slots: void closeAnyCommandWidget(); void printOutput(QString msg, int index = -1); void getOutput(); + void changeDebugActionToStart(); //search void find(); diff --git a/nasm.cpp b/nasm.cpp index a0ebae38..550efc60 100644 --- a/nasm.cpp +++ b/nasm.cpp @@ -54,10 +54,12 @@ QString NASM::getAssemblerPath() #endif } -quint64 NASM::getMainOffset(QFile &lst) +quint64 NASM::getMainOffset(QFile &lst, QString entryLabel) { + if (entryLabel == "start") + return 0; QTextStream lstStream(&lst); - QRegExp mainLabel("CMAIN:|main:"); + QRegExp mainLabel(entryLabel + ":"); bool flag = false; while (!lstStream.atEnd()) { QString line = lstStream.readLine(); @@ -67,7 +69,8 @@ quint64 NASM::getMainOffset(QFile &lst) //omit this string continue; } - char *s = line.toLocal8Bit().data(); + QByteArray lineArr = line.toLocal8Bit(); + const char *s = lineArr.constData(); quint64 a, b, c; if (sscanf(s, "%llu %llx %llx", &a, &b, &c) == 3) { if (!(b == 0 && c == 0)) { //exclude 0 0 @@ -83,16 +86,26 @@ quint64 NASM::getMainOffset(QFile &lst) } void NASM::parseLstFile(QFile &lst, QVector &lines, bool ioIncIncluded, quint64 ioIncSize, quint64 offset) +{ + if (!parseStringsInLstFile(lst, lines, ioIncIncluded, ioIncSize, offset, true)) + parseStringsInLstFile(lst, lines, ioIncIncluded, ioIncSize, offset, false); +} + +bool NASM::parseStringsInLstFile(QFile &lst, QVector &lines, + bool ioIncIncluded, quint64 ioIncSize, quint64 offset, bool considerTextSection) { bool inTextSection = false; QRegExp sectionTextRegExp("[Ss][Ee][Cc][Tt][Ii][Oo][Nn]\\s+\\.text"); QRegExp sectionRegExp("[Ss][Ee][Cc][Tt][Ii][Oo][Nn]"); quint64 omitLinesCount = 0; //for skipping strings from section .data - for processLst() QTextStream lstStream(&lst); + bool hasTextSection = false; + lstStream.seek(0); while (!lstStream.atEnd()) { QString line = lstStream.readLine(); if (line.indexOf(sectionTextRegExp) != -1) { inTextSection = true; + hasTextSection = true; } else if (line.indexOf(sectionRegExp) != -1) { inTextSection = false; } @@ -102,8 +115,9 @@ void NASM::parseLstFile(QFile &lst, QVector &lines, bool ioI omitLinesCount++; continue; } - if (inTextSection) { - char *s = line.toLocal8Bit().data(); + if (inTextSection || !considerTextSection) { + QByteArray lineArr = line.toLocal8Bit(); + const char *s = lineArr.constData(); quint64 a, b, c; if (sscanf(s, "%llu %llx %llx", &a, &b, &c) == 3) { if (!(b == 0 && c == 0)) { //exclude 0 0 @@ -118,6 +132,7 @@ void NASM::parseLstFile(QFile &lst, QVector &lines, bool ioI } } } + return hasTextSection; } QString NASM::getStartText() diff --git a/nasm.h b/nasm.h index 22c29bde..788afa35 100644 --- a/nasm.h +++ b/nasm.h @@ -49,8 +49,9 @@ class NASM : public Assembler public: explicit NASM(bool x86, QObject *parent = 0); QString getAssemblerPath(); - quint64 getMainOffset(QFile &lst); + quint64 getMainOffset(QFile &lst, QString entryLabel); void parseLstFile(QFile &lst, QVector &lines, bool ioIncIncluded, quint64 ioIncSize, quint64 offset); + bool parseStringsInLstFile(QFile &lst, QVector &lines, bool ioIncIncluded, quint64 ioIncSize, quint64 offset, bool considerTextSection); void fillHighligherRules(QVector &highlightingRules, QList &formats, bool &multiLineComments, diff --git a/settings.ui b/settings.ui index 8db64370..2e16e5e7 100644 --- a/settings.ui +++ b/settings.ui @@ -1911,6 +1911,26 @@ + + + + Assembler path: + + + + + + + Linker path: + + + + + + + + + diff --git a/signallocker.cpp b/signallocker.cpp new file mode 100644 index 00000000..f067f11d --- /dev/null +++ b/signallocker.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** SASM - simple IDE for NASM development +** Copyright (C) 2013 Dmitriy Manushin +** Contact: site: http://dman95.github.io/SASM/ +** e-mail: Dman1095@gmail.com +** +** This file is part of SASM. +** +** SASM 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 3 of the License, or +** (at your option) any later version. +** +** SASM 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 SASM. If not, see . +** +** Этот файл — часть SASM. +** +** SASM - свободная программа: вы можете перераспространять ее и/или +** изменять ее на условиях Стандартной общественной лицензии GNU в том виде, +** в каком она была опубликована Фондом свободного программного обеспечения; +** либо версии 3 лицензии, либо (по вашему выбору) любой более поздней +** версии. +** +** SASM распространяется в надежде, что она будет полезной, +** но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА +** или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной +** общественной лицензии GNU. +** +** Вы должны были получить копию Стандартной общественной лицензии GNU +** вместе с этой программой. Если это не так, см. +** .) +** +****************************************************************************/ + +#include "signallocker.h" + +SignalLocker::SignalLocker(QObject *parent) : + QObject(parent) +{ + locked = false; +} + +bool SignalLocker::tryLock() +{ + if (locked) + return false; + lock(); + return true; +} + +void SignalLocker::lock() +{ + locked = true; +} + +void SignalLocker::unlock() +{ + locked = false; +} diff --git a/signallocker.h b/signallocker.h new file mode 100644 index 00000000..482963a3 --- /dev/null +++ b/signallocker.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** SASM - simple IDE for NASM development +** Copyright (C) 2013 Dmitriy Manushin +** Contact: site: http://dman95.github.io/SASM/ +** e-mail: Dman1095@gmail.com +** +** This file is part of SASM. +** +** SASM 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 3 of the License, or +** (at your option) any later version. +** +** SASM 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 SASM. If not, see . +** +** Этот файл — часть SASM. +** +** SASM - свободная программа: вы можете перераспространять ее и/или +** изменять ее на условиях Стандартной общественной лицензии GNU в том виде, +** в каком она была опубликована Фондом свободного программного обеспечения; +** либо версии 3 лицензии, либо (по вашему выбору) любой более поздней +** версии. +** +** SASM распространяется в надежде, что она будет полезной, +** но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА +** или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной +** общественной лицензии GNU. +** +** Вы должны были получить копию Стандартной общественной лицензии GNU +** вместе с этой программой. Если это не так, см. +** .) +** +****************************************************************************/ + +#ifndef SIGNALLOCKER_H +#define SIGNALLOCKER_H + +#include + +class SignalLocker : public QObject +{ + Q_OBJECT + bool locked; +public: + explicit SignalLocker(QObject *parent = 0); + +signals: + +public slots: + void unlock(); + bool tryLock(); + void lock(); + +}; + +#endif // SIGNALLOCKER_H