HOME
PROJECTS
LOOK AT MORE TUTORIALS
Emulation Document Part The Second - Memory Emulation
Author: zenogais Difficulty Level: Newbie


Beginning Emulator Writing

    When you first begin your emulator the first thing you'd usually do is choose a language. There are many many programming languages out there each with their own strengths and weaknesses. My personal favorite is C++, but emulators have been writing in various other languages. Of course since C++ is my language of choice the code contained in this tutorial will also be C++ code. Now you've picked your langauge right? Good because its time to start discussing
    Whenever you begin an emulator there are always certain things which must be emulated first. From my experience these are the memory and CPU subsystems. These two systems are the heart of most emulators, and taking your time to write them well can have a huge pay-off in the end. After these two subsystems are created, all the other subsystems can be built and used effectively. Now lets go into a little more detail.

The Memory Subsystem

    The memory subsystem is relied upon by just about every part of your emulator. The CPU retrieves opcodes and data to work on from memory, the graphics device retrieves textures, the sound devices retrieves encoded sounds. It is basically the core of your emulator. Memory is emulated usually by controlling access when someone is either reading or writing to it. This is done by creating functions or classes which act as interfaces to the memory subsystem by utilizing functions you can map memory and control access to memory mapped devices. The typical memory interface would look something like this:

 

class EmulatedConsoleMemory
{
public:
	//////////////////////////////////////////////////////////////////////////////////////
	// Constructors
	
	/** The console memory will be initialized here.
	  */
	EmulatedConsoleMemory()
	{
		mConsoleMemory = new unsigned char[CMS_MEMORY_SIZE];
		assert(mConsoleMemory != NULL);
	}

	//////////////////////////////////////////////////////////////////////////////////////
	// Destructors
	
	/** The console memory will be deallocated here.
	  */
	~EmulatedConsoleMemory()
	{
		if(mConsoleMemory != NULL)
		{
			delete [] mConsoleMemory;
			mConsoleMemory = NULL;
		}
	}

	//////////////////////////////////////////////////////////////////////////////////////
	// Byte Reads and Writes
	
	/** Writes a byte of data to memory.
	  */
	void WriteByte(unsigned short address, unsigned char data)
	{
		mConsoleMemory[(address&0xFFFE)] = data;
	}
	/** Reads a byte of data from memory.
	  */
	unsigned char ReadByte(unsigned short address) const
	{
		return mConsoleMemory[(address&0xFFFE)];
	}
private:
	//////////////////////////////////////////////////////////////////////////////////////
	// Private Enumerations
	
	enum ConsoleMemorySize
	{
		CMS_MEMORY_SIZE = 0xFFFF
	};
	
	//////////////////////////////////////////////////////////////////////////////////////
	//  Private Variables

	unsigned char* mConsoleMemory; ///< A pointer to the allocated block of memory.
};
					

    Before I go any farther I'd like to state that this example has a flaw. It violates a programming principle known as the DRY(Don't Repeat Yourself) principle. This principle simply states that you should avoid needless repetition at all costs, because it only makes code harder to change in the future. There are many ways to emulate the memory mappings and arrangements of the various systems out there, in this article I'll try to present the ones that I've seen used in order to aid you later on in emulator development when you will be forced to decide if you're memory emulation scheme is indeed violating the DRY principal.