1541ultimate icon indicating copy to clipboard operation
1541ultimate copied to clipboard

[U64] V1.37 Barry McGuigan Boxing - hangs at start screen, can't get into the game anymore

Open Jumpman64 opened this issue 4 years ago • 6 comments

U64 rev1.2 fw v1.37 (latest unofficial 06.12.) In Barry McGuigans Boxing https://csdb.dk/release/?id=18668 (or any other version of the game) you need to press F1/F2 on the first text screen to continue. Using fw 1.37 nothing happens. (tried several settings. original rom, turbo off, etc.) Still works fine with fw 1.28.

Jumpman64 avatar Jan 15 '21 22:01 Jumpman64

I have tested this game on a real c64 and after some 9 seconds it automatically goes to the player selection screen when you do not press F1. When pressing the F1 key within those 9 seconds or so, it will go to the player selection upon registering the key press.

On the U64 this does not happen. It stays on the game introduction screen forever. Meaning that it might be something else then not registering keyboard presses. Tested this on firmware 1.37, 1.34 and 1.33 (the retracted firmware).

Jumpman64 already tested it on v1.28 and mentioned that it works as expected on this firmware.

Grrrolf avatar Jan 16 '21 23:01 Grrrolf

Using retro replay I figured out the following:

  1. There's some sort of protection or anti-cheat sub-routine at address $50ec after decompression (to get to that point, with the retro replay cartridge selected, start the game, break into the monitor when the cracked by.. screen appears and set a freeze point at $0c52).

  2. This routine is called from the IRQ handler from $e4e2. (vector at $fffe points to $ff48 which calls ($0314) pointing to $e4d7)

         handler pointed to from $314
    
         e4d7 a9 01           LDA        #0x1
         e4d9 8d 19 d0        STA        DAT_d019
         e4dc 20 11 36        JSR        FUN_3611                                         undefined FUN_3611()
         e4df 20 09 c0        JSR        thunk_FUN_c1c3                                   undefined thunk_FUN_c1c3()
         e4e2 20 ec 50        JSR        FUN_50ec                                         undefined FUN_50ec()
         e4e5 a9 fa           LDA        #0xfa
         e4e7 8d 12 d0        STA        DAT_d012                                         = A8h
         e4ea a9 00           LDA        #0x0
         e4ec 8d f2 0d        STA        DAT_0df2
         e4ef ad 0d dc        LDA        DAT_dc0d                                         = 69h
         e4f2 68              PLA
         e4f3 a8              TAY
         e4f4 68              PLA
         e4f5 aa              TAX
         e4f6 68              PLA
         e4f7 40              RTI
    
        suspicious routine
    
                          **************************************************************
                          *                          FUNCTION                          *
                          **************************************************************
                          undefined suspicious()
          undefined         A:1            <RETURN>
                          suspicious                                      XREF[2]:     e4e2(c), fca9(W)  
         50ec a2 0f           LDX        #0xf
                          LAB_50ee                                        XREF[1]:     50f7(j)  
         50ee bd 00 de        LDA        0xde00,X=>DAT_de0f                               = AAh
         50f1 dd 02 50        CMP        0x5002,X=>DAT_5011
         50f4 d0 0e           BNE        LAB_5103+1
         50f6 ca              DEX
         50f7 10 f5           BPL        LAB_50ee
         50f9 ae 00 50        LDX        DAT_5000                                         = 0Ah
         50fc ca              DEX
         50fd 10 03           BPL        LAB_5102
         50ff 4c a4 fc        JMP        LAB_fca4
                          LAB_5102                                        XREF[1]:     50fd(j)  
         5102 8a              TXA
                          LAB_5103+1                                      XREF[0,1]:   50f4(j)  
         5103 2c a9 0a        BIT        DAT_0aa9
         5106 8d 00 50        STA        DAT_5000                                         = 0Ah
         5109 a2 0f           LDX        #0xf
                          LAB_510b                                        XREF[1]:     5112(j)  
         510b bd 00 de        LDA        0xde00,X=>DAT_de0f                               = AAh
         510e 9d 02 50        STA        0x5002,X=>DAT_5011
         5111 ca              DEX
         5112 10 f7           BPL        LAB_510b
         5114 a2 0f           LDX        #0xf
                          LAB_5116                                        XREF[1]:     511f(j)  
         5116 bd 00 df        LDA        0xdf00,X=>DAT_df0f                               = EAh
                                                                                          = 5Ah
         5119 dd 12 50        CMP        0x5012,X=>DAT_5021
         511c d0 0e           BNE        LAB_512b+1
         511e ca              DEX
         511f 10 f5           BPL        LAB_5116
                          LAB_5121                                        XREF[1]:     fca6(W)  
         5121 ae 01 50        LDX        DAT_5001                                         = 0Ah
         5124 ca              DEX
         5125 10 03           BPL        LAB_512a
         5127 4c a4 fc        JMP        LAB_fca4
                          LAB_512a                                        XREF[1]:     5125(j)  
         512a 8a              TXA
                          LAB_512b+1                                      XREF[0,1]:   511c(j)  
         512b 2c a9 0a        BIT        DAT_0aa9
         512e 8d 01 50        STA        DAT_5001                                         = 0Ah
         5131 a2 0f           LDX        #0xf
                          LAB_5133                                        XREF[1]:     513a(j)  
         5133 bd 00 df        LDA        0xdf00,X=>DAT_df0f                               = EAh
                                                                                          = 5Ah
         5136 9d 12 50        STA        0x5012,X=>DAT_5021
         5139 ca              DEX
         513a 10 f7           BPL        LAB_5133
         513c 60              RTS
    

Note that this routine has jumps to the middle of an instruction (for example at $50f4)

  1. If the routine at $50ec detects something suspicious, it will jump to $fca4 which will overwrite some memory with garbage.

                          corrupt_memory?                                 XREF[2]:     50ff(j), 5127(j)  
         fca4 a0 00           LDY        #0x0
                          LAB_fca6                                        XREF[1]:     fcb6(j)  
         fca6 99 22 50        STA        FUN_5022,Y
         fca9 99 ec 50        STA        suspicious,Y
         fcac 99 22 35        STA        FUN_3522,Y
         fcaf 99 b9 fc        STA        FUN_fcb9,Y                                       = 19h
         fcb2 4d 1b d4        EOR        DAT_d41b                                         = 55h
         fcb5 88              DEY
         fcb6 d0 ee           BNE        LAB_fca6
         fcb8 52              ??         52h    R
    
  2. NOPing the JSR at $e4e2 seems to make the game work. I have not played it extensively so I don't know if there are other traps.

Questions remaining:

  1. What does the code at $50ec do?

  2. Why does it not crash on fw 1.28 or on an actual C64?

Hopefully someone who's more familiar with this kind of code can help here.

p2mate avatar Mar 01 '21 21:03 p2mate

Ok.. this explains a lot! Thank you for looking into this.

What happens in the routine $50ec, is that it is verified that the space in DE00 is actually empty, thus floating. If a cartridge is there, the game will refuse to start. It must be an anti-grabbing protection feature. The timing of this routine is such that it will read known data from the VIC. The VIC fetches are setup such that the other half of the cycle (the CPU half) reads the data that is expected to be fetched by the VIC half a cycle earlier.

Why does this work on a real C64? Because there is only one address/data bus, and all bytes will be visible there and linger on the bus until pushed to another value by any selected device. If no device is selected during the CPU half of the cycle, the VIC data from the previous half cycle is still there. Why does it work on FW 1.28? Because the external bus to the cartridge port is actually used for all accesses, also when internal memory is addressed. (The RAM in the FPGA drives the memory value on the bus, and the CPU reads it again from the external pins...). This mimics the original C64 very closely.

Why does it not work on FW 1.37? Because there are multiple address / data buses. The architecture was changed such that all internal addresses remain internal, and the external bus is only used when some data needs to be fetched from a cartridge, or written to the SID, for example. This has the benefit that the internal bus can be much faster than 1 MHz, which is a requirement for the Turbo Mode. In addition, there is much less noise on the SID chips, which increases sound fidelity.

What needs to be added is an internal bus model with floating lines. In that case, the game will work when the 'internal' cartridge mode is selected. Right now, the switch between internal and external cart is made "late". In other words, the architecture doesn't know about two independent cartridge buses. When an address decodes to an external location, the bus bridge is invoked and the address and data will appear on the pins of the FPGA, regardless of the internal / external setting. Then, depending on the external / internal switch the correct byte is selected for reading (the one from the built-in U2+, or the one from the external cartridge). This can be solved by decoding the internal / external address early, and implementing two independent bus bridges. The internal bridge should then read the previous VIC data, and the program will think the data is floating on the bus, while retaining the quiet bus state.

GideonZ avatar Mar 01 '21 21:03 GideonZ

It's always fun to see repatched titles after all these years. :) Thanks @p2mate , i tired what you did in the beginning but i only got to that X Y NOP loop in the VICE monitor, fiddeled around with breakpoints but gave up finally. Opening this issue then was easier. ;)
Thanks for the detailed explanation @GideonZ . Looks like this find and solution will lead to a general solution that fixes it for some other titles too.

Jumpman64 avatar Mar 15 '21 09:03 Jumpman64

In this context the georamtest.prg is checking successfully the expansion even if there's no GeoRAM cartridge selected on the U64. Try georamtest.zip

If GeoRAM is not active on a real C64/1541U2 (3.7) the prg quits with this message "Sorry, no active ram-expansion found."

In fw 3.10 GeoRAM is not available in the cartridge section but that would be another issue. At least GeoRAM is still "there". :)

Jumpman64 avatar Jul 31 '21 07:07 Jumpman64