In the original Need for Speed 1 game, there are 9 open-road tracks in environments (Alpine, Coastal, City). They are ‘real’ roads with traffic, as opposed to the circuit tracks where you simply raced a number of laps. The end of each open-road track is marked by a checkpoint, and once you drive underneath it, the car brakes to a stop. The faster you drive under the checkpoint, the further past the checkpoint you would travel before stopping. When I played this game back in 1995, I was fascinated with what lay beyond the checkpoint, and always tried to see around that last corner! For people who haven’t played the original (or did, but forgot in the last 20 years, heres a quick clip of exactly that happening.) See the track carrying on to who knows where!
With OpenNFS1, (my open-source NFS1 engine) we control whether the checkpoints stop your car or if allow you to drive right past them.
We can now see those parts of the track which haven’t been seen by anyone except the original developers back in 1995.
In this screenshot, we’ve driven past the checkpoint, and are looking back at it. This is the first time a screenshot has been captured showing the track from this position :)
So how are the checkpoints implemented in the Need for Speed engine?
A checkpoint is a piece of scenery, just like a tree or road sign. Each scenery item has a position and an orientation, relative to the track, and a pointer to a scenery descriptor. A scenery descriptor describes a type of scenery, including size, textures, vertices, animations etc. Many scenery items can share a single descriptor.
After some digging, it turns out that the checkpoint scenery descriptor always has a resourceId of 0x7c, which points to the checkpoint texture for the track, and also flags to the engine that this is the checkpoint.
There are no easter eggs unfortunately - it would have been great to find a photo of the dev team or something at the end of a track. But anyway, heres a video showing each of the previously lost sections of the open-road tracks!
I’m going to go ahead and assume you know (or can figure out) 6502 assembly, buffering, interrupts and raster timings. In any case, heres a incredibly brief description of the graphics configuration: I’m working in character mode, so the screen area is 40x25 characters, and the RAM for the screen data is a relocatable block of 1000 bytes. In addition, there is a fixed 1000 bytes of color RAM to provide a color for each character. There are many different graphics configurations, but in this simple case, we’re assuming character-mode, single color-per-character graphics. The VIC-II is the graphics chip in the C64. Programs can set interrupts on the VIC-II based on the current raster line, so we can run code, for example, after the last line has been drawn each frame to update game logic etc.
Default screen memory is at $0400, color RAM is at $d800. The following simple code will put some characters on the screen. This is nothing you wouldn’t find in any hello world tutorial, but it shows just how simple it is to start coding for the C64.
Jumping ahead now, taking that simple example and expanding on it, lets imagine we now have drawn a screen like this:
How do we scroll the background across the screen? The simplest approach is to move each byte in each line of screen RAM to the left. Then, at the right edge of the screen, we draw the new column. This results in jerky movement though, as the screen moves a character (8 pixels) per frame.
To avoid this, the VIC-II chip has two hardware scroll registers, one horizontal, one vertical. They allow the screen to be offset by up to 7 pixels in each direction. Heres how that works:
Start by setting the scroll register to 7 (assuming we want to scroll to the left)
Each frame, you decrement the scroll register by 1
The screen contents move 1 pixel to the left
Once it falls below 0, shift the entire screen RAM contents over by 1 byte
Reset the scroll register to 7
Now the screen contents have again appeared to move to the left by 1 pixel
Rinse and repeat.
Here is an implementation:
We’re now copying 1000 bytes of screen RAM, 1000 bytes of color RAM, plus adding the new columns and dealing with the hardware scroll register. And we need to do it all before the VIC-II starts drawing the first line of the next frame. You think the 6502 CPU can do all this?
Nope! Not a chance! The flickering and tearing you see there is caused by screen/color RAM being updated while the screen is being drawn, and hardware scroll register being updated in the middle of the frame.
Alright, we need to get a bit smarter at this. Currently, we do nothing for 7 frames except twiddle the scroll register, waiting for it to fall below 0. To use this time, we need to implement double buffering. We’ll reserve another 1000 byte block in RAM to be our back buffer. This allows us to shift screen data at any time, because we are writing to the back buffer, not the visible screen.
Unfortunately, this still isn’t good enough, because it can take longer than the vblank to shift the screen ram, which means there is no time left over for game logic. We have to split it out further, and shift portions of the screen on different vblank intervals. In the example below, we copy screen when the scroll value is 4 and then 2.
The interesting thing to note here, is that we can shift portions of screen RAM across multiple vblanks thanks to our back buffer, but we can’t do the same with color RAM. Color RAM is fixed and cannot be buffered or relocated in memory. Unfortunately, we still don’t have enough time to shift 1000 bytes of color RAM, and run music and game logic in a single vblank. What can happen is as we’re shifting color RAM (and adding new colors at the right edge of each row), the VIC-II is also rasterizing the screen, pulling values from the same color RAM that we’re writing to. When the shifted colors are different, the screen tears and flickers as colors are updated.
The final piece of the puzzle, then, is how to deal with this color RAM. From reading a bunch of forum posts it sounds like there are several ways to handle it, but here is what I’ve implement and works nicely:
Add a new interrupt a few lines below the top of the screen. When the interrupt fires, and xscroll==0, we know that next frame we need updated color RAM.
In that case, shift the top half of color RAM now. Because we start the copy a couple of lines below the top of the screen, we follow behind the raster beam down the screen, shifting color entries which have already been used on this frame.
On the vblank interrupt, after swapping screen buffers etc, we start shifting the lower half of color RAM. As long as we’re careful, we can shift it all before the VIC-II starts rasterizing the lower half of the next frame!
And there you have it. If anyone has a different implementation of this, I’d sure be interested to see it! :) Now, onto actually making a playable game…
TL;DR - Dumped a 17 year old debugging symbol file for a game called Carmageddon. Take a look at the symbols folder to see the output.
In the Carmageddon Splat Pack folder, there is a file called ‘DETHRSC.SYM’, last modified 13th November 1997.
It has sat there, un-noticed and un-loved for the last 17 years, ignored by the internet. Having made a remake of the Carmageddon engine, and being generally curious about random binary files, I tried to figure out the file format. Immediately by looking at it in a hex editor, it was obviously a debugging symbol file, the question was which type of symbol file? Of course, there are many symbol files with a .sym extension, and after some trial and error, it turned out to be a Watcom symbol file. I grabbed a copy of OpenWatcom and fired up the debugger, wd. It could read the symbols, but I never found any Carmageddon executable that matched up with it. It seems likely it was left there by mistake from a debug build.
Using wd to look at the symbols 1 by 1 in a little DOS window quickly becomes tiring, so then I wanted to dump the symbols out. For that, I needed the source code for the Watcom tools, and a working Open Watcom development environment.
(…Fast forward a while getting the environment up and finding where the code for handling symbol files lives…)
In the Watcom world, symbol file support is provided by various DIPs. To use a DIP dll requires the calling program to implement various client-side methods to allow the DIP to alloc memory etc, and then to provide callback functions for the DIP to call when walking the symbol list. It’s all pretty complex, but luckily there are a couple of utilities which illustrate generally how it should be done. I based jsonsymdump off dipdump (which is advertised as dumping symbol files to text format, but crashes on DETHRSRC.SYM). I did the minimal amount of work in C required to generate a valid json file, then wrote a node.js script to take that json file and generate some semi-valid-ish c files.
We now know all the methods and properties that were implemented in at least some build of the Carmageddon engine. It’s not going to allow anyone to reverse-engineer the engine without a matching executable, and we don’t know how any of the methods were actually implemented. But it is interesting to see how things were named and organized, and by reading the method names, one can make some good guesses at what is happening at a high level in the game engine.
Files underneath the DETHRACE directory are Carmageddon.
Files underneath the BRSRC13 directory are BRender, a 3d graphics engine which Carmageddon used.
Some of the symbol names are funny, some might offend people, and some make no sense unless you are one of the original developers! A few examples:
How to build and run yourself (if anyone ever wants to…)
Follow the README in jsonsymdump to build the C binary