Jump to content
NHL'94 Forums

REFERENCE: NHL 94 ROM hacking school

Tony H

Recommended Posts

Since the original message is old and newer tools are out, I'm updating TonyH's post.
Without Tony's posts, I would never have done any ROM hacking.
It was his posts that let me find the weight bug code and fix it.


A few people here in the NHL94 forums are interested in
finding out how I find useful ROM addresses in Genesis
games (ie. NHL 94). I (TonyH) have decided to write
a guide that explains how to do it. If anyone has any
questions as I go along, please feel free to ask. I'm
typing this in a hurry, so I may not explain everything
too well.

Here's a list of what you'll need...

1) Gens ReRecording

2) A hex editor (your choice. suggestions: HxD (it's free) or bpsoft.com (free trial version).

3) NHL94 ROM (in bin format -- use the Maha/swos version
because the checksum has been removed)

4) NotePad++

5a) Some basic 68000 assembly language knowledge is
helpful, but not absolutely necessary.

5b) Knowing hexadecimal numbers might be useful, too

Here's what that stuff is for...

- We'll use Gens ReRecorder to find RAM addresses
and make memory traces and assembly trace logs.
Some people use Kega Fusion to find RAM addresses.

- The hex editor is for finding ROM
addresses, and the NHL94 ROM is what we'll be poking
around in trying to find ROM addresses.
- Notepad++ is used to open the really huge 'trace'
files that Gens ReRecording creates. The normal
notepad that comes with Windows can't really handle
how big they are (in XP anyway). You can omit it and
use the default Notepad in windows, but it'll take
a long time to open the files.

To try to keep this relatively simple, we'll find the
ROM address that determines how much time you get for
each period. The game normally lets you select from
5, 10 and 20 minute periods, and there is the "cheat"
that lets you have 30 second periods. I'll show you
how to find those values in the ROM so you can change
them to any value you want, and I'll show you how to
stop the clock completely so you'll have infinite time.
Not that you'd want infinite time, but mainly to show
you how it's done.

Since making Game Genie codes is kinda my "thing", I'll
also show you how to make Game Genie codes for all this

Okay, lets get to the good stuff...


- The first thing we need to do is find the RAM address
for the clock. This is very easy to do. Just start up
Gens ReRecording, load your NHL94 ROM, and start
a game of the updated Maha/swos ROM.
- When the clock starts to count down, click the ESC
key to pause the game. Then, click on "Tools ->
RAM Search". Click the "reset" button.
- Click back on the emulator and then hit ESC again
to resume the game. Let the clock count down 1 or 2
seconds and click ESC again to pause.
- Now, in the RAM Search window, make sure "less than",
"Previous Value", and "1 byte", are all checked,
then click on "Search".
- Go back to the game (ESC to unpause) and let the clock
count down a few more seconds. click on "Search" again.
- Keep repeating this until you only have a few RAM addresses left.
- Unpause the game again, and watch the numbers. You'll
see one that decrements each game second. That's the clock
value in RAM. (It won't have the same time as the clock,
but it'll be decreasing at the same rate as the clock)
- For this example, I'll tell you that the RAM address for
the clock is FFC469. It's actually FFC468, but for our
purposes (and to keep this shorter), FFC469 will work

Okay, so we know that the RAM address for the clock
is FFC469. Now we need to find out where in the ROM
this RAM address is being loaded, modified, etc.


In the Gens ReRecorder directory, there is a text file called
"hook_log.txt". Open up that text file
and change it so it looks exactly like this...

hook_pc1 0 -1 -1
hook_pc2 1 -1 -1
hook_pc3 1 -1 -1

hook_rd1 0 -1 -1
hook_rd2 0 -1 -1
hook_rd3 0 -1 -1

hook_wr1 0 ffc469 ffc469
hook_wr2 0 -1 -1
hook_wr3 0 -1 -1

hook_ppu1 1 -1 -1
hook_ppu2 1 -1 -1
hook_ppu3 1 -1 -1

(There's other stuff down here, but just leave it as is).

As you can see, we put our RAM address for the clock
in there for a hook on write (hook_wr1). What this
does is tells ReRecorder to make a note every time
that RAM address is changed, and what ROM address
caused it to change. This is similar to breakpoints.

Save the changes you made to your "hook_log.txt" file.

IMPORTANT: You need to close and re-run Gens ReRecorder
each time you change this file, so it sees your changes.

(^ No longer true as of version r304)

- Load the ROM.
- As soon as you see the "NHL Hockey 94" title screen, go to
"Tools -> Tracer Tools -> Hook RAM". This starts the memory
(RAM) tracer.
- Now keep pressing the "Start" button (usually the
"Enter/Return" key on your keyboard) until the game starts.
- Wait until the clock counts down about 3 or 4 seconds
and go to "Tools -> Tracer Tools -> Hook RAM" again (or use
the Ctrl+Shift+[period] shortcut key). This stops the
memory tracer. Exit Gens ReRecording.

The memory trace that you just made for RAM address
FFC469 has been saved to a text file called "hook.txt".
When you open up "hook.txt", this is what you should

(NOTE: if you had 10 minute periods, you'll see 0258.
5 minute periods, 012C. 7 minute periods, 01A4)


[01:730A] W32 = 00000000 [FFC468]
[00:7830] W16 = 0258 [FFC468]
[00:7830] W16 = 0258 [FFC468]
[01:5DFC] W16 = 0257 [FFC468]
[01:5DFC] W16 = 0256 [FFC468]
[01:5DFC] W16 = 0255 [FFC468]
[01:5DFC] W16 = 0254 [FFC468]


There's some very useful info in this file. A quick

[01:730A] - this is the address of the instruction in
the game ROM that wrote to the RAM address we traced.
It is usually not exactly right, but close.
W32 - "W" means "write." "R" means "read".
"W32" means it was a write of 32 bits. W16 means
write of 16 bits.
00000000 - the value written to RAM
[FFC468] - The address of the RAM that was written
to. Notice that it's FFC468 not FFC469. So the game
actually wrote to FFC468 (and the number size was bigger
than 8 bits, so also modified the next byte(s))

Next I'll show you how to use the info in the "hook.txt"
file to find the ROM addresses we're looking for.


P.S. Let me know if there's something I didn't explain
too well.

(See next message for more...)

  • Thanks 1
Link to comment
Share on other sites

Part 2 of Tony's guide, updated for new tools

In this section, you see CPU instructions. If you want to learn about the instructions, try either of these tutorials:

tutorial one tutorial two (no particular order.. use the one you find easier..)


Glad to see some interest in this post. That will help

motivate me to finish.

Okay, lets get started again.

In my last post, here was the memory trace we made:


[01:730A] W32 = 00000000 [FFC468]
[00:7830] W16 = 0258 [FFC468]
[00:7830] W16 = 0258 [FFC468]
[01:5DFC] W16 = 0257 [FFC468]
[01:5DFC] W16 = 0256 [FFC468]
[01:5DFC] W16 = 0255 [FFC468]
[01:5DFC] W16 = 0254 [FFC468]


(NOTE: if you had 10 minute periods, you'll see 0258.

5 minute periods, 012C. 7 minute periods, 01A4)

Here's what each of those lines mean...

[01:730A] W32 = 00000000 [FFC468]

This one just sets the value to 00000000. nothing we need.

[00:7830] W16 = 0258 [FFC468]

This one tells us which ROM address moves the value 0258

to our RAM address for the clock. 0258 is a hex number.

0258 = 600 in decimal. There are 60 seconds in a minute,

so 600 divided by 60 = 10 minutes. 10 minutes is the

amount of time that is on the clock when the game started.

We can use the ROM address in this line to help us find

the actual ROM address we're looking for (the ROM address

to change the amount of time you start the game with).

[01:5DFC] W16 = 0257 [FFC468]

This one tells us the ROM address that is subtracting one

number from the clock value for every second that goes by.

We can use this ROM address to help us find the instruction

that is causing the clock to count down. By changing this

instruction, we can stop the clock (infinite time). This

is where a little 68000 assembly knowledge can be helpful.

[01:5DFC] W16 = 0256 [FFC468]

Same as above, except one second less.

[01:5DFC] W16 = 0255 [FFC468]

Same as above, except one second less.

And so on.

After you've done several of these memory traces, you can

quite often just open up the ROM with a hex editor and

go to the ROM address listed and figure out what you need

to know. For example, I can open up my NHL94 ROM with a

hex editor, go to ROM address $015DFC, and instantly see

what instruction I need to change to have infinite time.

But since learning how to read raw 68000 assembly is

something most people don't want to do, here is how you

can make an assembly trace log that will show you everything

that's going on in the CPU, and send it all to a text file

in a "relatively" easy to read format. When you first see

your assembly trace log, it's easy to be overwhelmed by all

the crap in there, but don't let it scare you off. After

you get used to it, it's not really that hard to understand.

An assembly trace log is getting down into the real nuts and

bolts of how a Genesis game works. If you can learn how to

"read" it, you can find all kinds of very useful ROM


Okay, here's how to make an assembly trace log. Open up

your NHL94 ROM using Gens ReRecorder. Get to the first option

screen (the one where you can change Play Mode, Per Length,

etc). Go to "Tools -> Tracer tools -> Trace" (or press the

Ctrl+Shift+/ keyboard shortcut. This starts the assembly trace

log (which slows the emulator down). Now press your "Start" button

3 times so that you're on the ice and ready to start playing. Wait

for the clock to count down a few seconds and then go to "Tools ->

Tracer tools -> Trace" again to stop the assembly trace log. Exit

Gens ReRecorder. Your newly created assembly trace log has been

sent to a file called "trace.log"

Keep in mind that the Genesis CPU is operating at about 4Mhz

(roughly 400,000 instructions per second) so the trace.log files

can be very big.

Gens ReRecorder is different from the old trace tool: if you make

a second trace, it will not overwite the old trace.log, but will

append the new trace to the end of the old one.

So, the file can get really really big if you run several traces.

I like to rename each trace file to something that describes what

I was looking for when I made the trace. Then if I make another

trace, they are separate.

Now lets open up our new Trace.log file and take a look at

it. I recommend using something like Notepad++ or MS Word to open it


We'll start off by figuring out how to make the game have

infinite time. When you have the file opened, use the "Find"

option and type in 01:5DFA. Here's how I came up with that

number... Look at the memory trace we made:

[01:730A] W32 = 00000000 [FFC468]

[00:7830] W16 = 0258 [FFC468]

[00:7830] W16 = 0258 [FFC468]

[01:5DFC] W16 = 0257 [FFC468]

[01:5DFC] W16 = 0256 [FFC468]

[01:5DFC] W16 = 0255 [FFC468]

[01:5DFC] W16 = 0254 [FFC468]

See where it shows the time starting to count down...

[01:5DFC] W16 = 0257 [FFC468]

[01:5DFC] W16 = 0256 [FFC468]

[01:5DFC] W16 = 0255 [FFC468]

[01:5DFC] W16 = 0254 [FFC468]

0257 seconds

0256 seconds

0255 seconds

0254 seconds

The ROM address shown for each time a second is subtracted

is always the same: 01:5DFC, so we know that the instruction

we're looking for is near by that ROM address. The way that

this memory trace works, you usually need to subtract

2 from the ROM address listed. So to get the ROM address

we need to look for in our Trace.log file, we just need to

subtract 2 from 01:5DFC => 01:5DFA. That's how I came up

with the number to put in the "Find" box.

Alright, back to the Trace.log file... type in the ROM

address we're looking for (01:5DFA) into the "Find" box and

click on "Find Next". Your first match should look like


01:5DFA 53 78 SUBQ.W #1,($C468) A0=FFFFBDEC A1=FFFFCA32


D0=FFFF0100 D1=00000122 D2=00000000 D3=00000183 D4=00000006 D5=0000000C

D6=0000000A D7=00000001 XNZvC

This tells us exactly what we need to know to give us

infinite time. SUBQ.W #1,($C468) means that it is

subtracting 1 from RAM address $C468. If you remember,

FFC468 is our RAM address for time. All we have to do

to get the clock to stop counting down (inf time), is

figure out how to kill that SUBQ.W instruction. We can

easily do this by changing the "53 78" opcode shown above

to a branch opcode (6002) instead. This will make the

game jump right over the subtraction part. We can test

this out by making a quick Game Genie code...


(smozoma: Or, go to the next section where you edit the ROM instead)

(smozoma: NOTE: I can't get the game genie codes to work in Gens

ReRecorder... so just go to the next section!)

Just use a Game Genie conversion program (you can get one

from my (Tony_H's) web site):

015DFA:6002 = AKRT-CA94

015DFA is the ROM address, and 6002 is the replacement


Because it is beyond the scope of this guide, I'm not

going to go into how I came up with 6002 for the opcode

to kill the subtraction. This is where some 68000 assembly

knowledge is helpful. Here's a quick tip... using a branch

opcode (usually 6002 or 6004) is the most common way to

kill many opcodes (subtraction, addition, etc). 6002 will

"jump" 2 bytes, and 6004 will "jump" 4 bytes. You'll

usually want to jump to the next opcode. NEVER jump to

an operand (this can cause the game to freeze).

Anyways, if you try our new Game Genie code (AKRT-CA94)

with your favorite emulator, or on a real Genesis, you'll

see that you now have infinite time.

A little short cut... Gens emulator will let you enter

Game Genie codes in raw format, so instead of converting

the ROM address and opcode into a Game Genie code, Gens

lets you enter it like this: 015DFA:6002. Saves a little



You can easily hack infinite time into your ROM by going

to ROM address 015DFA and changing the 5378 to 6002. Of

course you'll also need to hack the master code too (or

else you'll get a blank screen) (smozoma: this is why I

say to use the Maha/swos ROM or any other edited ROM,

since it'll have the checksum removed so you don't need

"the master code").

Well, that's it for now. Next we'll find out how to find

the ROM addresses for period lengths. I'll also explain

what some of that stuff is in the assembly trace log.


P.S. As always, if something is confusing, let me know

and I'll try to explain it better.


I (smozoma) made a program called "trace nice-ifier" that makes the trace files in this tutorial a little easier to read.

Here it is:

trace nice-ifier.zip

Download and unzip to location of your choice.

Drag-and-drop a trace.log file onto "trace_niceifier.exe"

It'll create another file called trace.nice.log that'll be a bit easier to read.

  • Thanks 1
Link to comment
Share on other sites

  • 2 years later...
  • 2 years later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Who's Online   0 Members, 0 Anonymous, 15 Guests (See full list)

    • There are no registered users currently online
  • Create New...