Sonic and Sega Retro Message Board: How to port Sonic 2's level art loader to Sonic 1 - Sonic and Sega Retro Message Board

Jump to content

Hey there, Guest!  (Log In · Register) Help
  • 2 Pages +
  • 1
  • 2
    Locked
    Locked Forum

How to port Sonic 2's level art loader to Sonic 1

#1 User is offline Clownacy 

  Posted 12 April 2014 - 05:22 PM

  • Needs to make an avatar
  • Posts: 309
  • Joined: 06-July 13
  • Gender:Male
  • Location:Englandland
Fancy giving your loading screens a speed boost? Sonic 1's Nemesis-compressed art adds quite the chunk to the loading times, and you'll find yourself staring at that blue oval more than you would that blue screen in Sonic 2. Porting Sonic 2's level art loading process cuts down a significant amount of waiting time, and here's how:

It's no secret, swapping out level-related decompression is trivial. You just change the compression of the mappings, go to LevelDataLoad and change the decompressors to something else, making sure to push whatever needed registers to the stack, and clear d0 if it's EniDec.

It's straightforward, even for a noob, granted that the noob's good with registers, but what about the art?

PLC_GHZ:	dc.w ((PLC_GHZ2-PLC_GHZ-2)/6)-1
		plcm	Nem_GHZ_1st, 0		; GHZ main patterns
		plcm	Nem_GHZ_2nd, $39A0	; GHZ secondary	patterns
		plcm	Nem_Stalk, $6B00	; flower stalk
		plcm	Nem_PplRock, $7A00	; purple rock
		plcm	Nem_Crabmeat, $8000	; crabmeat enemy
		plcm	Nem_Buzz, $8880		; buzz bomber enemy
		plcm	Nem_Chopper, $8F60	; chopper enemy
		plcm	Nem_Newtron, $9360	; newtron enemy



Oh crap, it's part of the PLCs.



Preparation
Grab yourself a KENS compressor. You can find some here (KENS) and here (KENSC). Get familiar with them. Also, you'll want to have ported QueueDMATransfer.


Recompression
First, we'll want to recompress our art from Nemesis to Kosinski, Sonic 2's standard level art compression.

Inside the 'artnem' folder of your disassembly, take the following files, and place them in a separate folder. However, keep a copy of '8x8 - GHZ1.bin' in artnem, and rename it to "8x8 - Title.bin"

  • 8x8 - GHZ1.bin
  • 8x8 - GHZ2.bin
  • 8x8 - LZ.bin
  • 8x8 - MZ.bin
  • 8x8 - SBZ.bin
  • 8x8 - SLZ.bin
  • 8x8 - SYZ.bin


Inside the new folder, decompress all of these files, then delete the Nemesis originals. Open both GHZ1 and GHZ2 in a hex editor, and append the latter to the end of the former, save the result as "8x8 - GHZ.bin". Delete GHZ1 and GHZ2. Now compress all of the files in Kosinski and move them to the 'artkos' folder.


Changing the pointers
Inside sonic.asm, change this...

Nem_GHZ_1st:	incbin	"artnem\8x8 - GH1.bin"	; GHZ primary patterns
		even
Nem_GHZ_2nd:	incbin	"artnem\8x8 - GHZ2.bin"	; GHZ secondary patterns
		even



...to this:

Nem_Title:	incbin	"artnem\8x8 - Title.bin"	; Title patterns
		even
Kos_GHZ:	incbin	"artkos\8x8 - GHZ.bin"	; GHZ primary patterns
		even



Follow this theme with the rest of the level art incbins: change the "Nem_" prefix to "Kos_", and change "artnem" to "artkos".

Now find this...

		lea	(Nem_GHZ_1st).l,a0 ; load GHZ patterns
		bsr.w	NemDec



...and change it to this:

		lea	(Nem_Title).l,a0 ; load Title patterns
		bsr.w	NemDec



This is simply correcting the title screen's pointer. Like in Sonic 2, the title art is still Nemesis-compressed.

Inside the '_inc' folder, open Pattern Load Cues.asm and LevelHeaders.asm.

In LevelHeaders.asm, you can see entries for the level art, but, like the music entries, they are unused. As seen above, the level art is actually chosen by the Pattern Load Cues. Sonic 2 would go on to actually use these entries, and, after the completion of this guide, your hack will too. But first, let's correct them to match our new labels.

  • Nem_GHZ_2nd → Kos_GHZ
  • Nem_LZ → Kos_LZ
  • Nem_MZ → Kos_MZ
  • Nem_SLZ → Kos_SLZ
  • Nem_SYZ → Kos_SYZ
  • Nem_SBZ → Kos_SBZ


Go to Pattern Load Cues.asm and comment out the entries for the level art. GHZ and the ending have two, the other levels have only the one.


Porting the level art loading process
Above LevelDataLoad, paste this:

LoadZoneTiles:
		moveq	#0,d0
		move.b	(v_zone).w,d0
		lsl.w	#4,d0
		lea	(LevelHeaders).l,a2
		lea	(a2,d0.w),a2
		move.l	(a2)+,d0
		andi.l	#$FFFFFF,d0	; 8x8 tile pointer
		movea.l	d0,a0
		lea	($FF0000).l,a1
		bsr.w	KosDec
		move.w	a1,d3
		move.w	d3,d7
		andi.w	#$FFF,d3
		lsr.w	#1,d3
		rol.w	#4,d7
		andi.w	#$F,d7

@loop:		move.w	d7,d2
		lsl.w	#7,d2
		lsl.w	#5,d2
		move.l	#$FFFFFF,d1
		move.w	d2,d1
		jsr	(QueueDMATransfer).l
		move.w	d7,-(sp)
		move.b	#$C,(v_vbla_routine).w
		bsr.w	WaitForVBla
		bsr.w	RunPLC
		move.w	(sp)+,d7
		move.w	#$800,d3
		dbf	d7,@loop

		rts
; End of function LoadZoneTiles



This is a modified Sonic 2 LoadZoneTiles, changed to match Sonic 1's LevelHeaders block size ($10 bytes over Sonic 2's $12 bytes).

Now to add some branches to this code.

Go to Level_SkipTtlCard and, above this...

		bsr.w	LevelDataLoad ; load block mappings and palettes



...add this:

		bsr.w	LoadZoneTiles	; load level art



Do the same at End_LoadData.

And with that, we're done, a short guide for shorter loading times! Save and build.


Improvements
As mentioned before, this can be taken even further:
Go grab Vladikcomper's improved KosDec and NemDec, along with CompDec. By using the improved KosDec, or switching to Comper compression, you can achieve even better load times. If going the Comper way, you can even switch the 16x16s and 256x256s to Comper and experience an even bigger boost (but be sure to backup a2 under LevelDataLoad).

That's about it. Enjoy your faster loading times!
This post has been edited by Clownacy: 10 November 2014 - 12:39 PM

#2 User is offline MarkeyJester 

Posted 14 April 2014 - 04:38 AM

  • Clouded in obscurity.
  • Posts: 1595
  • Joined: 22-July 08
  • Gender:Male
  • Location:Japan
  • Wiki edits:16
Someone spends their time to help out, and no-one has anything to say? Really? No-one? Well alright...

I think it's a wonderful job you've done here, and deserves a little more recognition than you're getting. I welcome anything that improves the game for good cause. Also, have you considered popping this up on the wiki as a how-to guide? I'm sure many who don't browse the forums would appreciate it d;

#3 User is offline MainMemory 

Posted 14 April 2014 - 05:29 AM

  • Every day's the same old thing... Same place, different day...
  • Posts: 3369
  • Joined: 14-August 09
  • Gender:Not Telling
  • Project:SonLVL
  • Wiki edits:1,339
It's been on the wiki for several hours now.

#4 User is offline Eduardo Knuckles 

Posted 14 April 2014 - 06:01 AM

  • Posts: 378
  • Joined: 07-January 07
  • Gender:Male
  • Location:You don't need to know it.
  • Project:Sonic Harder Levels: Rebirth
  • Wiki edits:66
Awesome job! There's something I was really waiting for such a long time. Thanks!!

#5 User is offline Clownacy 

Posted 14 April 2014 - 11:07 AM

  • Needs to make an avatar
  • Posts: 309
  • Joined: 06-July 13
  • Gender:Male
  • Location:Englandland
I'm working on documenting LoadZoneTiles, can anyone confirm if my understanding of the instructions used and function of the DMA queue are correct?

LoadZoneTiles:
		moveq   #0,d0			; Clear d0
		move.b  (v_zone).w,d0		; Load number of current zone to d0
		lsl.w   #4,d0			; Multiply by $10, converting the zone ID into an offset
		lea     (LevelHeaders).l,a2	; Load LevelHeaders's address into a2
		lea     (a2,d0.w),a2		; Offset LevelHeaders by the zone-offset, and load the resultant address to a2
		move.l  (a2)+,d0		; Move the first longword of data that a2 points to to d0, this contains the zone's first PLC ID and its art's address. The auto increment is pointless as a2 is overwritten later, and nothing reads from a2 before then
		andi.l  #$FFFFFF,d0    		; Filter out the first byte, which contains the first PLC ID, leaving the address of the zone's art in d0
		movea.l d0,a0			; Load the address of the zone's art into a0 (source)
		lea     (v_256x256).l,a1	; Load Chunk Table/StartOfRAM (in this context, an art buffer) into a1 (destination)
		bsr.w   KosDec			; Decompress a0 to a1 (Kosinski compression)

		move.w  a1,d3			; Move a word of a1 to d3, note that a1 doesn't exactly contain the address of v_256x256/StartOfRAM anymore, after KosDec, a1 now contains v_256x256/StartOfRAM offsetted by the size of the file decompressed to it, the result is that d3 now contains the length of the file that was decompressed
		move.w  d3,d7			; Move d3 to d7, for use in seperate calculations

		andi.w  #$FFF,d3		; Remove the high nibble of the high byte of the length of decompressed file, this nibble is how many $1000 bytes the decompressed art is
		lsr.w   #1,d3			; Half the value of 'length of decompressed file', d3 becomes the 'DMA transfer length'
	
		rol.w   #4,d7			; Rotate (left) length of decompressed file by one nibble
		andi.w  #$F,d7			; Only keep the low nibble of low byte, this nibble is the same one filtered out of d3 above: this nibble is how many $1000 bytes the decompressed art is

@loop:		move.w  d7,d2			; Move d7 to d2, note that the ahead dbf removes 1 byte from d7 each time it loops, meaning that d2 will receive a different value each time
		lsl.w   #7,d2
		lsl.w   #5,d2			; Shift (left) d2 by $C, making its contents, a nibble, high nibble of the high byte, d2 is now the size of the decompressed file rounded down to the nearest $1000 bytes, d2 becomes the 'destination address' (VRAM)

		move.l  #$FFFFFF,d1		; Fill d1 with $FF
		move.w  d2,d1			; Move d2 to d1, overwriting the last word of $FF's with d2, this turns d1 into 'StartOfRAM'+'However many $1000 bytes the decompressed art is', d1 becomes the 'source address' (RAM)

		jsr     (QueueDMATransfer).l	; Use d1, d2, and d3 to locate the decompressed art and ready for transfer to VRAM
		move.w  d7,-(sp)		; Store d7 in the Stack
		move.b  #$C,(v_vbla_routine).w
		bsr.w   WaitForVBla
		bsr.w   RunPLC
		move.w  (sp)+,d7		; Restore d7 from the Stack
		move.w  #$800,d3		; Force the DMA transfer length to be $1000/2... Wait, if this is set, and the FIRST cycle is dynamic... The art's DMA'd backwards?! (After further evaluation, yes, it is)
		dbf     d7,@loop		; Loop for each $1000 bytes the decompressed art is

		rts
; End of function LoadZoneTiles

This post has been edited by Clownacy: 14 April 2014 - 05:43 PM

#6 User is offline Shockwave 

Posted 14 April 2014 - 02:46 PM

  • Posts: 26
  • Joined: 08-July 11
  • Gender:Male
  • Location:LA, CA
  • Project:Sonic: South Island Warped

View PostMarkeyJester, on 14 April 2014 - 04:38 AM, said:

Someone spends their time to help out, and no-one has anything to say? Really? No-one? Well alright...

I think it's a wonderful job you've done here, and deserves a little more recognition than you're getting. I welcome anything that improves the game for good cause.


I second this. Especially as someone with a hack that has level art constantly being decompressed during gameplay, I consider this an incredibly welcome improvement to how the game functions. Great job implementing this Clownacy, and also great job with how you wrote this guide. Even with an older disassembly I can't imagine too many people having trouble following this (but you never know I suppose =P).

Also, your documentation looks about right, nothing seems like it's explained incorrectly, but if I were doing something like this I would ask about it too, so maybe you'd want someone else to check it over as well.

#7 User is offline Super Egg 

Posted 14 April 2014 - 05:57 PM

  • Master of MS Paint.
  • Posts: 144
  • Joined: 01-July 10
  • Gender:Male
  • Location:Tomball, TEXAS
  • Project:Sonic 2 beta 3 hoax, SONIC X ABRIDGED BITCH!!!
  • Wiki edits:46
Cool stuffz Clownacy. You've made a guide to something that should have been documented a long time ago. I myself have actually implemented it in my personal S2B and S2NA disasms eons ago, but have been too lazy to do the same thing for S1. So yeah, you've just done and made the work for me much less. =P

#8 User is offline KingofHarts 

Posted 15 April 2014 - 01:30 AM

  • Call me back when people stop shitting in the punch bowl...
  • Posts: 1480
  • Joined: 07-August 10
  • Gender:Male
  • Wiki edits:1
I just popped online for the first time since coming back home to the US... TTHIS is awesome, and is going straight to REV C. Thank you Clownacy!

EDIT: It IS in REV C... I can definitely tell the difference. Job well done
This post has been edited by KingofHarts: 15 April 2014 - 03:10 AM

#9 User is offline KingofHarts 

Posted 21 April 2014 - 09:39 PM

  • Call me back when people stop shitting in the punch bowl...
  • Posts: 1480
  • Joined: 07-August 10
  • Gender:Male
  • Wiki edits:1
Double posting to report a potential bug... wondering if anyone else is getting this.

Place this fix in a ROM, and change Chunks and Blocks to load from the ROM, directly. (Use Mercury's ReadySonic for this)

Finally, use flamewing's earlier posted Kosinski decompressor for the art tiles.


NOW, what I'm getting... is in the title card before the level fades in, there is garbled art that appears near the bottom. Now... it does NOT appear with every level, and with every single act it DOES appear in, it looks different. Here is an example of one... in Marble Zone Act 1.
IMAGE (Because it doesn't display here...)

I believe it's more related to the blocks and chunks... but I wanna throw this out there. Anyone else having this issue, or something similar to it?
This post has been edited by KingofHarts: 21 April 2014 - 09:42 PM

#10 User is offline Clownacy 

Posted 21 April 2014 - 10:44 PM

  • Needs to make an avatar
  • Posts: 309
  • Joined: 06-July 13
  • Gender:Male
  • Location:Englandland
You tried backing up the registers? The decompressors love messing with those and not restoring them. Enigma does, though. I had to put up with that when figuring out how to switch blocks from Enigma to Kosinski/Comper.

To elaborate, backup all of the registers to stack right before the branch to KosDec, then restore them after it. I say this because listing that alternate Flamewing (?) decompressor as a main cause throws a bunch of red flags. I've had GHZ's flowers use a different palette line because I didn't backup a2 when using KosDec on my S2-format layouts. I've also had the game outright crash when I didn't backup a2 when using KosDec on blocks.

I'm off to test this.

EDIT: Oh hang on, be selective with what registers to restore; the code following the branch to KosDec needs the modified outputted a1.

EDIT2: Well, that didn't work. Commenting out this:

        move.b  #$C,(v_vbla_routine).w
        bsr.w   WaitForVBla


Also has tiles pop up on the transparency, while eliminating that on the title card. Aside from speculation, I'm lost on this one. It doesn't appear to be this. That's all I can say. I must be looking in the wrong area.

I'm actually getting this without the new decompressor.
This post has been edited by Clownacy: 21 April 2014 - 11:19 PM

#11 User is offline KingofHarts 

Posted 21 April 2014 - 11:20 PM

  • Call me back when people stop shitting in the punch bowl...
  • Posts: 1480
  • Joined: 07-August 10
  • Gender:Male
  • Wiki edits:1
I was about to inquire on HOW to backup the registers... but I guess I don't need to worry about that for now, if it doesn't fix this issue.

#12 User is offline Clownacy 

Posted 21 April 2014 - 11:32 PM

  • Needs to make an avatar
  • Posts: 309
  • Joined: 06-July 13
  • Gender:Male
  • Location:Englandland
...It bothers me that bug even managed to show up. I one-for-one ported the loader, yet a bug slips through... how? I'll have to examine GM_Level: and compare it to S2's Level: and see if there are any additional WaitForVBlanks.

My understanding of register backing up is that you can use RAM, or use the Stack. You write to the Stack backwards and you can backup multiple registers at once with the 'movem' instruction.

To backup

        movem.l    d0-a6,-(sp)


For restoring, you'd use

        movem.l    (sp)+,d0-a6

This post has been edited by Clownacy: 21 April 2014 - 11:34 PM

#13 User is offline KingofHarts 

Posted 22 April 2014 - 12:24 AM

  • Call me back when people stop shitting in the punch bowl...
  • Posts: 1480
  • Joined: 07-August 10
  • Gender:Male
  • Wiki edits:1
Well... I'm looking forward to this bug getting squashed, because the time shaved off from loading is amazing. I'll also give it a look tomorrow as well.

#14 User is offline Clownacy 

Posted 22 April 2014 - 06:32 AM

  • Needs to make an avatar
  • Posts: 309
  • Joined: 06-July 13
  • Gender:Male
  • Location:Englandland
I just noticed, in one of my S2 disasms I have the S1 title cards ported, and on a custom zone, that very bug appears (on a greater scale)... but the hack doesn't have ROM chunks or blocks, though it does have the decompressor. It's strange, when I documented LoadZoneTiles, I found that the process had $1000 bytes processed, then it waited for a Vertical Interupt to occur, I believe this has to do with the Race Conditions.

Speaking of that, would it be a possibility for everything to be processing too fast? The tiles are decompressing to the chunk table using a significantly faster decompressor and are transfered while alternating with LoadPLC, the chunks and blocks simply write an address to the chunk table.

The garbage art that appears (in Marble Zone at least) appears to be a single tile of whatever $1000 was DMA'd in that frame. It displays at least two of these in succession.

#15 User is offline KingofHarts 

Posted 22 April 2014 - 09:23 AM

  • Call me back when people stop shitting in the punch bowl...
  • Posts: 1480
  • Joined: 07-August 10
  • Gender:Male
  • Wiki edits:1
It could be. I'm running a test now, and this bug occurs on all of the following zones for me. I've provided a brief simple description for each instance of the bug.

GHZ - NO BUG IN ANY ACT


MZ 1 - The screenshot I show you, then that tile changes to another one, then all instances disappear.
MZ 2 - This time, two tiles are seen instead of 1, like in MZ 1. Like the first example, these tiles change to a different tile, before disappearing.
MZ 3 - NO BUG

SYZ 1 - Similar to MZ 1.
SYZ 2 - Identical to SYZ 1... down to the tiles that are displayed on screen.
SYZ 3 - NO BUG


LZ 1 - Shows a wide array of tiles along the bottom half of the screen. They are not multiple instances of the same tile either... instead being a bunch of tiles from random spots in VRAM, including some leftover tiles from the GHZ title art. Some of them change, before they ALL disappear.
LZ 2 - This one appears more similar to MZ 1. The first tile shown is the same as seen in the MZ 1 screenshot I took.

LZ 3 - Identical to LZ 2

SLZ - NO BUG IN ANY ACT

SBZ 1 - Identical to LZ 2.
SBZ 2 - NO BUG
SBZ 3 (LZ 4) - NO BUG
SBZ 4 (FZ) - Similar to SBZ 1, but different tiles.

Various points in the ending demos also exhibit this bug. Hope this helps in any way at all. I've not changed much else in how the level starts... so I believe applying to a clean Sonic 1 will produce near identical results... though I could be wrong.

EDIT... Would it have ANYTHING to do with the 256x256 chunk format in Sonic 1?? I'm going to apply this to my 128 HG port and report back.
This post has been edited by KingofHarts: 22 April 2014 - 09:24 AM

  • 2 Pages +
  • 1
  • 2
    Locked
    Locked Forum

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users