Sonic 1 - Level tiles bug

Discussion in 'Discussion and Q&A Archive' started by Psycho RFG, Nov 12, 2013.

Thread Status:
Not open for further replies.
  1. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    Hi to all!!!

    Well, I don't know where this come from because it doesn't happen always, but sometimes I can see that square where the level tiles are not drawn... I only noticed it in water levels...

    Any idea of what is causing it?

    Thanks!
     
  2. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    There's a race condition involving LoadTilesFromStart routine, which causes this graphical glitch to happen. This one of absolutely rare bugs hidden in the game's engine -- chances you spot it are 1 in one hundred, since an entire set of circumstances should be met simultaneously in order to reproduce this bug.

    Level loading process itself is quite complex, as Sonic Team tried hard to disguise any loading times in the game. A prefect example of such disguise is Sonic Team Presents screen, during which game is actually frozen, too busy decompressing data and preparing title screen to be displayed. This is also a reason the whole Pattern Load Cues system existed. Its concept, while pretty basic, still can be treated as what we call multi-threading today -- the main loop processes game logic, while the other "thread", tied to VBlank, decompresses Nemesis art simultaneously. Their effort was quite impressive for the time -- many other games that used slow Nemesis compression to store the art, could freeze even mid-game to decompress boss art, most notably Moonwalker and Golden Axe series.

    Sonic Team didn't wanted the game to freeze even for a split second during title cards sequence, when most of level's data is being loaded. They made use of their own Pattern load cues to process title cards and decompress level art at the same time, they also enabled interrupts for the whole loading sequence to keep music playing and PLC system working, which caused a set of race conditions leading to rare graphical glitches.

    The level map is being drawn shortly after PLC system finished decompressing all the level art in title cards loop, as seen in the following code:


    Level_TtlCard:
    move.b #$C,($FFFFF62A).w
    bsr.w DelayProgram
    jsr ObjectsLoad
    jsr BuildSprites
    bsr.w RunPLC_RAM
    move.w ($FFFFD108).w,d0
    cmp.w ($FFFFD130).w,d0 ; has title card sequence finished?
    bne.s Level_TtlCard ; if not, branch
    tst.l ($FFFFF680).w ; are there any items in the pattern load cue?
    bne.s Level_TtlCard ; if yes, branch
    jsr Hud_Base

    loc_3946:
    moveq #3,d0
    bsr.w PalLoad1 ; load Sonic's pallet line
    bsr.w LevelSizeLoad
    bsr.w DeformBgLayer
    bset #2,($FFFFF754).w
    bsr.w MainLoadBlockLoad ; load block mappings and pallets
    bsr.w LoadTilesFromStart
    <...>

    LoadTilesFromStart is the one routine responsible for rendering level's background and foreground in the place you start from. The game has already run a number of "heavy" routines within this display frame, including ObjectsLoad, BuildSprites, RunPLC_RAM, DeformBgLayer and MainLoadBlockLoad. Obviously, the M68K processor isn't able to process so many complex routine during one display frame, their execution will prolong for the next one or even further. This where race condition is coming from. When the current display frame is finished rendering, a vertical interrupt occurs, which updates music and does a few VDP transfers to update palette and screen scrolling. If the interrupt occurs during LoadTilesFromStart execution, the graphical glitch in the question may occur.

    The basic concept of why this happens is simple. LoadTilesFromStart renders the level map directly into VRAM by means of 16x16 blocks. To render each row (2 tiles) of a block it does the following:

    1. Sends a request to write at the destination address of VRAM to the VDP control port.

    2. Sends tiles via VDP data port.

    But what if an interrupt occurs between steps 1 and 2? During it, different VRAM addresses are requested and accessed, and those changes cannot be reverted after interrupt is finished. Therefore, tiles sent at step 2 will go to a different VRAM location and won't appear where they are supposed to be.

    To fix this bug, try disabling interrupts right before LoadTilesFromStart is executed. Re-enable them when this routine is finished, like so:


    move #$2700,sr
    bsr.w LoadTilesFromStart
    move #$2300,sr

    Disabling interrupts for long may cause music to freeze, but this won't be noticeable if it only lasts 1 frame or 2.
     
  3. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    There is no other way? That fix is not a good option because the "freeze music" is very noticeable, specially in the water levels and I even can see a small flash (in water levels again...).
     
  4. nineko

    nineko I am the Holy Cat Member

    Joined:
    Mar 24, 2008
    Messages:
    1,902
    Location:
    italy
    Oh.


    5 years, 5 months, and 11 days later, I finally know what was going on here.


    I still don't know why it fixed itself a few builds later, though.
     
  5. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    The first time I saw this bug was in Lz1 too, and it was always there and even in real hardware... I changed the begining of the level with other design and it didn't happened anymore... Then, the same problem in SBz3, and same solution, I restore the begining of the level and it didn't happened again... but the other day I could see it again, but it doesn't happen always (I noticed it when die, but not always), so I have no idea of how to make the bug to occur always...
     
  6. SuperEgg

    SuperEgg I'm a guy that knows that you know that I know Member

    Joined:
    Oct 17, 2009
    Messages:
    Location:
    THE BEST GOD DAMN STATE OF TEXAS
    I'd suggest you move on to using a z80 driver as opposed to the m68k driver. That'll definitely help you music wise.
     
  7. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    415
    If the music issue is too noticeable, try this instead:

    Code:
            move.b  #$C,($FFFFF62A).w
            bsr.w   DelayProgram
    	bsr.w	LoadTilesFromStart
    
     
  8. nineko

    nineko I am the Holy Cat Member

    Joined:
    Mar 24, 2008
    Messages:
    1,902
    Location:
    italy
    I agree with SuperEgg.
     
  9. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    Yeah, that works much better, thanks.
     
Thread Status:
Not open for further replies.