You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
16 KiB
340 lines
16 KiB
/***************************************************************************
|
|
* 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 <tqtimer.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(TQObject *parent)
|
|
: TQObject(parent, "interactive")
|
|
{
|
|
setView(_view);
|
|
::signal(SIGINT, signalHandler);
|
|
#if defined(HAVE_READLINE)
|
|
using_history();
|
|
#else
|
|
_stdin.open(IO_ReadOnly, stdin);
|
|
#endif
|
|
TQTimer::singleShot(0, this, TQ_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 = TQString(line);
|
|
if ( !_input.isEmpty() ) add_history(line);
|
|
free(line);
|
|
#else
|
|
fprintf(stdout, "> ");
|
|
fflush(stdout);
|
|
char buffer[1000];
|
|
_stdin.readLine(buffer, 1000);
|
|
_input = TQString(buffer);
|
|
#endif
|
|
lineRead();
|
|
}
|
|
|
|
CLI::ExitCode CLI::Interactive::processLine(const TQString &s)
|
|
{
|
|
TQStringList words = TQStringList::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], TQString());
|
|
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], TQString());
|
|
}
|
|
if ( words[0]=="get" ) {
|
|
if ( words.count()!=2 ) return errorExit(i18n("This command takes one argument."), ARG_ERROR);
|
|
TQString 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] : TQString());
|
|
}
|
|
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 = TQMAX(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, TQString(" #%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()
|
|
{
|
|
TQString s = _input.stripWhiteSpace();
|
|
_input = TQString();
|
|
if ( processLine(s)==EXITING ) {
|
|
tqApp->exit(OK);
|
|
return;
|
|
}
|
|
TQTimer::singleShot(0, this, TQ_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:"));
|
|
TQValueVector<Pic::RegisterNameData> list = Pic::sfrList(data);
|
|
for (uint i=0; i<uint(list.count()); i++) log(Log::LineType::Normal, TQString(" %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, TQString(" %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();
|
|
TQValueVector<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, TQString(" %1: %2").arg(toHexLabel(list[i].data().address(), rdata.nbCharsAddress())).arg(list[i].label()));
|
|
return OK;
|
|
}
|
|
|
|
Address CLI::Interactive::findRegisterAddress(const TQString &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;
|
|
}
|
|
TQValueVector<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();
|
|
TQMap<TQString, Address> variables = coff->variables();
|
|
if ( variables.contains(name) ) return variables[name];
|
|
return Address();
|
|
}
|
|
|
|
CLI::ExitCode CLI::Interactive::executeRegister(const TQString &name, const TQString &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 TQString &filename)
|
|
{
|
|
TQFile 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 (;;) {
|
|
TQString s;
|
|
if ( file.readLine(s, 1000)==-1 ) break;
|
|
bool write = !s.startsWith(" ");
|
|
s = s.stripWhiteSpace();
|
|
if ( s.isEmpty() ) continue;
|
|
if (write) {
|
|
if ( !programmer->hardware()->rawWrite(s) ) return EXEC_ERROR;
|
|
} else {
|
|
TQString 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."));
|
|
}
|
|
|
|
#include "cli_interactive.moc"
|