Как исправить баг с загрузкой Pattern Load Cues
Автор гида: FraGag, Перевод на русский язык и модификация: vladikcomperОригинальный гид: «SCHG How-to:Fix a race condition with Pattern Load Cues»
В Соник 1 и Соник 2 на первом этапе загрузки Pattern Load Cues, или сокращенно PLC, в редких случаях может произойти баг, приводящий к падению игры. Баг можно заметить в Соник 1 на уровнях LZ1 и LZ2: если вода находится на определенном уровне относительно начала экрана, игра зависает про попытке загрузить арт для экрана 'SONIC HAS PASSED' (см. видео)
В Соник 2 создать условия для возникновения бага почти невозможно. В Соник 3 и Соник и Наклз баг был исправлен как показано ниже.
Исправление бага
Шаги, рассказанные ниже, действуют как для кода Соник 1 (Hivebrain's 2005 disassembly / S1HS2 disassembly), так и для Соник 2 (Xenowhirl's Disassembly). Код, с которыми вы будете работать, в обоих играх абсолютно одинаков, будут только несколько визуальных отличий между дизасемблами. Представленный ниже код взять из Соник 1, дизасембл S1HS2.
Найдите процедуру "RunPLC_RAM", вы увидите следующий код:
; --------------------------------------------------------------------------- ; Subroutine to use graphics in PLC queue ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| RunPLC_RAM: ; XREF: Pal_FadeTo tst.l ($FFFFF680).w ; is PLC queue empty? beq.s RunPLC_Rts ; if yes, branch tst.w ($FFFFF6F8).w ; is decompression in progress? bne.s RunPLC_Rts ; if yes, branch movea.l ($FFFFF680).w,a0 ; load PLC queue 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 RunPLC_Rts: rts ; End of function RunPLC_RAM
Переместите строчку
move.w d2,($FFFFF6F8).w
на новое место, прямо за этой строкой:
move.l d6,($FFFFF6F4).w
Причины бага
Pattern Load Cues - это механизм, разделяющий загрузку сжатых алгоритмом Nemesis тайлов на несколько кадров, чтобы игра могла синхронно (не зависая) работать наряду декомпрессором тайлов, так как этот процесс отнимает очень много времени. В начале PLC запроса, некоторые тайлы разжимаются на месте, затем процедура настраивает значения в памяти, чтобы другая часть декомпрессора смогла продолжить декомпрессию тайлов. Тайлы разжимаются небольшими порциями во время вертикального прерывания (VBlank), этот процесс растягивается на несколько кадров, и благодаря этому остальные части игры также могут функционировать. Баг происходит из-за того, что одно из значений ($FFF6F8, флаг PLC) задается до декомпрессии первых тайлов, в то время как остальные задаются после. И если вертикальные или горизонтальные прерывания (VBlank, HBlank) случаются во время начальной декомпрессии, процедуры в коде VBlank'а (sub_1642 и sub_165E) попытаются "подхватить" (продолжить) декомпрессию, потому что флаг $FFF6F8 активен, однако остальные значения в этот момент не инициализированы. В частности, адрес $FFF6E0, который ссылается одну из процедур декомпрессора, будет ссылаться на неверное место. Это и проводит к падению игры с ошибкой ILLEGAL INSTRUCTION.
Чтобы исправить проблему, флаг $FFF6F8 должен быть задан после того, как все значения инициализированы.