The Z80 CPU uses 16-bit addressing, which limits it to a 64KB address space. As well as fitting the program ROM into this space, we need to fit in the machine's 8KB RAM, limiting us even further.
To get around this limitation, the memory is broken down into a series of windows ("frames"), and you can change what is visible in some of these windows.
The memory range $C000..$DFFF can be used to address the 8KB RAM. This is mirrored from $E000..$FFFF; that is, reads and writes to $E000 work as if you were reading and writing to $C000.
There are three other windows; from $0000..$3FFF, $4000..$7FFF and $8000..$BFFF. By changing the contents of RAM (addresses $FFFC..$FFFF) you can adjust what is accessible from these memory ranges.
The lower 1KB cannot be paged out - this is because when the device boots the contents of RAM (and thus paging registers) are undefined, and you need something fixed in place to set up the machine correctly.
I had already implemented most of the above, with two omissions. The first was that certain cartridges contained their own additional RAM chips (accessible by setting flag bits in paging register $FFFC) that could be used as saved game area. Ys appears to use this area, and so didn't work without it. That was the garbled screenshot I posted earlier:
The Flash now works too.
The second omission was that Codemasters games use a different mapping system to the standard one. Fortunately it is significantly simpler; the 32KB from $0000..$7FFF is locked, and the 16KB from $8000..$BFFF is offset based on a value previously written to address $8000. ($C000..$FFFF is 8KB work RAM as usual).
The Excellent Dizzy Collection as seen at the top of this post doesn't entirely work; when you pick a game it resets. Some other emulators do this too. The Game Gear games Cosmic Spacehead and Micro Machines (1 and 2) work well, though.
After some fiddling around with the VDP emulation and interrupts, some more games work (but I'm not sure why they now work):
Other parts of the emulator have been improved. It can now be set to either domestic (Japanese) or export (everyone else) modes, which affects some games in a variety of ways - from translated text, to removing the Mark III bumper screen, to changing the title of the game.
For Game Gear games, detecting whether it's a Japanese machine or not is simple - test the 6th bit returned when you read port $00. If it's set, you've got an export machine.
However, port $00 is Game Gear-specific - the Master System hardware has a peculiarity that is used to detect region. There are two ports for controllers, and each controller has up, down, left, right, TL, TR and TH lines. You can set the TR and TH lines to be inputs or outputs (and the output level if configured as output) by writing to the I/O control port. If you set them to outputs and try and read from them, on export hardware you get the values you are telling them to output. On Japanese machines, however, they return 0s regardless of the chosen output level.
A more fun addition is Game Genie support. A simple call to Emulator.AddGameGenieCode("3A0-21C-2A2"); and Sonic can stand here all day:
Of course, the technical information above is my interpretation of what I've gathered so far from various documents from far brainier chaps than I, and the fact that all I have is a broken emulator indicates that it is probably complete rubbish. [smile]