86Box / 86Box

Emulator of x86-based machines.
GNU General Public License v2.0
2.65k stars 330 forks source link

Electronic Arts IBM Interlock copy protection #4659

Closed Digitoxin1 closed 1 month ago

Digitoxin1 commented 1 month ago

What happened?

86Box is unable to boot floppy disk images of disks protected with Electronic Arts IBM Interlock copy protection. These are all "booter" disks release by Electronic Arts. The format of the disk images does not matter. Neither .mfm or .86f is supported. The following titles are protected with Electronic Arts IBM Interlock.

These disk images have the following characteristics:

  1. Side 0 or tracks 0-14 and tracks 16-39 have 10 sectors per track instead of the standard 9. The 10th sector on these tracks is empty (Filled with &HF6).
  2. The sectors are interleaved on the above mentioned tracks. There are in the order 6, 1, 7, 2, 8, 3, 9, 4, 10, 5.
  3. Track 16, side 0 has 96 overlapping interleaved sectors.

Configuration file

[Tandy 1000]
display_type = 0

[Microsoft Serial Mouse]
port = 0
buttons = 2

[General]
vid_renderer = qt_software
host_cpu = AMD Ryzen 9 3900X 12-Core Processor            
emu_build_num = 5906
uuid = 5826524b-9886-5137-8d28-740e5c063053

[Machine]
machine = tandy
cpu_family = 8088_europc
cpu_speed = 4772728
cpu_multi = 1
cpu_use_dynarec = 0
time_sync = local
fpu_softfloat = 0
mem_size = 512

[Video]
gfxcard = internal

[Input devices]
mouse_type = msserial
joystick_type = 2axis_2button
joystick_0_nr = 0
joystick_1_nr = 0

[Sound]
fm_driver = nuked

[Storage controllers]
hdc = st506_xt_st11_m
cassette_mode = load

[Floppy and CD-ROM drives]
fdd_02_type = 35_2dd

[Everex EV-170 Magic I/O]
base = 02C0
irq = -1

[PC/XT XTIDE]
bios = xt

[Hard disks]
hdd_01_parameters = 17, 10, 965, 0, mfm
hdd_01_fn = MS-DOS_5.img
hdd_01_mfm_channel = 0

[Other peripherals]
isartc_type = ev170

[ST-11M MFM Fixed Disk Adapter]
base = 0320
irq = 2
bios_addr = C8000
revision = 5

Operating system

Windows 10

CPU

AMD Ryzen 9 3900X

86Box version

v4.2 build 6000

Build architecture

Windows - x64 (64-bit)

Build type

Download source

Official website (Jenkins, GitHub)

Additional context

No response

OBattler commented 1 month ago

Not setting your drive to 3.5" 720k woud have helped, given that these appear to be 360k disks.

Digitoxin1 commented 1 month ago

Not setting your drive to 3.5" 720k woud have helped, given that these appear to be 360k disks.

That is drive 2, the first drive is 360k

OBattler commented 1 month ago

And of course they don't work - they are in the original MFM format and at least some of the tracks wrap around in the middle of a sector - that's completely unusable since the original MFM format stores bitcell lengths rounded to the nearest byte, so there's no way to determine at what bitcell exactly do they wrap around.

Digitoxin1 commented 1 month ago

And of course they don't work - they are in the original MFM format and at least some of the tracks wrap around in the middle of a sector - that's completely unusable since the original MFM format stores bitcell lengths rounded to the nearest byte, so there's no way to determine at what bitcell exactly do they wrap around.

Will the .86f format get around this issue?

OBattler commented 1 month ago

Wait no, that's the gap they wrap around at, so I was wrong. That's good. Let me see what's the problem then.

OBattler commented 1 month ago

Except for the copy-protected track - that one has no gap, and the image wraps around in the middle of a sector. So it's HxC MFM's rounding messing it up.

OBattler commented 1 month ago

I just converted one example directly from KryoFlux to modified MFM using my ancient modded version of HxC from 2016, let's see if that works.

OBattler commented 1 month ago

Nope, not even that works.

OBattler commented 1 month ago

At this point, the only thing I can think of is that it also relies on undocumented FDC behavior.

NRS-NewRisingSun commented 1 month ago

Electronic Arts Interlock relies on all bytes of the results phase of an FDC read command being correct.

I have attached three files to help you. The DOS program CHECKEA.COM reads the copy protected track 15.0 from an Interlock disk in A, reading every one of the 96 sectors on that track, and outputting the result bytes that BIOS stored at 0040:0042 to the console, which are the status bytes that Interlock's keydisk checking code looks at. CHECKEA sets the Disk Parameter Table's "last sector" to 0, just as Interlock's keydisk checking code does, prior to reading; (note that the IBM PCjr BIOS does not send that value to the FDC, substituting its own value instead). RESULTS-PC.TXT and RESULTS-JR.TXT are the results from running the program on a real IBM PC/XT and real IBM PCjr, respectively. I included both since the results differ due to the PCjr running in non-DMA mode. Note that on the IBM PCjr, every successful Read Data operation ends with the FDC signalling End of Track error, which BIOS converts to a successful operation (see IBM PCjr Technical Reference, page A-79), so in the IBM PCjr case, interpreting the output of RESULTS-JR.TXT requires understanding how BIOS changes the bytes of the FDC Results Phase when converting an End of Track error into a successful operation. checkea.zip

If you manage to modify your FDC emulation to make CHECKEA.COM to return the same result as the real hardware results that I included, you should be able to get the Interlock games running in 86Box as well, assuming there is not another issue that has not been accounted for.

The Interlock track basically consists just of 96 Sector IDs with little data in them. The even-numbered sectors have a Deleted Data Address Mark (DDAM) rather than a Data Address Mark (DAM). The reason it is called "Interlock" is that all the sectors overlap heavily, yet they still manage to get the CRC fields of every odd-numbered sector from 1-43 right. 45-47 cross the index and therefore do not have a correct CRC, and do not need to; the bit-exact track length is irrelevant (since it was mentioned). Correct emulation requires setting the correct FDC status bits when encounting a Deleted Data Address Mark (DDAM), signalling CRC errors correctly (duh), and getting the treatment of the "last sector on track" field, that is sent of every command phase, meticulously correct in both the CRC error and non-CRC error, DDAM and DAM, and DMA and non-DMA scenarios, both in terms of status bits and the post-transfer "current cylinder", "current head" and "current sector" bytes of the results phase (the last three bytes of each line of RESULTS-??.TXT).

NRS-NewRisingSun commented 1 month ago

TRK15-0.zip Also, here is a raw binary dump of the Interlock track 15.0 if that helps.

I have not tried a recent build of 86Box. The last time I tried it was with build 5111, in which not even the ECA Logo was displayed correctly. Since that logo is displayed before the Interlock track is checked, there probably is another issue, possibly due to the combination of having ten sectors per track together with the "last sector" field of the Disk Parameter Table being set by the loader's code to 0.

OBattler commented 1 month ago

I can tell you that that issue is still very much present - I see the logo displaying like garbage.

OBattler commented 1 month ago

Also, the sector read results on my end match what's in your real hardware log, which makes it even more puzzling why the game isn't liking it.

This is what I see in my log:

Reading sector E6 (drive 0) (0) (0 0 1 2) (8 42 255) TC FDC OK : 00 00 00 00 00 02 02 Reading sector E6 (drive 0) (0) (0 0 9 2) (0 42 255) TC FDC OK : 00 00 00 00 00 0A 02 Reading sector E6 (drive 0) (0) (1 0 1 2) (0 42 255) TC FDC OK : 00 00 00 01 00 02 02 Reading sector E6 (drive 0) (0) (2 0 1 2) (0 42 255) TC FDC OK : 00 00 00 02 00 02 02 Reading sector E6 (drive 0) (0) (3 0 1 2) (0 42 255) TC FDC OK : 00 00 00 03 00 02 02 Reading sector E6 (drive 0) (0) (16 0 9 2) (0 42 255) TC FDC OK : 00 00 00 10 00 0A 02 Reading sector E6 (drive 0) (0) (17 0 9 2) (0 42 255) TC FDC OK : 00 00 00 11 00 0A 02 Reading sector E6 (drive 0) (0) (18 0 9 2) (0 42 255) TC FDC OK : 00 00 00 12 00 0A 02 Reading sector E6 (drive 0) (0) (19 0 9 2) (0 42 255) TC FDC OK : 00 00 00 13 00 0A 02 Reading sector E6 (drive 0) (0) (15 0 15 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 10 02 Reading sector E6 (drive 0) (0) (15 0 13 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 0E 02 Reading sector E6 (drive 0) (0) (15 0 11 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 0C 02 Reading sector E6 (drive 0) (0) (15 0 9 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 0A 02 Reading sector E6 (drive 0) (0) (15 0 7 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 08 02 Reading sector E6 (drive 0) (0) (15 0 5 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 06 02 Reading sector E6 (drive 0) (0) (15 0 3 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 04 02 Reading sector E6 (drive 0) (0) (0 0 9 2) (0 42 255) TC FDC OK : 00 00 00 00 00 0A 02 Reading sector E6 (drive 0) (0) (1 0 1 2) (0 42 255) TC FDC OK : 00 00 00 01 00 02 02 Reading sector E6 (drive 0) (0) (2 0 1 2) (0 42 255) TC FDC OK : 00 00 00 02 00 02 02 Reading sector E6 (drive 0) (0) (3 0 1 2) (0 42 255) TC FDC OK : 00 00 00 03 00 02 02 Reading sector E6 (drive 0) (0) (16 0 9 2) (0 42 255) TC FDC OK : 00 00 00 10 00 0A 02 Reading sector E6 (drive 0) (0) (17 0 9 2) (0 42 255) TC FDC OK : 00 00 00 11 00 0A 02 Reading sector E6 (drive 0) (0) (18 0 9 2) (0 42 255) TC FDC OK : 00 00 00 12 00 0A 02 Reading sector E6 (drive 0) (0) (19 0 9 2) (0 42 255) TC FDC OK : 00 00 00 13 00 0A 02 Reading sector E6 (drive 0) (0) (15 0 15 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 10 02 Reading sector E6 (drive 0) (0) (15 0 13 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 0E 02 Reading sector E6 (drive 0) (0) (15 0 11 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 0C 02 Reading sector E6 (drive 0) (0) (15 0 9 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 0A 02 Reading sector E6 (drive 0) (0) (15 0 7 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 08 02 Reading sector E6 (drive 0) (0) (15 0 5 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 06 02 Reading sector E6 (drive 0) (0) (15 0 3 2) (0 42 255) TC FDC OK : 00 00 00 0F 00 04 02

OBattler commented 1 month ago

I do notice that EOT is set to 0, and I'm not quite sure what the real FDC's behavior is supposed to be in that case - I spent two hours googling last night, to no avail.

OBattler commented 1 month ago

This is fun - on the emulated PCjr, the logo is displayed correctly. So there's a bug in DMA mode. But it still hangs, so something isn't returned correctly in PIO mode, either.

NRS-NewRisingSun commented 1 month ago

The FDC ends a Read Data command after the current sector if the current sector is equal, not greater or equal, to EOT, with an End of Track error, or successfully when the DMA Terminal Count signal is received. The loader sets EOT to 0, and since there is no sector number 0, it means that the Read Data Command ends only when the DMA Terminal Count signal is received.

Yes, the logo is read directly into video memory at segment B800 without being buffered in-between. I don't know how video memory is mapped into address space internally in 86Box, but I could imagine this causing problems -- on my real Pentium II system with an AGP graphics card, the logo is not visible at all because old-style DMA directly into PCI/AGP video meory is apparently not possible.

You state that the sector read results match my hardware log, but they don't for the Deleted Data Address Mark sectors (even numbered: 2,4,6,...,42). The keydisk checking routine relies on these values. Attached text file has two columns: left is the result from running my CHECKEA utility on real hardware, right is what 86Box build 6000 (Windows 64-bit) returns (XT machine type). Obviously, those are not identical. Check the treatment of Deleted Data Address Marks (DDAM) in your FDC emulation.

XTvs86Box.zip

Edit: The NEC µPD756 data sheet description of what to do when a Deleted Data Address Mark is encountered is confusing as hell. The Intel 82077AA data sheet explains it much better, in Table 5-4 ("Skip Bit vs. READ DATA Command").

OBattler commented 1 month ago

They matched your log for the limited sector readthat occur on the XT. On the PCjr, they indeed lacked the "wrong address mark" bit in ST2, I have now locally rectified that and corrected some terminating behaivor as well and the game now boots properly (but the graphics are messed up because there must be a bug somewhere in the PCjr video code).

This leaves the question as to what's wrong in DMA mode.

OBattler commented 1 month ago

Also, I don't see the second column in yoru attached file.

NRS-NewRisingSun commented 1 month ago

The first column is "Real XT", the second is "86Box". They are separated by a pipe character. Real XT 86Box Sector 01: 00 00 00 0F 00 02 | 00 00 00 0F 00 02 Sector 02: 00 00 40 0F 00 04 | 40 04 00 0F 00 02 ...

OBattler commented 1 month ago

I don't see that in the file: imagem .

NRS-NewRisingSun commented 1 month ago

Silly me, I posted the wrong file. Here is the correct file. XTvs86BoxV2.zip

OBattler commented 1 month ago

I fixed the PCjr video bug, now it works properly on the PCjr: imagem . Now to figure out why it messes up on the XT.

OBattler commented 1 month ago

OK, how did you even get it to read the other sectors on 86Box? For me, it stops and retries from the start after reading sector 3 of track 15.

NRS-NewRisingSun commented 1 month ago

I did not get the game's own loader to read the other sectors; I only got my CHECKEA.COM program to read all sectors from 1-96.

The content of sectors 15, 13, 11, 9, 7, 5, 3 is used as a decryption key for the second part of the Interlock checker. If the content is wrong, then the encrypted second part of the Interlock checker will be decrypted incorrectly and therefore crash or freeze. If the DMA problems that you mentioned prevent the correct sector data from being transfered, then that is likely the explanation.

OBattler commented 1 month ago

I have the EA logo in DMA mode now: imagem .

OBattler commented 1 month ago

OK so, the game appears to be using command E6 which is read with skipping sectors with the wrong address mark. Strange that it's expected to return it.

OBattler commented 1 month ago

Also, sector 36 has bad CRC according to HxC, but in your read, it returns OK.

NRS-NewRisingSun commented 1 month ago

That is because CHECKEA reads the sector also with command E6, causing sector 36 to be skipped, so the CRC is computed over the data of the next sector (37), which has a good CRC. HxC on the other hand does not skip the sector. The idea is that when the deleted data address mark is skipped, it's not that nothing is returned, but that the following sector is returned.

(The whole Deleted Data Address Mark business is headache-inducing.)

OBattler commented 1 month ago

I have that working correctly now. Now the last remainin puzzle are header CRC errors - does the real FDC still attempt to read the data in that case?

NRS-NewRisingSun commented 1 month ago

No, any time a CRC error is encountered, the Execution phase is immediately "abnormally" terminated; CRC error in ID means the Data is not even read, and CRC error in Data means that any additional sectors that could be read (and that might be good) are not read.

OBattler commented 1 month ago

Wait, I got it to work! imagem .

OBattler commented 1 month ago

Looks much better in composite mode: imagem .

Digitoxin1 commented 1 month ago

I can confirm that all Electronic Arts Interlock protected games now boot successfully in 86Box.

I only had issues with the following two games: Arctic Fox Marble Madness

These two games only pass the copy protection check intermittently. They appear to be using a new version of Interlock and the issue with these two games appears to be a timing issue.

OBattler commented 1 month ago

Do they use specifically timed bitcells?

NRS-NewRisingSun commented 1 month ago

No, the checking code is just buggy; I am told that the issue can appear on real hardware as well, though it appears with some regularity in 86Box. I cannot quite pinpoint yet what is going on, as I cannot reproduice the issue in my own build of DOSBox.

NRS-NewRisingSun commented 1 month ago

When it fails in Marble Madness, it fails because BIOS Int13 returns error code 80h ("Drive not ready"). The checking code executes (xx: random odd sector number below 48, yy: xx+1) AH=0, INT13 AX=0201, CX=0Fxx, INT13 AX=0201, CX=0Fxx, INT13 (yes, again) AH=0, INT13 AX=0201, CX=0Fxx, INT13 AX=0201, CX=0Fyy, INT13 <- Returns with drive not ready? It would go on with x +=2Fh, but in my debugging sessions did not reach that point.

dbalsom commented 1 month ago

@NRS-NewRisingSun

Just wanted to say thank you for the incredible level of technical detail you have provided here.

Here's a visualization of 'Murder on the Zinderneuf' showing the Interlock copy protection track. zinderneuf