Difference between revisions of "Fix a race condition with Pattern Load Cues"
From Sonic Retro
(Added Sonic 2, changed section titles) |
m (Text replacement - "\[\[Category:SCHG How-tos.*" to "") |
||
(10 intermediate revisions by 3 users not shown) | |||
Line 3: | Line 3: | ||
In ''[[Sonic the Hedgehog (16-bit)|Sonic 1]]'', there is a race condition with Pattern Load Cues that can cause the game to crash. This situation can happen if the player rolls and pans the camera down at the end of Labyrinth Zone acts 1 and 2 ([http://www.youtube.com/watch?v=C5Hf2rimXGY#t=34 video]). | In ''[[Sonic the Hedgehog (16-bit)|Sonic 1]]'', there is a race condition with Pattern Load Cues that can cause the game to crash. This situation can happen if the player rolls and pans the camera down at the end of Labyrinth Zone acts 1 and 2 ([http://www.youtube.com/watch?v=C5Hf2rimXGY#t=34 video]). | ||
− | This bug is also present in [[Sonic the Hedgehog 2 (16-bit)|Sonic 2]], but is harder to reproduce. In [[Sonic the Hedgehog 3|Sonic 3]] and [[Sonic & Knuckles]], the bug is fixed as below. | + | This bug is also present in ''[[Sonic the Hedgehog 2 (16-bit)|Sonic 2]]'', but is harder to reproduce. In ''[[Sonic the Hedgehog 3|Sonic 3]]'' and ''[[Sonic & Knuckles]]'', the bug is fixed as below. |
==How to fix the bug== | ==How to fix the bug== | ||
− | ===Sonic 1, SVN disassembly=== | + | ===Sonic 1, SVN/GitHub disassembly=== |
This is the '''RunPLC''' routine: | This is the '''RunPLC''' routine: | ||
− | <asm>; --------------------------------------------------------------------------- | + | <syntaxhighlight lang="asm"> |
+ | ; --------------------------------------------------------------------------- | ||
; Subroutine to use graphics listed in a pattern load cue | ; Subroutine to use graphics listed in a pattern load cue | ||
; --------------------------------------------------------------------------- | ; --------------------------------------------------------------------------- | ||
Line 46: | Line 47: | ||
Rplc_Exit: | Rplc_Exit: | ||
rts | rts | ||
− | ; End of function RunPLC</ | + | ; End of function RunPLC</syntaxhighlight> |
Move this line: | Move this line: | ||
− | <asm> move.w d2,(f_plc_execute).w</ | + | <syntaxhighlight lang="asm"> move.w d2,(f_plc_execute).w</syntaxhighlight> |
after this line: | after this line: | ||
− | <asm> move.l d6,($FFFFF6F4).w</ | + | <syntaxhighlight lang="asm"> move.l d6,($FFFFF6F4).w</syntaxhighlight> |
===Sonic 1, Hivebrain's 2005 disassembly=== | ===Sonic 1, Hivebrain's 2005 disassembly=== | ||
− | + | ||
− | <asm> | + | This is the '''RunPLC_RAM:''' label: |
− | + | <syntaxhighlight lang="asm"> | |
− | <asm> move.w d2,($FFFFF6F8).w</asm> | + | ; --------------------------------------------------------------------------- |
− | + | ; Subroutine to use graphics listed in a pattern load cue | |
+ | ; --------------------------------------------------------------------------- | ||
+ | |||
+ | ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| | ||
+ | |||
+ | |||
+ | RunPLC_RAM: ; XREF: Pal_FadeTo | ||
+ | tst.l ($FFFFF680).w | ||
+ | beq.s locret_1640 | ||
+ | tst.w ($FFFFF6F8).w | ||
+ | bne.s locret_1640 | ||
+ | movea.l ($FFFFF680).w,a0 | ||
+ | lea (loc_1502).l,a3 | ||
+ | lea ($FFFFAA00).w,a1 | ||
+ | move.w (a0)+,d2 | ||
+ | bpl.s loc_160E | ||
+ | adda.w #$A,a3 | ||
+ | |||
+ | loc_160E: | ||
+ | andi.w #$7FFF,d2 | ||
+ | move.w d2,($FFFFF6F8).w | ||
+ | bsr.w NemDec4 | ||
+ | move.b (a0)+,d5 | ||
+ | asl.w #8,d5 | ||
+ | move.b (a0)+,d5 | ||
+ | moveq #$10,d6 | ||
+ | moveq #0,d0 | ||
+ | move.l a0,($FFFFF680).w | ||
+ | move.l a3,($FFFFF6E0).w | ||
+ | move.l d0,($FFFFF6E4).w | ||
+ | move.l d0,($FFFFF6E8).w | ||
+ | move.l d0,($FFFFF6EC).w | ||
+ | move.l d5,($FFFFF6F0).w | ||
+ | move.l d6,($FFFFF6F4).w | ||
+ | |||
+ | locret_1640: | ||
+ | rts | ||
+ | ; End of function RunPLC_RAM | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Move this line: | ||
+ | <syntaxhighlight lang="asm"> move.w d2,($FFFFF6F8).w</syntaxhighlight> | ||
+ | |||
+ | after this line: | ||
+ | <syntaxhighlight lang="asm"> move.l d6,($FFFFF6F4).w</syntaxhighlight> | ||
===Sonic 2, SVN disassembly=== | ===Sonic 2, SVN disassembly=== | ||
This is the '''RunPLC_RAM''': | This is the '''RunPLC_RAM''': | ||
− | <asm>; --------------------------------------------------------------------------- | + | <syntaxhighlight lang="asm">; --------------------------------------------------------------------------- |
; Subroutine to use graphics listed in a pattern load cue | ; Subroutine to use graphics listed in a pattern load cue | ||
; --------------------------------------------------------------------------- | ; --------------------------------------------------------------------------- | ||
Line 98: | Line 143: | ||
+ | + | ||
rts | rts | ||
− | ; End of function RunPLC_RAM</ | + | ; End of function RunPLC_RAM</syntaxhighlight> |
Move this line: | Move this line: | ||
− | <asm> move.w d2,(Plc_Buffer_Reg18).w</ | + | <syntaxhighlight lang="asm"> move.w d2,(Plc_Buffer_Reg18).w</syntaxhighlight> |
after this line: | after this line: | ||
− | <asm> move.l d6,(Plc_Buffer_Reg14).w</ | + | <syntaxhighlight lang="asm"> move.l d6,(Plc_Buffer_Reg14).w</syntaxhighlight> |
===Sonic 2, Xenowhirl's 2007 disassembly=== | ===Sonic 2, Xenowhirl's 2007 disassembly=== | ||
The line: | The line: | ||
− | <asm> move.w d2,(Plc_Buffer_Reg18).w</ | + | <syntaxhighlight lang="asm"> move.w d2,(Plc_Buffer_Reg18).w</syntaxhighlight> |
appears as: | appears as: | ||
− | <asm> move.w d2,($FFFFF6F8).w</ | + | <syntaxhighlight lang="asm"> move.w d2,($FFFFF6F8).w</syntaxhighlight> |
and the line: | and the line: | ||
− | <asm> move.l d6,(Plc_Buffer_Reg14).w</ | + | <syntaxhighlight lang="asm"> move.l d6,(Plc_Buffer_Reg14).w</syntaxhighlight> |
appears as: | appears as: | ||
− | <asm> move.l d6,($FFFFF6F4).w</ | + | <syntaxhighlight lang="asm"> move.l d6,($FFFFF6F4).w</syntaxhighlight> |
Follow the steps for the SVN disassembly above. | Follow the steps for the SVN disassembly above. | ||
Line 119: | Line 164: | ||
To fix the problem, f_plc_execute ($FFF6F8) must be set after all the other values are initialized. | To fix the problem, f_plc_execute ($FFF6F8) must be set after all the other values are initialized. | ||
+ | {{S1Howtos}} | ||
+ | |||
+ | |||
+ | {{S2Howtos}} | ||
+ | |||
+ | |{{PAGENAME}}]] |
Latest revision as of 11:11, 25 August 2018
(Original guide by FraGag)
In Sonic 1, there is a race condition with Pattern Load Cues that can cause the game to crash. This situation can happen if the player rolls and pans the camera down at the end of Labyrinth Zone acts 1 and 2 (video).
This bug is also present in Sonic 2, but is harder to reproduce. In Sonic 3 and Sonic & Knuckles, the bug is fixed as below.
Contents
How to fix the bug
Sonic 1, SVN/GitHub disassembly
This is the RunPLC routine:
; ---------------------------------------------------------------------------
; Subroutine to use graphics listed in a pattern load cue
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
RunPLC: ; XREF: GM_Level; et al
tst.l (v_plc_buffer).w
beq.s Rplc_Exit
tst.w (f_plc_execute).w
bne.s Rplc_Exit
movea.l (v_plc_buffer).w,a0
lea (loc_1502).l,a3
lea (v_ngfx_buffer).w,a1
move.w (a0)+,d2
bpl.s loc_160E
adda.w #$A,a3
loc_160E:
andi.w #$7FFF,d2
move.w d2,(f_plc_execute).w
bsr.w NemDec4
move.b (a0)+,d5
asl.w #8,d5
move.b (a0)+,d5
moveq #$10,d6
moveq #0,d0
move.l a0,(v_plc_buffer).w
move.l a3,(v_ptrnemcode).w
move.l d0,($FFFFF6E4).w
move.l d0,($FFFFF6E8).w
move.l d0,($FFFFF6EC).w
move.l d5,($FFFFF6F0).w
move.l d6,($FFFFF6F4).w
Rplc_Exit:
rts
; End of function RunPLC
Move this line:
move.w d2,(f_plc_execute).w
after this line:
move.l d6,($FFFFF6F4).w
Sonic 1, Hivebrain's 2005 disassembly
This is the RunPLC_RAM: label:
; ---------------------------------------------------------------------------
; Subroutine to use graphics listed in a pattern load cue
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
RunPLC_RAM: ; XREF: Pal_FadeTo
tst.l ($FFFFF680).w
beq.s locret_1640
tst.w ($FFFFF6F8).w
bne.s locret_1640
movea.l ($FFFFF680).w,a0
lea (loc_1502).l,a3
lea ($FFFFAA00).w,a1
move.w (a0)+,d2
bpl.s loc_160E
adda.w #$A,a3
loc_160E:
andi.w #$7FFF,d2
move.w d2,($FFFFF6F8).w
bsr.w NemDec4
move.b (a0)+,d5
asl.w #8,d5
move.b (a0)+,d5
moveq #$10,d6
moveq #0,d0
move.l a0,($FFFFF680).w
move.l a3,($FFFFF6E0).w
move.l d0,($FFFFF6E4).w
move.l d0,($FFFFF6E8).w
move.l d0,($FFFFF6EC).w
move.l d5,($FFFFF6F0).w
move.l d6,($FFFFF6F4).w
locret_1640:
rts
; End of function RunPLC_RAM
Move this line:
move.w d2,($FFFFF6F8).w
after this line:
move.l d6,($FFFFF6F4).w
Sonic 2, SVN disassembly
This is the RunPLC_RAM:
; ---------------------------------------------------------------------------
; Subroutine to use graphics listed in a pattern load cue
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
; sub_168A:
RunPLC_RAM:
tst.l (Plc_Buffer).w
beq.s ++ ; rts
tst.w (Plc_Buffer_Reg18).w
bne.s ++ ; rts
movea.l (Plc_Buffer).w,a0
lea NemDec_WriteAndStay(pc),a3
nop
lea (Decomp_Buffer).w,a1
move.w (a0)+,d2
bpl.s +
adda.w #NemDec_WriteAndStay_XOR-NemDec_WriteAndStay,a3
+
andi.w #$7FFF,d2
move.w d2,(Plc_Buffer_Reg18).w
bsr.w NemDecPrepare
move.b (a0)+,d5
asl.w #8,d5
move.b (a0)+,d5
moveq #$10,d6
moveq #0,d0
move.l a0,(Plc_Buffer).w
move.l a3,(Plc_Buffer_Reg0).w
move.l d0,(Plc_Buffer_Reg4).w
move.l d0,(Plc_Buffer_Reg8).w
move.l d0,(Plc_Buffer_RegC).w
move.l d5,(Plc_Buffer_Reg10).w
move.l d6,(Plc_Buffer_Reg14).w
+
rts
; End of function RunPLC_RAM
Move this line:
move.w d2,(Plc_Buffer_Reg18).w
after this line:
move.l d6,(Plc_Buffer_Reg14).w
Sonic 2, Xenowhirl's 2007 disassembly
The line:
move.w d2,(Plc_Buffer_Reg18).w
appears as:
move.w d2,($FFFFF6F8).w
and the line:
move.l d6,(Plc_Buffer_Reg14).w
appears as:
move.l d6,($FFFFF6F4).w
Follow the steps for the SVN disassembly above.
Explanation of the bug
Pattern Load Cues are a mechanism to spread out decompression of Nemesis-compressed patterns over several frames to keep the game from hanging, because it is a slow process. When a PLC request is queued, some tiles are immediately decompressed, then some values are stored in RAM so that processing can resume later in VBlank at the point where processing was suspended. The race condition occurs because one of these values, f_plc_execute ($FFF6F8), is initialized before the initial decompression is started, whereas the other values are initialized after the initial decompression is complete. If the VBlank interrupt occurs while the initial decompression is still running, the routines in VBlank (specifically, at sub_1642 and sub_165E) will start decompression, because f_plc_execute is not 0, but using uninitialized values. The critical value is v_ptrnemcode ($FFF6E0), which is a pointer to the routine to use to output the decompress data. Its value when no PLCs are suspended is 0, and there is no valid code at address 0, so when the game tries to jump there, it crashes.
To fix the problem, f_plc_execute ($FFF6F8) must be set after all the other values are initialized.
|Fix a race condition with Pattern Load Cues]]