--- /dev/null
+/*!
+ @file SReadline.h
+ @brief C++ wrapper around libreadline.
+ Supported: editing, history, custom completers, keymaps.
+ Attention: implementation is not thread safe!
+ It is mainly because the readline library provides
+ pure C interface and has many calls for an "atomic"
+ completion operation
+ */
+//
+// Date: 17 December 2005
+// 03 April 2006
+// 20 April 2006
+// 07 May 2006
+//
+// Copyright (c) Sergey Satskiy 2005 - 2006
+// <sergesatsky@yahoo.com>
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all copies.
+// This software is provided "as is" without express or implied
+// warranty, and with no claim as to its suitability for any purpose.
+//
+
+#ifndef SREADLINE_H
+#define SREADLINE_H
+
+#include <stdio.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <readline/keymaps.h>
+
+#include <string>
+#include <fstream>
+#include <vector>
+#include <stdexcept>
+#include <map>
+
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/function.hpp>
+
+
+ /*! @brief Used to avoid linking errors in case
+ of including into many compilation units
+ */
+namespace
+{
+ //! @brief Tokens in a single variation of a user command
+ typedef std::vector< std::string > TokensStorage;
+ //! @brief Set of variations of user commands
+ typedef std::vector< TokensStorage > CompletionsStorage;
+ //! @brief Pressed key callback. Must return 0 if OK, != 0 otherwise
+ typedef boost::function< int ( int, int ) > KeyCallback;
+ //! @brief A set of keys binding
+ typedef std::map< int, KeyCallback > KeysBind;
+
+
+ const size_t DefaultHistoryLimit( 64 ); //!< Default value for the history length
+ CompletionsStorage Completions; //!< Global storage of custom completions
+ TokensStorage Tokens; //!< Tokens storage for a single completion session
+ std::map< Keymap, KeysBind > Keymaps; //!< Global storage for keymaps
+
+ bool KeymapWasSetup( false ); //!< Has sense if a keymap was
+ //!< setup before the first readline call
+ Keymap Earlykeymap( 0 ); //!< The keymap which was setup before the first readline call
+
+
+ /*! @brief Custom completion generator
+ @param text Pointer to a token to be completed
+ @param State 0 for a first call, non 0 for all consequent calls
+ */
+ char * Generator( const char * text, int State );
+
+
+ /*! @brief The function is called before trying to complete a token
+ @param text A token to be completed
+ @param start Index of the beginning of the token in the readline buffer
+ @param end Index of the end of the token in the readline buffer
+ */
+ char ** UserCompletion( const char * text,
+ int start,
+ int end );
+
+
+ /*! @brief The function selects the set of bindings and
+ makes the corresponding call.
+ @param Count The parameter is passed by readline
+ @param Key The pressed key
+ */
+ int KeyDispatcher( int Count, int Key );
+
+
+ /*! @brief The readline startup hook. It is required to setup the proper keymap.
+ */
+ int StartupHook( void );
+
+
+ /*! @brief Compares all the Input tokens with starts tokens in the Pattern
+ @param Pattern pattern tokens
+ @param Input user input tokens
+ @return true if first Input.size() tokens are equal to the pattern tokens
+ */
+ template < typename Container >
+ bool AreTokensEqual( const Container & Pattern,
+ const Container & Input )
+ {
+ if ( Input.size() > Pattern.size() ) return false;
+
+ typename Container::const_iterator k( Pattern.begin() );
+ typename Container::const_iterator j( Input.begin() );
+ for ( ; j != Input.end(); ++k, ++j )
+ {
+ if ( *k == "%file" ) continue;
+ if ( *k != *j ) return false;
+ }
+ return true;
+ }
+
+
+ // See description near the prototype
+ template < typename ContainerType >
+ void SplitTokens( const std::string & Source, ContainerType & Container )
+ {
+ typedef boost::tokenizer< boost::char_separator< char > > TokenizerType;
+
+ boost::char_separator<char> Separators( " \t\n" ); // Set of token separators
+ TokenizerType Tokenizer( Source, Separators ); // Tokens provider
+ std::string SingleToken; // Temporary storage for a token
+
+ Container.clear();
+ for ( TokenizerType::const_iterator k( Tokenizer.begin() ); k != Tokenizer.end(); ++k )
+ {
+ SingleToken = *k;
+ boost::algorithm::trim( SingleToken );
+ Container.push_back( SingleToken );
+ }
+ }
+
+
+ // See description near the prototype
+ char ** UserCompletion( const char * text, int start, int /*end*/ )
+ {
+ // No default completion at all
+ rl_attempted_completion_over = 1;
+
+ if ( Completions.empty() ) return NULL;
+
+ // Memorize all the previous tokens
+ std::string PreInput( rl_line_buffer, start );
+ SplitTokens( PreInput, Tokens );
+
+
+ // Detect whether we should call the standard file name completer or a custom one
+ bool FoundPretender( false );
+
+ for ( CompletionsStorage::const_iterator k( Completions.begin() ); k != Completions.end(); ++k )
+ {
+ if ( ! AreTokensEqual( *k, Tokens ) ) continue;
+
+ if ( (*k).size() > Tokens.size() )
+ {
+ FoundPretender = true;
+ if ( (*k)[ Tokens.size() ] == "%file" )
+ {
+ // Standard file name completer - called for the "%file" keyword
+ return rl_completion_matches( text, rl_filename_completion_function );
+ }
+ }
+ }
+
+ if ( FoundPretender )
+ {
+ return rl_completion_matches( text, Generator );
+ }
+ return NULL;
+ }
+
+
+
+ // See description near the prototype
+ char * Generator( const char * text, int State )
+ {
+ static int Length;
+ static CompletionsStorage::const_iterator Iterator;
+
+
+ if ( State == 0 )
+ {
+ Iterator = Completions.begin();
+ Length = strlen( text );
+ }
+
+ for ( ; Iterator != Completions.end(); ++Iterator )
+ {
+ if ( ! AreTokensEqual( *Iterator, Tokens ) ) continue;
+
+ if ( (*Iterator).size() > Tokens.size() )
+ {
+ if ( (*Iterator)[ Tokens.size() ] == "%file" ) continue;
+
+
+ if ( strncmp( text, (*Iterator)[ Tokens.size() ].c_str(), Length ) == 0 )
+ {
+ // readline will free the allocated memory
+ char * NewString( (char*)malloc( strlen( (*Iterator)[ Tokens.size() ].c_str() ) + 1 ) );
+ strcpy( NewString, (*Iterator)[ Tokens.size() ].c_str() );
+ ++Iterator;
+ return NewString;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+
+ // See the description near the prototype
+ int KeyDispatcher( int Count, int Key )
+ {
+ std::map< Keymap, KeysBind >::iterator Set( Keymaps.find( rl_get_keymap() ) );
+ if ( Set == Keymaps.end() )
+ {
+ // Most probably it happens bacause the header was
+ // included into many compilation units and the
+ // keymap setting calls were made in different files.
+ // This is the problem of "global" data.
+ // The storage of all the registered keymaps is in anonymous
+ // namespace.
+ throw std::runtime_error( "Error selecting a keymap." );
+ }
+
+ (Set->second)[ Key ]( Count, Key );
+ return 0;
+ }
+
+
+ // See the description near the prototype
+ int StartupHook( void )
+ {
+ if ( KeymapWasSetup )
+ {
+ rl_set_keymap( Earlykeymap );
+ }
+ return 0;
+ }
+
+} // Anonymous namespace
+
+
+
+
+ /*! @brief The wrapper namespace.
+ The namespace is also used for other library elements.
+ */
+namespace swift
+{
+
+ /*! @brief The readline keymap wrapper.
+ Attention: It is not thread safe!
+ Supports: key binding, key unbinding
+ */
+ class SKeymap
+ {
+ private:
+ Keymap keymap; // Readline keymap
+
+ public:
+ /*! @brief Creates a new keymap
+ @param PrintableBound if true - the printable characters are bound
+ if false - the keymap is empty
+ */
+ explicit SKeymap( bool PrintableBound = false ) :
+ keymap( NULL )
+ {
+ if ( PrintableBound )
+ {
+ keymap = rl_make_keymap(); // Printable characters are bound
+ }
+ else
+ {
+ keymap = rl_make_bare_keymap(); // Empty keymap
+ }
+
+ if ( keymap == NULL )
+ {
+ throw std::runtime_error( "Cannot allocate keymap." );
+ }
+
+ // Register a new keymap in the global list
+ Keymaps[ keymap ] = KeysBind();
+ }
+
+ /*! @brief Creates a new keymap which is a copy of Pattern
+ @param Pattern A keymap to be copied
+ */
+ explicit SKeymap( Keymap Pattern ) :
+ keymap( rl_copy_keymap( Pattern ) )
+ {
+ if ( keymap == NULL )
+ {
+ throw std::runtime_error( "Cannot allocate keymap." );
+ }
+
+ // Register a new keymap in the global list
+ Keymaps[ keymap ] = KeysBind();
+ }
+
+ /*! @brief Frees the allocated keymap
+ */
+ ~SKeymap()
+ {
+ // Deregister the keymap
+ Keymaps.erase( keymap );
+ rl_discard_keymap( keymap );
+ }
+
+ /*! @brief Binds the given key to a function
+ @param Key A key to be bound
+ @param Callback A function to be called when the Key is pressed
+ */
+ void Bind( int Key, KeyCallback Callback )
+ {
+ Keymaps[ keymap ][ Key ] = Callback;
+ if ( rl_bind_key_in_map( Key, KeyDispatcher, keymap ) != 0 )
+ {
+ // Remove from the map just bound key
+ Keymaps[ keymap ].erase( Key );
+ throw std::runtime_error( "Invalid key." );
+ }
+ }
+
+
+ /*! @brief Unbinds the given key
+ @param Key A key to be unbound
+ */
+ void Unbind( int Key )
+ {
+ rl_unbind_key_in_map( Key, keymap );
+ Keymaps[ keymap ].erase( Key );
+ }
+
+ // void Bind( const std::string & Sequence, boost::function< int ( int, int ) > );
+ // void Unbind( std::string & Sequence );
+
+ public:
+ /*! @brief Copy constructor
+ @param rhs Right hand side object of SKeymap
+ */
+ SKeymap( const SKeymap & rhs ) : keymap(0)
+ {
+ if ( this == & rhs )
+ {
+ return;
+ }
+ keymap = rl_copy_keymap( rhs.keymap );
+ }
+
+ /*! @brief operator=
+ @param rhs Right hand side object of SKeymap
+ */
+ SKeymap & operator=( const SKeymap & rhs )
+ {
+ if ( this == & rhs )
+ {
+ return *this;
+ }
+ keymap = rl_copy_keymap( rhs.keymap );
+ return *this;
+ }
+
+ friend class SReadline;
+ };
+
+
+ /*! @brief The readline library wrapper.
+ Attention: It is not thread safe!
+ Supports: editing, history, custom completers
+ */
+ class SReadline
+ {
+ public:
+ /*! @brief Constructs the object, sets the completion function
+ @param Limit History size
+ */
+ SReadline( const size_t Limit = DefaultHistoryLimit ) :
+ HistoryLimit( Limit ),
+ HistoryFileName( "" ),
+ OriginalCompletion( rl_attempted_completion_function )
+ {
+ rl_startup_hook = StartupHook;
+ rl_attempted_completion_function = UserCompletion;
+ using_history();
+ }
+
+ /*! @brief Constructs the object, sets the completion function, loads history
+ @param historyFileName File name to load history from
+ @param Limit History size
+ */
+ SReadline( const std::string & historyFileName,
+ const size_t Limit = DefaultHistoryLimit ) :
+ HistoryLimit( Limit ),
+ HistoryFileName( historyFileName ),
+ OriginalCompletion( rl_attempted_completion_function )
+ {
+ rl_startup_hook = StartupHook;
+ rl_attempted_completion_function = UserCompletion;
+ using_history();
+ LoadHistory( HistoryFileName );
+ }
+
+ /*! @brief Saves the session history (if the file name was provided)
+ and destroys the object
+ */
+ ~SReadline()
+ {
+ rl_attempted_completion_function = OriginalCompletion;
+ SaveHistory( HistoryFileName );
+ }
+
+ /*! @brief Gets a single line from a user
+ @param Prompt A printed prompt
+ @return A string which was actually inputed
+ */
+ std::string GetLine( const std::string & Prompt )
+ {
+ bool Unused;
+ return GetLine( Prompt, Unused );
+ }
+
+
+ /*! @brief Gets a single line from a user
+ @param Prompt A printed prompt
+ @param ReadTokens A user inputed string splitted into tokens.
+ The container is cleared first
+ @return A string which was actually inputed
+ */
+ template < typename Container >
+ std::string GetLine( const std::string & Prompt,
+ Container & ReadTokens )
+ {
+ bool Unused;
+ return GetLine( Prompt, ReadTokens, Unused );
+ }
+
+
+ /*! @brief Gets a single line from a user
+ @param Prompt A printed prompt
+ @param BreakOut it is set to true if the EOF found
+ @param ReadTokens A user inputed string splitted into tokens.
+ The container is cleared first
+ @return A string which was actually inputed
+ */
+ template < typename Container >
+ std::string GetLine( const std::string & Prompt,
+ Container & ReadTokens,
+ bool & BreakOut )
+ {
+ std::string Input( GetLine( Prompt, BreakOut ) );
+ SplitTokens( Input, ReadTokens );
+ return Input;
+ }
+
+
+ /*! @brief Gets a single line from a user
+ @param Prompt A printed prompt
+ @param BreakOut it is set to true if the EOF found
+ @return A string which was actually inputed
+ */
+ std::string GetLine( const std::string & Prompt,
+ bool & BreakOut )
+ {
+ BreakOut = true;
+
+ char * ReadLine( readline( Prompt.c_str() ) );
+ if ( ReadLine == NULL ) return std::string();
+
+ // It's OK
+ BreakOut = false;
+ std::string Input( ReadLine );
+ free( ReadLine );
+
+ boost::algorithm::trim( Input );
+ if ( !Input.empty() )
+ {
+ if ( (history_length == 0) ||
+ (Input != history_list()[ history_length - 1 ]->line)
+ )
+ {
+ add_history( Input.c_str() );
+ if ( history_length >= static_cast< int >( HistoryLimit ) )
+ {
+ stifle_history( HistoryLimit );
+ }
+ }
+ }
+
+ return Input;
+ }
+
+
+ /*! @brief Fills the given container with the current history list
+ Does not clear the given container
+ */
+ template < typename ContainerType >
+ void GetHistory( ContainerType & Container )
+ {
+ for ( int k( 0 ); k < history_length; ++k )
+ {
+ Container.push_back( history_list()[ k ]->line );
+ }
+ }
+
+ /*! @brief Saves the history to the given file stream
+ @param OS output file stream
+ @return true if success
+ */
+ bool SaveHistory( std::ostream & OS )
+ {
+ if ( ! OS ) return false;
+ for ( int k( 0 ); k < history_length; ++k )
+ {
+ OS << history_list()[ k ]->line << std::endl;
+ }
+ return true;
+ }
+
+ /*! @brief Saves the history to the given file
+ @param FileName File name to save the history to
+ @return true if success
+ */
+ bool SaveHistory( const std::string & FileName )
+ {
+ if ( FileName.empty() ) return false;
+
+ std::ofstream OS( FileName.c_str() );
+ return SaveHistory( OS );
+ }
+
+ /*! @brief Clears the history. Does not affect the file where
+ the previous session history is saved.
+ */
+ void ClearHistory()
+ {
+ clear_history();
+ }
+
+
+ /*! @brief Loads a history from a file stream
+ @param IS Input file stream
+ @return true if success
+ */
+ bool LoadHistory( std::istream & IS )
+ {
+ if ( ! IS ) return false;
+
+ ClearHistory();
+ std::string OneLine;
+
+ while ( ! getline( IS, OneLine ).eof() )
+ {
+ boost::algorithm::trim( OneLine );
+ if ( (history_length == 0) ||
+ (OneLine != history_list()[ history_length - 1 ]->line)
+ )
+ {
+ add_history( OneLine.c_str() );
+ }
+ }
+ stifle_history( HistoryLimit );
+ return true;
+ }
+
+ /*! @brief Loads a history from the given file
+ @param FileName File name to be load from
+ @return true if success
+ */
+ bool LoadHistory( const std::string & FileName )
+ {
+ if ( FileName.empty() ) return false;
+
+ std::ifstream IS( FileName.c_str() );
+ return LoadHistory( IS );
+ }
+
+ /*! @brief Allows to register custom completers.
+ Supports a special keyword: %file. It means to use the standard file name completer
+ For example the given container elements could be as follows:
+ command1 opt1
+ command1 opt2 %file
+ command2
+ command2 opt1
+ Each container element must describe a single possible command line
+ The container element must have a conversion to std::string operator
+ @param Container A container which has all the user possible commands
+ */
+ template < typename ContainerType >
+ void RegisterCompletions( const ContainerType & Container )
+ {
+ Completions.clear();
+ for ( typename ContainerType::const_iterator k( Container.begin() );
+ k != Container.end(); ++k )
+ {
+ std::vector< std::string > OneLine;
+ SplitTokens( (*k).get<0>(), OneLine );
+ Completions.push_back( OneLine );
+ }
+ }
+
+
+ /*! @brief Sets the given keymap
+ @param NewKeymap The keymap that should be used from now
+ */
+ void SetKeymap( SKeymap & NewKeymap )
+ {
+ rl_set_keymap( NewKeymap.keymap );
+ KeymapWasSetup = true;
+ Earlykeymap = NewKeymap.keymap;
+ }
+
+ private:
+ const size_t HistoryLimit; //!< Constructor accepted or default
+ const std::string HistoryFileName; //!< Original constructor accepted
+ rl_completion_func_t * OriginalCompletion; //!< Original state will be restored
+ };
+
+
+} // namespace swift
+
+#endif
+
#include <iostream>
+#include <iomanip>
#include <fstream>
#include <boost/tokenizer.hpp>
#include <boost/program_options.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <map>
+#include <list>
+
+#include <boost/function.hpp>
+#include <boost/functional.hpp>
+#include <boost/tuple/tuple.hpp>
#include "disasm.h"
#include "ccpu.hpp"
#include "CInstrFactory.hpp"
#include "uint32_from_hex.hpp"
+#include "SReadline/SReadline.h"
+using namespace swift;
+
+typedef boost::function<void (const vector<string> &)> Func;
+typedef boost::tuple<string, Func> CompleterElement;
+
+typedef list<CompleterElement> MyCompleterContainer;
+
+class LookupFunctor
+{
+ public:
+ // Creates a functor and memorises tokens
+ LookupFunctor(const vector<string>& tokens) : Tokens(tokens) {}
+
+ // Compares the first token only
+ bool operator()(const CompleterElement& ele) const
+ {
+ return (strncmp(Tokens.begin()->c_str(), ele.get<0>().c_str(), Tokens.begin()->size()) == 0);
+ }
+
+ private:
+ const vector<string> &Tokens;
+};
+
+
+
+
+class CHelpExec
+{
+ private:
+ const MyCompleterContainer &m_completers;
+
+ public:
+ CHelpExec(const MyCompleterContainer &cont) : m_completers(cont) {}
+
+ void operator() (const vector<string>&)
+ {
+ cout << "Available commands: " << endl;
+ for(auto iter = m_completers.begin(); iter != m_completers.end(); ++iter) {
+ cout << (*iter).get<0>() << endl;
+ }
+ }
+};
+
+void close_prog(const std::vector<std::string> &);
+
CCpu* Iinstr::m_cpu;
+CCpu* global_cpu = NULL;
+
+void doExit(const vector<string>&)
+{
+ exit(EXIT_SUCCESS);
+}
+
+void execStep(const vector<string>& in)
+{
+ int count = 1;
+ if(in.size() == 2) {
+ try {
+ count = lexical_cast<int>(in.back());
+ }
+ catch(bad_cast&) {
+ cerr << "given parameter to step is not a number" << endl;
+ }
+ }
+ while(count > 0) {
+ try {
+ global_cpu->tick();
+ }
+ catch(std::string& e) {
+ cerr << e << endl;
+ }
+ count--;
+ }
+}
+
+void printReg(const vector<string>&)
+{
+ int i, start = 0, end = 15;
+ /* Todo:
+ * 1) make 2 columns
+ * 2) let use select registers to show (one register, range of registers)
+ */
+
+ for(i = start; i <= end; i++) {
+ cout << setw(2) << i << ": 0x" << std::hex << setw(8) << setfill('0') << global_cpu->getRegister(i) << std::dec << endl;
+ }
+}
+
+
using boost::lexical_cast;
using boost::bad_lexical_cast;
exit(EXIT_FAILURE);
}
-
CCpu cpu(16,1000,1000);
+ global_cpu = &cpu;
+
Iinstr::setCPU(&cpu);
cout << endl;
-
+/*
for(int i = 0; i <= 32; i += 4) {
Iinstr *pinstr = cpu.getProg(i);
if(pinstr != NULL) {
CDat data = cpu.getRAM(i);
cout << i << " : " << std::hex << i << std::dec << " " << data << endl;
}
-
+*/
cpu.setRegister(1, 4);
cpu.setRegister(2, 0);
cpu.setRAM(0,5);
//set return to nowhere for ret
cpu.setRAM(500,50);
+ SReadline Reader;
+
+ MyCompleterContainer Completers;
+
+ CHelpExec HelpExec(Completers);
+
+ Completers.push_back(CompleterElement("help", boost::bind1st( boost::mem_fun( &CHelpExec::operator()), &HelpExec)));
+ Completers.push_back(CompleterElement("quit", &doExit));
+ Completers.push_back(CompleterElement("exit", &doExit));
+ Completers.push_back(CompleterElement("q", &doExit));
+ Completers.push_back(CompleterElement("step [count]",&execStep));
+ Completers.push_back(CompleterElement("reg",&printReg));
+
+ Reader.RegisterCompletions(Completers);
+
+ string UserInput;
+ vector<string> Tokens, lastTokens;
+ bool EndOfInput = false;
+
+ auto Found(Completers.end());
+
+ Func lastFunc = NULL;
+
+
+ while(1) {
+ UserInput = Reader.GetLine("> ", Tokens, EndOfInput);
+ if(EndOfInput) {
+ break;
+ }
+
+ if(!Tokens.empty()) {
+ Found = find_if(Completers.begin(), Completers.end(), LookupFunctor(Tokens));
+
+ if(Found != Completers.end()) {
+ if((*Found).get<1>() != 0) {
+ lastFunc = (*Found).get<1>();
+ lastFunc(Tokens);
+ lastTokens = Tokens;
+ //(*Found).get<1>()(Tokens);
+ }
+ }
+ else {
+ lastFunc = NULL;
+ cout << "Unknown command. 'help' displays help" << endl;
+ }
+ }
+ else {
+ if(lastFunc != NULL) {
+ lastFunc(lastTokens);
+ }
+ else {
+ cout << "Unknown command. 'help' displays help" << endl;
+ }
+ }
+ }
+
+
+/*
for(int i = 0; ; i++) {
try {
cpu.tick();
break;
}
}
-
+*/
return EXIT_SUCCESS;
}