Open Jumpman64 opened 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.
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.
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.
patched version Barry McGuigan World Championship Boxing_MRZ_patched.zip
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.
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". :)
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.