From now on, I'll take Wonder Boy III: The Dragon's Trap (Monster World II) as my main example. The problem that occured when Maxim started doing the VGM package for this game is that he was simply unable to get the VS. Dragon (boss music) theme to record correctly. In many games, music is cut during short periods to play sound effects, and this is very inconvenient if a certain piece of music only plays while action is happening. Obviously in Wonder Boy III, there's no way of playing the full boss music without being interrupted by boss attacks, etc. - the same problem is likely to occur in many action games (especially shooters).
Last Hope
Some games contain a sound test, sometimes available from the option screen, sometimes accessible by using a secret trick. In a few cases, games also offer the possibility to disable in-game sound effects.
Before starting to do intensive hacking on a game, try checking out cheat bibles or Sega 8-bit gurus to make sure there isn't a hidden sound test or a way to disable sound effects. If there's one, you're saved.
( R-Type: running your fingers clockwise around the D-Button on the continue screen activates a sound test )
First step to hack a game is to have a Z80 disassembly of its code that you can examine. Obviously, you also need you to be familiar with Z80 programming. S8-Dev has several pieces of documentation about the Z80 and examples available that are dedicated to the Sega 8-bit systems, but you should be able to find plenty of more generic pieces of information on the web. As much as I would want to, information regarding how one may program the Z80 is out of the scope of this document, and is far too complicated. If you have specific requests about Z80 or programming points, feel free to ask questions on the S8-Dev Forum.
Generally speaking, I think experience in programming is needed.
The third obvious requirement is to be able to run a modified copy of the ROM for testing, and an emulator that supports VGM logging. A debugger is also quite useful for reverse engineering; some emulators provide one (eg: eSMS, BrSMS, MEKA, although the latter one has a quite primitive debugger as of yet).
There're two usual ways of modifying a ROM:
Here we go. I'd first like to state this text only gives ideas about things to do, and is not a strict guideline. All games are different and working differently so it might be slighty more or less complicated depending on the game. Last but not least, you are always welcome to share your research and results.
The main idea is to force the game, one way or another, to play the music you want with nothing interfering it. There are two approaches I can think of:
I used the second approach to hack Wonder Boy III, and that is the one I will develop. You can download my disassembly of Wonder Boy III (175 kb) as an exemple.
Locating the exact part of the code to modify is 95% of the work. As a somewhat experimented Z80 programmer and highly-experimented SMS hacker, it took me between 2 and 3 hours to track down what I needed in Wonder Boy III, and it was one of my first hack. If you're lucky, more experienced or if the game code is easy to follow, this could take a lot less time. This could also take much more :|
Everything can be a clue to understand the position and inner working of the sound engine. If you don't know where to start, here are some half-organised ideas:
Here is Wonder Boy III's interrupt code, cutted (poorly) to show only the interesting parts.
Again, part of the work is to figure out what's going on, label functions and find out what you have and haven't got to study deeply.
; Interrupt entry point
00000038: DI ; Disable interrupts
00000039: PUSH AF
0000003A: INA (BFh) ; Read VDP Status
0000003C: BIT 7,A ; VBlank ?
0000003E: JR Z,+20h ; if not, jump to 0060h
[...]
0000005D: JP 0071h
00000060: POP AF
00000061: EI ; Re-enable Interrupts
00000062: RETI ; Return from INT
[...]
00000071: CALL 0129h ; XXX 1 Inputs_Update()
00000074: LD A,(CF82h) ; if RAM[0xF82]
00000077: OR A ;
00000078: JR NZ,+0Ch >--+ ;
0000007A: CALL 01D1h ¦ ; { XXX 2
0000007D: CALL 0202h ¦ ; XXX 3
00000080: CALL 0190h ¦ ; XXX 4
00000083: CALL 0168h ¦ ; XXX 5 }
00000086: CALL 1065h <--+ ; XXX 6 Sound_Update()
[...]
00000094: JP 0060h ; End of interrupt
As you can see, the game only processes VBlank interrupts by testing bit 7 of the VDP Status register, and get out if it isn't set.
Then, six different functions are called, which I first labelled 'XXX 1' to 'XXX 6'. 'XXX 1' and 'XXX 6' are always called, while 'XXX 2' to 'XXX 5' are only called if the byte at (CF82h) is non-zero. My first (and correct, luckily) guess was to think that the four inner functions are ones that handle the gameplay.
A quick look at 'XXX 1' showed that this function was processing user inputs, so I labelled it 'Inputs_Update()'. Then I looked at 'XXX 6'..
00001065: LD A,03h
00001067: LD (FFFFh),A
0000106A: CALL 8006h
0000106D: RET
This function writes to (FFFFh), which is a mapping register on the Master System. The third page is mapped at 8000h, and a function within this page is called. On the hacker-side, this has the bad effect that the code is shifted: it is executed from the 8000h page, but is stored in ROM at C000h. Thus, absolute addressing will have to be looked at carefully. Code at C000h looks like:
0000C000: JP 8009h
0000C003: JP 804Dh
0000C006: JP 809Fh
So our function executed from 8006h (and stored in ROM at C006h) is nothing but a jump to 809Fh (C09Fh in ROM). Those three consecutive jump instructions certainly look interesting. It looks like an API (Application Programming Interface).
When several programmers have to work on the same project, it is common practice to make use of your code easy to use for other programmers - that is, to hide the implementation of the code and leave only the interface exposed.
From personal knowledge, I know that at least two different programmers worked on Wonder Boy III, one being the musician (Shinichi Sakamoto) who programmed the sound engine himself. This would explain the table: Mr. Sakamoto gave the fixed 8000h/8003h/8006h entry points as the interface to the sound engine, so the main game programmer won't have to worry if the actual code of the function has to move.
It turned out that this table was indeed the function table of the sound engine. The first function does fully initializes the PSG and FM hardware, muting channels and setting them to their starting configuration. We'll call it Sound_Init().
The third function is called once a frame as we found out, and thus is clearly the updating function, we'll call it Sound_Update().
There's only one left, what could it be ? The function to play a music or a sound ? Bingo!
Sound_Play().
00000251: XOR A ; A = 0 00000252: LD (C232h),A ; save A to RAM [232h]The following code is executed if the YM-2413 is found:
00000286: LD A,01h ; A = 01h 00000288: LD (C232h),A ; save A to RAM [232h]How useful is that ? Well, by looking for references to (C232), I could find where that information is being used. The exact point of interest turned out to be:
0000C182: LD A,(C232h) ; FM enabled ? 0000C185: BIT 0,A ; 0000C187: JR NZ,+04h ; 0000C189: CALL 84C1h ; PSG_Update() 0000C18C: RET ; ret 0000C18D: CALL 878Fh ; FM_Update() 0000C190: RET ; retThe functions were later labelled by myself, after studying them.
0000273F: LD A,(HL) 00002740: LD (CFF9h),A 0000BA7F: LD A,09h 0000BA81: LD (CFF9h),A 0000BCA5: LD A,0Ah 0000BCA7: LD (CFF9h),ATwo being constant values, and one being taken from memory pointed to by the HL register. I guessed that the variable assignment is part of a function used in-game, and the constant ones are used in special cases.
I know Wonder Boy III enough to say there isn't any music on the title screen or menus. I can, however, think of some special cases: the two pieces of ending musics, and the introduction. Apart from them, all other tunes are being played while the game runs.
So, I modified those constant values by using a MEKA.PAT patch, and finished the game. Bingo! At the pictured point, an unusual piece of music started to play.
Me and Maxim took a bit of time to try out all values and list them. Some would play in-game music, some a sound effect. We finally found the correct value to put in to play the boss music, and that enabled us to easily log it during the ending sequence.