KansasFest 2019 presentation, “Ready Player Two”
One of the first games I ever played on my family’s Apple IIe was the Jordan Mechner master work, Karateka. Fight the bad guys, save the princess – it’s a simple but classic formula. My brother and I would take turns making our way through Akuma’s fortress. Even then, we wanted a two player version.
Fast forward more than 30 years, and I’ve taught myself 6502 assembly after getting back into the Apple II, through the thriving community online. The idea of a two player version of Karateka came back to me while at KansasFest a couple of years ago. I noodled a little on it back then, getting distracted by finding the code that created the unique music in the game.
Long story short: I finally found the places in the game code that needed patching to allow a second player to control the enemies in the game, and create a functioning two player version of Karateka. The resulting patch is only 42 bytes long. A DSK image is downloadable and playable online at the Internet Archive.
[Update, August 20, 2018]: Some people seem to think I had access to the Karateka source code to make my edits. As far as I know, that source code is unreleased, still in Jordan Mechner’s archives somewhere. Instead, I fired up the game in my Apple II emulator of choice, Virtual ][ and froze the game state. Stepping through instructions, I followed the assembly as it built up each frame of animation, triggered events and eventually found where the player and enemy performed their attacks. By comparing memory states from before and after the enemy attacks, I found a handful of zero-page variables that, when I changed them manually, would trigger a punch or kick.
From there, it was a matter of finding the routines that read and wrote to those memory locations, and inserting my own code to listen instead for the keyboard to trigger each action.
[Update, August 23, 2018]: Robin Harbron has ported the two-player patch over to the Commodore 64 port of Karateka. You can read Robin’s write-up here: http://psw.ca/robin/?p=271
Digging a little deeper than my original patches, Robin added the ability for the second player to move backward as well as forward. I’ll have to back-port that to my version.
Player 1 controls:
- Q,A,Z to punch
- W,S,X to kick
- C,V to move/run
- SPACE to go from standing to fighting stance and back.
Player 2 controls:
- N to kick
- M to punch
- < to move left
Thanks to Chris Torrence, for helping spelunk the game’s memory layout; to Antoine Vignau for his clean DSK crack and a patch to skip the intro and get right into fighting; 4AM for inspiration and helping test.
Addresses are locations in memory after the game finishes loading.
Before the enemy fighter punches, code loads the Accumulator with D7 then jumps to $6540. A kick is similar, but loads A with C5. I patched those pieces at $6C11 (punch) and $6C20 (kick) instead to read the keyboard buffer and load the appropriate byte into the Accumulator. Otherwise, it goes along to $6540 with 00, which does nothing.
- $6C11: D0 1C C5 21 D0 18 A9 00 85 29 A9 D7 -> AD 00 C0 C9 CD D0 02 A9 D7 8D 10 C0 (M to punch, clears strobe)
- $6C20: 20 95 6C C5 D7 B0 08 20 71 6C A9 C5 -> AD 00 C0 C9 CE D0 02 A9 C5 8D 10 C0 (N to kick, clears strobe)
Code at $6B9B seems to check if the distance between fighters ($33) is too far, trigger the enemy to move closer. This happens with a JMP to 6C52. I read the keyboard ($C000) and check for the comma/less-than.
- $6B9B: A6 33 E0 0C 90 03 4C 52 6C E0 07 B0 02 -> AD 00 C0 EA EA EA C9 AC D0 03 4C 52 6C (< to move player 2)
To be able to check the keyboard while the game is usually busy animating the enemy fighter, I had to find where the keyboard strobe ($C010) was cleared, and NOP it. This happens several places in the main loop, but patching at $6EA9 kept the keyboard buffer long enough for me to check it.
- $6EA9: 8D 10 C0 -> EA EA EA (keep from clearing keyboard strobe)
Once the second player controls were working, I modified the first player controls so both players could huddle around the keyboard and not cross over each other:
- $6e8e: 88 -> C3 (C to move left instead of ctrl-H/left arrow)
- $6e98: 95 -> D6 (V to move right instead of ctrl-U/right arrow)
Bonus: Now defaults to keyboard controls without having to hit K at the beginning.
- $b9a8: A5 C4 D0 00 -> A9 01 85 C4 (default to keyboard control, ignores joystick)