Friday, January 12, 2018

Can't escape!

Reader Silver Sonic 1992 asks:
Also, do you know what the "Special Stage 1" is for in the level select? Is it remnant of the super emerald special stages?
Doesn't look like it, but it's interesting nonetheless.

The special stage entries in the level select actually refer to an invalid zone ID, zone $40. When this zone is selected, a special handler prevents the invalid zone from getting loaded, and instead modifies the value of the Game_mode RAM variable, setting it to $2C for act 1, and $34 for act 2:
LevelSelect_PressStart:
    move.w  ($FFFFFF82).w,d0
    add.w   d0,d0
    move.w  LS_Level_Order(pc,d0.w),d0
    bmi.w   LevelSelect_Return
    cmpi.w  #$5555,d0                   ; This is used for the S&K zones
    beq.w   LevelSelect_Main
    cmpi.w  #$4001,d0                   ; Is Special Stage 2 selected?
    beq.w   LevelSelect_SpecialStage    ; If so, branch
    cmpi.w  #$4000,d0                   ; Is Special Stage 1 selected?
    bne.w   LevelSelect_StartZone       ; If not, branch
    move.b  #$2C,(Game_mode).w
    rts
; ---------------------------------------------------------------------------

LevelSelect_SpecialStage:
    move.b  #$34,(Game_mode).w
    rts
Once this variable is changed, the game instantly switches away from the current mode, $28 (level select) to whichever one got picked above. In Sonic & Knuckles, mode $2C refers to the title screen for the Blue Sphere bonus game, but in S3A, both it and $34 point at the special stage game mode.
GameModes:
    dc.l Sega_Screen                ;   0
    dc.l Title_Screen               ;   4
    dc.l Level                      ;   8
    dc.l Level                      ;  $C
    dc.l JumpToSegaScreen           ; $10
    dc.l ContinueScreen             ; $14
    dc.l JumpToSegaScreen           ; $18
    dc.l LevelSelect_S2Options      ; $1C
    dc.l S3Credits                  ; $20
    dc.l LevelSelect_S2Options      ; $24
    dc.l LevelSelect_S2Options      ; $28
    dc.l SpecialStage               ; $2C
    dc.l SpecialStage               ; $30
    dc.l SpecialStage               ; $34
    dc.l Competition_Menu           ; $38
    dc.l Competition_PlayerSelect   ; $3C
    dc.l Competition_LevelSelect    ; $40
    dc.l Competition_Results        ; $44
    dc.l SpecialStage_Results       ; $48
    dc.l SaveScreen                 ; $4C
    dc.l TimeAttack_Records         ; $50
At the end of the special stage mode's main loop, the Game_mode RAM variable is checked. If it's anything other than $34, then instead of jumping back to the start of the loop, execution progresses into a second loop, in which the screen progressively fades to white. The equivalent code can be found at loc_851A in the S&K disassembly.
    cmpi.b  #$34,(Game_mode).w
    beq.s   loc_77D2
    tst.w   (Demo_mode_flag).w
    beq.s   loc_7828
    move.b  #0,(Game_mode).w

loc_7828:
    move.w  #$3C,(Demo_timer).w
    move.w  #$3F,(Palette_fade_info).w
    clr.w   ($FFFFF794).w

loc_7838:
    move.b  #$1C,(V_int_routine).w
    bsr.w   Wait_VSync
    jsr     (Process_Sprites).l
    bsr.w   Animate_SSRings
    bsr.w   sub_8C1A
    jsr     (Render_Sprites).l
    jsr     Draw_SSSprites(pc)
    bsr.w   sub_8B9A
    bsr.w   sub_89E2
    bsr.w   Process_Nem_Queue_Init
    jsr     (Process_Kos_Module_Queue).l
    subq.w  #1,($FFFFF794).w
    bpl.s   loc_787C
    move.w  #2,($FFFFF794).w
    bsr.w   Pal_ToWhite

loc_787C:
    tst.w   (Demo_timer).w
    bne.s   loc_7838
Now, if the debug cheat is enabled, then pressing the A button while the game is paused makes the game jump directly to the level select. This is achieved by setting the Game_mode variable to $28, the value for the level select mode:
Pause_Loop:
    move.b  #$10,(V_int_routine).w
    bsr.w   Wait_VSync
    tst.b   (Slow_motion_flag).w
    beq.s   Pause_NoSlowMo
    btst    #6,(Ctrl_1_pressed).w
    beq.s   Pause_ChkFrameAdvance   ; branch if A isn't pressed
    move.b  #$28,(Game_mode).w
    nop
    bra.s   Pause_ResumeMusic
And that's what that check in the special stage mode was for. When the Game_mode variable changes from its regular value of $34, the game knows that it has to exit the special stage and go someplace else (for instance, the level select) so it begins fading to white, and once the fade is over, it jumps to the new game mode.

Except that, when we choose the "special stage 1" option, the Game_mode variable starts off with the value $2C. This calls up the special stage mode all the same, except the check fails immediately, triggering a fade out. Once the fade is over, the game jumps to the "new" game mode, $2C... which calls up the special stage mode all over again.

The cherry on top of the cake is this code at the end of that second loop:
loc_787C:
    tst.w   (Demo_timer).w
    bne.s   loc_7838
    addq.b  #1,($Current_special_stage).w
    cmpi.b  #7,(Current_special_stage).w
    bcs.s   locret_7894
    move.b  #0,(Current_special_stage).w

locret_7894:
    rts
This code ensures that the current stage value is properly incremented when the player exits the stage using the debug shortcut. That way, next time a special stage is accessed, the game will advance to the next stage in line, just like if the previous one had completed normally.

Inadvertently, this causes the "special stage 1" bug to cycle through each stage in an infinite loop. In Sonic & Knuckles, not only was the bug fixed, but the debug shortcut now leads to the SEGA screen, so the above code was removed.

6 comments:

  1. Was there a reason they didn't make it do nothing when selected?

    ReplyDelete
    Replies
    1. My guess is that mode $2C did something interesting at one point, but the developers just forgot to remove the entry point from the level select like they did with the S&K stages.

      Delete
    2. Perhaps mode $2C was Blue Sphere before that mode was shifted to S&K and removed from S3A?

      Delete
    3. Is the level select screen from an early build that predates everything after Sandopolis? It would be quite helpful to have a prototype...

      Delete
  2. Kinda unrelated, I know, but this just popped into my head a while ago...

    Why does the Lava Reef Giant Hand miniboss crush characters when they are in their Super form and spindashing? And I think Tails always get crushed too. Is it because of their size? It's kinda odd to have a miniboss being able to kill you in such an unexpected way...

    ReplyDelete
  3. Could you talk about the removed water in Launch Base act 1?

    ReplyDelete