Comparing Sonic 1 and Sonic 2, there are many differences between each other. One of them is entering the special stages. With Sonic 1, you need to have 50 rings until going into the big ring at the end of the act. With Sonic 2, you need to have 50 rings then touch a lamppost which will take you into the special stage. There was originally a Russian tutorial on how to add it to Sonic 1 but since I’m not from Russia, nor speaking the language, I decided to make an English tutorial for you guys to try. Without further ado, let’s get started. Just a quick note: it is designed for Hivebrain disassembles. If you have SVN/GitHub, then it’s up to you to add it. Also, this isn’t just some copy and paste guides. I’m teaching people how to port things from Sonic 2 to 1 correctly without messing up. So before going into the juicy stuff we need to add the routine for the stars from the lamppost as that is used in Sonic 2's lamppost code so in Obj79_Index, after: Code: dc.w Obj79_Twirl-Obj79_Index insert this: Code: dc.w Obj79_Star-Obj79_Index Now that we have inserted a new routine, let's move on. Let’s take a look on Obj79 in the Sonic 2 Xenowhirl disassembly, mainly in Obj79_CheckActivation: Code: Obj79_CheckActivation: andi.b #$7F,d1 move.b subtype(a0),d2 andi.b #$7F,d2 cmp.b d2,d1 bcc.w loc_1F222 move.w x_pos(a3),d0 sub.w x_pos(a0),d0 addi.w #8,d0 cmpi.w #$10,d0 bcc.w return_1F220 move.w y_pos(a3),d0 sub.w y_pos(a0),d0 addi.w #$40,d0 cmpi.w #$68,d0 bcc.w return_1F220 move.w #$21+$80,d0 ; checkpoint ding-dong sound jsr (PlaySound).l jsr (SingleObjLoad).l bne.s loc_1F206 _move.b #$79,0(a1) ; load obj79 move.b #6,routine(a1) ; => Obj79_Dongle move.w x_pos(a0),objoff_30(a1) move.w y_pos(a0),objoff_32(a1) subi.w #$14,objoff_32(a1) move.l mappings(a0),mappings(a1) move.w art_tile(a0),art_tile(a1) move.b #4,render_flags(a1) move.b #8,width_pixels(a1) move.b #4,priority(a1) move.b #2,mapping_frame(a1) move.w #$20,objoff_36(a1) move.w a0,parent(a1) tst.w (Two_player_mode).w bne.s loc_1F206 cmpi.b #7,(Emerald_count).w ; DeltaWooloo: from here and below is what we'll be focusing on beq.s loc_1F206 cmpi.w #$32,(Ring_count).w bcs.s loc_1F206 bsr.w Obj79_MakeSpecialStars From where I marked is what we need to focus on. Here, it tells us if the sparkles will show if we don't have 7 emeralds collected or if we have 50 rings. We need to make sure that these requirements are added in our disassembly so the stars can appear once we follow the rules of enabling them. Insert this at the end of Obj79_HitLamp from where I marked to the end of the code. Now we need to change the code so it can work with Sonic 1. In Sonic 1, there are 6 emeralds, not 7 and the disassembly doesn't understand Sonic 2's constants. So we need to edit this line: Code: cmpi.b #7,(Emerald_count).w Replace the 7 with a 6 and replace the emerald count with $FFFFFE57 as that is the RAM used for the number of emeralds in Sonic 1. For the line below it, we need to change the branch loc_1F206 to loc_16F76. Do the same to the other line which is two lines below it. Now for this line: Code: cmpi.w #$32,(Ring_count).w Replace ring_count with $FFFFFE20. Now, all that is left is the line which jumps to Obj79_MakeSpecialStars. We need to port that and its contents to Sonic 1. First off, copy this below Obj79_LoadInfo: Code: ; loc_1F4C4: Obj79_MakeSpecialStars: moveq #4-1,d1 ; execute the loop 4 times (1 for each star) moveq #0,d2 - bsr.w SingleObjLoad2 bne.s return_1F534 _move.b 0(a0),0(a1) ; load obj79 move.l #Obj79_MapUnc_1F4A0,mappings(a1) move.w #$47C,art_tile(a1) move.b #4,render_flags(a1) move.b #8,routine(a1) ; => Obj79_Star move.w x_pos(a0),d0 move.w d0,x_pos(a1) move.w d0,objoff_30(a1) move.w y_pos(a0),d0 subi.w #$30,d0 move.w d0,y_pos(a1) move.w d0,objoff_32(a1) move.b priority(a0),priority(a1) move.b #8,width_pixels(a1) move.b #1,mapping_frame(a1) move.w #-$400,x_vel(a1) move.w #0,y_vel(a1) move.w d2,objoff_34(a1) ; set the angle addi.w #$40,d2 ; increase the angle for next time dbf d1,- ; loop return_1F534: rts ; =========================================================================== ; loc_1F536: Obj79_Star: move.b collision_property(a0),d0 beq.w loc_1F554 andi.b #1,d0 beq.s + move.b #1,($FFFFF7CD).w move.b #$10,(Game_Mode).w ; => SpecialStage + clr.b collision_property(a0) loc_1F554: addi.w #$A,objoff_34(a0) move.w objoff_34(a0),d0 andi.w #$FF,d0 jsr (CalcSine).l asr.w #5,d0 asr.w #3,d1 move.w d1,d3 move.w objoff_34(a0),d2 andi.w #$3E0,d2 lsr.w #5,d2 moveq #2,d5 moveq #0,d4 cmpi.w #$10,d2 ble.s + neg.w d1 + andi.w #$F,d2 cmpi.w #8,d2 ble.s loc_1F594 neg.w d2 andi.w #7,d2 loc_1F594: lsr.w #1,d2 beq.s + add.w d1,d4 + asl.w #1,d1 dbf d5,loc_1F594 asr.w #4,d4 add.w d4,d0 addq.w #1,objoff_36(a0) move.w objoff_36(a0),d1 cmpi.w #$80,d1 beq.s loc_1F5BE bgt.s loc_1F5C4 loc_1F5B4: muls.w d1,d0 muls.w d1,d3 asr.w #7,d0 asr.w #7,d3 bra.s loc_1F5D6 ; =========================================================================== loc_1F5BE: move.b #$D8,collision_flags(a0) loc_1F5C4: cmpi.w #$180,d1 ble.s loc_1F5D6 neg.w d1 addi.w #$200,d1 bmi.w JmpTo10_DeleteObject bra.s loc_1F5B4 ; =========================================================================== loc_1F5D6: move.w objoff_30(a0),d2 add.w d3,d2 move.w d2,x_pos(a0) move.w objoff_32(a0),d2 add.w d0,d2 move.w d2,y_pos(a0) addq.b #1,anim_frame(a0) move.b anim_frame(a0),d0 andi.w #6,d0 lsr.w #1,d0 cmpi.b #3,d0 bne.s + moveq #1,d0 + move.b d0,mapping_frame(a0) bra.w JmpTo_MarkObjGone ; =========================================================================== JmpTo10_DeleteObject jmp DeleteObject ; =========================================================================== nop JmpTo_MarkObjGone jmp MarkObjGone ; =========================================================================== Don't build! Just because we added the code doesn't mean the curtains will close and you can enjoy yourself. We need to make the code compatible with Sonic 1 as the disassembly doesn't recognize the plus branches and the constants. In Obj79_MakeSpecialStars replace - (minus) with Obj79_MakeStarsLoop (this will be a new branch) Below replace bsr.w SingleObjLoad2 with jsr SingleObjLoad2 Below _move.b 0 (a0), 0 (a1) by move.b 0 (a0), 0 (a1) Before return_1F534 replace dbf d1, - with dbf d1,Obj79_MakeStarsLoop In Obj79_Star replace the pluses with loc_1F553 In loc_1F554 replace the pluses with loc_1F555 In loc_1F594 replace the pluses with loc_1F595 In loc_1F5D6 replace the pluses with loc_1F5D7 This should fix the issues with the branches. Now to finish off doing the constants. The symbol ">" should tell you what to replace what with. Obj79_MapUnc_1F4A0 > Map_obj79b mappings > $04 art_tile > $02 render_flags > $01 routine > $24 x_pos > $08 y_pos > $0C objoff_30 > $30 objoff_32 > $32 priority > $18 width_pixels > $19 mapping_frame > $ 1A x_vel > $10 y_vel > $12 objoff_34 > $ 34 collision_property > $21 Game_Mode > $FFFFF600 objoff_36 > $36 collision_flags > $20 anim_frame > $1B Check and see if everything builds. If so, well done. If not, check for any mistakes made. Now you are thinking about the art and the mappings right. You can do the art and mappings yourself if you want or you can download this zip which should have the art and mappings ready. http://sonicresearch.org/community/...4/&temp_hash=ceb3fc9fb55b800d6d680d1e94c9d815 Add the lamppost art to artnem and obj79 and obj79b to _maps. Add the monitors art if you added the spindash dust, I'll explain why later. We need to include obj79b to our mappings so after map_obj79, insert this: Code: Map_obj79b: include "_maps\obj79b.asm" Now we have two things left. Loading the stars art into VRAM and fixing the collision. Let's do the VRAM issue first. Go to this line in Obj79_MakeStarsLoop: Code: move.w #$47C,$02(a1) The VRAM location, $47C is loaded by another art so we need to replace that with another location that is free. Replace $47C with $7A0. This will make sure the stars load into VRAM. SKIP THIS IF YOU HAVEN'T ADDED THE SONIC 2 SPINDASH TO SONIC 1 If you added the spindash dust, you would know that the dust overwrites the lamppost so rather than inserting ($D800/$20), replace $47C with #($D740/$20). You need to also do that in Obj79_Main. You may ask why I added the monitors art into the zip file. It is because since we merged the stars art with the lamppost art into one file, it starts to overwrites the monitors' VRAM thus giving you a corrupt looking lamppost. We don't want that, so we just moved the art's VRAM a bit back to give room to the monitors' art. Speaking of the monitors. You may or may not notice but the file is smaller comparing to the original Sonic 1 monitor art. That is because I had to use ReadySonic's optimized monitor art and free tiles so there'll be less tiles to deal with and the art wouldn't overwrite with the lamppost art if you are adding new art to the lamppost which needs more tiles or it just overwrites for an unspecified reason. COME HERE, ALL OF YOU! THIS IS THE FINAL STEP! So the last thing is to fix the collision since if you go through the rings, you cannot go into the special stage. That is an easy fix actually and shouldn't take a lot of time. Replace Obj79_Star with this: Code: Obj79_Star: tst.b $21(a0) beq.s loc_1F554 move.b #$10,($FFFFF600).w And go to loc_1F5BE and replace the only line there to this: Code: move.b #$D7,$20(a0) The reason why we did this is because Sonic 2 handles the collision to the lamppost differently comparing to Sonic 1 and that game doesn't understand how to handle the collision the way Sonic 2 does. And with all that, you should have the starposts fully working in Sonic 1. Grab 50 rings and fly to the special stage. Enjoy collecting those emeralds easily. OPTIONAL STEP In Knuckles in Sonic 2, when returning from a starpost from a special stages, you keep your rings. If you would like that to happen in Sonic 1, here is what you do. In Obj79_LoadInfo, delete this line: Code: clr.w ($FFFFFE20).w This should keep your rings you collected in the special stage and when you've died. I don't why you would want that but here you go. Credits: This tutorial was written by me Original creator of the tutorial by Shooter OG tutorial: https://sonic-world.ru/forum/topic/14074-туториалы-по-хакингу-sonic-genesismega-drive/?page=9 ReadySonic by Mecury RandomName helped me with a few bugs so credits to him
So... In my game, this happened. Which RAM I can use because $D740 is already being used for centiseconds. Which address I can choose?
If your emulator has a VRAM viewer, look there at anywhere where it's blank or filled with title screen stuff between all levels. If not, you'll have to do a VRAM dump to look for the same things, and multiplying the ID of the first tile you intend to use by $20 (32) to get your address.