Extend the Level Select in Sonic 2
From Sonic Retro
(Original guide by Clownacy)
As you may have seen with Sonic 3 & Knuckles or ColinC10's Sonic 1 and 2, the Sonic 2 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 modifications 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 (look here).
This guide will target the Git 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 support for more than just two pages is available.
Contents
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 of ROM space. For reference, this is my example:
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, in order, 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, in order, 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):
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 this...
lea (Chunk_Table+$8C0).l,a1
...into this:
lea (PageX_RAM+$8C0).l,a1
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:
Page1_RAM = Chunk_Table
Page2_RAM = Page1_RAM+$8C0
Page3_RAM = Page2_RAM+$8C0
You see, a decompressed full-screen Level Select mappings file always takes up 2240 bytes. This is 0x8C0 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.
; 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"
With that done, go to MenuScreen_LevelSelect and add these two lines below the label:
clr.b (LevSel_Page).w
clr.w (Level_select_zone).w
Then replace the two "(Chunk_Table).l"s with "(Page1_RAM).l". After that you should see this (slighted formatted by myself):
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
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
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
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:
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
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:
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
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:
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
Once all of your tables are complete, we will bundle them all together with an index. Above LevelSelect_Order, paste this:
LevelSelect_OrderIndex: offsetTable
offsetTableEntry.w LevelSelect_Order ; 0
offsetTableEntry.w LevelSelect_Order2 ; 1
offsetTableEntry.w LevelSelect_Order3 ; 2
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 this...
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
...becomes this:
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
LevSel_IconTable
Not much to say here, I just use the Sound Test icon for 'NEXT' and 'PREV'. Though you can make your own.
Here's a list of the all the icons.
dc.b 0,0 ;0 EHZ
dc.b 7,7 ;2 CPZ
dc.b 8,8 ;4 ARZ
dc.b 6,6 ;6 CNZ
dc.b 2,2 ;8 HTZ
dc.b 5,5 ;$A MCZ
dc.b 4,4 ;$C OOZ
dc.b 1,1,1 ;$E MTZ
dc.b 9 ;$11 SCZ
dc.b $A ;$12 WFZ
dc.b $B ;$13 DEZ
dc.b $C ;$14 Special Stage
dc.b $E ;$15 Sound Test
dc.b 3 ;$16 HPZ
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:
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
Replace this...
move.w (Level_select_zone).w,d0
add.w d0,d0
move.w LevelSelect_Order(pc,d0.w),d0
...with this:
moveq #0,d0
move.b (LevSel_Page).w,d0
add.w 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
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:
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
Replace this...
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
...with this:
move.b (LevSel_Page).w,d0
add.w 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
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:
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
...
Replace this...
move.w (Level_select_zone).w,d0
lea (LevSel_IconTable).l,a3
...with this:
moveq #0,d0
move.b (LevSel_Page).w,d0
add.w d0,d0
move.w LevSel_IconTableIndex(pc,d0.w),d0
lea LevSel_IconTableIndex(pc,d0.w),a3
move.w (Level_select_zone).w,d0
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:
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
...
Replace this...
lea (Chunk_Table).l,a4
lea (LevSel_MarkTable).l,a5
...with this:
moveq #0,d0
move.b (LevSel_Page).w,d0
add.w d0,d0
move.w LevelSelect_MarkFieldsSubIndex(pc,d0.w),d0
jsr LevelSelect_MarkFieldsSubIndex(pc,d0.w)
And above the LevelSelect_MarkFields label, add this:
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
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 add an 'even at the end. Here's mine:
LevSel_LimitTable:
dc.b $16
dc.b $17
dc.b $16
even
Under LevSelControls, replace this...
subq.w #1,d0 ; decrease by 1
bcc.s + ; >= 0?
moveq #$15,d0 ; set to $15
...with this:
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
And replace this...
cmpi.w #$16,d0
blo.s + ; smaller than $16?
moveq #0,d0 ; if not, set to 0
...with this:
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
Now go to LevSelControls_CheckLR and replace this...
cmpi.w #$15,(Level_select_zone).w ; are we in the sound test?
...with this:
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?
Then go to LevelSelect_MarkFields (at the very bottom, right above the LevelSelect_DrawSoundNumber label) and replace this...
cmpi.w #$15,(Level_select_zone).w
...with this:
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?
Using the new pages
Now to make another choice. Do you want the 'NEXT' and 'PREV' to be activated using , like several other elements, or
/
/
, like the Sound Test?
Using Start
Now to make 'NEXT' and 'PREV' work. Like the others, they'll be activated using .
Go to LevelSelect_PressStart and after this...
bmi.w LevelSelect_Return ; sound test
...paste this:
cmpi.w #$3000,d0 ; was the selection Next Page?
bne.s ++
addq.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.s ++
subq.b #1,(LevSel_Page).w
bpl.s +
move.b #MaxPageNum,(LevSel_Page).w
+
bra.w LevelSelect_LoadPage
+
Using A/B/C
Now to make 'NEXT' and 'PREV' work. In this section, we'll make it so you activate them using the /
/
buttons, similar to the Sound Test.
Go to LevelSelect_PressStart and after this...
bmi.w LevelSelect_Return ; sound test
...paste this:
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
Now go to LevelSelect_Main, find this:
andi.b #button_start_mask,d0
bne.s LevelSelect_PressStart ; yes
bra.w LevelSelect_Main ; no
Replace it with this:
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
Go to LevelSelect_Return, and above the label, paste this:
LevelSelect_PressABC:
moveq #0,d0
move.b (LevSel_Page).w,d0
add.w 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.s ++
addq.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.s ++
subq.b #1,(LevSel_Page).w
bpl.s +
move.b #MaxPageNum,(LevSel_Page).w
+
bra.w LevelSelect_LoadPage
+
bra.w LevelSelect_Main
Then go to LevelSelect_StartZone, and above the label, paste this:
LevelSelect_LoadPage:
moveq #0,d0
move.b (LevSel_Page).w,d0
add.w 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
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...
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
...with this:
moveq #0,d0
move.b (LevSel_Page).w,d0
add.w 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
Also, remove these two lines found at the top of MenuScreen_LevelSelect:
clr.b (LevSel_Page).w
clr.w (Level_select_zone).w
(Optional) Using uncompressed mappings
Only follow this if you want uncompressed mappings for whatever reason.
Go to MenuScreen_LevelSelect and remove this:
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
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.)
LevelSelect_LoadPageSub1:
lea (MapEng_LevSel).l,a1
rts
LevelSelect_LoadPageSub2:
lea (MapEng_LevSel2).l,a1
rts
LevelSelect_LoadPageSub3:
lea (MapEng_LevSel3).l,a1
rts
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
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:
move.w LevelSelect_OrderIndex(pc,d0.w),d0
lea LevelSelect_OrderIndex(pc,d0.w),a0
Rearrange them to follow this format:
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
After fixing some 'branch out of range' errors, you should be good to go. Save and build.
|Extend the Level Select in Sonic 2]]