Wednesday, November 1, 2017

Dynamic pattern load cues, part 2

Welcome back. Last time, we saw how the Tulipon enemy's second frame of animation shares the same stalk graphics as the first frame, but employs unique bulb graphics by bumping up the base tile on the bulb's sprite pieces beyond the last tile of the preceding frame.


Now, let's take a look at the Rhinobot enemy, whose sprite mappings can be found in the Map - RhinoBot.asm file. The first mapping frame is made up of the following three sprite pieces:


Here's the mapping definition. We'll dissect it as we did before, by looking at the shape in the second byte and the base tile in the fourth byte:
Frame_3615B8:   dc.w  3
                dc.b    0,   1,   0,   0, $FF, $E8
                dc.b  $F0,  $F,   0,   2, $FF, $F0
                dc.b  $F8,   2,   0, $12,   0, $10
From left to right, the first piece has a shape value of 1 and a base tile of 0. The second piece uses shape $F and base tile 2, and the third piece uses shape 2 and base tile $12. If you add everything together, you'll find that this frame uses every tile from 0 through $14.

Next, let's look at the second frame, which too is divided into three pieces:


Unlike Tulipon, all of Rhinobot's animation frames are completely distinct, and share no tiles between one another. You would then assume that the base tile values for the second frame would start at $15, since tiles 0 through $14 are used up by the first frame. Checking the mapping definition however, proves that this is not the case:
Frame_3615CC:   dc.w  3
                dc.b    0,   1,   0,   0, $FF, $EC
                dc.b  $F0,   4,   0,   2, $FF, $F4
                dc.b  $F8,  $E,   0,   4, $FF, $F4
Once again, just like the first frame, the base tile values start at 0 and build up contiguously, with the whole frame using tiles 0 through $F. The remaining frames are much of the same. Something's not right; this is equivalent to asking for all frames to be rendered using tiles from the first frame. In fact, it's exactly what sprite editors such as SonMapEd (which I use extensively) give you when you naïvely feed it the enemy's art and mapping files:


So what's going on? The mappings aren't lying, the tiles for the second frame really are at the same VRAM address as the tiles for the first frame. But how? The only way that might be possible is if at some point before the second frame is displayed, the graphics for the first frame get replaced by the graphics for the second frame, and vice-versa.

Which is exactly what happens.


This effect is achieved by a special kind of PLC, called DPLC or Dynamic Pattern Load Cue. Like regular PLCs, DPLCs specify a set of patterns (tiles) that are loaded (to a specified VRAM address) on cue. They are similarly composed by a pair of pointers: a pointer to the tile data in ROM, and this time, a pointer to a DPLC script, also stored in ROM:
DPLCPtr_87132:  dc.l ArtUnc_AIZRhino
                dc.l DPLC_RhinoBot
Objects can perform a DPLC request by calling the (aptly named) Perform_DPLC function. This function compares the object's mapping frame attribute to the value it held in the previous execution, which the function stores at offset $3A of the object's SST, similarly to the previous animation attribute used by the animation system.
Obj_RhinoBot:
    ...
    lea     DPLCPtr_87132(pc),a2
    jsr     Perform_DPLC(pc)
    jmp     Sprite_CheckDeleteTouchSlotted(pc)
When these two values differ, it means the object just switched over to a different mapping frame, so the function looks up the DPLC script entry for the new mapping frame, and adds the necessary patterns to the DMA queue.

To illustrate this, here is the script entry for the Rhinobot enemy's second frame, found in the DPLC - RhinoBot.asm file:
Frame_361586:   dc.w  2
                dc.w  $151
                dc.w  $171
                dc.w  $19B
There are three requests, one for each sprite piece. The low nybble indicates the number of tiles to load minus one; the remainder specifies the base tile from which to start reading. So for the first piece, that's two tiles being loaded starting from tile $15. The second piece resumes from tile $17 and loads two more tiles, while the third piece continues from tile $19 and loads $C (12) tiles. Check this with the mappings dissection we did above, everything should add up.

All this nonsense about tile counts and base tiles boils down to the use of uncompressed art, which is a requirement for using DMA, which itself is a requirement because we must somehow load all the new tiles into VRAM after every object has been processed, but before the VDP outputs the next image to the TV screen. We have less than one 60th of a second to work with here, there's absolutely no time to be spent running decompression algorithms!

4 comments:

  1. Do you know if SonMapEd works on Windows 10?

    ReplyDelete
    Replies
    1. Wouldn't be adding pictures to these posts if it didn't

      Delete
    2. Yeah, I really do not want to worry about software compatibility especially when Kega Fusion seems to suffer on Windows 10.

      Delete
    3. It works on the computer I use, which has the latest version of Windows.

      Delete