One of my dirty secrets is that I still love to play Commodore 64 games from my youth. I have a good emulator program, and the experience is just like the original except for the hardware.
One of my favorites is Dino Eggs. I’ve become curious about how it was actually programmed. I’d like to find out how certain variables and timed events in the game are determined and executed.
I haven’t delved into computer programming since… well, since doing BASIC on the C64 when I was a kid. So I have no idea what I’m getting into here. Even if I could somehow expose the code, it would probably mean nothing to me. How can I gain an understanding of how the game was programmed and what’s happening when I play?
On this page there are some 65xx disassemblers. The CPU is pretty simple, but one issue with the Commodore 64 is that many/most games used all kinds of direct hardware access and clever hacks to squeeze every last bit of performance out if this low-end machine. If the game uses some really crazy stuff it might be difficult to understand if one is not used to the machine, but stuff like simple timing that you describe sounds pretty straightforward.
There’s a fine line between “ingenious hacks” and “incomprehensible spaghetti”. And games of that era tended to walk a tightrope down that line. Most modern programmers would probably scream in pain at the code (at least, at the portions they could understand).
You are probably never going to be able to find the original source code, so the best you can do to see how it was programmed is to disassemble the binary code, like DPRK suggested. Unfortunately, as you note, the end result is likely going to be fairly incomprehensible to you, as the game’s original variable names, subroutine names, and source code comments are all long gone.
If you want to really understand how the game was made, you will need to really dig into serious 6502/6510 processors and how they were programmed in assembly, and even then, deciphering the output from a disassembler isn’t going to be easy.
btw IDA Pro is a good disassembler if you are not averse to commercial software, and there is also a free version.
The first link above (codebase64.org) also lists some of the “ingenious hacks” Chronos mentions. E.g., you’re obviously going to use undocumented opcodes, self-modifying code, etc all over the place to shave off a few execution cycles or bytes of RAM.
Brings back memories of being a little kid. We had a C64 at home and one day my dad was working on some programming thing and remarked that he really wish he had a disassembler. I biked my 8 year-old self down to a local computer store ready to surprise him with the greatest gift ever for father’s day. They had one, it was $250.
Even for an experienced developer of that type of game on that hardware, reverse engineering the code can be a bit challenging and time consuming. If you don’t have experience with assembly and interacting with the hardware it will be even tougher.
For example, on the machine I worked on, the analog to digital converters (joystick input) and the digital to analog converters (sound output) were just bytes in memory (special bytes) you either read or write to. If you were looking at the code without any background, you would be wondering by the program seems to update memory location 178 frequently (sound output, 178 is made up because I forgot which byte, it might have been something like 65384).
There were such things as unassemblers for the 6502 back then. These would take the raw output from a dumb disassembler and try to substitute the known names for system calls and hardware registers, and assign (random) names to the program’s branch points, variables and memory areas. With lots of hours of work, you could eventually end up with a program you could modify and reassemble. Ah! when we have the luxury of time…
This blog post by the author (also this one) describes some of the code for Dino Eggs (“My core graphic routine was a central chunk of self-modifying code…”); that’s describing the original Apple II version and not the C64 port, but the processors are pretty compatible.
If you just want to see some sample source code, the codebase web site has several small sample games in addition to tutorials on game programming techniques, C64 hardware info, etc. Also http://covertbitops.c64.org/ has a bunch of games along with their source code.
I had a copy of this when I was a kid. It’s an exceptionally good walk-through of the machine - starts with Commodore Basic and how that interacts with the hardware, and then moves on, explaining as it goes. You’ll need something like a 64MON cartridge if you’ve got an actual C64 to play with, or a simulation thereof, if you really want to do the assembly language.
What I’m saying is: don’t try to understand decades-old disassembled code that the original developer probably doesn’t even have much of of a clue about. Have a go yourself!
Did you ever define a sprite, or muck about with the sound chip? Because if you did then you had a grasp of how the machine architecture works. Those POKES and DATA arrays - they were way closer to the hardware in a way that doesn’t just happen these days.
It’s been many, many years since I’ve done 6502 assembly and the C64, but what you would need to do is, well, learn 6502 assembly, as well as become intimately familiar with the memory map of the C64 and an understanding of the computer at a very low level. It’s not impossible – that’s just what we did in the 80s if we wanted to get beyond slow-ass BASIC and get some arcade-style performance out of the machine. I think it’s possible to figure out what’s going on without the actual source code, but just the assembly instructions, as you’re working with a fairly small memory map and code space here. But if you have no experience at all with 6502 assembly and no idea how the C64s memory addresses are laid out, then, yeah, it’s gonna be quite a lot of studying to get up to the level where you can start figuring routines out.
Within the ridiculously tight confines of available memory of that era, there was also a thin line between “ingenious hack” and “deep Magic”. Which sadly doesn’t really seem to exist in our world any more.
Basically what most people think of as programming is higher level programming- it deals primarily with concepts and data elements.
However, in the compilation or interpretation process, that gets translated into machine level code, i.e. assembly code. This is about one level above bits and bytes.
So for example, if you were to have a few lines of code like this:
a = b + c // assign a the value of b + c, where b and c are already assigned values.
That translates into a whole raft of assembly
First, you’d have to move the values of B and C into registers out of memory.
Then you’d have to add the two registers together.
Then you’d have to store the result back in memory.
All this is extremely dependent on the actual architecture of the system in question. The flip side is that since it’s so low level, you can really optimize it to be fast or small or whatever.
And you can often do stuff that compilers and interpreters don’t let you do- like change the code of your program on the fly by rewriting the bits and bytes to change the actual instructions.
A decompiler would basically take the machine language code and try and basically turn it into something a bit more readable and abstract than assembly code. But it’s necessarily inexact, because there’s not always a 1:1 correspondence between chunks of assembly code and higher level language components.