Saturday, November 06, 2010

Support for Tile Layers Implemented

The hardware tile layer is now supported in my framework. So in addition to the text layer previously mentioned, the ported code can now utilize tiles.

Here's a screenshot to provide an example of this, using ported code to display the tiles from the music selection screen:


Much of the detail from the music select screen is missing, because it also makes use of sprites, which are currently unsupported by the framework.

To summarize the components of the port, the following 68k code has been ported to C++ in order to reach the above stage:
  • Routines to setup palette ram
  • A new text routine to blit text with a height of two tiles to the text layer (this displays the Select Music By Steering text string) 
  • The routine which decompresses a tile map from rom and outputs it to tile ram
  • The routine to update tile hardware on a vertical interrupt
And the framework itself emulates the following:
  • Tile Layers
  • Text Layers
  • Palette Hardware
So we're getting to a stage where basic routines are coming along nicely. The final ported C++ code is more readable and far more concise than the original assembler.

Tuesday, November 02, 2010

The OutRun Rewrite Begins

I've made a start on the rewrite. Here's a quick summary of what I've been up to aside from brushing up on my C++ skills. Firstly I've installed and configured a suitable build environment, which consists of:

- Visual Studio 2010 Express C++
- DirectX SDK
- SDL (for rendering)

From there I've written code to:

- Read the tile and data roms into memory.
- Ported emulation code to emulate the text layer and convert the pixel format.
- Rewritten three 68k assembler routines decompiled from the OutRun source code.

The end result is that I can now use a fully ported C++ routine that blits text, in the form of tiles, to the screen. This routine takes the precise same input format as the original code. In the following screenshot, I've called the routine to display various text strings from the game. A simple call to blit_text(address_of_text_data) renders the text with the correct palette and screen positioning. Exactly the same as the original assembler.



The structure of the text data (which contains palette info, tile info and screen positioning) is pulled straight from ROM. Although I imagine I will eventually extract such data structures so that they are completely native.

I appreciate that drawing some text isn't particularly riveting, but the first steps in a project are always the most laborious. I'm pretty happy with the way things are going.

Tuesday, October 12, 2010

GOAL

Decompilation is complete: 13 months later, over 400 subroutines, ~20,000 of lines of commented assembler (a rough estimate) and countless late nights.

Pretty much everything is done to a standard I'm satisfied with. But it's important to also consider what has been omitted:
  • Sitdown Motor Code. I have only decompiled a small portion of this, as it's not particularly useful for the rewrite. It's also hard to verify without a simulation of the hardware. Having said that, I've decompiled and commented the motor code for both the upright hardware variants. This could be adapted to a simple rumble controller easily.
  • Z80 Sound Code. Currently, this isn't needed. The master 68000 CPU triggers the sound samples and sets sample volumes for the engine pitch. Therefore, it's straightforward to work out what is going on without resorting to lower levels. I may decompile this at a later stage out of curiosity.
  • Complex Algorithms. There are a minor number of routines which, although commented and broadly understood, could be better explained by someone with a better knowledge of Maths/Physics than myself. In many cases it's obvious that a routine is performing a square root or similar. But there are certain routines that I might have not fully grasped. Nevertheless, I am confident I have done enough to convert these to meaningful C routines that aren't gibberish. 
  • I'm sure I'll find and fix mistakes in my comments as I convert the game.
The upcoming enormous challenge is to rewrite the game engine in comprehensible C++. I will be releasing the rewritten source code as I work, so that those interested can follow my progress. I initially expect visible results to be slow, so please be patient. If there are any questions, please post in the comments and I'll happily answer anything sensible.

Thursday, September 30, 2010

Binary Translation, Emulation and Decompilation

Here's a link to an interesting article outlining the choice between binary translation and emulation. It might help convey why I'm taking a radical approach in porting OutRun and not automating binary translation. As mentioned, binary translation certainly has its uses in producing code that can be hand optimised and altered. However, it ultimately results in code that is also unwieldy and in hard to decipher.

Even in cases where the original source code is available and can be translated by a tool, the results are often no better. I've worked with C code, automatically translated from 68k assembler, for a number of games during my days as a mobile developer. Often the output from such tools didn't resemble the target programming language in a meaningful manner.

Although decompiling and manually rewriting OutRun is a massive task, hopefully the end results will yield code that is understandable, portable and easy to enhance. By the end of the year, I will have completed the decompilation phase and commented an estimated tens of the thousands of lines of assembler. Maybe the end results will be superior to the original source code in areas. It's hard to say, as I don't have access to it!

One of the more gruelling aspects of the decompilation is the sheer amount of code. For example, I'm currently commenting code that controls the animation relating to smoke, dirt and other debris emitted from the Ferrari's wheels. The animation format and code to handle these routines differs yet again from all previous examples. I've lost count of how many animation formats the game contains.

One of the reasons the code is so bulky is that the programmers have seemingly reinvented the wheel for various aspects of the game. It wouldn't be difficult to code a generalised animation system, at the expense of a small amount of speed. As it is, many high-level features share little in terms of code or design despite relying on the same low-level routines. This results in a lot of code duplication.

Wednesday, September 08, 2010

OutRun Unused Lap Time Code

There is a fair amount of unused code compiled into the OutRun ROMs. Much of it consists of redundant helper functions, that serve no interesting purpose.

I came across an unused routine today that's slightly more interesting. Firstly, OutRun stores the 'lap-time' of each stage you've completed - even though this information isn't really made use of (other than showing your previous lap-time on passing the checkpoint).

Either for debug purposes or as an unfinished feature the programmers included a routine to sequentially print these times during gameplay below the HUD.

Entering the following command in the mame debugger will print the lap-time for a particular stage you've already completed in a race.

d0 = x; pc = 7fea; g; [where x is the stage time to print]

Here's an example screen grab:


Here, I'm on Stage 3 and I've printed the lap-times for the previous two stages using the unused code routine. The lack of apostrophes in the additional lap print outs indicate that this idea was probably pulled at an early stage.

Code decompilation is progressing nicely. I'm on track to finish this year.

Monday, August 23, 2010

Animation Format

I've disassembled the code relating to the animation sequences displayed after you complete a route. There are five animation sequences dependent on the route you've taken. I'd hoped that the animation format would be similar to the code to handle the animation sequences during collision, for example the Ferrari rolling over. Unfortunately it was completely different.

If you're using the same revision of the ROMs as myself, you can trigger the sequences instantly in the MAME debugger with the following command:

w@60be8 = x; pc = 9978; g [where x is the end sequence number 0-4]

Animation frames  are stored in groups of 8 bytes, and formatted as follows:

+00 [Byte] Sprite Colour Palette
+01 [Byte] Bit 7: Make X Position Negative
           Bits 4-6: Sprite To Sprite Priority
           Bits 0-3: Top Bits Of Sprite Data Address
+02 [Word] Sprite Data Address
+04 [Byte] Sprite X Position
+05 [Byte] Sprite Y Position
+06 [Byte] Sprite To Road Priority
+07 [Byte] Bit 7: Set To Load Next Block Of Sprite Animation Data To 0x1E
           Bit 6: Set For H-Flip
           Bit 4:
           Bits 0-3: Animation Frame Delay (Before Incrementing To Next Block Of 8 Bytes)


Each sprite (or object if you like) is assigned an address containing the animation sequence data. The animation terminates when bit 7 of byte 7 of a chunk is set. Two other animations use the same format at the start of the game - the map waving the start flag, and the Ferrari driving in from the side of the screen.

Saturday, July 17, 2010

Object Logic

The sun has been out, I changed jobs plus I'm getting married in a couple of months. These factors have conspired against me getting as much done lately.

Having said that, I've finished decompiling and understanding the routines that handle both the logic of in-game objects and their rendering behaviour. In plain English, the scenery that makes up the levels. There are 15 routines in total with a lot of code duplication between them. The level object structure contains a field that denotes which routine should be used.

One example is the checkpoint sign at the end of a stage. Each sign consists of two objects; the pillar of the sign and the header which reads "CHECK". Each part of the sign is drawn by a separate routine. The top of the checkpoint sign is drawn by a routine that also determines when the user has past the sign (to handle further game logic like loading the next stage). Whereas the base is handled by a similar routine that doesn't make this check. Additionally, there are two checkpoint signs at the end of each level and only one of them handles the check. Therefore, it cannot be assumed that because an object looks the same, it will always be handled by the same routine.

There is further complexity in these routines. Let's take the water strip sprites, seen on the left hand side of Coconut Beach (Stage 1) as an example. Each strip is processed by a custom routine that examines its screen z value. If the z value is particularly close to the screen, the sprite is simply zoomed. However, at distances stretching into the horizon the frame is completely changed as well as zooming being applied. There are alternate versions of this routine that swap frames based on the y value of the sprite. This variant is only used on one of the Stage 5 levels.

The palette changing logic for the countdown lights at the start of the game is also embedded as part of this logic. Some routines handle collision checking where necessary too. It's worth noting that I had to play all 15 levels just to understand where the routines were used. Simply looking at the code to figure out what was going on was impossible at times.

Moving on, I'm going to work sequentially through the rest of the decompiled program code. I'm at a stage now where a significant amount of work is complete so there are just gaps to fill in.