Monday, September 25, 2006

PIF fixes

It's about time I posted an update with news of some of the things I've been working on! Annoyingly the '.' key on my keyboard has decided to stop working so apologies in advance for any dodgy punctuation :)

I've been having a look at fixing a couple of issues related to the way that Daedalus handles the N64 PIF (peripheral interface) emulation. The PIF is responsible for a number of different functions, including:


  • Reading the controller status

  • Reading/writing to the eeprom

  • Reading/writing to the controller mempak



Access to the PIF is controlled through a 64 byte region of memory. Writing to this area triggers the PIF to interpret the block of data as a sequence of commands (such as those listed above), and the CPU can read back from this region to obtain the results.

Correctly handling the way the block is formatted is key to emuating the PIF accurately. Over the years I've rewritten this code several times as I've fixed bugs, each time fixing certain incorrect assumptions made during the previous version. Usually these assumptions were made on incredibly useful, but incomplete information (such as early docs from LaC (*waves*) ) which described how to use the PIF rather than how to emulate it. Unfortunately there was lots of gaps in this information and so I'd keep finding roms that used the PIF is various ways that I'd not taken into account.

A few years ago I stumbled across something rather amazing - a document which described the PIF hardware in great detail. Not only was it very thorough - including details on all the various formatting commands I'd spent hours reverse engineering - it also included an insane amount of detail on things such as the chip timings and the even method by which the PIF is used as a 'security system' to lock out pirated roms.

What was this document? Did I find it tucked away on some obscure N64 hacking site? No - it was US Patent 6,394,905, filed by Nintendo in September 2000. Genius!

There are actually a whole series of N64 patents (see 5,426,762 and 4,799,635 for a couple of other similar examples.) It's not all good news though because unfortunately they're all written in a horribly convulted form of Legalese:

There are six JoyChannels available in the present exemplary embodiment. Each Channel's transmit data and receive data byte sizes are all independently assignable by setting size parameters. In the exemplary embodiment, all six channels size parameter setups are required, whether they are used or not.


Ouch.

This is the statement I spotted a couple of weeks ago that explained why one of the previous assumptions I'd made wasn't working in all cases:

If the 64 bit CPU 100 wants to change JoyChannel's Tx/RxData assignment, a 32 bit format flag is used, where a certain bit(s) specify the desired format. For example, when Wr64B or Wr4B is issued when this flag is "1", PIF executes each JoyChannel's Tx/RxData assignment based on each channel's Tx/Rx Size. In other words, unless this flag is set to "1" with Wr64B or Wr4B, Tx/RxData area assignment does not change. After Tx/RxData assignment, this flag is reset to "0" automatically.


After painfully wading through the text, it's basically saying that if you write a '1' to the last byte of the PIF block, you need to reformat all the channels. Otherwise you just assume the same formatting as the previous command.

The bug that I've since fixed was actually caused by two problems. Firstly, I was reformatting the channels regardless of how this last byte was set. I'd assumed that it was always set to '1' by the CPU to indicate that a command was ready, and set to '0' by the PIF when it was finished executing. In a few roms I was seeing '0' being written, but I didn't know why. Quite often when I saw them do this they'd hang shortly aftwerwards.

The other problem that contributed to the bug was that I was accidentally overwritting the 'Tx/RxData assignment' area with 0 as I was processing the PIF block (actually, I was clearing the top 4 bits of each byte, rather than just the top 2). This meant that when I reformatted the Tx/RxData assigments the areas were smaller than expected, so the PIF handling code would abort and return with failure.

These two problems combined to cause certain roms to get confused and hang when trying to access the mempak. The total fix was a few dozen lines of code to separate out the PIF formatting from the command execution, and a single-line fix for the second issue. All the roms I found exhibiting this issue now make further progress (hooray!) There may well be other issues that have been resolved with this fix too :)

It's bedtime for me now (well, maybe a little Dead Rising on the 360 first :) I'll try and get back into the habit of updating a bit more often from this week on. Sorry if I've not replied to your email - it's not personal, it's just me being rubbish.

-StrmnNrmn

125 comments: