Actions

SCHG How-to

Difference between revisions of "Improve the fade in\fade out progression routines in Sonic 2"

From Sonic Retro

(Created page with "{{GuideBy|MarkeyJester}} The palette fading routines in Sonic 1 and Sonic 2 (and possibly to a degree Sonic 3 & Knuckles) known as "Pal_FadeTo" and "Pal_FadeFrom", are not 100% ...")
 
m (cleanup)
 
(9 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 
{{GuideBy|MarkeyJester}}
 
{{GuideBy|MarkeyJester}}
  
The palette fading routines in Sonic 1 and Sonic 2 (and possibly to a degree Sonic 3 & Knuckles) known as "Pal_FadeTo" and "Pal_FadeFrom", are not 100% true to their name. Here is a screenshot of Sonic 2's title screen 6 frames during fade in:
+
''Updated by [[User:WBilgini]]''
 +
 
 +
''Updated again by [[User:CosmotheFoxxo]]''
 +
 
 +
The palette fading routines in Sonic 1 and Sonic 2 (and possibly to a degree Sonic 3 & Knuckles) known as "Pal_FadeFromBlack" and "Pal_FadeToBlack", are not working exactly well. Here is a screenshot of Sonic 2's title screen 6 frames during fade in:
  
 
[[Image:FadeInS2Old.png|center]]
 
[[Image:FadeInS2Old.png|center]]
Line 33: Line 37:
 
==The Main Routine==
 
==The Main Routine==
  
The main palette fading routines are known as "Pal_FadeTo" and "Pal_FadeFrom", you'll need to go to the routine "Pal_FadeTo" and replace everything from there down to "; End of function Pal_DecColor" with this:
+
The main palette fading routines are known as "Pal_FadeFromBlack", "Pal_FadeToBlack","Pal_FadeFromWhite", and "Pal_FadeToWhite". You'll need to go to the routine "Pal_FadeFromBlack" and replace everything from there down to the end of "Pal_FadeToWhite" with this:
  
<asm>Pal_FadeTo:
+
<syntaxhighlight lang="asm">; ---------------------------------------------------------------------------
move.w #$3F,($FFFFF626).w
+
; Subroutine to fade in from black
 +
; ---------------------------------------------------------------------------
 +
 
 +
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 +
 
 +
; sub_23C6: Pal_FadeTo:
 +
Pal_FadeFromBlack:
 +
move.w #$3F,(Palette_fade_range).w
 
moveq #0,d0
 
moveq #0,d0
 
lea (Normal_palette).w,a0
 
lea (Normal_palette).w,a0
move.b ($FFFFF626).w,d0
+
move.b (Palette_fade_start).w,d0
 
adda.w d0,a0
 
adda.w d0,a0
 
moveq #0,d1
 
moveq #0,d1
move.b ($FFFFF627).w,d0
+
move.b (Palette_fade_length).w,d0
; loc_23DE:
+
; loc_23DE: Pal_ToBlack:
Pal_ToBlack:
+
.palettewrite:
 
move.w d1,(a0)+
 
move.w d1,(a0)+
dbf d0,Pal_ToBlack ; fill palette with $000 (black)
+
dbf d0,.palettewrite ; fill palette with $000 (black)
 
moveq #$0E,d4 ; MJ: prepare maximum colour check
 
moveq #$0E,d4 ; MJ: prepare maximum colour check
 
moveq #$00,d6 ; MJ: clear d6
 
moveq #$00,d6 ; MJ: clear d6
  
- bsr.w RunPLC_RAM
+
.nextframe:
move.b #$12,(Delay_Time).w
+
bsr.w RunPLC_RAM
bsr.w DelayProgram
+
move.b #$12,(Vint_routine).w
 +
bsr.w WaitForVint
 
bchg #$00,d6 ; MJ: change delay counter
 
bchg #$00,d6 ; MJ: change delay counter
beq - ; MJ: if null, delay a frame
+
beq .nextframe ; MJ: if null, delay a frame
bsr.s Pal_FadeIn
+
bsr.s .UpdateAllColours
 
subq.b #$02,d4 ; MJ: decrease colour check
 
subq.b #$02,d4 ; MJ: decrease colour check
bne - ; MJ: if it has not reached null, branch
+
bne .nextframe ; MJ: if it has not reached null, branch
move.b #$12,(Delay_Time).w ; MJ: wait for V-blank again (so colours transfer)
+
move.b #$12,(Vint_routine).w ; MJ: wait for V-blank again (so colours transfer)
bra DelayProgram ; MJ: ''
+
bra WaitForVint ; MJ: ''
  
 
; End of function Pal_FadeTo
 
; End of function Pal_FadeTo
  
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Palette fade-in subroutine
+
; Subroutine to update all colours once
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
 
+
; sub_23FE: Pal_FadeIn:
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
.UpdateAllColours:
 
 
; sub_23FE:
 
Pal_FadeIn:
 
 
moveq #0,d0
 
moveq #0,d0
 
lea (Normal_palette).w,a0
 
lea (Normal_palette).w,a0
lea (Second_palette).w,a1
+
lea (Target_palette).w,a1
move.b ($FFFFF626).w,d0
+
move.b (Palette_fade_start).w,d0
 
adda.w d0,a0
 
adda.w d0,a0
 
adda.w d0,a1
 
adda.w d0,a1
move.b ($FFFFF627).w,d0
+
move.b (Palette_fade_length).w,d0
  
- bsr.s Pal_AddColor
+
.nextcolor
dbf d0,-
+
bsr.s .UpdateColour
 +
dbf d0,.nextcolor
 
tst.b (Water_flag).w
 
tst.b (Water_flag).w
beq.s return_243C
+
beq.s .skipunderwater
 
moveq #0,d0
 
moveq #0,d0
 
lea (Underwater_palette).w,a0
 
lea (Underwater_palette).w,a0
lea (Underwater_palette_2).w,a1
+
lea (Underwater_target_palette).w,a1
move.b ($FFFFF626).w,d0
+
move.b (Palette_fade_start).w,d0
 
adda.w d0,a0
 
adda.w d0,a0
 
adda.w d0,a1
 
adda.w d0,a1
move.b ($FFFFF627).w,d0
+
move.b (Palette_fade_length).w,d0
  
- bsr.s Pal_AddColor
+
.nextcolor2
dbf d0,-
+
bsr.s .UpdateColour
 +
dbf d0,.nextcolor2
  
return_243C:
+
.skipunderwater:
 
rts
 
rts
; End of function Pal_FadeIn
 
  
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 
  
; sub_243E:
+
; ---------------------------------------------------------------------------
Pal_AddColor:
+
; Subroutine to update a single colour once
 +
; ---------------------------------------------------------------------------
 +
; sub_243E: Pal_AddColor:
 +
.UpdateColour:
 
move.b (a1),d5 ; MJ: load blue
 
move.b (a1),d5 ; MJ: load blue
 
move.w (a1)+,d1 ; MJ: load green and red
 
move.w (a1)+,d1 ; MJ: load green and red
Line 127: Line 139:
 
rts ; MJ: return
 
rts ; MJ: return
  
; End of function Pal_AddColor
 
  
 +
 +
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to fade out to black
 +
; ---------------------------------------------------------------------------
  
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
  
; sub_246A:
+
; sub_246A: Pal_FadeFrom:
Pal_FadeFrom:
+
Pal_FadeToBlack:
move.w #$3F,($FFFFF626).w
+
move.w #$3F,(Palette_fade_range).w
 
moveq #$07,d4 ; MJ: set repeat times
 
moveq #$07,d4 ; MJ: set repeat times
 
moveq #$00,d6 ; MJ: clear d6
 
moveq #$00,d6 ; MJ: clear d6
  
- bsr.w RunPLC_RAM
+
.nextbframe
move.b #$12,(Delay_Time).w
+
bsr.w RunPLC_RAM
bsr.w DelayProgram
+
move.b #$12,(Vint_routine).w
 +
bsr.w WaitForVint
 
bchg #$00,d6 ; MJ: change delay counter
 
bchg #$00,d6 ; MJ: change delay counter
beq - ; MJ: if null, delay a frame
+
beq .nextbframe ; MJ: if null, delay a frame
bsr.s Pal_FadeOut
+
bsr.s .UpdateAllColours
dbf d4,-
+
dbf d4,.nextbframe
 
rts
 
rts
; End of function Pal_FadeFrom
+
; End of function Pal_FadeToBlack
  
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
; Palette fade-out subroutine
+
; Subroutine to update all colours once
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
 
+
; sub_248A: Pal_FadeOut:
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
+
.UpdateAllColours:
 
 
; sub_248A:
 
Pal_FadeOut:
 
 
moveq #0,d0
 
moveq #0,d0
 
lea (Normal_palette).w,a0
 
lea (Normal_palette).w,a0
move.b ($FFFFF626).w,d0
+
move.b (Palette_fade_start).w,d0
 
adda.w d0,a0
 
adda.w d0,a0
move.b ($FFFFF627).w,d0
+
move.b (Palette_fade_length).w,d0
  
- bsr.s Pal_DecColor
+
.Nextcolour
dbf d0,-
+
bsr.s .UpdateColour
 +
dbf d0,.Nextcolour
 
moveq #0,d0
 
moveq #0,d0
 
lea (Underwater_palette).w,a0
 
lea (Underwater_palette).w,a0
move.b ($FFFFF626).w,d0
+
move.b (Palette_fade_start).w,d0
 
adda.w d0,a0
 
adda.w d0,a0
move.b ($FFFFF627).w,d0
+
move.b (Palette_fade_length).w,d0
  
- bsr.s Pal_DecColor
+
.Nextcolour2
dbf d0,-
+
bsr.s .UpdateColour
 +
dbf d0,.Nextcolour2
 
rts
 
rts
; End of function Pal_FadeOut
 
  
  
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 
  
; sub_24B8:
+
; ---------------------------------------------------------------------------
Pal_DecColor:
+
; Subroutine to update a single colour once
 +
; ---------------------------------------------------------------------------
 +
; sub_24B8: Pal_DecColor:
 +
.UpdateColour:
 
move.w (a0),d5 ; MJ: load colour
 
move.w (a0),d5 ; MJ: load colour
 
move.w d5,d1 ; MJ: copy to d1
 
move.w d5,d1 ; MJ: copy to d1
Line 201: Line 218:
 
move.w d5,(a0)+ ; MJ: save new colour
 
move.w d5,(a0)+ ; MJ: save new colour
 
rts ; MJ: return
 
rts ; MJ: return
 +
; End of function Pal_FadeToBlack
 +
; Everything after this point was implemented by CosmotheFoxxo
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to update all colours once
 +
; ---------------------------------------------------------------------------
 +
; sub_248A: Pal_FadeOut:
 +
.UpdateAllColours:
 +
; Update above-water palette
 +
moveq #0,d0
 +
lea (Normal_palette).w,a0
 +
move.b (Palette_fade_start).w,d0
 +
adda.w d0,a0
 +
 +
move.b (Palette_fade_length).w,d0
 +
.nextcolour:
 +
bsr.s .UpdateColour
 +
dbf d0,.nextcolour
 +
 +
; Notice how this one lacks a check for
 +
; if Water_flag is set, unlike Pal_FadeFromBlack?
 +
 +
; Update underwater palette
 +
moveq #0,d0
 +
lea (Underwater_palette).w,a0
 +
move.b (Palette_fade_start).w,d0
 +
adda.w d0,a0
 +
 +
move.b (Palette_fade_length).w,d0
 +
.nextcolour2:
 +
bsr.s .UpdateColour
 +
dbf d0,.nextcolour2
 +
 +
rts
 +
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to update a single colour once
 +
; ---------------------------------------------------------------------------
 +
; sub_24B8: Pal_DecColor:
 +
.UpdateColour:
 +
move.w (a0),d5
 +
move.w d5,d1
 +
move.b d1,d2
 +
move.b d1,d3
 +
andi.b #$0E,d3
 +
beq .updategreen
 +
subq.b #$02,d5
 +
 +
; loc_24C8: Pal_DecGreen:
 +
.updategreen:
 +
andi.b #$E0,d2
 +
beq .updateblue
 +
subi.b #$20,d5
 +
 +
; loc_24D6: Pal_DecBlue:
 +
.updateblue:
 +
andi.w #$0E00,d1
 +
beq .updatenone
 +
subi.w #$0200,d5
 +
 +
; loc_24E4: Pal_DecNone:
 +
.updatenone:
 +
move.w d5,(a0)+
 +
rts
 +
 +
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to fade in from white
 +
; ---------------------------------------------------------------------------
 +
 +
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 +
 +
; sub_24E8: Pal_MakeWhite:
 +
Pal_FadeFromWhite:
 +
move.w #$3F,(Palette_fade_range).w
 +
moveq #0,d0
 +
lea (Normal_palette).w,a0
 +
move.b (Palette_fade_start).w,d0
 +
adda.w d0,a0
 +
move.w #$EEE,d1
 +
 +
move.b (Palette_fade_length).w,d0
 +
.palettewrite:
 +
move.w d1,(a0)+
 +
dbf d0,.palettewrite
 +
moveq #$0E,d4
 +
moveq #$00,d6
 +
.nextframe:
 +
move.b #VintID_Fade,(Vint_routine).w
 +
bsr.w WaitForVint
 +
bchg #$00,d6
 +
beq .nextframe
 +
bsr.s .UpdateAllColours
 +
subq.b #$02,d4
 +
bne .nextframe
 +
move.b #$12,(Vint_routine).w
 +
bra WaitForVint
 +
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to update all colours once
 +
; ---------------------------------------------------------------------------
 +
; sub_2522: Pal_WhiteToBlack:
 +
.UpdateAllColours:
 +
; Update above-water palette
 +
moveq #0,d0
 +
lea (Normal_palette).w,a0
 +
lea (Target_palette).w,a1
 +
move.b (Palette_fade_start).w,d0
 +
adda.w d0,a0
 +
adda.w d0,a1
  
; End of function Pal_DecColor</asm>
+
move.b (Palette_fade_length).w,d0
 +
.nextcolour:
 +
bsr.s .UpdateColour
 +
dbf d0,.nextcolour
 +
 
 +
tst.b (Water_flag).w
 +
beq.s .skipunderwater
 +
; Update underwater palette
 +
moveq #0,d0
 +
lea (Underwater_palette).w,a0
 +
lea (Underwater_target_palette).w,a1
 +
move.b (Palette_fade_start).w,d0
 +
adda.w d0,a0
 +
adda.w d0,a1
 +
 
 +
move.b (Palette_fade_length).w,d0
 +
.nextcolour2:
 +
bsr.s .UpdateColour
 +
dbf d0,.nextcolour2
 +
 
 +
.skipunderwater:
 +
rts
 +
 
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to update a single colour once
 +
; ---------------------------------------------------------------------------
 +
; sub_2562: Pal_DecColor2:
 +
.UpdateColour:
 +
move.b (a1),d5
 +
move.w (a1)+,d1
 +
move.b d1,d2
 +
lsr.b #$04,d1
 +
andi.b #$0E,d2
 +
move.w (a0),d3
 +
cmp.b d5,d4
 +
bls A_NoBlue
 +
subi.w #$0200,d3
 +
 
 +
A_NoBlue:
 +
cmp.b d1,d4
 +
bls A_NoGreen
 +
subi.b #$20,d3
 +
 
 +
A_NoGreen:
 +
cmp.b d2,d4
 +
bls A_NoRed
 +
subq.b #$02,d3
 +
 
 +
A_NoRed:
 +
move.w d3,(a0)+
 +
rts
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to fade out to white (used when you enter a special stage)
 +
; ---------------------------------------------------------------------------
 +
 
 +
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
 +
 
 +
; sub_2592: Pal_MakeFlash:
 +
Pal_FadeToWhite:
 +
move.w #$3F,(Palette_fade_range).w
 +
moveq #$07,d4 ; MJ: set repeat times
 +
moveq #$00,d6 ; MJ: clear d6
 +
.nextcframe:
 +
move.b #VintID_Fade,(Vint_routine).w
 +
bsr.w WaitForVint
 +
bchg #$00,d6
 +
beq .nextcframe
 +
bsr.s .UpdateAllColours
 +
dbf d4,.nextcframe
 +
rts
 +
; End of function Pal_FadeToWhite
 +
 
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to update all colours once
 +
; ---------------------------------------------------------------------------
 +
; sub_25B2: Pal_ToWhite:
 +
.UpdateAllColours:
 +
; Update above-water palette
 +
moveq #0,d0
 +
lea (Normal_palette).w,a0
 +
move.b (Palette_fade_start).w,d0
 +
adda.w d0,a0
 +
 
 +
move.b (Palette_fade_length).w,d0
 +
.nextcolour:
 +
bsr.s .UpdateColour
 +
dbf d0,.nextcolour
 +
 
 +
; Notice how this one lacks a check for
 +
; if Water_flag is set, unlike Pal_FadeFromWhite?
 +
 
 +
; Update underwater palette
 +
moveq #0,d0
 +
lea (Underwater_palette).w,a0
 +
move.b (Palette_fade_start).w,d0
 +
adda.w d0,a0
 +
 
 +
move.b (Palette_fade_length).w,d0
 +
.nextcolour2:
 +
bsr.s .UpdateColour
 +
dbf d0,.nextcolour2
 +
 
 +
rts
 +
 
 +
; ---------------------------------------------------------------------------
 +
; Subroutine to update a single colour once
 +
; ---------------------------------------------------------------------------
 +
; sub_25E0: Pal_AddColor2:
 +
.UpdateColour:
 +
move.w (a0),d5
 +
move.w d5,d1
 +
move.b d1,d2
 +
move.b d1,d3
 +
andi.w #$0E00,d1
 +
cmpi.w #$0E00,d1
 +
beq a_NoBlue
 +
addi.w #$0200,d5
 +
 
 +
a_NoBlue:
 +
andi.b #$E0,d2
 +
cmpi.b #$E0,d2
 +
beq a_NoGreen
 +
addi.b #$20,d5
 +
 
 +
a_NoGreen:
 +
andi.b #$0E,d3
 +
cmpi.b #$0E,d3
 +
beq a_NoRed
 +
addq.b #$02,d5
 +
 
 +
a_NoRed:
 +
move.w d5,(a0)+
 +
rts
 +
; End of function Pal_AddColor2
 +
 
 +
</syntaxhighlight>
  
 
==Title Screen Specific Routine==
 
==Title Screen Specific Routine==
Line 210: Line 471:
 
At routine "ObjC9", you'll find this code below:
 
At routine "ObjC9", you'll find this code below:
  
<asm>ObjC9:
+
<syntaxhighlight lang="asm">ObjC9:
 
moveq #0,d0
 
moveq #0,d0
 
move.b routine(a0),d0
 
move.b routine(a0),d0
move.w loc_132FE(pc,d0.w),d1
+
move.w ObjC9_Index(pc,d0.w),d1
jmp loc_132FE(pc,d1.w)
+
jmp ObjC9_Index(pc,d1.w)
 +
; ===========================================================================
 +
ObjC9_Index: offsetTable
 +
offsetTableEntry.w ObjC9_Init ; 0
 +
offsetTableEntry.w ObjC9_Main ; 2
 
; ===========================================================================
 
; ===========================================================================
  
loc_132FE:
+
ObjC9_Init:
ori.b #$46,d4
 
 
addq.b #2,routine(a0)
 
addq.b #2,routine(a0)
 
moveq #0,d0
 
moveq #0,d0
Line 224: Line 488:
 
lea (PaletteChangerDataIndex).l,a1
 
lea (PaletteChangerDataIndex).l,a1
 
adda.w (a1,d0.w),a1
 
adda.w (a1,d0.w),a1
move.l (a1)+,objoff_3A(a0)
+
move.l (a1)+,ttlscrpalchanger_codeptr(a0)
 
movea.l (a1)+,a2
 
movea.l (a1)+,a2
 
move.b (a1)+,d0
 
move.b (a1)+,d0
move.w d0,objoff_34(a0)
+
move.w d0,ttlscrpalchanger_start_offset(a0)
lea (Second_palette).w,a3
+
lea (Target_palette).w,a3
 
adda.w d0,a3
 
adda.w d0,a3
 
move.b (a1)+,d0
 
move.b (a1)+,d0
move.w d0,objoff_36(a0)
+
move.w d0,ttlscrpalchanger_length(a0)
 +
 
 +
- move.w (a2)+,(a3)+
 +
dbf d0,-
  
loc_1332E:
 
move.w (a2)+,(a3)+
 
dbf d0,loc_1332E
 
 
move.b (a1)+,d0
 
move.b (a1)+,d0
move.b d0,objoff_30(a0)
+
move.b d0,ttlscrpalchanger_fadein_time_left(a0)
move.b d0,objoff_31(a0)
+
move.b d0,ttlscrpalchanger_fadein_time(a0)
move.b (a1)+,objoff_32(a0)
+
move.b (a1)+,ttlscrpalchanger_fadein_amount(a0)
 
rts
 
rts
 
; ===========================================================================
 
; ===========================================================================
; unused/dead code ; a0=object
 
  
subq.b #1,objoff_30(a0)
+
ObjC9_Main:
bpl.s return_1337A
+
subq.b #1,ttlscrpalchanger_fadein_time_left(a0)
move.b objoff_31(a0),objoff_30(a0)
+
bpl.s +
subq.b #1,objoff_32(a0)
+
move.b ttlscrpalchanger_fadein_time(a0),ttlscrpalchanger_fadein_time_left(a0)
 +
subq.b #1,ttlscrpalchanger_fadein_amount(a0)
 
bmi.w DeleteObject
 
bmi.w DeleteObject
movea.l objoff_3A(a0),a2
+
movea.l ttlscrpalchanger_codeptr(a0),a2
 
movea.l a0,a3
 
movea.l a0,a3
move.w objoff_36(a0),d0
+
move.w ttlscrpalchanger_length(a0),d0
move.w objoff_34(a0),d1
+
move.w ttlscrpalchanger_start_offset(a0),d1
 
lea (Normal_palette).w,a0
 
lea (Normal_palette).w,a0
 
adda.w d1,a0
 
adda.w d1,a0
lea (Second_palette).w,a1
+
lea (Target_palette).w,a1
 
adda.w d1,a1
 
adda.w d1,a1
  
- jsr (a2) ; dynamic call! to Pal_AddColor, loc_1344C, or loc_1348A, assuming the PaletteChangerData pointers haven't been changed
+
- jsr (a2) ; dynamic call! to Pal_FadeFromBlack.UpdateColour, loc_1344C, or loc_1348A, assuming the PaletteChangerData pointers haven't been changed
 
dbf d0,-
 
dbf d0,-
 +
 
movea.l a3,a0
 
movea.l a3,a0
 +
+
 +
rts</syntaxhighlight>
  
return_1337A:
+
Here are the necessary changes required for the fading to perform correctly:
rts</asm>
 
  
There's a problem here however, that first ori.b #$46,d4 is infact two words of pointer data being 0004 and 0046, these are the routines address minus the table start address, this means that the "; unused/dead code" isn't actually unused, the table was just disassembled incorrectly, below I have provided a fix for this mistake, along with the necessary changes required for the fading to perform correctly:
+
<syntaxhighlight lang="asm">ObjC9:
 
+
moveq #0,d0
<asm>ObjC9:
+
move.b routine(a0),d0
        moveq   #0,d0
+
move.w ObjC9_Index(pc,d0.w),d1
        move.b routine(a0),d0
+
jmp ObjC9_Index(pc,d1.w)
        move.w loc_132FE(pc,d0.w),d1
+
; ===========================================================================
        jmp     loc_132FE(pc,d1.w)
+
ObjC9_Index: offsetTable
 +
offsetTableEntry.w ObjC9_Init ; 0
 +
offsetTableEntry.w ObjC9_Main ; 2
 
; ===========================================================================
 
; ===========================================================================
  
loc_132FE:
+
ObjC9_Init:
        dc.w   loc_13302-loc_132FE
+
addq.b #2,routine(a0)
         dc.w    NewRoutine-loc_132FE
+
moveq #0,d0
 +
move.b subtype(a0),d0
 +
lea (PaletteChangerDataIndex).l,a1
 +
adda.w (a1,d0.w),a1
 +
move.l (a1)+,ttlscrpalchanger_codeptr(a0)
 +
movea.l (a1)+,a2
 +
move.b (a1)+,d0
 +
move.w d0,ttlscrpalchanger_start_offset(a0)
 +
lea (Target_palette).w,a3
 +
adda.w d0,a3
 +
move.b (a1)+,d0
 +
move.w d0,ttlscrpalchanger_length(a0)
 +
         move.b  #$0E,objoff_33(a0)                      ; MJ: set fade counter
  
loc_13302:
+
- move.w (a2)+,(a3)+
        addq.b  #2,routine(a0)
+
dbf d0,-
        moveq  #0,d0
 
        move.b  subtype(a0),d0
 
        lea    (PaletteChangerDataIndex).l,a1
 
        adda.w (a1,d0.w),a1
 
        move.l  (a1)+,objoff_3A(a0)
 
        movea.l (a1)+,a2
 
        move.b  (a1)+,d0
 
        move.w  d0,objoff_34(a0)
 
        lea    (Second_palette).w,a3
 
        adda.w  d0,a3
 
        move.b  (a1)+,d0
 
        move.w  d0,objoff_36(a0)
 
                move.b  #$0E,objoff_33(a0)                      ; MJ: set fade counter
 
  
loc_1332E:
+
move.b (a1)+,d0
        move.w  (a2)+,(a3)+
+
move.b d0,ttlscrpalchanger_fadein_time_left(a0)
        dbf    d0,loc_1332E
+
move.b d0,ttlscrpalchanger_fadein_time(a0)
        move.b (a1)+,d0
+
move.b (a1)+,ttlscrpalchanger_fadein_amount(a0)
        move.b d0,objoff_30(a0)
+
rts
        move.b d0,objoff_31(a0)
 
        move.b (a1)+,objoff_32(a0)
 
        rts
 
 
; ===========================================================================
 
; ===========================================================================
        ; unused/dead code ; a0=object
 
  
NewRoutine:
+
ObjC9_Main:
        subq.b #1,objoff_30(a0)
+
subq.b #1,ttlscrpalchanger_fadein_time_left(a0)
        bpl.s   return_1337A
+
bpl.s +
        move.b objoff_31(a0),objoff_30(a0)
+
move.b ttlscrpalchanger_fadein_time(a0),ttlscrpalchanger_fadein_time_left(a0)
        subq.b #1,objoff_32(a0)
+
subq.b #1,ttlscrpalchanger_fadein_amount(a0)
        bmi.w   DeleteObject
+
bmi.w DeleteObject
 
                 moveq  #$00,d4                                ; MJ: clear d4
 
                 moveq  #$00,d4                                ; MJ: clear d4
 
                 move.b  objoff_33(a0),d4                        ; MJ: load fade counter
 
                 move.b  objoff_33(a0),d4                        ; MJ: load fade counter
 
                 subq.b  #$02,objoff_33(a0)                      ; MJ: decrease fade counter
 
                 subq.b  #$02,objoff_33(a0)                      ; MJ: decrease fade counter
         movea.l objoff_3A(a0),a2                                ; load routine to run
+
         movea.l ttlscrpalchanger_codeptr(a0),a2                                ; load routine to run
 
         movea.l a0,a3                                          ; store current object
 
         movea.l a0,a3                                          ; store current object
         move.w  objoff_36(a0),d0                                ; load length
+
         move.w  ttlscrpalchanger_length(a0),d0                                ; load length
         move.w  objoff_34(a0),d1                                ; load loadtooffset
+
         move.w  ttlscrpalchanger_start_offset(a0),d1                                ; load loadtooffset
        lea     (Normal_palette).w,a0
+
movea.l ttlscrpalchanger_codeptr(a0),a2
        adda.w d1,a0
+
movea.l a0,a3
        lea     (Second_palette).w,a1
+
move.w ttlscrpalchanger_length(a0),d0
        adda.w d1,a1
+
move.w ttlscrpalchanger_start_offset(a0),d1
 +
lea (Normal_palette).w,a0
 +
adda.w d1,a0
 +
lea (Target_palette).w,a1
 +
adda.w d1,a1
  
-       jsr     (a2)   ; dynamic call! to Pal_AddColor, loc_1344C, or loc_1348A, assuming the PaletteChangerData pointers haven't been changed
+
- jsr (a2) ; dynamic call! to Pal_FadeFromBlack.UpdateColour, loc_1344C, or loc_1348A, assuming the PaletteChangerData pointers haven't been changed
        dbf     d0,-
+
dbf d0,-
        movea.l a3,a0
 
  
return_1337A:
+
movea.l a3,a0
        rts</asm>
+
+
 +
rts
 +
</syntaxhighlight>
  
 
This will ensure the object uses our new fading routines correctly, the next thing to do is at:
 
This will ensure the object uses our new fading routines correctly, the next thing to do is at:
  
<asm>off_1338C: C9PalInfo Pal_AddColor, Pal_1342C, $60, $F,2,$15</asm>
+
<syntaxhighlight lang="asm">off_1338C: C9PalInfo Pal_FadeFromBlack.UpdateColour, Pal_1342C, $60, $F,2,$15</syntaxhighlight>
  
 
Because of the nature of the new fade in routine being faster in the transition, we need to change the end value $15 to something smaller, say $07:
 
Because of the nature of the new fade in routine being faster in the transition, we need to change the end value $15 to something smaller, say $07:
  
<asm>off_1338C: C9PalInfo Pal_AddColor, Pal_1342C, $60, $F,2,$07 ; MJ: 15 to 7</asm>
+
<syntaxhighlight lang="asm">off_1338C: C9PalInfo Pal_FadeFromBlack.UpdateColour, Pal_1342C, $60, $F,2,$07 ; MJ: 15 to 7</syntaxhighlight>
 +
 
 +
This ensures the fading in routine is used correctly by the object, now there's one more fix to look at, if the player presses start to skip the transition halfway through fading in, the object continues fading in even though the entire full palette is loaded, to fix this goto "TitleScreen_SetFinalState":
  
This ensures the fading in routine is used correctly by the object, now there's one more fix to look at, if the player presses start to skip the transition halfway through fading in, the object continues fading in even though the entire full palette is loaded, to fix this goto "loc_135D6:":
+
<syntaxhighlight lang="asm">
 +
- move.l (a1)+,(a2)+
 +
dbf d6,-
  
<asm>loc_135D6:
 
move.l (a1)+,(a2)+
 
dbf d6,loc_135D6
 
 
tst.b objoff_30(a0)
 
tst.b objoff_30(a0)
bne.s return_135E8
+
bne.s + ; rts
moveq #$19+$80,d0 ; title music
+
moveq #signextendB(MusID_Title),d0 ; title music
bsr.w JmpTo4_PlayMusic</asm>
+
jsrto (PlayMusic).l, JmpTo4_PlayMusic
 +
+
 +
rts
 +
</syntaxhighlight>
 +
 
  
And place this instruction just after the "dbf d6,loc_135D6" instruction:
+
And place this instruction just after the "dbf d6,-" instruction:
  
<asm> sf.b (Object_RAM+($100+$32)).w ; set fade counter to 00 (finish)</asm>
+
<syntaxhighlight lang="asm"> sf.b (Object_RAM+($100+$32)).w ; set fade counter to 00 (finish)</syntaxhighlight>
  
 
This will set the palette fading objects fade counter to 0 and set the object to delete itself, thus preventing it from messing with the palette if the transition was skipped.
 
This will set the palette fading objects fade counter to 0 and set the object to delete itself, thus preventing it from messing with the palette if the transition was skipped.
Line 357: Line 630:
  
 
[[Image:FadeInS2New.png|center]]
 
[[Image:FadeInS2New.png|center]]
 +
 +
{{S2Howtos}}

Latest revision as of 22:27, 27 March 2023

(Original guide by MarkeyJester)

Updated by User:WBilgini

Updated again by User:CosmotheFoxxo

The palette fading routines in Sonic 1 and Sonic 2 (and possibly to a degree Sonic 3 & Knuckles) known as "Pal_FadeFromBlack" and "Pal_FadeToBlack", are not working exactly well. Here is a screenshot of Sonic 2's title screen 6 frames during fade in:

FadeInS2Old.png

From Sonic Team's point of view, it may not be incorrect and is possibly intentional, however, from a logical point of view, this is incorrect fading, what happens here is it fades the blue parts in first, then fades the green parts in, and then finally fades the red parts in, the same goes for fading out, red first, then green, finally blue. Strictly speaking for a the nearest possible perfect fade, all colours need to be fading in and out simultaneously (At the correct timing of course), so for example the colour 06E4 should fade in the pattern of:

0020 0040 0060 0080 02A0 04C2 06E4

And fade out in the pattern of:

04C2 02A0 0080 0060 0040 0020 0000

To make the above function for Sonic 2, there are two main places to look at:

The Main Routine

The main palette fading routines are known as "Pal_FadeFromBlack", "Pal_FadeToBlack","Pal_FadeFromWhite", and "Pal_FadeToWhite". You'll need to go to the routine "Pal_FadeFromBlack" and replace everything from there down to the end of "Pal_FadeToWhite" with this:

; ---------------------------------------------------------------------------
; Subroutine to fade in from black
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_23C6: Pal_FadeTo:
Pal_FadeFromBlack:
	move.w	#$3F,(Palette_fade_range).w
	moveq	#0,d0
	lea	(Normal_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	moveq	#0,d1
	move.b	(Palette_fade_length).w,d0
; loc_23DE: Pal_ToBlack:
.palettewrite:
	move.w	d1,(a0)+
	dbf	d0,.palettewrite	; fill palette with $000 (black)
		moveq	#$0E,d4					; MJ: prepare maximum colour check
		moveq	#$00,d6					; MJ: clear d6

.nextframe:
	bsr.w	RunPLC_RAM
	move.b	#$12,(Vint_routine).w
	bsr.w	WaitForVint
		bchg	#$00,d6					; MJ: change delay counter
		beq	.nextframe					; MJ: if null, delay a frame
	bsr.s	.UpdateAllColours
		subq.b	#$02,d4					; MJ: decrease colour check
		bne	.nextframe					; MJ: if it has not reached null, branch
		move.b	#$12,(Vint_routine).w			; MJ: wait for V-blank again (so colours transfer)
		bra	WaitForVint				; MJ: ''

; End of function Pal_FadeTo

; ---------------------------------------------------------------------------
; Subroutine to update all colours once
; ---------------------------------------------------------------------------
; sub_23FE: Pal_FadeIn:
.UpdateAllColours:
	moveq	#0,d0
	lea	(Normal_palette).w,a0
	lea	(Target_palette).w,a1
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	adda.w	d0,a1
	move.b	(Palette_fade_length).w,d0

.nextcolor
	bsr.s	.UpdateColour
	dbf	d0,.nextcolor
	tst.b	(Water_flag).w
	beq.s	.skipunderwater
	moveq	#0,d0
	lea	(Underwater_palette).w,a0
	lea	(Underwater_target_palette).w,a1
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	adda.w	d0,a1
	move.b	(Palette_fade_length).w,d0

.nextcolor2
	bsr.s	.UpdateColour
	dbf	d0,.nextcolor2

.skipunderwater:
	rts



; ---------------------------------------------------------------------------
; Subroutine to update a single colour once
; ---------------------------------------------------------------------------
; sub_243E: Pal_AddColor:
.UpdateColour:
		move.b	(a1),d5					; MJ: load blue
		move.w	(a1)+,d1				; MJ: load green and red
		move.b	d1,d2					; MJ: load red
		lsr.b	#$04,d1					; MJ: get only green
		andi.b	#$0E,d2					; MJ: get only red
		move.w	(a0),d3					; MJ: load current colour in buffer
		cmp.b	d5,d4					; MJ: is it time for blue to fade?
		bhi	FCI_NoBlue				; MJ: if not, branch
		addi.w	#$0200,d3				; MJ: increase blue

FCI_NoBlue:
		cmp.b	d1,d4					; MJ: is it time for green to fade?
		bhi	FCI_NoGreen				; MJ: if not, branch
		addi.b	#$20,d3					; MJ: increase green

FCI_NoGreen:
		cmp.b	d2,d4					; MJ: is it time for red to fade?
		bhi	FCI_NoRed				; MJ: if not, branch
		addq.b	#$02,d3					; MJ: increase red

FCI_NoRed:
		move.w	d3,(a0)+				; MJ: save colour
		rts						; MJ: return




; ---------------------------------------------------------------------------
; Subroutine to fade out to black
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_246A: Pal_FadeFrom:
Pal_FadeToBlack:
	move.w	#$3F,(Palette_fade_range).w
		moveq	#$07,d4					; MJ: set repeat times
		moveq	#$00,d6					; MJ: clear d6

.nextbframe
	bsr.w	RunPLC_RAM
	move.b	#$12,(Vint_routine).w
	bsr.w	WaitForVint
		bchg	#$00,d6					; MJ: change delay counter
		beq	.nextbframe					; MJ: if null, delay a frame
	bsr.s	.UpdateAllColours
	dbf	d4,.nextbframe
	rts
; End of function Pal_FadeToBlack

; ---------------------------------------------------------------------------
; Subroutine to update all colours once
; ---------------------------------------------------------------------------
; sub_248A: Pal_FadeOut:
.UpdateAllColours:
	moveq	#0,d0
	lea	(Normal_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	move.b	(Palette_fade_length).w,d0

.Nextcolour
	bsr.s	.UpdateColour
	dbf	d0,.Nextcolour
	moveq	#0,d0
	lea	(Underwater_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	move.b	(Palette_fade_length).w,d0

.Nextcolour2
	bsr.s	.UpdateColour
	dbf	d0,.Nextcolour2
	rts



; ---------------------------------------------------------------------------
; Subroutine to update a single colour once
; ---------------------------------------------------------------------------
; sub_24B8: Pal_DecColor:
.UpdateColour:
		move.w	(a0),d5					; MJ: load colour
		move.w	d5,d1					; MJ: copy to d1
		move.b	d1,d2					; MJ: load green and red
		move.b	d1,d3					; MJ: load red
		andi.w	#$0E00,d1				; MJ: get only blue
		beq	FCO_NoBlue				; MJ: if blue is finished, branch
		subi.w	#$0200,d5				; MJ: decrease blue

FCO_NoBlue:
		andi.b	#$E0,d2					; MJ: get only green
		beq	FCO_NoGreen				; MJ: if green is finished, branch
		subi.b	#$20,d5					; MJ: decrease green

FCO_NoGreen:
		andi.b	#$0E,d3					; MJ: get only red
		beq	FCO_NoRed				; MJ: if red is finished, branch
		subq.b	#$02,d5					; MJ: decrease red

FCO_NoRed:
		move.w	d5,(a0)+				; MJ: save new colour
		rts						; MJ: return
; End of function Pal_FadeToBlack
; Everything after this point was implemented by CosmotheFoxxo
; ---------------------------------------------------------------------------
; Subroutine to update all colours once
; ---------------------------------------------------------------------------
; sub_248A: Pal_FadeOut:
.UpdateAllColours:
	; Update above-water palette
	moveq	#0,d0
	lea	(Normal_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0

	move.b	(Palette_fade_length).w,d0
.nextcolour:
	bsr.s	.UpdateColour
	dbf	d0,.nextcolour

	; Notice how this one lacks a check for
	; if Water_flag is set, unlike Pal_FadeFromBlack?

	; Update underwater palette
	moveq	#0,d0
	lea	(Underwater_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0

	move.b	(Palette_fade_length).w,d0
.nextcolour2:
	bsr.s	.UpdateColour
	dbf	d0,.nextcolour2

	rts

; ---------------------------------------------------------------------------
; Subroutine to update a single colour once
; ---------------------------------------------------------------------------
; sub_24B8: Pal_DecColor:
.UpdateColour:
		move.w	(a0),d5
		move.w	d5,d1
		move.b	d1,d2
		move.b	d1,d3
		andi.b	#$0E,d3
		beq	.updategreen
		subq.b	#$02,d5

; loc_24C8: Pal_DecGreen:
.updategreen:
	andi.b	#$E0,d2
	beq	.updateblue
	subi.b	#$20,d5

; loc_24D6: Pal_DecBlue:
.updateblue:
	andi.w	#$0E00,d1
	beq	.updatenone
	subi.w	#$0200,d5

; loc_24E4: Pal_DecNone:
.updatenone:
	move.w	d5,(a0)+
	rts


; ---------------------------------------------------------------------------
; Subroutine to fade in from white
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_24E8: Pal_MakeWhite:
Pal_FadeFromWhite:
	move.w	#$3F,(Palette_fade_range).w
	moveq	#0,d0
	lea	(Normal_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	move.w	#$EEE,d1

	move.b	(Palette_fade_length).w,d0
.palettewrite:
	move.w	d1,(a0)+
	dbf	d0,.palettewrite
		moveq	#$0E,d4
		moveq	#$00,d6
.nextframe:
	move.b	#VintID_Fade,(Vint_routine).w
	bsr.w	WaitForVint
		bchg	#$00,d6
		beq	.nextframe
	bsr.s	.UpdateAllColours
		subq.b	#$02,d4
		bne	.nextframe
		move.b	#$12,(Vint_routine).w
		bra	WaitForVint

; ---------------------------------------------------------------------------
; Subroutine to update all colours once
; ---------------------------------------------------------------------------
; sub_2522: Pal_WhiteToBlack:
.UpdateAllColours:
	; Update above-water palette
	moveq	#0,d0
	lea	(Normal_palette).w,a0
	lea	(Target_palette).w,a1
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	adda.w	d0,a1

	move.b	(Palette_fade_length).w,d0
.nextcolour:
	bsr.s	.UpdateColour
	dbf	d0,.nextcolour

	tst.b	(Water_flag).w
	beq.s	.skipunderwater
	; Update underwater palette
	moveq	#0,d0
	lea	(Underwater_palette).w,a0
	lea	(Underwater_target_palette).w,a1
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0
	adda.w	d0,a1

	move.b	(Palette_fade_length).w,d0
.nextcolour2:
	bsr.s	.UpdateColour
	dbf	d0,.nextcolour2

.skipunderwater:
	rts

; ---------------------------------------------------------------------------
; Subroutine to update a single colour once
; ---------------------------------------------------------------------------
; sub_2562: Pal_DecColor2:
.UpdateColour:
		move.b	(a1),d5
		move.w	(a1)+,d1
		move.b	d1,d2
		lsr.b	#$04,d1
		andi.b	#$0E,d2
		move.w	(a0),d3
		cmp.b	d5,d4
		bls	A_NoBlue
		subi.w	#$0200,d3

A_NoBlue:
		cmp.b	d1,d4
		bls	A_NoGreen
		subi.b	#$20,d3

A_NoGreen:
		cmp.b	d2,d4
		bls	A_NoRed
		subq.b	#$02,d3

A_NoRed:
		move.w	d3,(a0)+
		rts
; ---------------------------------------------------------------------------
; Subroutine to fade out to white (used when you enter a special stage)
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

; sub_2592: Pal_MakeFlash:
Pal_FadeToWhite:
	move.w	#$3F,(Palette_fade_range).w
		moveq	#$07,d4					; MJ: set repeat times
		moveq	#$00,d6					; MJ: clear d6
.nextcframe:
	move.b	#VintID_Fade,(Vint_routine).w
	bsr.w	WaitForVint
		bchg	#$00,d6
		beq	.nextcframe
	bsr.s	.UpdateAllColours
	dbf	d4,.nextcframe
	rts
; End of function Pal_FadeToWhite

; ---------------------------------------------------------------------------
; Subroutine to update all colours once
; ---------------------------------------------------------------------------
; sub_25B2: Pal_ToWhite:
.UpdateAllColours:
	; Update above-water palette
	moveq	#0,d0
	lea	(Normal_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0

	move.b	(Palette_fade_length).w,d0
.nextcolour:
	bsr.s	.UpdateColour
	dbf	d0,.nextcolour

	; Notice how this one lacks a check for
	; if Water_flag is set, unlike Pal_FadeFromWhite?

	; Update underwater palette
	moveq	#0,d0
	lea	(Underwater_palette).w,a0
	move.b	(Palette_fade_start).w,d0
	adda.w	d0,a0

	move.b	(Palette_fade_length).w,d0
.nextcolour2:
	bsr.s	.UpdateColour
	dbf	d0,.nextcolour2

	rts

; ---------------------------------------------------------------------------
; Subroutine to update a single colour once
; ---------------------------------------------------------------------------
; sub_25E0: Pal_AddColor2:
.UpdateColour:
		move.w	(a0),d5
		move.w	d5,d1
		move.b	d1,d2
		move.b	d1,d3
		andi.w	#$0E00,d1
		cmpi.w	#$0E00,d1
		beq	a_NoBlue
		addi.w	#$0200,d5

a_NoBlue:
		andi.b	#$E0,d2
		cmpi.b	#$E0,d2
		beq	a_NoGreen
		addi.b	#$20,d5

a_NoGreen:
		andi.b	#$0E,d3
		cmpi.b	#$0E,d3
		beq	a_NoRed
		addq.b	#$02,d5

a_NoRed:
		move.w	d5,(a0)+
		rts
; End of function Pal_AddColor2

Title Screen Specific Routine

Unfortunately, because of how unique the title screen is, the above change will not apply very well to it, hence, we have to change the specific routines responsible for its special fading/control.

At routine "ObjC9", you'll find this code below:

ObjC9:
	moveq	#0,d0
	move.b	routine(a0),d0
	move.w	ObjC9_Index(pc,d0.w),d1
	jmp	ObjC9_Index(pc,d1.w)
; ===========================================================================
ObjC9_Index:	offsetTable
		offsetTableEntry.w ObjC9_Init	; 0
		offsetTableEntry.w ObjC9_Main	; 2
; ===========================================================================

ObjC9_Init:
	addq.b	#2,routine(a0)
	moveq	#0,d0
	move.b	subtype(a0),d0
	lea	(PaletteChangerDataIndex).l,a1
	adda.w	(a1,d0.w),a1
	move.l	(a1)+,ttlscrpalchanger_codeptr(a0)
	movea.l	(a1)+,a2
	move.b	(a1)+,d0
	move.w	d0,ttlscrpalchanger_start_offset(a0)
	lea	(Target_palette).w,a3
	adda.w	d0,a3
	move.b	(a1)+,d0
	move.w	d0,ttlscrpalchanger_length(a0)

-	move.w	(a2)+,(a3)+
	dbf	d0,-

	move.b	(a1)+,d0
	move.b	d0,ttlscrpalchanger_fadein_time_left(a0)
	move.b	d0,ttlscrpalchanger_fadein_time(a0)
	move.b	(a1)+,ttlscrpalchanger_fadein_amount(a0)
	rts
; ===========================================================================

ObjC9_Main:
	subq.b	#1,ttlscrpalchanger_fadein_time_left(a0)
	bpl.s	+
	move.b	ttlscrpalchanger_fadein_time(a0),ttlscrpalchanger_fadein_time_left(a0)
	subq.b	#1,ttlscrpalchanger_fadein_amount(a0)
	bmi.w	DeleteObject
	movea.l	ttlscrpalchanger_codeptr(a0),a2
	movea.l	a0,a3
	move.w	ttlscrpalchanger_length(a0),d0
	move.w	ttlscrpalchanger_start_offset(a0),d1
	lea	(Normal_palette).w,a0
	adda.w	d1,a0
	lea	(Target_palette).w,a1
	adda.w	d1,a1

-	jsr	(a2)	; dynamic call! to Pal_FadeFromBlack.UpdateColour, loc_1344C, or loc_1348A, assuming the PaletteChangerData pointers haven't been changed
	dbf	d0,-

	movea.l	a3,a0
+
	rts

Here are the necessary changes required for the fading to perform correctly:

ObjC9:
	moveq	#0,d0
	move.b	routine(a0),d0
	move.w	ObjC9_Index(pc,d0.w),d1
	jmp	ObjC9_Index(pc,d1.w)
; ===========================================================================
ObjC9_Index:	offsetTable
		offsetTableEntry.w ObjC9_Init	; 0
		offsetTableEntry.w ObjC9_Main	; 2
; ===========================================================================

ObjC9_Init:
	addq.b	#2,routine(a0)
	moveq	#0,d0
	move.b	subtype(a0),d0
	lea	(PaletteChangerDataIndex).l,a1
	adda.w	(a1,d0.w),a1
	move.l	(a1)+,ttlscrpalchanger_codeptr(a0)
	movea.l	(a1)+,a2
	move.b	(a1)+,d0
	move.w	d0,ttlscrpalchanger_start_offset(a0)
	lea	(Target_palette).w,a3
	adda.w	d0,a3
	move.b	(a1)+,d0
	move.w	d0,ttlscrpalchanger_length(a0)
        move.b  #$0E,objoff_33(a0)                      ; MJ: set fade counter

-	move.w	(a2)+,(a3)+
	dbf	d0,-

	move.b	(a1)+,d0
	move.b	d0,ttlscrpalchanger_fadein_time_left(a0)
	move.b	d0,ttlscrpalchanger_fadein_time(a0)
	move.b	(a1)+,ttlscrpalchanger_fadein_amount(a0)
	rts
; ===========================================================================

ObjC9_Main:
	subq.b	#1,ttlscrpalchanger_fadein_time_left(a0)
	bpl.s	+
	move.b	ttlscrpalchanger_fadein_time(a0),ttlscrpalchanger_fadein_time_left(a0)
	subq.b	#1,ttlscrpalchanger_fadein_amount(a0)
	bmi.w	DeleteObject
                moveq   #$00,d4                                 ; MJ: clear d4
                move.b  objoff_33(a0),d4                        ; MJ: load fade counter
                subq.b  #$02,objoff_33(a0)                      ; MJ: decrease fade counter
        movea.l ttlscrpalchanger_codeptr(a0),a2                                ; load routine to run
        movea.l a0,a3                                           ; store current object
        move.w  ttlscrpalchanger_length(a0),d0                                ; load length
        move.w  ttlscrpalchanger_start_offset(a0),d1                                ; load loadtooffset
	movea.l	ttlscrpalchanger_codeptr(a0),a2
	movea.l	a0,a3
	move.w	ttlscrpalchanger_length(a0),d0
	move.w	ttlscrpalchanger_start_offset(a0),d1
	lea	(Normal_palette).w,a0
	adda.w	d1,a0
	lea	(Target_palette).w,a1
	adda.w	d1,a1

-	jsr	(a2)	; dynamic call! to Pal_FadeFromBlack.UpdateColour, loc_1344C, or loc_1348A, assuming the PaletteChangerData pointers haven't been changed
	dbf	d0,-

	movea.l	a3,a0
+
	rts

This will ensure the object uses our new fading routines correctly, the next thing to do is at:

off_1338C:	C9PalInfo Pal_FadeFromBlack.UpdateColour, Pal_1342C, $60, $F,2,$15

Because of the nature of the new fade in routine being faster in the transition, we need to change the end value $15 to something smaller, say $07:

off_1338C:	C9PalInfo Pal_FadeFromBlack.UpdateColour, Pal_1342C, $60, $F,2,$07	; MJ: 15 to 7

This ensures the fading in routine is used correctly by the object, now there's one more fix to look at, if the player presses start to skip the transition halfway through fading in, the object continues fading in even though the entire full palette is loaded, to fix this goto "TitleScreen_SetFinalState":

-	move.l	(a1)+,(a2)+
	dbf	d6,-

	tst.b	objoff_30(a0)
	bne.s	+	; rts
	moveq	#signextendB(MusID_Title),d0 ; title music
	jsrto	(PlayMusic).l, JmpTo4_PlayMusic
+
	rts


And place this instruction just after the "dbf d6,-" instruction:

	sf.b	(Object_RAM+($100+$32)).w			; set fade counter to 00 (finish)

This will set the palette fading objects fade counter to 0 and set the object to delete itself, thus preventing it from messing with the palette if the transition was skipped.

If the above instructions have been followed carefully, then this will ensure that the colour fading scheme fades normally and smoothly, here is the new fading routine in action, 6 frames after fading in:

FadeInS2New.png
SCHG How-To Guide: Sonic the Hedgehog 2 (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix Super Sonic Bugs | Use Correct Height When Roll Jumping | Fix Jump Height Bug When Exiting Water | Fix Screen Boundary Spin Dash Bug | Correct Drowning Bugs | Fix Camera Y Position for Tails | Fix Tails Subanimation Error | Fix Tails' Respawn Speeds | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix Rexon Crash | Fix Monitor Collision Bug | Fix EHZ Deformation Bug | Correct CPZ Boss Attack Behavior | Fix Bug in ARZ Boss Arrow's Platform Behavior | Fix ARZ Boss Walking on Air Glitch | Fix ARZ Boss Sprite Behavior | Fix Multiple CNZ Boss Bugs | Fix HTZ Background Scrolling Mountains | Fix OOZ Launcher Speed Up Glitch | Fix DEZ Giant Mech Collision Glitch | Fix Boss Deconstruction Behavior | Fix Speed Bugs | Fix 14 Continues Cheat | Fix Debug Mode Crash | Fix 99+ Lives | Fix Sonic 2's Sega Screen
Design Choices
Remove the Air Speed Cap | Disable Floor Collision While Dying | Modify Super Sonic Transformation Methods & Behavior | Enable/Disable Tails in Certain Levels | Collide with Water After Being Hurt | Retain Rings When Returning at a Star Post | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Insert LZ Water Ripple Effect | Restore Lost CPZ Boss Feature | Prevent SCZ Tornado Spin Dash Death | Improve ObjectMove Subroutines | Port S3K Rings Manager | Port S3K Object Manager | Port S3K Priority Manager | Edit Level Order with ASM‎ | Alter Ring Requirements in Special Stages | Make Special Stage Characters Use Normal DPLCs | Speed Up Ring Loss Process | Change spike behaviour in Sonic 2
Adding Features
Create Insta-kill and High Jump Monitors | Create Clone and Special Stage Monitors | Port Knuckles
Sound Features
Expand Music Index to Start at $00 | Port Sonic 1 Sound Driver | Port Sonic 2 Clone Driver | Port Sonic 3 Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Expand the Music Index to Start at $00 (Sonic 2 Clone Driver Version) | Play Different Songs Per Act
Extending the Game
Extend the Level Index Past $10 | Extend the Level Select | Extend Water Tables | Add Extra Characters | Free Up 2 Universal SSTs