For my first hack around November 2004, I tried to modify Woody Pop on the Game Gear to work on the SMS, since somebody had just posted a link to a Japanese guy's site with a rather complete disassembly of the major code locations and routines in the games. Since the locations of the palette were documented, it was easy to find the palette on the SMS and just patch it into the Game Gear version. Fading was screwy, but the rest of the game started up and looked nice. It was totally playable.
However, the game crashed randomly, and too often to be just random, so I gave up on Woody Pop. It was just a test, anyway. But what I learned from the hack was that even though the GG's palette RAM is 64 bytes; and the SMS', 32; if you run the GG code on the SMS, after it writes the first 32 bytes into the palette, the palette pointers loop back to the first palette index (or else you could say they are written to mirrors of the SMS palette). So even without editing the palette-writing code, it's possible to just hack the 2nd 32 bytes of the GG palette to look OK on the Master System.
Pop Breaker 12/14/2004
This was a nicely-programmed game, I think. Even though I had almost never looked at Z-80 code before, it was not so difficult to figure out what the game was doing.
First of all, I had to (temporarily) stop the GG's "phantom" start button from triggering when it was running on the SMS. The GG's start button is read from port $00, but there's nothing there on the SMS. So with a disassembler, I searched for the probable reads from port $00 via a INA ($00) instruction. It usually also does an AND $80 or some compare to get the top bit... so that's 4 bytes for the two instructions. Good... I need 3 free bytes to place a CALL $my_code instruction to jump to some code that replaces the start button functionality with something else.
For all the games, I had to search through the ROM from $0000 to $7FFF for any free space where I could put some of my own subroutines. If there was nothing found, well, for each palette converted from GG format to SMS format, I knew I could free up 32 bytes for my own use. Fortunately, Pop Breaker had a bit of free space. In goes a little bit of hand-coded Z80 code. I could choose to have my_code read the controller for some button press to simulate the GG Start button, but I could not read the SMS Pause button directly. So, first of all, I simply checked that both 1+2 are pressed; that will start/pause the game. Unfortunately, I found that pressing 1+2 during the game triggered the self-destruct function for your player! So, I had to read the Pause button...
Pop Breaker has some code at the NMI entry point of $0066 because the Game Gear uses NMIs for 2-player link cable play. This could cause some big problems if the SMS pause button causes the game to crash or try to link up... Anyway, the code at $0066 was mostly useless now, so I rerouted some of it to set a flag in RAM that the pause button has been pressed. My own code at my_code reads this RAM location, resets it to zero, then ORs (loosely-speaking) what was read with the already-checked 1+2 combination. So if either the pause button, or 1+2 have been pushed, it'll pause/start the game. No problems, I think.
Now to play with the palettes. First, I had to find the locations of all the palettes used in the game. This was not difficult. For all the screens, I looked at the contents of the PAL in MEKA's debugger, then searched for these bytes in the game code. I found 7 palettes in total for this game. That's a very nice and small number of palette entries to convert. I wrote up a simple little program (in 6502) that converts the 12-bit palettes of the GG into the 6-bit palette of the SMS. Pop Breaker had 7 palettes of 64 bytes each to convert. It loads a whole palette for all colour entries (bg & sprites) at once, so once the data was converted from 64 bytes to 32, I had to repeat the 32-byte entry. (Later games, like Berlin Wall and Frogger, load different palettes in the bg and the sprite palettes, so those games had multiple 32-byte palettes, not 64 like Pop Breaker.)
The hard part was with finding the palette reading, writing, and fading routines and modifying them to write only what was needed for the SMS, and to handle the B,G,R triplets properly. I searched for writes to port $BF which went LD A, $xx; OUTA ($BF); LD A, $C0; OUTA ($BF) and did a OUTA ($BE) or did a LD C, $BE; OUTI. This was the code that did palette writing (since the palette starts at $C0xx in video memory). Usually a loop value of $40 was loaded into the DE register; this usually can be changed to $20 to write only 32 values.
In total, I found about 6
or so subroutines to handle palette writing/fading. There were separate routines
for the in-game fading and for the still screens (intermission, ending). Here
is a short list of some code in the ROM that does something (you can see my
bad documentation here.) They are not necessarily subroutine entry points.
$1BF - Main Palette
$454 - ?
$4B5 - Fade sub ingame?
$502 - Fade intro?
$563 - Fade 2
$5AC - Fade SEGA logo
There were separate subroutines for fading to black and fading to white, which is what happens in-game when you hit some switches. There are also separate routines for fade-in and fade-out. So, I had to change them all. I won't quote code directly, but I'll describe the process. A setup routine copied the palette to be used from ROM to 64 bytes of RAM. It also copied a 2nd copy to another 64 bytes of RAM (usually right above it). The first palette copy is the master palette; the 2nd will be modified (faded out to black, in from black, up to white, down from white...). The subroutine for fading to black waits a certain number of VBlanks then decrements the B,G,R colour channels by 1 each until the channel is 0 (truthfully, until the channel loops past 0). Then this 2nd palette is copied to video RAM in one 64-byte chunk. Once all the palette entries are 0 (at most 16 steps), then the fading has finished. The fade to white does the reverse, increasing each channel from the regular palette to the maximum, F, for all channels. Fading up from black starts the 2nd palette copy out as all 00s, and slowly increments each B,G,R channel until the colour is the same as the master copy of the palette. Fade from white does the same, but decrements every channel from the maximum until it matches the master copy. This was a little plodding, but logical.
So modifying all of this meant that there would be only 4 steps for the fade, not 16, and that the B,G,R triplets are packed into 1 byte rather than spread out as 3 nybbles on the GG. The original code worked with these nybbles easily, by using the RRD instruction which rotated 4 bits at a time between (HL) and the accumulator. My code had to load the byte stored in (HL), shift it, fade it, store it somewhere, then do the same with 2 other channels. THEN, load up each channel, OR it with the next, then save it all back to (HL). And advance the HL pointer. I had to write this code down on paper first. But, after sorting out a few mistakes, it worked and faded okay, but quickly (since it went through 1/4 of the fading steps before finishing). Well, I had to search for the RAM location that acted as a delay for the fading routine and adjust that accordingly. Now the game drew each graphic and faded nicely.
So the fun part comes: comparing the graphics in all parts of the game with the original GG version to see what colours look bad, went missing, etc. All of the in-game graphics looked great, but some of the colours in the intermission and ending scenes remapped badly. I paused the game in MEKA and edited the PAL values in the debugger until it looked satisfactory. Then, I edited these changes back into the ROM.
And that's pretty much all there was to Pop Breaker. It was a single-screen game; it scrolled but never changed the background data substantially during the level. The one thing which I did for all these GG games is change what the game writes to video register $80 so that it masks the first left-hand column of graphics (sprites and split-screen scrolling glitches appear there.)
One funny thing: in the ending credits, one of the designer's names is written to the screen at a location where it can be seen on the SMS, but it is just below the viewing area on the Game Gear. Whether this was intentional or accidental, I don't know. But I feel sorry for the guy whose credits don't show up when a player finishes this game.
The Berlin Wall 12/29/2004
Here's another fun, single-screen game that would be nice to play on the TV screen. Hacking this was a little tougher than Pop Breaker, since it had many levels, palettes, and so on. After I hacked the pause button and tried the game out, I noticed a problem: the game would crash sometimes (a little less often than Woody Pop, but still problematically), and it would completely freeze on the title screen when the cursor is moved down to the Link Game position. I think it froze because when you move the cursor, it checks for another GG on the cable (phantom ports $01-$05) and hangs when it receives nothing. So, I had to hack the code for moving the cursor to have it skip the link mode test (and skip the line entirely) and just go down to the options screen line. This was not so tough, just alternate between $00 and $02 rather than INC a value in RAM. Also, I tried to scour the code to NOP out any reads or writes to these ports (thanks for the advice, Maxim!)
Intercepting the reads of the GG Start button was not difficult either, and this time, I could make the 1 button act as a start on the title screen. 1+2 wouldn't work in this game, because that combination makes the character jump in the game (if you have bought the jumping boots.)
Next was the whole palette thing. The Berlin Wall separates its palettes between bg and sprite palettes in 32-byte sections which caused a few problems for me. One, when I remapped the palette, I had to remap the 32 bytes down to 16, and then duplicate that. Two, palette writes to both $C0xx and $C02x would have to be looked after. Three, the fading routine setup was more complicated. In the above game, Pop Breaker, the game copied one full palette into 2 locations in RAM, and modified it as one chunk. In The Berlin Wall, the setup routine copied the bg palette into one location, and the sprite palette into another location exactly 32 bytes later. If I simply modified the palette writing routine, the sprite palette would always be missing, since the entire palette no longer fits into 64 bytes, but rather 32 bytes. So, I had to change the setup routines so that it copied the master palette and the palette copy for the sprite palette into a higher location (only 16 bytes after the bg, not 32). This was implemented and turned out OK.
But then, I played through the game and after the first level, the enemies began to look bad. They had some wrong colours. I then discovered that on each level, the sprite palettes had an additional 4-entry "mini-palette" (stored in $CC0-$D0F in the ROM) to give the enemies different colours appropriate to them (like: shades of red for the octopus boss, shades of purple for the eggplant boss...) After I found this, I simply looked for the routine that copied these mini-palettes to the master palette copy, and adjusted for the smaller SMS palette size. No more problems.
Fading was implemented a little simpler than Pop Breaker, since there was only 1 routine that handled it all. The Berlin Wall had a RAM location that acted as a fade level. After a delay, the routine would subtract this fade level from each B,G,R colour channel and save it into the 2nd copy of the palette. To fade out, make this RAM location go from 0 (nothing subtracted) to F (all black). To fade in, make the location go from F to 0. Simple. I had to write code to fade the SMS' palette, as before, but at least I had to do it only one time.
So there's nothing left to
say but to copy out some interesting ROM and RAM locations from my notes:
$0E36 - Cursor routine for title screen
Palette at $2A21-$2D3F, mini-palette at $0CC0-$0D0F
Sound code in bank $04, $8003 - init, $8006 - play (no luck making a working KSS rip, though.)
$C14B - Option cursor. Sound, lives, credits: $C14C,$C14D,$C14E
$C612 - Number of "passwords" received. 5 gets you the good ending.
$C300 - Enemy table
$C169 - Number of enemies. 0 completes the round.
$C01A - Stage number: 0-World 1; 5-Boss 1; 6-World 2; B-Boss 2; C-World 3; 11-Boss 3; 12-World 4; 17-Boss 4; 18-World 5; 1D-Boss 5; 1E-22-Final Boss Levels->Good Ending; 23-27-Bonus Levels
Oh, so I added a trainer to cheat through the game, too. It checks buttons on controller 2, and makes sure that no button is pressed on controller 1 (for those idiots whose emulators might map controller 1 and 2 onto the same keys.) This may not be foolproof.
Frogger (Prototype) 12/29/2004
Let's start this off with an important notice to trim your Frogger ROMs! The ROM that was distributed is 256K large, but the second half of that is unnecessary garbage. Frogger is just a 128k game (and its header says so.)
Frogger was like The Berlin Wall in that it loaded bg and sprite palettes separately. I handled them the same way as above. The fade routine was also done the same way as The Berlin Wall. So, not much to talk about there, except that in the ending, the game cycles 2 colours; the routine of which would have to be changed so that it cycled the proper palette indices, and loaded the proper values into them. At least that's what I think it did. Going through each level and checking for bad colour remapping was not fun, however. Most colours looked great; however, the game has grass, rivers, and frogs, all of which use some shade of green that the SMS has in low quantities (along with every other hue.) So, it was a painstaking task remapping the 4-6 shades of green into something acceptable on the SMS. Apart from that, everything else was mostly OK, Frogger having a nice and bright palette overall. I think with these 3 games that I've completed so far, I won't go through all the bother of changing every subroutine in the future. In future hacks, I might just hack the VRAM writing subroutine directly so that it remaps the GG palette on-the-fly to SMS colour entries. Maybe that would simplify things overall.
It was not very difficult to hack the GG Start button routine in. But I did notice while I was hacking that it recorded my keypresses at the title screen. That's a dead giveaway for a sequence-determined cheat or debug code! It recorded my keypresses in $C0CF-$C0DE and incremented a counter at $C0DF. I searched through the ROM to see what sequence it would compare that RAM code to, and then entered that sequence on the controller. Bingo! Level select and debug mode activated! But anyway, since this code checks all the buttons plus the start button, I couldn't hack any button to replace the GG Start button. That had to remain mapped to the SMS Pause button.
Since Frogger had scrolling in its levels, and it also updated the background tiles for the scrolling level, the game had some graphic glitches when put into SMS mode. Also, the game uses split-screen scrolling throughout; again, with some irregularities at the borders of the screen. Unfortunately, my skills are not high enough to fix these two eyesores, so I've set this GG->SMS hack at a level not quite at 1.00 until I get help from somebody or I fix the problem eventually.
The next fun part was editing the title screen, bonus game, and ending so that they didn't show garbage beyond the GG's borders. For the title and ending, the default character at the borders was $10, which was Frogger's eye (eew!!) in the title, and a square block in the ending. Happily, these screens were left in a nice uncompressed table, so extra graphics could be put beyond the GG's borders to get a nice wide-screen view on the SMS. So, I tweaked a few graphics on the title screen, and stretched its dimensions as far as it could go. In the tilemap for the ending, there are lots of unused tiles, so I figured I could add some nice graphics (but not too distracting, I hope) to complement the scene in the middle of the screen. Now, it hardly looks like a tiny GG window, I hope.
The bonus game had these big red markers past the GG borders so that the programmers could get the raster effects to work right. I changed them to barriers and grass, so that they looked like the player couldn't go there, but they still blended into the scene.
That's about all for Frogger!
Time to provide some notes:
ROM Map: $0000-$7FFF game code; $8000-$17FFF tiles & palette data; $18000-$1FFFF level & screen data.
$0010 - VRAM write
$01EB - Pal. write
$0AEC - Loads Pal to RAM
$0717 - Fade routine
$0BE2 - 16-bytes to compare to keypress in RAM for the debug mode
Pal in ROM: $12260 - $1267F or thereabouts
SEGA Pal: $07EA
$C017 - Level number
$C020 - Palette copies for fading routine