Sega Master System / Mark III / Game Gear
You can make your own trainers by using selected tools to hack/modify the original SMS ROMs, and it is a fun and, at times, challenging endeavour. There is no universal way to do it, and ROM hackers have various preferences with regards to tools and approaches.
This section is written by SMS Power! user hang-on, in the style of step-by-step instructions interwoven with a personal narrative. I'm not a coding/computing authority, but I believe I have a healthy interest in tinkering with the SMS-games. The narrative format allows me to present my findings as personal experiences gained from having hours of fun with games and tools of the console from my childhood. These experiences are written up as steps and recommendations, to provide clarity for the reader, and to make the text function as a kind of tutorial, that might inspire others to embark on making their own trainers out of the good old SMS games.
The approaches taken and described in this guide, might not be the most simple or straight forward ways to achieve quick patches like unlimited lives etc. My motivation for hacking ROMs is not only the pleasure of enabling trainer features by patching some isolated bytes (but this IS also a major thrill for me). As a child, I did spend a lot of time wondering about how my SMS games where made, how the console worked, etc. Now, as I'm coming back to all this SEGA 8-bit stuff after 25 years, hacking the games is more like a practice of reworking some of the great mysteries of my childhood. So I'm fuelled by a passion for exploring the inner workings of the games. I'm set on both process and product here.
It is assumed that you know a little about hexadecimal numbers, the workings of the Z80 inside the SMS, etc. For an excellent primer on SMS programming and essential info on hex numbers, opcodes, etc., please refer to Maxim's programming tutorial for the SMS/GG.
It is also assumed that you have access to copies of the ROMs you are going to hack. This guide will demonstrate how to create a trainer for Altered Beast. A zip file with .asm source code and .ips patch for the total Altered Beast trainer project is located towards the end.
Inspired by the definition of "a more accomplished trainer" given in this this document (not written by me), I went on from Altered Beast and created a trainer patch for Golden Axe. This patch includes a demo screen with animations and music, and a trainer menu to turn cheats on and off. Thus below the Altered Beast trainer D.I.Y. tutorial, I have included some notes and a download link for the Golden Axe trainer. I have tried to comment the source code in a fairly detailed way. Contact me (hang-on) if you have got any comments or questions.
Edit: The following descriptions of workflows involving the Emulicious emulator are outdated. These days (2018), Emulicious will nicely disassemble the rom for you. No need to do all the workarounds involving Firefox etc. Thanks to Calindro for keeping this Emulator updated and in top shape!
This section contains notes on some of the various tools that can come in handy when creating your own trainers.
I will describe the details of a workflow for creating a trainer version of the Altered Beast ROM. You will learn how to:
Screenshot of Zeus informing you about the pause button power up feature!
Screenshot of the title screen level selector in action
To get in the right mood for modding this classic SEGA game, I strongly recommend that you spend a few minutes watching how someone must have struggled with porting it to an inferior 8-bit console back then.
Oh yeah, it is time to get to work.
First step is to make sure your ROM is healthy and clean. This is mainly to avoid confusion with unauthorized headers put on by some older copier devices back in the days. If the ROM contains this kind of header, the ROM addresses you see in the debugger, will not correspond to those you want to modify using in the hex editor. This is because the debuggers and emulators tend to ignore those unauthorized headers, but the hex editor shows you the raw file as it is.
My Altered Beast ROM is a file on my computer called "Altered Beast.sms". In the rest of this section I will refer to it just as the ROM.
I open SMS Checker, and point it to the directory containing the ROM. Then I click Scan. I'm going for a "Good dump", in which case I can proceed directly to step 2, but it turns out my ROM is head/footered!
Then I click Fix, confirm my intentions in the dialog that opens, and tadaa, it is now of type "Good dump". Now that I know that my ROM is in good, hackable condition, it is time to proceed to next step, and that is searching the web for cheat codes.
Sometimes it is necessary to use a debugger like Meka to establish a cheat code for a given game. At other times you are fortunate enough to be able to draw on cheat codes found by others in the community.
To support the SMS Power! community, I recommend that you first step by the "Games" section here. From the front page, click on "Games" > "A" under Master System > "Altered Beast". Now you will be at the general information page on Altered Beast. Click the "Cheats" links in the right hand sidebar, and you are transported to the cheat page for Altered Beast. This page displays various cheats, but what I'm after is the so-called Pro Action Replay codes . The Pro Action Replay (PAR) is a cheat cartridge that was made for the Master System and Game Gear. It sits between the cartridge and the system and offers the user the ability to search for, and apply, RAM patches. These RAM patches work well as a starting point for debugging a ROM to create your own trainer.
I'm in luck! The PAR code for infinite lives and power up has already been reported:
(there are other PAR codes for Altered Beast).
The anatomy of a 8 digit PAR code: 1) Forget about the two leading zeroes. 2) The next four digits is the RAM location to be patched. 3) The last two digits is the value to be written (patched) to the specified RAM address.
For example, you can interpret the infinite lives PAR code (00C08F03) like this: It tells the Pro Action Replay device to constantly write the value 03 to RAM location C08F. From this we can guess that unlimited lives, in this case, means having always 03 lives. The most important thing learned from this code is the RAM address (C08F); we can assume it is the place where the game stores the number of lives left.
Note: You can check this thread on how to use the Cheat Finder feature of Meka. Good feature to be familiar with in those cases where you are not so lucky that you can just look up the codes.
I create a new folder and put the ROM into it. I rename it "altbeast.sms" for simplicity, and because I do not trust filenames with spaces in them.
Then I open the ROM in Emulicious, and in the "Tools" menu I click "Create Disassembly". This populates my altbeast folder with an .asm file (containing the disassembled parts of the ROM), a .link file and 19 .inc files. Then I open the .asm file in Firefox. I believe this is a good way to view the disassembled ROM, without being tempted to alter things and create confusion. I keep the ROM open in Emulicious, as I might need Emulicious' handy memory tracer features, which assists you with regards to guessing from where in the ROM tiles and sprites are drawn onto the name table or into the tile generator. In Firefox, I open two tabs that supports my attempt at learning the Z80 instructions set: The first is a nice reference where you can click on each instruction and get details about how it works. The second is all the opcodes on one page, and I use it if I sit with the raw hex codes and do not remember what, for example, 0C means. So the disassembled ROM in one tab, and two tabs with reference material. I would like to mention SMS Power!'s own list of Z80 opcodes - it is very good!
I also open the ROM in Meka, as this is my favorite debugger. It has a full memory viewer that can view both RAM and ROM (among other things), you can set breakpoints, and even utilize the handy cheat finder feature, in those cases where you are not so lucky that you can draw on cheat codes already reported. Isolated functions, addresses etc. I discover while debugging with Meka, are easily put in context with the disassembled code which is view- and searchable in the Firefox window (as discussed in the paragraph above).
I use the regular Windows notepad for keeping notes about my findings on addresses in RAM and ROM.
Lastly, I open the built-in Windows calculator, and set it to "Programmer" in the "View" menu. I also keep Frhed the hex editor close if I need to inspect the ROM as raw hex codes beyond the scope of Meka's debugger.
The actual modifications will be made using the Context editor (see Maxim's programming lesson mentioned in the beginning). Assuming you have set Context up like Maxim describes, this is the initial moves:
Your .asm file in Context should look like this:
.MEMORYMAP SLOTSIZE $4000 DEFAULTSLOT 2 SLOT 0 $0000 SLOT 1 $4000 SLOT 2 $8000 .ENDME .ROMBANKMAP BANKSTOTAL 16 BANKSIZE $4000 BANKS 16 .ENDRO .BACKGROUND "altbeast.sms"
You can check that it assembles well in WLA-DX if you press F9 (still assuming the recommended setup as described by Maxim). Please note that you are not making any changes to the ROM specified in the "BACKGROUND" directive. F9 is set up to assemble the .asm file into a file called "output.sms". At this point the output.sms is identical to the altbeast.sms, since we have not (yet) done any coding in Context (apart from the assembler directives). You can load output.sms into your favorite emulator to see for yourself that this is, for now, the unchanged Altered Beast game.
With all this set up, we are ready to begin reverse engineering Altered Beast.
As described above, I have already got the memory location containing the number of lives - it is C08F. Before I do any modifications in Context, I want to examine the game in Meka to get to know more about how and where in the ROM the game deals with the players' lives. In Meka's memory editor I can also poke changes directly into RAM and ROM, and thus test modifications before I code them using Context.
I open the ROM in Meka, and enable the memory editor. I point the memory editor to location C08F in RAM, and start the game. As I get the command from Zeus, I can see in the editor that location C08F holds the hex value 02. When I loose a life on the second wave of skeletons, C08F gets decremented to 01 along with the number of lives displayed at the top bar in the game. I can even type in some values of my own, using Meka's memory editor. If I type 04 into the byte at C08F, I instantly get 4 lives in the top bar. This ensures me that C08F is indeed the container of the player's lives.
OK. So now I want to hijack the code that decrements the value at location C08F. In Meka, I put the game in a state where I'm about to loose a life. I save this state (F5), so that I can always load it (F7) later if needed. Then I break into the code by clicking the debug window and hitting Enter. At the prompt in the debugger window, I enter the command to break whenever some code writes to location C08F:
b w C08F - meaning "Break on Writes to location C08F". By doing this I hope to catch the code that decrements the number of lives. At the new line of the debugger prompt, I enter the letter
c to continue with the game. I hold my breath and wait for the break to happen...
Just as I loose my first life, the game breaks as expected! The 'guilty' Z80 instruction is located at position 0C84:
0C84: LD (HL),A ; treat the four bytes contained in HL as an address in RAM ; and load the value contained in A into this address
The game breaks because HL at this point contains C08F. Studying the nearby code in Meka's debugger, and in the Firefox tab that displays the disassembled ROM, I get a sense of how the game handles the situation when the player looses a life. As it turns out (see screenshots), almost everything is nicely crammed into one function.
You can view a screenshot from the Meka debugger, where I have marked the code responsible for decrementing the number of lives and writing the decremented value to the required RAM location (C08F). You can also view a screenshot of the disassembled code in the Firefox tab, where I have marked the 'loose life' function as a whole.
In the box below, I have pasted the loose life function copied from the disassembled code. I have added some comments for clarification:
_LABEL_C77_: ld a, ($C0EF) ; C0EF is a flag: 01=player is loosing a life, 00=not the case or a ; poking 01 to C0EF using Meka memory editor will instantly ret z ; kill the player (loose one life) ; this check is done to protect the player from (more) harm ; while the 'loose one life' function is running. If flag is not ; 00, the game will not proceed further with decrementing lives ld hl, $C08F ; point to the location holding number of lives ld a, (hl) ; put this number in register a sub $01 ; decrement register a jr c, _LABEL_C96_ ; if the decremented value is below 00 (carry flag set), ; jump to game ending procedure (all lives are lost) ld (hl), a ; else put the decremented value back into the memory location ; storing number of lives ex de, hl ; begin respawning... ld (hl), $02 inc hl ld (hl), $05 di ld a, $0C ld ($FFFF), a call _LABEL_32D3B_ ei ret
Looking at the code above, I think a good way to achieve infinite lives is to stop the game from decrementing the value in register a. If there was no "sub $01" instruction happening, then the number of lives would be loaded from $C08F, the carry flag (for the ending procedure) would never be set, and the unchanged number would be put back into $C08F.
I need to know the precise address of this sub $1 instruction. In Meka debugger I write
d c77, to get a disassembly starting at address C77. Then I learn this:
0C80: D6 01 SUB 01h
Explanation: 0C80 is the precise location in the ROM, D6 01 is the hexadecimal representation of the two bytes of 0's and 1's that make up the instruction SUB 01h (which is the mnemonic for subtract $01 from the value currently in the register A). Meka uses 'h' to denote hex numbers. In Context, writing for the WLA-DX assembler, I use '$' to denote hex numbers. I'm sorry for the confusion.
Anyway, to patch the ROM so that the SUB instruction never happens, I need to turn the two bytes (D6 01) into NOP's. NOP is an instruction that does nothing for 4 clock cycles. It is a 1-byte instruction, and the byte is
In my alterbeast_trainer.asm, waiting for me in Context, I write the following patch:
.orga $0C80 ; overwrites SUB 01h (D6 01) with 00's so that lives are not decremented .db $00 $00
Please refer to the WLA-DX documentation for the full details. Briefly explained: .orga tells the assembler to put the assembled instructions starting at the address specified. In this case it is $0C80, which is where the two bytes of sub $01 start. I write a comment for clarification. The .db $00 $00 defines two bytes of $00 (the NOP instructions). These 00's will overwrite the D6 01, effectively enabling infinite lives by disabling the subtraction of lives.
I assemble it with F9, test it with F10, and it works! I put it on my Everdrive and defeat the final boss just to celebrate. Haha - die, you evil lord of the underworld. It won't help you to turn into that rhino. You might recently have gotten some fame from being in Wreck-it Ralph, but you know who will win when it's down to just you, me and the patched ROM.
I want to replace the pause handler with a routine that activates power up (transform the hero into a muscle guy or a werebeast) instead of pausing the game. I already know that writing a $01 to $C220 starts the power up (see above), so I overwrite the original pause handler with new code that does just that. From the Sega software developers manual I know that hitting the pause button invokes a non-maskable interrupt that begins at $0066. Using a combination of the disassembled code in the Firefox tab, the Meka debugger for disassembling
d 66 and breaking
b 66 around $0066, and the Z80 Heaven page (see above) for info on the precise byte sizes of the relevant Z80 opcodes, I write the following patch in Context. It's ready to be assembled with WLA-DX:
.orga $0066 ; the pause handler (always at $0066) push af ld a, $01 ld ($C220), a ; write $01 to the RAM location that invokes the power up pop af retn ; return from pause handler .db $FF $FF $FF ; as my custom handler is 3 bytes smaller than the original handler ; I put these in. Well, I don't know, it makes it feel a bit cleaner ; I guess (it seems like $FF is used for marking unused bytes in the ROM).
This patch effectively alters the behaviour of the pause button. Now you can power up at will (but you cannot pause the game... welcome to your doom, hahahaha!). One more thing: You can actually power up beyond the standard werebeast. The fourth or fith time I powered up the poor level 1 werewolf, he got the dragon's ability to electrocute enemies in his own, buggy way (he evens turns green - and then burning red when he attacks).
Even though this buggy behavior is a little amusing, I have tried to put in some additional code to check the current state of transformation, and only releasing the power up if the player is not already a werebeast. Using the Cheat Finder in Meka (comparing to $00 > power up > comparing to $01 > power up > comparing to $02), I quickly located the byte that holds info about current state of transformation: $C24C. But I have not yet found a good way to implement this check-up, as I cannot find sufficient free space in the ROM. For now, I'll go with the free transform pause power up routine. (Note: At a later point in this guide, I will disable the demos, and use the space allocated for demo control, for my own sinister purposes, including a fix that prevents attempts at the third transformation).
This patch does not improve the gameplay, but I like it anyway. In the absence of a demo style title screen for my trainer (refer to the beginning of this document on trainers), I want to let Zeus inform the player about the pause button power up feature.
I enable the Memory Tracer in Emulicious.
Then I start the game, and Zeus greats me the usual way: "I command you to rise from your grave and rescue my daughter". While Zeus' command is being typed, I open the Tilemap Viewer. When I (using the mouse) point to the tile representing the first letter ("I"), Emulicious tells me that the source ROM location is 1016h. The source means the address of the byte in ROM that was copied to this particular position at the tilemap. In this case it seems that the string of letters making up Zeus' command starts at ROM location 1016h.
In Meka I use the memory editor to examine what goes on at ROM location 1016h. Below I have attached an annotated screenshot that shows the bytes defining the characters in Zeus' command. I can guess that the code for the letter "I" must be "AC", a space is "FE" and so on:
In order to alter the message, I must know the codes for all letters. Going back to Emulicious I open the Tile Viewer, where the letter tiles (characters) are visible in the bottom half. I point the mouse over the "I" tile, and learn that it is at index 428. The first letter in the character set is "A", and it is at index 403. 428-403=25; which is 19h (hexadecimal). From the peeking done using Meka, I know the letter "I" has the hexadecimal code ACh. I can calculate the code for the first letter ("A") this way: ACh-19h = 93h. Update: Silly me. I was blessed by a moment of sudden clarity, and I saw how the index number 403 is of course 193h (hexadecimal). Reading about this stuff in the Sega Software Reference Manual http://www.smspower.org/Development/OfficialDocumentation/ made me realize this, and also that the third hex digit (the 1) is added because I'm dealing with the tiles in the bottom of the tilebank (also called the character generator in the reference). The Meka tiles viewer comes in handy here, as I can use it to directly read the hex index values of the individual tiles. If the tile index is 193h for a given tile, the one-byte version of the index is simply 93h (refer to the table below).
Knowing the code (index) for the first character in the set, I can make a table for all the available characters by looking at the Tile Viewer, taking each letter at a time, incrementing my way through. This is the resulting table:
A 93 Z 9B Q A3 H AB 6 B3 ¨ BB B 94 J 9C R A4 I AC 7 B4 [ ] FE W 95 K 9D S A5 0 AD 8 B5 D 96 L 9E T A6 1 AE 9 B6 X 97 M 9F U A7 2 AF - B7 F 98 N A0 V A8 3 B0 , B8 G 99 O A1 C A9 4 B1 . B9 Y 9A P A2 E AA 5 B2 x BA
After a little bit of celebration, I use the table above to write the following patch in Context:
.orga $1016 ; this patch alters Zeus' command at the start of the game ; "RISE FROM YOUR GRAVE": .db $a4 $ac $a5 $aa $fe $98 $a4 $a1 $9f $fe $9a $a1 $a7 $a4 $fe $99 $a4 $93 $a8 $aa ; fill with invisible spaces to the right border of the screen: .db $fe $fe $fe $fe $fe $fe $fe $fe $fe $fe $fe ; "PRESS PAUSE TO POWER UP": .db $a2 $a4 $aa $a5 $a5 $fe $a2 $93 $a7 $a5 $aa $fe $a6 $a1 $fe $a2 $a1 $95 $aa $a4 $fe $a7 $a2 ; fill with invisible spaces to right border of screen: .db $fe $fe $fe $fe $fe
I you want, you can double check with the table that this is correct. Or even better: You can assemble the .asm file (F9) and run it in Meka (F10) to see (hear?) for yourself what Zeus has to say!
Rather than making a separate demo style custom title screen, I want to work with what I got in the ROM as far as possible. I've got Zeus informing the player about the power up feature, and I also want it to be clear from the title screen that this is a trainer version of Altered Beast.
Altered Beast is very quick to proceed from the title screen to one of the preset demos. I feel a need to make the game stay at the title screen until the player press a button. Using Meka, I reset the game, to make the title screen appear. Then I press F5 to save state. Now I can scroll through the memory editor while pressing F7 to load the saved state (effectively restarting the title screen over and over). I'm looking for some kind of counter that I'm guessing the game uses for 'knowing' when to switch from title screen to demo. So I'm looking for a value that either increments or decrements rapidly/steadily during the title screen.
At this point, I'm getting into some trial and error, as several RAM addresses looks like the counter I'm after. I examine them one by one in Meka by breaking on reads/writes to the given address. I also examine them in the disassembled code in the Firefox tabs, searching (Ctrl+F) for the RAM address in question. This allows me to jump between functions that refer to the given RAM address.
This step actually required quite a lot of work on my part, and looking back I don't know why. Because it is in fact very simple and straight forward. The counter I'm after is in RAM address 219h, and it is checked at ROM address 306h. I have copied the relevant code disassembled by Emulicious and viewed in Firefox (my comments):
_LABEL_306_: ; this is at address 0306h in the ROM ld a, ($C219) ; load register A with the contents of title screen counter at C219h cp $DC ; CP is a subtraction from A that doesn't update A, ; only the flags it would have set/reset if it really was subtracted. ; In this case: As long as the value in C219h is lesser than DCh ; then the the carry flag is set jr nc, _LABEL_320_ ; if the carry flag is not set (the counter reaches DCh), ; then jump to demo
Explanation of CP opcode comes from Z80 Heaven . From the code above I learn two useful things. First I know now where to apply a patch in order to make the game never leave the title screen for a demo (I can overwrite the jump instruction). Second I know the location of the demo handler (320h), and if I'm going to disable the demos, this is instead to be treated as a source of unused bytes at my disposal (keeping in mind that I ran out of space for a good quality pause button hack).
In Meka's debugger I disassemble at ROM address 306h, and I see the jump instruction a few bytes down:
030B: 30 13 JR NC,+13h (0320h)
So now it is fairly easy to apply the famous NOPs using Context:
.orga $030B ; this patch makes the game wait for the player at the title screen .db $00 $00
At the title screen the words "PUSH START BUTTON" flashes below the game's title. I want to change these words to "x TRAINER x" in order to advertise that this is a trainer version of Altered Beast. This is a little different from creating and prepending a demo style custom title screen (refer to the beginning of this document on trainers). The demo style custom title screen is also a very good idea, but modifying the game's original title screen, forces me to spend even more time learnng about how the game is set up, which has intrinsic value for me (at least in this case of Altered Beast). It also brings certain extra constraints and possibilities, which is a good thing for me. Instead of importing graphics etc., I'm tempted to focus more on the tiles the game has already loaded at this given point.
I point the mouse to the "P" in "PUSH..." in Emulicious' Tilemap Viewer. Then I learn that the source for this character is at 69EBh. I do some disassembling at this address in the Meka Debugger, and I can see how values from the table above are crammed into the bytes starting at 69EBh. After I finish experiment a little with poking some of the codes from the table directly into the ROM using Meka, I switch to Context and type in the following patch:
.bank 1 .slot 1 .orga $69EB ; this patch causes the word "x TRAINER x" to be flashing from the title screen ; spaces and an x: .db $00 $00 $00 $ba $00 ; "TRAINER" .db $a6 $a4 $93 $ac $a0 $aa $a4 ; spaces and an x: .db $00 $ba $00 $00 $00
I present some notes and main points, to go with the (commented) source code for the patches described above, and for the coding I've done in order to create a level selection menu. I also touch upon how I have used the space, that was originally reserved for the code that handles the demos, to improve the pause button hack beyond the bugs mentioned. The source code is attached as an .asm file at the end of this document.
I have appended 32 kb to the original ROM. In my experience, the Everdrive and some emulators have trouble with ROMs not meeting specific demands with regards to size. Even though I do not (at this time at least) need all of the 32.768 extra bytes for code and data, I believe my Everdrive needs the bytes to handle the ROM correctly. I open the original ROM ("altbeast.sms") in Frhed, the hex-editor mentioned earlier. I press Ctrl+P, or click Edit > Append... in the menu, and then input 32.768 when asked for the amount of bytes to append. Then I save the expanded ROM under a new name; "altbeast_32.sms".
.ROMBANKMAP BANKSTOTAL 18 BANKSIZE $4000 BANKS 18 .ENDRO .background "altbeast_32.sms"
These new values reflect that we have appended 32.768 bytes, which is $8000 in hexadecimal. With a bank size of $4000 (as we can see directly from the rom bank map), this corresponds to adding two additional banks (from 16 to 18). Edit: I'm having trouble with my Everdrive and this game. Everdrive seems to require ROMs to fit into a power of two file size. So in the version attached for download below, I have expanded the ROM to 512 kb (32 ROM banks). I state this here just to avoid confusion.
When I disabled the demos in the patch earlier, I also cut the connection to the code used for handling these demos. As long as the demoes remain disabled (I do not intend to put them back on), I am free to use the bytes occupied by the demo handling code for something else. From debugging the title screen in Meka, and comparing with the disassembled code that Emulicious produced, I have found out that a fair amount of bytes, starting from address $320, is thus open to my custom use. I redefine that section to house the custom pause button hack, improved to include a check of the variable that contains information about the current state of the player (so that you cannot power up if you are already a werebeast). The section also houses a custom routine that saves the current page number in a seemingly unused byte near the stack, in order to facilitate a seamless call to a function located in the bytes appended to the original ROM. Upon returning from this function (it is the level select menu), the old page number is loaded, and control is given back to the original code.
Please refer to the source code provided. I have tried to make comprehensive comments and labels.
Click on the link below to download the assembler source code for the total project, including the level select menu and the modifications described above. Also included is the whole trainer project as an IPS patch, ready to apply to your own copy of Altered Beast:
|Patch name and download||Date||System||Game||Cheat||Group|
|ALTERED BEAST +3||2013/12/04||SMS||Altered Beast||Infinite lives, power-up at will, select level||h1|
Edit: (dec. 15th 2013) An optimized version of the code for the Altered Beast hack is now available. See the download link below.
These are the main changes, compared to the version described in the tutorial above:
I have used the same tools, workspace and techniques as described above, to make a trainer for Golden Axe. I have included .asm source and .ips patch below. I will not go through every step, but I have tried to comment richly inside the .asm file. Contact me by PM or on the forums if you have questions or comments, I would be happy to help. For me, the thrill of creating this Golden Axe trainer was not so much to actually locate and tweak selected bytes in ROM, but more working with (and learning) some basic building blocks of Z80 assembler programming to create an animated demo screen with sound, and a trainer menu where you can enable or disable the various cheats/trainer features.
|Demo screen||Trainer menu|
Some aspects of the Golden Axe trainer I want to highlight and flesh out a little:
I knew I wanted music on the demo screen. I remember the old days, over at my friend's house playing his Amiga, enjoying the demo screens of the various games. Well, less would have to do, as my skills are limited. Anyway, this was the opportunity for me to dwelve into the PSG chip of the SMS. I read some of the documents, experimented with Martin Konrad's music tracker software, and eventually came to a point where I could play some music from a blank screen 'game' on the SMS.
In the process I learned about how the interrupt handler will call a function something like "play_next_note", that will... ahem, play next note. And also that a function initializes the PSG and sets up track data to be loaded into the PSG port one note at a time. I thought about the benefits of hi-jacking Golden Axe's own music handler functions: Not only would the world be spared of my desperate attempts at creating chiptunes (as I could play tracks and sound effects already included), but I could maybe include a kind of sound test as part of the trainer features (where you can play back each level's soundtrack). Emulicious had disassembled the game for me, so I had a few Firefox tabs displaying this disassembled code. Switching between Meka memory viewer, disassembled code in Firefox, and the regular Windows notepad, I started from Golden Axe's interrupt handler, and worked my way backwards towards the music handling part of the code. This part of the process was pretty low tech but hard work: I simply traced (in the Firefox tabs displaying disassembled code) all calls recursively until I eventually was sent back to the interrupt handler. In the end, I had a notepad full of addresses all relating to each other, within a certain part of the ROM; the part that contains functions related to music/sound handling. I did this to confirm that all music handling was done in one bank (it was), and to learn about some key variables needed to be in place to support a play-note like function on an interrupt basis.
In the box below, I have included only the code related to setting up and playing sound from the original ROM. I spent quite a few hours reverse engineering in order to arrive at the necessary definitions and functions:
.DEFINE vPSGControl $DE84 ; variable used by the game's own sound ; handler to determine which tune/sound effect ; to load and prepare to play .DEFINE fInitSound $8000 ; - used to set up/load track or sound effect .DEFINE fPlayNote $8007 ; - called from interrupt handler to play next note .DEFINE dMusicLevel5 $85 ; the castle ... ; prepare a selected track ld a,$2 ; music handler expects slot 2 = bank 2 ld ($FFFF),a ld a, dMusicLevel5 ; set up music handler to play tune from lev. 5 call loadSound ; - see also fPlayNote .... loadSound: ld (vPSGControl), a ld a, $02 ld ($FFFF), a call fInitSound ret
I had some fun with going through all the music and sound effects. I have not confirmed the sound effects in the list below, but I surprised myself how well I (think I) recognized even small soundbites of noise... I have played my share of Golden Axe already, and I will never outgrow this classic SEGA game :)
----------------------------- music ----------------------------- 81 = forest (level 1) 82 = turtle village (level 2) 83 = port town (level 3) 84 = fiend's path 4 85 = the castle (level 5) 86 = select magic 87 = ending 88 = thief/camp theme 89 = intro ----------------------------- sound effects ----------------------------- 90 = sword slash 91 = shoulder bash 92 = chicken dragon tail whip 93 = dragon fireball? 94 = enemy slash 95 = dragon flame 96 = slash variant 97 = pulsing flame magic 98 = another magic 99 = earth magic A0 = flames moving magic A1 = yet another magic A2 = lightning magic A3 = short slash/hit? A4 = hit A5 = shoulder bash? A6 = fall to ground A7 = skeletons appearing from ground A8 = kicking thief A9 = pick up potion (+ select magic?) AA = magic? AB = death adders magic ----- end --------------
I expect my actual code for the demo screen to give away that this is my first attempt at working with sprites. I found the Golden Axe kanji logo on the internet, and edited it in photoshop to fit into 8 x 8 tiles, and a limited amount of colors. Then I used BMP2Tile to process the image from png to a collection of files for inclusion into my project: data for the tiles, the tilemap/name table data and the palette. At first I had a wild fantasy about building the logo with sprites, and having a clouded background as.. well, a background. Just like the arcade version, then the background will scroll vertically, giving the impression that the logo is flying. Reality caught up with me pretty fast, but I still believe that the idea with using all 64 sprites for the logo, and then scrolling the background is doable, and would yield a nice result. Well, what I settled on was little white dots on a transparent background. I like to think of them as stars, and then we are still having a sky theme for the demo/intro screen, haha. Anyway they move vertically once pr. frame, some by one pixels, others by two pixels.
I realized that I really needed at kind of frame-based program flow, like the one you can have with vblanks enabled and an interrupt handler. But I thought that I had better to keep out of the game's interrupt handler, because that I would not like my modifications to slow down the game. And I believe that this would be the case if I hijacked the interrupt handler to have it accomodate to my custom needs as well as handling things for the game. As you might know, the last thing Golden Axe needs is more tasks to be carried out on a frame-by-frame basis (here I'm alluding to the sometimes chubby flow of things when three enemies are on screen with you). Studying the various ressources on SMS Power!, including some of the open source demos, I learned how to create a frame-by-frame flow without having to turn on interrupts and using a handler.
I have this function in the beginning of the main loop of both the demo screen and the trainer menu:
waitVBlank: -: in a,(dVDPCounter_V) ; wait for Vertical Blank cp $80 ; creating a non-interrupt based ; frame-by-frame flow... jr nz, - ret ; ...relying on this definition: .DEFINE dVDPCounter_V $7e
I found out about this counter in Charles McDonald's VDP documentation.
The cheat page for Golden Axe has PAR codes for infinite lives, magic and energy. Using Meka's memory editor while playing the game, I discovered/confirmed the following definitions pretty straight forward (using Meka's memory editor and savestates):
.DEFINE vMagicType $C004 .DEFINE vLives $C005 .DEFINE vPotions $C006
Also it was obvious that something related to the screen/level was connected to bytes at $C002 and $C003.
Before the title screen is displayed, everything is set up and initialized. At address $2AAA, there is a call to a function that uses LDIR to initialize RAM. I changed this to call the following function, which I placed in some unused bytes in the beginning of the ROM, in order to transfer control to the demo screen. My demo screen starts by carrying out this LDIR function, so RAM is still initialized, but I get to preserve some important info related to enabling/disabling the cheats of the trainer menu:
.bank 0 slot 0 .orga $0017 demoScreenCaller: ; placed in unused ROM di push af ld a,:demoScreen ; put the bank containing ld ($FFFE),a ; demo screen code in slot 1 ; (music needs to be in slot 2) call demoScreen ; call demo screen w. menu ld a,$01 ; put bank $01 back in slot 1 ld ($FFFE), a ; leaving no trace pop af ei
When everything about the demo screen and trainer menu is done, and the user clicks either "GAME" from the demo screen or "START GAME" from the trainer menu, the program returns to the demoScreenCaller above, reinserts the correct page in slot 1, and carry on with preparing everything for the original title screen.
Apart from the initial jump to the demo screen and trainer menu, I need to jump into my appended space at three occasions, where two of these are occuring within the action of the game play:
As far as I can see, the original 512 kb's of ROM do not have enough unused bytes within it for these modifications. So I need to put the functions into the appended space, and I need to jump to this space on the occasions mentioned above. Unused space is scarce within the original ROM, so instead of having three separate caller functions (they use quite a few bytes, as they also handle paging), I have a kind of router function used by all three occasions. This router function jumps to the appended space, where register A is used as a kind of parameter, routing the program flow towards either handling level, lives or potions depending on the value in A.
.orga $0040 ; placed in unused part of ROM invokeRouter: ld (vRouterArg), a push af ld a,($FFFF) ; read current page number push af ; save page number on the stack ld a,:router ld ($FFFF),a call router ; route to life patch, potions patch or level patch ; depending on vRouterArg pop af ; retrieve saved page number ld ($FFFF),a ; and activate it pop af ret .bank 32 slot 2 router: ; jump to potion-, level- or life patch ld a, l cp $19 ; register l holds $19 when adding potions jp z, potionPatch ld a, (vRouterArg) cp $07 ; register a holds $07 when loading level 1 jp z, levelPatch jp lifePatch ; ... if none of the above, then jump to lifePatch
Please download and explore the source included in the .zip file below. I have tried to add plenty of comments to the code. Included is also an .ips patch so that you can almost one-click recreate the complete trainer hack described here.
|Patch name and download||Date||System||Game||Cheat||Group|
|GOLDEN AXE +3||2013/12/04||SMS||Golden Axe||Infinite lives, potions x 2, select level||h1|