summaryrefslogtreecommitdiffstats
path: root/src/piklab-prog/cli_interactive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/piklab-prog/cli_interactive.cpp')
-rw-r--r--src/piklab-prog/cli_interactive.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/piklab-prog/cli_interactive.cpp b/src/piklab-prog/cli_interactive.cpp
new file mode 100644
index 0000000..148ba62
--- /dev/null
+++ b/src/piklab-prog/cli_interactive.cpp
@@ -0,0 +1,347 @@
+/***************************************************************************
+ * Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * 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. *
+ ***************************************************************************/
+#include "cli_interactive.h"
+
+#if defined(HAVE_READLINE)
+# include <readline/readline.h>
+# include <readline/history.h>
+#else
+# include <stdio.h>
+#endif
+#include <signal.h>
+#include <qtimer.h>
+#include <stdlib.h>
+
+#include "progs/base/generic_prog.h"
+#include "cmdline.h"
+#include "cli_prog_manager.h"
+#include "common/cli/cli_log.h"
+#include "devices/base/device_group.h"
+#include "devices/pic/base/pic_register.h"
+#include "devices/pic/prog/pic_prog.h"
+#include "coff/base/text_coff.h"
+#include "devices/pic/prog/pic_debug.h"
+
+//-----------------------------------------------------------------------------
+const CLI::CommandData CLI::INTERACTIVE_COMMAND_DATA[] = {
+ { "property-list", NoCommandProperty, I18N_NOOP("Display the list of available properties.") },
+ { "register-list", NoCommandProperty, I18N_NOOP("Display the list of registers.") },
+ { "variable-list", NoCommandProperty, I18N_NOOP("Display the list of variables.") },
+ { "range-list", NoCommandProperty, I18N_NOOP("Display the list of memory ranges.") },
+ { "help", NoCommandProperty, I18N_NOOP("Display help.") },
+ { "quit", NoCommandProperty, I18N_NOOP("Quit.") },
+ { "set", NoCommandProperty, I18N_NOOP("Set property value: \"set <property> <value>\" or \"<property> <value>\".") },
+ { "unset", NoCommandProperty, I18N_NOOP("Unset property value: \"unset <property>\".") },
+ { "get", NoCommandProperty, I18N_NOOP("Get property value: \"get <property>\" or \"<property>\".") },
+ { "disconnect", NeedProgrammer, I18N_NOOP("Disconnect programmer.") },
+ { "start", NeedDevice | NeedProgrammer, I18N_NOOP("Start or restart debugging session.") },
+ { "step", NeedDevice | NeedProgrammer, I18N_NOOP("Step one instruction.") },
+ { "x", NeedDevice | NeedProgrammer, I18N_NOOP("Read or set a register or variable: \"x PORTB\" or \"x W 0x1A\".") },
+ { "break", NeedDevice | NeedProgrammer, I18N_NOOP("Set a breakpoint \"break e 0x04\" or list all breakpoints \"break\".") },
+ { "clear", NeedDevice | NeedProgrammer, I18N_NOOP("Clear a breakpoint \"clear 0\", \"clear e 0x04\", or clear all breakpoints \"clear all\".") },
+ { "raw-com", NeedDevice | NeedProgrammer, I18N_NOOP("Write and read raw commands to port from given file.") },
+ { 0, NoCommandProperty, 0 }
+};
+
+const CLI::PropertyData CLI::PROPERTY_DATA[] = {
+ { "programmer", "programmer <name>", "p", I18N_NOOP("Programmer to use."), "programmer-list", I18N_NOOP("Return the list of supported programmers.") },
+ { "hardware", "hardware <name>", "h", I18N_NOOP("Programmer hardware configuration to use (for direct programmer)."), "hardware-list", I18N_NOOP("Return the list of supported programmer hardware configurations.") },
+ { "device", "device <name>", "d", I18N_NOOP("Target device."), "device-list", I18N_NOOP("Return the list of supported devices.") },
+ { "format", "format <name>", "f", I18N_NOOP("Hex output file format."), "format-list", I18N_NOOP("Return the list of supported hex file formats.") },
+ { "port", "port <name>", "t", I18N_NOOP("Programmer port (\"usb\" or device such as \"/dev/ttyS0\")"), "port-list", I18N_NOOP("Return the list of detected ports.") },
+ { "firmware-dir", "firmware-dir <dir>", 0, I18N_NOOP("Firmware directory."), 0, 0 },
+ { "target-self-powered", "target-self-powered <true|false>", 0, I18N_NOOP("Set if target device is self-powered."), 0, 0 },
+ { "hex", 0, 0, I18N_NOOP("Hex file to be used for programming."), 0, 0 },
+ { "coff", 0, 0, I18N_NOOP("COFF file to be used for debugging."), 0, 0 },
+ { "processor", 0, 0, I18N_NOOP("Same as \"device\"."), 0, 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+//-----------------------------------------------------------------------------
+void CLI::Interactive::signalHandler(int n)
+{
+ if ( n!=SIGINT ) return;
+ fprintf(stdout, "<CTRL C> Break\n");
+ fflush(stdout);
+ Programmer::Base *prog = Programmer::manager->programmer();
+ if ( prog && prog->state()==Programmer::Running ) Programmer::manager->halt();
+ _interactive->redisplayPrompt();
+}
+
+CLI::Interactive::Interactive(QObject *parent)
+ : QObject(parent, "interactive")
+{
+ setView(_view);
+ ::signal(SIGINT, signalHandler);
+#if defined(HAVE_READLINE)
+ using_history();
+#else
+ _stdin.open(IO_ReadOnly, stdin);
+#endif
+ QTimer::singleShot(0, this, SLOT(displayPrompt()));
+}
+
+void CLI::Interactive::redisplayPrompt()
+{
+#if defined(HAVE_READLINE)
+ rl_forced_update_display();
+#else
+ fprintf(stdout, "> ");
+ fflush(stdout);
+#endif
+}
+
+void CLI::Interactive::displayPrompt()
+{
+#if defined(HAVE_READLINE)
+ char *line = readline("> ");
+ _input = QString(line);
+ if ( !_input.isEmpty() ) add_history(line);
+ free(line);
+#else
+ fprintf(stdout, "> ");
+ fflush(stdout);
+ char buffer[1000];
+ _stdin.readLine(buffer, 1000);
+ _input = QString(buffer);
+#endif
+ lineRead();
+}
+
+CLI::ExitCode CLI::Interactive::processLine(const QString &s)
+{
+ QStringList words = QStringList::split(" ", s);
+ if ( words.count()==0 ) return ARG_ERROR;
+ if ( words[0]=="command-list" || words[0]=="property-list" || words[0]=="range-list"
+ || isPropertyList(words[0]) ) return static_cast<Main *>(_main)->list(words[0]);
+ if ( words[0]=="register-list" ) return registerList();
+ if ( words[0]=="variable-list" ) return variableList();
+ if ( isProperty(words[0]) ) {
+ if ( words.count()==1 ) words.prepend("get");
+ else if ( words.count()==2 ) words.prepend("set");
+ else return errorExit(i18n("This command takes no or one argument"), ARG_ERROR);
+ }
+ if ( findCommand(words[0])!=OK ) return ARG_ERROR;
+ const CommandData *data = findCommandData(words[0]);
+ if ( words[0]=="quit" ) return EXITING;
+ if ( words[0]=="set" ) {
+ if ( words.count()==2 ) return static_cast<Main *>(_main)->executeSetCommand(words[1], QString::null);
+ if ( words.count()!=3 ) return errorExit(i18n("This command takes two arguments."), ARG_ERROR);
+ return static_cast<Main *>(_main)->executeSetCommand(words[1], words[2]);
+ }
+ if ( words[0]=="unset" ) {
+ if ( words.count()!=2 ) return errorExit(i18n("This command takes one argument."), ARG_ERROR);
+ return static_cast<Main *>(_main)->executeSetCommand(words[1], QString::null);
+ }
+ if ( words[0]=="get" ) {
+ if ( words.count()!=2 ) return errorExit(i18n("This command takes one argument."), ARG_ERROR);
+ QString s = static_cast<Main *>(_main)->executeGetCommand(words[1]);
+ if ( !s.isEmpty() ) log(Log::LineType::Normal, words[1] + ": " + s);
+ return OK;
+ }
+ if ( words[0]=="help" ) return static_cast<Main *>(_main)->list("command-list");
+ if ( words[0]=="x" ) {
+ if ( words.count()!=2 && words.count()!=3 ) return errorExit(i18n("This command takes one or two argument."), ARG_ERROR);
+ return executeRegister(words[1], words.count()==3 ? words[2] : QString::null);
+ }
+ if ( words[0]=="break" ) {
+ if ( words.count()==3 ) {
+ if ( words[1]=="e" ) {
+ bool ok;
+ ulong address = fromAnyLabel(words[2], &ok);
+ if ( !ok ) return errorExit(i18n("Number format not recognized."), ARG_ERROR);
+ PURL::Url dummy;
+ Breakpoint::Data data(dummy, address);
+ if ( Breakpoint::list().contains(data) ) return okExit(i18n("Breakpoint already set at %1.").arg(toHexLabel(address, nbChars(NumberBase::Hex, address))));
+ Breakpoint::list().append(data);
+ Breakpoint::list().setAddress(data, address);
+ Breakpoint::list().setState(data, Breakpoint::Active);
+ return okExit(i18n("Breakpoint set at %1.").arg(toHexLabel(address, nbChars(NumberBase::Hex, address))));
+ }
+ return errorExit(i18n("The first argument should be \"e\"."), ARG_ERROR);
+ }
+ if ( words.count()==1 ) {
+ uint nb = Breakpoint::list().count();
+ if ( nb==0 ) return okExit(i18n("No breakpoint set."));
+ log(Log::LineType::Normal, i18n("Breakpoints:"));
+ uint nbc = 0;
+ if (_device) nbc = _device->nbCharsAddress();
+ else for (uint i=0; i<nb; i++) {
+ Address address = Breakpoint::list().address(Breakpoint::list().data(i));
+ nbc = QMAX(nbc, nbChars(NumberBase::Hex, address.toUInt()));
+ }
+ for (uint i=0; i<nb; i++) {
+ Address address = Breakpoint::list().address(Breakpoint::list().data(i));
+ log(Log::LineType::Normal, QString(" #%1: %2").arg(i).arg(toHexLabel(address, nbc)));
+ }
+ return OK;
+ }
+ if ( words.count()!=1 && words.count()!=3 ) return errorExit(i18n("This command takes no or two argument."), ARG_ERROR);
+ return errorExit(i18n("Arguments not recognized."), ARG_ERROR);
+ }
+ if ( words[0]=="clear" ) {
+ if ( words.count()!=2 ) return errorExit(i18n("This command takes one argument."), ARG_ERROR);
+ if ( words[1]=="all" ) {
+ Breakpoint::list().clear();
+ return okExit(i18n("All breakpoints removed."));
+ }
+ bool ok;
+ uint i = words[1].toUInt(&ok);
+ if ( !ok ) return errorExit(i18n("Argument should be breakpoint index."), ARG_ERROR);
+ if ( i>=Breakpoint::list().count() ) return errorExit(i18n("Breakpoint index too large."), ARG_ERROR);
+ Breakpoint::Data data = Breakpoint::list().data(i);
+ Address address = Breakpoint::list().address(data);
+ Breakpoint::list().remove(data);
+ return okExit(i18n("Breakpoint at %1 removed.").arg(toHexLabelAbs(address)));
+ }
+ if ( words[0]=="raw-com" ) {
+ if ( words.count()!=2 ) return errorExit(i18n("This command needs a commands filename."), ARG_ERROR);
+ } else if ( words[0]=="program" || words[0]=="read" || words[0]=="verify" || words[0]=="erase" || words[0]=="blank_check" ) {
+ uint hexi = 1;
+ if ( words.count()>=2 && words[1]=="-r" ) {
+ hexi = 3;
+ if ( words.count()==2 ) return errorExit(i18n("You need to specify the range."), ARG_ERROR);
+ ExitCode code = static_cast<Main *>(_main)->extractRange(words[2]);
+ if ( code!=OK ) return code;
+ }
+ if ( data->properties & (InputHex|OutputHex) ) {
+ if ( uint(words.count())>(hexi+1) ) return errorExit(i18n("Too many arguments."), ARG_ERROR);
+ if ( uint(words.count())==(hexi+1) )_hexUrl = PURL::Url(runDirectory(), words[hexi]);
+ if ( _hexUrl.isEmpty() ) return errorExit(i18n("This command needs an hex filename."), ARG_ERROR);
+ } else if ( uint(words.count())>hexi ) return errorExit(i18n("Too many arguments."), ARG_ERROR);
+ } else if ( words.count()!=1 ) return errorExit(i18n("This command takes no argument."), ARG_ERROR);
+
+ ExitCode code = static_cast<Main *>(_main)->prepareCommand(words[0]);
+ if ( code!=OK ) return code;
+ if ( words[0]=="raw-com" ) return executeRawCommands(words[1]);
+ return static_cast<Main *>(_main)->executeCommand(words[0]);
+}
+
+void CLI::Interactive::lineRead()
+{
+ QString s = _input.stripWhiteSpace();
+ _input = QString::null;
+ if ( processLine(s)==EXITING ) {
+#if QT_VERSION<0x040000
+ qApp->exit(OK);
+#else
+ QCoreApplication::exit(OK);
+#endif
+ return;
+ }
+ QTimer::singleShot(0, this, SLOT(displayPrompt()));
+}
+
+CLI::ExitCode CLI::Interactive::registerList()
+{
+ if ( _device==0 ) return errorExit(i18n("No device specified."), NOT_SUPPORTED_ERROR);
+ if ( _device->group().name()!="pic" ) return errorExit(i18n("No register."), NOT_SUPPORTED_ERROR);
+ const Pic::Data &data = static_cast<const Pic::Data &>(*_device);
+ const Coff::Object *coff = Debugger::manager->coff();
+ const Pic::RegistersData &rdata = data.registersData();
+ log(Log::LineType::Normal, i18n("Special Function Registers:"));
+ QValueVector<Pic::RegisterNameData> list = Pic::sfrList(data);
+ for (uint i=0; i<uint(list.count()); i++) log(Log::LineType::Normal, QString(" %1: %2").arg(toHexLabel(list[i].data().address(), rdata.nbCharsAddress())).arg(list[i].label()));
+ log(Log::LineType::Normal, i18n("General Purpose Registers:"));
+ list = Pic::gprList(data, coff);
+ for (uint i=0; i<uint(list.count()); i++) log(Log::LineType::Normal, QString(" %1: %2").arg(toHexLabel(list[i].data().address(), rdata.nbCharsAddress())).arg(list[i].label()));
+ return OK;
+}
+
+CLI::ExitCode CLI::Interactive::variableList()
+{
+ if ( _device==0 ) return errorExit(i18n("No device specified."), NOT_SUPPORTED_ERROR);
+ const Coff::Object *coff = Debugger::manager->coff();
+ if ( coff==0 ) return errorExit(i18n("No COFF file specified."), NOT_SUPPORTED_ERROR);
+ if ( _device->group().name()!="pic" ) return errorExit(i18n("No register."), NOT_SUPPORTED_ERROR);
+ const Pic::Data &data = static_cast<const Pic::Data &>(*_device);
+ const Pic::RegistersData &rdata = data.registersData();
+ QValueVector<Pic::RegisterNameData> list = Pic::variableList(data, *coff);
+ if ( list.count()==0 ) log(Log::LineType::Normal, i18n("No variable."));
+ for (uint i=0; i<uint(list.count()); i++) log(Log::LineType::Normal, QString(" %1: %2").arg(toHexLabel(list[i].data().address(), rdata.nbCharsAddress())).arg(list[i].label()));
+ return OK;
+}
+
+Address CLI::Interactive::findRegisterAddress(const QString &name)
+{
+ const Pic::Data &data = static_cast<const Pic::Data &>(*_device);
+ const Coff::Object *coff = Debugger::manager->coff();
+ const Pic::RegistersData &rdata = data.registersData();
+ bool ok;
+ Address address = fromAnyLabel(name, &ok);
+ if (ok) {
+ if ( address>rdata.addressFromIndex(rdata.nbRegisters()-1) ) return Address();
+ return address;
+ }
+ QValueVector<Pic::RegisterNameData> sfrs = Pic::sfrList(data);
+ for (uint i=0; i<uint(sfrs.count()); i++) if ( name.lower()==sfrs[i].label().lower() ) return sfrs[i].data().address();
+ if ( coff==0 ) return Address();
+ QMap<QString, Address> variables = coff->variables();
+ if ( variables.contains(name) ) return variables[name];
+ return Address();
+}
+
+CLI::ExitCode CLI::Interactive::executeRegister(const QString &name, const QString &value)
+{
+ if ( Debugger::manager->debugger()==0 ) return errorExit(i18n("You need to start the debugging session first (with \"start\")."), ARG_ERROR);
+ const Pic::Data &data = static_cast<const Pic::Data &>(*_device);
+ const Pic::RegistersData &rdata = data.registersData();
+ uint nbChars = rdata.nbChars();
+ bool ok;
+ ulong v = fromAnyLabel(value, &ok);
+ if ( !ok ) return errorExit(i18n("Number format not recognized."), ARG_ERROR);
+ if ( v>maxValue(NumberBase::Hex, nbChars) ) return errorExit(i18n("The given value is too large (max: %1).").arg(toHexLabel(maxValue(NumberBase::Hex, nbChars), nbChars)), ARG_ERROR);
+ Register::TypeData rtd;
+ if ( name.lower()=="w" || name.lower()=="wreg" )
+ rtd = static_cast<Debugger::PicBase *>(Debugger::manager->debugger())->deviceSpecific()->wregTypeData();
+ else if ( name.lower()=="pc" )
+ rtd = Debugger::manager->debugger()->pcTypeData();
+ else {
+ Address address = findRegisterAddress(name);
+ if ( !address.isValid() ) return errorExit(i18n("Unknown register or variable name."), ARG_ERROR);
+ rtd = Register::TypeData(address, rdata.nbChars());
+ }
+ if ( value.isEmpty() ) {
+ if ( !Debugger::manager->readRegister(rtd) ) return ARG_ERROR;
+ return okExit(i18n("%1 = %2").arg(name.upper()).arg(toHexLabel(Register::list().value(rtd), nbChars)));
+ }
+ return (Debugger::manager->writeRegister(rtd, v) ? OK : EXEC_ERROR);
+}
+
+CLI::ExitCode CLI::Interactive::executeRawCommands(const QString &filename)
+{
+ QFile file(filename);
+ if ( !file.open(IO_ReadOnly) ) return errorExit(i18n("Could not open filename \"%1\".").arg(filename), ARG_ERROR);
+ if ( Programmer::manager->programmer()==0 ) {
+ Programmer::manager->createProgrammer(_device);
+ if ( !Programmer::manager->programmer()->simpleConnectHardware() ) return EXEC_ERROR;
+ }
+ Programmer::Base *programmer = Programmer::manager->programmer();
+ for (;;) {
+ QString s;
+#if QT_VERSION<0x040000
+ if ( file.readLine(s, 1000)==-1 ) break;
+#else
+ char buffer[1000];
+ if ( file.readLine(buffer, 1000)==-1 ) break;
+ s += buffer;
+#endif
+ bool write = !s.startsWith(" ");
+ s = s.stripWhiteSpace();
+ if ( s.isEmpty() ) continue;
+ if (write) {
+ if ( !programmer->hardware()->rawWrite(s) ) return EXEC_ERROR;
+ } else {
+ QString rs;
+ if ( !programmer->hardware()->rawRead(s.length(), rs) ) return EXEC_ERROR;
+ if ( rs!=s ) log(Log::LineType::Warning, i18n("Read string is different than expected: %1 (%2).").arg(rs).arg(s));
+ }
+ }
+ return okExit(i18n("End of command file reached."));
+}