A technical problem I had to figure out when developing Socket the Hedgeduck ROM hack was to enable Sonic 1 to use up to $FF chunks in a zone. The original Socket/Time Dominator game had many zones which utilized upto $FF chunks. Socket's chunks are the same size as Sonic 1's (256x256 px or an array of 16x16 blocks), which was a contributing factor to selecting the Sonic 1 engine for the hack. Spoiler: Tech Details about Sonic 1 chunks Sonic 1 and Sonic CD handle chunks differently than the other Genesis games in the series. Sonic 2 and onwards use chunks sized at 128x128 px (or an array of 8x8 blocks), while Sonic 1 use chunks sized at 256x256 px (or an array of 16x16 blocks). Due to the double-sized chunks, it takes more RAM to hold each chunk's configuration in memory. The stock Sonic 1 engine can only handle $52 chunks (+1 empty one) in RAM, while Sonic 2 and onwards can handle upto $FF chunks in RAM. Despite this RAM problem, it is possible to bypass it by modifying the game to load chunks from ROM for particular zones (what I will refer to as "ROM Chunks"). ROM Chunks are required to be stored uncompressed in ROM (due to too much overhead to decompress from ROM on-the-fly, since we are no longer using RAM to store them uncompressed). Size comparison between chunk types Not only do the chunks have different sizes between Sonic1 and the other games, but Sonic 1 also handles loops and roll tunnels differently from Sonic 2 and onwards. Sonic 2 and onwards use a path swapper object to enable alternate collision for each block for loops and such, and a roll object to force rolling, while Sonic 1 uses some hard-coded LUTs to enable collision swapping for loops and auto-rolling functionality for roll-tunnels. This LUT holds upto 2 chunk IDs for loops and upto 2 chunk IDs for roll tunnels in each zone. LUT slots that aren't used in a zone are just set to $7F. In all of the Sonic games, chunk IDs are a byte long; Sonic 2 and onwards use all 8 bits (upto $FF or 256 chunks), while Sonic 1 uses only 7 bits (upto $7F or 128 chunks). In Sonic 1, the MSB is used as a loop flag; the base chunk ID with the MSB set is the value used in the LUT. (So, for example, if you want to use chunk $35 (%00110101 in binary) in GHZ1 as a loop chunk, either add $7F to base chunk ID or just set the MSB for the chunk ID ($35+$7F=$B4, or %10110101 in binary), and then use this new value in the LUT as the chunk ID.) In Sonic 1, the "collision swapping" for loops is done lazily by replacing one chunk with an alternative one, based on which direction Sonic is facing. This alternate chunk for collision is hardcoded to be the next ID. (So if chunk $35 is the first half of the loop, chunk $36 is the other half). There is one exception to this rule: in vanilla Sonic 1, SLZ has a chunk ID that is hardcoded to use another in the collision swapping code. Roll-tunnel chunks in the LUT don't use the MSB flag or any collision swapping; while Sonic is touching these roll-tunnel chunks he will be forced into a roll until he leaves the chunk. Labyrinth Zone also uses a separate LUT of chunk IDs to enable the water slide function. Loop system (Sonic 1) To make a long story short, even if you use ROM Chunks to bypass the $53 chunk limit imposed by RAM in Sonic 1, you will only be allowed to use upto $7F chunks, due to the loop-flag being the MSB of the chunk ID. With the ROM Chunk system, attempting to use chunk IDs above $7F will display either the wrong graphics or use the wrong collision. This tutorial will show the user how to enable particular ROM Chunk zones to use upto $FF ROM Chunks (what I call "Big ROM Chunks") and have the game properly handle the LUTs for loop, roll-tunnel chunks, and LZ water slide chunks. This tutorial is written for Sonic 1 Github disasm, and will handle applying ROM Chunks and Big ROM Chunks to Sonic 1. Credits go to FraGag for the original ROM Chunk guide and to MarkeyJester for some additional commentary on the chunk drawing code; the first part of this tutorial is FraGag's guide rewritten to target Sonic 1 Github's nomenclature. We will also modifiy the SonLVL project files for the disasm to properly handle Big ROM Chunks (S2NA format)chunk IDs. Spoiler: ROM Chunks (Github edition) Using the uncompressed chunk mappings Since the game normally stores compressed data, we need to change it to store uncompressed data instead in Sonic.asm . For example, for Green Hill Zone, replace this: Code: Blk256_GHZ: incbin map256\ghz.bin even with this: Code: Blk256_GHZ: incbin map256_u\ghz.bin even Make sure to add the new map256_u folder to the root of your disasm. The original ROM Chunk guide mentions about modifying the SonLVL files to make the affected ROM Chunk zones point to the proper uncompressed chunk art. To keep things much simpler, in Socket the Hedgeduck, I just keep all zones for SonLVL to point and to handle the compressed chunk art, and let the build batch file call another batch file to decompress the art as appropriately when assembling. You will need to download the command-line forms of The Sega Data Compressor and place "koscmp.exe" and "Kosinski.dll" at the root of your disasm. At the root folder, create a new file called "Uncompress.bat", and fill it in as such: Code: REM Map256 Decompression (for levels using (BIG) ROM Chunks) Cls koscmp.exe -x -v map256\GHZ.bin map256_u\GHZ.bin REM Add other entries like this to decompress for other (Big) ROM Chunk zones In build.bat, above this line Code: asm68k /k /p /o ae- sonic.asm, s1built.bin >errors.txt, , sonic.lst add Code: REM Map256 Decompression Call "Uncompress.bat" This will decompress the appropriate zone's chunks before building. Skipping loading chunks to RAM (title screen) If you decide to read the chunks from ROM for Green Hill Zone and you still use the Green Hill Zone data for your title screen, you should skip decompressing the chunks to avoid overwriting other data in RAM. In Sonic.asm, goto Tit_LoadText and find the following lines: Code: lea (Blk256_GHZ).l,a0 ; load GHZ 256x256 mappings lea (v_256x256).l,a1 bsr.w KosDec Comment them out. Skipping loading chunks to RAM (levels) In Sonic.asm at LevelDataLoad, the chunks data, amongst others, is decompressed to RAM. We are going to disable this. Find the following lines: Code: lea (v_256x256).l,a1 ; RAM address for 256x256 mappings bsr.w KosDec bsr.w LevelLayoutLoad move.w (a2)+,d0 move.w (a2),d0 andi.w #$FF,d0 cmpi.w #(id_LZ<<8)+3,(v_zone).w ; is level SBZ3 (LZ4) ? bne.s @notSBZ3 ; if not, branch moveq #palid_SBZ3,d0 ; use SB3 palette and replace them with the following: Code: cmpi.b #id_GHZ,(v_zone).w ; are we in GHZ? beq.s @no_dec ; if yes, branch cmpi.b #id_MZ,(v_zone).w ; are we in MZ? beq.s @no_dec ; if yes, branch ;Ditto for other level slots that use ROM Chunks... @dec: lea (v_256x256).l,a1 ; RAM address for 256x256 mappings jsr KosDec @no_dec: bsr.w LevelLayoutLoad move.w (a2)+,d0 move.w (a2),d0 andi.w #$FF,d0 cmpi.w #(id_LZ<<8)+3,(v_zone).w ; is level SBZ3 (LZ4) ? bne.s @notSBZ3 ; if not, branch moveq #palid_SBZ3,d0 ; use SB3 palette Fixing art The chunks are composed of blocks, which contain art tiles. Since the chunks are not in RAM anymore, you will get empty levels. In Sonic.asm, goto DrawBlocks_2 and replace the code from Code: moveq #-1,d3 to Code: rts with Code: cmpi.b #id_GHZ,(v_zone).w ; are we in GHZ? beq.w @ghz ; if yes, branch cmpi.b #id_MZ,(v_zone).w ; are we in MZ? beq.s @mz ; if yes, branch ;Repeat for all other zones with ROM Chunks... @NoRomChunks: moveq #-1,d3 ; load chunks from RAM. MJ: prepare FFFF in d3 bsr.w LocateBlock jmp @Continue @ghz: moveq #0,d3 bsr.s LocateBlock add.l #Blk256_GHZ,d3 bra.w @Continue @mz: moveq #0,d3 bsr.w LocateBlock add.l #Blk256_MZ,d3 bra.s @Continue nop ;Repeat with local labels for all other ROM Chunk zones, ;replacing the Blk256 label with the appropriate one for the zone @continue: movea.l d3,a0 ; MJ: set address (Chunk to read) move.w (a0),d3 andi.w #$3FF,d3 lsl.w #3,d3 adda.w d3,a1 rts ; --------------------------------------------------------------------------- LocateBlock: move.b (a4,d0.w),d3 ; load chunk ID in d3. MJ: collect correct chunk ID from layout beq.s LocateBlock_EmptyChunk subq.b #1,d3 andi.w #$7F,d3 ; MJ: keep within 7F ror.w #7,d3 add.w d4,d4 andi.w #$1E0,d4 ; MJ: keep Y pos within 480 pixels (?) andi.w #$1E,d5 ; MJ: keep X pos within 30(?) add.w d4,d3 ; MJ: add calc'd Y pos to ror'd d3 add.w d5,d3 ; MJ: add calc'd X pos to ror'd d3 rts ; --------------------------------------------------------------------------- LocateBlock_EmptyChunk: addq.w #4,sp ; pop a stack frame to leave a1 pointing at the first tile rts Fixing collision The chunks also contain collision information, so now we need to tell the game that the data is loaded elsewhere. In the file "_incObj\sub FindNearestTile.asm", find the Floor_ChkTile_LocateBlock subroutine and replace the entirety with the following code: Code: ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Floor_ChkTile_LocateBlock: lea (v_lvllayout).w,a1 move.b (a1,d0.w),d1 ; get 256x256 tile number beq.w @blanktile ; branch if 0 bmi.s @specialtile ; branch if >$7F subq.b #1,d1 ext.w d1 ror.w #7,d1 move.w d2,d0 add.w d0,d0 andi.w #$1E0,d0 add.w d0,d1 move.w d3,d0 lsr.w #3,d0 andi.w #$1E,d0 add.w d0,d1 rts ; --------------------------------------------------------------------------- @specialtile: andi.w #$7F,d1 ;Keep chunk ID within $7F btst #6,obRender(a0) ; is object "behind a loop"? beq.s @treatasnormal ; if not, branch addq.w #1,d1 ;Code below is Sega's sleazy SLZ hardcoded loop IDs. If SLZ is modified, comment out ;In SLZ, If chunk ID=$29, use $51 as loop complement cmpi.b #id_slz,(v_zone).w ; are we in slz? bne.s @treatasnormal ; if not, skip Sega's shameful hardcoding cmpi.w #$29,d1 bne.s @treatasnormal move.w #$51,d1 @treatasnormal: subq.b #1,d1 ror.w #7,d1 move.w d2,d0 add.w d0,d0 andi.w #$1E0,d0 add.w d0,d1 move.w d3,d0 lsr.w #3,d0 andi.w #$1E,d0 add.w d0,d1 rts ; --------------------------------------------------------------------------- @blanktile: lea ($FFFFFF00).w,a1 ; override a1 addq.w #4,sp ; pop a stack frame to avoid adding the address of the chunk mappings to a1 rts ; =========================================================================== ; --------------------------------------------------------------------------- ; Subroutine to find which tile the object is standing on ; input: ; d2 = y-position of object's bottom edge ; d3 = x-position of object ; output: ; a1 = address within 256x256 mappings where object is standing ; (refers to a 16x16 tile number) ; --------------------------------------------------------------------------- FindNearestTile: move.w d2,d0 ; get y-pos. of bottom edge of object lsr.w #1,d0 andi.w #$380,d0 move.w d3,d1 ; get x-pos. of object lsr.w #8,d1 andi.w #$7F,d1 add.w d1,d0 ; combine ;Add other checks here for other ROM Chunk zones cmpi.b #id_ghz,(v_zone).w ; are we in GHZ? beq.s @ghz ; if yes, branch cmpi.b #id_mz,(v_zone).w ; are we in MZ? beq.s @mz ; if yes, branch moveq #-1,d1 bsr.w Floor_ChkTile_LocateBlock movea.l d1,a1 rts ; --------------------------------------------------------------------------- @ghz: moveq #0,d1 bsr.w Floor_ChkTile_LocateBlock add.l #Blk256_GHZ,d1 movea.l d1,a1 rts @mz: moveq #0,d1 bsr.w Floor_ChkTile_LocateBlock add.l #Blk256_MZ,d1 movea.l d1,a1 rts ; End of function Compared to the original guide, I have added an additional check to apply Sega's sleazy loop chunk ID hardcoding for only SLZ's loops. In the original game, if the chunk loop ID is $29 (assumed to only happen in SLZ), it's complement will be $51; otherwise the next chunk ID is used. If your chunk IDs shuffle around for SLZ, you should either adjust or comment out the affected code appropriately. And you've did it you can now have up to $80 Chunks and it frees a lot of ram! ($0000 to $A3FF) Which means you can Sonic 3's Object and ring managers to sonic 1! Please proceed to the Big ROM Chunks guide below to extend ROM Chunks to $FF chunks, and to learn how to enable loops in zones other than GHZ or SLZ. Spoiler: Big ROM Chunks This tutorial will modify the ROM Chunks system to allow up to $FF chunks ("Big ROM Chunks"). It will allow you to selectively enable Big ROM Chunks for particular ROM Chunk zones, and utilizes a new flag to tell the game when to do so. Essentially it removes ANDing the chunk ID by $7F, freeing usage to higher chunk IDs for various LUTs. Big ROM Chunks setup In order to allow SonLVL to handle Big ROM Chunks, in "SonLVL INI Files\SonLVL.ini" underneath Code: romfile=../s1built.bin add Code: layoutfmt=S2NA This will enable SonLVL to handle Sonic 2 Nick Arcade format level layouts (upto $FF Sonic 1 chunks). Any levels which utilize Big ROM Chunks and which use loops (such as GHZ and SLZ) shouldn't use SonLVL's loopchunk setting. If they do use the setting, remove the line. The setting will loop something like this Code: loopchunks=35 in the zone's act definitions. Next we will setup a new flag to indicate Big ROM Chunks. In variables.asm, after the line Code: v_limitleft3: = $FFFFF732 ; left level boundary, at the end of an act (2 bytes) add Code: f_ROMChunk: = $FFFFF734 ; Is this a ROM-Chunk level? Fixing the art (toggle flag, extend the chunk IDs) Where the game adjusts the block's art for chunks, the game will need to set/reset the Big ROM Chunk flag appropriately. In Sonic.asm, goto the line labeled DrawBlocks_2. Underneath there, you will find the local label @NoRomChunks. Add this line underneath the label Code: move.b #0, (f_ROMChunk).w ; Turn ROM Chunks off Directly underneath @NoRomChunks, you should have many local labels for each ROM Chunk zone to point to the appropriate Blk256 labels and load chunks from ROM. Underneath each zone's local label in which you wish to extend to Big ROM Chunks, add this line Code: move.b #1, (f_ROMChunk).w ;!@ BIG_Rom_Chunk. Set ROM_Chunk flag For example, for the changes to the local label of @ghz used in the prior tutorial Code: @ghz: move.b #1, (f_ROMChunk).w ;!@ BIG_Rom_Chunk. Set ROM_Chunk flag moveq #0,d3 bsr.s LocateBlock add.l #Blk256_GHZ,d3 bra.w @Continue Close by you should find the line labeled LocateBlock. Change this Code: LocateBlock: move.b (a4,d0.w),d3 ; load chunk ID in d3. MJ: collect correct chunk ID from layout beq.s LocateBlock_EmptyChunk subq.b #1,d3 andi.w #$7F,d3 ; MJ: keep within 7F ror.w #7,d3 add.w d4,d4 andi.w #$1E0,d4 ; MJ: keep Y pos within 480 pixels (?) andi.w #$1E,d5 ; MJ: keep X pos within 30(?) add.w d4,d3 ; MJ: add calc'd Y pos to ror'd d3 add.w d5,d3 ; MJ: add calc'd X pos to ror'd d3 rts into Code: LocateBlock: move.b (a4,d0.w),d3 ; load chunk ID in d3. MJ: collect correct chunk ID from layout beq.s LocateBlock_EmptyChunk subq.b #1,d3 ;!@ BIG_ROM_Chunks tst.b (f_ROMChunk).w ;!@ Is this a ROM Chunk level? bne.s @BIG_Chunks ; if so, branch andi.w #$7F,d3 ; MJ: keep within 7F ror.w #7,d3 bra.s @LocateBlock_Continue ;!@ BIG_Rom_Chunks ;!@ BIG_ROM_Chunks @BIG_Chunks: andi.w #$FF,d3 ; Keep within FF instead lsl.l #8,d3 add.l d3,d3 @LocateBlock_Continue: add.w d4,d4 andi.w #$1E0,d4 ; MJ: keep Y pos within 480 pixels (?) andi.w #$1E,d5 ; MJ: keep X pos within 30(?) add.w d4,d3 ; MJ: add calc'd Y pos to ror'd d3 add.w d5,d3 ; MJ: add calc'd X pos to ror'd d3 @LocateBlock_locret: rts If the Big ROM Chunk flag is set, the chunk ID will be limited to $FF chunks; otherwise $7F (standard Sonic 1 chunk behavior). Fixing collision (handle flag, extend chunk IDs) In the file "_incObj\sub FindNearestTile.asm", replace the entirety with the following: Code: ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Floor_ChkTile_LocateBlock: ;!@ BIG_Rom_Chunk lea (v_lvllayout).w,a1 tst.b (f_ROMChunk).w ;Is this a Big ROM Chunk level? bne.s @bigchunk ;if so, branch move.b (a1,d0.w),d1 ; get 256x256 tile number beq.w @blanktile ; branch if 0 bmi.s @specialtile ; branch if >$7F bra.s @continue @bigchunk: move.b (a1,d0.w),d1 ; get 256x256 tile number beq.w @blanktile ; branch if 0 cmp.b (v_256loop1).w,d1 ; is Sonic on 1st loop tile? beq.s @specialtile ; if yes, branch cmp.b (v_256loop2).w,d1 ; is Sonic on 2nd loop tile? beq.s @specialtile ; if yes, branch @continue: subq.b #1,d1 ;!@ Big_ROM_Chunk tst.b (f_ROMChunk).w ;Is this a Big ROM Chunk level? bne.s @normalfix ;if so, branch ;Keep within 7F ext.w d1 ror.w #7,d1 bra.s @normalcont ;!@ Big_Rom_Chunk @normalfix: andi.w #$FF,d1 ; Keep within FF instead lsl.l #8,d1 add.l d1,d1 @normalcont: move.w d2,d0 add.w d0,d0 andi.w #$1E0,d0 add.w d0,d1 move.w d3,d0 lsr.w #3,d0 andi.w #$1E,d0 add.w d0,d1 rts ; --------------------------------------------------------------------------- @specialtile: ;!@ Big_Rom_Chunk tst.b (f_ROMChunk).w ;Is this a Big ROM Chunk level? bne.s @bigandy ;if so, branch andi.w #$7F,d1 ;Keep chunk ID within $7F bra.s @specialcont @bigandy: andi.w #$FF,d1 ;Keep chunk ID within $FF instead @specialcont: btst #6,obRender(a0) ; is object "behind a loop"? beq.s @treatasnormal ; if not, branch addq.w #1,d1 ;Code below is Sega's sleazy SLZ hardcoded loop IDs. If SLZ is modified, comment out ;In SLZ, If chunk ID=$29, use $51 as loop complement cmpi.b #id_slz,(v_zone).w ; are we in slz? bne.s @treatasnormal ; if not, skip Sega's shameful hardcoding cmpi.w #$29,d1 bne.s @treatasnormal move.w #$51,d1 @treatasnormal: subq.b #1,d1 ;!@ Big_Rom_Chunk tst.b (f_ROMChunk).w ;Is this a Big ROM Chunk level? bne.s @treatbigfix ;if so, branch ;Keep within $7FF ror.w #7,d1 bra.s @treatcont @treatbigfix: ;Keep within $FF lsl.l #8,d1 add.l d1,d1 @treatcont: move.w d2,d0 add.w d0,d0 andi.w #$1E0,d0 add.w d0,d1 move.w d3,d0 lsr.w #3,d0 andi.w #$1E,d0 add.w d0,d1 ;movea.l d1,a1 rts ; --------------------------------------------------------------------------- @blanktile: lea ($FFFFFF00).w,a1 ; override a1 addq.w #4,sp ; pop a stack frame to avoid adding the address of the chunk mappings to a1 rts ; =========================================================================== ; --------------------------------------------------------------------------- ; Subroutine to find which tile the object is standing on ; input: ; d2 = y-position of object's bottom edge ; d3 = x-position of object ; output: ; a1 = address within 256x256 mappings where object is standing ; (refers to a 16x16 tile number) ; --------------------------------------------------------------------------- FindNearestTile: move.w d2,d0 ; get y-pos. of bottom edge of object lsr.w #1,d0 andi.w #$380,d0 move.w d3,d1 ; get x-pos. of object lsr.w #8,d1 andi.w #$7F,d1 add.w d1,d0 ; combine ;Add other checks here for other ROM Chunk zones cmpi.b #id_ghz,(v_zone).w ; are we in GHZ? beq.s @ghz ; if yes, branch cmpi.b #id_mz,(v_zone).w ; are we in MZ? beq.s @mz ; if yes, branch moveq #-1,d1 bsr.w Floor_ChkTile_LocateBlock movea.l d1,a1 rts ; --------------------------------------------------------------------------- @ghz: moveq #0,d1 bsr.w Floor_ChkTile_LocateBlock add.l #Blk256_GHZ,d1 movea.l d1,a1 rts @mz: moveq #0,d1 bsr.w Floor_ChkTile_LocateBlock add.l #Blk256_MZ,d1 movea.l d1,a1 rts ; End of function If Big ROM Chunks are not used, the chunk ID in which Sonic is located in will be compared against $00 (empty chunk), if negative (>$7F, loop chunks), or anything else (normal chunks). If the chunk is $0, a stack frame is popped to avoid messing up address arithmetic for proper usage. If it is negative (a loop chunk), $7F is subtracted from the loop chunk ID to get the real chunk ID, and either this ID or the next one are used for collision. (The exception to this rule is the sleazily hardcoded loop chunk in SLZ.) Which ID is used is determined by Sonic's current sprite priority (low/high), handled by Sonic_Loops function. For all other normal chunks, the chunk ID is kept within $7F. If the Big ROM Chunks are used, the comparisons and actions to take work as usual, except for the following. For loop chunks, the chunk ID is compared directly against the 2 loop chunk IDs stored in RAM from the loop/roll-tunnel LUT (no fiddling with the MSB) instead of based on if it is negative. Chunks are kept within $FF for math for all three cases. Extending Loop/Roll LUT to handle Big ROM Chunks The file "_inc\LevelSizeLoad & BgScrollSpeed.asm" contains the LUT with chunk IDs for loops and roll-tunnels. It can be found at label LoopTileNums, and looks something like Code: ; --------------------------------------------------------------------------- ; Which 256x256 tiles contain loops or roll-tunnels ; --------------------------------------------------------------------------- LoopTileNums: ; loop loop tunnel tunnel dc.b $B5, $7F, $1F, $20 ; Green Hill dc.b $7F, $7F, $7F, $7F ; Labyrinth dc.b $7F, $7F, $7F, $7F ; Marble dc.b $AA, $B4, $7F, $7F ; Star Light dc.b $7F, $7F, $7F, $7F ; Spring Yard dc.b $7F, $7F, $7F, $7F ; Scrap Brain dc.b $7F, $7F, $7F, $7F ; Ending (Green Hill) zonewarning LoopTileNums,4 even ; =========================================================================== As previously explained, the first 2 bytes for each zone's entry is the chunk ID for upto 2 loop chunks, while the last 2 bytes are for roll-tunnels. These should be updated accordingly for your modified zones. It is strongly recommended to set unused bytes for Big ROM Chunk zones to $FF; normal levels should use $7F. Both chunk $7F and $FF should ideally be empty. Big ROM Chunk zones require using just the absolute chunk ID (no need to fiddle with the MSB) for the LUT's values. The file "_incObj\Sonic Loops.asm" actually handles being rolled by roll-tunnels and settings Sonic's proper sprite priority (which drives the loop collision), based on the IDs in the above LUT. At the beginning of the function are some checks to only allow roll-tunnel/loops in certain zones. You will need to adjust these comparisons appropriately in order to allow loops/roll-tunnels in other zones. Those comparisons look something like this: Code: ; --------------------------------------------------------------------------- ; Subroutine to make Sonic run around loops (GHZ/SLZ) ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Sonic_Loops: ;Comparisons here cmpi.b #id_SLZ,(v_zone).w ; is level SLZ ? beq.s @isstarlight ; if yes, branch tst.b (v_zone).w ; is level GHZ ? bne.w @noloops ; if not, branch @isstarlight: ;Code to handle loops/roll-tunnels here rts @noloops: ;Skips loop/roll-tunnel handling rts ; =========================================================================== Fortunately, that function does comparisons directly against the chunk ID (no fiddling with the MSB), so no adjustments are needed for Big ROM Chunks. Extending LZ water slides to handle Big ROM Chunks In the file "_inc\LZWaterFeatures.asm" is another LUT with chunk IDs used to make Sonic ride down water slides. it can be found at label Slide_Chunks and looks like Code: Slide_Chunks: dc.b 2, 7, 3, $4C, $4B, 8, 4 Update this LUT as appropriately. The actual sliding is handled at LZWaterSlides label. Change Code: LZWaterSlides: lea (v_player).w,a1 btst #1,obStatus(a1) ; is Sonic jumping? bne.s loc_3F6A ; if not, branch move.w obY(a1),d0 lsr.w #1,d0 andi.w #$380,d0 move.b obX(a1),d1 andi.w #$7F,d1 add.w d1,d0 lea (v_lvllayout).w,a2 move.b (a2,d0.w),d0 lea Slide_Chunks_End(pc),a2 moveq #Slide_Chunks_End-Slide_Chunks-1,d1 into Code: LZWaterSlides: lea (v_player).w,a1 btst #1,obStatus(a1) ; is Sonic jumping? bne.s loc_3F6A ; if not, branch move.w obY(a1),d0 lsr.w #1,d0 andi.w #$380,d0 move.b obX(a1),d1 ;!@ BIG_ROM_Chunks tst.b (f_ROMChunk).w ;!@ Is this a ROM Chunk level? bne.s @bigandy ; if so, branch andi.w #$FF,d1 beq.s @continue @bigandy: andi.w #$7F,d1 @continue: add.w d1,d0 lea (v_lvllayout).w,a2 move.b (a2,d0.w),d0 lea Slide_Chunks_End(pc),a2 moveq #Slide_Chunks_End-Slide_Chunks-1,d1 in order to handle water slides with higher chunk IDs. Congratulations, you have extended ROM Chunks to handle upto $FF chunks! Use this to build larger, more complex zones. Proper collision/properties of Big ROM Chunks in action
Why don't you use the path swapping objects that Sonic 2 and 3K use? MarkeyJester's Project 128 disassembly has them pre-ported over.
I could, but ideally, this guide should have the loop system work with both Big ROM Chunks and with normal Sonic 1 RAM chunks or small ROM Chunks (for backwards compatibility purposes), especially since MarkeyJester's guide has the path swapper system ported over for Sonic 2 styled chunks, not for Sonic 1 styled ones. The guide is meant for maxing out the range of Sonic 1 chunks to an entire byte's worth, for those who need more 256 px chunks (like I needed for Socket the Hedgeduck). Not having the loop system functional wasn't an issue with Socket the Hedgeduck, since Socket doesn't have any real loops (closest thing to a loop is depicted below, and I was able to get away without using it). It is an issue for anybody wishing to use Big ROM Chunks in a more generic use case, however. The problem I was having with the guide is that the loop chunks won't update to their alternate chunk when Sonic goes upside down with Big ROM Chunk levels; I haven't yet been able to locate where in the game's code it handles swapping the loop chunk. (Sonic_Loops function only handles changing Sonic's low/high sprite priority as he goes upside down in a loop; I have a hunch the chunk swapping is buried within several Sonic physics-related functions, using something more complex than an andi #$7f, some_data_register opcode (where some_data_register opcode is the current chunk ID Sonic is located in). Anyone know about the specifics on how Sonic 1 swaps the loop chunk?
loc_1499A handles the loops, by adding 1 to the chunk ID, if the chunk ID has MSB set, and the "high plane" flag is set for Sonic. All you'd really have to do, is modify it to work with $FF chunks. Of course, you have to pay careful attention to the loop chunks code as well, to make sure it detects the player correctly.
Now that I think about it, I release how that couldn't work since Sonic 1 uses only 1 collision plane and does loops differently. I don't know exactly how Sonic 1's loops work, but I think it would be best to hardcode which chunks are loops like what is done with the tunnels.
Expanding the 256x256 per chunk byte limit as opposed to switching to 128x128 is a fair enough decision in my book, you do get better overall chunk space, though I would still recommend 128x128. ...but the path swapping, you would be foolish to say no to that form of advantage. There is enough free space within the chunk (per word block) to hold a secondary "Top/LRB" setting, the bit order just needs to be rearranged a little, I would say that the addition of a second path of bits would cause less efficient compression, but since you have the chunks uncompressed stored in ROM, the space would effectively be identical in size for the data. As others have said, this would also allow you to surpass 7F as your limit as the MSB would not be reserved for chunk swapping. Both SonED and SonLVL should support a mix-mash of chunk formats and multi-path support, so it's not like you would be at a disadvantage in terms of tools either. Bottom line, if you wanna say no to 128x128, fine, but don't say no to path swappers, the flexibility you'll throw away would be absolutely insulting to your project. It's not to say you cannot make your desired layout without them, but it would make your laying out job much easier and it would solve your 7F problem.
I've updated the guide with the proper fixes to support loops with Big ROM Chunks. For fixing Big ROM Chunks, loop chunk IDs are detected by comparing the current chunk ID against the values in the loop/roll-tunnel LUT and keeping the chunk ID within $FF, instead of checking if the chunk value is >$7F. This allows backwards compatibility with the normal loop system and gets the new system to work.