Simple Machine Translator(SML) is a simulator that executes code written in hexadecimal. It supports features such as read, write, add, subtract and many more. My previous question concerning this minimal exercise link can be found here for those who wants to follow up. I made a lot of changes, restructured and move things around and would appreciate a review.
SML.h
#ifndef SML_SML_H_
#define SML_SML_H_
#include "evaluator.h"
#include <string>
constexpr size_word register_max_size = 6;
enum REGISTERS
{
ACCUMULATOR = 0,
INSTRUCTION_COUNTER = 1,
TEMPORARY_COUNTER = 2,
INSTRUCTION_REGISTER = 3,
OPERATION_CODE = 4,
OPERAND = 5
};
class SML
{
friend void swap( SML &lhs, SML &rhs );
friend class Evaluator;
public:
SML() = default;
SML( const int memory_size, const int word_lower_lim, const int word_upper_lim );
SML( const SML &s );
const SML& operator=( const SML s );
SML( SML &&s );
~SML();
void display_welcome_message() const;
void load_program();
void execute();
private:
size_word registers[ register_max_size ];
std::string temp_str; // holds the string before it is written into the memory
bool debug;
static const size_word read_ = 0xA; // Read a word(int) from the keyboard into a specific location in memory
static const size_word write_ = 0xB; // Write a word(int) from a specific location in memory to the screen
static const size_word read_str_ = 0xC; // Read a word(string) from the keyboard into a specific location in memory
static const size_word write_str_ = 0xD; // Write a word(string) from a specific location in memory to the screen
static const size_word load_ = 0x14; // Load a word from a specific location in memory to the accumulator
static const size_word store_ = 0x15; // Store a word from the accumulator into a specific location in memory
static const size_word add_ = 0x1E; /* Add a word from a specific location in memory to the word in the accumulator; store the
result in the accumulator */
static const size_word subtract_ = 0x1F;
static const size_word multiply_ = 0x20;
static const size_word divide_ = 0x21;
static const size_word modulo_ = 0x22;
static const size_word branch_ = 0x28; // Branch to a specific location in the memory
static const size_word branchneg_ = 0x29; // Branch if accumulator is negative
static const size_word branchzero_ = 0x2A; // Branch if accumulator is zero
static const size_word halt_ = 0x2B; // Halt the program when a task is completed
static const size_word newline_ = 0x32; // Insert a new line
static const size_word end_ = -0x1869F; // End the program execution
static const size_word sml_debug_ = 0x2C; // SML debug ( 1 to turn on, 0 to turn off )
size_word word_lower_limit; /* A word should not exceed */
size_word word_upper_limit; /* this limits */
size_word memory_size;
size_word *memory = nullptr;
void set_registers();
void memory_dump() const;
};
#endif
SML.cpp
#include "sml.h"
#include "evaluator.h"
#include <iostream>
#include <iomanip>
#include <algorithm>
SML::SML( const int mem_size, const int word_lower_lim, const int word_upper_lim )
: debug( false ), word_lower_limit( word_lower_lim ),
word_upper_limit( word_upper_lim ), memory_size( mem_size )
{
set_registers();
memory = new size_word[ memory_size ];
}
void SML::set_registers()
{
registers[ static_cast<unsigned>( ACCUMULATOR ) ] = 0;
registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ] = 0;
registers[ static_cast<unsigned>( TEMPORARY_COUNTER ) ] = 0;
registers[ static_cast<unsigned>( INSTRUCTION_REGISTER ) ] = 0;
registers[ static_cast<unsigned>( OPERATION_CODE ) ] = 0;
registers[ static_cast<unsigned>( OPERAND ) ] = 0;
}
SML::SML( const SML &s )
{
temp_str = s.temp_str;
debug = s.debug;
word_lower_limit = s.word_lower_limit;
word_upper_limit = s.word_upper_limit;
std::copy( std::cbegin( s.registers ), std::cend( s.registers ), registers );
memory_size = s.memory_size;
memory = new size_word[ memory_size ];
std::copy( s.memory, s.memory + s.memory_size, memory );
}
SML::SML( SML &&s )
{
swap( *this, s );
memory = new size_word[ memory_size ];
std::move( s.memory, s.memory + s.memory_size, memory );
}
const SML& SML::operator=( SML s )
{
swap( *this, s );
memory = new size_word[ memory_size ];
std::move( s.memory, s.memory + s.memory_size, memory );
return *this;
}
void swap( SML &lhs, SML &rhs )
{
using std::swap;
swap( lhs.temp_str, rhs.temp_str );
swap( lhs.debug, rhs.debug );
swap( lhs.word_lower_limit, rhs.word_lower_limit );
swap( lhs.word_upper_limit, rhs.word_upper_limit );
swap( lhs.memory_size, rhs.memory_size );
swap( lhs.registers, rhs.registers );
}
void SML::display_welcome_message() const
{
std::cout << "***" << " WELCOME TO SIMPLETRON! " << "***\n\n";
std::cout << std::setw( 5 ) << std::left << "***"
<< "Please enter your program one instruction"
<< std::setw( 5 ) << std::right << "***\n";
std::cout << std::setw( 5 ) << std::left << "***"
<< "(or data word) at a time. I will type the"
<< std::setw( 5 ) << std::right << "***\n";
std::cout << std::setw( 5 ) << std::left << "***"
<< "location number and a question mark (?)."
<< std::setw( 6 ) << std::right << "***\n";
std::cout << std::setw( 5 ) << std::left << "***"
<< "You then type the word for that location"
<< std::setw( 6 ) << std::right << "***\n";
std::cout << std::setw( 5 ) << std::left << "***"
<< "Type the sentinel -0x1869F to stop entering"
<< std::setw( 5 ) << std::right << "***\n";
std::cout << std::setw( 5 ) << std::left << "***"
<< "your program"
<< std::setw( 5 ) << std::right << "***";
std::cout << "\n\n" << std::flush;
}
void SML::load_program()
{
size_word &ins_cnt = registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ];
size_word temp;
while( ins_cnt != memory_size )
{
std::cout << std::setw( 2 ) << std::setfill( '0' )
<< ins_cnt << " ? ";
std::cin >> std::hex >> temp;
if( temp == end_ ) {
break;
}
if( temp >= word_lower_limit && temp < word_upper_limit )
memory[ ins_cnt++ ] = temp;
else
continue;
}
ins_cnt = 0;
std::cout << std::setfill( ' ' );
std::cout << std::setw( 5 ) << std::left << "***"
<< "Program loaded into memory"
<< std::setw( 5 ) << std::right << "***\n";
std::cout << std::setw( 5 ) << std::left << "***"
<< "Program execution starts..."
<< std::setw( 5 ) << std::right << "***\n";
execute();
std::cout << std::endl;
}
void SML::execute()
{
int divisor;
size_word &ins_cnt = registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ];
size_word &ins_reg = registers[ static_cast<unsigned>( INSTRUCTION_REGISTER ) ];
while( memory[ ins_cnt ] != 0 )
{
ins_reg = memory[ ins_cnt++ ];
if( ins_reg < 1000 ) divisor = 0x10;
else if( ins_reg >= 1000 && ins_reg < 10000 ) divisor = 0x100;
else if( ins_reg >= 10000 && ins_reg < 100000 ) divisor = 0x1000;
Evaluator eval( *this ); // create an instance of evaluator
try
{
if( eval.evaluate( *this, ins_reg, divisor ) == 0 )
break;
}
catch ( std::invalid_argument &e )
{
std::cout << e.what() << "\n";
}
if( debug )
memory_dump();
}
}
void SML::memory_dump() const
{
std::cout << "\nREGISTERS:\n";
std::cout << std::setw( 25 ) << std::left << std::setfill( ' ' ) << "accumulator" << std::showpos
<< std::setw( 5 ) << std::setfill( '0' ) << std::internal << registers[ 0 ] << '\n';
std::cout << std::setw( 28 ) << std::left << std::setfill( ' ' )
<< "instruction counter" << std::noshowpos << std::setfill( '0' )
<< std::right << std::setw( 2 ) << registers[ 1 ] << '\n';
std::cout << std::setw( 25 ) << std::left << std::setfill( ' ' )
<< "instruction register" << std::showpos << std::setw( 5 ) << std::setfill( '0' )
<< std::internal << registers[ 3 ] << '\n';
std::cout << std::setw( 28 ) << std::left << std::setfill( ' ' )
<< "operation code" << std::noshowpos << std::setfill( '0' )
<< std::right << std::setw( 2 ) << registers[ 4 ] << '\n';
std::cout << std::setw( 28 ) << std::left << std::setfill( ' ' )
<< "operand" << std::noshowpos << std::setfill( '0' )
<< std::right << std::setw( 2 ) << registers[ 5 ] << '\n';
std::cout << "\n\nMEMORY:\n";
std::cout << " ";
for( int i = 0; i != 10; ++i )
std::cout << std::setw( 6 ) << std::setfill( ' ') << std::right << i;
for( size_word i = 0; i != memory_size; ++i )
{
if( i % 10 == 0 )
std::cout << "\n" << std::setw( 3 ) << std::setfill( ' ' ) << i << " ";
std::cout << std::setw( 5 ) << std::setfill( '0' ) << std::showpos << std::internal << memory[ i ] << " ";
}
std::cout << std::endl;
}
SML::~SML()
{
// resets all the registers
set_registers();
// free the memory
delete [] memory;
}
Evaluator.h
#ifndef SML_EVALUATOR_H_
#define SML_EVALUATOR_H_
#include <iostream>
#include <stdint.h>
typedef int32_t size_word;
constexpr size_word instruction_max_sixe = 70;
class SML;
class Evaluator
{
public:
Evaluator() = default;
Evaluator( const SML & );
int evaluate( SML &s, const int ins_reg, const int divisor );
private:
void read( SML &s, const int opr );
void write( SML &s, const int opr );
void read_str( SML &s, const int opr );
void write_str( SML &s, const int opr );
void load( SML &s, const int opr );
void store( SML &s, const int opr );
void add( SML &s, const int opr );
void subtract( SML &s, const int opr );
void multiply( SML &s, const int opr );
void divide( SML &s, const int opr );
void modulo( SML &s, const int opr );
void branch( SML &s, const int opr );
void branchneg( SML &s, const int opr );
void branchzero( SML &s, const int opr );
void newline( SML &s, const int opr );
void smldebug( SML &s, const int opr );
bool division_by_zero( SML &s, const int opr );
void (Evaluator::*instruction_set[ instruction_max_sixe ])( SML &, int );
};
#endif
Evaluator.cpp
#include "evaluator.h"
#include "sml.h"
Evaluator::Evaluator( const SML &s )
{
instruction_set[ s.read_ ] = &Evaluator::read;
instruction_set[ s.write_ ] = &Evaluator::write;
instruction_set[ s.read_str_ ] = &Evaluator::read_str;
instruction_set[ s.write_str_ ] = &Evaluator::write_str;
instruction_set[ s.load_ ] = &Evaluator::load;
instruction_set[ s.store_ ] = &Evaluator::store;
instruction_set[ s.add_ ] = &Evaluator::add;
instruction_set[ s.subtract_ ] = &Evaluator::subtract;
instruction_set[ s.multiply_ ] = &Evaluator::multiply;
instruction_set[ s.divide_ ] = &Evaluator::divide;
instruction_set[ s.modulo_ ] = &Evaluator::modulo;
instruction_set[ s.branch_ ] = &Evaluator::branch;
instruction_set[ s.branchneg_ ] = &Evaluator::branchneg;
instruction_set[ s.branchzero_ ] = &Evaluator::branchzero;
instruction_set[ s.newline_ ] = &Evaluator::newline;
instruction_set[ s.sml_debug_ ] = &Evaluator::smldebug;
}
int Evaluator::evaluate( SML &s, const int ins_reg, const int divisor)
{
size_word &opr_code = s.registers[ static_cast<unsigned>( OPERATION_CODE ) ];
size_word &opr = s.registers[ static_cast<unsigned>( OPERAND ) ];
opr_code = ins_reg / divisor;
opr = ins_reg % divisor;
if( opr_code == s.halt_ )
return 0;
else
(this->*(instruction_set[ opr_code ]))( s, opr );
return 1;
}
void Evaluator::read( SML &s, const int opr )
{
std::cin >> s.memory[ opr ];
}
void Evaluator::write( SML &s, const int opr )
{
std::cout << s.memory[ opr ];
}
void Evaluator::read_str( SML &s, const int opr )
{
std::cin >> s.temp_str;
s.memory[ opr ] = s.temp_str.size();
for( std::string::size_type i = 1; i != s.temp_str.size() + 1; ++i )
s.memory[ opr + i ] = int( s.temp_str[ i - 1 ] );
}
void Evaluator::write_str( SML &s, const int opr )
{
for( int i = 0; i != s.memory[ opr ] + 1; ++i )
std::cout << char( s.memory[ opr + i ]);
}
void Evaluator::load( SML &s, const int opr )
{
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
accumulator = s.memory[ opr ];
}
void Evaluator::store( SML &s, const int opr )
{
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
s.memory[ opr ] = accumulator;
}
void Evaluator::add( SML &s, const int opr )
{
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
accumulator += s.memory[ opr ];
}
void Evaluator::subtract( SML &s, const int opr )
{
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
accumulator -= s.memory[ opr ];
}
void Evaluator::multiply( SML &s, const int opr )
{
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
accumulator *= s.memory[ opr ];
}
void Evaluator::divide( SML &s, const int opr )
{
if( division_by_zero( s, opr ) )
throw std::invalid_argument( "Division by zero: Program terminated abnormally." );
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
accumulator /= s.memory[ opr ];
}
void Evaluator::modulo( SML &s, const int opr )
{
if( division_by_zero( s, opr ) )
throw std::invalid_argument( "Division by zero: Program terminated abnormally." );
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
accumulator /= s.memory[ opr ];
}
bool Evaluator::division_by_zero( SML &s, const int opr )
{
return ( s.memory[ opr ] == 0 );
}
void Evaluator::branchneg( SML &s, const int opr )
{
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
if( accumulator < 0 )
branch( s, opr );
}
void Evaluator::branchzero( SML &s, const int opr )
{
size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
if( accumulator == 0 )
branch( s, opr );
}
void Evaluator::branch( SML &s, const int opr )
{
size_word &ins_cnt = s.registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ];
s.registers[ static_cast<unsigned>( TEMPORARY_COUNTER ) ] = ins_cnt;
ins_cnt = opr;
s.execute();
ins_cnt = s.registers[ static_cast<unsigned>( TEMPORARY_COUNTER ) ];
}
void Evaluator::newline( SML &s, const int opr )
{
std::cout << '\n' << std::flush;
}
void Evaluator::smldebug( SML &s, const int opr )
{
if ( opr == 1 ) s.debug = true;
else if ( opr == 0 ) s.debug = false;
}
main.cpp
#include "sml.h"
int main()
{
SML sml(1000, -999999, 999999 );
sml.display_welcome_message();
sml.load_program();
}
Below are instructions written to test the machine
Tests
0xA60 // read a value and store in address 60( written to index 96(decimal) in the array,
0xA61 // read another value and store in address 61
0x1460 // write the value stored in address 60 to the accumulator
0x1e61 // add the value stored in address 61 to the accumulator
0x320 // print a newline
0x1562 // store the value in the accumulatore to address 62
0xb62 // write the value in address 62 to the screen
0x320 // print a newline
0xc67 // read a string and store it size at address 67, the characters would be stored from 68 to end of character
0xd67 // write the characters to screen
0x2c1 // turn on debug
-0x1869f // start execution
evaluate()
function.0xC0000005
\$\endgroup\$