Two-disk replica of Prince of Persia

A recent discussion of a buggy crack for Lords of Conquest raised the issue of preserving the original bits from protected software.

So I'm exploring a different approach -- instead of patching games to bypass copy-protection code (ie: 'cracking') why not devise methods of satisfying the copy-protection checks without patching the code?

For example, the primary protection in Prince of Persia hides zero-bits between bytes on the disk, where they can't ordinarily be read or copied unless software  intentionally manupulates the controller's state-machine to find it. Here's a sample from Beneath Apple ProDOS  showing how the controller would ordinarily read the disk (top) and how the copy-protection detects a hidden pattern (bottom):

My own notes from that signature shows it as two columns of nibbles.  The disk controller would ordinarily read the nibbles in the left-hand column, discarding the hidden 0-bits.  The copy-protection routine synchronizes the controller so that it reads the nibbles in the right column, discarding the embedded 0-bits that would ordinarily be embedded in the middle of the E7 nibbles.

The hidden bits make the original signature difficult to copy, but it's possible to craft an easy-to-copy signature that satisfies the original signature check because the software merely verifies that it can extract the pattern E7 FC EE E7 FC EE EE FC after it manipulates the controller's state-machine and receives an EE "start" signal.  In fact, it could be an ordinary data sector so long as it meets the copy-protection's requirements as follows:

  1. The sector starts with standard data-start D5 AA AD
  2. There is a group of E7 E7 E7 nibbles that starts within the first 255 bytes, without any prior instances of E7
  3. There are some throwaway nibbles to give the program sufficient time to manipulate the controller's state-machine
  4. There is an EE nibble to signal the start of the signature
  5. Immediately after the EE nibble, the signature pattern follows E7 FC EE E7 FC EE EE FC

All the nibbles used in the copy-protection are valid nibbles for ordinary data, so it simply entails intentionally making data that is recorded with a pattern that will bring the controller back into normal synchronization followed by data that would ordinarily be encoded with the nibbles in the signature.

One candidate for sector data would be:

    00 AC 00 AC AC AC C0 10 E0 24 88 78 BC 10 E0 24 E0 10

When that data is 6-and-2 encoded, the RWTS transforms the top 6-bits of each byte into the nibble pattern:

    96 E7 E7 E7 96 96 CB F3 FC EE E7 FC EE E7 FC EE EE FC

That pattern satisfies the copy-protection routine because:

  • Upon reading the E7 E7 E7 nibbles, the copy-protection routine manipulates the controller's synchronization to force it out-of-sync
  • The 96 96 nibbles pass by harmlessly while the copy-protection routine is adjusting controller's synchronization
  • The nibble pattern CB F3 FC contains a bit-pattern 11001011 11110011 11111100 that forces the controller back into normal synchronization
  • The rest of the data directly encodes as the EE start signal and the E7 FC EE E7 FC EE EE FC signature pattern

The result is a sector that's copyable, error-free, and still satisfies the copy-protection's signatgure check.

BONUS: because the sector is error-free, the rest of the sector is available to store data -- so long as we ensure that its 6-and-2 encoding doesn't accidentally store any E7 nibbles prior to the impersonated signature.  (The copy-protection routine is sensitive to that!)  In this instance, I used the extra space to store a patch to fix a bug in Prince of Persia's KEYS routine, which affects gameplay when played on the keyboard on an Apple //e or //c.  And I modified 6 bits of the boot sector to make it load the bonus sector and call the patch routine.

Content Type: 

Comments

Could you please explain more about the keyboard patch. What is the problem, what does the patch do?

S.Elliott's picture

roughana wrote:

Could you please explain more about the keyboard patch. What is the problem, what does the patch do?

Ok, I will publish more details eventually -- but here's a quick summary.

  1. Prince of Persia relies on the AKD flag, which indicates when any key is pressed down.  Unfortunately, as noted here on page 7-12 of James Sather's book Understanding the Apple IIe, AKD does not wait for a key to be decoded, so software must "wait until KEYSTROBE is set before interpreting the keyboard ASCII.  Using AKD for this purpose could easily lead to errors."   Prince of Persia suffers from exactly the sort of error the book describes.
  2. The bug shows up when playing the game on the keyboard.  (Joysticks don't have the problem.)  When all keys are released, Prince of Persia will sometimes detect AKD before the key has been decoded, which causes the game to receive the wrong keypress and to perform the wrong action.  Sometimes with fatal results.
  3. The "fix" patches Prince of Persia's KEYS routine so that it stops polling AKD whenever all keys have been released.  When KEYSTROBE indicates that a new key has been pressed and decoded safely, the KEYS routine resumes polling AKD until the player releases all keys again.

It's hard to explain how badly the bug affects game play, so here's a YouTube video that demonstrates the bug...occurring unpredictably several times! https://youtu.be/eZEiDViovb4

That is a huge problem. I wonder why play testing didn't pick that up. Ok, thanks for your explanation and video demo. Very clear what is going on. I will ensure a patch goes into Total Replay to fix this issue.

MacFly's picture

That's a really interesting write-up of how to solve the copy protection issue. Thanks! Good to see a solution which preserves the original code, but does not require special hardware to recreate actual disks.

Someone knitpicking could argue that, while the "original bits of the code are now preserved", the solution still does not preserve the bits of the disk. So there still was a theoretical possibility that the game somehow noticed the patch (maybe due to slightly different timing when verifying the signature). But it's obvious that this approach is on a much better level than patching the code itself (and any risks of remaining issues are much lower). And concerning "preserving the bits": we still have the woz images (which at least work for disk emulators, though they are not easily rewritten to an actual disk).

S.Elliott's picture

roughana wrote:

That is a huge problem. I wonder why play testing didn't pick that up. Ok, thanks for your explanation and video demo. Very clear what is going on. I will ensure a patch goes into Total Replay to fix this issue.

I'm sure play testing did pick up on it.  It was a widely known problem at the time, but we thought it was an unavoidable hardware problem so I built a circuit using a 7402 NOR gate and two 40-pin sockets to gate the AKD signal against the KEYSTROBE signal.

It was only after building this circuit that I realized its logic could be adapted into the software.  My college roommate had an Apple IIc, which didn't have room for a circuit like that, so I made a pre-boot disk (named "PoPABoL") that hooked the boot sector, then hooked RWTS18, then patched the keyboard routine while it loaded...all without disturbing the copy-protection.

If Broderbund Play testing had picked it up, then they should have fixed it before release. Or released another version later that fixed it.

Your dedication is admirable.

Do you have source code for the fix you created?

S.Elliott's picture

roughana wrote:

If Broderbund Play testing had picked it up, then they should have fixed it before release. Or released another version later that fixed it.

Your dedication is admirable.

Do you have source code for the fix you created?

I still contend they discovered it during play testing but couldn't fix it for Apple //e and //c.

  1. Other users told me Broderbund called it a "hardware issue" on their BBS.  (Admittedly hearsay -- I didn't read it myself.)
  2. It only affected Apple //e and //c models with the GI keyboard encoder (or its SMC clone).  It did not affect Apple IIgs, which used a different encoder.
  3. An examination of the Prince of Persia source code suggests (but doesn't quite prove) that someone tried to fix the bug...without success.  The KREAD routine invests a strange effort into testing the keyboard flags, discarding the low-bits from AKD and transferring its flag to the ASCII received from the other port.  It doesn't accomplish anything because both ports contain the same bits at that point in the program, but there's no apparent reason the programmer would have written such bizarre code unless he was attempting to fix a bug that had something to do with reading the wrong key codes.  He had the right idea, but too late -- by the time this test executes, a critical piece of data had already been destroyed.

 

Here's where the bug actually arises, line 164-166 of the KEYS routine.  When the game detects that the player has released all keys, it must not execute those two instructions in the red box because the first instruction "lda $C010" can pre-emptively clear the keyboard strobe if a new key is being debounced at that time, and the second instruction "sta keydown" erases the previous keyboard state required for the test in the KREAD routine.

PS: The bug does not occur if you replace the GI keyboard encoder with the recently-developed JCM-1 Universal Keyboard Encoder.  Its designer said he intentionally debounced the AKD signal because it was a sensible engineering choice.  Too bad GI didn't get that memo!

Thanks for the further detail. So am I inferring correctly that your suggested patch is to NOP out the two instructions in red?

S.Elliott's picture

roughana wrote:

Thanks for the further detail. So am I inferring correctly that your suggested patch is to NOP out the two instructions in red?

Oopsie, no, not quite.

Those two instructions are critical to game play because they enable the character to stop an action, like running or climbing.  NOP'ing them would cause the kid to keep going nonstop even after the player releases the control keys...like Lode Runner does.  (When playing on keyboard, the Lode Runner man just keeps endlessly running or climbing until you press some other key.)

Due to its typewriter-style keyboard IO, most Apple games sufferred from "unstoppable, runaway action" if they supported keyboard at all.  That's why so many Apple games required a joystick, while Commodore and Atari games typically supported your choice of joystick or keyboard.

The ideal fix would replace those two instructions with a test of the previously-saved AKD state, and a branch to skip the LDA $C010 in the case when the previous AKD indicated all keys had been released, plus a test LDA $C000 to resume normal activity after the next KEYSTROBE.  Unfortunately this "ideal" solution would have required inserting more instructions into the source code and then re-assembling the program, but I had to devise a patch/kludge that could fit into the program without inserting any more bytes.

I confess, I didn't quite get it right.  But it warrants another blogpost, with code and clearer pictures.