Introduction This topic is meant to be used as a small database for details about Sonic games, design and assembly that may be hard to notice without extended and time consuming testing. The purpose is not to provide lengthy tutorials, but only to point at facts and issues and give directions about how to correct problems when necessary. It is essentially about debugging and optimizing, and collecting facts and statistics about the level design in the official games. You can post your informations for all Sonic games so they will be included in the main list. Only facts and data please, opinions do not belong here. Please PM me for comments, suggestions and feedbacks, they are welcome but could make the thread harder to read. Contents: Part 1 - Level Design 1.1 Informations related to level design in the games 1.2 Specific problems after changes in gameplay and levels 1.2.1 Sonic 1 188.8.131.52 Green Hill zone 184.108.40.206 Marble zone 220.127.116.11 Labyrinth zone 18.104.22.168 Star Light zone 22.214.171.124 Scrap Brain zone 126.96.36.199 Special Stage 1.2.2 Sonic 2 188.8.131.52 Chemical Plant zone PART 2 - General problems in game 2.1 Global 2.2 Sonic 1 PART 3 - General ASM informations 3.1 Some vocabulary 3.2 Assembly general fixes 3.3 Assembly hints and tips ----PART 1---- Level design This section contains informations related to gameplay elements pertaining to the design of the levels. ---1.1 Informations related to level design in the games--- This subsection contains data and facts about the content of levels in Sonic games. • Loops: Although loops are extremely popular, most of them are concentrated in a few levels in the main four megadrive/mega cd games. Not counting tubes: - there are 18 loops in Sonic 1 (GHz:3, SLz:15 ) - there are 50 loops in Sonic 2 (EHz:9, CPz:22, ARz:7, CNz:7, HTz:5) - there are 20 loops in Sonic CD (PP:6, SS: 14) - there are 125 loops in Sonic 3 & Knuckles (AIz: 9, Hz: 21, MGz: 7, ICz: 33 (all in act 2), MHz: 31, FBz: 6, Sz: 18) • Water: Only 10 zones out of 39 have underwater areas through the main four megadrive/mega cd games. - there are two zones with underwater areas in Sonic 1 (Lz: 3 acts, SBz: 1 act) - there are two zones with underwater areas in Sonic 2 (CPz, ARz) - there is one zone with underwater areas in Sonic CD (TT) - there are 5 zones with underwater areas in Sonic 3 & Knuckles (all in Sonic 3: AIz, Hz, CNz, ICz, LBz) ---1.2 Specific problems after changes in gameplay and levels--- This subsection lists errors and glitches that can become visible when gameplay elements (like special moves or object placement) are being modified. --1.2.1 Sonic 1-- • New loops in levels: Add 80 (in hexadecimal) to the chunk's ID to make the game recognize it as a loop. In SonED2 this is done by pressing space on the chunk. -184.108.40.206 Green Hill Zone- • Open chunks (act 1): There is nothing to block the player from entering the rightmost S tube's chunks from the above on the left. • Open chunks (act 2): There is nothing to block the player from entering the chunk of the breakable walls on the left and on the right, and nothing either on the right side of the S tube at the end of the level. • Lost crabmeat (act 2): There is a crabmeat out of the level's dynamic boundary at 10D0-370. • Open chunks (act 3): There is nothing to block the player from entering the S tube's chunks from the above on the right. -220.127.116.11 Marble Zone- • Shortcut (all acts): There is an upper path that allows the player to skip most of the level. • Sprite walls and ceilings (all acts): The levels use sprites to fill gaps in the chunks, making actions such as climbing them impossible. - Reason: This was probably meant to save chunk space. - Solution: The empty chunk slots can be used without conflicting with anything. • Lava wall (act 2): The lava wall will begin to move if you jump out of the corridor. - Reason: The object checks if you went out of its activation area, - Solution: Deactivate it when you're out of range vertically but not horizontally. • Lava geysers: The lava geyser positions are to be modified in the pushable block routine. • Last monitor in act 3: This monitor doesn't have its "remember state" bit set. -18.104.22.168 Labyrinth Zone- • Current tunnels (or wind tunnels): The positions of those tunnels are set in an array after the tunnel's code in the disassembly. • Rolling after the signpost at the end of act 2 will crash the game (no reason or solution proposed yet) -22.214.171.124 Star Light Zone- • Death when leaving the screen after being hurt by an Orbinaut (act 1): This could actually happen in any level, but it's only known to be visible after being hurt by an Orbinaut at the top of Star Light zone act 1. - reason: the Sonic_HurtStop routine (as named in Hivebrain's disassembly) kills Sonic when he goes out of the screen. -126.96.36.199 Scrap Brain Zone- • Hidden monitors (act 2): It seems these monitors are mistakenly replacing object 71. - There is an eggman monitor hidden in the wall at 710-1B0 - There are 3 monitors (S, super sneakers and invincibility) hidden in the walls at A30-200, AD0-1E0, AD0-270 - There are 6 invincibility monitors hidden at 4F0-3D0, 590-3D0, 5F0-3D0, 690-3D0, 5F0-4D0 and 690-4D0 • Cut scene (act 2): The chunk above Robotnik is empty and may be visible. -188.8.131.52 Special Stage- • Goal blocks: The goal blocks won't be activated if Sonic pushes on them while standing on a wall. They will be activated only when Sonic is either falling (or bouncing) or jumping. (no reason or solution proposed yet) --1.2.2 Sonic 2-- -184.108.40.206 Chemical Plant Zone- • Warp pipes / spin tubes: A warp pipe won't correctly move the character when he falls in it after being hurt (video). (no reason or solution proposed yet) ----PART 2---- General problems in game This section contains informations about issues that are not glitches or bugs but can negatively affect the gameplay. -2.1 Global- • Sprites are flickering: This happens when more than 20 pieces of sprites (pieces as seen in sonmapEd) are on the same scanline (aligned horizontally) in 40 cell mode (regular gameplay), or 16 pieces on the same scanline in 32 cells mode (like Sonic 2's special stage). • Slowdowns: Most common causes for slowdown are: - too many complex objects being processed - too many objects using "calcsine" or equivalent subroutines being processed - too many sprite pieces in objects on screen - creating many objects at the same time • Incorrect background deformation when respawning at a checkpoint: When restarting at a checkpoint post, the deformation position is slightly incorrect, moving back to the beginning of the level shows the deformation position is out by a few pixels. - reason: Sonic's position is recorded as he touches the post, that position may not be the same as his position when he restarts at that post. • Incorrect first plane when respawning at a checkpoint: A visual glitch (often an horizontal bar at the top of the screen) may appear if the lamppost's base is lower that the top of the ground's collision. -2.2 Sonic 1- • Title screen not scrolling: When you drown and get a game over, the titlescreen won't scroll. This happens both with the original background deformations and rev01 deformations. - reason: When Sonic drowns, the game sets a flag (in $FFFFF744) to prevent the screen from scrolling, but that flag is not reset by the time the title screen should begin to scroll. - solution: Reset the flag when necessary. • Landing on solid objects and platforms with a falling animation: If you add a falling animation, it may not reset properly when Sonic lands on a platform or solid object. - reason: the routine won't change the animation number immediately, it wasn't necessary in the original game. - solution: set the animation to "walk" in the two subroutines related to platforms and solid objects before "jsr Sonic_ResetOnFloor". • Sprites as walls: Sonic 1 uses sprites to complete walls. Since those sprites don't use the same collision routines as walls, changes related to collisions may not affect them. • GAME OVER on level end: If you get a "game over" during the "Sonic Got Through" scene, the "game over" text is messed up. If you can pass the level before the game restarts, you play with 0 lives and can't pause the game. ----PART 3---- General ASM informations -3.1 Some vocabulary- This section explains some of the names and symbols to make assembly easier to read. • The "d" in d0, d1, d2... d7: This "d" stands for "data register". It holds "immediate data" (numerical values), is 32 bits long and is stored in the cpu's memory, so changing it doesn't affect ram. • The "a" in a0, a1, a2... a7: This "a" stands for "address register". What is being stored here is an address, and the numbers before it are to be seen as relative addressing. They don't modify the address in the address register. /! -(an) is a predecrement and (an)+ is a postincrement of the address, meaning the address in the address register will be changed. /! a7 can only use even addresses. Increments and decrements will automatically skip the odd address. eg: addi.b #$1C,$18(a0) ; this adds 1C to the value stored 24 (18 in hexadecimal) addresses from the address stored in a0. • The "a" in adda, cmpa, lea, movea, pea, suba: This "a" stands for "address". The address is what is being processed here, not the data it contains. • The "i" in addi, andi, cmpi, eori, ori, subi: This "i" stands for "immediate data", it means the first term (often an hexadecimal number) won't be an address but a number standing for its own value. eg: andi.b #$18,d0 ; this compares the hexadecimal value 18 with d0 • The "q" in addq, moveq, subq: This "q" stands for "quick". It can only be used for values of 3 bits or less for addq and subq, and 8 bits (one byte) for moveq (note: moveq can only be used on a data register). • BNE / BEQ: Understanding BNE (Branch if Not Equal) and BEQ (Branch if EQual) only requires to know what those conditional tests do. They test the zero bit (Z-bit) of the condition code register. BNE branches when the Z-bit is clear, BEQ branches when the Z-bit is set. The Z-bit is set when the result of the operation equals zero. examples: cmpi.b #5, d0 ; subtracts 5 from d0 but doesn't save the result. It only sets the Condition Code Register. ; If d0 equals 5, the Z-bit is set (so it will "Branch if EQual"). btst #2, d0 ; tests the 3rd bit in d0, if the bit is 0, it sets the Z-bit (so it will "Branch if EQual"), ; otherwise Z is clear (and will "Branch if Not Equal"). tst.b d0 ; tests all bits in d0, if they're all 0, it sets the Z-bit, if one of them is set, it clears the Z-bit. -3.2 Assembly general fixes- This section is meant to provide simple solutions and explanations to common asm problems. • "Out of range" error: You changed something in your disassembly (some code, art, anything), and receive "out of range" errors. The line of the error should be given in the error message. - reason: This error happens when an address is further than the branch operand can handle. When the error happens on BRA (BRAnch) or BSR (Branch to Sub Routine), check the size written after it (if nothing is written, consider it's a .w): .s can branch 127 bytes before or after itself, .w can branch 32767 bytes before or after itself. - solution: Increase the range of the branch: if it was a .s, try with a .w. But if it was a .w, you need to use a branching operand of higher level: BRA.w has to be replaced with JMP (Jump) BSR.w has to be replaced with JSR (Jump to Sub Routine) - to make it short: BRA.s < BRA.w < JMP BSR.s < BSR.w < JSR - note: While BRA and BSR are using relative addressing (you can read the value given after them, a "label", as a distance), JMP and JSR are using absolute addressing (the data following them is the "effective address"). This is why they aren't really equivalent. - For other cases, check this thread: http://sonicresearch...p?showtopic=857 • Garbled graphics after some changes and new additions: If you add some binary data or arrays to your asm, some graphics that were fine before may suddenly look wrong. - reason: the addresses for graphic data have to be even. - solution: add an "even" after "incbin" lines and arrays. --3.3 Assembly hints and tips-- This section contains some basic asm tricks. • Loading palettes: - PalLoad1 is used to load a palette to make it fade, otherwise it will not work. example: moveq #2,d0 ; load the second palette in the palette list. jsr PalLoad1 jsr Pal_FadeTo - PalLoad2 is used to load palettes and apply them immediately. This one is not to be used if the palette has to fade. example: moveq #2,d0 ; load the second palette in the palette list. jsr PalLoad2 • Set and test several flags at the same address: You don't have to use a whole byte for a flag of 1 bit. The functions bchg, bclr, bset, andi and btst can save some RAM. examples: bset #5,($FFFFFF90).w ; sets the 6th bit at address FFFFFF90 bchg #3,($FFFFFF90).w ; changes the 4th bit at address FFFFFF90, into a 0 if it was a 1 and vice versa andi.b #$28,($FFFFFF90).w ; resets all but 4th and 6th bits in FFFFFF90 ($28=0010 1000 in binary) • Test multiple conditions at once with "or": When you're adding conditions in an existing routine, it may be easier to use "or" to test two bits rather than creating new branches. examples: ; (in this example, only the first bit is used in FFFFFF90 and FFFFFF92) move.b ($FFFFFF90).w,d0 ; load the first flag or.b ($FFFFFF92).w,d0 ; set the flag in d0 if either one of the flags in FFFFFF90 or FFFFFF92 is set • Using d7: If you use the data register d7, the game will probably go wrong or crash. - reason: d7 is being used by the ObjectsLoad subroutine and stores the entry number of the next object to be processed. d7 is multiplied by 40 (in hex) and added to FFFFD000, which gives the address of the object in memory. The value at that address is used to determine which object should be processed, and the data after it is used by the program. If that data is not compatible with the object's program (especially the routine counter), the game will crash. - example of a solution: move.w d7,-(sp) ; ++ save d7 state into the stack ; your code involving a new d7 here ; ... ; ... move.w (sp)+,d7 ; ++ load saved d7 state See this post for more details.