Currently going gang busters on the slave CPU road code. It's big, it's ugly, but it's unfortunately necessary to translate a large chunk to C++ before I can proceed with more visible aspects of the game code. The level generation is highly dependent on it. Even after translation, I expected to spend a couple of weeks doing a line by line debug - Visual Studio vs. Mame Debugger. Let battle commence.
Meanwhile, Garnet Hertz provided an update back in October, with regard to their real life OutRun driving cabinet.
Check out a recent video here:
Wednesday, November 24, 2010
Tuesday, November 16, 2010
Sprite Support Implemented
A big step forward; I now have full sprite support in my framework.
Furthermore, I have ported all the low-level OutRun routines from 68k to C++ that abstract the sprite hardware from the general game code. This was a considerable effort and required some serious debugging.
You can think of the dependencies as follows:
High-Level OutRun Game Code (68k) -> Low-Level OutRun Sprite Routines (68k) -> Video Hardware.
I'm at the stage where the second two components in this sequence are done. The ported routines control some of the following areas:
Here's a slightly dull screenshot, which shows the OutRun logo being rendered. Well most of it, the observant among you will notice I didn't hook up the bird sprites as it was getting late:
So progress is good. Once I get to the stage where there is something more interesting, I'll release a demo build.
Furthermore, I have ported all the low-level OutRun routines from 68k to C++ that abstract the sprite hardware from the general game code. This was a considerable effort and required some serious debugging.
You can think of the dependencies as follows:
High-Level OutRun Game Code (68k) -> Low-Level OutRun Sprite Routines (68k) -> Video Hardware.
I'm at the stage where the second two components in this sequence are done. The ported routines control some of the following areas:
- Initializing and caching sprite palette data in RAM
- Ordering sprites based on priority
- Converting the programmer friendly format used by OutRun game objects to the format required by hardware
- Setting horizontal and vertical zoom settings from a lookup table
- Setting the height and width from a lookup table in relation to the above
- Setting the sprite anchor point
- Setting rendering hints based on horizontal flip bits etc.
Here's a slightly dull screenshot, which shows the OutRun logo being rendered. Well most of it, the observant among you will notice I didn't hook up the bird sprites as it was getting late:
It doesn't look like much, but the important thing is I can initialize a sprite simply by setting a few jump table properties using fully ported code. Here's an example of the code required to initialize a sprite object, where 'e' is a jump table entry:
e->jump_index = 0;
e->x = 0;
e->y = 0x70;
e->road_priority = 0xFF;
e->priority = 0x1FA;
e->zoom = 0x7F;
e->pal_src = 0x99;
e->draw_props = 0;
e->control = 0;
e->shadow = 3;
e->addr = ADDRESS_OF_SPRITE_DATA;
map_palette(e);
do_spr_order_shadows(e);
So progress is good. Once I get to the stage where there is something more interesting, I'll release a demo build.
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:
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.
- 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:
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.
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.
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.
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.
Subscribe to:
Posts (Atom)



