Monday, July 17, 2017

Maybe you'll stop feeling so bad

When an object is no longer useful, it can delete itself from object RAM by calling the Delete_Current_Sprite function. This function zeroes out all 74 bytes of the object's SST, preparing the slot for the next object that'll take its place.
Delete_Current_Sprite:
    movea.l a0,a1
    moveq   #$11,d0
    moveq   #0,d1

loc_1ABBC:
    move.l  d1,(a1)+
    dbf     d0,loc_1ABBC
    move.w  d1,(a1)+
    rts
Note that the object processor only considers the code pointer when deciding whether a slot is empty or not; wiping the rest of the SST is the kind of convenience one might expect from a modern, memory-managed programming language, rather than assembly code written in the early 90s.

Objects which have scrolled far away are discarded through a similar process. Unlike in a managed environment, there is no garbage-collecting entity which erases objects when they are no longer active. Instead, each object is responsible for freeing its own memory by calling a function like Sprite_OnScreen_Test below:
Sprite_OnScreen_Test:
    move.w  $10(a0),d0
    andi.w  #$FF80,d0
    sub.w   (Camera_X_pos_coarse_back).w,d0
    cmpi.w  #$280,d0
    bhi.w   loc_1B5A0
    bra.w   Draw_Sprite
; ---------------------------------------------------------------------------

loc_1B5A0:
    move.w  respawn_addr(a0),d0
    beq.s   loc_1B5AC
    movea.w d0,a2
    bclr    #7,(a2)

loc_1B5AC:
    bra.w   Delete_Current_Sprite
Much like the code which loads objects from a level's object layout, what this function does is check whether the object is still within the same vertical slice of the level as the player (or more specifically, the camera), and if not, then it jumps to Delete_Current_Sprite, which in turn clears the object's SST.

An extra detail about this function is that it loads the value of respawn_addr(a0), offset $48 in the object's SST, into an address register and clears bit 7 of that address before deleting the object. What's that all about?

Separate from object RAM, the game maintains an "object respawn table" which consists of one byte for each object in the level's object layout. Every time an object is loaded from the layout, the object manager sets bit 7 of its entry in the respawn table, and then writes the entry's address to offset $48 in the object's SST.

It is the object's responsibility to once again clear bit 7 before deleting itself. If the object manager loads an object from the layout and bit 7 of its respawn entry is already set, then it will be considered to have been permanently deleted and will not spawn again. This is why Sprite_OnScreen_Test clears the bit and Delete_Current_Sprite does not: the former temporarily removes the object when it's too far away, whereas the latter is called at the end of an object's life, such as when an enemy is destroyed.

Finally, the remaining bits in a respawn table entry can be used however the object sees fit. For instance, monitors use bit 0 as a makeshift respawn flag, while keeping bit 7 clear at all times. This causes them to always respawn, and then their init code makes them switch over to a broken appearance if bit 0 is set.

No comments:

Post a Comment