So I recently acquired a Punch-Out arcade game (one of the ones I remember playing as a kid, could never defeat Bald Bull!). It came with a helpful freeplay mod installed (which I am assuming came from here ) which is a really nice improvement from the stock Nintendo freeplay mode. The stock freeplay mode basically coins up the game on power-up and sits there waiting for a button press, which puts the game's CRT monitors at risk of burn-in. The freeplay mod I linked is a big improvement but it still has some room for improvement. Specifically, when 'coining up' by pressing 'uppercut', the player is taken to a screen where they must manually press the left punch button in order to start the game. As far as I can tell, this screen never goes away. Why is this bad? Well, I have kids and I have a day job. If the kids power on my Punch-Out while I'm gone and leave it in this aforementioned state, my CRT is at risk of burn-in. Believe me, I do _NOT_ want to find a replacement for these CRTs! I've got much better things to do with my time!
So I decided to see if I could improve the freeplay mod I mentioned before to never leave the screen stuck waiting for a button press.
I used MAME's source code to get memory map and I/O map of the game, MAME's excellent built-in debugger to set breakpoints and watchpoints, and my trusty copy of IDA (Interactive Disassembler) to make understanding the code easier. I also compared the aforementioned freeplay mod with the unmodified ROM to take a shortcut to understanding where the game looks for coin insertions.
Here is what the previous mod changes:
ROM:039A in a, (3) ; query dip switches
ROM:039C and 0Fh ; isolate free play
ROM:039E cp 0Fh ; are we in free play mode?
ROM:03A0 jr nz, NotFreePlay ; branch if not in free play mode
The mod changes 39C to AND with 0 instead of 0Fh and to compare with 1 instead of comparing with 0Fh. This has the result of always taking the branch of 3A0 which pops us into the normal attract mode on power-up instead of Nintendo's crappy original freeplay behavior.
Next mod is at 61d:
ROM:061D in a, (1) ; query joystick and coins
ROM:061F ld e, a
ROM:0620 ld b, 80h ; isolate coin1
ROM:0622 call ActIfCoinInserted
ROM:0625 ld l, 0D6h
ROM:0627 ld a, e
ROM:0628 ld b, 40h ; '@' ; isolate coin2
ROM:062A call ActIfCoinInserted
the mod moves this code to an unused section of the ROM at $2AC0 and adds a check to see if the upper-cut button (aka button 3) is pressed. If it is, it does some other simple checks and eventually ensures that 1 credit is now available which normally puts the game into the mode where it prompts the player to press left punch to start a new game.
I had no idea where this loop took place that repeatedly polled the state of the left punch button. Fortunately, MAME's built-in debugger is quite powerful and allows setting of I/O watch points. I referred to MAME's source code for Punch-Out and discovered that the buttons are read by reading I/O port 0. So I simply told MAME to break any time something somewhere in the code read this port.
In addition to MAME breaking at the modified code to check to see if the uppercut button was being pressed, it rewarded me by breaking here:
ROM:12BA CheckLeftPunchNewGame: ; CODE XREF: ROM:12DA↓j
ROM:12BA in a, (0)
ROM:12BC and 1 ; check to see if button 1 is pressed
ROM:12BE jr nz, loc_12B5 ; branch if it's pressed
ROM:12C0
ROM:12C0 LeftNotPressed: ; CODE XREF: ROM:12F0↓j
ROM:12C0 call sub_2937
ROM:12C3 jr loc_1289
This was clearly the code I was looking for to see if left punch was getting pressed.
The only danger was that I didn't know if this code only got called after a coin was inserted and while waiting to start a new game or if it was a generic 'read the button' routine and was used during the game itself. If the former, I could just change the behavior of the branch to always assume that the button was being pressed. If the latter, I would have to dig deeper to avoid altering the behavior of the gameplay itself.
I disabled the breakpoint, then started a game. As soon as Glass Joe appeared, I re-enabled the breakpoint at $12BA and to my delight, the breakpoint was not getting hit. This meant that the code was only used when waiting for a new game. Woohoo!
Now I just had to find the code that checked to see if the player wanted to do a 'rematch' after losing the game. I knew that I had to change the behavior from the way the original game worked to prevent potential burn-in, so I had to make a decision about whether to always start a new game if a player 'coins up' during the fast timer, or always do a rematch if the player 'coins up' during the fast timer. I decided that if the player 'coins up' after losing the game, this means that they always want to do a rematch (never a new game). If the player wants to do a new game, they'll just have to let the fast timer expire.
I had noticed some other code near $12BA which also read from I/O port 0 but checked for button 2 (aka right punch). The code was here:
ROM:12E6 CheckBothButtonsGameOverCreditIsNonZero:
ROM:12E6 ; CODE XREF: ROM:12E2↑j
ROM:12E6 in a, (0)
ROM:12E8 and 4
ROM:12EA jr nz, loc_12B5 ; branch if right is pressed
ROM:12EC in a, (0)
ROM:12EE and 1
ROM:12F0 jr z, LeftNotPressed
So I set a breakpoint at $12E6, then destroyed Glass Joe and allowed Piston Hurricane to defeat me.
After my game was over, I saw the 'fast timer' urging me to insert a coin. I hit 'uppercut' to credit up, and was gratified to have the debugger break at $12E6. Woohoo!
From this point, future modifications were easy:
I simply had to change the "jr nz, 12b5" at $12BE to "jr 12b5" (always jump instead of jump if non-zero) and do a similar change at $12EA from "jr nz, 12b5" to "jr 12b5".
This mod was fairly easy for me to perform but I stood on the shoulders of giants who did all of the hard work. The tools that made it easy:
- MAME source code that had the I/O ports and memory map
- MAME debugger (super nice for a free project)
- The pre-existing freeplay mod that allowed me to take some major shortcuts
TOO LONG, DIDN'T READ:
- Apply the Punch-Out freeplay mod from here .
- Using a hex editor, make these additional changes inside of the EPROM at 8L (MAME calls it chp1-c.8l),
change $12BE from 20 F5 to 18 F5
and
change $12EA from 20 C9 to 18 C9
Hopefully this makes it so you never have to worry about CRT burn-in even if your kids are playing your game while you are gone.
No comments:
Post a Comment