GideonZ / 1541ultimate

Official GIT archive of 1541 ultimate II sources
GNU General Public License v3.0
174 stars 45 forks source link

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

Open Jumpman64 opened 3 years ago

Jumpman64 commented 3 years ago

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.

Grrrolf commented 3 years ago

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.

p2mate commented 3 years ago

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)

3) 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

4) 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.

GideonZ commented 3 years ago

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.

p2mate commented 3 years ago

patched version Barry McGuigan World Championship Boxing_MRZ_patched.zip

Jumpman64 commented 3 years ago

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 commented 2 years ago

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". :)