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

An attack pattern is the order of moves an opponent follows. Mike Tyson’s Punch-Out!! writes each move as a script of timed steps1 and picks the next one from a list in which some steps go by chance2. Glass Joe’s right hook is one such script3.

  • Script and chanceMike Tyson's Punch-Out!! (1987). Each 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.1,2,3

Games

GameYearHowNote
Mike Tyson's Punch-Out!!1987Script 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
Every claim about this mechanic, by game (3)
Mike Tyson's Punch-Out!! 1987In 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

References

  1. ^ abIn 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. ^ abThe 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. ^ abGlass 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

Sources

Code