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