Puyo Puyo Tsu/Falling Pair Spawning Process
Game logic overview
The game function which handles most of the sequence of actions throughout a battle looks like this:
This routine is called right after a player has placed a new pair on the board, and will handle most of the game logic before returning control to him.
The following paragraph simplifies things a bit and is not 100% accurate.
The logic begins by generating the falling pair (yellow), which may fail if the 3rd column is filled: at least one player has lost the battle, and the game branches to a code path that ends quickly (light green). If the falling pair was successfully spawned, the main logic occurs and makes successive calls to many subroutines which perform the various computations to handle resolving chains, scoring, etc. At the end, a video frame update occurs.
Pair spawning
Function/routine names have been arbitrarily chosen.
Pair generation picks two successive puyos from the randomized pair pools to place them in the "hands" of the player. The game calls function gen_cur_fal_pair() which will either return with a carry flag set in the status register (SR) or not. If the carry is set, the game could not generate a falling pair because the 3rd column was obstructed.
Here's the gen_cur_fal_pair() routine:
The routine first shifts the next 3 pairs that are displayed (well, only 2 are displayed but 3 are actually in the corresponding buffer). To do so, it calls the distrib_shift() routine which will put the very next pair in the d0 and d1 registers, while shifting the pairs in memory. distrib_shift() also picks another pair from the randomized pool by calling distrib_next_pair().
Remember, the 3 upcoming pairs for each player are stored at 0xFF85A0 and 0xFF8DA0. Those are the places where the pairs are shifted from one position to the next, before being taken away from the buffer to be put in d0 and d1, then finally in the falling pair buffer.
Here's distrib_shift():
And here's distrib_next_pair(), displayed in both graph and text views to include a statically predefined array in ROM:
The "distrib_offset" array at 0x004C9C stores offsets to select the relevant randomized pair pool (0xFFAD00 + 0x100 = 0xFFAE00, pair pool for difficulty #3, while #4 and #5 will result in a 0x200 offset).
Now back to the gen_cur_fal_pair() routine. After shifting the pairs, the function get_board_and_offset() returns the pointer to the current player's board in RAM (0xFF8000 or 0xFF8800). This allows it to check if byte 0xFF801C or 0xFF881C is non-zero, thus if the upper cell of the 3rd column is obstructed.
Here's the get_board_and_offset() function, which returns the address in a2:
(the offset the function computes in d0 is ignored in our case, but will be of importance later)
If the cell is unobstructed, the game will allocate two structures in memory that will each describe a single puyo of the currently falling pair. Without going into details (they will be included in the very next post), those structures hold the current position of the falling puyo on the board, its color and the current vertical offset within a cell (that's what makes the puyos gradually fall through a cell). The color is taken from the d0 and d1 registers that were previously filled by the distrib_shift() routine, from the upcoming pairs buffer.
There is a "master" puyo, which is the lowest one when the pair appears, around which the other one revolves. This is the only puyo which gets its absolute position stored. The other puyo is a "slave" puyo, the position of which is relative to the master one.
The slave puyo's position constraints player movements, and when the master puyo's y-axis position tries to get beyond the bottom line 0x0D (after being corrected by the relative position of the slave puyo), the pair is placed on the board by the place_puyo() routine (called from somewhere else):
The compute_offset routine calculates the memory address at which the puyos will be stored: from the beginning of the board's buffer, it's an offset that depends on the puyos coordinates (roughly 2x+num_rows*y):
load_gfx() takes the puyo's sprite ID and position, and loads it to the VDP, making it appear at the next video frame update.