codeslinger.co.uk

Gameboy - Memory Control and Map.

Memory Map:

Taken from nocash-s pandocs we have the following memory map:

0000-3FFF 16KB ROM Bank 00 (in cartridge, fixed at bank 00)
4000-7FFF 16KB ROM Bank 01..NN (in cartridge, switchable bank number)
8000-9FFF 8KB Video RAM (VRAM) (switchable bank 0-1 in CGB Mode)
A000-BFFF 8KB External RAM (in cartridge, switchable bank, if any)
C000-CFFF 4KB Work RAM Bank 0 (WRAM)
D000-DFFF 4KB Work RAM Bank 1 (WRAM) (switchable bank 1-7 in CGB Mode)
E000-FDFF Same as C000-DDFF (ECHO) (typically not used)
FE00-FE9F Sprite Attribute Table (OAM)
FEA0-FEFF Not Usable
FF00-FF7F I/O Ports
FF80-FFFE High RAM (HRAM)
FFFF Interrupt Enable Register

As explained earlier the internal memory only has room for 0x8000 (0x0000 - 0x7FFF) of the game memory. However most games are bigger in size than 0x8000 which is why memory banking is needed. The first 0x4000 bytes is where the first 0x4000 (0000-0x3FFF) (Rom bank 0) bytes of the game memory is placed. The second 0x4000 (0x4000 - 0x7FFF) is also for the game memory however this area is the rom bank area so depending on the current state of the game this memory area will contain different game memory banks. Banking is discussed in much more detail in the Rom and Ram Banking section. Memory area 0xA000-0xBFFF is also for banking but this time it's ram banking. Once again I'll discuss this is more detail in the next section. The ECHO memory region (0xE000-0xFDFF) is quite different because any data written here is also written in the equivelent ram memory region 0xC000-0xDDFF. Hence why it is called echo. You maybe wondering if writing to RAM memory also writes its data to the ECHO memory. The answer is I dont know. I'd imagine that it did however whenever I emulated this it never worked correctly so I took it out and have never had any problems. The HRAM is where the stack stores its data. The stack pointer starts at 0xFFFE and works its way down in memory whenever something is pushed onto the stack.

I will discuss the rest of the regions as needed in the following sections.

Memory Control:

Now we have a breakdown of the internal memory it becomes quite obvious that read and write access to the memory needs to be well controlled. For instance the first 0x8000 bytes are read only so nothing should ever get written there. Also anything that gets written to ECHO memory needs to be reflected in work RAM. Also when reading from one of the banks it is important that it gets read from the correct bank. The best way to control read and write access to the memory is creating a ReadMemory function and a WriteMemory function. Whenever you read and write to memory you MUST use these two functions, this way you will be certain your memory access will be controlled correctly. The only exception to this is when you initialize Rom memory which was shown on the previous section Hardware. Below is an example of how the WriteMemory will help you control memory access:

void Emulator::WriteMemory(WORD address, BYTE data)
{
   // dont allow any writing to the read only memory
   if ( address < 0x8000 )
   {
   }

   // writing to ECHO ram also writes in RAM
   else if ( ( address >= 0xE000 ) && (address < 0xFE00) )
   {
     m_Rom[address] = data ;
     WriteMemory(address-0x2000, data) ;
   }

   // this area is restricted
   else if ( ( address >= 0xFEA0 ) && (address < 0xFEFF) )
   {
   }

   // no control needed over this area so write to memory
   else
   {
     m_Rom[address] = data ;
   }
}

This is a very simplified version of the WriteMemory function but it should give you a basic idea of the importance of it. This function will expand throughout the other sections. The ReadMemory is discussed in more detail in the next section Rom and Ram Banking