During the last few months, I've been working on something entirely unlike anything I've done before: a game for the Nintendo Entertainment System, a console that's about as old as I am. And I love it!
You don't often hear good things about writing code for resource-constrained systems from the 1980s. This ain't your dad's VAX. It's closer to one of those mind-numbing 8051-based machines than to the elegant M68K-based computers of the Amiga era.
I've written code for 1980s-era machines, and I've painstakingly reverse-engineered firmware for 1980s-era machines. I was pretty familiar with the 6502 when I started, but it's really not the CPU core that gives you trouble on machines from that era, it's the peripherals around it. (Unless that core is the 8051, which is more dreadful than a Turing machine with fragile paper tape and a really bad tape reader). With few exceptions, touching machines from that era isn't fun at all. Most of them are nothing like the Amiga, they're obscure systems that integrate obscure peripherals with all sorts of obscure quirks.
But I was really curious about it, and cc65 seemed to do pretty well with the C64, a platform I'm very familiar with. So I thought I'd give it a try.
Well, much to my surprise, the NES is great to write code for!
Like most machines from that era, the NES isn't fast enough to give your pixel-level access to the video memory. You blit sprites -- 8x8 blocks of pixels -- at specific positions on the screen.
My game has a rudimentary, but fairly convincing physics engine. The main loop is essentially:
- Process input
- Do lots of fancy (OK, what the 6502 would call fancy) math in order to...
- ...set each sprite's position
- Draw sprites
Barring two minor leaky abstractions, it's as MVC as a good Ruby on Rails app.
Scrolling horizontally or vertically is pretty trivial: there's just enough VRAM memory to arrange two screens next to each other, horizontally or vertically. A 256-pixel window "glides" across these, and a pair of hardware registers hold the vertical and horizontal offset of the upper-left corner of this window. Scrolling both horizontally and vertically requires some mind bending but it's so refreshingly straightforward that working with OpenGL now makes me grumpy.
The most straightforward way to do profiling is graphical. If you write a particular magic value in a particular control register, every row of pixels drawn after that is drawn in a slightly darker color. If a function is ran while the screen is being painted (and most of your logic does), you can write the magic value at the beginning of the function, and restore the old one at the end of the function. Then all you gotta do is count the scanlines. My profiling tool is, literally, a ruler for Windows. FCEUX's built-in debugger is pretty good, too.
The system is documented in great detail. I've written serious code for serious systems that were documented in less detail, and more inaccurately, than the NES. (If you're just getting started, Doug Fraker has a set of tutorials that aren't bad at all!)
Writing code with so little friction is something we rarely get to do anymore. It's a quality that the platforms and development tools of our age don't really prioritize anymore, and I don't know what to think about that.