Sunday, July 24, 2011

OutRun Controller Project

The aim of this project was to have a fully playable OutRun PCB in my lounge, without the overhead of the original controls or cabinet. As an extra requirement, I wanted to be able to swap the original control panel in if desired without any complex rewiring.



The original hardware consists of three analog controls: the accelerator, brake and steering. A perfect replacement pad appeared to be the Dreamcast controller. It contains two analog triggers, which we can map to acceleration and braking plus an analog thumbstick for the steering.

Unfortunately, the original Dreamcast pad uses hall effect sensors for the analog controls and would be unsuitable. Thankfully, many of the third party controllers aren't made to the same standard and instead use cheaper potentiometers, just like the original arcade hardware! In addition to this, there are plenty of digital buttons we can map to things like the gear change and the start button.



I plumped for a Madcatz Dreamcast pad. It's a nasty piece of work, but suitable for rewiring. I cut the original cable off, bypassing the pad's logic circuits and soldered wires directly onto the necessary components. One problem is that many of the components are connected via the pad circuity in a way that makes them unusable. For example, the buttons share a ground connection and the pots are also connected. You'll want to use a continuity tester and cut the PCB tracks between the components.



Thanks to some forum assistance, I found out that the connectors on the OutRun wiring harness are 'Amp Mate N Lok' which can be bought from Swallow Amusements. Using these connectors, I was able to create a wiring harness that would plug straight into the original OutRun wiring loom. It's possible to create your own plugs without buying an official (and expensive) crimping tool. You can simply add solder to the wire, press it into the crimping pin and hope it makes a firm connection. When it does, crush the crimping pin around the wire with some pliers. It might take some time to get it right, but you can still achieve decent results.


One other thing to note is that the OutRun 50P controller connector requires 5v to be sent to pins A24 and A25 or your controls won't work. An odd design decision on behalf of Sega it would seem. The pinouts for the ports can be found below; I've used brackets to denote pins that I didn't wire up:

AMP 50P

Pin A1 Coin Sw #2                     Pin B1  Coin Sw Ground
Pin A2 Coin Sw #1                     Pin B2  (Ground)           
Pin A3                                Pin B3  (Ground)
Pin A4 Shift Switch                   Pin B4  Shift Switch Ground 
Pin A5 Start Sw                       Pin B5  Start Sw Ground
Pin A6 Service Sw                     Pin B6  Service & Test Ground
Pin A7 Test Sw                        Pin B7  (Ground)               
Pin A8                                Pin B8  (Ground)
Pin A9                                Pin B9  (Ground)
Pin A10                               Pin B10 (Ground)
Pin A11                               Pin B11 (Ground)
Pin A12                               Pin B12 (Ground)
Pin A13                               Pin B13 (Ground)    
Pin A14                               Pin B14 Ground
Pin A15                               Pin B15 Ground
Pin A16                               Pin B16 (Ground)                
Pin A17                               Pin B17 (+5V)
Pin A18                               Pin B18 (+5V)
Pin A19                               Pin B19 (+5V)
Pin A20                               Pin B20 (+5V)
Pin A21 (Start Lamp Ground)           Pin B21 (Start Lamp)
Pin A22                               Pin B22 (+5V)
Pin A23                               Pin B23 (+5V)
Pin A24 +5V                           Pin B24 (+5V)
Pin A25 +5V                           Pin B25 (+5V)


AMP 20P

Pin A1 Accel Pot                      Pin B1 Steering Pot
Pin A2 Accel Pot Wiper                Pin B2 Steering Pot Wiper
Pin A3 Accel Pot                      Pin B3 Steering Pot
Pin A4                                Pin B4 Brake Pot
Pin A5                                Pin B5 Brake Pot Wiper
Pin A6                                Pin B6 Brake Pot 
Pin A7                                Pin B7
Pin A8                                Pin B8
Pin A9                                Pin B9
Pin A10                               Pin B10


Here's a video of the final setup in action!


Sunday, December 05, 2010

Road Layer and Slave CPU Code Converted

The entire code for the slave 68k CPU has been ported. This CPU solely controls road generation and interfacing with the road hardware. It's probably the most complex area of the game code. As expected, debugging the code was relatively painful. The code now needs a considerable clean-up, but I'll do that once more of the game code is hooked up, to ensure it's more obvious if I break something whilst refactoring.

The following screenshot shows a section of curved track using both road layers on Coconut Beach. You can begin to see that all the elements are coming together and we're now in a position where we have the building blocks to rewrite the higher level code.

Wednesday, November 24, 2010

Translation Update & Driving Cabinet

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:

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:

  • 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:
  • 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.