Back when I was a wee lad we had a wondrous machine at home, a computer. You could hook it up to your TV and make it do what you wanted. It had color, sound and lots of games. This particular incarnation was called Commodore 64, or C64 for short.
Very fast I turned to writing programs and games, albeit in BASIC. It was cumbersome, and slow, and you could get nowhere near commercial quality. For a short period I tried to get into Assembly, failed at a (in hindsight) little problem and gave up.
Years went by... In the meantime I meandered to the PC (DOS, then Windows) where I started a programmer's career.
Fast forward to today. Today's development is heaps and bounds beyond imagination from 30 years ago but I've always had a soft spot for the C64 after all this years. Emulators appeared and became better and better, so I sat down and tried to start assembly programming on a C64 (via emulator). Having learned a lot in the meantime it just felt easy. One game grew under my fingers, supposed to be a kind of farewell to a good old friend, and it became Joe Gunn. And behold, making it was tremendous fun!
So finally this Tutorial series was born, to show a younger audience how a C64 game would be written from the ground up. The subtle and not so subtle pitfalls of the architecture and the developing approach will be shown, and maybe lure one or the other veteran back in ;)
Note: This article is both introduction and partial post mortem on a series of journal posts that detail the development of a Commodore 64 game over the period of two years, including required assets and code source. To directly jump to the tutorial entries go here. Read on for further details of the C64 system and how the game turned out.
A C64 Game in Several Steps
The C64 is a rather simple machine for today's conditions. It has 64 kB of RAM, with ROM overlayed, like shown below. One of its main advantages are specialised chips for sound and graphics. They are controlled by "registers", which are blended in at a specific RAM location and can be written to/read from (I/O in the graph below). For example: write a value to $d020 (53280) to change the color of the displayed screen border.
Conveniently the ROM can be selectively turned off to have code running and accessing otherwise hidden RAM, which was used for most games excessively.
Graphic display is handled by the VIC II. Display comes in several classes: For one there's the mostly used text mode. Characters are made up of 8 by 8 pixels and can be redefined. A screen is then worth 40x25 bytes for characters plus 40x25 bytes for the colors. Then there's bitmap mode which allows a higher diversity in display and colors, but then weighs in at 8000 + 1000 + 1000 bytes (quite a lot for only 64 kB total). And last but not least, the sprites. Sprites are little (24 by 21 pixel) objects than can be put anywhere freely. Most common was the text mode, as it allows quite nice graphics without too much memory used.
Sound is handled by the famous SID chip. It allows 3 voices with separate envelopes in parallel. In the right hands some awesome music was and is composed.
There are a handful of decent emulators out there, which are also available for most systems. Currently I prefer VICE for the great development support. A monitor with a full fledged (albeit a bit unwieldy) debugger is built in.
Today you wouldn't use a code editor on the C64 itself anymore. There is a nice batch of different cross assemblers and even IDEs. I started out with ACME and TextPad for a code editor, but later wrote my own, Visual Studio inspired IDE. Connecting to VICE it allows for convenient debugging through your code, which helped me tremendously for those nasty bugs. With a key press code is assembled and started in the emulator.
The 6510 comes with a simple and rather rudimentary set of opcodes. If you ever touched assembly on a x86/64 you will miss a lot of nice mnemonics and most of all, all the registers. The 6510 has one accumulator and two registers. Everything else must be stored in RAM. It has a few convenience functions (indexed addressing) that allowed for easy, indirect lookups (think pointer tables). Due to being 8 bit any math opcodes only operate on 8 bit values.
The CPU does not come with things like multiplication and division. One of the things that stumped me was displaying a decimal number. You just have to manually calculate the division by 10. Not really hard to do but the absence of such a common functionality today feels weird.
Where things get funny
There are two hardware types, PAL and NTSC. Both run at different frequencies (60 Hz NTSC, 50 Hz PAL), but at the same speed. NTSC however has less time per frame. A lot of games simply run 20% faster on NTSC.
Display is tightly coupled to CPU cycles. One cycle equals eight pixels. The more complex your game becomes the more you may need to fiddle to time a graphic setting at the right moment to avoid flicker.
Generic code is nigh impossible. The machine is not too fast, and especially for writing games you mostly need to tailor the code to the situation at hand.
There is no safety net. Code is data, and vice versa. Write a byte at the wrong location and your code crashes in unpredictable ways. On the other hand this allows you to copy code all over the place, and create self modifying code (actually useful).
Emulation is very good - you can write whole games without ever having a real machine. However still, the more in-depth you go, the better it is to actually have someone test your game on the real thing. It's a good thing there is still quite an active community out there, so you're likely to find someone willing to help you out.
For the game in this series I had a rough idea in my head. It should have Bubble Bobble style gameplay, but with a touch of survival. During the first third of the project the game engine was built up and a lot of detail was used on the code part. Design came during the latter two thirds. Once the player could run around among enemies the gameplay would be tackled. Since at the time I was watching Supernatural that's the direction the game took.
One of the first things to make the gameplay more survival-like was the need to actually stand still to reload your gun. I think that worked out pretty nice actually. It gets quite frantic when you're in the middle of a bunch of spiders and you just fired your last bullet.
As a nice fit for a second game where Sam had to make an appearance. To make the gameplay differ the second player got a magic force that worked quite different from the shotgun. Reload was not required, but standing still while the force hurt the trapped enemy worked quite similar.
At a later time I wanted to present a choice for the player types, but in the retail version I ran into all kinds of problems. Quite a few code parts were specialised to the shotgun player being player number 1 and the force player being number 2. Getting those ironed out took a lot of testing.
Especially in the beginning there was no new ground to cover and everything was simply writing out required base code. Sprite display, controlling, enemy behaviour, level structure, nothing ground breaking.
Music and sound effects again do not need much work for the actual game coder. Richard Bayliss provided the music and sound effects as a runnable binary. Include the data, jump at a routine at regular intervals, done.
Things got interesting with the screen buildup. Every screen is compiled by several primitive types, simple character lines, single elements, lines of elements or areas of elements (an element being a rectangular block of characters and colors). This was a nice exercise as this part can and has been reused for other games.
During the last 20 steps memory got full. The C64 doesn't allow you to load a file into the full 64 kB RAM, only from 2049 up to 49152. The memory above can be used but data has to be copied there manually. That's when Exomizer came in. Exomizer is an awesome cross packer specialised in older machines. Exomizer would pack a file usually to about 60% to 70% of the original size. Remember, free memory is precious.
For the retail release disk and tape versions were to be made. That was quite easy, as the game was designed as a single load and could be directly put on those media. However there should also be a cartridge version. The C64 allows for a cartridge via the cartridge port. Two in lines would determine how the memory of the cart would be shown to the CPU.
The cart in question had a 64 kB ROM. The C64 however only allows 8192 bytes of cart memory be accessible at once (actually that's two times 4196 bytes). To access the other parts the shown "banks" could be controlled by writing specific values to a memory location.
The memory at the cart location would just behave like the normal ROM, read from ROM, write to RAM below. This was tackled with the simple approach: Blend in cart banks, copy data into RAM, blend out the cart and work like before.
It is awesome to see how far you can go all on your own (well, mostly). Still, having others, professionals, help out with graphics and music is still very much required. And what else I learned: Back then people didn't have all these convenient tools at their hands. Or an internet to look things up.