Actions

SCHG How-to

Dynamic Special Stage Walls system

From Sonic Retro

(Original guide by Mercury)

This guide allows you to have dynamically loaded wall art in the Special Stages. Visually, this makes little to no difference when playing the Special Stage, but it will free VRAM, leaving A LOT of room for other things, like the HUD, or new blocks/objects! This guide follows the current HG disassembly version of Sonic 1.

Part 1: Fixing Wall Sprites

So the very first thing we will do, aside from backing up our disassembly, is decompress the Special Stage wall art. Now, you could simply decompress the art, and that's that, right? Wrong! Because we are using uncompressed animated art, we will be changing how the art is mapped together. The absolute easiest way to do this is to have all of the wall sprites under ONE single mapping frame. So, that means that for every possible wall frame, we will want 16 tiles. This requires some slight editing of the wall art to make every possible wall frame centered within a 32x32 pixel image. This shouldn't be too hard to do, as it really only requires fixing up the tiles for the first frame. A visual reference will be provided in the near future, to aid you. After this is done, you will want to save this art uncompressed, and relocate it to the artunc folder. With that finished, we want the asm pointing to the new art location. In sonic.asm, search for the following pointer:

Nem_SSWalls:	incbin	"artnem\Special Walls.bin" ; special stage walls
		even

and replace it with this:

Art_SSWalls:	incbin	"artunc\Special Walls.bin" ; special stage walls
		even

You don't need to change the pointer to "Art_SSWalls", but if you do, just know that you will need to perform a "Find & Replace" to correct any possible errors.

Now, we are going to change the mappings for the wall art. They currently look something like this:

; ---------------------------------------------------------------------------
; Sprite mappings - walls of the special stage
; ---------------------------------------------------------------------------
Map_SSWalls:	dc.w byte_2C584-Map_SSWalls, byte_2C58A-Map_SSWalls
		dc.w byte_2C590-Map_SSWalls, byte_2C596-Map_SSWalls
		dc.w byte_2C59C-Map_SSWalls, byte_2C5A2-Map_SSWalls
		dc.w byte_2C5A8-Map_SSWalls, byte_2C5AE-Map_SSWalls
		dc.w byte_2C5B4-Map_SSWalls, byte_2C5BA-Map_SSWalls
		dc.w byte_2C5C0-Map_SSWalls, byte_2C5C6-Map_SSWalls
		dc.w byte_2C5CC-Map_SSWalls, byte_2C5D2-Map_SSWalls
		dc.w byte_2C5D8-Map_SSWalls, byte_2C5DE-Map_SSWalls
byte_2C584:	dc.b 1
		dc.b $F4, $A, 0, 0, $F4
byte_2C58A:	dc.b 1
		dc.b $F0, $F, 0, 9, $F0
byte_2C590:	dc.b 1
		dc.b $F0, $F, 0, $19, $F0
byte_2C596:	dc.b 1
		dc.b $F0, $F, 0, $29, $F0
byte_2C59C:	dc.b 1
		dc.b $F0, $F, 0, $39, $F0
byte_2C5A2:	dc.b 1
		dc.b $F0, $F, 0, $49, $F0
byte_2C5A8:	dc.b 1
		dc.b $F0, $F, 0, $59, $F0
byte_2C5AE:	dc.b 1
		dc.b $F0, $F, 0, $69, $F0
byte_2C5B4:	dc.b 1
		dc.b $F0, $F, 0, $79, $F0
byte_2C5BA:	dc.b 1
		dc.b $F0, $F, 0, $89, $F0
byte_2C5C0:	dc.b 1
		dc.b $F0, $F, 0, $99, $F0
byte_2C5C6:	dc.b 1
		dc.b $F0, $F, 0, $A9, $F0
byte_2C5CC:	dc.b 1
		dc.b $F0, $F, 0, $B9, $F0
byte_2C5D2:	dc.b 1
		dc.b $F0, $F, 0, $C9, $F0
byte_2C5D8:	dc.b 1
		dc.b $F0, $F, 0, $D9, $F0
byte_2C5DE:	dc.b 1
		dc.b $F0, $F, 0, $E9, $F0
		even

These are mappings for 16 frames. All of them but the first one are 4 tiles x 4 tiles, with the first frame being a 3x3 frame. This requires 249 ($F9) tiles loaded into VRAM to work. What we are going to do is consolidate this down to one single mapping. It will look like this:

; ---------------------------------------------------------------------------
; Sprite mappings - walls of the special stage
; ---------------------------------------------------------------------------
Map_SSWalls:	dc.w byte_2C584-Map_SSWalls
byte_2C584:	dc.b 1
		dc.b $F0, $F, 0, 0, $F0
		even

Now every frame can be created with one single mapping. Each one will be a 4x4 sprite, and will only require 16 tiles to be loaded into VRAM to make it happen. As the frame change is needed, the game simply needs to change the 16 tiles that are loaded into VRAM, and because EVERY wall tile will always be on the same frame at the same time, that's that! Now let's get the game to do just that!

Part 2: Code Tweaking

Next, we need to make some tweaks to the code. Go to VBla_0A: and find @nochg:. Just below it, insert this code:

		cmpi.b	#96,(v_hbla_line).w
		bcc.s	@update
		bra.w	@end

	@update:
		jsr	SS_LoadWalls

Now let's go to VBla_16:, and, again, find @nochg:. Just below it, insert the same code block as above.

Now we need to go to GM_Special: and find SS_ClrNemRam:. You will see this chunk of code.

	SS_ClrNemRam:
		move.l	d0,(a1)+
		dbf	d1,SS_ClrNemRam	; clear	Nemesis	buffer

		clr.b	(f_wtr_state).w
		clr.w	(f_restart).w
		moveq	#palid_Special,d0
		bsr.w	PalLoad1	; load special stage palette
		jsr	SS_Load		; load SS layout data
		move.l	#0,(v_screenposx).w
		move.l	#0,(v_screenposy).w
		move.b	#9,(v_objspace).w ; load special stage Sonic object
		bsr.w	PalCycle_SS
		clr.w	(v_ssangle).w	; set stage angle to "upright"

Just below the line that loads Special Stage Sonic, add this line:

		move.b	#$FF,(v_ssangleprev).w	; fill previous angle with obviously false value to force an update

This v_ssangleprev is a new RAM variable, so we are going to need to add an equate for it. In Variables.asm, add it wherever you have a free byte of RAM.

Next we are going to SS_AniWallsRings: to change how the walls are animated. Remove the following chunk right at the start of the section:

		lea	($FF400C).l,a1
		moveq	#0,d0
		move.b	(v_ssangle).w,d0
		lsr.b	#2,d0
		andi.w	#$F,d0
		moveq	#$23,d1

loc_1B2A4:
		move.w	d0,(a1)
		addq.w	#8,a1
		dbf	d1,loc_1B2A4

Next, go to the end of the section where you will find a data table that looks like this:

; ===========================================================================
SS_WaRiVramSet:	dc.w $142, $6142, $142,	$142, $142, $142, $142,	$6142
		dc.w $142, $6142, $142,	$142, $142, $142, $142,	$6142
		...

Just above that, we are going to create a new subroutine to load the wall art dynamically. Insert this code above the table:

SS_LoadWalls:
		moveq	#0,d0
		move.b	(v_ssangle).w,d0	; get the Special Stage angle
		lsr.b	#2,d0			; modify so it can be used as a frame ID
		andi.w	#$F,d0
		cmp.b	(v_ssangleprev).w,d0	; does the modified angle match the recorded value?
		beq.s	@return			; if so, branch

		lea	($C00000).l,a6
		lea	(Nem_SSWalls).l,a1	; load wall art
		move.w	d0,d1
		lsl.w	#8,d1
		add.w	d1,d1
		add.w	d1,a1

		locVRAM	$2840			; VRAM address

		move.w	#$F,d1			; number of 8x8 tiles
		jsr	LoadTiles
		move.b	d0,(v_ssangleprev).w	; record the modified angle for comparison

	@return:
		rts

SS_LoadWalls uses the new RAM variable to determine when new tiles are loaded into VRAM. Every time tiles are loaded, $10 tiles will be loaded to VRAM at address $2840. You can find this in a Regen VRAM dump at location 142, the same location that the tileset was previously loaded to. The big difference now, is that you have room for an additional 233 ($E9) tiles for use. You can use that for plenty of things, most notably a HUD! Now there is one final thing we need to do. We still have a Pattern Loading Cue for Special Stage walls to be loaded in this same spot... Having this means that all 256 tiles will be loaded to VRAM, meaning that not only will we not have the desired free VRAM, but even worse, the Bumper art will be overwritten! To fix this, simply go to inc\Pattern Load Cues.asm and at PLC_SpecialStage:, remove the following line:

		plcm	Nem_SSWalls, $2840	; walls

That should be it! Enjoy the freed VRAM! Also note that another advantage to this method is that you can freely add more art to make more in-between frames to make the rotation even smoother!

(Additional fix by Cinossu)
Speaking of smoother rotation, you may also want to implement this change as well. Go to SS_ShowLayout: and take out this line:

		andi.b	#$FC,d0
SCHG How-To Guide: Sonic the Hedgehog (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix the SEGA Sound | Display the Press Start Button Text | Fix the Level Select Menu | Fix the Hidden Points Bug | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix the Walk-Jump Bug | Correct Drowning Bugs | Fix the Death Boundary Bug | Fix the Camera Follow Bug | Fix Song Restoration Bugs | Fix the HUD Blinking | Fix the Level Select Graphics Bug | Fix a remember sprite related bug
Changing Design Choices
Change Spike Behavior | Collide with Water After Being Hurt | Fix Special Stage Jumping Physics | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Remove the Speed Cap | Port the REV01 Background Effects | Port Sonic 2's Level Art Loader | Retain Rings Between Acts | Add Sonic 2 (Simon Wai Prototype) Level Select | Improve ObjectMove Subroutines | Add Sonic 2 Level Select | Collide with Water After Being Hurt | Smooth Out Rotation in Special Stages
Adding Features
Add Spin Dash ( Part 1 (GitHub)/(Hivebrain) / Part 2 / Part 3 / Part 4 ) | Add Eggman Monitor | Add Super Sonic | Add Extended Camera | Add the Air Roll | Add 6-Button Support
Sound Features
Expand the Sound Index | Play Different Songs Per Act | Port Sonic 2 Final Sound Driver | Port Sonic 3's Sound Driver | Change The SEGA Sound | Correct PAL Music Tempo
Extending the Game
Load Chunks From ROM | Add Extra Characters | Make an Alternative Title Screen | Use Dynamic Tilesets | Make GHZ Load Alternate Art | Make Ending Load Alternate Art | Add a New Zone | Set Up the Goggle Monitor | Add New Moves | Add a Dynamic Collision System | Dynamic Special Stage Walls System | Extend Sprite Mappings and Art Limit | Enigma Credits | Use Dynamic Palettes
Miscellaneous
Convert the Hivebrain 2005 Disassembly to ASM68K
Split Disassembly Guides
Set Up a Split Disassembly | Basic Level Editing | Basic Art Editing | Basic ASM Editing (Spin Dash)

|Dynamic Special Stage Walls system in Sonic 1]]