IF YOU HAVE JUST ARRIVED HERE, PLEASE SEE: http://megalomaniacbore.blogspot.co.uk/2014/04/virtual-cpu-in-c-4001-cpu.html
-- main.cpp --
#include <iostream>
#include <exception>
#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;
}
-- 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_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();
public:
CPU(Memory* p_TheMemory, const bool& p_DebugMode = false);
~CPU();
void Reset();
void Run();
};
}
#endif
-- CPU.cpp --
#include "CPU.h"
#include <iostream>
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_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;
if ( m_DebugMode )
{
std::cout << "After [" << (int)m_Register0 << ", " << (int)m_Register1 << "]" << std::endl;
}
}
void CPU::Multiply()
{
if ( m_DebugMode )
{
Log("Add");
std::cout << "Before [" << (int)m_Register0 << ", " << (int)m_Register1 << "]" << std::endl;
}
m_Register0 = m_Register0 * m_Register1;
if ( m_DebugMode )
{
std::cout << "After [" << (int)m_Register0 << ", " << (int)m_Register1 << "]" << 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::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;
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