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