On this page
  1. Related
  2. Mechanics
  3. References
  4. Sources

The atlas reads the NES Rev 1 release of Mike Tyson’s Punch-Out!! through Nick Mikstas’s Ophis disassembly. Assembled, it matches the ROM’s PRG byte for byte. It’s a work in progress: the opponent state machine, the engine bank, and Glass Joe’s data are labeled and commented, while much of the rest is raw. Labels and comments quoted here are his. Nothing has been measured in the ROM yet.

Each opponent move is an script of timed steps. One step marks the punch active and branches on the result: landed, blocked, ducked, or dodged1. A second list, read while the opponent waits, picks the next move. Some of its steps go by a roll against a random byte. The NMI handler rebuilds that byte from moved bits of the frame counter and of a running total of controller 1’s buttons2. Glass Joe’s right hook shows a whole move. It loads its pose, warns, waits a variable time, and throws the punch at Little Mac’s left with damage 7 added to his base of 43.

The warning is a telegraph: the script recolors the opponent’s outline for a few frames, from Glass Joe’s normal color to his dodge color4.

Little Mac’s hearts are his stamina. Against Glass Joe they start rounds 1 to 3 at 20, 20, and 15. A round never starts below its count, and at zero hearts Little Mac can’t punch5. When an opponent goes down in a knockdown, the game sets the count he’ll rise on, from his data or a roll on a table of eight; Glass Joe’s table runs from 2 to 86.

Mechanics

MechanicKindHow
Attack patternScript and chanceeach move is a script of timed steps that branches on block, duck, or dodge; a selector list picks the next, some steps by an n-in-16 roll
TelegraphColor flasha script recolors the opponent's outline before a punch; Glass Joe's right hook sets a count of 4, 4 frames by the comment
StaminaAction costhearts drop by 1 or 3; at zero Little Mac can't punch, and each round starts at no less than a set count
KnockdownSet counta downed opponent's get-up count comes from his data or a roll on an eight-entry table
Every claim about this game, by concept (6)
Attack patternIn Mike Tyson's Punch-Out!! (NES, Rev 1), each opponent move is a script of state data that one interpreter, OppStateUpdate, steps through. A control byte's upper nibble picks a handler, and for upper nibbles $B to $F the lower nibble picks a sub-command from a second table. The commands, named by the disassembly's ST_ constants, load sprites and frame timers, call and return from shared sub-scripts, jump, branch when a zero-page byte holds a given value, repeat a step, mark the opponent as punching, and set the punch's side and damage. At a punch's active step the script waits until the punch is resolved. A landed punch goes on past the step's three index bytes; a blocked, ducked, or dodged one jumps to the first, second, or third index. A path through a move ends with ST_END, which marks the state STAT_FINISHED ("Indicate the end of the current state has been reached", per the comment), or with ST_WAIT_STATE, which stores $81 in OppCurState: the opponent's wait state, in the comment's words.Code
Glass Joe's right hook in the NES Rev 1 is the commented script GJRighHook. It loads defense values, shows sprite data $0A ("6 frames for this sub-state", per the comment), flags the opponent as punching, and writes OPP_RGHT_HOOK to $BB. It then sets OppOutlineTimer to $84, a count of 4 calls of SetOppOutlineClr (the comment: "Change outline color for 4 frames"), and waits a variable time, VariableStTime. At index $1E, ST_CHK_REPEAT jumps to index $24 while its repeat counter, loaded as 1, hasn't run out; with 1 it falls through. If MacStatus holds MAC_SUPER_PUNCH, it branches to index $59; otherwise it starts the punch sound and sets the punch on Little Mac's left side with damage 7, which the comment says is added to base damage. Glass Joe's fight data sets that base damage at 4. At the punch's active step (index $32), a landed punch falls through to index $36: a 4-frame timer (per the comment), sprite moves, and ST_END at index $44. The step gives index $45 for a block, which jumps into his straight punch's state at index $3B, and index $49 for a duck or a dodge, which loads a special timer, sets his defense values to zero, moves his sprites, and waits for the combo timer before the wait state.Code
The NES Rev 1 picks an opponent's next move with a second, uncommented script reader at $B196, run from the main game loop. While $38 is nonzero and OppCurState is 1 (the wait state, in the atlas's reading of the defines), it decrements $39 on each call and, when that reaches zero, reads a list of command bytes. In the atlas's reading of the code: 0n s sets OppCurState to s and $39 to n, the count of calls before the next command (n = 0 gives 256, as $39 wraps to $FF); 1n i jumps to list index i when n is greater than the low four bits of a random byte, n/16 of the time (computed), and always when n is 15; and 2n with eight state bytes after it sets $39 to n and starts one of the eight, chosen by the random byte's low three bits. The random byte is $18, which an uncommented routine at $85E9 in bank 7 sets to the sum of two scrambles: $ED, bits of FrameCounter moved to new positions, and $EC, moved bits of $19 with $19 AND $81 ORed in. $19 is a running total, kept from call to call, to which each call adds moved bits of Joy1Buttons. Two paths of the NMI handler call the routine, each right after it increments FrameCounter. $AEF1 rotates $18 three times after each read. Bytes from $80 up hold other commands, not read here.Code
KnockdownWhen KnockdownSts is 1 ("Opp down", per the define) in the NES Rev 1, uncommented code at $B2EB counts the knockdown in OppKDRound and OppKDFight, and at the third in a round it stops there. Otherwise, unless $BA held $FF, it reads one entry from the opponent's table at OppRefillPtr (its index set by conditions not traced here). An entry of 0 sets nothing. An entry whose upper nibble n is nonzero sets OppGetUpCount to $99 + n, which the define reads as a count of n. An entry whose upper nibble is 0 picks one of the eight entries of OppGetUpTable with three bits of the random byte $18; Glass Joe's eight read, by the comment's encoding, 2, 3, 4, 5, 6, 8, 3, and 4.Code
StaminaLittle Mac's hearts in the NES Rev 1 are a two-digit decimal count. Glass Joe's fight data, in its comments, starts rounds 1, 2, and 3 at 20, 20, and 15 hearts, with normal recoveries of 15, 15, and 10 and reduced ones of 9, 9, and 5. The round setup routine, uncommented (it also loads the round's clock rate and starts the round timer), sets hearts to the table's value in round 1 and raises them to it in rounds 2 and 3 if they're lower, and it copies the two recovery values for the round. An uncommented routine at $843E takes hearts away, 1 or 3 at each of its six callers; when the count reaches zero or below it clears MacCanPunch, which the define reads as "Little Mac can't punch".Code
TelegraphThe NES Rev 1 cues a dodge by recoloring the opponent's outline. A script writes OppOutlineTimer with its high bit set; the main game loop calls SetOppOutlineClr, which then clears that bit, keeps the count, and writes the fight's dodge outline color into four sprite palette entries. Each later call counts the timer down, and at zero it writes the normal color back. Glass Joe's fight data gives palette values $26 for the dodge outline and $08 for the normal one, and his right hook sets the timer to $84, a count of 4 later calls (4 frames by the comment), before its variable wait and the punch.Code

References

  1. ^In Mike Tyson's Punch-Out!! (NES, Rev 1), each opponent move is a script of state data that one interpreter, OppStateUpdate, steps through. A control byte's upper nibble picks a handler, and for upper nibbles $B to $F the lower nibble picks a sub-command from a second table. The commands, named by the disassembly's ST_ constants, load sprites and frame timers, call and return from shared sub-scripts, jump, branch when a zero-page byte holds a given value, repeat a step, mark the opponent as punching, and set the punch's side and damage. At a punch's active step the script waits until the punch is resolved. A landed punch goes on past the step's three index bytes; a blocked, ducked, or dodged one jumps to the first, second, or third index. A path through a move ends with ST_END, which marks the state STAT_FINISHED ("Indicate the end of the current state has been reached", per the comment), or with ST_WAIT_STATE, which stores $81 in OppCurState: the opponent's wait state, in the comment's words.Codenmikstas/mike-tysons-punch-out-disassemblyrevised
    How we know
    4654;This function is the entry point for updating the opponent's state.  It uses the upper nibble of
    4655;a control byte to pick a function to run from the table below.  The function it calls may then
    4656;call another set of functions with the lower nibble of the same control byte(or it may not).
    ⋮
    4664LC556:  AND #$0F                ;Keep lower nibble for later.
    ⋮
    4670LC55C:  JSR Div16               ;($BF99)Shift upper nibble to lower nibble.
    4671LC55F:  JSR IndFuncJump         ;($AED4)Indirect jump to desired function below.
    ⋮
    4673LC562:  .word OppLoadSprites,  SpritesNxtXYState, $C5B3,           $C5B7
    4674LC56A:  .word $C5C8,           $C5CE,             SprtMove,        OppMoveSprites
    4675LC572:  .word OppSetTimer,     $C600,             OppBigMove,      OppStateUpdate1
    4676LC57A:  .word OppStateUpdate1, OppStateUpdate1,   OppStateUpdate1, OppStateUpdate2
    
    source_files/PRG_BankDEF.asm lines 4654–4676 at 60b2fa0 · upstream ↗
    4889OppStateUpdate1:
    4890LC670:  TXA                     ;Get index into table below.
    4891LC671:  JSR IndFuncJump         ;($AED4)Indirect jump to desired function below.
    ⋮
    4893LC674:  .word OppCallFunc,  OppReturnFunc, NULL_PNTR, NULL_PNTR
    4894LC67C:  .word OppVarStTime, OppNotPunch,   $C6CA,     $C6D7
    4895LC684:  .word $C6DF,        $C6EA,         NULL_PNTR, NULL_PNTR
    4896LC68C:  .word InitAudio
    
    source_files/PRG_BankDEF.asm lines 4889–4896 at 60b2fa0 · upstream ↗
    5017OppStateUpdate2:
    5018LC720:  TXA                     ;Get index into table below.
    5019LC721:  JSR IndFuncJump         ;($AED4)Indirect jump to desired function below.
    ⋮
    5021LC724:  .word PunchActive,  OppStateJump, ChkMemAndBranch, ChkRepeatState
    5022LC72C:  .word OppWaitState, OppRepeat,    OppPunchDirDmg,  OppDefInline
    5023LC734:  .word $C7E1,        OppPunch,     WriteZPageByte,  OppSpecTimer
    5024LC73C:  .word OppComboWait, $C816,        $C81D,           StateDone
    
    source_files/PRG_BankDEF.asm lines 5017–5024 at 60b2fa0 · upstream ↗
    508.alias ST_SPRITES       $00     ;Load sprite data for current opponent sub-state.
    ⋮
    513.alias ST_TIMER         $80     ;Number of frames for sub-state to wait.
    ⋮
    515.alias ST_CALL_FUNC     $E0     ;Call an opponent state subroutine.
    516.alias ST_RETURN_FUNC   $E1     ;Return from an opponent state subroutine.
    ⋮
    519.alias ST_PNCH_ACTIVE   $F0     ;Indicates an opponent's punch is active.
    520.alias ST_JUMP          $F1     ;Jump to new state base address and index.
    521.alias ST_CHK_BRANCH    $F2     ;Check memory for value and branch in state data if value found.
    522.alias ST_CHK_REPEAT    $F3     ;Check if a sub-state needs to repeat.
    523.alias ST_WAIT_STATE    $F4     ;Put opponent into wait state.
    524.alias ST_REPEAT        $F5     ;Load a repeat value for this sub-state.
    525.alias ST_PUNCH_SIDE    $F6     ;Indicate what side of Little Mac a punch is approaching.
    ⋮
    527.alias ST_PUNCH         $F9     ;Indicate the opponent is punching.
    ⋮
    
    source_files/Mike_Tysons_Punchout_Defines.asm lines 508–531 at 60b2fa0 · upstream ↗
    5028PunchActive:
    5029LC744:  LDX OppPunchSts         ;Is the punch initialized but not yet processed?
    5030LC746:  BMI OppStateWait        ;If so, branch.
    ⋮
    5038LC74E:  DEC OppStateIndex       ;Stay on this state and exit.
    ⋮
    5042LC751:  CPX #PUNCH_LANDED       ;Was punch landed?
    5043LC753:  BEQ PunchLanded         ;If so, branch.
    ⋮
    5045LC755:  BCC PunchBlckDck        ;Was punch blocked or ducked? If so, branch.
    ⋮
    5048LC757:  DEX                     ;Dodged punch must have index increased by 2 (4-2=2).
    ⋮
    5051LC758:  DEX                     ;Only decrement punch status by 1 for block/duck.
    5052LC759:  TXA                     ;
    5053LC75A:  CLC                     ;
    5054LC75B:  ADC OppStateIndex       ;Get the index to jump to for blocked/ducked or dodged punches.
    ⋮
    
    source_files/PRG_BankDEF.asm lines 5028–5072 at 60b2fa0 · upstream ↗
    540.alias PUNCH_BLOCKED    $01     ;Punch blocked by Little Mac.
    541.alias PUNCH_DUCKED     $02     ;Punch is ducked by Little Mac.
    542.alias PUNCH_LANDED     $03     ;Punch hit Little Mac.
    543.alias PUNCH_DODGED     $04     ;Punch dodged by Little Mac.
    
    source_files/Mike_Tysons_Punchout_Defines.asm lines 540–543 at 60b2fa0 · upstream ↗
    5147OppWaitState:
    5148LC7B6:  LDA #$81                ;
    5149LC7B8:  STA OppCurState         ;Put the opponent into their wait state.
    
    source_files/PRG_BankDEF.asm lines 5147–5150 at 60b2fa0 · upstream ↗
    5259StateDone:
    5260LC821:  DEC OppStateIndex       ;
    5261LC823:  LDA #STAT_FINISHED      ;Indicate the end of the current state has been reached.
    ⋮
    5264LC825:  STA OppStateStatus      ;
    
    source_files/PRG_BankDEF.asm lines 5259–5264 at 60b2fa0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  2. ^The NES Rev 1 picks an opponent's next move with a second, uncommented script reader at $B196, run from the main game loop. While $38 is nonzero and OppCurState is 1 (the wait state, in the atlas's reading of the defines), it decrements $39 on each call and, when that reaches zero, reads a list of command bytes. In the atlas's reading of the code: 0n s sets OppCurState to s and $39 to n, the count of calls before the next command (n = 0 gives 256, as $39 wraps to $FF); 1n i jumps to list index i when n is greater than the low four bits of a random byte, n/16 of the time (computed), and always when n is 15; and 2n with eight state bytes after it sets $39 to n and starts one of the eight, chosen by the random byte's low three bits. The random byte is $18, which an uncommented routine at $85E9 in bank 7 sets to the sum of two scrambles: $ED, bits of FrameCounter moved to new positions, and $EC, moved bits of $19 with $19 AND $81 ORed in. $19 is a running total, kept from call to call, to which each call adds moved bits of Joy1Buttons. Two paths of the NMI handler call the routine, each right after it increments FrameCounter. $AEF1 rotates $18 three times after each read. Bytes from $80 up hold other commands, not read here.Codenmikstas/mike-tysons-punch-out-disassemblyrevised
    How we know
    725MainGameEngine:
    ⋮
    752LA6A2:  JSR $B196
    
    source_files/PRG_BankDEF.asm lines 725–752 at 60b2fa0 · upstream ↗
    2213LB196:  LDA $38
    2214LB198:  BEQ $B1CD
    2215LB19A:  LDX OppCurState
    2216LB19C:  DEX
    2217LB19D:  BNE $B1CD
    2218LB19F:  DEC $39
    2219LB1A1:  BNE $B1CD
    2220LB1A3:  INC $39
    2221LB1A5:  LDY $3A
    2222LB1A7:  LDA ($3B),Y
    2223LB1A9:  AND #$0F
    2224LB1AB:  TAX
    ⋮
    
    source_files/PRG_BankDEF.asm lines 2213–2261 at 60b2fa0 · upstream ↗
    1731LAEAB:  STA $EF
    1732LAEAD:  LDA #$0F
    1733LAEAF:  STA $EE
    1734LAEB1:  LDA $EF
    1735LAEB3:  CMP $EE
    1736LAEB5:  BEQ $AEC7
    1737LAEB7:  LDA $18
    1738LAEB9:  JSR $AEF1
    1739LAEBC:  AND $EE
    1740LAEBE:  STA $EE
    1741LAEC0:  LDA $EF
    1742LAEC2:  CMP $EE
    ⋮
    
    source_files/PRG_BankDEF.asm lines 1731–1745 at 60b2fa0 · upstream ↗
    1783LAEF1:  ROR $18
    1784LAEF3:  ROR $18
    1785LAEF5:  ROR $18
    1786LAEF7:  RTS
    
    source_files/PRG_BankDEF.asm lines 1783–1786 at 60b2fa0 · upstream ↗
    725L85E9:  LDA FrameCounter        ;($1E)
    726L85EB:  ROR
    727L85EC:  STA $E8
    ⋮
    733L85F5:  AND #$08
    734L85F7:  STA $ED
    ⋮
    759L8625:  STA $ED
    760L8627:  LDA Joy1Buttons
    ⋮
    789L865C:  ORA $EC
    790L865E:  CLC
    791L865F:  ADC $19
    792L8661:  STA $19
    793L8663:  ROR
    ⋮
    
    source_files/PRG_Bank007.asm lines 725–831 at 60b2fa0 · upstream ↗
    12L8012:  JMP $85E9
    
    source_files/PRG_Bank007.asm line 12 at 60b2fa0 · upstream ↗
    705LA644:  INC FrameCounter        ;($1E)
    ⋮
    709LA64C:  JSR PushPRGBank07       ;($AA3C)
    710LA64F:  JSR $8012
    
    source_files/PRG_BankDEF.asm lines 705–710 at 60b2fa0 · upstream ↗
    802LA6F8:  INC FrameCounter        ;($1E)
    ⋮
    826LA724:  JSR PushPRGBank07       ;($AA3C)
    827LA727:  JSR $8012
    
    source_files/PRG_BankDEF.asm lines 802–827 at 60b2fa0 · upstream ↗
    53.alias OppCurState      $90     ;Opponent's current state. Set MSB=initialize new state.
    
    source_files/Mike_Tysons_Punchout_Defines.asm line 53 at 60b2fa0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  3. ^Glass Joe's right hook in the NES Rev 1 is the commented script GJRighHook. It loads defense values, shows sprite data $0A ("6 frames for this sub-state", per the comment), flags the opponent as punching, and writes OPP_RGHT_HOOK to $BB. It then sets OppOutlineTimer to $84, a count of 4 calls of SetOppOutlineClr (the comment: "Change outline color for 4 frames"), and waits a variable time, VariableStTime. At index $1E, ST_CHK_REPEAT jumps to index $24 while its repeat counter, loaded as 1, hasn't run out; with 1 it falls through. If MacStatus holds MAC_SUPER_PUNCH, it branches to index $59; otherwise it starts the punch sound and sets the punch on Little Mac's left side with damage 7, which the comment says is added to base damage. Glass Joe's fight data sets that base damage at 4. At the punch's active step (index $32), a landed punch falls through to index $36: a 4-frame timer (per the comment), sprite moves, and ST_END at index $44. The step gives index $45 for a block, which jumps into his straight punch's state at index $3B, and index $49 for a duck or a dodge, which loads a special timer, sets his defense values to zero, moves his sprites, and waits for the combo timer before the wait state.Codenmikstas/mike-tysons-punch-out-disassemblyrevised
    How we know
    634GJRighHook:
    ⋮
    637L8C4D:  .byte ST_CALL_FUNC      ;Call state data subroutine
    638L8C4E:  .word DefenseStats3     ;Defense data pointer.
    ⋮
    649L8C54:  .byte ST_SPRITES_XY+6   ;Load sprites, change position, 6 frames for this sub-state.
    ⋮
    655L8C58:  .byte ST_PUNCH          ;Indicate opponent is punching.
    ⋮
    658L8C59:  .byte ST_DEFENSE        ;Load opponent's defense data from following 4 bytes.
    ⋮
    664L8C5F:  .byte ST_WRITE_BYTE     ;Write a byte to zero page memory.
    665L8C60:  .byte GameStatusBB      ;address $BB.
    666L8C61:  .byte OPP_RGHT_HOOK     ;Indicate a right hook is being thrown.
    ⋮
    678L8C67:  .byte ST_WRITE_BYTE     ;Write a byte to zero page memory.
    679L8C68:  .byte OppOutlineTimer   ;Change opponent's outline color to indicate its time to dodge.
    680L8C69:  .byte $84               ;Change outline color for 4 frames.
    ⋮
    
    source_files/PRG_Bank000.asm lines 634–709 at 60b2fa0 · upstream ↗
    641L8C50:  .byte ST_REPEAT         ;Load sub-state repeat.
    642L8C51:  .byte $01               ;No repeat for this sub-state.
    
    source_files/PRG_Bank000.asm lines 641–642 at 60b2fa0 · upstream ↗
    685;Index #$1E.
    686L8C6B:  .byte ST_CHK_REPEAT     ;Check if repeat counter is active.
    687L8C6C:  .byte $24               ;Jump to index #$24 is repeat counter is active.
    
    source_files/PRG_Bank000.asm lines 685–687 at 60b2fa0 · upstream ↗
    5130ChkRepeatState:
    5131LC7A7:  DEC OppStRepeatCntr     ;Decrement sub-state repeat counter.
    5132LC7A9:  BEQ ChkRepeatEnd        ;Does state need to be repeated? If not, branch to exit.
    
    source_files/PRG_BankDEF.asm lines 5130–5143 at 60b2fa0 · upstream ↗
    717;Index #$32.
    718L8C7F:  .byte ST_PNCH_ACTIVE    ;Indicate a punch is active.
    ⋮
    723;Index #$36 - Punch landed.
    724L8C83:  .byte ST_TIMER          ;Load timer for this sub-state.
    725L8C84:  .byte $04               ;This sub-state lasts 4 frames.
    ⋮
    728L8C85:  .byte ST_SPRT_MV_NU+4   ;Move sprites. Sub-state lasts for 4 frames.
    ⋮
    744L8C8D:  .byte ST_SPRT_MV_NU+15  ;Move sprites. Sub-state lasts for 15 frames.
    ⋮
    751;Index #$44.
    752L8C91:  .byte ST_END            ;End of state.
    
    source_files/PRG_Bank000.asm lines 717–752 at 60b2fa0 · upstream ↗
    718L8C7F:  .byte ST_PNCH_ACTIVE    ;Indicate a punch is active.
    719L8C80:  .byte $45               ;Index to jump to if punch was blocked.
    720L8C81:  .byte $49               ;Index to jump to if punch was ducked.
    721L8C82:  .byte $49               ;Index to jump to if punch was dodged.
    ⋮
    755L8C92:  .byte ST_JUMP           ;Jump to new state and index.
    756L8C93:  .word GJStrtPunch       ;Base address of new state.
    757L8C95:  .byte $3B               ;Index of new state.
    ⋮
    760L8C96:  .byte ST_SPEC_TIMER     ;Load a special timer value.
    ⋮
    763L8C97:  .byte ST_CALL_FUNC      ;Call state data subroutine
    764L8C98:  .word DefenseStats6     ;Defense data pointer.
    ⋮
    787L8CA4:  .byte ST_COMBO_WAIT     ;Wait for the combo timer to expire.
    ⋮
    790L8CA5:  .byte ST_WAIT_STATE     ;Put opponent into wait state.
    
    source_files/PRG_Bank000.asm lines 710–790 at 60b2fa0 · upstream ↗
    1078DefenseStats6:
    1079L92B0:  .byte ST_DEFENSE        ;Load opponent's defense data from following 4 bytes.
    1080L92B1:  .byte $00, $00          ;Face punch defense values.
    1081L92B3:  .byte $00, $00          ;Stomach punch defense values.
    
    source_files/PRG_Bank000.asm lines 1078–1081 at 60b2fa0 · upstream ↗
    4938OppVarStTime:
    4939LC6BD:  LDA VariableStTime      ;
    4940LC6C0:  STA OppStateTimer       ;Set a varying amount of time for this opponent's state time.
    
    source_files/PRG_BankDEF.asm lines 4938–4941 at 60b2fa0 · upstream ↗
    1100;Glass Joe Fight data. Loaded into $05A0 through $05FF.
    ⋮
    1113L9325:  .byte $04               ;Base damage if opponent lands a hit on Little Mac.
    
    source_files/PRG_Bank000.asm lines 1100–1113 at 60b2fa0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  4. ^The NES Rev 1 cues a dodge by recoloring the opponent's outline. A script writes OppOutlineTimer with its high bit set; the main game loop calls SetOppOutlineClr, which then clears that bit, keeps the count, and writes the fight's dodge outline color into four sprite palette entries. Each later call counts the timer down, and at zero it writes the normal color back. Glass Joe's fight data gives palette values $26 for the dodge outline and $08 for the normal one, and his right hook sets the timer to $84, a count of 4 later calls (4 frames by the comment), before its variable wait and the punch.Codenmikstas/mike-tysons-punch-out-disassemblyrevised
    How we know
    66.alias OppOutlineTimer  $9C     ;Timer for dodge indicator outline color. MSB set=set timer.
    
    source_files/Mike_Tysons_Punchout_Defines.asm line 66 at 60b2fa0 · upstream ↗
    4493SetOppOutlineClr:
    4494LC440:  LDA OppOutlineTimer     ;Is outline color timer active?
    4495LC442:  BEQ OppOutlineEnd       ;If not, branch to exit.
    ⋮
    4497LC444:  BMI SetOutlineTimer     ;Does ouotline color timer need to be initialized? If so, branch.
    ⋮
    4499LC446:  DEC OppOutlineTimer     ;Decrement outline color timer. Has timer expired?
    4500LC448:  BNE OppOutlineEnd       ;If not, branch to exit.
    ⋮
    4502LC44A:  LDY #$01                ;Prepare to set normal outline color.
    ⋮
    4506LC44E:  AND #$7F                ;Clear MSB to indicate timer is running.
    4507LC450:  STA OppOutlineTimer     ;Set outline color timer.
    4508LC452:  LDY #$00                ;Prepare to set dodge indicator outline color.
    ⋮
    4511LC454:  LDA OppOutline,Y        ;Get opponent outline color.
    4512LC457:  LDX #$04                ;Prepare to change 4 sprite palette bytes.
    ⋮
    
    source_files/PRG_BankDEF.asm lines 4493–4530 at 60b2fa0 · upstream ↗
    725MainGameEngine:
    ⋮
    763LA6C3:  JSR SetOppOutlineClr    ;($C440)Set opponent outline color.
    
    source_files/PRG_BankDEF.asm lines 725–763 at 60b2fa0 · upstream ↗
    224.alias OppOutline       $05EC   ;Base address for determining the opponent's outline color.
    
    source_files/Mike_Tysons_Punchout_Defines.asm line 224 at 60b2fa0 · upstream ↗
    1139L9363:  .byte $26               ;Opponent outline color when Little Mac should dodge.
    1140L9364:  .byte $08               ;Normal opponent outline color.
    
    source_files/PRG_Bank000.asm lines 1139–1140 at 60b2fa0 · upstream ↗
    678L8C67:  .byte ST_WRITE_BYTE     ;Write a byte to zero page memory.
    679L8C68:  .byte OppOutlineTimer   ;Change opponent's outline color to indicate its time to dodge.
    680L8C69:  .byte $84               ;Change outline color for 4 frames.
    ⋮
    683L8C6A:  .byte ST_VAR_TIME       ;Set sub-state time to a variable amount.
    
    source_files/PRG_Bank000.asm lines 677–683 at 60b2fa0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  5. ^Little Mac's hearts in the NES Rev 1 are a two-digit decimal count. Glass Joe's fight data, in its comments, starts rounds 1, 2, and 3 at 20, 20, and 15 hearts, with normal recoveries of 15, 15, and 10 and reduced ones of 9, 9, and 5. The round setup routine, uncommented (it also loads the round's clock rate and starts the round timer), sets hearts to the table's value in round 1 and raises them to it in rounds 2 and 3 if they're lower, and it copies the two recovery values for the round. An uncommented routine at $843E takes hearts away, 1 or 3 at each of its six callers; when the count reaches zero or below it clears MacCanPunch, which the define reads as "Little Mac can't punch".Codenmikstas/mike-tysons-punch-out-disassembly
    How we know
    1100;Glass Joe Fight data. Loaded into $05A0 through $05FF.
    ⋮
    1104L931C:  .byte $20               ;Starting number of hearts, round 1(base 10).
    1105L931D:  .byte $15               ;Normal heart recovery, round 1(base 10).
    1106L931E:  .byte $09               ;Reduced heart recovery, round 1(base 10).
    1107L931F:  .byte $20               ;Starting number of hearts, round 2(base 10).
    1108L9320:  .byte $15               ;Normal heart recovery, round 2(base 10).
    1109L9321:  .byte $09               ;Reduced heart recovery, round 2(base 10).
    1110L9322:  .byte $15               ;Starting number of hearts, round 3(base 10).
    1111L9323:  .byte $10               ;Normal heart recovery, round 3(base 10).
    1112L9324:  .byte $05               ;Reduced heart recovery, round 3(base 10).
    
    source_files/PRG_Bank000.asm lines 1100–1112 at 60b2fa0 · upstream ↗
    207.alias HeartTable       $05A3   ;Table of heart values for this fight. (Indexing starts at 3)
    
    source_files/Mike_Tysons_Punchout_Defines.asm line 207 at 60b2fa0 · upstream ↗
    2733LB5A9:  LDA #$01
    2734LB5AB:  STA MacCanPunch
    2735LB5AD:  LDA RoundNumber
    2736LB5AF:  CMP #$04
    2737LB5B1:  BCC $B5B6
    2738LB5B3:  JMP $B621
    2739LB5B6:  ASL
    2740LB5B7:  ADC RoundNumber
    2741LB5B9:  TAX
    2742LB5BA:  CMP #$03
    2743LB5BC:  BEQ $B5CB
    2744LB5BE:  LDA HeartTable,X        ;($05A3)
    ⋮
    
    source_files/PRG_BankDEF.asm lines 2733–2786 at 60b2fa0 · upstream ↗
    514L843E:  TAX
    515L843F:  LDA CurHeartsLD
    516L8442:  ORA CurHeartsUD
    517L8445:  BEQ $849E
    518L8447:  TXA
    519L8448:  SEC
    520L8449:  STA $E7
    521L844B:  LDA CurHeartsLD
    522L844E:  SBC $E7
    523L8450:  BCS $8455
    524L8452:  ADC #$0A
    525L8454:  CLC
    ⋮
    
    source_files/PRG_Bank007.asm lines 514–535 at 60b2fa0 · upstream ↗
    109L80D1:  LDA #$01
    110L80D3:  JSR $843E
    
    source_files/PRG_Bank007.asm lines 109–110 at 60b2fa0 · upstream ↗
    163L8143:  LDA #$03
    164L8145:  JSR $843E
    
    source_files/PRG_Bank007.asm lines 163–164 at 60b2fa0 · upstream ↗
    94.alias MacCanPunch      $BC     ;#$00=Little Mac can't punch, #$01=Little Mac can punch.
    
    source_files/Mike_Tysons_Punchout_Defines.asm line 94 at 60b2fa0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  6. ^When KnockdownSts is 1 ("Opp down", per the define) in the NES Rev 1, uncommented code at $B2EB counts the knockdown in OppKDRound and OppKDFight, and at the third in a round it stops there. Otherwise, unless $BA held $FF, it reads one entry from the opponent's table at OppRefillPtr (its index set by conditions not traced here). An entry of 0 sets nothing. An entry whose upper nibble n is nonzero sets OppGetUpCount to $99 + n, which the define reads as a count of n. An entry whose upper nibble is 0 picks one of the eight entries of OppGetUpTable with three bits of the random byte $18; Glass Joe's eight read, by the comment's encoding, 2, 3, 4, 5, 6, 8, 3, and 4.Codenmikstas/mike-tysons-punch-out-disassembly
    How we know
    2386LB2EB:  LDX $05
    2387LB2ED:  DEX
    2388LB2EE:  BNE $B2E7
    ⋮
    2394LB2FB:  INC OppKDRound          ;($03CA)
    2395LB2FE:  INC OppKDFight          ;($03D1)
    2396LB301:  LDA OppKDRound          ;($03CA)
    2397LB304:  CMP #$03
    2398LB306:  BEQ $B2E2
    2399LB308:  LDA #$00
    2400LB30A:  LDX $BA
    2401LB30C:  STA $BA
    2402LB30E:  INX
    ⋮
    
    source_files/PRG_BankDEF.asm lines 2386–2466 at 60b2fa0 · upstream ↗
    2380LB2E2:  LDA #$00
    2381LB2E4:  STA $C0
    2382LB2E6:  RTS
    ⋮
    2385LB2EA:  RTS
    
    source_files/PRG_BankDEF.asm lines 2380–2385 at 60b2fa0 · upstream ↗
    14.alias KnockdownSts     $05     ;Knockdown status #$01=Opp down, #$02=Mac down
    
    source_files/Mike_Tysons_Punchout_Defines.asm line 14 at 60b2fa0 · upstream ↗
    100.alias OppGetUpCount    $C4     ;Count opponent will get up on. #$9A=1 through #$A2=9.
    
    source_files/Mike_Tysons_Punchout_Defines.asm line 100 at 60b2fa0 · upstream ↗
    1134;Random opponent stand up times after knock down. #$9B=2 through #$A1=8.
    1135L9356:  .byte $9B, $9C, $9D, $9E, $9F, $A1, $9C, $9D
    
    source_files/PRG_Bank000.asm lines 1134–1135 at 60b2fa0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

Sources

Code