Actions

SCHG How-to

Difference between revisions of "Extend the Level Select in Sonic 2"

From Sonic Retro

(Fixed incompatible-with-word-sized-offsets code in Memorising LevSel_Page. Restored the line that clears Level_select_zone in LevelSelect_LoadPage, no idea why I removed it.)
m (Replaced spaces with tabs)
Line 57: Line 57:
  
 
Now we'll make our constants. Do so in this manner:
 
Now we'll make our constants. Do so in this manner:
<asm>Page1_RAM =                     Chunk_Table
+
<asm>Page1_RAM = Chunk_Table
Page2_RAM =                     Page1_RAM+$8C0
+
Page2_RAM = Page1_RAM+$8C0
Page3_RAM =                     Page2_RAM+$8C0</asm>
+
Page3_RAM = Page2_RAM+$8C0</asm>
  
 
You see, a decompressed full-screen Level Select mappings file '''always''' takes up 2240 bytes. This is 8C0 in hex. Doing this makes sure that no space is wasted, allowing more pages without encountering space issues.
 
You see, a decompressed full-screen Level Select mappings file '''always''' takes up 2240 bytes. This is 8C0 in hex. Doing this makes sure that no space is wasted, allowing more pages without encountering space issues.
Line 67: Line 67:
 
<asm>; level select page 1 screen mappings (Enigma compressed)
 
<asm>; level select page 1 screen mappings (Enigma compressed)
 
; byte_9ADE:
 
; byte_9ADE:
MapEng_LevSel:         BINCLUDE "mappings/misc/Level Select.bin"
+
MapEng_LevSel: BINCLUDE "mappings/misc/Level Select.bin"
  
 
; level select page 2 screen mappings (Enigma compressed)
 
; level select page 2 screen mappings (Enigma compressed)
  
MapEng_LevSel2:         BINCLUDE "mappings/misc/Level Select2.bin"
+
MapEng_LevSel2: BINCLUDE "mappings/misc/Level Select2.bin"
  
 
; level select page 3 screen mappings (Enigma compressed)
 
; level select page 3 screen mappings (Enigma compressed)
  
MapEng_LevSel3:         BINCLUDE "mappings/misc/Level Select3.bin"</asm>
+
MapEng_LevSel3: BINCLUDE "mappings/misc/Level Select3.bin"</asm>
  
  
Line 81: Line 81:
 
With that done, go to MenuScreen_LevelSelect and add these two lines below the label:
 
With that done, go to MenuScreen_LevelSelect and add these two lines below the label:
  
<asm>       clr.b   (LevSel_Page).w
+
<asm> clr.b (LevSel_Page).w
        clr.w   (Level_select_zone).w</asm>
+
clr.w (Level_select_zone).w</asm>
  
 
Then replace the two "(Chunk_Table).l"s with "(Page1_RAM).l". After that you should see this (slighted formatted by myself):
 
Then replace the two "(Chunk_Table).l"s with "(Page1_RAM).l". After that you should see this (slighted formatted by myself):
 
<asm>MenuScreen_LevelSelect:
 
<asm>MenuScreen_LevelSelect:
        lea     (Page1_RAM).l,a1
+
lea (Page1_RAM).l,a1
        lea     (MapEng_LevSel).l,a0   ; 2 bytes per 8x8 tile, compressed
+
lea (MapEng_LevSel).l,a0 ; 2 bytes per 8x8 tile, compressed
        move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
+
move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
        bsr.w   EniDec
+
bsr.w EniDec
  
        lea     (Page1_RAM).l,a1
+
lea (Page1_RAM).l,a1
        move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
+
move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
        moveq   #$27,d1
+
moveq #$27,d1
        moveq   #$1B,d2 ; 40x28 = whole screen
+
moveq #$1B,d2 ; 40x28 = whole screen
        bsr.w   JmpTo_PlaneMapToVRAM   ; display patterns</asm>
+
bsr.w JmpTo_PlaneMapToVRAM ; display patterns</asm>
  
 
The upper section of code decompresses the Enigma-compressed mappings to a RAM address, the second reads the decompressed data and applies the visuals. Using this we will load our new mappings. Here we have a choice: Do we make the new mappings decompress pre-fade in or post-fade in? Pre-fade will increase the duration of the loading screen (time the screen stays black), but loading it post-fade will run the risk of someone with speedy fingers loading the page before its mappings have fully decompressed.
 
The upper section of code decompresses the Enigma-compressed mappings to a RAM address, the second reads the decompressed data and applies the visuals. Using this we will load our new mappings. Here we have a choice: Do we make the new mappings decompress pre-fade in or post-fade in? Pre-fade will increase the duration of the loading screen (time the screen stays black), but loading it post-fade will run the risk of someone with speedy fingers loading the page before its mappings have fully decompressed.
Line 101: Line 101:
 
===={{red|Pre}}-Fade====
 
===={{red|Pre}}-Fade====
 
{{red|Copy the upper section of the code found above and paste it after}}
 
{{red|Copy the upper section of the code found above and paste it after}}
<asm>       lea     (MapEng_LevSel).l,a0   ; 2 bytes per 8x8 tile, compressed
+
<asm> lea (MapEng_LevSel).l,a0 ; 2 bytes per 8x8 tile, compressed
        lea     (Page1_RAM).l,a1
+
lea (Page1_RAM).l,a1
        move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
+
move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
        bsr.w   EniDec</asm>
+
bsr.w EniDec</asm>
  
 
===={{green|Post}}-Fade====
 
===={{green|Post}}-Fade====
Line 111: Line 111:
  
 
Then make some adjustments to make it load your second page by changing the "MapEng_LevSel" to "MapEng_LevSel2", and adding to the "Page1_RAM". Do so again for any other pages you have, using MapEng_LevSel'''X''' and using other available RAM spaces. The result should look like this:
 
Then make some adjustments to make it load your second page by changing the "MapEng_LevSel" to "MapEng_LevSel2", and adding to the "Page1_RAM". Do so again for any other pages you have, using MapEng_LevSel'''X''' and using other available RAM spaces. The result should look like this:
<asm>       lea     (Page2_RAM).l,a1
+
<asm> lea (Page2_RAM).l,a1
        lea     (MapEng_LevSel2).l,a0   ; 2 bytes per 8x8 tile, compressed
+
lea (MapEng_LevSel2).l,a0 ; 2 bytes per 8x8 tile, compressed
        move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
+
move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
        bsr.w   EniDec
+
bsr.w EniDec
 
          
 
          
        lea     (Page3_RAM).l,a1
+
lea (Page3_RAM).l,a1
        lea     (MapEng_LevSel3).l,a0   ; 2 bytes per 8x8 tile, compressed
+
lea (MapEng_LevSel3).l,a0 ; 2 bytes per 8x8 tile, compressed
        move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
+
move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
        bsr.w   EniDec</asm>
+
bsr.w EniDec</asm>
  
 
==Table expansion==
 
==Table expansion==
Line 125: Line 125:
 
Go to LevelSelect_Order. Remember where you placed your 'NEXT'? That's going to come into play now. Insert "dc.w $3000" into the table to match where the 'NEXT' is on your page 1 mappings. If you're following my example, it should look like this:
 
Go to LevelSelect_Order. Remember where you placed your 'NEXT'? That's going to come into play now. Insert "dc.w $3000" into the table to match where the 'NEXT' is on your page 1 mappings. If you're following my example, it should look like this:
 
<asm>LevelSelect_Order:
 
<asm>LevelSelect_Order:
        dc.w   emerald_hill_zone_act_1
+
dc.w emerald_hill_zone_act_1
        dc.w   emerald_hill_zone_act_2         ; 1
+
dc.w emerald_hill_zone_act_2 ; 1
        dc.w   chemical_plant_zone_act_1       ; 2
+
dc.w chemical_plant_zone_act_1 ; 2
        dc.w   chemical_plant_zone_act_2       ; 3
+
dc.w chemical_plant_zone_act_2 ; 3
        dc.w   aquatic_ruin_zone_act_1         ; 4
+
dc.w aquatic_ruin_zone_act_1 ; 4
        dc.w   aquatic_ruin_zone_act_2         ; 5
+
dc.w aquatic_ruin_zone_act_2 ; 5
        dc.w   casino_night_zone_act_1         ; 6
+
dc.w casino_night_zone_act_1 ; 6
        dc.w   casino_night_zone_act_2         ; 7
+
dc.w casino_night_zone_act_2 ; 7
        dc.w   hill_top_zone_act_1             ; 8
+
dc.w hill_top_zone_act_1 ; 8
        dc.w   hill_top_zone_act_2             ; 9
+
dc.w hill_top_zone_act_2 ; 9
        dc.w   mystic_cave_zone_act_1         ; 10
+
dc.w mystic_cave_zone_act_1 ; 10
        dc.w   mystic_cave_zone_act_2         ; 11
+
dc.w mystic_cave_zone_act_2 ; 11
        dc.w   oil_ocean_zone_act_1           ; 12
+
dc.w oil_ocean_zone_act_1 ; 12
        dc.w   oil_ocean_zone_act_2           ; 13
+
dc.w oil_ocean_zone_act_2 ; 13
        dc.w   $3000                           ; 14 - next page
+
dc.w $3000 ; 14 - next page
        dc.w   metropolis_zone_act_1           ; 15
+
dc.w metropolis_zone_act_1 ; 15
        dc.w   metropolis_zone_act_2           ; 16
+
dc.w metropolis_zone_act_2 ; 16
        dc.w   metropolis_zone_act_3           ; 17
+
dc.w metropolis_zone_act_3 ; 17
        dc.w   sky_chase_zone_act_1           ; 18
+
dc.w sky_chase_zone_act_1 ; 18
        dc.w   wing_fortress_zone_act_1       ; 19
+
dc.w wing_fortress_zone_act_1 ; 19
        dc.w   death_egg_zone_act_1           ; 20
+
dc.w death_egg_zone_act_1 ; 20
        dc.w   $4000                           ; 21 - special stage
+
dc.w $4000 ; 21 - special stage
        dc.w   $FFFF                           ; 22 - sound test</asm>
+
dc.w $FFFF ; 22 - sound test</asm>
  
  
 
We will now create our new pages' tables. In the place of 'PREV' you must place "dc.w $5000". My second page's table looks like this:
 
We will now create our new pages' tables. In the place of 'PREV' you must place "dc.w $5000". My second page's table looks like this:
 
<asm>LevelSelect_Order2:
 
<asm>LevelSelect_Order2:
        dc.w   emerald_hill_zone_act_1
+
dc.w emerald_hill_zone_act_1
        dc.w   emerald_hill_zone_act_2         ; 1
+
dc.w emerald_hill_zone_act_2 ; 1
        dc.w   death_egg_zone_act_1           ; 2
+
dc.w death_egg_zone_act_1 ; 2
        dc.w   chemical_plant_zone_act_2       ; 3
+
dc.w chemical_plant_zone_act_2 ; 3
        dc.w   aquatic_ruin_zone_act_1         ; 4
+
dc.w aquatic_ruin_zone_act_1 ; 4
        dc.w   aquatic_ruin_zone_act_2         ; 5
+
dc.w aquatic_ruin_zone_act_2 ; 5
        dc.w   casino_night_zone_act_1         ; 6
+
dc.w casino_night_zone_act_1 ; 6
        dc.w   casino_night_zone_act_2         ; 7
+
dc.w casino_night_zone_act_2 ; 7
        dc.w   hill_top_zone_act_1             ; 8
+
dc.w hill_top_zone_act_1 ; 8
        dc.w   hill_top_zone_act_2             ; 9
+
dc.w hill_top_zone_act_2 ; 9
        dc.w   mystic_cave_zone_act_1         ; 10
+
dc.w mystic_cave_zone_act_1 ; 10
        dc.w   mystic_cave_zone_act_2         ; 11
+
dc.w mystic_cave_zone_act_2 ; 11
        dc.w   oil_ocean_zone_act_1           ; 12
+
dc.w oil_ocean_zone_act_1 ; 12
        dc.w   oil_ocean_zone_act_2           ; 13
+
dc.w oil_ocean_zone_act_2 ; 13
        dc.w   $3000                           ; 14 - next page
+
dc.w $3000 ; 14 - next page
        dc.w   $5000                           ; 15 - prev page
+
dc.w $5000 ; 15 - prev page
        dc.w   metropolis_zone_act_1           ; 16
+
dc.w metropolis_zone_act_1 ; 16
        dc.w   metropolis_zone_act_2           ; 17
+
dc.w metropolis_zone_act_2 ; 17
        dc.w   metropolis_zone_act_3          ; 18
+
dc.w metropolis_zone_act_3          ; 18
        dc.w   sky_chase_zone_act_1           ; 19
+
dc.w sky_chase_zone_act_1 ; 19
        dc.w   wing_fortress_zone_act_1       ; 20
+
dc.w wing_fortress_zone_act_1 ; 20
        dc.w   death_egg_zone_act_1           ; 21
+
dc.w death_egg_zone_act_1 ; 21
        dc.w   $4000                           ; 22 - special stage
+
dc.w $4000 ; 22 - special stage
        dc.w   $FFFF                           ; 23 - sound test</asm>
+
dc.w $FFFF ; 23 - sound test</asm>
  
 
Once all of your tables are complete, we will bundle them all together with an index. Above LevelSelect_Order, paste this:
 
Once all of your tables are complete, we will bundle them all together with an index. Above LevelSelect_Order, paste this:
 
<asm>LevelSelect_OrderIndex: offsetTable
 
<asm>LevelSelect_OrderIndex: offsetTable
                offsetTableEntry.w LevelSelect_Order    ; 0
+
offsetTableEntry.w LevelSelect_Order    ; 0
                offsetTableEntry.w LevelSelect_Order2  ; 1
+
offsetTableEntry.w LevelSelect_Order2  ; 1
                offsetTableEntry.w LevelSelect_Order3  ; 2</asm>
+
offsetTableEntry.w LevelSelect_Order3  ; 2</asm>
  
 
For each page/table you have, make an entry in the offset table. They must be in order!
 
For each page/table you have, make an entry in the offset table. They must be in order!
Line 191: Line 191:
 
Do as you did with LevelSelect_Order: A table for each page, add an index (same format as the one used above, uses word-sized offsets) Note, if you placed your 'NEXT' before the last entry of the table, then all references to entries after it will be shifted by it. So:
 
Do as you did with LevelSelect_Order: A table for each page, add an index (same format as the one used above, uses word-sized offsets) Note, if you placed your 'NEXT' before the last entry of the table, then all references to entries after it will be shifted by it. So:
 
<asm>LevelSelect_SwitchTable:
 
<asm>LevelSelect_SwitchTable:
        dc.b $E
+
dc.b $E
        dc.b $F         ; 1
+
dc.b $F ; 1
        dc.b $11       ; 2
+
dc.b $11 ; 2
        dc.b $11       ; 3
+
dc.b $11 ; 3
        dc.b $12       ; 4
+
dc.b $12 ; 4
        dc.b $12       ; 5
+
dc.b $12 ; 5
        dc.b $13       ; 6
+
dc.b $13 ; 6
        dc.b $13       ; 7
+
dc.b $13 ; 7
        dc.b $14       ; 8
+
dc.b $14 ; 8
        dc.b $14       ; 9
+
dc.b $14 ; 9
        dc.b $15       ; 10
+
dc.b $15 ; 10
        dc.b $15       ; 11
+
dc.b $15 ; 11
        dc.b $C         ; 12
+
dc.b $C ; 12
        dc.b $D         ; 13
+
dc.b $D ; 13
        dc.b 0         ; 14
+
dc.b 0 ; 14
        dc.b 1         ; 15
+
dc.b 1 ; 15
        dc.b 1         ; 16
+
dc.b 1 ; 16
        dc.b 2         ; 17
+
dc.b 2 ; 17
        dc.b 4         ; 18
+
dc.b 4 ; 18
        dc.b 6         ; 19
+
dc.b 6 ; 19
        dc.b 8         ; 20
+
dc.b 8 ; 20
        dc.b $A         ; 21</asm>
+
dc.b $A ; 21</asm>
  
 
Becomes:
 
Becomes:
 
<asm>LevelSelect_SwitchTable:
 
<asm>LevelSelect_SwitchTable:
        dc.b $F
+
dc.b $F
        dc.b $10       ; 1
+
dc.b $10 ; 1
        dc.b $12       ; 2
+
dc.b $12 ; 2
        dc.b $12       ; 3
+
dc.b $12 ; 3
        dc.b $13       ; 4
+
dc.b $13 ; 4
        dc.b $13       ; 5
+
dc.b $13 ; 5
        dc.b $14       ; 6
+
dc.b $14 ; 6
        dc.b $14       ; 7
+
dc.b $14 ; 7
        dc.b $15       ; 8
+
dc.b $15 ; 8
        dc.b $15       ; 9
+
dc.b $15 ; 9
        dc.b $16       ; 10
+
dc.b $16 ; 10
        dc.b $16       ; 11
+
dc.b $16 ; 11
        dc.b $C         ; 12
+
dc.b $C ; 12
        dc.b $D         ; 13
+
dc.b $D ; 13
        dc.b $E         ; 14 NEXT
+
dc.b $E ; 14 NEXT
        dc.b 0         ; 15
+
dc.b 0 ; 15
        dc.b 1         ; 16
+
dc.b 1 ; 16
        dc.b 1         ; 17
+
dc.b 1 ; 17
        dc.b 2         ; 18
+
dc.b 2 ; 18
        dc.b 4         ; 19
+
dc.b 4 ; 19
        dc.b 6         ; 20
+
dc.b 6 ; 20
        dc.b 8         ; 21
+
dc.b 8 ; 21
        dc.b $A         ; 22</asm>
+
dc.b $A ; 22</asm>
  
  
Line 254: Line 254:
 
Starting with LevelSelect_Order, go to LevelSelect_PressStart, you should see this:
 
Starting with LevelSelect_Order, go to LevelSelect_PressStart, you should see this:
 
<asm>LevelSelect_PressStart:
 
<asm>LevelSelect_PressStart:
        move.w (Level_select_zone).w,d0
+
move.w (Level_select_zone).w,d0
        add.w   d0,d0
+
add.w d0,d0
        move.w LevelSelect_Order(pc,d0.w),d0
+
move.w LevelSelect_Order(pc,d0.w),d0
        bmi.w   LevelSelect_Return     ; sound test
+
bmi.w LevelSelect_Return ; sound test
        cmpi.w #$4000,d0
+
cmpi.w #$4000,d0
        bne.s   LevelSelect_StartZone</asm>
+
bne.s LevelSelect_StartZone</asm>
  
 
Replace
 
Replace
<asm>       move.w (Level_select_zone).w,d0
+
<asm> move.w (Level_select_zone).w,d0
        add.w   d0,d0
+
add.w d0,d0
        move.w LevelSelect_Order(pc,d0.w),d0</asm>
+
move.w LevelSelect_Order(pc,d0.w),d0</asm>
  
 
With
 
With
<asm>       moveq   #0,d0
+
<asm> moveq #0,d0
        move.b (LevSel_Page).w,d0
+
move.b (LevSel_Page).w,d0
        add.b   d0,d0
+
add.b d0,d0
        move.w LevelSelect_OrderIndex(pc,d0.w),d0
+
move.w LevelSelect_OrderIndex(pc,d0.w),d0
        lea     LevelSelect_OrderIndex(pc,d0.w),a0
+
lea LevelSelect_OrderIndex(pc,d0.w),a0
        move.w (Level_select_zone).w,d0
+
move.w (Level_select_zone).w,d0
        add.w   d0,d0
+
add.w d0,d0
        movea.w (a0,d0.w),d0</asm>
+
movea.w (a0,d0.w),d0</asm>
  
  
Line 282: Line 282:
 
====LevelSelect_SwitchTable====
 
====LevelSelect_SwitchTable====
 
Go to LevSelControls_SwitchSide, you should see this:
 
Go to LevSelControls_SwitchSide, you should see this:
<asm>LevSelControls_SwitchSide:     ; not in soundtest, not up/down pressed
+
<asm>LevSelControls_SwitchSide: ; not in soundtest, not up/down pressed
        move.b (Ctrl_1_Press).w,d1
+
move.b (Ctrl_1_Press).w,d1
        andi.b #button_left_mask|button_right_mask,d1
+
andi.b #button_left_mask|button_right_mask,d1
        beq.s   +                               ; no direction key pressed
+
beq.s + ; no direction key pressed
        move.w (Level_select_zone).w,d0       ; left or right pressed
+
move.w (Level_select_zone).w,d0 ; left or right pressed
        move.b LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table
+
move.b LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table
        move.w d0,(Level_select_zone).w
+
move.w d0,(Level_select_zone).w
 
+
 
+
        rts</asm>
+
rts</asm>
  
 
Replace
 
Replace
<asm>       move.w  (Level_select_zone).w,d0       ; left or right pressed
+
<asm> move.w  (Level_select_zone).w,d0 ; left or right pressed
        move.b  LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table</asm>
+
move.b  LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table</asm>
  
 
With
 
With
<asm>       move.b (LevSel_Page).w,d0
+
<asm> move.b (LevSel_Page).w,d0
        add.b   d0,d0
+
add.b d0,d0
        move.w LevelSelect_SwitchTableIndex(pc,d0.w),d0
+
move.w LevelSelect_SwitchTableIndex(pc,d0.w),d0
        lea     LevelSelect_SwitchTableIndex(pc,d0.w),a0
+
lea LevelSelect_SwitchTableIndex(pc,d0.w),a0
        move.w (Level_select_zone).w,d0       ; left or right pressed
+
move.w (Level_select_zone).w,d0 ; left or right pressed
        move.b (a0,d0.w),d0 ; set selected zone according to table</asm>
+
move.b (a0,d0.w),d0 ; set selected zone according to table</asm>
  
 
LevSelControls_SwitchSide will now dynamically load LevelSelect_SwitchTable/2/3 depending on what page you're on.
 
LevSelControls_SwitchSide will now dynamically load LevelSelect_SwitchTable/2/3 depending on what page you're on.
Line 310: Line 310:
 
Go to LevelSelect_DrawIcon, you should see this:
 
Go to LevelSelect_DrawIcon, you should see this:
 
<asm>LevelSelect_DrawIcon:
 
<asm>LevelSelect_DrawIcon:
        move.w (Level_select_zone).w,d0
+
move.w (Level_select_zone).w,d0
        lea     (LevSel_IconTable).l,a3
+
lea (LevSel_IconTable).l,a3
        lea     (a3,d0.w),a3
+
lea (a3,d0.w),a3
        lea     (Chunk_Table+$8C0).l,a1
+
lea (Chunk_Table+$8C0).l,a1
        moveq   #0,d0
+
moveq #0,d0
        move.b (a3),d0
+
move.b (a3),d0
        lsl.w   #3,d0
+
lsl.w #3,d0
        move.w d0,d1
+
move.w d0,d1
        add.w   d0,d0
+
add.w d0,d0
        add.w   d1,d0
+
add.w d1,d0
 
...</asm>
 
...</asm>
  
 
Replace
 
Replace
<asm>       move.w (Level_select_zone).w,d0
+
<asm> move.w (Level_select_zone).w,d0
        lea     (LevSel_IconTable).l,a3</asm>
+
lea (LevSel_IconTable).l,a3</asm>
  
 
With
 
With
<asm>       moveq   #0,d0
+
<asm> moveq #0,d0
        move.b (LevSel_Page).w,d0
+
move.b (LevSel_Page).w,d0
        add.b   d0,d0
+
add.b d0,d0
        move.w LevSel_IconTableIndex(pc,d0.w),d0
+
move.w LevSel_IconTableIndex(pc,d0.w),d0
        lea     LevSel_IconTableIndex(pc,d0.w),a3
+
lea LevSel_IconTableIndex(pc,d0.w),a3
        move.w (Level_select_zone).w,d0</asm>
+
move.w (Level_select_zone).w,d0</asm>
  
 
LevelSelect_DrawIcon will now dynamically load LevSel_IconTable/2/3 depending on what page you're on.
 
LevelSelect_DrawIcon will now dynamically load LevSel_IconTable/2/3 depending on what page you're on.
Line 342: Line 342:
 
Go to LevelSelect_MarkFields, you should find this:
 
Go to LevelSelect_MarkFields, you should find this:
 
<asm>LevelSelect_MarkFields:
 
<asm>LevelSelect_MarkFields:
        lea     (Chunk_Table).l,a4
+
lea (Chunk_Table).l,a4
        lea     (LevSel_MarkTable).l,a5
+
lea (LevSel_MarkTable).l,a5
        lea     (VDP_data_port).l,a6
+
lea (VDP_data_port).l,a6
        moveq   #0,d0
+
moveq #0,d0
        move.w (Level_select_zone).w,d0
+
move.w (Level_select_zone).w,d0
 
...</asm>
 
...</asm>
  
 
Replace
 
Replace
<asm>       lea     (Chunk_Table).l,a4
+
<asm> lea (Chunk_Table).l,a4
        lea     (LevSel_MarkTable).l,a5</asm>
+
lea (LevSel_MarkTable).l,a5</asm>
  
 
With
 
With
<asm>       moveq   #0,d0
+
<asm> moveq #0,d0
        move.b (LevSel_Page).w,d0
+
move.b (LevSel_Page).w,d0
        add.b   d0,d0
+
add.b d0,d0
        move.w LevelSelect_MarkFieldsSubIndex(pc,d0.w),d0
+
move.w LevelSelect_MarkFieldsSubIndex(pc,d0.w),d0
        jsr     LevelSelect_MarkFieldsSubIndex(pc,d0.w)</asm>
+
jsr LevelSelect_MarkFieldsSubIndex(pc,d0.w)</asm>
  
 
And above the LevelSelect_MarkFields label, add this:
 
And above the LevelSelect_MarkFields label, add this:
 
<asm>LevelSelect_MarkFieldsSubIndex: offsetTable
 
<asm>LevelSelect_MarkFieldsSubIndex: offsetTable
        offsetTableEntry.w LevelSelect_MarkFieldsSub1   ; 0
+
offsetTableEntry.w LevelSelect_MarkFieldsSub1 ; 0
        offsetTableEntry.w LevelSelect_MarkFieldsSub2   ; 1
+
offsetTableEntry.w LevelSelect_MarkFieldsSub2 ; 1
        offsetTableEntry.w LevelSelect_MarkFieldsSub3   ; 2
+
offsetTableEntry.w LevelSelect_MarkFieldsSub3 ; 2
  
 
LevelSelect_MarkFieldsSub1:
 
LevelSelect_MarkFieldsSub1:
        lea     (Page1_RAM).l,a4
+
lea (Page1_RAM).l,a4
        lea     (LevSel_MarkTable).l,a5
+
lea (LevSel_MarkTable).l,a5
        rts
+
rts
  
 
LevelSelect_MarkFieldsSub2:
 
LevelSelect_MarkFieldsSub2:
        lea     (Page2_RAM).l,a4
+
lea (Page2_RAM).l,a4
        lea     (LevSel_MarkTable2).l,a5
+
lea (LevSel_MarkTable2).l,a5
        rts
+
rts
  
 
LevelSelect_MarkFieldsSub3:
 
LevelSelect_MarkFieldsSub3:
        lea     (Page3_RAM).l,a4
+
lea (Page3_RAM).l,a4
        lea     (LevSel_MarkTable3).l,a5
+
lea (LevSel_MarkTable3).l,a5
        rts</asm>
+
rts</asm>
  
 
For each mapping, an entry, mapping decompression RAM address, and MarkTable is added. Since I have three pages, three 'subs' entries exist. Adjust accordingly.
 
For each mapping, an entry, mapping decompression RAM address, and MarkTable is added. Since I have three pages, three 'subs' entries exist. Adjust accordingly.
Line 390: Line 390:
 
Find these values for each page, and compile a small table, all in bytes, label it "LevSel_LimitTable" and place it above LevSelControls. Don't forget to EVEN it. Here's mine:
 
Find these values for each page, and compile a small table, all in bytes, label it "LevSel_LimitTable" and place it above LevSelControls. Don't forget to EVEN it. Here's mine:
 
<asm>LevSel_LimitTable:
 
<asm>LevSel_LimitTable:
        dc.b   $16
+
dc.b $16
        dc.b   $17
+
dc.b $17
        dc.b   $16
+
dc.b $16
        even</asm>
+
even</asm>
  
 
Under LevSelControls, replace
 
Under LevSelControls, replace
<asm>       subq.w #1,d0   ; decrease by 1
+
<asm> subq.w #1,d0 ; decrease by 1
        bcc.s   +       ; >= 0?
+
bcc.s + ; >= 0?
        moveq   #$15,d0 ; set to $15</asm>
+
moveq #$15,d0 ; set to $15</asm>
  
 
With
 
With
<asm>       subq.w #1,d0   ; decrease by 1
+
<asm> subq.w #1,d0 ; decrease by 1
        bcc.s   +       ; >= 0?
+
bcc.s + ; >= 0?
        moveq   #0,d0
+
moveq #0,d0
        move.b (LevSel_Page).w,d3
+
move.b (LevSel_Page).w,d3
        move.b LevSel_LimitTable(pc,d3.w),d0</asm>
+
move.b LevSel_LimitTable(pc,d3.w),d0</asm>
  
 
And
 
And
<asm>       cmpi.w #$16,d0
+
<asm> cmpi.w #$16,d0
        blo.s   +       ; smaller than $16?
+
blo.s + ; smaller than $16?
        moveq   #0,d0   ; if not, set to 0</asm>
+
moveq #0,d0 ; if not, set to 0</asm>
  
 
With
 
With
<asm>       moveq   #0,d2
+
<asm> moveq #0,d2
        move.b (LevSel_Page).w,d3
+
move.b (LevSel_Page).w,d3
        move.b LevSel_LimitTable(pc,d3.w),d2
+
move.b LevSel_LimitTable(pc,d3.w),d2
  
        cmp.w   d2,d0
+
cmp.w d2,d0
        bls.s   +       ; smaller than $18?
+
bls.s + ; smaller than $18?
        moveq   #0,d0   ; if not, set to 0</asm>
+
moveq #0,d0 ; if not, set to 0</asm>
  
 
Now go to LevSelControls_CheckLR and replace
 
Now go to LevSelControls_CheckLR and replace
<asm>       cmpi.w #$15,(Level_select_zone).w     ; are we in the sound test?</asm>
+
<asm> cmpi.w #$15,(Level_select_zone).w ; are we in the sound test?</asm>
  
 
With
 
With
 
<asm>
 
<asm>
        moveq   #0,d0
+
moveq #0,d0
        move.b (LevSel_Page).w,d3
+
move.b (LevSel_Page).w,d3
        move.b LevSel_LimitTable(pc,d3.w),d0
+
move.b LevSel_LimitTable(pc,d3.w),d0
        move.w (Level_select_zone).w,d1
+
move.w (Level_select_zone).w,d1
        cmp.w   d0,d1   ; are we in the sound test?</asm>
+
cmp.w d0,d1 ; are we in the sound test?</asm>
  
 
Then go to LevelSelect_MarkFields (at the very bottom, right above the LevelSelect_DrawSoundNumber label) and replace
 
Then go to LevelSelect_MarkFields (at the very bottom, right above the LevelSelect_DrawSoundNumber label) and replace
<asm>       cmpi.w #$15,(Level_select_zone).w</asm>
+
<asm> cmpi.w #$15,(Level_select_zone).w</asm>
  
 
With
 
With
<asm>       moveq   #0,d0
+
<asm> moveq #0,d0
        move.b (LevSel_Page).w,d1
+
move.b (LevSel_Page).w,d1
        lea     (LevSel_LimitTable).l,a0
+
lea (LevSel_LimitTable).l,a0
        move.b (a0,d1.w),d0
+
move.b (a0,d1.w),d0
        move.w (Level_select_zone).w,d1
+
move.w (Level_select_zone).w,d1
        cmp.w   d0,d1   ; are we in the sound test?</asm>
+
cmp.w d0,d1 ; are we in the sound test?</asm>
  
 
==Using the new pages==
 
==Using the new pages==
Line 451: Line 451:
  
 
{{red|Go to LevelSelect_PressStart and after}}
 
{{red|Go to LevelSelect_PressStart and after}}
<asm>       bmi.w   LevelSelect_Return     ; sound test</asm>
+
<asm> bmi.w LevelSelect_Return ; sound test</asm>
  
 
{{red|Paste this:}}
 
{{red|Paste this:}}
<asm>       cmpi.w #$3000,d0       ; was the selection Next Page?
+
<asm> cmpi.w #$3000,d0 ; was the selection Next Page?
        bne.w   ++
+
bne.w ++
        add.b   #1,(LevSel_Page).w
+
add.b #1,(LevSel_Page).w
        cmpi.b #MaxPageNum+1,(LevSel_Page).w
+
cmpi.b #MaxPageNum+1,(LevSel_Page).w
        bne.s   +
+
bne.s +
        clr.b   (LevSel_Page).w
+
clr.b (LevSel_Page).w
 
+
 
+
        bra.w   LevelSelect_LoadPage
+
bra.w LevelSelect_LoadPage
 
+
 
+
        cmpi.w #$5000,d0       ; was the selection Prev Page?
+
cmpi.w #$5000,d0 ; was the selection Prev Page?
        bne.w   ++
+
bne.w ++
        sub.b   #1,(LevSel_Page).w
+
sub.b #1,(LevSel_Page).w
        bpl.s   +
+
bpl.s +
        move.b #MaxPageNum,(LevSel_Page).w
+
move.b #MaxPageNum,(LevSel_Page).w
 
+
 
+
        bra.w   LevelSelect_LoadPage
+
bra.w LevelSelect_LoadPage
 
+</asm>
 
+</asm>
  
Line 477: Line 477:
  
 
{{green|Go to LevelSelect_PressStart and after}}
 
{{green|Go to LevelSelect_PressStart and after}}
<asm>       bmi.w   LevelSelect_Return     ; sound test</asm>
+
<asm> bmi.w LevelSelect_Return ; sound test</asm>
  
 
{{green|Paste this:}}
 
{{green|Paste this:}}
<asm>       cmpi.w #$3000,d0       ; was the selection Next Page?
+
<asm> cmpi.w #$3000,d0 ; was the selection Next Page?
        beq.w   LevelSelect_Main
+
beq.w LevelSelect_Main
        cmpi.w #$5000,d0       ; was the selection Prev Page?
+
cmpi.w #$5000,d0 ; was the selection Prev Page?
        beq.w   LevelSelect_Main</asm>
+
beq.w LevelSelect_Main</asm>
  
  
 
{{green|Now go to LevelSelect_Main, find this:}}
 
{{green|Now go to LevelSelect_Main, find this:}}
<asm>       andi.b #button_start_mask,d0
+
<asm> andi.b #button_start_mask,d0
        bne.s   LevelSelect_PressStart ; yes
+
bne.s LevelSelect_PressStart ; yes
        bra.w   LevelSelect_Main       ; no</asm>
+
bra.w LevelSelect_Main ; no</asm>
  
 
{{green|Replace it with this:}}
 
{{green|Replace it with this:}}
<asm>       andi.b #button_start_mask,d0
+
<asm> andi.b #button_start_mask,d0
        bne.s   LevelSelect_PressStart ; yes
+
bne.s LevelSelect_PressStart ; yes
        move.b (Ctrl_1_Press).w,d0
+
move.b (Ctrl_1_Press).w,d0
        or.b   (Ctrl_2_Press).w,d0
+
or.b (Ctrl_2_Press).w,d0
        andi.b #button_A_mask|button_B_mask|button_C_mask,d0
+
andi.b #button_A_mask|button_B_mask|button_C_mask,d0
        bne.w   LevelSelect_PressABC   ; yes
+
bne.w LevelSelect_PressABC ; yes
        bra.w   LevelSelect_Main       ; no</asm>
+
bra.w LevelSelect_Main ; no</asm>
  
 
{{green|Go to LevelSelect_Return, and above the label, paste this:}}
 
{{green|Go to LevelSelect_Return, and above the label, paste this:}}
 
<asm>LevelSelect_PressABC:
 
<asm>LevelSelect_PressABC:
        moveq   #0,d0
+
moveq #0,d0
        move.b (LevSel_Page).w,d0
+
move.b (LevSel_Page).w,d0
        add.b   d0,d0
+
add.b d0,d0
        lea     LevelSelect_OrderIndex(pc),a0
+
lea LevelSelect_OrderIndex(pc),a0
        move.w (a0,d0.w),d0
+
move.w (a0,d0.w),d0
        lea     (a0,d0.w),a0
+
lea (a0,d0.w),a0
        move.w (Level_select_zone).w,d0
+
move.w (Level_select_zone).w,d0
        add.w   d0,d0
+
add.w d0,d0
        movea.w (a0,d0.w),d0
+
movea.w (a0,d0.w),d0
        cmpi.w #$3000,d0       ; was the selection Next Page?
+
cmpi.w #$3000,d0 ; was the selection Next Page?
        bne.w   ++
+
bne.w ++
        add.b   #1,(LevSel_Page).w
+
add.b #1,(LevSel_Page).w
        cmpi.b #MaxPageNum+1,(LevSel_Page).w
+
cmpi.b #MaxPageNum+1,(LevSel_Page).w
        bne.s   +
+
bne.s +
        clr.b   (LevSel_Page).w
+
clr.b (LevSel_Page).w
 
+
 
+
        bra.w   LevelSelect_LoadPage
+
bra.w LevelSelect_LoadPage
 
+
 
+
        cmpi.w #$5000,d0       ; was the selection Prev Page?
+
cmpi.w #$5000,d0 ; was the selection Prev Page?
        bne.w   ++
+
bne.w ++
        sub.b   #1,(LevSel_Page).w
+
sub.b #1,(LevSel_Page).w
        bpl.s   +
+
bpl.s +
        move.b #MaxPageNum,(LevSel_Page).w
+
move.b #MaxPageNum,(LevSel_Page).w
 
+
 
+
        bra.w   LevelSelect_LoadPage
+
bra.w LevelSelect_LoadPage
 
+
 
+
        bra.w   LevelSelect_Main</asm>
+
bra.w LevelSelect_Main</asm>
  
 
Then go to LevelSelect_StartZone, and above the label, paste this:
 
Then go to LevelSelect_StartZone, and above the label, paste this:
 
<asm>LevelSelect_LoadPage:
 
<asm>LevelSelect_LoadPage:
        moveq   #0,d0
+
moveq #0,d0
        move.b (LevSel_Page).w,d0
+
move.b (LevSel_Page).w,d0
        add.b   d0,d0
+
add.b d0,d0
        move.w LevelSelect_LoadPageSubIndex(pc,d0.w),d0
+
move.w LevelSelect_LoadPageSubIndex(pc,d0.w),d0
        jsr     LevelSelect_LoadPageSubIndex(pc,d0.w)
+
jsr LevelSelect_LoadPageSubIndex(pc,d0.w)
  
        move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
+
move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
        moveq   #$27,d1
+
moveq #$27,d1
        moveq   #$1B,d2 ; 40x28 = whole screen
+
moveq #$1B,d2 ; 40x28 = whole screen
        bsr.w   JmpTo_PlaneMapToVRAM   ; display patterns
+
bsr.w JmpTo_PlaneMapToVRAM ; display patterns
  
clr.w   (Level_select_zone).w
+
clr.w (Level_select_zone).w
  
        moveq   #0,d3
+
moveq #0,d3
        bsr.w   LevelSelect_DrawSoundNumber
+
bsr.w LevelSelect_DrawSoundNumber
  
        bra.w   LevelSelect_Main
+
bra.w LevelSelect_Main
  
LevelSelect_LoadPageSubIndex:   offsetTable
+
LevelSelect_LoadPageSubIndex: offsetTable
        offsetTableEntry.w LevelSelect_LoadPageSub1     ; 0
+
offsetTableEntry.w LevelSelect_LoadPageSub1 ; 0
        offsetTableEntry.w LevelSelect_LoadPageSub2     ; 1
+
offsetTableEntry.w LevelSelect_LoadPageSub2 ; 1
        offsetTableEntry.w LevelSelect_LoadPageSub3     ; 2
+
offsetTableEntry.w LevelSelect_LoadPageSub3 ; 2
  
 
LevelSelect_LoadPageSub1:
 
LevelSelect_LoadPageSub1:
        lea     (Page1_RAM).l,a1
+
lea (Page1_RAM).l,a1
        rts
+
rts
  
 
LevelSelect_LoadPageSub2:
 
LevelSelect_LoadPageSub2:
        lea     (Page2_RAM).l,a1
+
lea (Page2_RAM).l,a1
        rts
+
rts
  
 
LevelSelect_LoadPageSub3:
 
LevelSelect_LoadPageSub3:
        lea     (Page3_RAM).l,a1
+
lea (Page3_RAM).l,a1
        rts</asm>
+
rts</asm>
  
 
Each LoadPageSub contains a constant defined back in [[#Setting up constants|Setting up constants]]. As with the other indexes, there is one entry per page. Adjust accordingly.
 
Each LoadPageSub contains a constant defined back in [[#Setting up constants|Setting up constants]]. As with the other indexes, there is one entry per page. Adjust accordingly.
Line 573: Line 573:
  
 
Go to MenuScreen_LevelSelect and replace this:
 
Go to MenuScreen_LevelSelect and replace this:
<asm>       lea     (Page1_RAM).l,a1
+
<asm> lea (Page1_RAM).l,a1
        move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
+
move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
        moveq   #$27,d1
+
moveq #$27,d1
        moveq   #$1B,d2 ; 40x28 = whole screen
+
moveq #$1B,d2 ; 40x28 = whole screen
        bsr.w   JmpTo_PlaneMapToVRAM   ; display patterns</asm>
+
bsr.w JmpTo_PlaneMapToVRAM ; display patterns</asm>
  
 
With this:
 
With this:
<asm>       moveq   #0,d0
+
<asm> moveq #0,d0
        move.b (LevSel_Page).w,d0
+
move.b (LevSel_Page).w,d0
        add.b   d0,d0
+
add.b d0,d0
        move.w LevelSelect_LoadPageSubIndex(pc,d0.w),d0
+
move.w LevelSelect_LoadPageSubIndex(pc,d0.w),d0
        jsr     LevelSelect_LoadPageSubIndex(pc,d0.w)
+
jsr LevelSelect_LoadPageSubIndex(pc,d0.w)
  
        move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
+
move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
        moveq   #$27,d1
+
moveq #$27,d1
        moveq   #$1B,d2 ; 40x28 = whole screen
+
moveq #$1B,d2 ; 40x28 = whole screen
        bsr.w   JmpTo_PlaneMapToVRAM   ; display patterns</asm>
+
bsr.w JmpTo_PlaneMapToVRAM ; display patterns</asm>
  
 
Also, remove these two lines found at the top of MenuScreen_LevelSelect:
 
Also, remove these two lines found at the top of MenuScreen_LevelSelect:
<asm>       clr.b   (LevSel_Page).w
+
<asm> clr.b (LevSel_Page).w
        clr.w   (Level_select_zone).w</asm>
+
clr.w (Level_select_zone).w</asm>
  
 
==(Optional) Using uncompressed mappings==
 
==(Optional) Using uncompressed mappings==
Line 599: Line 599:
  
 
Go to MenuScreen_LevelSelect and remove this:
 
Go to MenuScreen_LevelSelect and remove this:
<asm>       lea     (Page1_RAM).l,a1
+
<asm> lea (Page1_RAM).l,a1
        lea     (MapEng_LevSel).l,a0   ; 2 bytes per 8x8 tile, compressed
+
lea (MapEng_LevSel).l,a0 ; 2 bytes per 8x8 tile, compressed
        move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
+
move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0
        bsr.w   EniDec</asm>
+
bsr.w EniDec</asm>
  
 
Do the same for the other pages'.
 
Do the same for the other pages'.
Line 610: Line 610:
 
Next, head to LevelSelect_LoadPageSub1 and LevelSelect_MarkFieldsSub and begin replacing the references to PageX_RAM with the in-ROM equivalents (MapEng_LevSel, MapEng_LevSel2, etc.)
 
Next, head to LevelSelect_LoadPageSub1 and LevelSelect_MarkFieldsSub and begin replacing the references to PageX_RAM with the in-ROM equivalents (MapEng_LevSel, MapEng_LevSel2, etc.)
 
<asm>LevelSelect_LoadPageSub1:
 
<asm>LevelSelect_LoadPageSub1:
        lea     (MapEng_LevSel).l,a1
+
lea (MapEng_LevSel).l,a1
        rts
+
rts
  
 
LevelSelect_LoadPageSub2:
 
LevelSelect_LoadPageSub2:
        lea     (MapEng_LevSel2).l,a1
+
lea (MapEng_LevSel2).l,a1
        rts
+
rts
  
 
LevelSelect_LoadPageSub3:
 
LevelSelect_LoadPageSub3:
        lea     (MapEng_LevSel3).l,a1
+
lea (MapEng_LevSel3).l,a1
        rts</asm>
+
rts</asm>
  
  
 
<asm>LevelSelect_MarkFieldsSub1:
 
<asm>LevelSelect_MarkFieldsSub1:
        lea     (MapEng_LevSel).l,a4
+
lea (MapEng_LevSel).l,a4
        lea     (LevSel_MarkTable).l,a5
+
lea (LevSel_MarkTable).l,a5
        rts
+
rts
  
 
LevelSelect_MarkFieldsSub2:
 
LevelSelect_MarkFieldsSub2:
        lea     (MapEng_LevSel2).l,a4
+
lea (MapEng_LevSel2).l,a4
        lea     (LevSel_MarkTable2).l,a5
+
lea (LevSel_MarkTable2).l,a5
        rts
+
rts
  
 
LevelSelect_MarkFieldsSub3:
 
LevelSelect_MarkFieldsSub3:
        lea     (MapEng_LevSel3).l,a4
+
lea (MapEng_LevSel3).l,a4
        lea     (LevSel_MarkTable3).l,a5
+
lea (LevSel_MarkTable3).l,a5
        rts</asm>
+
rts</asm>
  
  
Line 650: Line 650:
  
 
...regarding lines such as these:
 
...regarding lines such as these:
<asm>       move.w LevelSelect_OrderIndex(pc,d0.w),d0
+
<asm> move.w LevelSelect_OrderIndex(pc,d0.w),d0
        lea     LevelSelect_OrderIndex(pc,d0.w),a0</asm>
+
lea LevelSelect_OrderIndex(pc,d0.w),a0</asm>
  
  
 
Rearrange them to follow this format:
 
Rearrange them to follow this format:
<asm>       lea     LevelSelect_OrderIndex(pc),a0   ; if this still doesn't work, use (LevelSelect_OrderIndex).l,a0 instead
+
<asm> lea LevelSelect_OrderIndex(pc),a0 ; if this still doesn't work, use (LevelSelect_OrderIndex).l,a0 instead
        move.w (a0,d0.w),d0
+
move.w (a0,d0.w),d0
        lea     (a0,d0.w),a0</asm>
+
lea (a0,d0.w),a0</asm>
  
 
After fixing some 'branch out of range' errors, you should be good to go. Save and build.
 
After fixing some 'branch out of range' errors, you should be good to go. Save and build.

Revision as of 12:35, 17 August 2014

(Original guide by Clownacy)

As you may have seen with S3K or ColinC10's Sonic 1 and 2, the S2 Level Select can become really cramped, really fast. Wouldn't it be great if you could somehow extend the menu? To do this, several changes will have to be made to the Level Select's code. This does not require a total rewrite, so your own hacks can coexist with this one. I recommend that you are familiar with the Level Select's many tables, as you will be creating your own (HINT: look here).

This guide will focus on the HG disassembly.

The result will be your average S2 Level Select with multiple pages, you toggle between the pages with the 'NEXT' and 'PREV' options. The switch is instantaneous (no fading, no loading screen) and supports numerous pages.

Making your mappings

First, open a plane map editor (or a hex editor) and edit your Level Select.bin to feature a 'NEXT' option and, optionally, a 'PREV' option. The 'PREV' will simply loop from the first page to the last page. You can also remove the zeroes from the Sound Test selection, as they are redundant and are overwritten almost immediately. Doing so will save you a few bytes. For reference, this is my example:

ExtendedLevSel Example1.png

Take note of the location of your 'NEXT'. Mine is placed after Oil Ocean, and before Metropolis.


Now we shall create our second page, so start listing some new zones! Also, Sound Test must be present on all pages and it must be the last entry. Use the stock Level Select as a template: The area allocated to the emblem must be left untouched, as should the Sound Test.

If this new page is your last (there are no pages after it), then you might not want to list a 'NEXT' option unless you want page looping. If it is, for example, the second out of three (there will be a following page), then you must list both 'NEXT' and 'PREV'.

To give you an idea of what I mean, here is another example (second page out of three):

ExtendedLevSel Example2.png

Once done, save as Level Select2.bin. The next page will be 3, then 4, etc.

Repeat the process for each new page you want.

Setting up constants

Before we touch any ASM, this is a good time to define the 1 byte large "LevSel_Page" RAM address constant. You should be able to do that on your own.

Now, wherever you feel it's appropriate, define the "MaxPageNum" constant. This constant will be used in calculations and checks regarding the maximum number of pages in your Level Select, such as those involved in page looping. Remember, however, that zero counts as a number, so if you have three pages, the constant must be defined as 2 (0,1,2).

In the next section, Mappings decompression, we'll be setting up the decompression of the mappings you made earlier. To make this easier, we'll define some constants, otherwise changing the decompression locations would require changing several lines, doing this requires only the one.

Again, where appropriate, define the following constants:

  • Page1_RAM
  • Page2_RAM
  • Page3_RAM


The number of constants need to match the number of pages you plan to have. Adjust accordingly.

By default, the first page occupies Chunk_Table, while the icons' mappings occupy Chunk_Table+$8C0, we're going to rearrange this a bit to fit with our new pages.

Go to MenuScreen_LevelSelect and change <asm> lea (Chunk_Table+$8C0).l,a1</asm>

to <asm> lea (PageX_RAM+$8C0).l,a1</asm>

With X being the number of your last page. Go to LevelSelect_DrawIcon and do the same thing.

Now we'll make our constants. Do so in this manner: <asm>Page1_RAM = Chunk_Table Page2_RAM = Page1_RAM+$8C0 Page3_RAM = Page2_RAM+$8C0</asm>

You see, a decompressed full-screen Level Select mappings file always takes up 2240 bytes. This is 8C0 in hex. Doing this makes sure that no space is wasted, allowing more pages without encountering space issues.

Mappings decompression

Inside your s2.asm, go to MapEng_LevSel and, beneath it, add entries for your new mappings. <asm>; level select page 1 screen mappings (Enigma compressed)

byte_9ADE

MapEng_LevSel: BINCLUDE "mappings/misc/Level Select.bin"

level select page 2 screen mappings (Enigma compressed)

MapEng_LevSel2: BINCLUDE "mappings/misc/Level Select2.bin"

level select page 3 screen mappings (Enigma compressed)

MapEng_LevSel3: BINCLUDE "mappings/misc/Level Select3.bin"</asm>


With that done, go to MenuScreen_LevelSelect and add these two lines below the label:

<asm> clr.b (LevSel_Page).w clr.w (Level_select_zone).w</asm>

Then replace the two "(Chunk_Table).l"s with "(Page1_RAM).l". After that you should see this (slighted formatted by myself): <asm>MenuScreen_LevelSelect: lea (Page1_RAM).l,a1 lea (MapEng_LevSel).l,a0 ; 2 bytes per 8x8 tile, compressed move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0 bsr.w EniDec

lea (Page1_RAM).l,a1 move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0 moveq #$27,d1 moveq #$1B,d2 ; 40x28 = whole screen bsr.w JmpTo_PlaneMapToVRAM ; display patterns</asm>

The upper section of code decompresses the Enigma-compressed mappings to a RAM address, the second reads the decompressed data and applies the visuals. Using this we will load our new mappings. Here we have a choice: Do we make the new mappings decompress pre-fade in or post-fade in? Pre-fade will increase the duration of the loading screen (time the screen stays black), but loading it post-fade will run the risk of someone with speedy fingers loading the page before its mappings have fully decompressed.

Pre-Fade

Copy the upper section of the code found above and paste it after <asm> lea (MapEng_LevSel).l,a0 ; 2 bytes per 8x8 tile, compressed lea (Page1_RAM).l,a1 move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0 bsr.w EniDec</asm>

Post-Fade

Copy the upper section of the code found above and paste it before the LevelSelect_Main label


Then make some adjustments to make it load your second page by changing the "MapEng_LevSel" to "MapEng_LevSel2", and adding to the "Page1_RAM". Do so again for any other pages you have, using MapEng_LevSelX and using other available RAM spaces. The result should look like this: <asm> lea (Page2_RAM).l,a1 lea (MapEng_LevSel2).l,a0 ; 2 bytes per 8x8 tile, compressed move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0 bsr.w EniDec

lea (Page3_RAM).l,a1 lea (MapEng_LevSel3).l,a0 ; 2 bytes per 8x8 tile, compressed move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0 bsr.w EniDec</asm>

Table expansion

LevelSelect_Order

Go to LevelSelect_Order. Remember where you placed your 'NEXT'? That's going to come into play now. Insert "dc.w $3000" into the table to match where the 'NEXT' is on your page 1 mappings. If you're following my example, it should look like this: <asm>LevelSelect_Order: dc.w emerald_hill_zone_act_1 dc.w emerald_hill_zone_act_2 ; 1 dc.w chemical_plant_zone_act_1 ; 2 dc.w chemical_plant_zone_act_2 ; 3 dc.w aquatic_ruin_zone_act_1 ; 4 dc.w aquatic_ruin_zone_act_2 ; 5 dc.w casino_night_zone_act_1 ; 6 dc.w casino_night_zone_act_2 ; 7 dc.w hill_top_zone_act_1 ; 8 dc.w hill_top_zone_act_2 ; 9 dc.w mystic_cave_zone_act_1 ; 10 dc.w mystic_cave_zone_act_2 ; 11 dc.w oil_ocean_zone_act_1 ; 12 dc.w oil_ocean_zone_act_2 ; 13 dc.w $3000 ; 14 - next page dc.w metropolis_zone_act_1 ; 15 dc.w metropolis_zone_act_2 ; 16 dc.w metropolis_zone_act_3 ; 17 dc.w sky_chase_zone_act_1 ; 18 dc.w wing_fortress_zone_act_1 ; 19 dc.w death_egg_zone_act_1 ; 20 dc.w $4000 ; 21 - special stage dc.w $FFFF ; 22 - sound test</asm>


We will now create our new pages' tables. In the place of 'PREV' you must place "dc.w $5000". My second page's table looks like this: <asm>LevelSelect_Order2: dc.w emerald_hill_zone_act_1 dc.w emerald_hill_zone_act_2 ; 1 dc.w death_egg_zone_act_1 ; 2 dc.w chemical_plant_zone_act_2 ; 3 dc.w aquatic_ruin_zone_act_1 ; 4 dc.w aquatic_ruin_zone_act_2 ; 5 dc.w casino_night_zone_act_1 ; 6 dc.w casino_night_zone_act_2 ; 7 dc.w hill_top_zone_act_1 ; 8 dc.w hill_top_zone_act_2 ; 9 dc.w mystic_cave_zone_act_1 ; 10 dc.w mystic_cave_zone_act_2 ; 11 dc.w oil_ocean_zone_act_1 ; 12 dc.w oil_ocean_zone_act_2 ; 13 dc.w $3000 ; 14 - next page dc.w $5000 ; 15 - prev page dc.w metropolis_zone_act_1 ; 16 dc.w metropolis_zone_act_2 ; 17 dc.w metropolis_zone_act_3  ; 18 dc.w sky_chase_zone_act_1 ; 19 dc.w wing_fortress_zone_act_1 ; 20 dc.w death_egg_zone_act_1 ; 21 dc.w $4000 ; 22 - special stage dc.w $FFFF ; 23 - sound test</asm>

Once all of your tables are complete, we will bundle them all together with an index. Above LevelSelect_Order, paste this: <asm>LevelSelect_OrderIndex: offsetTable offsetTableEntry.w LevelSelect_Order  ; 0 offsetTableEntry.w LevelSelect_Order2  ; 1 offsetTableEntry.w LevelSelect_Order3  ; 2</asm>

For each page/table you have, make an entry in the offset table. They must be in order!

We'll be following this template for several other tables.


LevelSelect_SwitchTable

Do as you did with LevelSelect_Order: A table for each page, add an index (same format as the one used above, uses word-sized offsets) Note, if you placed your 'NEXT' before the last entry of the table, then all references to entries after it will be shifted by it. So: <asm>LevelSelect_SwitchTable: dc.b $E dc.b $F ; 1 dc.b $11 ; 2 dc.b $11 ; 3 dc.b $12 ; 4 dc.b $12 ; 5 dc.b $13 ; 6 dc.b $13 ; 7 dc.b $14 ; 8 dc.b $14 ; 9 dc.b $15 ; 10 dc.b $15 ; 11 dc.b $C ; 12 dc.b $D ; 13 dc.b 0 ; 14 dc.b 1 ; 15 dc.b 1 ; 16 dc.b 2 ; 17 dc.b 4 ; 18 dc.b 6 ; 19 dc.b 8 ; 20 dc.b $A ; 21</asm>

Becomes: <asm>LevelSelect_SwitchTable: dc.b $F dc.b $10 ; 1 dc.b $12 ; 2 dc.b $12 ; 3 dc.b $13 ; 4 dc.b $13 ; 5 dc.b $14 ; 6 dc.b $14 ; 7 dc.b $15 ; 8 dc.b $15 ; 9 dc.b $16 ; 10 dc.b $16 ; 11 dc.b $C ; 12 dc.b $D ; 13 dc.b $E ; 14 NEXT dc.b 0 ; 15 dc.b 1 ; 16 dc.b 1 ; 17 dc.b 2 ; 18 dc.b 4 ; 19 dc.b 6 ; 20 dc.b 8 ; 21 dc.b $A ; 22</asm>


LevSel_IconTable

Not much to say here, I just use the Sound Test icon for 'NEXT' and 'PREV'. Though you can make your own.


LevSel_MarkTable

Do not give this one an index. You should know what to do by this point.

Dynamic data

LevelSelect_Order

Now we make use of those tables!

Starting with LevelSelect_Order, go to LevelSelect_PressStart, you should see this: <asm>LevelSelect_PressStart: move.w (Level_select_zone).w,d0 add.w d0,d0 move.w LevelSelect_Order(pc,d0.w),d0 bmi.w LevelSelect_Return ; sound test cmpi.w #$4000,d0 bne.s LevelSelect_StartZone</asm>

Replace <asm> move.w (Level_select_zone).w,d0 add.w d0,d0 move.w LevelSelect_Order(pc,d0.w),d0</asm>

With <asm> moveq #0,d0 move.b (LevSel_Page).w,d0 add.b d0,d0 move.w LevelSelect_OrderIndex(pc,d0.w),d0 lea LevelSelect_OrderIndex(pc,d0.w),a0 move.w (Level_select_zone).w,d0 add.w d0,d0 movea.w (a0,d0.w),d0</asm>


LevelSelect_PressStart will now dynamically load LevelSelect_Order/2/3 depending on what page you're on.


LevelSelect_SwitchTable

Go to LevSelControls_SwitchSide, you should see this: <asm>LevSelControls_SwitchSide: ; not in soundtest, not up/down pressed move.b (Ctrl_1_Press).w,d1 andi.b #button_left_mask|button_right_mask,d1 beq.s + ; no direction key pressed move.w (Level_select_zone).w,d0 ; left or right pressed move.b LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table move.w d0,(Level_select_zone).w + rts</asm>

Replace <asm> move.w (Level_select_zone).w,d0 ; left or right pressed move.b LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table</asm>

With <asm> move.b (LevSel_Page).w,d0 add.b d0,d0 move.w LevelSelect_SwitchTableIndex(pc,d0.w),d0 lea LevelSelect_SwitchTableIndex(pc,d0.w),a0 move.w (Level_select_zone).w,d0 ; left or right pressed move.b (a0,d0.w),d0 ; set selected zone according to table</asm>

LevSelControls_SwitchSide will now dynamically load LevelSelect_SwitchTable/2/3 depending on what page you're on.


LevSel_IconTable

Go to LevelSelect_DrawIcon, you should see this: <asm>LevelSelect_DrawIcon: move.w (Level_select_zone).w,d0 lea (LevSel_IconTable).l,a3 lea (a3,d0.w),a3 lea (Chunk_Table+$8C0).l,a1 moveq #0,d0 move.b (a3),d0 lsl.w #3,d0 move.w d0,d1 add.w d0,d0 add.w d1,d0 ...</asm>

Replace <asm> move.w (Level_select_zone).w,d0 lea (LevSel_IconTable).l,a3</asm>

With <asm> moveq #0,d0 move.b (LevSel_Page).w,d0 add.b d0,d0 move.w LevSel_IconTableIndex(pc,d0.w),d0 lea LevSel_IconTableIndex(pc,d0.w),a3 move.w (Level_select_zone).w,d0</asm>

LevelSelect_DrawIcon will now dynamically load LevSel_IconTable/2/3 depending on what page you're on.


LevSel_MarkTable

This will be quite a bit different from the other three.

Go to LevelSelect_MarkFields, you should find this: <asm>LevelSelect_MarkFields: lea (Chunk_Table).l,a4 lea (LevSel_MarkTable).l,a5 lea (VDP_data_port).l,a6 moveq #0,d0 move.w (Level_select_zone).w,d0 ...</asm>

Replace <asm> lea (Chunk_Table).l,a4 lea (LevSel_MarkTable).l,a5</asm>

With <asm> moveq #0,d0 move.b (LevSel_Page).w,d0 add.b d0,d0 move.w LevelSelect_MarkFieldsSubIndex(pc,d0.w),d0 jsr LevelSelect_MarkFieldsSubIndex(pc,d0.w)</asm>

And above the LevelSelect_MarkFields label, add this: <asm>LevelSelect_MarkFieldsSubIndex: offsetTable offsetTableEntry.w LevelSelect_MarkFieldsSub1 ; 0 offsetTableEntry.w LevelSelect_MarkFieldsSub2 ; 1 offsetTableEntry.w LevelSelect_MarkFieldsSub3 ; 2

LevelSelect_MarkFieldsSub1: lea (Page1_RAM).l,a4 lea (LevSel_MarkTable).l,a5 rts

LevelSelect_MarkFieldsSub2: lea (Page2_RAM).l,a4 lea (LevSel_MarkTable2).l,a5 rts

LevelSelect_MarkFieldsSub3: lea (Page3_RAM).l,a4 lea (LevSel_MarkTable3).l,a5 rts</asm>

For each mapping, an entry, mapping decompression RAM address, and MarkTable is added. Since I have three pages, three 'subs' entries exist. Adjust accordingly.

Fixing the Sound Test

Now for some hardcoded values to become a little less hardcoded. The hardcoded Sound Test functions!

Note where your Sound Test is (in the context of Level_select_zone). Count how many entries down LevelSelect_Order/2/3 "dc.w $FFFF ; sound test" is in hex, and there's your value. In vanilla S2, Sound Test is $15, but on your new pages, this is likely to have changed.

Find these values for each page, and compile a small table, all in bytes, label it "LevSel_LimitTable" and place it above LevSelControls. Don't forget to EVEN it. Here's mine: <asm>LevSel_LimitTable: dc.b $16 dc.b $17 dc.b $16 even</asm>

Under LevSelControls, replace <asm> subq.w #1,d0 ; decrease by 1 bcc.s + ; >= 0? moveq #$15,d0 ; set to $15</asm>

With <asm> subq.w #1,d0 ; decrease by 1 bcc.s + ; >= 0? moveq #0,d0 move.b (LevSel_Page).w,d3 move.b LevSel_LimitTable(pc,d3.w),d0</asm>

And <asm> cmpi.w #$16,d0 blo.s + ; smaller than $16? moveq #0,d0 ; if not, set to 0</asm>

With <asm> moveq #0,d2 move.b (LevSel_Page).w,d3 move.b LevSel_LimitTable(pc,d3.w),d2

cmp.w d2,d0 bls.s + ; smaller than $18? moveq #0,d0 ; if not, set to 0</asm>

Now go to LevSelControls_CheckLR and replace <asm> cmpi.w #$15,(Level_select_zone).w ; are we in the sound test?</asm>

With <asm> moveq #0,d0 move.b (LevSel_Page).w,d3 move.b LevSel_LimitTable(pc,d3.w),d0 move.w (Level_select_zone).w,d1 cmp.w d0,d1 ; are we in the sound test?</asm>

Then go to LevelSelect_MarkFields (at the very bottom, right above the LevelSelect_DrawSoundNumber label) and replace <asm> cmpi.w #$15,(Level_select_zone).w</asm>

With <asm> moveq #0,d0 move.b (LevSel_Page).w,d1 lea (LevSel_LimitTable).l,a0 move.b (a0,d1.w),d0 move.w (Level_select_zone).w,d1 cmp.w d0,d1 ; are we in the sound test?</asm>

Using the new pages

Now to make another choice. Do you want the 'NEXT' and 'PREV' to be activated using the start button, like several other elements, or A/B/C, like the Sound Test?


Using Start

Now to make 'NEXT' and 'PREV' work. Like the others, they'll be activated using the start button.

Go to LevelSelect_PressStart and after <asm> bmi.w LevelSelect_Return ; sound test</asm>

Paste this: <asm> cmpi.w #$3000,d0 ; was the selection Next Page? bne.w ++ add.b #1,(LevSel_Page).w cmpi.b #MaxPageNum+1,(LevSel_Page).w bne.s + clr.b (LevSel_Page).w + bra.w LevelSelect_LoadPage + cmpi.w #$5000,d0 ; was the selection Prev Page? bne.w ++ sub.b #1,(LevSel_Page).w bpl.s + move.b #MaxPageNum,(LevSel_Page).w + bra.w LevelSelect_LoadPage +</asm>


Using A/B/C

Now to make 'NEXT' and 'PREV' work. In this section, we'll make it so you activate them using the A/B/C buttons, similar to the Sound Test.

Go to LevelSelect_PressStart and after <asm> bmi.w LevelSelect_Return ; sound test</asm>

Paste this: <asm> cmpi.w #$3000,d0 ; was the selection Next Page? beq.w LevelSelect_Main cmpi.w #$5000,d0 ; was the selection Prev Page? beq.w LevelSelect_Main</asm>


Now go to LevelSelect_Main, find this: <asm> andi.b #button_start_mask,d0 bne.s LevelSelect_PressStart ; yes bra.w LevelSelect_Main ; no</asm>

Replace it with this: <asm> andi.b #button_start_mask,d0 bne.s LevelSelect_PressStart ; yes move.b (Ctrl_1_Press).w,d0 or.b (Ctrl_2_Press).w,d0 andi.b #button_A_mask|button_B_mask|button_C_mask,d0 bne.w LevelSelect_PressABC ; yes bra.w LevelSelect_Main ; no</asm>

Go to LevelSelect_Return, and above the label, paste this: <asm>LevelSelect_PressABC: moveq #0,d0 move.b (LevSel_Page).w,d0 add.b d0,d0 lea LevelSelect_OrderIndex(pc),a0 move.w (a0,d0.w),d0 lea (a0,d0.w),a0 move.w (Level_select_zone).w,d0 add.w d0,d0 movea.w (a0,d0.w),d0 cmpi.w #$3000,d0 ; was the selection Next Page? bne.w ++ add.b #1,(LevSel_Page).w cmpi.b #MaxPageNum+1,(LevSel_Page).w bne.s + clr.b (LevSel_Page).w + bra.w LevelSelect_LoadPage + cmpi.w #$5000,d0 ; was the selection Prev Page? bne.w ++ sub.b #1,(LevSel_Page).w bpl.s + move.b #MaxPageNum,(LevSel_Page).w + bra.w LevelSelect_LoadPage + bra.w LevelSelect_Main</asm>

Then go to LevelSelect_StartZone, and above the label, paste this: <asm>LevelSelect_LoadPage: moveq #0,d0 move.b (LevSel_Page).w,d0 add.b d0,d0 move.w LevelSelect_LoadPageSubIndex(pc,d0.w),d0 jsr LevelSelect_LoadPageSubIndex(pc,d0.w)

move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0 moveq #$27,d1 moveq #$1B,d2 ; 40x28 = whole screen bsr.w JmpTo_PlaneMapToVRAM ; display patterns

clr.w (Level_select_zone).w

moveq #0,d3 bsr.w LevelSelect_DrawSoundNumber

bra.w LevelSelect_Main

LevelSelect_LoadPageSubIndex: offsetTable offsetTableEntry.w LevelSelect_LoadPageSub1 ; 0 offsetTableEntry.w LevelSelect_LoadPageSub2 ; 1 offsetTableEntry.w LevelSelect_LoadPageSub3 ; 2

LevelSelect_LoadPageSub1: lea (Page1_RAM).l,a1 rts

LevelSelect_LoadPageSub2: lea (Page2_RAM).l,a1 rts

LevelSelect_LoadPageSub3: lea (Page3_RAM).l,a1 rts</asm>

Each LoadPageSub contains a constant defined back in Setting up constants. As with the other indexes, there is one entry per page. Adjust accordingly.

(Optional) Memorising LevSel_Page

If you want the page you are on to be memorised, à la Level_Select_Zone, so that when you return to the Level Select after leaving it, you're placed back where you were when you left, you'll have to do the following. This one's a little messy, as if you want that behaviour, you cannot have your page mappings decompress post-fade. Make this so before continuing.

Go to MenuScreen_LevelSelect and replace this: <asm> lea (Page1_RAM).l,a1 move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0 moveq #$27,d1 moveq #$1B,d2 ; 40x28 = whole screen bsr.w JmpTo_PlaneMapToVRAM ; display patterns</asm>

With this: <asm> moveq #0,d0 move.b (LevSel_Page).w,d0 add.b d0,d0 move.w LevelSelect_LoadPageSubIndex(pc,d0.w),d0 jsr LevelSelect_LoadPageSubIndex(pc,d0.w)

move.l #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0 moveq #$27,d1 moveq #$1B,d2 ; 40x28 = whole screen bsr.w JmpTo_PlaneMapToVRAM ; display patterns</asm>

Also, remove these two lines found at the top of MenuScreen_LevelSelect: <asm> clr.b (LevSel_Page).w clr.w (Level_select_zone).w</asm>

(Optional) Using uncompressed mappings

Only follow this if you want uncompressed mappings for whatever reason.

Go to MenuScreen_LevelSelect and remove this: <asm> lea (Page1_RAM).l,a1 lea (MapEng_LevSel).l,a0 ; 2 bytes per 8x8 tile, compressed move.w #make_art_tile(ArtTile_VRAM_Start,0,0),d0 bsr.w EniDec</asm>

Do the same for the other pages'.

Replace the nearby reference to Page1_RAM with MapEng_LevSel, note that you will have deleted that reference if you chose to memorise LevSel_Page.

Next, head to LevelSelect_LoadPageSub1 and LevelSelect_MarkFieldsSub and begin replacing the references to PageX_RAM with the in-ROM equivalents (MapEng_LevSel, MapEng_LevSel2, etc.) <asm>LevelSelect_LoadPageSub1: lea (MapEng_LevSel).l,a1 rts

LevelSelect_LoadPageSub2: lea (MapEng_LevSel2).l,a1 rts

LevelSelect_LoadPageSub3: lea (MapEng_LevSel3).l,a1 rts</asm>


<asm>LevelSelect_MarkFieldsSub1: lea (MapEng_LevSel).l,a4 lea (LevSel_MarkTable).l,a5 rts

LevelSelect_MarkFieldsSub2: lea (MapEng_LevSel2).l,a4 lea (LevSel_MarkTable2).l,a5 rts

LevelSelect_MarkFieldsSub3: lea (MapEng_LevSel3).l,a4 lea (LevSel_MarkTable3).l,a5 rts</asm>


Doing this removes the decompression to RAM and instead loads directly from ROM. Decompressed plane mappings are quite a bit larger than their Enigma-compressed counterparts. My first page's mappings went to 2240 bytes (the usual size for full-screen mappings) from 340.

Conclusion

If you encounter any of these errors...

> > >s2.asm(11686): error: addressing mode not allowed on 68000

> > > move.b LevelSelect_OrderIndex(pc,d0.w),d0


...regarding lines such as these: <asm> move.w LevelSelect_OrderIndex(pc,d0.w),d0 lea LevelSelect_OrderIndex(pc,d0.w),a0</asm>


Rearrange them to follow this format: <asm> lea LevelSelect_OrderIndex(pc),a0 ; if this still doesn't work, use (LevelSelect_OrderIndex).l,a0 instead move.w (a0,d0.w),d0 lea (a0,d0.w),a0</asm>

After fixing some 'branch out of range' errors, you should be good to go. Save and build.

SCHG How-To Guide: Sonic the Hedgehog 2 (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix Super Sonic Bugs | Use Correct Height When Roll Jumping | Fix Jump Height Bug When Exiting Water | Fix Screen Boundary Spin Dash Bug | Correct Drowning Bugs | Fix Camera Y Position for Tails | Fix Tails Subanimation Error | Fix Tails' Respawn Speeds | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix Rexon Crash | Fix Monitor Collision Bug | Fix EHZ Deformation Bug | Correct CPZ Boss Attack Behavior | Fix Bug in ARZ Boss Arrow's Platform Behavior | Fix ARZ Boss Walking on Air Glitch | Fix ARZ Boss Sprite Behavior | Fix Multiple CNZ Boss Bugs | Fix HTZ Background Scrolling Mountains | Fix OOZ Launcher Speed Up Glitch | Fix DEZ Giant Mech Collision Glitch | Fix Boss Deconstruction Behavior | Fix Speed Bugs | Fix 14 Continues Cheat | Fix Debug Mode Crash | Fix 99+ Lives | Fix Sonic 2's Sega Screen
Design Choices
Remove the Air Speed Cap | Disable Floor Collision While Dying | Modify Super Sonic Transformation Methods & Behavior | Enable/Disable Tails in Certain Levels | Collide with Water After Being Hurt | Retain Rings When Returning at a Star Post | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Insert LZ Water Ripple Effect | Restore Lost CPZ Boss Feature | Prevent SCZ Tornado Spin Dash Death | Improve ObjectMove Subroutines | Port S3K Rings Manager | Port S3K Object Manager | Port S3K Priority Manager | Edit Level Order with ASM‎ | Alter Ring Requirements in Special Stages | Make Special Stage Characters Use Normal DPLCs | Speed Up Ring Loss Process | Change spike behaviour in Sonic 2
Adding Features
Create Insta-kill and High Jump Monitors | Create Clone and Special Stage Monitors | Port Knuckles
Sound Features
Expand Music Index to Start at $00 | Port Sonic 1 Sound Driver | Port Sonic 2 Clone Driver | Port Sonic 3 Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Expand the Music Index to Start at $00 (Sonic 2 Clone Driver Version) | Play Different Songs Per Act
Extending the Game
Extend the Level Index Past $10 | Extend the Level Select | Extend Water Tables | Add Extra Characters | Free Up 2 Universal SSTs