The game

This game doesn’t use a “write trigger” to control its music, so I need
to hack it the hard way :)

The general case when there isn’t a write trigger is that the game needs
a function call to start a new track. This function will need a
parameter to specify the new track but it could be passed in a register
or in memory.

So, I search for “($7f)” and find that it is happening at locations in
the source between $c18a and $c526. (There are also some false matches at
higher addresses, but these are due to data being interpreted as code.
The code around there just looks wrong - too many repeated operations
that make no sense.)

So, the sound engine seems to be in code around that area. Knowing about
the SMS, I know that you can’t directly access that area - you have to
use paging to allow the CPU to access it. Paging works by
selecting the bank of the code. The bank number is the offset
divided by $4000 (hex); so, the bank from $c000 to $ffff is bank number
3.

The game must have to page in this bank when it calls the “start
new music” function I am looking for. To do this, it must write $03 to
the paging register - which is generally offset $ffff, but
sometimes $fffe or (rarely) $fffd. In some unusual games, it is totally
different but all Sega-made games use these three paging registers.

If it uses paging register $ffff, the code will be mapped into the range
$8000-$bfff. $fffe maps into range $4000-$7fff and $fffd maps into range
$0100-$3fff.

where xx is the page number, and $ffff could be $fffe, as explained
above. The first one is more common. I will search for “a,$03″ and see
where it is just before similar lines referencing $ffff or $fffe.

There is one at offset $c3. It is using paging register $fffe, so when
it calls the function I am looking for, it will be an address between
$4000 and $7fff. The only call like that near this match is a call to
$4000 and it doesn’t look like it is passing a parameter - there is no
ld code before the function call.

This is paging in the sound code, loading $d2d2 with a value and then
calling a function at $4012. This looks very much like a “new track”
function call.

If I load the game in Meka, I can use the Memory Editor to see what is
stored in $d2d2; the value there is a small number, like $06 or $01, and
it does change with the music. However, editing it does not make the
music change. I need to hack the code.

Well, I can see that the code fragment above is designed for doing this
- all I have to do is add some code somewhere that does this:

This will start a new tune and then hang the game so it won’t be
interrupted. Restart Meka and try it out. When I press Pause, it starts
the Bridge Zone music and hangs the game, while the music keeps playing.
Success!

If I load the Memory Editor and press the ROM button, I can find
the patched code at $0066. At $0067 is the 01 byte that was the
parameter for the music.

Writing another number there (eg. 02) and pressing Pause makes a
different track start, so I can easily try lots of different values.
Here they are:

Value

Music

00

Green Hill Zone

01

Bridge Zone

02

Jungle Zone

03

Labyrinth Zone

04

Scrap Brain Zone

05

Sky Base Zone

06

Title Screen

07

Act Start

08

Invincible

09

Act Complete

0a

Death

0b

Boss Theme

0c

Boss Theme

0d

Boss Theme

0e

Ending

0f

Green Hill Zone

10

Bonus Zone

11

Green Hill Zone

12

Green Hill Zone

13

Emerald Magic (not ripped)

14

Found Emerald

15+

Error

Anyway, there it is. The only music not in the current pack is the
Emerald Magic sound (when the chaos emeralds do their thing in the
ending sequence, if you collected them all), and it’s not really music so
it’s not worth adding. But I have managed to make sure there aren’t any
unused tracks.

I’m not sure why some of the tracks are repeated. They seem to produce
exactly the same music.

Alternative techniques

Since I found that $2d8 is the “new track” function, I could search for
references to it. Well, actually it’s $2d7; it’s only referenced at
$0018, which is an interrupt vector so it will be invoked by
rst 18h opcodes. There are a couple of places (the first two
are at $d3e and $12da) where a constant parameter is used for these;
the second one is for the title screen. Editing the $06 byte there will
allow the title screen music to be changed. The demo interrupts it but
the pause hack is good for solving that.

Knowing this means I could have slightly simplified my hack, by
replacing the call $02d8 3-byte patch with a 1-byte rst 18h opcode. My hack works perfectly, and spending more time to make a more elegant hack would give no extra benefit.