As we know, the game can only communicate with the PIC through the upper twelve bits of the CPU address bus. The game does this by calling wait loop procedures that are spaced 16 bytes apart. In the case of 3D Block, there is one wait loop procedure each at E180, E190... E1F0. The PIC sees address E18x for several cycles and concludes that its function 0 is to be executed; it sees address E1Fx and concludes that its function 7 is to be executed --- you get the idea. Both the functions and the addresses that trigger them are game-specific; 3D Block's are incompatible to Block Force's. 3D Block's PIC functions are:
- Function 0: Main game screen split --- raise one IRQ about 12,400 M2 cycles later.
- Function 1: Clear internal 32-bit register and prepare for further data --- raise one IRQ immediately to acknowledge.
- Function 2: Start raising IRQs timed such that a counter in the main game, increased on every IRQ being generated, reaches 247, no lower, no higher. (I don't know the exact timing values, only the result that must be reached in the main game.)
- Function 3: Send a 1 bit to the PIC. Raise one IRQ immediately to acknowledge.
- Function 4: Level introduction screen split --- raise several IRQs about 2,175-or-so cycles apart. I don't know the exact duration or how many; I'm basing this on the fact that the pattern table is switched after the IRQ handler has been called six times, and the pattern switch still needs to be about mid-screen for the playfield to not look wong.
- Function 5: Send a 0 bit to the PIC. Raise one IRQ immediately to acknowledge.
- Function 6: Receive a result byte from the PIC. The number of IRQs generated resembles the result byte value and can be 0 if no IRQ is generated.
- Function 7: Reset the IRQ generation counter for subfunction 2.
Functions 1, 3 and 5 are used in-game whenever the wire-frame pieces are being redrawn. The main game packs most of its game state into four bytes and sends them to the PIC. After receiving 32 bits, the PIC apparently starts processing them.
Function 6 is called four times to receive four result bytes. Basically, based on the current game state that was sent before, the PIC spits out the next position of the current piece. That is why not emulating the PIC causes the pieces to never move. Mercifully, the proper game logic can be deduced from the later 1990 release of the game.
Functions 0 and 4 are only cosmetic and cause pattern table switches and X position updates in the case of 4.
Block Force has sixteen functions from FE00 to FEF0, again spaced in 16-byte distances:
- Function 12: status bar during game play. Raise one IRQ after about 25,000 M2 cycles.
- Function 15: something at bootup. I just raise one IRQ after 6,000 M2 cycles.
- Functions 9 and 11: unknown, but raising one IRQ immediately minimizes the pauses.
This is all preliminary, because I have not tested the games at length. Block Force is supposed to display an initial high score of 006502, which it does not with the above description. But it's playable with the status bar.
Try a current build of NintendulatorNRS if you want, and be sure to set your unmodified ROM images to NES 2.0 Mapper 355. Credit to krzysiobal for pointing out the 16-bytes-spaced wait loops that signal a function request to the PIC, and credit to me for determining what the subfunctions that have been identified do.
This does not remove the need to have both games' PIC prgoram to be dumped.