People who know me know one of my favourite arcade games of long ago was a vertically scrolling SHMUP named Star Force. I can still remember those family trips to Hunstanton on the coast every summer, and me eager to play SF… Anyway, as I owned a C64 at the time, I did write a game inspired by SF, one I named Neutron.
Before Neutron could be finished, I got my first job at a place named Orpheus, and Neutron was to be one of my 3 games completed and released. Due to its likeness to SF, I had to make some changes (okay, so I did copy the music a little), but finished the game.
Unfortunately, Orpheus closed down before the game was released, and when I gained a new job at Imagitec, the Amiga had become the machine to code for, so that pretty much ended my C64 days…
Time passed, and all my old disks and computers were trashed, me foolishly thinking they were obsolete and thus of no use anymore. Yes, I do regret doing that, more than anyone can guess…
Fast forward to the present, and my return to C64 programming, something fun for me to do while on a writing break caused by a touch of writing burnout. I started coding an RPG based on my Briley Witch books, and I still have every intention of finishing that game once my current project is finished.
But towards the end of December 2018, I came across a competition, the RGCD 16KB Cartridge Game Dev Compo. So on 21st December, I decided I’d enter a game. I wanted to code something fairly simple, something that would fit into 16K, and remembering my old Neutron, I decided I’d reincarnate that and code a brand new version using all the knowledge I’d gained over the many years of programming.
Neutron Is Reborn!
So there was around about a 6 month time limit for completing a game, and that suited me; I do like a challenge, so I was extremely motivated to code Neutron.
For one thing, I wouldn’t be starting from scratch as I already had most of the tools I needed from all my work on my RPG. Build system and 6502 assembler were mostly good to go, and so was my sprite editor. I did have to adjust my character/map editor a bit, but more on that later… And I already knew how to boot from a cartridge as my RPG is a 512K cartridge game. I did have to change my build system to create and run 16K cartridges, but that was a pretty trivial change. Game code-wise I had all the code from my RPG to use, including keyboard/joystick reading, maths routines, data decompression, and a vertical scrolling routine (with CRAM scroll). So to start, I took the all the code from my Briley Witch RPG and stripped out everything I didn’t need, right down to the bare essentials. At the start I didn’t have a general sprite multiplexer, but I accepted the challenge of coding one…
16K Isn’t A Lot Of Space…
It’s true, 16K isn’t a lot of memory. But I knew I could use a few coding tricks to make it fit. For starters, and since compression was allowed (and encouraged), I knew the game could be much bigger than 16K as long as the final size could be compressed to under the 16K limit. For my RPG I’ve been using PUCrunch, and since my build system was already using that, I used the same code for Neutron. The main data file for Neutron ended up being ~23K in size, compressing down to ~13K. The sprite file is 5.4K, compressing down to ~2.4K. I had to use some tricks to help the sprite file compress better, but more on that another time…
The C64 has a 40×25 character screen and uses an accompanying 4-bit colour RAM (CRAM) screen of the same size. Multi-colour character mode uses two bits per pixel = 4 colours. One colour is the screen colour, while two more are from the multi-colour registers shared across the screen. The 4th is the character colour, and that is read from the CRAM.
I knew from working on my RPG that it’s not possible to scroll both screen and CRAM at the same time and have enough CPU time to run a game. Instead, my RPG uses a double-buffer scrolling system, and Neutron would use the same system.
I had a quick look at other games and knew I wanted to get as much colour on screen as possible, and that meant a full-screen colour scroll. Some older games looked like they were using 4×4 character blocks, but I think that can look ugly, so I dismissed that idea right away.
At first I used the same block size as my RPG: 2×2 characters. But unlike my RPG, I decided to restrict the scroll routine to just one character colour per 2×2 block instead of per character. Again, I had to alter my character editor, but that proved to be a quick and simple change. Using one char colour per block made a massive difference to the scroll system, which I’ll come back to…
The C64 has hardware scrolling, or rather the ability to shift the screen by 0 – 7 pixels. On the 8th pixel the scroll is reset and the whole screen needs to be scrolled. Since Neutron scrolls at a constant speed of one pixel per frame, that gave me 7 frames before the whole character screen needed to be scrolled. I use a system where the front buffer is copied to the back buffer in 4 character line slices, offset by one line. It takes 6 frames to “scroll” from front to back, and this minimises the CPU hit per frame. The 7th frame is used to fetch the next line of data to be scrolled. On the 8th frame, the two screen buffers are swapped and the CRAM scrolled.
CRAM was a problem at first for one major reason: scrolling the entire screen takes a lot of CPU time! The fastest way I know is a series of LDA $aaaa, STA $bbbb instructions for every character on screen. But that takes up loads of code, and I had to fit everything into 16K. Well, the solution was simple: build the code. In other words, I added a small amount of code that could build the massive subroutine calls I needed. I should mention that the competition states the cartridge has to be 16K, not that everything has to fit into 16K of RAM, so for speed Neutron uses a lot of the C64s RAM by building lots of unrolled loops.
And since I had decided on using one character colour per block, that reduced the amount of data the CRAM scroller had to shift. For example: If line 0 and 1 is green, and 2 and 3 are blue, after a scroll, 0 is new data, 1 and 2 are green, 3 and 4 are blue etc… Since lines 1 and 3 haven’t changed, they don’t need to be scrolled each time. As a result, only even or odd lines need to be scrolled. That halves the amount of data that needs to be moved. Also, since the blocks are 2×2, CRAM only needs to be fetched for every 2nd character, speeding it up a bit more. This plus a massive subroutine of load/store instructions meant there was plenty of CPU time remaining.
So after knocking out some quick graphics, I could code the scroll system. For colours, I decided to use the tried and tested (on my Briley Witch RPG at least) multi-colours of Brown and Medium-Grey. In fact, I used some of the graphics from Briley for the edge of the islands as it fitted pretty well.
For all the stages I wanted to fit in memory, I knew I couldn’t define the block maps without some sort of compression. To make life easy, I opted to use a map section system. Basically, the map consisted of sections that were 20×8 blocks, and I could use my map editor to define these. To build a level, all that was needed was a per-level sequence of bytes selecting each section. By varying the sequence, I could make different looking levels. So a sequence ended up looking like:
At first it was a bit of a nightmare typing in all the numbers, especially as at first (for speed) I was typing in the numbers pre-multiplied by 8 and with 7 added (to point at the bottom line of each new section). Seeing a string of numbers like 48+7 and 56+7 didn’t look very clear, and I had to keep checking back with the map editor to see what went where, a most laborious process.
The solution: add a map sequence editor to my map editor so I had a visual aid to build all the levels. Oh, BTW, did I mention I enjoy coding tools..?
A Change Of Block Size
One last change to the scroll system was the block size. I wasn’t happy with the 2×2 block size, feeling it looked a little squashed in the X. So, I tried a 3×2 block size. Disadvantage was a non-power of 2 size, making addressing screen blocks (needed for player bullet -> background detection) a bit trickier, plus adding extra characters to the character set. But the advantages are 1) it looked much better, 2) the CRAM scroll was slightly faster, 3) the map section data was much smaller, 13×8 blocks instead of 20×8. I knew saving RAM would be important, so 3×2 blocks it was! Also, to address a screen block quickly, I added a divide by 3 table. Oh, and to save cart size, I added code to generate the table, saving a few precious bytes.
A block size of 3×2 and a screen size of 40 characters would normally mean I would’ve needed 14 blocks to span the screen, but I solved that by reducing the visible screen width to 38 columns by activating the horizontal scroll. Also, 13 blocks equals 39 characters, which also means one column less of screen data to scroll…
Another feature of Neutron is a slight amount of horizontal scroll. Now Star Force has a good bit of side scroll, and I wanted to incorporate something like it. Using the hardware smooth scroll on the C64 lets me move the screen left/right by up to 7 pixels. A small amount perhaps, but I think it adds a bit of dynamics to the whole screen. This is also a feature that I had added to the original Neutron, so it was a nice feeling to recreate it for the new game.
I See Stars…
With the scroll working, there was one last element to add, and that was a starfield. At first I wanted to add a parallax starfield, but opted for something that wouldn’t consume too much CPU time. So I preset the number of stars, fixing their locations.
Although the stars are static, the screen is still scrolling. To keep the stars steady is just a simple case of scrolling a star character’s data up as the screen scrolls down.
For speed, I added some code to auto-generate four routines: two to plot the star characters (one routine per double-buffered screen), and another two to erase them. You see, the way my scroll works, copying from the front screen to the back screen, needs the front screen to be clear of stars when it performs the copy. So when the scroll system is called at the start of the v-blank period, first the stars are cleared, then a screen section is copied, before the stars are re-plotted. There’s enough CPU time in the v-blank to do this before the raster beam begins to repaint the screen.
For speed, my clear stars routine is nothing more than a list of LDA #??, STA $aaaa instructions. I opted not to mess with the CRAM, making my stars use either multi-colour 1 (medium grey) or multi-colour 2 (brown). Since multi-colours are shared across the screen, using them does not involve messing with the CRAM, thus simplifying things no end.
Plotting stars is just a straight block of code that basically reads from the screen and self-modifies the appropriate clear routine to re-plot the character. If the character read is 0 (my transparent char), a star is plotted, else the plot is skipped.
Well, that’s all for part 1! Hope you found it interesting. Anything I missed, please add a comment. Will probably do a part 2 all about sprites…