Thursday, December 3, 2009

SCV - SNES Code Visualiser

Hello fellow SNES lovers, this post is to discuss my SNES Code Visualiser, otherwise know as SCV. This code was hacked together very quickly, just to show an idea - it's not a finished product and I'd love to hear suggestions for improvements.

Basically I started thinking about what it would be like to plot the CPU program counter. I figured it would look like a 'pulse', and I even had a cool name for it, I called it the program counter pulse. In slow motion, I imagined seeing the PC going across the screen, following the same paths again and again. And occasionally new paths would be seen as player input forced different branch and jump logic to become true.

It was that point that stuck to me. What if we could visually identify specific code, as it happens?

The way I have achieved this is by taking advantage of the small ROM page size of the SNES. 16-bit addressing means page size is limited to 2^15 bytes when loROM is being used and 2^16 bytes with hiROM. If you assume the worst case scenario of hiROM, you can actually visually represent every single byte on the page in a neat 256 x 256 pixel square (256*256=2^16)!

To show you how effective this can be, I'm going to run you through an example usage, to find some really arcane code. Since F-Zero is my favourite, let's find the code that is executed everytime a car jumps onto and over a ramp.

Now keep in mind, the old way this would have worked is probably by tracing through the ROM over a couple of days, writing comments on a disassembled listing of the ROM, until you grasp the mechanics of the program enough to isolate events like jumping on a ramp. With this debugger, you can isolate the code in less than one minute.

Here's how: you start the ROM up

and get past the title screen, choose 'practice mode' so no other cars are bothering you, choose your car, league etc, and you'll notice as you progress further in to the game, more and more red pixels are lighting up on the screen immediately to your right. So keep going until you get to the race screen.

Now suddenly it's like a fireworks display, many many more pixels are turning red. Each of these pixels, gentlemen, is the representation of an instruction that has been executed in the ROM. In this case, you are looking at bank 0, but you can change the bank by pressing page up or down. Of course, tracking is done on all pages simultaneously, so you never miss anything.

So you start the race, and drive around a bit. Collide in to a wall. More red pixels suddenly light up. Is there any doubt these pixels represent the code that deals with collisions? Nonetheless, as more and more pixels are triggered, it's getting a bit hard to differentiate what code is what. Actually this plays to our advantage, because the more pixels that turn red, the more code you have identified as not being part of the ramp jumping routine.

Eventually, no more pixels are turning red. You are ready. Proceed to the ramp, rev your engines.

Press the 'end' key to change the color of any new pixels to green, and fly over the ramp. Immediately you see a bunch of suspicious green pixels. The tool immediately disassembles all the green pixels in to assembly language. But it's not just a disassembly - what you are now seeing on the screen is an actual trace of the code that occurred in the past, starting with the last red pixel to be run before the green pixels started. This first instruction in the trace, the red pixel which calls the first green pixel, is our testing branch.

If you press page down, you can see ROM bank 3's pixels, including a few tiny green ones. The white pixel (it's hard to see) is the highlighted ASM instruction on the right (aka. the branch).

For fun, let's NOP it. Select the instruction and press 'N'. This replaces the two bytes that make up the branch instruction with 0xEA, 0xEA. Now restart the ROM, and try to jump over the ramp. You can't. Congratulations you have just delved deep inside a SNES ROM and made a change in about 1 minute. That is the power of this tool.