If you want a compiled ROM with this tutorial applied, download it here:
http://goo.gl/qEGNV
Disassemblies that will be used:
Sonic 1 disassembly by Hivebrain
Sonic 2 disassembly by Xenowhirl
If you get a "out of range" error when compiling, I'am believing that you know resolve this problem, but if you not know, ask about it.
------------------------------------------------------
Part 1 - First we will get the art files from Sonic 2 disassembly:
We only will need the "title screen menu item's art" of the "A few menu blocks".art\nemesis\A few menu blocks.bin
art\nemesis\Standard font.bin
To be more easy, uncompress and combine the two files like "A few menu blocks.bin" + "Standard font.bin" = "titlemenu.bin", delete the useless art blocks, compress in Nemesis format and put on artnem folder.
You can download my edited art here:
http://goo.gl/x4nCG
Now search by "Nem_TitleTM:" and above of this, add:
- Code:
Nem_TitleMenu: incbin artnem\titlemenu.bin ; Menu text on title screen
even
Part 2 -- Now we will load this art on VRAM
In sonic1.asm go to Title_ClrPallet, and below this:
- Code:
moveq #3,d0 ; load Sonic's pallet
bsr.w PalLoad1
move.b #$8A,($FFFFD080).w ; load "SONIC TEAM PRESENTS" object
jsr ObjectsLoad
jsr BuildSprites
bsr.w Pal_FadeTo
move #$2700,sr
move.l #$40000001,($C00004).l
lea (Nem_TitleFg).l,a0 ; load title screen patterns
bsr.w NemDec
move.l #$60000001,($C00004).l
lea (Nem_TitleSonic).l,a0 ; load Sonic title screen patterns
bsr.w NemDec
move.l #$62000002,($C00004).l
lea (Nem_TitleTM).l,a0 ; load "TM" patterns
bsr.w NemDec
- Code:
move.l #$62400002,($C00004).l
lea (Nem_TitleMenu).l,a0 ; load "Title Menu text" patterns
bsr.w NemDec
How to change the address?
If you want change the address that art will be loaded, change the bits of the #$62400002 that say the address, to the new address, making this:
1º - Get the VRAM' start byte that you want put the art; in this case i will use A240 (after TM art)
2º - Transform to binary A240-> 1010 0010 0100 0000
3º - Arrange like the VDP Address Port will know what to do:
Will be like this:CCAA AAAA AAAA AAAA 0000 0000 CCCC 00AA
A = address.
C = settings for the VDP; like write and read for C-RAM V-RAM and VS-RAM. (We will only use VRAM write mode in this import)
0 = 0
For VRAM write mode, only set the 2nd C to 1.
6240000 is the value that we need.1010 0010 0100 0000 = Address
Arranging correctly (remember, the first two bits in end)
CC10 0010 0100 0000 0000 0000 CCCC 0010
Now the C's:
0110 0010 0100 0000 0000 0000 0000 0010
Transform to Hexadecimal
01100010010000000000000000000010 -> 62400002
If you want further information, see the Markey Jester's VDP Questions Topic:
http://sonicresearch.org/forums/index.php?showtopic=2199
------------------------------------------------------
Part 3 - Loading the PRESS START BUTTON (fixing the not loading PRESS START BUTTON bug)
Apply this guide:
http://info.sonicretro.org/SCHG_How-to:Display_the_Press_Start_Button_text
------------------------------------------------------
Part 4 - Obj0F programming
Go to "Obj0F" and you will see this:
- Code:
; ---------------------------------------------------------------------------
; Object 0F - "PRESS START BUTTON" and "TM" from title screen
; ---------------------------------------------------------------------------
Obj0F: ; XREF: Obj_Index
moveq #0,d0
move.b $24(a0),d0
move.w Obj0F_Index(pc,d0.w),d1
jsr Obj0F_Index(pc,d1.w)
bra.w DisplaySprite
; ===========================================================================
Obj0F_Index: dc.w Obj0F_Main-Obj0F_Index
dc.w Obj0F_PrsStart-Obj0F_Index
dc.w locret_A6F8-Obj0F_Index
; ===========================================================================
Obj0F_Main: ; XREF: Obj0F_Index
addq.b #2,$24(a0)
move.w #$D0,8(a0)
move.w #$130,$A(a0)
move.l #Map_obj0F,4(a0)
move.w #$200,2(a0)
cmpi.b #2,$1A(a0) ; is object "PRESS START"?
bcs.s Obj0F_PrsStart ; if yes, branch
addq.b #2,$24(a0)
cmpi.b #3,$1A(a0) ; is the object "TM"?
bne.s locret_A6F8 ; if not, branch
move.w #$2510,2(a0) ; "TM" specific code
move.w #$170,8(a0)
move.w #$F8,$A(a0)
locret_A6F8: ; XREF: Obj0F_Index
rts
; ===========================================================================
Obj0F_PrsStart: ; XREF: Obj0F_Index
lea (Ani_obj0F).l,a1
bra.w AnimateSprite
; ===========================================================================
Now we will program to first show the Press Start Button, and after show the Menu.
If you want, you can program to the Press Start Button be the Title Screen Menu.
In Obj0F_Index, add this line in the end:
- Code:
dc.w Obj0F_Menu-Obj0F_Index
- Code:
Obj0F_Index: dc.w Obj0F_Main-Obj0F_Index
dc.w Obj0F_PrsStart-Obj0F_Index
dc.w locret_A6F8-Obj0F_Index
dc.w Obj0F_Menu-Obj0F_Index
On s2.asm (Sonic 2 disassembly by Xenowhirl) go to Obj0F, and copy all the Obj0F_Main below the place of the Obj0F_PrsStart (change the routine name to Obj0F_Menu; Obj0F_Main to Obj0F_Menu), copy too the JmpTo4_PlaySound routine below this Obj0F_Main copy, you code will be like the below:
- Code:
Obj0F_PrsStart: ; XREF: Obj0F_Index
lea (Ani_obj0F).l,a1
bra.w AnimateSprite
Obj0F_Menu:
moveq #0,d2
move.b (Title_screen_option).w,d2
move.b (Ctrl_1_Press).w,d0
or.b (Ctrl_2_Press).w,d0
btst #button_up,d0
beq.s +
subq.b #1,d2
bcc.s +
move.b #2,d2
+
btst #button_down,d0
beq.s +
addq.b #1,d2
cmpi.b #3,d2
blo.s +
moveq #0,d2
+
move.b d2,mapping_frame(a0)
move.b d2,(Title_screen_option).w
andi.b #button_up_mask|button_down_mask,d0
beq.s + ; rts
moveq #SndID_Blip,d0 ; selection blip sound
bsr.w JmpTo4_PlaySound
+
rts
JmpTo4_PlaySound
jmp (PlaySound).l
We need make the changes between the disassemblies.
To simplify the end of the code, change this:
Change all the Title_screen_option for a address you think better, in this case ill use the $FFFFFFB0
Change the Ctrl_1_Press for $FFFFF605
Comment the or.b (Ctrl_2_Press).w,d0
Change button_up for 0
Change button_down for 1
Change mapping_frame for $1A
Change button_up_mask|button_down_mask for 3
Change SndID_Blip for a sound you think better, a good sound is $CD (change for $CD)
- Code:
moveq #$CD,d0 ; selection blip sound
bsr.w JmpTo4_PlaySound
+
rts
JmpTo4_PlaySound
jmp (PlaySound).l
- Code:
move.w #$CD,d0 ; selection blip sound
jsr PlaySound
+
rts
Now make names for the subroutines, like this:
Change the 1st, 2nd and 3rd plus ('+') with loc_13645
Change the 4th, 5th and 6th plus with loc_13646
Change the 7th and 8th plus with loc_13647
After these changes, your code will be like this:
- Code:
Obj0F_Menu:
moveq #0,d2
move.b ($FFFFFFB0).w,d2
move.b ($FFFFF605).w,d0
; or.b (Ctrl_2_Press).w,d0
btst #0,d0
beq.s loc_13645
subq.b #1,d2
bcc.s loc_13645
move.b #2,d2
loc_13645:
btst #1,d0
beq.s loc_13646
addq.b #1,d2
cmpi.b #3,d2
blo.s loc_13646
moveq #0,d2
loc_13646:
move.b d2,$1A(a0)
move.b d2,($FFFFFFB0).w
andi.b #3,d0
beq.s loc_13647 ; rts
move.w #$CD,d0 ; selection blip sound
jsr PlaySound
loc_13647:
rts
Part 5 - Making the map, setup the art location and positioning the object on screen
This object cant use the same map that is used on Sonic 2.
Using the art (titlemenu.bin) that you get on "Part 1", edit your own map using a map editor like SonMapEd (name the output map as obj0F_Menu.asm).
In my case, i edited for get a menu with "START GAME", "LEVEL SELECT" and "SPECIAL STAGE".
You can download my edited map here:
http://goo.gl/adsqO
Note: not use the original obj0F map (_maps\obj0F.asm), because the 'TM' and 'PRESS START BUTTON' use it. You can use the original obj0F map, only if you will not overwrite the 'PRESS START BUTTON' and 'TM' maps. If you do this, need reprogram the obj0F that i will show in this tutorial to the game get the correct mappings, the variable $1A.
Go to Map_obj0F, and below this:
- Code:
Map_obj0F:
include "_maps\obj0F.asm"
- Code:
Map_obj0F_Menu:
include "_maps\obj0F_Menu.asm"
After get the map, we need correct their position, use the correct art and load the edited map.
In our case is better "change and load" all these things in the transition between PRESS START BUTTON and the MENU.
Making the transition - when you press start, the PRESS START BUTTON will be changed to the TITLE MENU
Change this:
- Code:
Obj0F_PrsStart: ; XREF: Obj0F_Index
lea (Ani_obj0F).l,a1
bra.w AnimateSprite
- Code:
Obj0F_PrsStart: ; XREF: Obj0F_Index
btst #7,($FFFFF605).w ; check if Start is pressed
beq.s Obj0F_PrsStart_Show ; if not, branch
addq.b #4,$24(a0) ; go to Menu in next frame
rts
Obj0F_PrsStart_Show:
lea (Ani_obj0F).l,a1
bra.w AnimateSprite
Using correct art:
On Obj0F_PrsStart, above the 'rts' add this line:
- Code:
move.w #$VVV,2(a0)
Get this value and divide by 20 (decimal 32).
A240/20=512
Change that VVV by 512.
In this case we will use the 1st pallet line; The TM use the 2nd pallet line, because this the '2' on side of the 510 in the "TM" specific code.
Loading the edited map:
On Obj0F_PrsStart, above the 'rts' add this line:
- Code:
move.l #Map_obj0F_Menu,4(a0)
Fixing the Menu position:
On Obj0F_PrsStart, above the 'rts' add these lines:
- Code:
move.w #$XX,8(a0)
move.w #$YY,$A(a0)
Here is the X and Y position on the screen.
Change these values until you get a good place on the screen, in my case i get this:
- Code:
move.w #$F5,8(a0)
move.w #$150,$A(a0)
Part 6 - Making the Menu work
We will program the Menu for no start the game when you press start, and for the options work.
Go to loc_13647 and put in place of the rts, these lines:
- Code:
btst #7,($FFFFF605).w ; check if Start is pressed
beq.s Obj0F_Menu_rts ; if not, branch
jmp DeleteObject ; if yes, delete the Title Screen Menu
Obj0F_Menu_rts:
rts
Go to Title_ChkLevSel, comment the 4 first lines and below these 4 lines add this:
- Code:
cmp.b #6,($FFFFD0A4).w ; is Title Menu on?
beq.w loc_317C ; if it not was deleted, branch
moveq #0,d2
move.b ($FFFFFFB0).w,d2 ; load the choice
add.w d2,d2 ; multiply by 2
move.w Obj0F_Menu_Choice(pc,d2.w),d2
jmp Obj0F_Menu_Choice(pc,d2.w) ; jump to the choice code
; ===========================================================================
Obj0F_Menu_Choice:
dc.w PlayLevel-Obj0F_Menu_Choice ; 0
dc.w Menu_LevelSelect-Obj0F_Menu_Choice ; 2
dc.w PlaySpecialStage-Obj0F_Menu_Choice ; 4
; ===========================================================================
- Code:
Title_ChkLevSel:
; tst.b ($FFFFFFE0).w ; check if level select code is on
; beq.w PlayLevel ; if not, play level
; btst #6,($FFFFF604).w ; check if A is pressed
; beq.w PlayLevel ; if not, play level
cmp.b #6,($FFFFD0A4).w ; is Title Menu on?
beq.w loc_317C ; if it not was deleted, branch
moveq #0,d2
move.b ($FFFFFFB0).w,d2 ; load the choice
add.w d2,d2 ; multiply by 2
move.w Obj0F_Menu_Choice(pc,d2.w),d2
jmp Obj0F_Menu_Choice(pc,d2.w) ; jump to the choice code
; ===========================================================================
Obj0F_Menu_Choice:
dc.w PlayLevel-Obj0F_Menu_Choice ; 0
dc.w Menu_LevelSelect-Obj0F_Menu_Choice ; 2
dc.w PlaySpecialStage-Obj0F_Menu_Choice ; 4
; ===========================================================================
moveq #2,d0
bsr.w PalLoad2 ; load level select pallet
lea ($FFFFCC00).w,a1
moveq #0,d0
move.w #$DF,d1
We need create the subroutines of the choices (in my case is PlayLevel, Menu_LevelSelect, PlaySpecialStage)
PlayLevel is to start a normal level, this already exists.
Menu_LevelSelect is to enter on level select, and not exists.
Create this subroutine above these lines (they are below the Obj0F_Menu_Choice):
- Code:
moveq #2,d0
bsr.w PalLoad2 ; load level select pallet
lea ($FFFFCC00).w,a1
moveq #0,d0
move.w #$DF,d1
PlaySpecialStage is to start the special stage, and not exists.
go to LevSel_Level_SS, and create this subroutine below these lines:
- Code:
add.w d0,d0
move.w LSelectPointers(pc,d0.w),d0 ; load level number
bmi.w LevelSelect
cmpi.w #$700,d0 ; check if level is 0700 (Special Stage)
bne.s LevSel_Level ; if not, branch
Last edited by OuricoDoido on Sun May 15, 2011 7:42 pm; edited 1 time in total