MTPO Milli Hack Description: Add granularity to the millisecond timer in MTPO reported at the end of fights. History: v1.0 (5/1/20): Initial ver, updated end of fights for all characters except Tyson. v2.0 (5/3/20): Update to add time to Tyson victory screen. v3.0 (5/7/20): Replaced points counter with realtime last-punch-landed timer. It should not affect game timing/lag. I have seen instances of overrun ahead of actual round timer, this only appears to occur on knockdowns not ending the fight. ===================================================================================== RAM position 0x306 contains the MS counter, while 0x307 appears to contain an inverse counter. It's not clear the role of 0x307, perhaps an anti-cheat/sanity check (not used afaict). At the beginning of round 1, 0x308/9 are loaded with: 04 F9 (1st rd), 05 6D (2nd rd), 05 F8 (3rd rd). The MS counter at 0x306 adds 0x308 to itself with each iteration, until it is >= 0x64 (100). This code can be traced: 03:8780:AD 07 03 LDA $0307 = #$AF 03:8783:18 CLC 03:8784:6D 09 03 ADC $0309 = #$F9 03:8787:8D 07 03 STA $0307 = #$AF 03:878A:AD 06 03 LDA $0306 = #$5E ; MS 03:878D:6D 08 03 ADC $0308 = #$04 03:8790:8D 06 03 STA $0306 = #$5E 03:8793:C9 64 CMP #$64 ; is MS >= 0x64 03:8795:90 52 BCC $87E9 ; if not, skip updating time 03:8797:E9 64 SBC #$64 ; subtract 0x64 03:8799:8D 06 03 STA $0306 = #$5E ; save the overflow Once victory occurs, the following code translates the MS on the win screen: 06:BC99:AD 06 03 LDA $0306 = #$14 ; load MS 06:BC9C:29 F0 AND #$F0 ; clear low nibble 06:BC9E:4A LSR ; shift right x1 06:BC9F:4A LSR ; "" 06:BCA0:4A LSR ; "" 06:BCA1:A8 TAY So the above essentially rounds the actual MS to the next lowest 16, then divides by 8. This basically gives us all the even #s from 0-12. Finally it takes this even # and pulls the limited MS values from a lookup table (there are a few versions): 06:BCA2:B9 B0 BC LDA $BCB0,Y @ $BD3D = #$5C 06:BCA5:8D 07 20 STA PPU_DATA = #$00 06:BCA8:C8 INY 06:BCA9:B9 B0 BC LDA $BCB0,Y @ $BD3D = #$5C 06:BCAC:8D 07 20 STA PPU_DATA = #$00 BCB0: 01 01 03 06 05 09 07 02 09 03 0A 08 0A 0A (if you subtract one from each, this is .00, .25, etc) So we replace the drawing code and lookup table with a routine to convert the hexadecimal MS (0x306) directly to decimal (+1 to adjust for tile ID): 06:BC99:AD 06 03 LDA $0306 = #$4A 06:BC9C:C9 64 CMP #$64 ; is the MS actually = 100? it can happen (race condition?) before the seconds are updated 06:BC9E:90 02 BCC $BCA2 06:BCA0:A9 63 LDA #$63 ; if it is, we will just make it .99 06:BCA2:A2 01 LDX #$01 06:BCA4:C9 0A CMP #$0A 06:BCA6:90 06 BCC $BCAE 06:BCA8:E9 0A SBC #$0A 06:BCAA:E8 INX 06:BCAB:4C A4 BC JMP $BCA4 06:BCAE:A8 TAY 06:BCAF:C8 INY 06:BCB0:8E 07 20 STX PPU_DATA = #$00 ; store tenths 06:BCB3:8C 07 20 STY PPU_DATA = #$00 ; store hundreths 06:BCB6:60 RTS *** v2.0: Disassembly for Tyson time addition *** First thing to do is modify the victory text(0x120F7) to make room for the time. I simply truncated it at the 3rd line and added 'TIME' to start 4th line. The Tyson victory screen code can be found at: 06:B757: 20 66 B6 JSR $B666 06:B75A: 20 5C AA JSR $AA5C 06:B75D: A9 FF LDA #$FF 06:B75F: 20 AE BF JSR $BFAE 06:B762: 20 B2 BF JSR $BFB2 06:B765: A9 06 LDA #$06 06:B767: A2 05 LDX #$05 06:B769: 20 21 BF JSR $BF21 ; this fills the PPU with the victory pic/text 06:B76C: A9 01 LDA #$01 ; replace with 06:B76E: 20 13 C1 JSR $C113 ; jmp to $8d50 06:B771: A9 00 LDA #$00 06:B773: A2 01 LDX #$01 06:B775: 20 0D BF JSR $BF0D So we intercept @ B76C and jump to our code in some unused ROM space: 04:8D50: A9 23 LDA #$23 04:8D52: 8D 06 20 STA PPU_ADDRESS = #$44 04:8D55: A9 56 LDA #$56 04:8D57: 8D 06 20 STA PPU_ADDRESS = #$44 ; set PPU write address to 2356, 4th line of text 04:8D5A: 20 F2 C0 JSR $C0F2 ; dump m:ss 04:8D5D: 20 94 BC JSR $BC94 ; dump .ms 04:8D60: A9 2B LDA #$2B 04:8D62: 8D 07 20 STA PPU_DATA = #$00 ; , 04:8D65: A9 1C LDA #$1C 04:8D67: 8D 07 20 STA PPU_DATA = #$00 ; R 04:8D6A: A6 06 LDX $06 = #$01 04:8D6C: E8 INX 04:8D6D: 8E 07 20 STX PPU_DATA = #$00 ; x 04:8D70: A9 01 LDA #$01 ; execute code 04:8D72: 20 13 C1 JSR $C113 ; we overwrote above 04:8D75: 4C 71 B7 JMP $B771 ; return to original code ************************************************* *** v3.0: Disassembly for pts => time hack ****** First change the 'POINTS' text in the ROM (0x191D2) to 'TIME:'. Next, the code for updating the points total occurs @ C0C3: 07:C0C3: A9 20 LDA #$20 07:C0C5: 8D 06 20 STA PPU_ADDRESS = #$00 07:C0C8: A9 88 LDA #$88 ; $2088 is where pts total starts 07:C0CA: 8D 06 20 STA PPU_ADDRESS = #$00 07:C0CD: A2 00 LDX #$00 07:C0CF: BD F1 03 LDA $03F1,X @ $0430 = #$00 07:C0D2: 8D 07 20 STA PPU_DATA = #$00 07:C0D5: E8 INX 07:C0D6: E0 06 CPX #$06 ; loop to print up to 6 digits 07:C0D8: D0 F5 BNE $C0CF The updated code for the time hack: 07:C0C3: A9 20 LDA #$20 07:C0C5: 8D 06 20 STA PPU_ADDRESS = #$4D 07:C0C8: A9 87 LDA #$87 ; adjusted start by -1 for more room 07:C0CA: 8D 06 20 STA PPU_ADDRESS = #$4D 07:C0CD: 20 F2 C0 JSR $C0F2 ; dump m:ss 07:C0D0: 20 94 BC JSR $BC94 ; dump .ms 07:C0D3: EA NOP 07:C0D4: EA NOP 07:C0D5: EA NOP 07:C0D6: EA NOP 07:C0D7: EA NOP 07:C0D8: EA NOP 07:C0D9: EA NOP ************************************************* Tested on all characters in FCEUX 2.2.3 / Mesen 0.9.9. Thanks to http://tasvideos.org/GameResources/NES/MikeTysonsPunchout/RamMap.html for helping get me started. Patch data below. Stag Shot ================================================================================== Source ROM: Mike Tyson's Punch-Out!! (USA).nes (md5=B9A66B2760DAA7D5639CBAD903DE8A18) Comparing files mtpo.nes and MTPO-MILLI_V3.NES 000120F7: 1D 25 000120FC: 10 FF 000120FD: 13 FF 000120FE: 18 1E 000120FF: 11 13 00012100: 0F 17 00012101: 1C 0F 00012103: 1D FF 00012104: 1A FF 00012105: 0F FF 00012106: 0F FF 00012107: 0E FF 0001210A: 0C FF 0001210B: 0F FF 0001210C: 10 FF 0001210D: 19 FF 0001211A: 1F 25 0001211B: 0D 25 0001211C: 12 2A 00012120: 1C FF 00012121: 0F FF 00012122: 25 FF 00012123: 2A FF 00012D60: 00 A9 00012D61: 00 23 00012D62: 00 8D 00012D63: 00 06 00012D64: 00 20 00012D65: 00 A9 00012D66: 00 56 00012D67: 00 8D 00012D68: 00 06 00012D69: 00 20 00012D6A: 00 20 00012D6B: 00 F2 00012D6C: 00 C0 00012D6D: 00 20 00012D6E: 00 94 00012D6F: 00 BC 00012D70: 00 A9 00012D71: 00 2B 00012D72: 00 8D 00012D73: 00 07 00012D74: 00 20 00012D75: 00 A9 00012D76: 00 1C 00012D77: 00 8D 00012D78: 00 07 00012D79: 00 20 00012D7A: 00 A6 00012D7B: 00 06 00012D7C: 00 E8 00012D7D: 00 8E 00012D7E: 00 07 00012D7F: 00 20 00012D80: 00 A9 00012D81: 00 01 00012D82: 00 20 00012D83: 00 13 00012D84: 00 C1 00012D85: 00 4C 00012D86: 00 71 00012D87: 00 B7 000191D2: 1A 1E 000191D3: 19 13 000191D4: 13 17 000191D5: 18 0F 000191D6: 1E 2C 000191D7: 1D FF 000191D8: 2C FF 0001B77C: A9 4C 0001B77D: 01 50 0001B77E: 20 8D 0001B77F: 13 EA 0001B780: C1 EA 0001BCAC: 29 C9 0001BCAD: F0 64 0001BCAE: 4A 90 0001BCAF: 4A 02 0001BCB0: 4A A9 0001BCB1: A8 63 0001BCB2: B9 A2 0001BCB3: B0 01 0001BCB4: BC C9 0001BCB5: 8D 0A 0001BCB6: 07 90 0001BCB7: 20 06 0001BCB8: C8 E9 0001BCB9: B9 0A 0001BCBA: B0 E8 0001BCBB: BC 4C 0001BCBC: 8D A4 0001BCBD: 07 BC 0001BCBE: 20 A8 0001BCBF: 60 C8 0001BCC0: 01 8E 0001BCC1: 01 07 0001BCC2: 03 20 0001BCC3: 06 8C 0001BCC4: 05 07 0001BCC5: 09 20 0001BCC6: 07 60 0001C0D9: 88 87 0001C0DD: A2 20 0001C0DE: 00 F2 0001C0DF: BD C0 0001C0E0: F1 20 0001C0E1: 03 94 0001C0E2: 8D BC 0001C0E3: 07 EA 0001C0E4: 20 EA 0001C0E5: E8 EA 0001C0E6: E0 EA 0001C0E7: 06 EA 0001C0E8: D0 EA 0001C0E9: F5 EA Patched ROM v3 md5=1E613757E0CA9FE41A637913FD8CA3AA