Instructions
Requirements and Specifications
Source Code
cpu.cpp
#include "cpu.h"
#include
#include
#include
#include
#include
// CPU class constructor
CPU::CPU()
{
stack = new Stack();
progSize = 0;
curInst = 0;
error = false;
}
// CPU class destructor
CPU::~CPU()
{
delete stack;
if (progSize != 0) // if there was a program before, delete it
{
for (int i = 0; i < progSize; i++)
delete program[i];
delete [] program;
}
}
// Load program to CPU. Empties stack and removes previous program if any.
void CPU::LoadProgram(std::string filename)
{
std::ifstream file(filename.c_str());
if (!file.is_open())
throw std::runtime_error("ERROR: Unable to open file: \"" + filename + "\"");
std::string line;
// if there was a program before, delete it
if (progSize != 0)
{
for (int i = 0; i < progSize; i++)
delete program[i];
delete [] program;
}
// get file size
progSize = 0;
while (std::getline(file, line))
{
if (line.length() != 0)
progSize++;
}
file.clear();
file.seekg(0, file.beg); // rewind file pointer to start
program = new Instruction*[progSize]; // create array of instructions
// load program from file
int i = 0;
while (std::getline(file, line))
{
if (line.length() != 0)
{
std::string name;
int operand = 0;
std::stringstream ss(line);
ss >> name; // read instruction name
if (name == "PSH")
{
ss >> operand; // read operand
}
else if (name != "ADD" && name != "MULT" && name != "SUB" &&
name != "DIV" && name != "CMP")
throw std::runtime_error("ERROR: Invalid instruction: \"" + name + "\"");
program[i++] = new Instruction(name, operand);
}
}
file.close();
// empty the stack if it's not empty
while (!stack->isEmpty())
stack->pop();
// reset current instruction counter
curInst = 0;
error = false;
}
// Runs the entire program and returns the final value in the stack. Throws an
// error if there is more than one value left in the stack.
int CPU::Run()
{
if (error)
return 0;
while (Next() == 0 && !error);
if (!error)
{
if (stack->isEmpty())
{
std::cout << "ERROR: stack is empty after run";
error = true;
return 0;
}
int val = stack->pop();
if (!stack->isEmpty())
{
std::cout << "ERROR: stack is not empty after run";
error = true;
}
return val;
}
return 0;
}
// Runs the next line in the program and returns 0. If there are no more lines
// returns -1
int CPU::Next()
{
if (error)
return -1;
if (curInst < progSize)
{
int a, b;
std::string name = program[curInst]->getName();
try {
if (name == "PSH")
{
stack->push(program[curInst]->getOperand());
}
else if (name == "ADD")
{
a = stack->pop();
b = stack->pop();
stack->push(a + b);
}
else if (name == "MULT")
{
a = stack->pop();
b = stack->pop();
stack->push(a * b);
}
else if (name == "SUB")
{
a = stack->pop();
b = stack->pop();
stack->push(b - a);
}
else if (name == "DIV")
{
a = stack->pop();
b = stack->pop();
if (a == 0)
{
std::cout << "ERROR: Division by zero\n";
error = true;
return -1;
}
stack->push(b / a);
}
else if (name == "CMP")
{
a = stack->pop();
b = stack->pop();
if (a > b)
stack->push(1);
else if (a == b)
stack->push(0);
else
stack->push(-1);
}
curInst++;
return 0;
} catch (const std::exception& e) {
std::cout << "ERROR: missing stack argument for " << name << "\n";
error = true;
return -1;
}
}
return -1;
}
std::ostream& operator <<(std::ostream& os, const CPU& cpu)
{
os << *cpu.stack;
return os;
}
// Implementation of the Stack data structure
// Stack constructor
Stack::Stack()
{
cur_size = 100;
data = new int[cur_size];
size = 0;
}
// Stack destructor
Stack::~Stack()
{
delete [] data;
}
// Add new data on top of the stack
void Stack::push(int x)
{
if (size >= cur_size) // if allocated space is not enough
{
cur_size *= 2; // double stack size
int *new_data = new int[cur_size]; // allocate larger space
for (int i = 0; i < size; i++) // copy old data
new_data[i] = data[i];
delete [] data; // free old data
data = new_data; // use newluy allocated space
}
data[size++] = x; // save on top
}
// Remove the value at the top of the stack
int Stack::pop()
{
if (size == 0) // if empty stack
throw std::runtime_error("ERROR: Pop on empty stack");
size--; // decrement stack size
return data[size]; // return top value
}
// Returns true of the stack is empty
bool Stack::isEmpty()
{
return (size == 0);
}
// Print the stack
std::ostream& operator <<(std::ostream& os, const Stack& stack)
{
os << "Stack: [ ";
for (int i = 0; i < stack.size; i++)
{
if (i != 0)
os << ", ";
os << std::dec << stack.data[i];
}
os << " ]";
return os;
}
// Implementation of the Instruction class
// Constructor of the Instruction class
Instruction::Instruction(std::string name, int operand)
{
this->name = name;
this->operand = operand;
}
// Returns the type of the instruction
std::string Instruction::getName()
{
return name;
}
// Returns the operand for the instruction
int Instruction::getOperand()
{
return operand;
}
cpu.h
#pragma once
#include
#include
// Definition of the class for the stack
class Stack
{
int *data; // array of integers
int size; // number of current elements in stack
int cur_size; // size of allocated array
public:
Stack();
~Stack();
void push(int x);
int pop();
bool isEmpty();
friend std::ostream& operator <<(std::ostream& os, const Stack& stack);
};
// Definition of the instruction class
class Instruction
{
std::string name; // instruction name
int operand; // operand for PSH
public:
Instruction(std::string name, int operand);
std::string getName();
int getOperand();
};
// Definition of the CPU class
class CPU
{
Stack *stack; // stack
Instruction **program; // loaded program
int curInst; // current instruction
int progSize; // size of loaded program
bool error; // indicates if an execution error has occurred
public:
CPU();
~CPU();
void LoadProgram(std::string filename);
int Run();
int Next();
friend std::ostream& operator <<(std::ostream& os, const CPU& cpu);
};