It's been nearly two years since I started rewriting OutRun, and three years since I begun decompilation work. This is, and feels like, a long time. According to Yu Suzuki, the original game took four programmers between eight and ten months to complete, so I'll take some reconciliation from the fact this was only a part-time project.
Following the success of the decompilation work, I expected the rewrite to be plain sailing. In fact, the rewrite proved tough - really tough! The size and complexity of the codebase meant I spent an inordinate length of time debugging. Writing your own code from scratch is comparitively easy; your intentions are clear and tracking errors is straight forward. Finding the source of a bug in thousands of lines of ported assembler can be a nightmare.
Debugging ultimately became a case of stepping through the suspected area of code line-by-line and comparing results with the MAME debugger. I invoked crazy tactics along the way; I coded routines to utilise MAME memory dumps for the road layer to quickly determine whether bugs were caused by erroneous code or if data in memory was at fault. This also enabled me to compare the original with my port from an identical starting point.
The other complication was the way in which the original codebase was designed and structured. The style of code varies dependent on who was working on it and by god, they produced a lot of it. The hardware specifications were insane by 1986 standards, and the coding team appear to have approached the project with the view that space and clarity were not a primary concern. There is a huge amount of code duplication and multiple routines that perform similar tasks with minor modifications. Despite evidence of code reuse at Sega, there should be no doubt that this is disposable code, not a reusable game engine. In fairness, the programming team would have been under considerable time pressure.
So what's next? I'm going to port the final chunk of code to handle the game completion sequence. This consists of a big switch table to manually send commands to the sub CPU handling the road layer (similar to the road split, but not quite the same), code to control the Ferrari AI during this period (similar to attract mode, but not quite the same), code to blit the timing information to the screen (similar to other digit blitting routines but not quite the same) and code to handle the animation sequences (similar to the start line intro sequence but not quite the same). Now, you're beginning to understand the OutRun codebase!
Once this is complete, I will release a new build for testing purposes. I was going to release sooner, but I'm so close in terms of porting the entire core engine I'm going to hold back. This build will run at 60fps and feature a selection of other minor improvements not in the original game. From this point onwards, the fun begins and we can start to include extra functionality and enhancements. It will also be a good point to port the code to a variety of platforms. I will be looking for help once I tidy up the codebase a little further.
I hope that explains where the project is, feel free to comment below if you have questions.
UPDATE: Tantalisingly close... bonus points code done, bonus sequence AI done, bonus track control done. Just the animation sequences now.
Following the success of the decompilation work, I expected the rewrite to be plain sailing. In fact, the rewrite proved tough - really tough! The size and complexity of the codebase meant I spent an inordinate length of time debugging. Writing your own code from scratch is comparitively easy; your intentions are clear and tracking errors is straight forward. Finding the source of a bug in thousands of lines of ported assembler can be a nightmare.
Debugging ultimately became a case of stepping through the suspected area of code line-by-line and comparing results with the MAME debugger. I invoked crazy tactics along the way; I coded routines to utilise MAME memory dumps for the road layer to quickly determine whether bugs were caused by erroneous code or if data in memory was at fault. This also enabled me to compare the original with my port from an identical starting point.
The other complication was the way in which the original codebase was designed and structured. The style of code varies dependent on who was working on it and by god, they produced a lot of it. The hardware specifications were insane by 1986 standards, and the coding team appear to have approached the project with the view that space and clarity were not a primary concern. There is a huge amount of code duplication and multiple routines that perform similar tasks with minor modifications. Despite evidence of code reuse at Sega, there should be no doubt that this is disposable code, not a reusable game engine. In fairness, the programming team would have been under considerable time pressure.
So what's next? I'm going to port the final chunk of code to handle the game completion sequence. This consists of a big switch table to manually send commands to the sub CPU handling the road layer (similar to the road split, but not quite the same), code to control the Ferrari AI during this period (similar to attract mode, but not quite the same), code to blit the timing information to the screen (similar to other digit blitting routines but not quite the same) and code to handle the animation sequences (similar to the start line intro sequence but not quite the same). Now, you're beginning to understand the OutRun codebase!
Once this is complete, I will release a new build for testing purposes. I was going to release sooner, but I'm so close in terms of porting the entire core engine I'm going to hold back. This build will run at 60fps and feature a selection of other minor improvements not in the original game. From this point onwards, the fun begins and we can start to include extra functionality and enhancements. It will also be a good point to port the code to a variety of platforms. I will be looking for help once I tidy up the codebase a little further.
I hope that explains where the project is, feel free to comment below if you have questions.
UPDATE: Tantalisingly close... bonus points code done, bonus sequence AI done, bonus track control done. Just the animation sequences now.
15 comments:
Marvellous work as always, I'm still hoping it'll work on an X68000, not likely!
Anyway, here's a issue I've been thinking about. You know how when going through the gate to the second stage the times tend to be the same, e.g. 58'25 or 59'21 and so on. How does it get these numbers, simply by rounding? How close are they to the actual time the player did the stage in etc.?
As an aside, I just got a Super Hang-On PCB and noticed that it saves the high-scores. Does it use the same principle as your new code for Out Run?
Thanks!
Continues to patiently wait ;-)
Magic Knight: Have you checked out my extend time post here:
http://reassembler.blogspot.com/2009/11/extend-time.html
Not sure how the Super Hang On PCB saves scores, but I'd imagine it's a similar principle as they're the same boardset :)
I think an X68000 port from this work is unlikely! That would be more of a rewrite.
I've seen that, yes, thanks. What I mean is: let's say you go through the gate in 58'49. Does this get rounded down to 58'25 or rounded up to 59'21 for the purpose of displaying it?
The X68000 has a tremendous version of Super Hang-On, it's the best home version by far. I don't know why Out Run was never translated to it. We got Full Throttle instead...
We can only dream some maniac will take on the task.
The laptime milliseconds can range from 0 to 99. OutRun runs at 30 fps, and it uses a lookup table to convert the frame number into a value between 0 and 99. Therefore, you are likely to see similar millisecond values reported (and never see other values reported!)
I think that answers your question as to why you'd see similar values if I understand correctly?
Magic Knight: I can answer your question about the individual stage times. I believe there is a bug in the original code that causes lap times to be displayed incorrectly. The value for seconds is accurate, but the hundredths of a second is not, as it appears to be mistakenly taken from the current lap instead of the lap just completed. You can verify this in MAME by pressing "P" to pause the game a few car lengths before the checkpoint, and then holding down the SHIFT key and pressing "P" repeatedly to advance one frame at a time. On the frame where the lap time first appears, you'll notice that the hundredths of a second matches the time for the "just started" stage (ex: 57.25 and 00.25). This behavior is consistently observed 100% of the time.
I'd not noticed that before.
I'm on a work trip this week, but once I'm home I'll look into this further - the code will reveal the answers.
I can potentially fix the bug on both the original game, and the remake.
Thank you both gentlemen! I've been testing both the Japanese early release (outrundx in MAME) and the later western releases. Going left I consistently get 59'25 and going right 59'21. If I get under 59 seconds going right it's 58'25 that I'm credited with. I have both the old Japanese version PCB and Rev A, and they display the same times as in MAME. I'm sure a look at the code will reveal all.
It's not a big deal or anything, but I was always curious as to why the time given never varies.
Thought I'd share a snap of the Outrun 2 cabinet whilst at the Arcades today. http://www.flickr.com/photos/skippyxp/7753913814/in/photostream
I looked into the lap timer bug.
The same routine is used by the timer in the top right and the extend time routine.
The routine takes the stage number, and looks up the appropriate time (minutes and seconds) from memory. However, it uses a cached millisecond value converted by an early routine.
Therefore, the millisecond value is indeed shared by mistake.
It's an easy fix to correct this on the new version. It would be a bit of a bodge on the original as I can't think of a way to cleanly insert the code yet...
That's great, nice to get confirmation on what stella_blue said. As you say, it should be an easy fix on your new version. I don't suppose it's too big a deal on the existing version, but it's interesting to know.
Yes, I'm pleased that my observations are supported by the underlying code. I've been chasing the Twin Galaxies MAME record on this title for several months now, and my pursuit has involved a fair amount of frame-by-frame analysis of the various stages. I have a number of other questions, comments and observations related to this game. Given that it may be a lengthy post, should I attach my comments here, or would an email message be more suitable?
E-mail them over (use the address in the following url: http://javagear.co.uk/images/email.png) - I can always answer them in a separate blog post if they will prove useful to others.
Do you think that an Amiga 1200 AGA port would be possible? Think about it, the Amiga port was terrible and should have been better but would it be possible in 320*256 in 256 colours without too much colour reduction. A 68020 at 14MHz with 2Mb is no match for 2x68000s at 12.5MHz + 1 Z80. Is there a clean separation of the rendering code and game code?
Also compressing it to fit on one floppy would be a challenge.
A decent conversion would be possible - a direct port not so likely.
By the time you added the overhead of emulating the OutRun road rendering hardware in software on the A1200, other optimisations would be needed. The A1200 does have hardware sprite scaling I believe, so that would help considerably with other areas of the video emulation.
Using the original code and graphics would yield better results than the original port in the hands of a capable programmer.
Not sure if the floppy issue would be the real challenge? You could just split over a couple of disks and load into RAM.
Post a Comment