High Speed Coding for the Neo Geo!
It was inevitable, wasn’t it, that I spend so much time digging into technical details of Neo Geo games, that I’d want to make one of my own. But I’m pretty busy and am starting a new job soon, so let’s make this go as fast as possible. I imposed myself a challenge for the new year: can I make a Neo Geo game in one week?
The challenge
So, for fun, and to keep my mind off of everything that’s made this holiday season difficult, I decided to give myself a small programming challenge: port the game 2048 to the Neo Geo in one week, using only assembly language, and write a blog post about it. (You’re reading that one) It doesn’t need to be pretty, but it does need to work, at least for the Arcade (MVS) system first. AES, CD, all that are stretch goals. Hell, having music is a stretch goal.
2048
But it’s 2021, and you might not be familiar with 2048; it’s been several years since the game went viral, after all. 2048 is a simple puzzle game where you slide tiles across a 4x4 grid. Each turn, if possible, a new tile appears. Combining two “2” tiles creates a “4” tile, combining two “4” tiles makes an “8” tile, and so on; the goal is to create a “2048” tile, 2^11. It’s a pretty simple concept but it actually becomes very addictive; its popularity was aided by it being free and open source, and you can play it online here.
As an aside, the game is, shall we say, heavily inspired by prior games, notably 1024 and its inspiration, Threes. Threes is a bit different in movement rules and uses powers of three rather than powers of two, and it’s a commercial game with a lot of effort gone into its unique art and presentation. The iOS version still works on modern versions of iOS; on that OS, that’s hardly guaranteed for a game from 2014; definitely recommend it.
I’m porting 2048 rather than Threes for two reasons:
- Threes is a commercial game, so I can’t look at their source code, and I wouldn’t want to step on their toes anyway
- 2048 has simplified rules; for example, no equivalent to the “1” and “2” tiles in Threes, so I figured it’d be easier
The guides
When working on a project like this, there’s no reason to deny yourself the resources that are out there. Here are some things I found utterly invaluable.
- The ChibiAkumas NeoGeo tutorial series; I started with his Hello World! and went from there.
- The Neo Geo Development Wiki
- AJ/Freem’s site, especially for sound tutorials
Being Nicole, I also had to make my own tools, including a simple conversion from my Terraformer format to Neo Geo sprite graphics and sound. Interestingly, MAME (which I used as the test emulator) requires you lay out your files in the same way as the ROM chips, and therefore you need to put your sprite bitplanes in separate files. It’s a bit weird; other than that, though, the format is pretty simple, and it wasn’t too hard to adapt the “Turboize” routines I wrote. Again, the ChibiAkumas tutorials were invaluable.
Tools
VASM
This was my first time using the VASM assembler; it’s a popular multi-platform assembler with a dizzying array of supported architectures and syntaxes. I used “Motorola-style” 68000 and the “Old-Style” Z80, since that seems to be the most common.
One interesting feature of the Motorola 68000 assembler is that it’s an optimizing assembler. Specifically, it can convert things like short-distance jumps into branches, and use more optimized equivalent instructions. This is interesting, because I’d expect most assembly programmers to want a byte-by-byte level of control. But for me, who is just getting started and would like the assembler to fix my newbie mistakes, it’s pretty useful.
In fact, the optimizations are disabled in the ChibiAkumas NeoGeo tutorial (since it’s in “devpac” mode). Why is that? It’s because in the 68000 program header of all Neo Geo games, there are hardcoded JMP
commands. The optimizer tries to turn those into branches, and that throws off all the addresses below. Oops.
In my code, I just hardcoded the bytes for a JMP
instruction, so I could benefit from the optimizations everywhere else. I wonder why they use a JMP
rather than just a pointer; I guess it made some things faster.
Format conversions
While I used my own graphics format for sprites, I did make some use of other tools for some other formats.
- NeoFixFormat, a plugin to allow editing the “FIX layer” format. Yep, just like the TurboGrafx-16, the Neo Geo’s incredibly limited tilemap doesn’t use the same graphics formats as the sprites do.
- ROMWak, to convert ROMs to the format that MAME expects
- And Freem’s ADPCM-A tool for converting samples
Getting started
This screenshot may look like a foolish joke. And it is! But it is also the first change I made, after getting the ChibiAkumas Hello World project to compile. It reveals something interesting about the Neo Geo.
The famous “MAX 330 MEGA” version of the boot sequence (the “eyecatcher”) is stored in the Neo Geo BIOS ROM. However, the graphics and sound for the boot sequence were not! Therefore, games that want to use that must include the ROMs and their own version of the boot music. Later games with the “GIGA POWER” opening don’t use the BIOS ROM eyecatcher at all.
I’m sure my “MAX 0 MEGA” gag would have prevented the game from getting certified by SNK. Oh no!
Modes
You might know that the Neo Geo arcade system was commonly seen in multi-video systems; in fact, that’s where the acronym MVS comes from. This referred to having multiple game cartridges in your game system, which are alternated between, and a select button to jump between them.
But what I didn’t realize is how much that means that the game BIOS needs to be aware of to make this all work. So there are four basic routines that are specified in the header:
USER
, which is code that requests the game to perform pre-specified actions and enter its game loop. The pre-specified actions are initialization, bootup, the demo, and the title screen; therefore, every game needs to recognize these. Things like jumping to the title screen when coins are inserted are handled by the BIOS.PLAYER_START
, which runs when the player starts a game (for example, a valid start button press is made)DEMO_END
, which ends the demo early, if a multi-slot board needs to switch, for example. This lets the program do any cleanup.COIN_SOUND
, which is used to let the game know a coin was inserted and to play a sound effect. The Neo Geo relies on each game’s sound driver to do this.
This makes early coding quite a bit more structured; if you don’t match the structure and behave as expected, the BIOS will likely break.
Nobody said your title screen had to be good, though. It just needs to exist. That timer, which forces you to start when it reaches zero, is yet another thing that’s provided by the Neo Geo BIOS. (Also, the time is stored in binary-coded decimal, for some reason) Whether you show a value or not, when that compulsion timer reaches zero, PLAYER_START
will be called.
Gameplay
The rules of 2048 are surprisingly subtle! Early on, I looped over and over to move tiles to the side when you pressed that direction, combining when possible. But consider the following order: 2 _ 2 4
. By the rules I described, you’d get _ _ _ 8
. In standard 2048, that should give _ _ 4 4
. So I had to turn to the source; converting the algorithm from object-oriented JavaScript to a 68000 function wasn’t easy. In fact, it convinced me that any large Neo Geo project I make in the future should use C! (After all, the 68000 is far better suited to it than the HuC6280)
Still, the game logic eventually worked out pretty well, and the game quickly became addicting. This meant that my coding worked! Bits of the old system remain in the check for if any possible moves remain. Oh, and I added a basic score system, though didn’t bother to do any stretch goals like saving it to a memory card.
I also added an incredibly simple “window-blinds” style animation to add new tiles to the game. It uses the Neo Geo’s horizontal scaling; compare the pixels to the “2” tile in the screenshots above. Neo Geo scaling horizontal skips columns of pixels.
Sound
The sound engine is one place where I relied heavily on pre-existing tools and demos; since I have no Z80 background, and also have never written a real sound engine (Aspect Star N used FamiTone, and Space Ava 201 CD-DA and PCM sound effects), I just relied on some basic ADPCM, using the example code on the Neo Geo Dev wiki.
This game is so simple, I probably could’ve made a small little tune and had space in the 1MB of bankswitch-less ADPCM-A sample area to spare. But all I did was throw in a silly joke Neo Geo theme and a coin insert sound effect as a proof of concept.
Conclusions
Neo 2048 is not a great game. It’s not even really a good game. I’m not sure it’s even a game; it’s more the proof of concept that, if I wanted to, I could make a Neo Geo game. But I’m counting this as a success!
I’ve posted the code on my GitHub, of course, it doesn’t include things like the copyrighted Neo Geo BIOS. While the code is the interesting part, I’ve also included a binary release.
I might try porting it to the Neo Geo CD or seeing if it works on AES later, but for now my week is up, time to post this, and then apologize to a furry cat for not making a game that will make the quarters fly to his arcade.