Virtual CPU - Complete - Multiply & Adders

IF YOU HAVE JUST ARRIVED HERE, PLEASE SEE: http://megalomaniacbore.blogspot.co.uk/2014/04/virtual-cpu-in-c-4001-cpu.html

This is the full listing as of 28/4/2014

-- Main.cpp ---

#include <iostream>
#include <exception>

#include "Electronics.h"
#include "Memory.h"
#include "CPU.h"

using namespace std;
using namespace CPU_4001;

void DefaultProgram(Memory*);
void ReportMemory(Memory*);
void ClearMemory (Memory*);
void LoadMemory(Memory*);
void SaveMemory(Memory*);

void ShowMenu();
const bool ChooseFromMenu(const char&, Memory*);
const bool IsExitOption(const char& selection);

void ProgramEntry(Memory*);

bool _debugMode = false;
void ToggleDebugMode();

int main ()
{
cout << "Init Memory...";
Memory* theMemory = new Memory();
cout << "Ready" << endl;

// Changes here: We have the CPU ONLY active
// for the cycle from the menu through, however
// we don't want to loose the memory, so that is
// created outside the loop
bool quitAll = false;
char selectedOption = ' ';
do
{
// Show the menu & read in a
// character (plus enter) for
// the next option to perform
ShowMenu();
cin >> selectedOption;

// Check if we're exiting
if ( !IsExitOption(selectedOption) )
{
// Check if we're running
if ( ChooseFromMenu(selectedOption, theMemory) )
{
cout << "************************************" << endl;
// Create the CPU
CPU* theCPU = new CPU(theMemory, _debugMode);
// Run the program in memory
theCPU->Run();
// The CPU is done
delete theCPU;
cout << "************************************" << endl;
}
}
else
{
quitAll = true;
}
}
while ( !quitAll );

delete theMemory;
}

void ProgramEntry(Memory* p_Memory)
{
cout << "Entering Program Entry, by memory address:" << endl;
cout << "N : Next address" << endl;
cout << "B : Previous Address" << endl;
cout << "<BYTE> changes current address + increment address" << endl;
if ( p_Memory != nullptr )
{
bool exitEditor = false;
byte value;
std::string input;
byte currentAddress = 2; // This is our base address
do
{
cout << "[" << (int)currentAddress << "] (" << (int)p_Memory->Read(currentAddress) << ") > ";
cin >> input;

// Now we need to know
// what our options are
// first off, if we enter
// a number it'll just
// get added to the current
// memory address and
// the memory address
// will increase by 1
try
{
// We also offer other options
if ( input == "x" || input == "X" )
{
exitEditor = true;
}
else if ( input == "b" || input == "B" || input == "back" )
{
--currentAddress;
}
else if ( input == "n" || input == "N" || input == "next" )
{
++currentAddress;
}
else
{
// So lets convert the string input
// to a number
int temp = atoi(input.c_str());
value = (byte)temp;

p_Memory->Write(currentAddress, value);
++currentAddress;
}
}
catch (std::exception& ex)
{
// Ptoblem
}
}
while ( !exitEditor );
}
}

void ShowMenu()
{
cout << endl << "===== Menu =====" << endl;
cout << "1. Load Default Program" << endl;
cout << "2. Clear the Memory" << endl;
cout << "3. Report Memory" << endl;
cout << "R. Run the current memory state through the CPU" << endl;
cout << "E. Enter Program Editor" << endl;
cout << endl;
cout << "D. Toggle Debug Settings [" << _debugMode << "]" << endl;
cout << "L. Load Memory" << endl;
cout << "S. Save Memory" << endl;
cout << endl;
cout << "X. Exit Interpter" << endl;
cout << endl;
cout << "Selection: ";
}

const bool IsExitOption(const char& selection)
{
return (selection == 'x' || selection == 'X');
}

const bool ChooseFromMenu(const char& selection, 
Memory* theMemory)
{
bool l_RunThroughCPU = false;

switch (selection)
{
case '1': 
DefaultProgram(theMemory);
break;

case '2':
ClearMemory(theMemory);
break;

case '3':
ReportMemory(theMemory);
break;

case 'r':
case 'R':
l_RunThroughCPU = true;
break;

case 'e':
case 'E':
ProgramEntry(theMemory);
break;

case 'd':
case 'D':
ToggleDebugMode();
break;

case 's':
case 'S':
SaveMemory(theMemory);
break;

case 'l':
case 'L':
LoadMemory(theMemory);
break;
}

return l_RunThroughCPU;
}

void ClearMemory(Memory* theMemory)
{
if ( theMemory != nullptr )
{
cout << "Clearing the memory...";
theMemory->Clear();
cout << "Complete" << endl;
}
}

void DefaultProgram(Memory* theMemory)
{
if ( theMemory != nullptr )
{
ClearMemory(theMemory);

// Add the program
cout << "Adding our default machine code program..." << endl;
// Load0 value 1
theMemory->Write(1, 1);
theMemory->Write(2, 1);

// Load1 Value 2
theMemory->Write(3, 2);
theMemory->Write(4, 2);

// Add
theMemory->Write(5, 3);

// Store to 12
theMemory->Write(6, 5);
theMemory->Write(7, 12);

// Print from 12
theMemory->Write(8, 6);
theMemory->Write(9, 12);

// Beep
theMemory->Write(10, 4);

// HALT
theMemory->Write(11, 0);
}
}

void ReportMemory(Memory* theMemory)
{
// Now, we only need to add "(int)" here, because the cout
// stream does not know to use our "byte" as a number, the
// C++ language would just assume that our memory spot is
// an "unsigned char"... or character, so we'd output garbage.
// (int) in front simply means "Treat this as a number"...
cout << "Memory Size: " << (int)theMemory->c_MaxAddress << endl;

cout << "Do you want to list the memory?";
char yesNo;
cin >> yesNo;
if ( yesNo == 'Y' || yesNo == 'y' )
{
for (byte currentAddress = 0; currentAddress < theMemory->c_MaxAddress; ++currentAddress)
{
// Again, add "(int)" to force usage as a number
cout << "Address [" << (int)currentAddress << "] = " << (int)theMemory->Read(currentAddress) << endl;
}
}
}

void LoadMemory(Memory* theMemory)
{
if ( theMemory != nullptr )
{
cout << endl << "Load Memory from: ";
string filename;
cin >> filename;
theMemory->Load(filename);
cout << endl;
}
}

void SaveMemory(Memory* theMemory)
{
if ( theMemory != nullptr )
{
cout << endl << "Save Memory to: ";
string filename;
cin >> filename;
theMemory->Save(filename);
cout << endl;
}
}

void ToggleDebugMode ()
{
_debugMode = !_debugMode;
}

-- Electronics.h --

#ifndef ELECTRONICS_HEADER
#define ELECTRONICS_HEADER

// We only include memory for the type "byte" to be known
#include "Memory.h"

namespace CPU_4001
{
class Electronics
{

private:


// The function is static so we can just call it without
// needing to instantiate a copy of "Electronics"
static void Adder (
const bool& A, // Bit from top of sum
const bool& B, // bit from bottom of sum
const bool& Cin, // Carry from previous column
bool& Cout, // Carry out
bool& Sum, // Sum of binary addition
const bool& p_Debug = false);


public:

// We're going to add both bytes 
// inside their own memory, hence
// they're passed in by reference
static void AddTwoBytes (
byte& p_Register0,
byte& p_Register1,
byte& p_Result,
bool& p_Overflow,
const bool& p_Debug = false);

// We saw the adder trace table, 
// this function lets us put it
// to the test!
static void TestAdder();

// Test routine for the adding of bytes
//static void TestAddTwoBytes();
};
}

#endif

-- Electronics.cpp --

#include "Electronics.h"

#include <iostream>
#include <bitset>

namespace CPU_4001
{

// The trace table for the adder:
// A  B  Cin  |  Cout  Sum
// 0  0  0    |  0     0
// 0  0  1    |  0     1
// 0  1  0    |  0     1
// 0  1  1    |  1     0
// 1  0  0    |  0     1
// 1  0  1    |  1     0
// 1  1  0    |  1     0
// 1  1  1    |  1     1
void Electronics::Adder(
const bool& A,
const bool& B,
const bool& Cin,
bool& Cout,
bool& Sum,
const bool& p_Debug)
{
// The logic here you can follow,
// but it comes from an electronic
// diagram of an adder :)
Sum = Cin ^ (A ^ B);
Cout = (A & B) | (Cin & (A ^ B));

if ( p_Debug )
{
std::cout << "Adder: " << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << std::endl;
}
}

// Tests the adder
void Electronics::TestAdder()
{
using namespace std;

bool Cout = false;
bool Sum = false;

// 0 0 0
bool A = false;
bool B = false;
bool Cin = false;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

// 0 0 1
A = false;
B = false;
Cin = true;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

// 0 1 0
A = false;
B = true;
Cin = false;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

// 0 1 1
A = false;
B = true;
Cin = true;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

// 1 0 0
A = true;
B = false;
Cin = false;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

// 1 0 1
A = true;
B = false;
Cin = true;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

// 1 1 0
A = true;
B = true;
Cin = false;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

// 1 1 1
A = true;
B = true;
Cin = true;
Adder (A, B, Cin, Cout, Sum);
cout << A << " " << B << " " << Cin << " | " << Cout << " " << Sum << endl;

cout << endl;
}

void Electronics::AddTwoBytes (
byte& p_Register0,
byte& p_Register1,
byte& p_Result,
bool& p_Overflow,
const bool& p_Debug)
{
bool l_CarryIn = false;
bool l_CarryOut = false;
bool l_Sum = false;

// For each bit we need to mask the
// right most bit out of the register
// meaning, we start at 00000001 and
// for each loop move the register
// so the bit we're interested is over
// the 8th position.


// Our mask never changes
byte l_mask = 0x01;

// For each bit we run the masking 
// then adder and handle switching
// the result into the register.
// You can find more efficient ways!
for (int i = 0; i < 8; ++i) // 8 bits in a byte
{
if ( p_Debug )
{
std::cout << "Cycle: " << i << std::endl;
std::bitset<8> msk { l_mask };
std::cout << "Mask: " << msk << std::endl;
std::bitset<8> reg0 { p_Register0 };
std::bitset<8> reg1 { p_Register1 };
std::cout << "Register 0 [" << reg0 << "]" << std::endl;
std::cout << "Register 1 [" << reg1 << "]" << std::endl;
}

// Get the A & B bits by shift & masking
// the register
bool A = ( ( ( p_Register0 >> i ) & l_mask) == 1);
bool B = ( ( ( p_Register1 >> i ) & l_mask) == 1);

// We have the carry in and the A & B now, so
// we can call the adder
// Because the Carry out, and the Sum, are separate
// in our code here, we don't need to alter "reg0" or
// "reg1", we can just logically add the bits set
// into the p_Result below!
Adder(A, B, l_CarryIn, l_CarryOut, l_Sum, p_Debug);

if ( p_Debug )
{
// This should be a value from our Adder trace table!
std::cout << "Adding: " << A << " " << B << " " << l_CarryIn << " | " << l_CarryOut << " " << l_Sum << std::endl;
}

// The carry out simply becomes the carry in
// I'm sure you can see one way to optimise this already!
l_CarryIn = l_CarryOut;

// Now the register change based on sum, but
// we also output the binary
if ( p_Debug )
{
std::bitset<8> resultBefore { p_Result };
std::cout << "Result Change: " << resultBefore << " -> ";
}

// Now the logic
// Now instead of pushing the logical
// summing into "Register0" parameter,
// we push it into the p_Result parameter!
if ( l_Sum )
{
// Mask is shifted, and always 1 in the i position
// so we always add a 1 back into the target
// register in the right location
p_Result = p_Result | ( l_mask << i);
}
else
{
// We know the mask is ON, so inversing it and moving it
// will give us an always off...
p_Result = p_Result & ~(l_mask << i);
}

// The register changed, so finish the debug statements
if ( p_Debug )
{
std::bitset<8> resultAfter { p_Result };
std::cout << resultAfter << std::endl;
}
}

// The final carry out becomes our
// over flow
p_Overflow = l_CarryOut;
}

// Out test is now invalid, so we'll just remove it
// from our code.
/*void Electronics::TestAddTwoBytes ()
{
using namespace std;

byte A = 0;
byte B = 0;
bool Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;

A = 0;
B = 1;
Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;

A = 60;
B = 14;
Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;

A = 42;
B = 42;
Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;

A = 128;
B = 16;
Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;

A = 200;
B = 50;
Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;

A = 250;
B = 60;
Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;

A = 255;
B = 255;
Overflow = false;

cout << (int)A << "\t+\t" << (int)B << "\t=";
AddTwoBytes(A, B, Overflow);
cout<< (int)A;
if ( Overflow )
{
cout << " it over flowed!";
}
cout << std::endl;
}*/

}

-- Memory.h --

#ifndef CPU_MEMORY
#define CPU_MEMORY

#include <string>

namespace CPU_4001
{

// Because C++ does not work directly in byte sized memory as numbers
// we'll use the byte sized character type as our byte of memory.
typedef unsigned char byte;

class Memory
{
public:

/// A constant we're going to define which
/// tells us the maximum address we can
/// read or write from.
const byte c_MaxAddress;

private:

/// This is the memory space we're going to
/// be using.
byte* m_MemorySpace;

public:

/// Construct a Memory class instance
/// for us, and clear the memory
Memory();

/// Delete the memory class, releasing
/// all the allocated memory space
~Memory();

/// Function to clear the memory values all to zero
void Clear();

/// Function to read the given address value
const byte& Read (const byte& p_Address);

/// Function to write the value to the given address
void Write (const byte& p_Address, const byte& p_Value);

/// Save to file
void Save(const std::string& p_Filename);

/// Load from file
void Load(const std::string& p_Filename);

};

}

#endif

-- Memory.cpp --

#include "Memory.h"

#include <iostream>
#include <fstream>
#include <cstdlib>

namespace CPU_4001
{

Memory::Memory()
:
c_MaxAddress(255), // The maximum constant
m_MemorySpace (new byte[c_MaxAddress]) // The memory
{
Clear(); // Our only function, clears the memory
// in C++ we allocate the variables in the
// constructor header, NOT here in the 
// Constructor body!
}

Memory::~Memory()
{
if ( m_MemorySpace != nullptr )
{
delete[] m_MemorySpace;
m_MemorySpace = nullptr;
}
}

void Memory::Clear()
{
for (byte i = 0; i < c_MaxAddress; ++i)
{
m_MemorySpace[i] = 0;
}
}

const byte& Memory::Read(const byte& p_Address)
{
return m_MemorySpace[p_Address];
}

void Memory::Write(const byte& p_Address, const byte& p_Value)
{
m_MemorySpace[p_Address] = p_Value;
}

void Memory::Save(const std::string& p_Filename)
{
using namespace std;
ofstream file (p_Filename, ios_base::out);
if ( file.good() )
{
for (byte i = 0; i < c_MaxAddress; ++i)
{
file << (int)m_MemorySpace[i] << endl;
}

file.close();
}
else
{
cout << "Bad path [" << p_Filename << "]" << endl;
}
}

void Memory::Load(const std::string& p_Filename)
{
Clear();

using namespace std;
ifstream file(p_Filename, ios_base::in);
if ( file.good() )
{
int i = 0;
int temp;
while ( file.good() )
{
string buff;
//cout << "Load [" << i << "]" << endl;
file >> buff;
//cout << "Loaded [" << buff << "]" << endl;
temp = atoi(buff.c_str());
//cout << "atoi [" << (int)temp << "]" << endl;
m_MemorySpace[i] = (byte)temp;
++i;
}


file.close();
}
else
{
cout << "File not found [" << p_Filename << "]" << endl;
}
}
}

-- CPU.h --

#ifndef CPU_PROCESSOR
#define CPU_PROCESSOR

#include "Memory.h"
#include <string>

namespace CPU_4001
{

class CPU
{
public:

const byte c_ReservedAddress;
const byte c_BaseAddress;
const byte c_JumpToAddress;
const byte c_AddressCeiling;

private:

byte m_ProgramCounter;
byte m_Accumulator;
byte m_Counter;
byte m_Register0;
byte m_Register1;
bool m_OverflowError;
bool m_UnderflowError;
bool m_SignedMode;
bool m_Halt;
bool m_DebugMode;

Memory* m_TheMemory;

const byte Fetch();

void Decode(const byte& p_OpCode);

void Halt();

void Add();

void Multiply();

void Beep();

void Store();

void Print();

void ClearRegister0();

void ClearRegister1();

void ClearBoth();

void JumpTo();

void JumpEqual();

// DOH, ALWAYS SAVE YOUR HEADER AND SOURCE FILES!
void Copy0();

void Copy1();

void Log(const std::string& p_Message);

void Load0();

void Load1();

void ClearAccumulator();

void LoadAccumulatorFromRegister0();
void LoadAccumulatorFromRegister1();
void SaveAccumulatorToRegister0();
void SaveAccumulatorToRegister1();

void ClearCounter();

public:

CPU(Memory* p_TheMemory, const bool& p_DebugMode = false);

~CPU();

void Reset();

void Run();

};

}

#endif

-- CPU.cpp --

#include "CPU.h"

#include <iostream>

#include "Electronics.h"

namespace CPU_4001
{

CPU::CPU(Memory* p_TheMemory,
const bool& p_DebugMode)
:
c_ReservedAddress(0),
c_BaseAddress(2),
c_JumpToAddress(1),
c_AddressCeiling(253),
m_ProgramCounter(c_BaseAddress),
m_Accumulator(0),
m_Counter(0),
m_Register0(0),
m_Register1(0),
m_OverflowError(false),
m_UnderflowError(false),
m_SignedMode(false),
m_TheMemory(p_TheMemory), // DOH!
m_Halt(false),
m_DebugMode(p_DebugMode)
{
}

CPU::~CPU()
{
m_TheMemory = nullptr;
}

void CPU::Reset()
{
m_Halt = false;
m_ProgramCounter = c_BaseAddress; // FIX!
m_OverflowError = false;
m_UnderflowError = false;
m_SignedMode = false;
}

const byte CPU::Fetch()
{
byte l_opCode = 0;
l_opCode = m_TheMemory->Read(m_ProgramCounter); // Whoops!
++m_ProgramCounter;
if ( m_ProgramCounter > c_AddressCeiling )
{
Halt();
}
return l_opCode;
}

void CPU::Halt()
{
if ( m_DebugMode )
{
Log("Halt");
}
m_Halt = true;
}

void CPU::Add()
{
if ( m_DebugMode )
{
Log("Add");
std::cout << "Before [" << (int)m_Register0 << ", " << (int)m_Register1 << "]" << std::endl;
}

//m_Register0 = m_Register0 + m_Register1;

Electronics::AddTwoBytes( // Binary add
m_Register0, // Register 0
m_Register1, // And Register 1 (Saving into reg0)
m_Accumulator, // Doh, we add into the accumulator!
m_OverflowError); // Set the overflow bit in the CPU

if ( m_DebugMode )
{
std::cout << "After [" << (int)m_Register0 << ", " << (int)m_Register1 << "]" << std::endl;
if ( m_OverflowError )
{
std::cout << "Overflow occurred!" << std::endl;
}
}

}

void CPU::Multiply()
{
if ( m_DebugMode )
{
Log("Add");
std::cout << "Before [" << (int)m_Register0 << ", " << (int)m_Register1 << "]" << std::endl;
}

ClearCounter();
ClearAccumulator();
for ( ; m_Counter < m_Register1; ++m_Counter)
{
Electronics::AddTwoBytes(
m_Accumulator,
m_Register0,
m_Accumulator,
m_OverflowError);

if ( m_OverflowError ) break;
}

// So, we add the register 0, to accumulator
// for register 1 times

// We always start from zero in the
// accumulator

// And really in a real chip the "for" loop
// here would be a logical operation


if ( m_DebugMode )
{
std::cout << "After [" << (int)m_Register0 << ", " << (int)m_Register1 << "]" << std::endl;
// Why my overflow debug gone?... Grrr
if ( m_OverflowError )
{
std::cout << "overflowed!" << std::endl;
}
}
}

void CPU::Beep()
{
if ( m_DebugMode )
{
Log("Beep");
}
std::cout << '\a';
}

void CPU::Store()
{
if ( m_DebugMode )
{
Log("Store");
}

// Load the target address into register 1
m_Register1 = m_TheMemory->Read(m_ProgramCounter);

if ( m_DebugMode )
{
std::cout << "Target Address: " << (int)m_Register1 << std::endl;
std::cout << "Value to Write: " << (int)m_Register0 << std::endl;
}

++m_ProgramCounter; // Skip the memory location data
// Write the register 0 value to this address
m_TheMemory->Write(m_Register1, m_Register0);
// Remember the order of our parameters
// was ADDRESS then VALUE!

if ( m_DebugMode )
{
std::cout << "Written value: " << (int)m_TheMemory->Read(m_Register1) << std::endl;
}
}

void CPU::Print()
{
if ( m_DebugMode )
{
Log("Print");
}

// Load the target addressinto register 1 // DOH!
m_Register1 = m_TheMemory->Read(m_ProgramCounter);
++m_ProgramCounter;

// The value in the register is now the value to load
m_Register0 = m_TheMemory->Read(m_Register1);

if ( m_DebugMode )
{
std::cout << "Printing from: " << (int)m_Register1 << " -> value: " << (int)m_Register0 << std::endl;
}

// Output the register
std::cout << (int)m_Register0 << std::endl;
// I'm going to add endline!
}

void CPU::ClearRegister0()
{
if ( m_DebugMode )
{
Log("Clear 0");
}

m_Register0 = 0;
}

void CPU::ClearRegister1()
{
if ( m_DebugMode )
{
Log("Clear 1");
}
m_Register1 = 0;
}

void CPU::ClearBoth()
{
if ( m_DebugMode )
{
Log("Clear Both");
}
ClearRegister0();
ClearRegister1();
}

void CPU::JumpTo ()
{
if ( m_DebugMode)
{
Log("Jump To");
}

// Read the program counter location
// into the jump to address for use
m_TheMemory->Write(
c_JumpToAddress, // Whoops
m_TheMemory->Read(m_ProgramCounter));
++m_ProgramCounter;

if ( m_DebugMode )
{
std::cout << "Jump Register set to [" << (int)m_TheMemory->Read(c_JumpToAddress) << "]" << std::endl;
}

// Read back the jump to address
// as the program counter
m_ProgramCounter = m_TheMemory->Read(c_JumpToAddress);

if ( m_DebugMode )
{
std::cout << "Program Counter now [" << (int)m_ProgramCounter << "]" << std::endl;
}
}

void CPU::JumpEqual()
{
if ( m_Register0 == m_Register1 )
{
if ( m_DebugMode )
{
Log ("Jump Equal - Jumping");
}
JumpTo();
}
else
{
if ( m_DebugMode )
{
Log ("Jump Equal - Not Jumping");
}
// Skip over the address of the jump!
++m_ProgramCounter;
}
}

void CPU::Copy0()
{
if ( m_DebugMode )
{
Log("Copy0");
}

m_Register1 = m_Register0;
}

void CPU::Copy1()
{
if ( m_DebugMode )
{
Log("Copy1");
}
m_Register0 = m_Register1;
}

void CPU::Log(const std::string& p_Message)
{
std::cout << p_Message << std::endl;
}

void CPU::Load0()
{
if ( m_DebugMode )
{
Log("Load0");
}

// Load the address
m_Register0 = m_TheMemory->Read(m_ProgramCounter);

//std::cout << "Register 0 Load Address [" << (int)m_Register0 << "]" << std::endl;

// Skip past the address data
++m_ProgramCounter;
// Read from memory
m_Register0 = m_TheMemory->Read(m_Register0);

//std::cout << "Register 0 [" << (int)m_Register0 << "]" << std::endl;
}

void CPU::Load1()
{
if ( m_DebugMode )
{
Log("Load1");
}

// Load the Address
m_Register1 = m_TheMemory->Read(m_ProgramCounter);

//std::cout << "Register 1 Load Address [" << (int)m_Register1 << "]" << std::endl;

// Skip past the address data
++m_ProgramCounter;
// Read frommemory
m_Register1 = m_TheMemory->Read(m_Register1);

//std::cout << "Register 1 [" << (int)m_Register1 << "]" << std::endl;
}

void CPU::ClearAccumulator()
{
m_Accumulator = 0;
}

void CPU::LoadAccumulatorFromRegister0()
{
m_Accumulator = m_Register0;
}

void CPU::LoadAccumulatorFromRegister1()
{
m_Accumulator = m_Register1;
}

void CPU::SaveAccumulatorToRegister0()
{
m_Register0 = m_Accumulator;
}

void CPU::SaveAccumulatorToRegister1()
{
m_Register1 = m_Accumulator;
}

void CPU::ClearCounter()
{
m_Counter = 0;
}

void CPU::Decode(const byte& p_OpCode) // DOH!
{
// Special, when the system has pre-halted in Fetch
// We do not decode!
if ( m_Halt ) return;


switch (p_OpCode)
{
// HALT
case 0:
Halt();
break;

// Load 0
case 1:
Load0();
break;

// Load 1
case 2:
Load1();
break;

// Add
case 3:
Add();
break;

// Beep
case 4:
Beep();
break;

// Store operation
case 5:
Store();
break;

// Print operation
case 6:
Print();
break;

// Clear Register 0
case 7:
ClearRegister0();
break;

// Clear Register 1
case 8:
ClearRegister1();
break;

// Jump to
case 9:
JumpTo();
break;

// Jump Equal
case 10:
JumpEqual();
break;

// Copy 0
case 11:
Copy0();
break;

// Copy 1
case 12:
Copy1();
break;

// Clear both
case 13:
ClearBoth();
break;

// Multiply
case 14:
Multiply();
break;

// Load Acc from Reg0
case 20:
LoadAccumulatorFromRegister0();
break;

// Load Acc from Reg1
case 21:
LoadAccumulatorFromRegister1();
break;

// Save acc to reg 0
case 22:
SaveAccumulatorToRegister0();
break;

// Save acc to reg 1
case 23:
SaveAccumulatorToRegister1();
break;

// Clear accumulator
case 24:
ClearAccumulator();
break;

// Clear Counter
case 25:
ClearCounter();
break;

default:
Log("Unknown Op code - halting");
Halt();
break;
}
}

void CPU::Run()
{
// While we don't see a HALT, keep going
while (!m_Halt)
{
if ( m_DebugMode )
{
std::cout << "[" << (int)m_ProgramCounter << "]" << std::endl;
}

// Fetch into the reserved memory
// location
m_TheMemory->Write(c_ReservedAddress, Fetch());

// Decode from the memory reserved address
Decode(m_TheMemory->Read(c_ReservedAddress));
}
}

}

No comments:

Post a comment