Skip to content

Understanding I O for the gameboy

Camilo Andrés Mella Lagos edited this page Jul 17, 2017 · 7 revisions

Coming from chip8 or any other lesser console than Gameboy you will inevitably find some oddities in the way things are done

For one, the memory has to be split in given sections or banks as each section store specific information.

This is by far the most daunting task when attempting to emulate a new system, you are not familiar with for the first time.

The idea for an MMU is quite simple, but you have to think about it logically in order to make sense out of it

First you must have containers for the target memory address and another one for where you want to save the information contained on a memory.

Second you need to understand that there will be times where you will want to READ and others when you will want to WRITE, from and to the memory. This may be giving you an idea of what you need and how to implement it... But this is just the MMU.

Now, what I meant with containers is this. Realistically you can't know whether a register, 8 bit inmediate or another entity will try to address the memory. That's why you have to account for every case.

Take in consideration that memory is a char type with values ranging from 0-255 on each element. As for the array length it should be 0xFFFF as maximum. NOW... this means the pointer to a memory address can be SHORT in length, but the container should be of char type. This will be really important when you try to wrap your head around those pesky LD (R), nn

So, to quick recap, you have your structure that allows you to read and write, your memory pointer and your container. You should be ready to tackle MMU management at a bare minimum, maybe you could do cout or printf to get some feedback back at what memory bank is it trying to read or write off to

When you think you are home free and understand everything there is to understand about the Gameboy, is when it throws you yet another curve ball.

I/O registers

As of now you've only dealt with CPU registers. and I know you think you got a good understanding of how they work and how you can harness they power to do your bidding, but then there are these I/O registers that seem to elude most people. Other discard them as... "Well i'm just gonna implement them later" while other go further still and think they are just tech nonsense and you don't really need them to get you emulator working.

Let me tell you here and now they are WRONG. I/O registers are a core part of the emulator.

I know well, that when you read I/O your first thought is INPUT as in gamepad support, but I/O is MUCH, MUCH more than that.

The Screen is a way of output, the sound, the timers, the clocks, the serial port etc etc... in chip8 it was easy we got around doing opcode specific stuff. I.e Opcodes that controlled input, opcodes in charge of drawing to the screen and this is all good, but it's far from the reality. Now you need to dig deeper into the complexities of the system, and I/O management is one of those

How would you go about managing I/O calls?

All I/O calls are mapped to an specific memory address and have their own set of rules for decoding the register

Let´s take for instance this opcode:

LD A, (C)

If you are confortable by now with this kind of opcode you will know it means Load the contents of memory addressed by register C to register A, but this WON´T work...

And why it doesn´t work is actually pretty simple.

When you started with chip8 you got the idea that the V[] registers were multipurpose and thus, it doesn't really matter the use you gave them, BUT! Gb is a different beast. Here the registers have a sole purpose

A: Accumulator register
F: Flag Storing
C: I/O Register Mode pointer register
D: Pointer

And you wouldn't know anything of this just by reading Cribsheet or the Pandocs as it's never really explained, but in the official documents it´s clear that C it´s not a multipurpose register, but rather a REALLY specific one

Now if you take a glance at the memory addresses the I/O register often work with, you will find stuff like this

FF01
FF02
FF10

So if you put them all of them in a list, you pretty much got the idea they operate in the I/O region (and Zero Page) of the memory from 0xFF00 to 0xFFFF

So let´s say you have a working MMU, you should be capable of catching these "attempts" to write or read from each I/O register

Now this is where it get tricky. You can have a fully working CPU, Flag handler and register combiner, but if you don't manage I/O calls. The emulator wouldn't know which color to display, at which position or at what time... or even if it should have the LCD screen turned on...

So in order to manage them first you should understand the control mechanism. Let's take a look to that opcode again

LD A, (C)

Now this opcode actually works like this:

Read the contents of the memory Address 0xFF00 + the value contained in I/O register handler C and store them into A

NOW! we are getting somewhere. We now have a way of targeting the I/O register, but how should we decode it?

Well every I/O registers has it own set of rules. So you should have cases that adress the state of the BITS you need to check in the register. Furthermore you are just reading at this point, put if you want to SELECT or make a register WORK differently, you need to be able to also WRITE into them

That´s why in most documents it says NR10 (R/W)

After a while you will get the hang of it, and this will be confirmation of your emulator really maturing.

As a rule for easy developement I always make veerbose programs that tell me what are they expecting, that way i never get stuck not knowing what should i implement next.

Try it the next time you stumble into a long non ending loop or some other problem you may encounter further down the road

As for me, my boss is giving me a weird look after this much typing haha, so i will cut this Wiki short for now :P

See you in the next one

]-[Dx