What's new

Game Boy

CodeSlinger

New member
Wow fast reply, thanks :)

I've been looking into how the game is loaded into memory. The game gets loaded into memory address 0 to 7FFF.

The first 0x0 - 3FFF of this memory is called "16KB ROM Bank #0" which i presume never gets switched. Now its the 4000 - 7FFF bytes im stuck on which is called "16KB switchable ROM Bank".

I know that we can determine whether MBC1 is turned on by looking at a byte in the rom header.

How does the game switch in and out of pages? Is there an opcode which specifies which page to use?

So for example, if i load the entire game into a 2d byte array like so.

Code:
BYTE m_GameMemory[NUM_PAGES][0x4000] ; // the entire game stores as pages
BYTE m_Rom[0xFFFF] ; // the game gets loaded into the first 8000 bytes


Once i have loaded in the game to m_GameMemory I then copy m_GameMemory[0] to m_Rom[0]
so bytes 0x0 - 0x3FFF in m_Rom is the first page of the game memory (this is "16KB ROM Bank #0"). I then copy m_GameMemory[1] to m_Rom[0x4000] so bytes 0x4000 - 0x7FFF in m_Rom are the same as page1 in the m_GameMemory (this is "16KB switchable ROM Bank").

Whenever the game then switches pages I then copy m_GameMemory[newPageNum] to m_Rom[0x4000] so bytes 0x4000 - 0x7FFF are the same as m_GameMemory[newPageNum]. This will always be switching "16KB switchable ROM Bank" memory and never switching "16KB ROM Bank #0".

Is this correct or am i way off?

Thanks again for your help.

Edit:

I've just read the following:

"MBC1 has two different maximum memory modes:
16Mbit ROM/8KByte RAM or 4Mbit ROM/32KByte RAM."

Does this mean the page sizes are only 8KB? If so then how does that fit into the address space 0x4000 - 0x7FFF which is 16KB.

Thanks
 
Last edited:

HyperHacker

Raving Lunatic
Bank switching is done by writing the bank # to anywhere in the range 0x2000 to 0x3FFF. You'd want to load the entire game into a 2D array of NUM_PAGES x 0x4000, and use pointers for switching. Copying data around every time it switches is going to be very slow; some games switch several times per frame.

The memory size setting affects how many pages of ROM and RAM a cartridge can have. The mapping doesn't change.
 

CodeSlinger

New member
Hyperhacker, you're being a great help mate, thanks!

I believe I understand now. I'll just get it confirmed with you.

So if i have a member variable m_CurrentRomBank which is set to 1 on CPU reset then if the game is using MBC1 and wishes to change rom bank then it will attempt to write a byte to a memory location between 2000 and 0x3FFF.

so my code would look something like this: (where the variable data is the byte to be written to memory)

Code:
// rom bank 0 and rom bank 1 both point to rom bank 1
if (data == 0)
data++ ;

// we are only interested in the least significant 5 bits of data
data &= 31;

m_CurrentRomBank = data ;

then i just change the pointer to point to the current rom bank?

Does this all sound correct?

I presume I change ram banks in the same way except with different memory addresses and the ram has to be enabled.

Thanks again mate
 

HyperHacker

Raving Lunatic
That looks right. IIRC, RAM bank switching is writing to 4000-5FFF, but don't quote me on that. You might want to just log ROM writes to see what area it is.

I'd just look it up in Martin Korth's documentation, but the site is down. :(
 

CodeSlinger

New member
I've been off and on the emulator for a few weeks now (mostly off due to other commitments) . I've managed to get most of the CPU emulated, enough to emulate a few roms. However I've done no graphical emulation yet as it looked difficult so I've been avoiding it. Having a closer look at the graphical emulation it really doesnt seem that difficult at all so soon i'll be taking a stab at it but I just need a few things cleared up first.

1. I dont understand the purpose of the monochrome palette located at address 0xFF47. This register assigns shades to colour numbers for the BG and window tiles. Well whats the point in this? Surely the shades are static so this register never needs to be addressed. I understand how a colour of a pixel is determed by looking at two bytes in the Tile Data area of memory and combining the bits to form a 2 Bit colour index. Where 00 = white. 01 = light gray. 10 = dark gray. 11 = black.

So once you find out the 2 bits to represent the pixel colour you already know what the colour is so whats the point in the monochrome palette register?

2. I understand how the scrolling registers work but i just thought id make sure that the scrolling registers cannot be changed unless it is during a V Blank period. Otherwise if the emulator is half way through drawing the scan lines (so the LY register is currently 72) and the Scroll register changes, then the second half of the draw scans wont match the top half, so then there would be tearing. Is this correct?

3. The way I understand the drawing of the screen works is that every time the LY register gets incrememnted it draws 1 line of pixels (160). How do I determine where to get the information for the current pixel im drawging from? I just need to understand how to determine if the pixel is a Background pixel, a window pixel or a sprite pixel, whilst also taking into account priority.

Thanks guys.
 

HyperHacker

Raving Lunatic
For perfect accuracy you'd want to draw one pixel every x cycles, but I think you'd get away with drawing one scanline at a time; possibly a few uber tech demos would break, but actual games would work. You can change scroll registers in Hblank (see wavy effects from certain attacks in Pokemon), not sure what happens if you try to change them during redraw.

For pixel colours, they aren't hardcoded like that. 00 = colour #0, 01 = colour #1, etc. FF47 sets which colours are assigned to which numbers. This lets you do colour-swap effects without having to iterate through all your graphics. (See pause effects in Metroid 2.) IIRC you can even assign the same colour to multiple indices, which Metroid 2 might also be doing.
 

CodeSlinger

New member
Ok. I'm a bit confused by the H-Blank, im guessing it is like this:

When the gameboy is drawing a scanline H-Blank is set to false. However when it is not drawing a scanline it is set to true because if it is not drawing the scanline then it must be moving on to the start of the next scanline which is the H-Blank period. However H-Blank is never set if the gameboy is in a V-Blank?

As I said earlier I draw the scanline all in one go. Which means surely im always in a H-Blank state unless im in a V-Blank state.

Does that sound right?
 

HyperHacker

Raving Lunatic
The LCD controller emulates a TV's scanning beam. HBlank is the time when the beam has finished drawing a line and is moving back to the other side of the screen. VBlank is when it's done drawing the frame and is moving back to the top. Drawing, VBlank, and HBlank periods always last a specific number of CPU cycles (documented in GBTek linked a few posts ago); most emulators just count cycles before switching modes, and draw a scanline whenever HBlank or VBlank occurs.

You do need to emulate this even if you're drawing entire scanlines at once, as some games depend on it. Video memory is not accessible during redraw periods.
 

Exophase

Emulator Developer
Felt like posting this, although I don't have anything I can actually show right now...

A week and a half ago I asked some friends of mine if they wanted to have a Gameboy emulator writing competion. At first it was actually just me and Lordus, author of jEnesisDS (Genesis emulator for Nintendo DS), but then DS scene programmer sgstair and another friend of mine joined up. sgstair was pretty confident he could get it done in 2 days so the tension was turned up about 20 notches and I was intent on giving it everything I got.

The competition started at 2:00 PM EST Sunday June 2, and the goal was to get the following five games working:

Super Mario Land
Super Mario Land 2
Kirby's Dreamland
Legend of Zelda: Link's Awakening
Tetris

For the first three games you had to be able to clear the first level, for Zelda you had to get the sword, and for Tetris you had to clear one of the "B" levels.

The sound and battery back up was supposed to work (although really with the goals listed the latter wasn't really pertinent to accomplishing them)... The sound was supposed to be "perfect." This was what ended up screwing us all over.

This is roughly how it turned out...

Me: Had the CPU/timer/div/IRQ emulation written in about 5 hours, then got rudimentary BG rendering written and spent the next 6-7 hours debugging it (using BGB to help follow along). Got first screenshot from Tetris around 13 hours in (title screen only), and had in-game some minutes later. Then spent the next 5 or so hours debugging the 5 games until they all worked correctly, so had things working at about 18 hours. Was slowing down a lot at this point; had to implement sound now (but was really tired). I figured this would just be busy work since I already emulated this for my GBA emulator. I ended up getting the first three channels done (no sweep), but for reasons that I haven't really determined it just sounded buggy. I pretty much stopped here, calling the emulator "close enough" and went to sleep (more on what I did with this later).

sgstair: Got results in the first day as well, but trailed behind my by some hours, then went to sleep. After he woke up he started doing sound and got it working for the first two channels, and it sounded more correct than my implementation (but still not perfect, and some games lacked it entirely, probably because of missing timer IRQs). Since we were roughly at about the same point and didn't feel like grinding to get the sound perfect we agreed upon a tie.

Lordus: Could only work five hours before sleeping the first day (time zone differences), after coming back the second day I believe he got some results in games. Generally had less time to work on it, but by now he does have things working in the games and sound is half done, but with some glitches.

I'm not totally sure where my other friend is, but since this is his first emulator it's more of a learning experience for him.

Anyway, I poked at my source after agreeing on the tie, but eventually decided I didn't feel like fixing the sound code so I just ripped it straight out of gpSP (my GBA emulator) and it took like an hour to fully transition and for the most part it sounded great. I did find a few bugs, some core related, some sound related, so it wasn't a total loss since it gave me an opportunity to fix some audio bugs in gpSP. I also dove into some other games and fixed CPU bugs, added battery back up, more hardware support, etc. I beat SML1 and Kirby's Dreamland in it, made it most of the way through Zelda (but I lost my playthrough of dungeon 7 because of a power outage), made it halfway through Final Fantasy Adventure before realizing that I didn't mark the hardware type as being battery backed and lost everything, and made it to the top of level 3 in Castlevania Adventure when my emulator crashed. Not trying that one again w/o savestates, but I probably fixed it by now.

EDIT: Some screenshots:

gamble1.png
gamble2.PNG
gamble3.png
gamble4.PNG

gamble5.PNG
gamble6.png
gamble7.PNG
 
Last edited:

CodeSlinger

New member
Thanks again HyperHacker.

ExoPhase great job on your GameBoy emu. It's looking really impressive.

Hopefully over the weekend i'll have my emulator displaying at least the bg tiles, if not the windows and sprites!

I havent looked at input emulation yet and as far as sound emulation goes I honestly have NO idea. Apprently it's the most difficult part.
 

CodeSlinger

New member
Thanks for all your help guys. I've managed to get my emulator to the state where apart from sound tetris is now completely finished.

I do seem to be having one probablem with the game bubble ghost. The emulator gets stuck servicing the following instructions:

Code:
rom: 0x394 :ld a, (C0F0)
rom: 0x397: or a
rom: ox398: jr z, 0394

which means load into register a the contents of memory addres 0xC0F0. Logically or register a with itself to set flags (the z flag is the interested flag). If the z flag is set them jump back to the first load instruction and continue until the z flag is not set (i.e. the contents of 0xC0F0 is not 0)

Now if the CPU is busy stuck in this loop then how is the contents of 0xC0F0 going to get changed?

I assumed it could be something to do with an interupt that loads something into 0xC0F0 but ive implemented all interupts and this doesnt help.

Thanks for any help
 

Exophase

Emulator Developer
Thanks for all your help guys. I've managed to get my
Now if the CPU is busy stuck in this loop then how is the contents of 0xC0F0 going to get changed?

I assumed it could be something to do with an interupt that loads something into 0xC0F0 but ive implemented all interupts and this doesnt help.

Thanks for any help

Like you said it's waiting on an interrupt to change it, this is typical form for an "idle loop." There must be a bug with when various interrupts are supposed to be triggered or something in executing the service routine itself that's preventing this from triggering. I'll check it out later today to see where that address is supposed to be written to.
 

CodeSlinger

New member
Thanks for the reply.

It turned out to be the timer interupt. I was accidentally requesting bit 3 not bit 2. Whoops.
Bubble ghost now works up until the ghost appears at the beginning of the game. I havent implemented all opcodes yet so it breaks.

Thanks again
 

CodeSlinger

New member
Hi Guys,

Time for a quick update.

So far my emulator has about 30% compatibility. Links awakening is probably the most advanced game it plays. Although the graphics are glitchy and the lightning at the beginning goes horizontally :) it is actually quite playable.

Im starting to bang my head at whats killing the other games though. Even games that are apparently easier to emulate than links awakening (like bubble ghost) have issues that I cant seem to resolve. Some games play up to a point and others dont even show the first screen like most of the final fantasy games.

I think my cpu is very close to being bug free with the only issues I think I have is to do with the half carry flag.

So im really stuck with what the problem could be. The only thing I think i've implemented wrong is the timers and dividers. Im guessing this could kill compat so i'll have to give it ago.
 

HyperHacker

Raving Lunatic
Yeah, timers are pretty important. I'd also look at the interrupt handling, any glitches there could break all sorts of things.

What exactly does the divider do anyway? Just increment at 16384hz? Why would that be called a divider?
 

Top