I also recall a glitch where Sonic will very briefly change to a different color, just after turning Super. It _may_ have just been an emulator bug circa 2000, but I've been playing Complete so long that it's hard to remember. Any insight on this?Chances are it was no emulator bug. There's definitely something wrong with the palette transition during Sonic's super transformation, but it only manifests itself after you've reverted back to normal Sonic at least once in the current level.
Here's the code which handles the palette transition:
SuperHyper_PalCycle_FadeIn: ; increment palette frame and update Sonic's palette lea (PalCycle_SuperSonic).l,a0 move.w (Palette_frame).w,d0 addq.w #6,(Palette_frame).w ; 1 palette entry = 1 word, Sonic uses 3 shades of blue cmpi.w #$24,(Palette_frame).w ; has palette cycle reached the 6th frame? blo.s SuperHyper_PalCycle_SonicApply ; if not, branch move.b #-1,(Super_Hyper_palette_status).w ; mark fade-in as done move.b #0,(Player_1+object_control).w ; restore Sonic's movement SuperHyper_PalCycle_SonicApply: lea (Normal_palette+$4).w,a1 move.l (a0,d0.w),(a1)+ ; Write first two palette entries move.w 4(a0,d0.w),(a1) ; Write last palette entry ...The disassembly comments already spell out most of it, so let's focus on that Palette_frame RAM variable. It is a word value used as an offset into the PalCycle_SuperSonic array, which contains Sonic's transformation colors, followed by Super Sonic's color cycle starting at offset $24:
PalCycle_SuperSonic: dc.w $E66,$C42,$822 ; 0 dc.w $E88,$C66,$844 ; 6 dc.w $EAA,$C88,$A66 ; $C dc.w $ECC,$EAA,$C88 ; $12 dc.w $EEE,$ECC,$EAA ; $18 dc.w $EEE,$EEE,$EEE ; $1E dc.w $CEE,$CEE,$AEE ; $24 dc.w $AEE,$8EE,$6CC ; $2A dc.w $8EE,$0EE,$0AA ; $30Once Palette_frame reaches $24, the transition code stops being called, and the code for the main palette cycle takes over, incrementing Palette_frame every 7 frames and setting it back to $24 when it goes outside the array's bounds:
SuperHyper_PalCycle_SuperSonic: ; run frame timer subq.b #1,(Palette_timer).w bpl.w locret_37EC move.b #6,(Palette_timer).w ; increment palette frame and update Sonic's palette lea (PalCycle_SuperSonic).l,a0 move.w (Palette_frame).w,d0 addq.w #6,(Palette_frame).w ; next frame cmpi.w #$36,(Palette_frame).w ; is it the last frame? blo.s loc_3898 ; if not, branch move.w #$24,(Palette_frame).w ; reset frame counter loc_3898: bra.w SuperHyper_PalCycle_SonicApplyNote how Palette_frame is post-incremented, that is, its value is being copied to d0 before the variable is incremented, so PalCycle_SuperSonic is always being accessed using a stale offset.
Finally, when the rings run out, the code below runs the transition colors in reverse, ending on Sonic's regular palette:
; decrement palette frame and update Sonic's palette lea (PalCycle_SuperSonic).l,a0 move.w (Palette_frame).w,d0 subq.w #6,(Palette_frame).w ; previous frame bhs.s loc_381E ; branch, if it isn't the first frame ; The following line should be a move.w ; This causes the fade-in to be bugged move.b #0,(Palette_frame).w move.b #0,(Super_Hyper_palette_status).w ; 0 = off loc_381E: bra.s SuperHyper_PalCycle_SonicApplyHere, a nasty consequence of using stale values rears its ugly head. After d0 has been set to zero, thus pointing at the first set of colors in the PalCycle_SuperSonic color array, Palette_frame is decremented one too many times to -6, or $FFFA, and must be manually set back to zero, so that it's ready next time Sonic transforms.
However, as the disassembly comments point out, the developers made a mistake, and instead of writing a word value of zero, the code writes a single zero byte to the Palette_frame RAM address, overwriting the variable's first byte. This corrupts the value $FFFA into $00FA, and so next time the transition code runs, it will read three colors from offset $FA of PalCycle_SuperSonic, which happens to straddle the color arrays for Hyper Sonic and Super Tails:
dc.w $EEE,$EEE,$EEE ; $F6 PalCycle_SuperTails: dc.w $0AE,$08E,$46A ; $FC dc.w $4CE,$2AE,$46A ; $102The result is that for a couple of frames, Sonic is a mix of white and Tails' oranges, which actually doesn't look that bad:
The transition code continues: Palette_frame is incremented from $FA to $100, which is definitely greater than $24, so the main palette cycle takes over, incrementing Palette_frame every 7 frames and setting it back to $24 when it leaves the array's bounds. $100 is definitely outside the array's bounds, so Palette_frame is reset.
Remember how PalCycle_SuperSonic is always accessed using a stale offset, though? This time around, the offset is $100, meaning that for the following 7 frames, Sonic will use the next three colors from Super Tails' palette cycle, which happens to place dark brown in an unfortunate place:
But wait, it's even better in standalone Sonic 3. Hyper Sonic and Super Tails don't exist yet, and as a result, offsets $FA and $100 just happen to land smack dab in the middle of the Pal_FromBlack function:
... moveq #0,d0 ; $FA lea (Water_palette).w,a0 ; $FC lea (Target_water_palette).w,a1 ; $100 move.b (Palette_fade_info).w,d0 ; $104 ...Translating the instructions into processor opcodes, we get the following word values:
ROM:000032BC 7000 ; $FA ROM:000032BE 41F8 F080 ; $FC ROM:000032C2 43F8 F000 ; $100 ROM:000032CA 1038 F626 ; $104Recalling that Mega Drive colors follow the bit pattern 0000BBB0GGG0RRR0, those word values become the colors
dc.w $000,$0E8,$080 ; $FA dc.w $2E8,$000,$028 ; $100which in turn translate to black / lime green / dark green, and lime green / black / dark red.
It's our boy, Ashura!
Can I call him Scourge or Super Scourge instead?
ReplyDeleteI was going to ask what happens in Sonic 3 but I see you already answered that.
ReplyDeleteThis is a bit unrelated, but does the RAM address that controls the super emerald count do anything in Sonic 3?
This comment has been removed by the author.
ReplyDeleteMost questions of the form "what happens when you do this thing" can be answered by doing the thing and observing the results.
DeleteNot sure if this is relevant to the blog, but the same bug exists in Sonic 2. Also Sonic's head is sheared off his body in the last few frames of the animation.
ReplyDeleteYeah, the bug was originally introduced in Sonic 2.
DeleteConcerning Sonic's head, I mentioned this in the preceding post. It's almost like I try to imbue some continuity into this blog. :)
Yup. Just my luck. I post the comment, get distracted for a few hours, come back to read the next post, and see it's too late to amend the comment =P.
ReplyDeleteThanks for answering! The dark green palette in standalone Sonic 3 was definitely what I was remembering.
ReplyDeleteSo interesting thing, when one of the frames of the transformation is used in launch base underwater, it changes sonic's color to a black with red streaks on his head, ironically it looks like Shadow.
ReplyDeleteMaybe a look into how water palettes work with the super palettes?
Scourge in an official Sonic game, done PROPERLY AS AN INTENDED CHARACTER; that would be interesting...
ReplyDelete