don't click here

How to fix Pattern Load Cues queue shifting bug

Discussion in 'Engineering & Reverse Engineering' started by vladikcomper, Mar 24, 2012.

  1. vladikcomper

    vladikcomper

    Tech Member
    203
    125
    43
    Sonic Warped
    Pattern Load Cues, or simply PLC, is a system for loading Nemesis-compressed art during game execution. PLC is used to load art at the beginning of the level, as well as at certain points of the game (like loading singpost or boss art at the end of level). This system is rather complicated, in Sonic 1 and 2 it has few bugs, one of the was described in "How to fix PLC race condition" guide.

    Recently I have figured out another bug regarding PLC queue shifting mechanism, which I considered to be simple queue overflow earlier.

    Introduction

    PLC system has a buffer, better to be called 'queue', taking $60 bytes in memory space. Queue is filled by the art load cues, each takes 6 bytes, first 4 being source ROM offset, and last two being destination VRAM offset. The cues are executed one by one until all the queue gets empty. To see what these cues are, check out _inc\Pattern Load Cues.asm file in Sonic 1's disassembly.

    PLC queue can hold up to $10 (16) cues. Cues execution starts from the beginning of the queue storage. When the cue is done, all the cues are getting moved: the first cues is replaced by the second, the second - by the third and so on. In other words, the whole queue gets shifted. However, due to program error, when the buffer is filled up with all $10 cues, the last one doesn't clear on shifting, which leads to overcopying it, so all the queue eventually overfills with the same cue, so the game gets stuck decompressing the same art over and over again.

    This bug is easy to meet in Sonic 1: once you have added two more cues into PLC_GHZ, the queue will take $10 cues, and the game will stuck at the Title Cards, though $10 cues is an is acceptable number.

    Explanation of the bug

    Let's look at queue shifting code to understand the reason of the bug. As an example, I will show you the code from Sonic 2 Xenowhirl's Disassembly, as it's more annotated in this place than Sonic 1's. Remember, the this code is the same in both Sonic 1 and Sonic 2.

    Code (ASM):
    1.  
    2. ; ===========================================================================
    3. ; pop one request off the buffer so that the next one can be filled
    4.  
    5. ; loc_177A:
    6. ProcessDPLC_Pop:
    7.     lea (Plc_Buffer).w,a0
    8.     moveq   #$15,d0
    9.  
    10. -   move.l  6(a0),(a0)+
    11.     dbf d0,-
    12.     rts
    Firstly, this code transfers $16*4 = $58 bytes, which doesn't even match an integral number of cues. It should've done $5A bytes, which is $F cues. Transferring $58 bytes means it won't transfer last 2 bytes of the last ($10-th) cue which are VRAM offset. So the VRAM offset will remain from the previous cue.

    Secondly, the last cue in the queue is not cleared, which will cause overcopying it. Eventually, all the queue will be filled with the last cue stored and the game will stuck endlessly processing the same cue.

    Fixing the bug

    Sonic 1 (Hivebrain's Disassembly)

    Go to 'loc_16DC' and replace the whole code with this:

    Code (ASM):
    1. loc_16DC:           ; XREF: sub_165E
    2.         lea ($FFFFF680).w,a0
    3.         lea 6(a0),a1
    4.         moveq   #$E,d0      ; do $F cues
    5.  
    6. loc_16E2:               ; XREF: sub_165E
    7.         move.l  (a1)+,(a0)+
    8.         move.w  (a1)+,(a0)+
    9.         dbf d0,loc_16E2
    10.        
    11.         moveq   #0,d0
    12.         move.l  d0,(a0)+    ; clear the last cue to avoid overcopying it
    13.         move.w  d0,(a0)+    ;
    14.         rts
    15. ; End of function sub_165E
    Sonic 2 (Xenowhirl's Disassembly)

    Go to 'ProcessDPLC_Pop' and replace the whole code with this:

    Code (ASM):
    1. ProcessDPLC_Pop:
    2.     lea (Plc_Buffer).w,a0
    3.     lea 6(a0),a1
    4.     moveq   #$E,d0      ; do $F cues
    5.  
    6. -   move.l  (a1)+,(a0)+
    7.     move.w  (a1)+,(a0)+
    8.     dbf d0,-
    9.  
    10.     moveq   #0,d0
    11.     move.l  d0,(a0)+    ; clear the last cue to avoid overcopying it
    12.     move.w  d0,(a0)+    ;
    13.     rts
    This new version of code will shift the queue correctly and protect the last cue from overcopying. The bug is gone! But don't forget about queue overflow, this will fix bug when queue is filled with $10 cues, but $11 and more cues are out of queue size!

    EDIT: GRAMMAR
     
  2. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,161
    65
    28
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    Looking over, it is also a bug in S3&K; the Hg disassembly even mentions these problems.

    Any way, nice work fixing it.
     
  3. Aerosol

    Aerosol

    Not here. Moderator
    10,970
    328
    63
    Not where I want to be.
    Sonic (?): Coming summer of 2055...?
    What does this actually affect during normal gameplay?
     
  4. vladikcomper

    vladikcomper

    Tech Member
    203
    125
    43
    Sonic Warped
    Nothing, this bug only appears when PLC buffer is full.
     
  5. Aerosol

    Aerosol

    Not here. Moderator
    10,970
    328
    63
    Not where I want to be.
    Sonic (?): Coming summer of 2055...?
    Oh alright then. Moving on then!

    Thanks for the fix.
     
  6. LOst

    LOst

    Tech Member
    4,891
    6
    18
    Is there any bug fix(es) for Sonic 1's Roller enemy rolling frame art corruption, and SBZ2 Eggman demo's jumping frame art corruption?

    Well, I am of course asking if your bug fixes relate to these issues?
     
  7. vladikcomper

    vladikcomper

    Tech Member
    203
    125
    43
    Sonic Warped
    Recently I've checked why Yardin's frame is corrupted. It happened to be simple art conflict: Caterkiller is somehow presented in SYZ's PLC list and its art simply overwrites some of Yardin tiles. If you delete Catterkiller from PLC, the bug will go.

    I'm not sure about Eggman in SBZ2, honestly, I haven't noticed it myself. I doubt it can be the bug I described. It can happen when PLC queue is full (contains $10 entries), which will barely happen at the end of SBZ2. Maybe it's the same issue as with Yardin.