It has been a year now since I started working at MuleSoft!
I spend my day in a mixture of coding (mainly NodeJS and Java), whiteboarding, standups, tracking down issues, sprint planning (often with the help of pizza). Its been a great year and I feel like I’ve learned a ton about building reliable distributed systems. Best of all there are lots of smart, friendly people to learn from :)
We’re always hiring, so contact me on twitter or github and I’ll be happy to answer any questions!
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…