Memory and Memory-Mapped I/O of the Gameboy — Part 3 of a Series

In the second part of this series I was talking about the Gameboy’s CPU and how it can be emulated. The DMG CPU’s contact to the outside world is through memory and memory alone — at least that’s what it looks like to the CPU itself. Some of the memory is actually peripherals in disguise. I’m about to explore what this means.

The Address Bus

That’s the only access to the outside world for the CPU. Every time the CPU wants to access something other than its internal registers, it has to use the address bus. And the only operations possible on the address bus are read and write. That’s why I say everything looks like memory to the CPU — it might as well be all memory, the CPU wouldn’t be able to tell the difference.

Memory-Mapped IO

Now, I haven’t done much research in terms of the actual hardware of the Gameboy since, for an emulator, the only relevant information is where inside the address space each of these peripherals is located and how they’re supposed to behave.

The 16 bits of the address bus can be used to address up to 2¹⁶ bytes which may sound like a lot yet is actually only 64 Kilobytes. Each peripheral gets a dedicated region or location inside this 64 kB address space. For example: the master switch for the sound controller (sound on/off) is located at the address 0xFF26. If the CPU wants to turn the audio on or off it has to write to this exact location on the address bus. I want to emphasize that it makes no difference to the CPU whether it’s writing to this specific location or any other location in memory. In fact, the CPU has no concept of a sound controller at all. It’s just the developer of the currently running code who’s instructing the CPU to write to this location. To the CPU it’s all just writing and reading on the address bus. Some of the writing operations may put images on the screen, some may control sound output while others just write bytes into RAM.

Memory Map of the Gameboy

Memory map of the the original Gameboy

Let’s have a look at each one of these regions, starting at the bottom.

ROM and Switchable ROM Bank

As my goal for now is to just run Tetris, I’m not going to implement switchable ROM banks just yet.

Video RAM

Switchable RAM Bank

Internal RAM

Sprite Attributes

I/O

  • Video Controller
  • Sound Controller
  • D-Pad and Button Inputs
  • Serial Data Transfer via Link Cable
  • Timer

For now, I’ll just be implementing parts of the video controller. No sound yet, no button inputs, no serial data connection and no timer.

I’ll be talking about the video controller in more detail in my next post.

High RAM

Interrupt Register

And now let’s talk about interrupts.

Interrupts

What does this mean? We’ve seen that the CPU just executes whatever instruction the program counter (PC) points at. The counter is automatically incremented and it can be altered by certain instructions. That’s all about how a program is executed — except for interrupts. When an interrupt occurs, the current program counter is pushed onto the stack and a new value for the PC is set according to the interrupt. Each interrupt has its own specific starting point in memory. For example: when a button is pressed, an interrupt is requested and the PC is set to 0x0060, meaning the program continues its execution at this location. At the end of whatever code there is, a return from interrupt instruction is meant to be called which pops the original PC from the stack and continues the ordinary program flow.

Interrupt program flow

The button interrupt isn’t a good example, however, as it’s rarely used. Button states are usually read once per frame — which brings us to another, very important interrupt: the vertical blank interrupt. This interrupt is requested by the video controller and it occurs at the end of each full redraw of the display and therefor about 60 times a second. Game developers may think of this as the main loop to read the current state of the buttons and generate a new frame. This is the most important interrupt for me to implement in order to run Tetris.

To summarize: The main program flow is interrupted 60 times a second to indicate that a frame has been fully rendered and a new one may be generated next.

Besides the vertical blank interrupt and the button interrupt there are a few other display-related interrupts as well as a timer interrupt and an interrupt for serial data transfer.

This has been a very high level overview of the Gameboy’s memory mapping and it’s by no means meant as a complete description of the topics discussed. If you want to know more I highly recommend the already cited Pan Docs as well as other great resources that are out there. As I’ll be building hardware, too, I cannot focus on writing the emulator alone. That’s why my first goal is to run Tetris, wich will be done with my next post, and then I’ll be focussing on the hardware for a while. This also means that future posts may get more practical compared to these last two highly theoretical ones.

Thanks for reading, I hope you enjoyed it. If you have any questions or would like to know more about the project, feel free to leave a comment or contact me on Instagram or Twitter!

Software Developer and Entrepreneur at productionbuild.de