Here we are, at the final installment of ROM hacking for Quadromania. I’ve got three areas left to discuss:
- Replacing graphics
- EEPROM support
- Encrypting scores to allow validated player score submissions online
Replacing graphics is not for the faint at heart. Yes, aspiring Lynx developers do have wonderful tools at their disposal — including the essential sprpck.exe sprite packer by Bastian Schick — but figuring out how to locate where graphics are stored and get the corresponding sprite control block (SCB) to point to them is another matter entirely.
When no source code is available, I have found the best approach is to disassemble the portion of the ROM which contains code (usually the second directory entry, since the first is the boot graphic), and look for spots in the ROM that push new SCBs to the Suzy chip. How do you do that? Glad you asked!
First, you need a 6502 disassembler. Actually, even better is a 65C02 disassembler, since the Lynx CPU does contain a few unique op codes which won’t be recognized by a generic 6502 disassembler. I was using a generic one (because apparently I’m too lazy or too dense to find one via Google) until Bastian recently made an enhanced d6502 disassembler for the Lynx which was just what I needed! If you’re a Windows guy like me, you’ll also need CygWin installed so you can emulate Linux in a command line shell in order to get d6502 running on a Windows PC.
Second, you need to find a directory entry in the ROM containing code to disassemble.
Finally, once you have disassembled output similar to the below snippet, you locate references to $FC10 and $FC11, commonly known as SCBNEXTL and SCBNEXTH, respectively. This combined hardware register is where Suzy is instructed to paint the SCB contained at the two-byte address. In disassembled code, you’ll commonly see references structured like this:
25DE A9 69 LDA #$69 25E0 8D 10 FC STA SCBNEXTL 25E3 A9 63 LDA #$64 25E5 8D 11 FC STA SCBNEXTH
(note I performed some text substitutions in the disassembly, so I could more easily see the Lynx hardware labels)
Given the endian-ness of the Lynx, the pointer to the SCB in this case is $6469. So let’s enjoy some basic math, assuming our program RAM load address is $400 and our program ROM address is $1253:
$6469 - $400 = $6069 offset
$6069 + $1253 = $72BC ROM address
Congratulations, now you have the ROM location of the SCB! Looking at the Handy specifications (have you noticed a pattern here, where I leverage a lot of the invaluable stuff Bastian has made available for the Lynx over the past 25 years?), we find that the sprite data pointer is located in the 5th and 6th bytes. Thus you can swap the old pointer for a new pointer to your fresh minty sprite data, assuming you have found some spare RAM where you can add said data. But wait! There’s one more caveat: you need to study that old crusty SCB to understand how the sprite data is encoded. How many bits per pixel? How is the palette data arranged? Is the sprite literal or packed? By studying the old SCB, you will learn how to run sprpck.exe to generate similar output. Then the student can become the master, and you can replace SCB pointers in ROM and see your creations come to life. I’ve done it multiple times, and it’s always rewarding to see some cool new graphic appear on the screen of a Lynx!
Feature #8: SUCCESS
OK, that section took longer to write up than I expected. Let’s move on to EEPROM support. I leveraged existing bits of code provided in the cc65 Lynx toolchain — in this case, lynxeeprom.s as discussed in this thread. If you’re writing Lynx code in C, you should be able to use the same library with C wrapper function calls around it, but in my case, I stuck with raw assembly power (cue evil laugh: MUAHAHA) because, well, it was easier than trying to figure out how to shoehorn C code into a partially disassembled ROM!
No original Lynx games had serial EEPROM support to save data; it’s strictly a hobby market creation. However, the most common EEPROM is 93c46 which can save exactly 128 bytes of data. Sounds tiny, but really it’s quite sufficient for most games if you just need some level unlocks or high scores or similar constructs.
Since all of my Songbird Lynx PCBs now feature the 93c46 EEPROM, it’s a no-brainer to utilize it on new games. I was able to take the lynxeeprom.s library with almost no modifications and incorporate it into my patch code for Quadromania. I had to carve out a couple of ZPAGE variables for the data bytes I want to read or write via the EEPROM, but otherwise it went much easier than I expected. Before long, I was saving the last level played in EEPROM and restoring it at power on. Very cool to see this feature in action!
Feature #9: SUCCESS
I also wanted to save high scores to EEPROM, as well as provide an encrypted has on a score so players could submit them online. But I had two problems:
- Saving a score to EEPROM was easy enough at this point, but Quadromania did not have a screen to show a high score, let alone a table of scores.
- Quadromania could only print decimal digits 0-9. All other text in the game is composed of sprite data.
I could have spent a lot of time adding these features as I had plenty of ROM space to play with, but in the end I felt it was diminishing returns given how much work it would be just for the sake of saving scores. Plus I figure the Lynx player base is small enough at this point that there is no need to worry about fake score submissions, should anyone ever host a high score competition for the game. Thus, while it was technically feasible to add these features, I did not pursue them.
Feature #10: FAILED
Thanks for joining me on this epic quest of ROM hacking! I hope you enjoyed the journey. 🙂 And like any good book series, while this chapter of Quadromania is closed, there is more ROM hacking which even now I am pursuing… who knows what mysteries may be revealed at a later date?