(This tutorial will be updated soon) Here's how to get an option menu working in Sonic 1, using the level select, and seperaing the two. First off you'd need to have the game read the level select as ASCII. You're gonna need to replace the LevelMenuText with... Code: LevelMenuText: dc.b "GREEN HILL ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "LABYRINTH STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "MARBLE ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "STAR LIGHT ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "SPRING YARD ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "SCRAP BRAIN ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "FINAL ZONE " dc.b "SPECIAL STAGE " dc.b "SOUND TEST " even OptionMenuText: dc.b "GREEN HILL ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "MARBLE ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "SPRING YARD ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "LABYRINTH STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "STAR LIGHT ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "SCRAP BRAIN ZONE STAGE 1" dc.b " STAGE 2" dc.b " STAGE 3" dc.b "FINAL ZONE " dc.b "SPECIAL STAGE " dc.b "SOUND TEST " You can change the option text to anything later go to Loc_3598 and replace Code: add.w d3,d0 ; combine char with VRAM setting move.w d0,(a6) ; send to VRAM dbf d2,Loc_3588 rts with... Code: cmp.w #$40, d0 ; Check for $40 (End of ASCII number area) blt.s @notText ; If this is not an ASCII text character, branch sub.w #$3,d0 ; Subtract an extra 3 (Compensate for missing characters in the font) @notText: sub.w #$30,d0 ; Subtract #$33 (Convert to S2 font from ASCII) add.w d3,d0 ; combine char with VRAM setting move.w d0,(a6) ; send to VRAM dbf d2,Loc_3588 rts and go to levsel_ChgSnd and change the addi.b #7,d0 to addi.b #4,d0 The way I have it, Pushing start enters the Options screen first. Since The Level Select And the Region Check code won't be needed, it will be deleted. Go to loc_317C and make bcs.s Title_ChkRegion branch to loc_3210 instead Here is what I have Code: cmpi.w #$1C00,d0 ; has Sonic object passed x-position $1C00? bcs.s loc_3210; ; if not, branch move.b #0,($FFFFF600).w ; go to Sega screen rts ; =========================================================================== ; Region Check Is Gone ; =========================================================================== ; The Level Select Code is gone ; =========================================================================== loc_3210: Now the Start Button should bring you to a glitched level select. time to fix it. open any tile editor, I done it with TLP (Tile Layer Pro), and open artunc/menutext twice. Fix this by having it to read 0123456789$-=>AB CDEFGHIJKLMNOPQR STUVWXYZ> Then everything should read fine. Time to seperate. Go to LevSelTextLoad and make a check for Options Code: LevSelTextLoad: cmpi.b #1,(LevelselectRam) ; Is this the level select? beq LevSelLoad ; if so, load level select text lea (OptionMenuText).l,a1 ; Load the Option text bra TextRead ; continue here LevSelLoad: lea (LevelMenuText).l,a1 ; load level select text TextRead: and another further down Code: loc_34FE: ; XREF: LevSelTextLoad+26j move.l d4,4(a6) bsr.w LevSel_ChgLine addi.l #$800000,d4 dbf d1,loc_34FE moveq #0,d0 move.w ($FFFFFF82).w,d0 move.w d0,d1 move.l #$62100003,d4 lsl.w #7,d0 swap d0 add.l d0,d4 cmpi.b #1,(LevelselectRam) beq LevSelLoad2 lea (OptionMenuText).l,a1 bra TextRead2 LevSelLoad2: lea (LevelMenuText).l,a1 TextRead2: Now we can work with setting up the options. Find some free ram to use. mine reads as LevelSelectRam = $FFFFF5C1 Go to LevSelControls, go to line 5 and change it to read your first option mine Code: LevSelControls: ; XREF: LevelSelect move.b ($FFFFF605).w,d1 andi.b #3,d1 ; is up/down pressed and held? bne.s LevSel_UpDown ; if yes, branch subq.w #1,($FFFFFF80).w ; subtract 1 from time to next move bpl.s LevSel_Char ; if time remains, branch do the same with Levsel_UpDown Code: LevSel_UpDown: move.w #$B,($FFFFFF80).w ; reset time delay move.b ($FFFFF604).w,d1 andi.b #3,d1 ; is up/down pressed? beq.s LevSel_Char ; if not, branch move.w ($FFFFFF82).w,d0 btst #0,d1 ; is up pressed? beq.s LevSel_Down ; if not, branch subq.w #1,d0 ; move up 1 selection bcc.s LevSel_Down moveq #$14,d0 ; if selection moves below 0, jump to selection $14 Now you can program in each option, leaving $14 as Sound Test an example to use Code: cmpi.b #1,(LevelSelectRam) beq LevSel_NoMove tst.w ($FFFFFF82).w ; is item $00 selected? bne.s LevSel_Debug_Mode ; if not, branch Now what's needed is an option for the LevelSelect. This is how I got mine to work Code: LevSel_TheLevelSelect: cmpi.b #1,(LevelSelectRam) beq LevSel_NoMove cmpi.w #$02,($FFFFFF82).w ; is item $14 selected? bne LevSel_SndTest ; if not, branch move.b ($FFFFF605).w,d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq.s LevSel_NoMove Move.b #1,(LevelSelectRam) jsr Titlescreen rts with this, Option 2 is the level Select. you can change it though. however what's needed is to have the Level Select pointers set for Level Select and not options. We need to make some checks. LevSel_Level, change it to Code: Option_Level_SS: ; Levsel_Level_SS loads Level Select Pointers, this jumps back to LevelSelect JMP LevelSelect ; =========================================================================== LevSel_Level: ; Stay at the Level Select if this is Options tst.b (LevelSelectRam) beq Option_Level_SS ; XREF: LevSel_Level_SS andi.w #$3FFF,d0 cmpi.b #1,(LevelSelectRam) bne LevelSelect move.w d0,($FFFFFE10).w ; set level number in LevelSelect, below the move.w ($FFFFFF82) Code: tst.b (LevelSelectRam) beq SkipLSelect cmpi.w #$14,d0 ; have you selected item $14 (sound test)? bne LevSel_Level_SS ; if not, go to Level/SS subroutine bra JapCredits SkipLSelect: cmpi.w #$14,d0 ; have you selected item $14 (sound test)? bne.s Option_Level_SS ; if not, skip the pointers, return to LevelSelect JapCredits: If everything worked out you can change the text. an example Code: OptionMenuText: dc.b "CHARACTER " ; Character Change dc.b "DEBUGGER " ; Activate Debug Mode dc.b "LEVEL SELECT " ; This is to switch to the level select dc.b "START " ; just start the game dc.b "NULL " ; This on down.. dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " dc.b "NULL " ; .. Here does nothing yet. dc.b "SOUND TEST " even and there you go. So far I haven't seen a bug, except Sound Test works only in options and not Level Select. If anything else, let me know. Also, suggestions on improving this is welcomed. Edit2: There were 2 bugs. 1. Game Over needs to reset the Level Select Ram 2. Ending needs to reset it too. Instead of adding a Clr.b to ending and game over, go to SegaScreen: and add a clr (levelselectram) anywhere. I put one between where it loads Sega logo patterns and mappings, below bsr.w nemdec above Lea ($Ff0000).l,a1. This fixes both and resets at SegaScreen.
You don't mention on the ascii menu conversion section that you need the sonic 2 font and palette files (as mentioned in https://forums.sonicretro.org/index.php?threads/how-to-convert-sonic-1-level-select-to-ascii.31729/), also when I tried to edit the menutext file in two seperate tile editors they were just garbled tiles. I have found another bug, you are referencing "LevSel_Char" twice but you haven't created that label anywhere and its not in the existing code, LevSel_CharOK however is, in section LevSelControls and Levsel_UpDown At your example "Now you can program in each option, leaving $14 as Sound Test" would that go under the sound test code under LevelSelect: ? Also after porting the code to work with the github dissasembly and fixing all the branching issues i am having an error with this line Code: LevSel_Level_SS: add.w d0,d0 move.w LevSel_Ptrs(pc,d0.w),d0 ; load level number <---- Error : Illegal value (148)
First, the font, if you prefer the sonic 2 font, use it instead and ignore the TLP usage. I preferred the Sonic 1 font and use TLP to shift art over the other correcting text issues. Edit 1: Look to Levsel_sndtest. The way I did it, so it won't get jumbled up I copied levsel_sndtest, edit the first line to be in order one above the other. In other words... Tst.b #$01 ... #$13 #$14 ... It is possible to go further past, up to $FF but it won't be visible (though I haven't done this and may need testing.) For Options that need left and right to change and numbers/letters beside, I'm currently working on it Edit 2: I have not messed with this line at all and everything ran correctly, seeing level select pointers are right below it, at least in Hivebrain. I would assume that changing it to levsel_Level_SS: JMP new_routine (whatever it may be) at the start. Move the rest of the code above the pointers with the new routine name. It should fix it up
Part 2 - More Pages If you feel some options needs another page, like a list of characters, more levels and such. Here is what's needed. In each option you made, add Code: cmpi.b #2,(LevelSelectRam) beq Character_Selector to the start of it's code here is my example. Code: Levsel_Char: cmpi.b #2,(LevelSelectRam) beq Character_Selector tst.b (LevelSelectRam) bne LevSel_NoMove tst.w ($FFFFFF82).w ; is item $14 selected? bne.s LevSel_Debug_Mode ; if not, branch tst.w ($FFFFFF82).w ; is item $14 selected? bne LevSel_Stages ; if not, branch move.b ($FFFFF605).w,d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq LevSel_NoMove Move.b #2,(LevelSelectRam) ;Change The Page to Character Select jsr LevSelTextLoad ; Reload text jmp LevelSelect ; Menu Of course you need to add more text lines, and make branch routines at LevSelTextLoad. Here is what i got. Code: LevSelTextLoad: cmpi.b #1,(LevelselectRam) ; Is this the level select? beq LevSelLoad cmpi.b #2,(LevelselectRam) ; Is this the level select? beq CharLoad1 ; if so, load level select text lea (OptionMenuText).l,a1 ; Load the Option text ;tst.w ($FFFFFF82).w ; is item $14 selected? ;bne bra TextRead ; continue here LevSelLoad: cmpi.b #1,(StagesRam) beq StagesSonic2 lea (LevelMenuText).l,a1 ; load level select text bra TextRead CharLoad1: lea (CharacterSelect).l,a1 ; load level select text bra TextRead StagesSonic2: lea (LevelMenuText2).l,a1 ; load level select text TextRead: lea ($C00000).l,a6 move.l #$62100003,d4 ; screen position (text) move.w #$E680,d3 ; VRAM setting moveq #$14,d1 ; number of lines of text loc_34FE: ; XREF: LevSelTextLoad+26j move.l d4,4(a6) ;Put a Ls routine here bsr.w LevSel_ChgLine ; branch it addi.l #$800000,d4 dbf d1,loc_34FE moveq #0,d0 move.w ($FFFFFF82).w,d0 move.w d0,d1 move.l #$62100003,d4 lsl.w #7,d0 swap d0 add.l d0,d4 cmpi.b #1,(LevelselectRam) beq LevSelLoad2 cmpi.b #2,(LevelselectRam) beq CharLoad2 lea (OptionMenuText).l,a1 bra TextRead2 LevSelLoad2: cmpi.b #1,(StagesRam) beq StagesSonic2ndB lea (LevelMenuText).l,a1 ; load level select text bra TextRead2 CharLoad2: lea (CharacterSelect).l,a1 ; load level select text bra TextRead2 StagesSonic2ndB: lea (LevelMenuText2).l,a1 ; load level select text TextRead2: the page Code: CharacterSelect: dc.b "SONIC 1 " ; Sonic from this one's original dc.b "SONIC 2 " ; Sonic The Hedgehog 2 dc.b "SONIC 3 " ; Sonic The Hedgehog 3K dc.b "SONIC NEBULA " ; Custom 1 from Spritesheet Nebula dc.b "NARUTO UZUMAKI " ; Naruto Uzumaki dc.b "RICHTER BELMONT " ; Richter Belmont dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " dc.b " " ; .. Here does nothing yet. dc.b "SOUND TEST " even . Also you need to code in this page. The way I have it setup, In example 1, Pushing start goes to the character select #2,(LevelSelectRam) In this character select, pushing Start on the character of choice sets the CharacterRam, plays music, and returns to the Options, Like this Code: Character_Selector: tst.b (LevelSelectRam) beq Levsel_char Character_Is_Sonic_One: cmpi.b #2,(LevelSelectRam) bne Character_Selector_rts tst.w ($FFFFFF82).w ; is item $14 selected? bne Character_Is_Sonic_Two ; if not, branch move.b ($FFFFF605).w,d1 ; save buttons to d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq LevSel_NoMove ; branch if not move.b #$00,(CharacterRam) move.b #$00,(LevelSelectRam) move.w #$8A,d0 jsr (PlaySound_Special).l ; play end-of-level music jsr LevSelTextLoad clr.w ($FFFFFF82).w ; is item $14 selected? jmp LevelSelect Character_Is_Sonic_Two: cmpi.b #2,(LevelSelectRam) bne Character_Selector_rts cmpi.w #1,($FFFFFF82).w ; is item $14 selected? bne Character_Is_Sonic_Three ; if not, branch move.b ($FFFFF605).w,d1 ; save buttons to d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq LevSel_NoMove ; branch if not move.b #$01,(CharacterRam) move.b #$00,(LevelSelectRam) move.w #$04,d0 jsr (PlaySound_Special).l ; play end-of-level music jsr LevSelTextLoad clr.w ($FFFFFF82).w ; is item $14 selected? jmp LevelSelect Character_Is_Sonic_Three: cmpi.b #2,(LevelSelectRam) bne Character_Selector_rts cmpi.w #2,($FFFFFF82).w ; is item $14 selected? bne Character_Is_Sonic_Nebula ; if not, branch move.b ($FFFFF605).w,d1 ; save buttons to d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq LevSel_NoMove ; branch if not move.b #$02,(CharacterRam) move.b #$00,(LevelSelectRam) move.w #$10,d0 jsr (PlaySound_Special).l ; play end-of-level music jsr LevSelTextLoad clr.w ($FFFFFF82).w ; is item $14 selected? jmp LevelSelect Character_Is_Sonic_Nebula: cmpi.b #2,(LevelSelectRam) bne Character_Selector_rts cmpi.w #3,($FFFFFF82).w ; is item $14 selected? bne Character_Is_Naruto ; if not, branch move.b ($FFFFF605).w,d1 ; save buttons to d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq LevSel_NoMove ; branch if not move.b #$03,(CharacterRam) move.b #$00,(LevelSelectRam) move.w #$18,d0 jsr (PlaySound_Special).l ; play end-of-level music jsr LevSelTextLoad clr.w ($FFFFFF82).w ; is item $14 selected? jmp LevelSelect Character_Is_Naruto: cmpi.b #2,(LevelSelectRam) bne Character_Selector_rts cmpi.w #4,($FFFFFF82).w ; is item $14 selected? bne Character_Is_Richter ; if not, branch move.b ($FFFFF605).w,d1 ; save buttons to d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq LevSel_NoMove ; branch if not move.b #$04,(CharacterRam) move.b #$00,(LevelSelectRam) move.w #$0F,d0 jsr (PlaySound_Special).l ; play end-of-level music jsr LevSelTextLoad clr.w ($FFFFFF82).w ; is item $14 selected? jmp LevelSelect Character_Is_Richter: cmpi.b #2,(LevelSelectRam) bne Character_Selector_rts cmpi.w #5,($FFFFFF82).w ; is item $14 selected? bne Character_Selector_rts ; if not, branch move.b ($FFFFF605).w,d1 ; save buttons to d1 andi.b #ButtonStart,d1 ; is left/right pressed? beq LevSel_NoMove ; branch if not move.b #$05,(CharacterRam) move.b #$00,(LevelSelectRam) move.w #$09,d0 jsr (PlaySound_Special).l ; play end-of-level music jsr LevSelTextLoad clr.w ($FFFFFF82).w ; is item $14 selected? jmp LevelSelect Character_Selector_RTS: rts The code after each Levsel_NoMove sets Character bit, Returns to the Option Menu, Plays Music, Reloads Text,returns selector to the top of the menu, and stays at the option menu. and there's your second page. You can add more pages by repeating steps, but change the LevelSelectRam to a higher number.
It needs a bit of change to the tutorial, but it’s possible to get plenty of things to work here. Will edit the tutorial soon