Fix Race Condition with PLCs - RetroKoH/S1Fixed GitHub Wiki

(Original Guide by FraGag)
Source: SCHG Page
Commit: 0880ce0

There is a race condition in Sonic 1, triggered in Labyrinth Zone Act 1 or Act 2 by ducking and panning the screen all the way down before the End-of-Act card appears. It is harder to reproduce this if the Spin Dash mod is enabled (due to the scroll delay applied), but it is still present. Fixing the bug is easy:

RunPLC:
		tst.l	(v_plc_buffer).w
		beq.s	Rplc_Exit
		tst.w	(v_plc_patternsleft).w
		bne.s	Rplc_Exit
		movea.l	(v_plc_buffer).w,a0
		lea	(NemPCD_WriteRowToVDP).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,(v_plc_patternsleft).w
		bsr.w	NemDec_BuildCodeTable
		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_plc_ptrnemcode).w
		move.l	d0,(v_plc_repeatcount).w
		move.l	d0,(v_plc_paletteindex).w
		move.l	d0,(v_plc_previousrow).w
		move.l	d5,(v_plc_dataword).w
		move.l	d6,(v_plc_shiftvalue).w
+		move.w	d2,(v_plc_patternsleft).w

Rplc_Exit:
		rts

FraGag's Explanation:

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, v_plc_patternsleft ($FFFFF6F8), 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 v_plc_patternsleft is not 0, but using uninitialized values. The critical value is v_plc_ptrnemcode ($FFFFF6E0), 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, v_plc_patternsleft ($FFFFF6F8) must be set after all the other values are initialized.