Before we proceed any further in our analysis, a brief digression. On the subject of
the Animate_Tiles function, reader
Silver Sonic 1992 commented:
Lava Reef zone has some type of dynamically reloading tiles. Is it just garbage data?
I completely missed this the first time around. Amidst all the pointers to null routines, the
Offs_AniFunc table actually contains a pointer to a properly-defined animation routine for Lava Reef Zone 1:
dc.w AnimateTiles_NULL-Offs_AniFunc
dc.w AniPLC_ALZ-Offs_AniFunc
dc.w AnimateTiles_LRZ1-Offs_AniFunc
dc.w AniPLC_ALZ-Offs_AniFunc
dc.w AnimateTiles_NULL-Offs_AniFunc
dc.w AniPLC_ALZ-Offs_AniFunc
Notably, apart from some tweaks to the target VRAM offsets to make the routine work also for act 2, as well as the fact that
ArtUnc_AniLRZ__BG2 seemingly grew to 1.5x its size sometime after Sonic 3's release, the bulk of the routine is quite similar to the code found in Sonic & Knuckles:
AnimateTiles_LRZ1: AnimateTiles_LRZ1:
move.w #$6400,d4
move.w #$6880,d6
bra.s loc_282D0
; ---------------------------------------------
AnimateTiles_LRZ2:
move.w #$6400,d4
move.w #$6880,d6
loc_282D0:
lea (Anim_Counters).w,a3 lea (Anim_Counters).w,a3
moveq #0,d0 moveq #0,d0
move.w ($FFFFEEE4).w,d0 move.w ($FFFFEEE4).w,d0
sub.w (Camera_X_pos_BG_copy).w,d0 sub.w (Camera_X_pos_BG_copy).w,d0
subq.w #1,d0
divu.w #$30,d0 divu.w #$30,d0
swap d0 swap d0
cmp.b 1(a3),d0 cmp.b 1(a3),d0
beq.s loc_27440 beq.s loc_2833C
move.b d0,1(a3) move.b d0,1(a3)
moveq #0,d1 moveq #0,d1
move.w d0,d2 move.w d0,d2
andi.w #7,d0 andi.w #7,d0
lsl.w #7,d0 lsl.w #7,d0
move.w d0,d1 move.w d0,d1
lsl.w #3,d0 lsl.w #3,d0
add.w d0,d1 add.w d0,d1
move.l d1,d5 move.l d1,d5
andi.w #$38,d2 andi.w #$38,d2
move.w d2,d0 move.w d2,d0
lsl.w #3,d2 lsl.w #3,d2
add.w d2,d1 add.w d2,d1
add.w d2,d2 add.w d2,d2
add.w d2,d1 add.w d2,d1
lsr.w #1,d0 lsr.w #1,d0
lea word_27446(pc,d0.w),a4 lea word_2834C(pc,d0.w),a4
lea (ArtUnc_AniALZ).l,a0 lea (ArtUnc_AniLRZ__BG).l,a0
move.w #$6020,d4
add.l a0,d1 add.l a0,d1
move.w d4,d2 move.w d4,d2
move.w (a4)+,d3 move.w (a4)+,d3
add.w d3,d4 add.w d3,d4
add.w d3,d4 add.w d3,d4
jsr (Add_To_DMA_Queue).l jsr (Add_To_DMA_Queue).l
move.l d5,d1 move.l d5,d1
add.l a0,d1 add.l a0,d1
move.w d4,d2 move.w d4,d2
move.w (a4)+,d3 move.w (a4)+,d3
beq.s loc_27440 beq.s loc_2833C
jsr (Add_To_DMA_Queue).l jsr (Add_To_DMA_Queue).l
loc_27440: loc_2833C:
cmpi.b #$16,(Current_zone).w
beq.s locret_2834A
addq.w #2,a3 addq.w #2,a3
bra.w loc_2745E bra.w loc_28364
; --------------------------------------------- ; ---------------------------------------------
locret_2834A:
rts
; ---------------------------------------------
word_27446: word_2834C:
dc.w $240, 0 dc.w $240, 0
dc.w $1E0, $60 dc.w $1E0, $60
dc.w $180, $C0 dc.w $180, $C0
dc.w $120, $120 dc.w $120, $120
dc.w $C0, $180 dc.w $C0, $180
dc.w $60, $1E0 dc.w $60, $1E0
; --------------------------------------------- ; ---------------------------------------------
loc_2745E: loc_28364:
moveq #0,d0 moveq #0,d0
move.w ($FFFFEEE2).w,d0 move.w ($FFFFEEE2).w,d0
sub.w (Camera_X_pos_BG_copy).w,d0 sub.w (Camera_X_pos_BG_copy).w,d0
andi.w #$1F,d0 andi.w #$1F,d0
cmp.b 1(a3),d0 cmp.b 1(a3),d0
beq.s locret_274BE beq.s loc_283CC
move.b d0,1(a3) move.b d0,1(a3)
moveq #0,d1 moveq #0,d1
move.w d0,d2 move.w d0,d2
andi.w #7,d0 andi.w #7,d0
lsl.w #8,d0 lsl.w #7,d0
move.w d0,d1
add.w d0,d0
add.w d1,d0
move.w d0,d1 move.w d0,d1
move.l d1,d5 move.l d1,d5
andi.w #$18,d2 andi.w #$18,d2
move.w d2,d0 move.w d2,d0
lsl.w #3,d2 lsl.w #2,d2
add.w d2,d1
add.w d2,d2
add.w d2,d1 add.w d2,d1
lsr.w #1,d0 lsr.w #1,d0
lea word_274C0(pc,d0.w),a4 lea word_283D2(pc,d0.w),a4
lea (ArtUnc_AniALZ).l,a0 lea (ArtUnc_AniLRZ__BG2).l,a0
move.w #$64A0,d4 move.w d6,d4
add.l a0,d1 add.l a0,d1
move.w d4,d2 move.w d4,d2
move.w (a4)+,d3 move.w (a4)+,d3
add.w d3,d4 add.w d3,d4
add.w d3,d4 add.w d3,d4
jsr (Add_To_DMA_Queue).l jsr (Add_To_DMA_Queue).l
move.l d5,d1 move.l d5,d1
add.l a0,d1 add.l a0,d1
move.w d4,d2 move.w d4,d2
move.w (a4)+,d3 move.w (a4)+,d3
beq.s locret_274BE beq.s loc_283CC
jsr (Add_To_DMA_Queue).l jsr (Add_To_DMA_Queue).l
locret_274BE: loc_283CC:
addq.w #2,a3
rts bra.w loc_286E8
; --------------------------------------------- ; ---------------------------------------------
word_274C0: word_283D2:
dc.w $80, 0 dc.w $C0, 0
dc.w $60, $20 dc.w $90, $30
dc.w $40, $40 dc.w $60, $60
dc.w $20, $60 dc.w $30, $90
; --------------------------------------------- ; ---------------------------------------------
Much like what happened with the level load block though, the uncompressed art used by this routine was completely wiped from the Sonic 3 ROM, leaving the code
pointing at whatever data came next. In this case it's
ArtUnc_AniALZ, which is uncompressed art normally used by
Azure Lake's animation routine.
At this point, I'm convinced Sonic 3 alone is just a published Sonic 3&K prototype.
ReplyDeleteUh, that's exactly what it is. The magic is in Sonic & Knuckles, where they had to design a way to somehow patch the prototype into a complete game.
DeleteMakes you wonder how the game would have worked if it was never split.
DeleteWhen you said all the Sonic & Knuckles zones contain an unaltered copy of Angel Island Zone's level load block; ID 0D instead seems to point at a few other offsets for some reason.
ReplyDeleteGood catch. While all the other slots are pointing at the AIZ1 intro graphics like the actual AIZ slot, zone $0D act 1 -- the ending level where Sonic and Tails fly by Angel Island in the Tornado -- is already pointing at the main level graphics as it does in Sonic & Knuckles.
DeleteSo they must have planned to use Act 1 originally, instead of Act 2 like in Sonic & Knuckles.
DeleteAh shoot, you're right. Act 2 is the one used for the ending scene in Sonic & Knuckles. What's happening is the LoadLevelLoadBlock function (which loads the level load block) loads stuff from zone $D instead if the current level is AIZ1 and any star post has been set (including the fake one at the start of the act).
DeleteI should really get around to blogging all of that.
Got question for you, whats the deal in ice cap with snowpile that transports you to launch base?
ReplyDelete