Mike Tyson's Punch-Out Disassembly

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

nispio
Posts: 13
Joined: Thu Aug 20, 2020 11:19 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by nispio » Fri Sep 04, 2020 7:32 pm

lidnariq wrote:
Fri Sep 04, 2020 5:14 pm
I'd recommend quickly browsing our documentation of how the MMC2 works: https://wiki.nesdev.com/w/index.php/MMC2
Thanks. That is what I did in order to understand as much as I did. I feel like I understand it well enough for my purposes now. If there is something I'm getting wrong, feel free to correct me! This is my first exposure to anything low level on the NES, or the 6502 in general. At this point I am up to speed enough to follow the game logic by reading the disassembly. I haven't even attempted to learn the PPU and APU yet.

tepples
Posts: 22047
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Mike Tyson's Punch-Out Disassembly

Post by tepples » Sat Sep 05, 2020 3:39 am

DirtyMcDingus wrote:
Fri Sep 04, 2020 3:17 pm
As far as I know, the MMC2 (used only in Punchout) is the only memory controller that bank switches in 8KB chunks.
Namco 108/MIMIC, MMC3/MMC6, MMC5, Sunsoft 5B/FME-7, RAMBO-1, Namco 163, VRC2/VRC4, VRC6, and VRC7 all have at least one 8 KiB PRG ROM window. Almost a third of NES releases licensed by Nintendo of America are on MMC3.

User avatar
Bregalad
Posts: 7951
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

Re: Mike Tyson's Punch-Out Disassembly

Post by Bregalad » Sat Sep 05, 2020 3:43 am

Probably he meant the only one with only a single 8kb switchable chunk at $8000-$9fff and 24kb hardwired bank at $a000-$ffff.
Although this configuration can easily be "simulated" on MMC3 and the like by only swapping $a000-$bfff once at reset and never changing it ever again :)
Useless, lumbering half-wits don't scare us.

nispio
Posts: 13
Joined: Thu Aug 20, 2020 11:19 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by nispio » Sat Sep 05, 2020 7:55 am

If 8KB bank switching is not unique, then the question still stands whether the ambiguous NameList in FCEUX (that assigns two different banks the same NL number) is intentional/configurable. Should it be filed as an issue with the FCEUX project?

EDIT:

I should note that this is the most salient thing related to 8KB banks switching that I was able to find in the help file included with FCEUX:
Memory contents are displayed in this form:

bb:mmmm:dd dd dd iiiiiiiii...
  • bb - 16k iNES bank, designates which 16k bank from the iNES file is mapped here. Note that the number may be not the same as the actual hardware bank of the mapper.
...

nispio
Posts: 13
Joined: Thu Aug 20, 2020 11:19 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by nispio » Sat Sep 05, 2020 12:21 pm

A somewhat mundane observation, but interesting to me, is that the programmers of this ROM seem to be quite consistent about their use of branching, jumping, and returning. What I mean by that is that the following guidelines seem to be applied throughout all of the code that I have looked at so far:
  • JMP instructions seem to be reserved for use in "tail calls." In other words, a JMP instruction always leads to another subroutine, not just another line within the same subroutine.
  • Unconditional jumps within a single function are always performed with a branching instruction. At first, it confused me to encounter so many branches that were always taken, but now I am getting accustomed to it. I still have to be careful not to assume that when I see an immediate load that the next branch is therefore always taken. That has tripped me up more than once now.
  • A branching instruction does not always lead to a line within the same subroutine. So even though JMP seems to always lead to another subroutine, a branch may stay within the same subroutine or jump to another one.
  • RTS instructions seem to be reserved for marking the true ending of a subroutine. Exiting a subroutine early is accomplished by either a JMP to another subroutine, or a branch to the nearest RTS. Often the nearest RTS is in the subroutine above the current one. I haven't actually spent any time analyzing whether the RTS pointed to by the branch is truly the nearest one, but that is my best guess as to why I see so many branches jumping backward to return, instead of just branching to the end of the current subroutine.
My reason for noting these observations here is to elicit feedback. I'm curious whether these conventions are typical, or if I might be misunderstanding or mischaracterizing things, etc. So feel free to provide feedback/corrections. I am here to learn!

User avatar
Bregalad
Posts: 7951
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

Re: Mike Tyson's Punch-Out Disassembly

Post by Bregalad » Sat Sep 05, 2020 2:02 pm

The problem when analysing disassembled code is that the 6502 itself doesn't have any "subroutine". It just has JSR and RTS instructions.

So for example what is the "end of a subroutine" can be very ambigious when it's typical practice to have multiple RTS points for the same entry point for example.

Now to answer your questions:
nispio wrote:
Sat Sep 05, 2020 12:21 pm
JMP instructions seem to be reserved for use in "tail calls." In other words, a JMP instruction always leads to another subroutine, not just another line within the same subroutine.
Normally you would use an always-taken-branch instead of a JMP wherever possible, to save a byte of ROM. This is possible if the destination is near enough (-128/+127) and if there is a flag that is in a known state.
However it is not normal 6502 practice to purposedly change a flag just to use a branch instruction instead of a JMP. The only point of doing it is writing entierely relocatable code, i.e. the binary can be copied as-is to another area in memory and execute fine. Maybe you just didn't encounter any situation where an always-taken branch was impossible.
A branching instruction does not always lead to a line within the same subroutine. So even though JMP seems to always lead to another subroutine, a branch may stay within the same subroutine or jump to another one.
Branch to another subroutine is untypical, however subroutine having two "tails" are common.
RTS instructions seem to be reserved for marking the true ending of a subroutine.
RTS used for early exit (equivalent to "break" in high level languages) is common. Having to branch to another RTS is a waste of time and ROM space.
Useless, lumbering half-wits don't scare us.

nispio
Posts: 13
Joined: Thu Aug 20, 2020 11:19 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by nispio » Sat Sep 05, 2020 8:14 pm

Bregalad wrote:
Sat Sep 05, 2020 2:02 pm
Now to answer your questions:
Thank you. These are terrific insights that I will keep in mind. The code seems to be well organized and broken into small subroutines, so it would not surprise me if locations are uncommon for which branching to a nearby RTS is not possible.

User avatar
Controllerhead
Posts: 147
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: Mike Tyson's Punch-Out Disassembly

Post by Controllerhead » Sat Sep 05, 2020 10:43 pm

nispio wrote:
Fri Sep 04, 2020 7:32 pm
This is my first exposure to anything low level on the NES, or the 6502 in general.
You seem to be picking up quick! The first 15min of this video explains 6502 architecture and its instructions very clearly:
https://www.youtube.com/watch?v=fWqBmmPQP40

It helped me a understand a ton coming from high level language land. It's a different world down here on the metal.

Good luck!
Image

3gengames
Formerly 65024U
Posts: 2277
Joined: Sat Mar 27, 2010 12:57 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by 3gengames » Sun Sep 06, 2020 10:26 pm

I was quite possibly going to do this game in the future, but looks like you're gonna beat me to it. If you need any help with what you're looking at, you can message me. I took apart Duck Hunt (4K SLOC) and Defender (12K 6809 SLOC) with a custom tool I made, and have looked at so much code I believe I can figure out anything, haha. But good luck otherwise! I'm sure you'll find some great info. I found things nobody knew about in Duck Hunt and Defender both, and disassembling them was great fun/adventure.

nispio
Posts: 13
Joined: Thu Aug 20, 2020 11:19 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by nispio » Fri Sep 11, 2020 9:22 pm

A quick update on my recent progress. I have started cleaning up some of my work and pushing it to GitHub so that it can be merged in later. However, I have been spending most of the time continuing to debug and reverse-engineer the game because it is so much more exiting than the documentation and cleanup tasks. :D

Of particular interest to me right now are all events that are driven by RNG. I have an understanding of how random HP refill after a knockdown is determined, how the count an opponent gets up on is determined, why there is such a strong correlation between the HP refill and the count, and how random health refills for Little Mac are determined when pressing "Select" during the screen in between rounds.

My current pursuit is to understand how the random crowd movements are determined. I have a start on it, but it is far more complicated than any of the things I mentioned above. There is some sort of state machine that has about 64 states to it. Progression through the states is linear, but most states last for only one frame, while other states persist for 5 or 6 frames before advancing. Each state has indirect subroutine calls associated with it based on values from a 64-byte table in the ROM. There are enough layers of indirection that it is taking me some time to unravel the meaning of it all.

nispio
Posts: 13
Joined: Thu Aug 20, 2020 11:19 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by nispio » Sun Sep 13, 2020 12:54 am

I did more cleanup today and pushed a new set of changes into the pull request on GitHub. The updates are related to the clock, health, hearts, and knockdowns. I will update this thread with a summary of some of my findings at a later time.

DirtyMcDingus
Posts: 38
Joined: Sat Jul 25, 2020 5:31 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by DirtyMcDingus » Sun Sep 13, 2020 3:58 am

nispio,
Alas, the real world is calling me and i can't focus on this as much as I have for the past month. I merged in your stuff! Please contact me through email so we can discuss how to move forward since you have an interest in keeping this going. I plan on picking it back up. But I need to focus on other things for a while.

nispio
Posts: 13
Joined: Thu Aug 20, 2020 11:19 pm

Re: Mike Tyson's Punch-Out Disassembly

Post by nispio » Thu Sep 17, 2020 11:59 pm

I have been looking into the various pieces that deal with randomness. There is a table pointer at `$44` and and index into that table at `$43`. The values in the table are used in a number of different ways. The 3 most significant bits are used to select a subroutine from another table. The 5 MSB are essentially used as an argument to the "functions" in the table. On any given frame, a function is called from the table, and the index `$43` is incremented. Sometimes the index `$43` is incremented more than once in a frame.

Many of the various functions called from the table have elements of randomness in them. Often, 4 random bits are drawn from the RNG and then one of two code branches is taken based on the outcome of the random draw. I am just getting started with untangling all of this different code paths and trying to understand which aspects of the game are controlled by this "state machine"

Post Reply