Disassembling Crossroads, part 1
Manage episode 450312748 series 3429299
One of my all-time favorite games for the Commodore 64—and I know I’m not alone in this—is Crossroads, the single-screen maze shoot’em’up by Steve Harter, published in Compute!’s Gazette magazine as a type-in program in December 1987. The game features dozens of enemies of a variety of types and colors, all fighting each other in a cacophony of attacks and explosions. You’re dropped into the fray to collect items called “spars,” which provide you some protection against attacks, and which the enemies also consume. The game supports one or two players on joysticks, and both players battle for survival simultaneously.
Some people like to say that typing in programs from magazines and books helped them learn how to write programs of their own. I can see how that might be true for BASIC programs, though I can’t honestly say I learned anything from type-ins when I was in grade school. For programs written in languages other than BASIC, Compute! offered no way to learn. In this interview with Steve Harter by Kirk Israel, Steve says he wrote Crossroads in assembly language, but Compute! never published assembly language listings for full programs in magazines. Instead, Compute! published such programs as columns of numbers, along with a helper program that assisted with keying these values directly into memory. The most you could learn from these type-ins was data entry, and perseverance.
(As an aside: I’m aware of only two cases where Compute! published full assembly language program listings for the C64, both as books: the LADS assembler and the SpeedScript word processor. Steve wrote Crossroads using LADS.)
Over the next two issues of this Digest, we’ll crack open the original Crossroads program and see what we can learn, using two modern reverse engineering tools for your PC. This month, we’ll take a look at Retro Debugger, a C64 emulator with real-time memory visualization features. To keep this newsletter to a reasonable length (😬) we’ll discuss just enough to get started, and ask a few specific questions of interest about the game.
As always, let us begin with some MEGA65 news!
New Filehost feature: high score tables
Filehost has a fun new feature for competitive MEGA65 gaming: high score tables! When you upload your game to Filehost, you can choose to enable “Highscore submission.” This allows players to submit their high scores by clicking the plus ("+") button on your game’s Filehost detail page, uploading a screenshot as evidence. All score submissions must be approved by you before appearing on the page. You will receive a message when a player submits a score for your game.
Check out the high score table on Classy as an example.
Thanks as always to Tayger for the Filehost innovations! Everyone go out and rack up those high scores!
Galaga, Ghosts’n’Goblins, and Xevious now available for R6 boards; TI-99/4A now available for R3 boards
Over the last year or so, muse has given us alternate cores for the arcade games Galaga, Ghosts’n’Goblins, and Xevious, originally released for the R3 mainboard. Now, thanks to Robert Jaremczak (rjaremczak), there are R6 versions for these cores: Galaga R6, Ghosts’n’Goblins R6, and Xevious R6. See the original installation instructions for the R3 cores, which also apply to the R6 versions.
zeldin has updated the TI-99/4A core several times since its initial release, and as of version 1.3, the core is available for the R3 mainboard as well as R6. Go grab the latest, and be sure to file any bugs you find.
I had a few people respond to last’s month’s Digest to point out that core availability is currently a sticking point with regards to mainboard revisions, with some cores only available for R3 and others only for R6. Core developers must put in additional effort to support both mainboards, and not everybody owns both boards for testing. If you’re working on a core and need assistance building it for a board revision that you do not own, please reach out to the MEGA65 community on Discord or by email. We will eagerly connect you with resources for building and testing your core on both revisions.
Thank you Robert and zeldin for working on cores!
Last call for R3 Real-Time Clock replacements (Grove RTC)
Back in 2022, we noticed that a small but significant percentage of R3 mainboards had faulty Real-Time Clock (RTC) chips. Paul quickly put together a workaround involving a core update and an inexpensive RTC breakout board and Grove connector. This requires a soldering iron to put together, but no expertise: it’s just four wires. Nevertheless, I wanted to make sure that everyone who needed one could get one easily, so I built a bunch of them and made them available for the cost of shipping. The MEGA65 team also supported this effort financially out of their own pockets.
I will be ending my distribution offer at the end of 2024. I have about a dozen units remaining, and am willing to send them out to anyone who needs them. If you have an R3 mainboard and believe your Real-Time Clock is faulty, please follow these instructions for testing your RTC and requesting a unit. I will email you with instructions to send me the shipping cost by PayPal, then get one out to you, while supplies last.
Please do not request one if you don’t need one. Only a fraction of the MEGA65s delivered in 2022 have this issue. Follow the instructions to test your MEGA65 before requesting a replacement. If you discover you need one later than the end of this year, consider ordering the parts and cooking one up yourself. These are easy to build!
The sega-adapter is great actually
This isn’t news so much as just boasting, but last month I mentioned the sega-adapter project as a fun way to connect Sega Genesis-style game controllers to Commodore computers. Since I sent newsletter, I had some boards printed, got the parts together, and assembled a few. Chat, I can heartily recommend it, especially when used with the 8bitDo Retro Receiver for Sega Genesis ($16 on Amazon). The 8bitDo Retro Receiver pairs with almost any modern Bluetooth game controller, including Nintendo, Playstation, and older Xbox controllers (but not newer Bluetooth LE Xbox controllers). 8bitDo also makes a Bluetooth Sega Genesis gamepad; be sure to get the M30 Bluetooth model, not the “M30 2.4G wireless” model. I got my Nintendo Switch Pro controller to be a 3-button game controller for my MEGA65!
The sega-adapter is a pretty simple project, with the following parts:
* A 9-pin male connector, for the incoming Sega Genesis controller signal (such as the 8bitDo Retro Receiver)
* A PIC microcontroller
* A capacitor
* Header pins, and a “pigtail” cable for the 9-pin female connector that goes to the MEGA65 (or what have you)
* Optional header pins and jumper to switch between C64 and Amiga modes—or you can just use solder to set it to C64 mode permanently
That’s it. If you buy the exact parts recommended by the project, everything goes together using a soldering iron, and something to apply crimping force to the pigtail’s ribbon cable. You also need an inexpensive PIC microcontroller programmer such as a generic “PICKit3” device; I bought one for $33 on Amazon. I got the boards from OSHPark and parts from Digikey, for a total unit cost of about $16 each for a batch of ten adapters. The recommended 9-pin connectors were the most expensive parts, and you might be able to find cheaper ones in different form factors.
I’d never worked with PIC microcontrollers before! For software, I only needed the free version of Microchip MPLAB X IDE and the pre-built release of the sega-adapter .hex file.
To program the PIC, put it on a breadboard, then connect five of the wires of the PICKit3 to pins of the PIC, as described in the pinout diagrams (which I’m including below). Leave the sixth wire disconnected. You also need to apply about 5 volts of DC power across the PIC power pins. The PICKit3 does not supply power the microcontroller, and it needs to be powered to accept programming. You can use a little battery box, a wall wart of appropriate voltage, or a bench power supply.
I’m not selling these, I’m just showing off. It was a fun afternoon project, with a satisfying result, and because I made ten I have my holiday gifts all sorted.
Playing Crossroads
The first step to reverse engineering anything is to understand what the thing does. We can learn a lot about Crossroads just from playing it.
Archive.org has complete scans of Compute!’s Gazette magazine, and Crossroads is in issue 54, December 1987. An amusingly nostalgic way to acquire the Crossroads program would be to enter the “MLX” data entry program listed in the magazine, then use MLX to enter the 6,676 hexadecimal numbers for the game. (That’s 5,935 program bytes, plus error checking digits.) I recommend simply downloading the Compute!’s Gazette cover disk image archive. The disk image for the December 1987 issue is named 1987-12.d64. This disk image works great with the C64 core for the MEGA65. (Alas, I could not get it to run in GO64 mode. Maybe later we can figure out why.)
We can learn some useful things about Crossroads even before we run it. If we browse the disk directory, we can see that there are many files from this issue of the magazine, but the game likely consists of a single PRG file named CROSSROADS.
LOAD "$",8 LIST
We can load this file from disk into memory, using the ,8,1 arguments to LOAD to tell it to use whatever memory start address that CROSSROADS prefers:
LOAD "CROSSROADS",8,1
The program loads, and we’re back at the READY. prompt. Because the game didn’t do anything fancy to start automatically, we can guess that the program has loaded to the start of BASIC memory, and probably begins with a BASIC bootstrap program that invokes the machine code, like we’ve done for our own programs. Let’s confirm this:
LIST
Sure enough, there is a one-line BASIC program in memory:
10 SYS2300
Connect a joystick to port 2 (first player), then start the program with the RUN command. The game starts without any further disk activity, which confirms our hypothesis that this is a single-file game.
If you’ve never played Crossroads before, take some time to enjoy it! Shoot bullets at monsters by pressing the fire button, and collect the white spinning “spars” to gain shields and progress to the end of the level. Connect a second joystick and invite a friend!
Observing the game
As you play, try to make some guesses about how the game works, and come up with some questions you would like answered. Your own questions and hypotheses will guide your reverse engineering process.
Here are just a few things I noticed that might be worth exploring:
Program states. The game starts with an “attract mode” where a bunch of enemies duke it out on a maze without a player present, and with a scrolling message across the top of the screen. Like many vintage arcade games, attract mode appears very similar to gameplay, and likely reuses the game’s logic with minor changes. The game starts the first level when the player presses the fire button. Each level starts with an introductory animation where creatures appear with special graphical and sound effects. The players are the last to arrive. Then play begins. A level ends when a player collects a number of spars, at which point the game pauses until a key is pressed, and the game restarts at the next level. The game ends when both players are out of lives.
Creature properties. Creatures and players have a number of shields that protect them from attacks. Each attack deducts a shield, and the creature is destroyed when attacked without shields. A creature or a player gains a shield by picking up a spar. The player’s shield count appears at the top of the screen, under the “S.” Other creatures also have shield counts, but these are not visible. Player actions are controlled by joystick inputs; creature actions are controlled by algorithms. Each creature is of one of nine types, and the type describes their action patterns, reactions to collisions with bullets and other creatures, and the initial number of shields.
Character graphics. The maze and the creatures appear to occupy a grid in the same shape as the C64’s 40 x 25 character mode, and every element appears to be the size of a single character. It’s likely that at least some of these graphical elements are using VIC-II character graphics, where the glyphs of some letters, numbers, and symbols in the PETSCII typeface have been replaced with these designs. Each element is of a single color and high resolution, 8 x 8 pixels per character.
Creature half-steps. Even though creatures are the size of a single grid tile, they appear to traverse the screen in half-tile steps! If creatures are implemented with character graphics, the game must be doing something interesting to allow a creature to appear this way.
Sprites. There appear to be too many creatures and bullets on the screen at one time for the game to be using the eight VIC sprites to draw them. While advanced techniques like sprite multiplexing (which we discussed previously) could use sprites to draw more than eight objects at a time, there’s nothing in the design of this game that prevents more than eight objects from appearing on the same scan line, so this seems unlikely. However, there is an explosion effect that occurs when a creature takes damage, and this appears to overlay the character graphics. This suggests that this effect might be done with sprites.
Sound. The soundscape of Crossroads consists entirely of sound effects that represent activity on the screen: creatures appearing at the start of the level, player bullets firing, enemy bullets firing, shields absorbing damage, creatures and bullets being destroyed, and spars getting collected. There’s so much going on early in the level that each sound may be difficult to pick out, and it’s a bit easier after the screen is mostly clear. I’ll be interested to see how sounds are allocated to the three SID channels, especially when there are more than three events causing sound at a time.
In that interview with the developer that I mentioned earlier, Steve confirms that the creatures use character graphics, and the explosions use sprites. Steve also mentions that the sprite images for the explosion animations are generated randomly.
Examining the PRG
We want to use modern PC tools to examine the program, so we need to extract the PRG file from the disk image. Graphical tools like DirMaster (Windows only) or DroiD64 (Java, multi-platform) work nicely, or you can use the c1541 command included with the VICE emulator:
c1541 1987-12.d64 -read crossroads crossroads.prg
You can use a tool called a hex viewer to examine the bytes of the PRG file. I like a terminal-based hex viewer called hexyl, and there are many others to choose from. We won’t spend more than a minute staring at raw bytes, but we can use this to get our bearings. Here are the first few dozen bytes of the PRG file:
00000000 01 08 0b 08 0a 00 9e 32 33 30 30 00 00 00 00 00 |.......2300.....| 00000010 44 aa aa 11 00 00 00 00 11 aa aa 44 00 00 00 00 |D..........D....| 00000020 00 3c 3c 00 00 00 00 00 00 3c 3c 00 00 00 38 38 |.<<......<<...88| 00000030 30 7f be 68 ce ec 38 38 30 7f 76 30 30 38 ff 8f |0..h..880.v008..| ...
As we’ve seen before, a PRG file starts with two bytes that specify the load address for the program. In this case, the first two bytes are $01 and $08, specifying the Commodore 64 BASIC start address of $0801 hexadecimal, or 2049 decimal. The PRG address bytes are not written to memory, they only tell the BASIC command LOAD "CROSSROADS",8,1 where to put the rest of the data.
The subsequent bytes describe the one-line BASIC program we saw earlier:
* $0b $08: The address of the next line. In this case, it’s the end-of-program marker: $080b.
* $0a $00: The line number. $000a = 10 decimal.
* $9e: The C64 BASIC token for SYS.
* $32 $33 $30 $30: PETSCII for 2300.
* $00: End of line.
* $00 $00: End of program. If you count from $0801, you’ll see this is at address $080b, from the first bytes of the first line.
These 12 bytes start at address 2049, which means the rest of the program data starts at address 2061 ($080d). Interestingly, the SYS command uses address 2300 ($08fc). The data starting at address 2061 might be code, or it might be something else. We don’t know yet. We only know that the byte at address 2300 is the beginning of a machine code instruction, and that it’s the first instruction executed by the program.
It’s amusing to compare this hex dump to the listing in the magazine. (Well I think it’s amusing.) Note that Compute!’s Gazette program listings have nine bytes on a line, but only the first eight bytes are part of the program. The ninth byte is a checksum, which the MLX program uses to verify that you entered the line correctly. If it didn’t do this, entering these programs would be a nightmare, as opposed to merely a nuisance.
Introducing Retro Debugger
If programming is the art of telling a computer what to do, debugging is the art of figuring out why it’s not doing what you meant. One of the best ways to do that is to peek inside the computer’s memory as it steps through your instructions, and try to line up the evolving state of the computer with your own mental model. This process is often so opaque and frustrating that I sometimes wish I could just rip the top off the chips and look inside as the program is running. Thanks to high fidelity emulators running on modern computers, this fantasy is now a reality.
Retro Debugger by Marcin Skoczylas is an emulator for the C64, Atari XL/XE, and Nintendo Entertainment System, with some powerful real-time debugging tools. As the emulator runs, you see the C64 screen in one window, and use other windows to render the memory in different useful ways: as hex values, as machine code instructions, as graphics in common VIC-supported formats, and as a color-coded memory map. All of this is updated as the emulation runs at full speed, and you can pause execution and manipulate system state as a program runs.
Let’s use Retro Debugger to inspect Crossroads, and follow up on a few of our assumptions about how the game does graphics.
Setting up Retro Debugger
Download the latest Retro Debugger release for your platform (Windows, macOS, or Linux). You also need the original C64 ROM and 1541 DOS code. Retro Debugger opted not to include these in the distribution, but you can get them directly by installing the VICE emulator. You’ll have to dig around the VICE folder for the appropriate .bin files. On my Mac, I found them here:
./VICE.app/Contents/Resources/share/vice/C64/ ./VICE.app/Contents/Resources/share/vice/DRIVES/
Retro Debugger needs the ROM files to be in a single folder, with specific filenames. Create a new folder for these (such as inside the Retro Debugger folder), make duplicates of the files from VICE, and rename them as shown:
* .../C64/basic-901226-01.bin -> basic
* .../C64/chargen-901225-01.bin -> chargen
* .../C64/kernal-901227-03.bin -> kernal
* .../DRIVES/dos1541-325302-01+901229-05.bin -> dos1541
* .../DRIVES/dos1541ii-251968-03.bin -> dos1541II
When you start Retro Debugger for the first time, you need to tell it where to find the ROM files. Open the Settings menu, select C64, then Select C64 ROMs folder. Navigate to the folder you created with the ROM files in it. Retro Debugger loads the ROMs and starts the emulation. If it complains, make sure the ROM files are in the right place and have been renamed as expected.
Behold, the insides of the C64! You can see the CPU bouncing around the instructions for blinking the cursor at the READY prompt, and all of the memory and register updates that go with it. With the “C64 Screen” window active, you can interact with the C64 as you would with VICE, typing BASIC commands and whatnot.
Retro Debugger starts with some of its tools visible, but there are many more. With the cursor still at the READY prompt, open the VIC Editor menu, and select C64 Charset. The C64 Charset window opens. At this point, you should see what the VIC is currently using for its character set, which at the moment is standard C64 PETSCII. Resize the window to make the characters easier to see.
On my computer, I need to make the text bigger to be useful. Some windows, like the C64 Screen and Charset windows, have fixed-sized content, and you can drag the window corners to resize. In other windows, to increase the text size, right-click on the window, then adjust the Font Size slider. These windows can also be resized to display more information. The app will remember your window layout between sessions.
Retro Debugger is based on VICE, and has all of its debugging features. To pause the emulator, press F10 (or open the Code menu, then select Pause). While paused, press F10 repeatedly to advance by one instruction at a time. Press F11 to resume full speed. You can also set breakpoints on instructions, set watchpoints on memory locations, and can navigate to instructions that access specific memory addresses. You can even rewind the emulation to the instruction that last accessed a memory address.
For more information on features and keyboard shortcuts, see docs/README-C64-65XE-NES_Debugger.txt, included with the program.
Playing Crossroads in the emulator
There are two ways to get Crossroads running in the emulator. One method is to open the D64 disk image, then type the BASIC commands to load and run the game, as you would on a C64. Because Crossroads is a single PRG file, you can also just open the PRG file that we extracted earlier. Opening the PRG is often faster, especially if you need to reset the program.
Open the File menu, then select Open. Select the crossroads.prg file. This will load and run the game immediately, so prepare your speaker or headphones volume for the game’s violent introduction.
Next, open the Settings menu, then Joystick #2, and select a configuration for the joystick. With the joystick set to “Keyboard,” the cursor keys move the stick, and the right Alt key is the fire button. Or you can connect any PC-compatible game controller. If a controller is connected, its device name will appear in the menu.
Select the C64 Screen window, then press fire to start a game. Good luck!
Investigating character graphics
We have a hypothesis about how Crossroads uses character graphics. Let’s see if we can confirm this hypothesis in Retro Debugger.
When you started the game, it installed a new character set and told the VIC to use it. The Charset window now shows new graphics tiles: a limited set of PETSCII letters and numbers, and nearly all of the graphics for the game. You can see four types of maze wall, the “spar,” and various images for each of the creature types and bullets facing all four directions. Each of these are unique tiles of 8-by-8 pixels, and the tiles can be placed anywhere on the 40-by-25 tile grid.
We’ve made a discovery! The spar appears to be animating directly inside the Charset window! While Crossroads animates creatures using multiple static tile definitions, it animates the spar simply by continuously updating its single tile definition. This conserves charset tiles, at the expense of some CPU time.
If we squint, we can start to see how the game achieves its “half step” effect for creatures. When a creature is standing fully on a tile position, it is drawn as a single tile based on which direction it is facing. When a creature is walking between two tile grid spaces, it occupies both space, with a tile for each half of the image. There is a tile pair for each of the four directions. Each creature has two animation frames, and it appears that Crossroads stores both frames as single tiles, and uses only the second frame for the half-step tiles, for a total of 16 tile definitions per creature.
How the VIC finds character sets
The Charset window determines the memory location of the character set data that the VIC is using based on hardware registers, then draws the data it finds as characters. We can use some Commodore 64 knowledge to figure out where in memory this character set is stored.
The way the VIC-II accesses memory is a little bit complicated. The VIC can only access one of the four 16 KB banks at a time. Additionally, the C64 architecture makes a special accommodation so the VIC can access the PETSCII character set from an upper bank of ROM even when a lower bank is selected. This configuration for how the VIC sees memory is managed by the second of the C64’s two CIA chips. This is controlled by the lowest two bits of the CIA data register at $DD00.
In Retro Debugger, select the C64 Memory window. With this window selected, you can use either the Page Up/Page Down keys or your mouse’s scroll wheel to browse memory. You can also jump directly to an address by pressing Ctrl+G (or on macOS, Command+G), then typing the address in hexadecimal. Use this to jump to the register address $DD00.
Tip: Be careful what you type when the Memory window is active. If the cursor is on a memory value, typing hexadecimal digits will modify memory. When you type Ctrl+G (or Command+G) to go to an address, double-check that the cursor has moved into the address column before typing the address.
With Crossroads running, the register at $DD00 is set to $C7, so the lowest two bits are %11. According to C64 technical documentation, this points the VIC at bank 0, addresses $0000-$3FFF. It also tells the VIC to see the PETSCII character set at addresses $1000-$1FFF, hiding the RAM at these addresses from the VIC. This only hides the RAM from the VIC, not the CPU.
Still in the C64 Memory window, jump to register $D018 (53272 decimal). This register tells the VIC where to find screen memory within the selected bank, and where to find the character set. With the game running, this appears to be set to $19. This value is interpreted as follows:
* $19 = %0001 1001
* The upper four bits specify the location of screen memory, as a multiple of 1024 ($0400) bytes. %0001 = 1, so screen memory starts at address $0400. This is an offset from the beginning of the selected bank, and in this case the bank is 0, so the final address is $0400.
* The next three bits specify the location of character memory, as a multiple of 2048 ($0800) bytes. %100 = 4, so character memory starts at address $2000.
* The last (least significant) bit is ignored.
Be aware that the character memory offset gets described in manuals and online resources in different ways. I just described it the way the C64 wiki does: a three-bit number times 2048. Other books, including the MEGA65 manual, describe it as the upper three bits of a four-bit number whose last bit is 0, times 1024. The math works out the same either way.
With the selected CIA configuration, a program could tell the VIC to use the PETSCII character set from ROM by setting the three bits to %010 = 2 times 2048 = address $1000. Crossroads tells the VIC to look at $2000 instead, which is RAM where Crossroads can install its custom character set.
Let’s confirm our understanding. In the C64 Memory window, go to address $2000.
The C64 Memory window doesn’t know what the values in memory mean, but it tries to help us out by displaying them in four different ways: as hex bytes, as PETSCII characters, as character graphics data, and as sprite graphics data. In this case, we’re looking at character graphics data, so that view makes the most sense, and the other views are mostly nonsense. Scroll down to address $21F8 to see the spar glyph data being animated by the program.
Cheating at Crossroads
The C64 Memory window lets us peek into the computer’s memory as it is running, and also lets us change memory as we like. If we knew where to look, we could change properties of the game, such as the number of lives the player has. So how do we know where to look?
Locating variables is such a common task that the debugger has a tool just for this. Open the C64 menu, then select the C64 Memory Monitor. (This is a new tool, not the C64 Memory window that we’ve been using.) Resize this innocuous-looking little window to make it bigger.
Start a new single-player game by pressing the fire button on the joystick configured for port #2. The level clears, creatures spawn in, and finally the player arrives.
The player starts with 4 lives. We can know for sure that somewhere in the computer’s memory is a representation of this value, and this representation will change when the player loses a life. We don’t know where it is, or how the value is represented.
Let’s take a guess that the number of lives is stored as a byte, encoded as a non-negative integer. In other words, if we search the computer’s memory for a value of 4, there’s a chance that this might be how it stores the number of lives. This is just a guess! We can start with this, and refine our guess if it doesn’t work out.
With the emulation still running, in the C64 Memory Monitor, click the Capture button. Retro Debugger takes a snapshot of all of memory, and a giant list of memory addresses and values appears. Next, set the filter settings:
* Check the box next to “Changed.” The list updates to show only the memory that differs from the captured snapshot in the current state of the emulation.
* Check the box next to “Matches prev.” Click the value box, type 04, then press Enter. (It’s important to press Enter in this field, or the value won’t stick.) The Memory Monitor now lists all of the memory locations that were set to $04 when you clicked the Capture button, but currently contain something else.
* Finally, check “Matches current,” and set its value to $03.
The list narrows to just the memory addresses that were 4, and are now 3. You may see more than one row, but likely only a few. Of all 65,536 memory addresses in the computer, these are the only ones that meet the criteria. This list updates in real-time as the emulation runs, and some rows will flicker in and out as memory changes.
Select the C64 Screen window again, and use your gamepad or the keyboard to move the player into harm’s way until they run out of shields and lose a life. The number of lives shown in the top display reduces to 3. Press F10 to pause the emulation.
One of the addresses that meet the filter criteria in the Memory Monitor is $0011. Let’s take another guess that this is player 1’s life count. This is a good guess for at least two reasons. It’s an address in the Zero Page (addresses $0000-$00FF), which is a common place to put variables. It’s also the only row that doesn’t flicker while the emulation is running.
On the row showing address $0011, click the “Memory” button. This updates the C64 Memory window to highlight that location. As expected, its value is currently $03. Now change this value: select the Memory window, then type 07. Press the F11 key to unpause the emulator.
Hmmm, the number of player lives on the screen still shows “3.” Was our guess incorrect? Try killing off the player one more time. Success! The life count now reads “6,” one less than the value we set. Double-check that the value at address $0011 is now $06.
When we dig deeper into the machine code next month, we should expect to find instructions that manipulate address $0011, and can probably assume that the code is related to the number of lives of player 1. We can use that information and some inductive reasoning to determine the purpose of the surrounding code, and figure out the rest of the program.
We got a bit lucky with our guesses in this example, though truthfully many games store the number of player lives this way. What if our guesses were wrong, and the number of lives wasn’t stored as a non-negative integer in a byte value? One possible strategy would be to locate the code that updated the “4” on the screen to a “3.” We could use the Memory Monitor against the screen codes for those characters, or we could locate the screen memory address and ask Retro Debugger to tell us which instruction last changed it. It might be possible to figure out from nearby code where it reads the value that it displays, and how the value is interpreted.
More things to try
Retro Debugger comes packed with plenty of toys for messing with the state of the emulator. Browse the menus and experiment! Most menu items open a new window with interesting features. Here are just a few ideas.
Locate that first instruction. Recall that the BASIC bootstrap invokes the machine code at address 2300, or $08FC in hexadecimal. Pause the emulator by pressing F10. Select the C64 Disassembly window, type Ctrl+G (or Command+G for macOS) to go to address $08FC. We can see that the first few instructions initialize some hardware registers: $D40E, $D40F, $D412. You can look up these addresses in C64 technical documentation to see what they do. Researching all of the code this way is a bit tedious, but it’s fun to see the code sitting right there in memory. We’ll use a more powerful tool for investigating this code in next month’s Digest.
Watch the SID chip make sound. Open the C64 menu, then the C64 SID sub-menu, then the C64 SID tool. The C64 SID window opens, showing information about the SID chip’s three sound voices. (Check out one of the earliest Digest issues for an introduction to the SID chip.) Keep this window open, play the game for a bit, and watch the SID waveforms as the enemies duke it out.
Draw on the screen. Open the VIC Editor menu, and select Show VIC Editor. This window resembles the screen, and by default shows the tile grid and the pixel grid. This is a powerful tool for updating the VIC data in real time, and there’s quite a bit of documentation of its features in the text file. For now, select the C64 VIC Editor window, then left-click on an empty space on the Crossroads maze. It fills in with a character. Try making a wall of characters in the maze. Another discovery! The characters block the path of creatures, and the player! It is likely that the game code reads screen memory directly to detect whether a creature is adjacent to a wall.
Watch for sprites. The VIC Editor highlights sprites with a red box. If you zoom out in this window with the mouse scroll wheel, you can see that Crossroads keeps the sprites off screen when not in use. Play the game to cause explosion effects as shields and creatures are destroyed. Notice that the red boxes appear around the explosions in progress, then retreat out of view when no longer needed.
We’ve only scratched the surface of what Retro Debugger can do, and we’ve already learned quite a bit about how Crossroads works. We’ll be able to apply this knowledge as we start investigating the code.
Next month, we’ll introduce Ghidra, a powerful tool for inspecting machine code. With some effort and ingenuity, we’ll be able to produce a complete human-readable assembly language source listing for the game—the listing that Compute!’s Gazette refused to print.
Before you ask: no, there is currently no real-time memory visualization tool like Retro Debugger for the MEGA65 or the Xemu emulator. Yes, it would be cool. LGB tells me more work is needed on Xemu before it can support real-time memory visualization. In the short term, we’re more likely to see investment in the serial debugging protocol and tools like m65dbg, which can be used with both the Xemu emulator and with real MEGA65 hardware over a JTAG connection.
This Digest is made possible by an amazing group of supporters. Only supporters are allowed to complain when the feature article isn’t specifically about the MEGA65. To become a supporter, visit: ko-fi.com/dddaaannn
More next month!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
27 jaksoa