Editing Objects - RetroKoH/S1Fixed GitHub Wiki

Objects in Sonic 1 are pretty straight-forward to modify once you get the hang of ASM editing. Object RAM starts at $FFFFD000, and each object consists of $40 (64) bytes of RAM. This guide will introduce you to the basics of where to find objects, and how to understand the basics of how they work.

Object Pointers

A file containing a list of pointers to every single object in the game can be found in _inc/Object Pointers.asm. Each object is tied to a single-byte index number ranging from 00-FF. It's important to note that not every object ID value is used, and some point to objects that go unused in the built game. These pointers can be used for new objects that you may either want to create on your own, or port over from other games.

This object list is for S1Fixed. A note regarding stock Sonic 1 will be made as well.

Index Number Object Name Notes
01 Sonic ...
02 Unused This ID slot is used for Tails in Sonic 2.
03 Path Swapper ID slot unused in stock S1. In later games, 03 is also the Path Swapper object.
04 Auto Roll Tag ID slot unused in stock S1. This object was back-ported from Sonic 2.
05 Unused ...
06 Special Stage Cursor This is a new object used in the new Special Stage Debug feature.
07 Dust Effects This object was back-ported from Sonic 2.
08 Water Splash ...
09 Special Stage Sonic ...
0A Drowning Countdown This object currently has a (rare) bug in S1Fixed that leads to a crash.
0B LZ Breakable Pole
0C LZ Flapping Door
0D Signpost This object has seen significant changes, including dynamic art loading.
0E Title Screen Sonic This object now uses dynamic art loading.
0F Press Start/TM The sprite blocker has been improved (Kilo), and a menu routine has been added for SaveProgressMod.
10 Subsprite Test Created by Devon to demonstrate the subsprites system.
11 GHZ Bridge This object now uses subsprites, like its Sonic 2 variant.
12 SYZ Light This object now uses sync animations, and will be amalgamated with the Scenery object in the future.
13 Fireball Maker ...
14 Fireball This object has been slightly optimized by Hivebrain.
15 Swinging Platforms RAM usage has been altered slightly to accomodate the changes made to Obj48.
16 LZ Harpoon This object has been reworked by Hivebrain and RetroKoH for increased functionality.
17 Spiked Log Helix This object has been reworked by RetroKoH to use subsprites. Increased functionality is planned.
18 Platforms ...
19 GHZ Giant Ball Absent in the final game; Restored from the prototype. Still incomplete.
1A Collapsing Ledge Optimized by RetroKoH. Shares some functionality with Obj53.
1B Water Surface This object will see improvements, along with alternate sprites in a mod.
1C Scenery This object displays the GHZ Bridge stump and the SLZ Cannon. The SYZ Light and LZ Wheel will be incorporated into this object.
1D "Magic Switch" (Unused in the final game)
1E Ball Hog Badnik The prototype version will be restored.
1F Crabmeat Badnik ...
20 Ball Hog's Cannonball A variant will be added for the prototype Ball Hog.
21 Invincibility Stars This ID slot was originally for the HUD. It's now for the inv. stars, which habe been split from the Shield object, and given dynamic art loading AND subsprite functionality.
22 Buzz Bomber Badnik ...
23 Buzz Bomber Missile ...
24 Buzz Bomber Missile Explosion Unused in the final game, will be re-implemented as a mod.
25 Rings (Debug) Due to the Rings Manager, this object is Debug-exclusive. Users planning to remove Debug Mode can replace this object.
26 Monitor Solidity improvements by RetroKoH. Further improvements to be made.
27 Item Explosion ...
28 Animals Additions and Improvements to this object are planned.
29 Points If you want to free VRAM or even remove score, you can remove/replace this object.
2A SBZ Small Door ...
2B Chopper Badnik ...
2C Jaws Badnik ...
2D Burrobot Badnik ...
2E Monitor Contents This object has seen improvements and additions by RetroKoH.
2F MZ Large Grassy Platforms ...
30 MZ Green Glass Blocks This object currently has a minor priority bug.
31 Chained Stompers This object currently has one minor visual bug with the longest variant.
32 Button This object will see improved functionality (Credit: Malachi)
33 Pushable Blocks ...
34 Title Cards This object interferes less with level art due to improved art loading. (Credit: Kilo, TheBlad768, RetroKoH)
35 Burning Grass ...
36 Spikes This object will see enhancements in the future.
37 Lost Rings This object has seen a massive overhaul from numerous editors, and now includes magnetic and badnik functionality.
38 Shield(s) This object now loads its art dynamically, and can include all Sonic 3K shields.
39 GAME OVER This object has received minor improvements and a minor bugfix (Credit: RetroKoH)
3A Got Through Card This object has seen changes and add-ons to accomodate new Bonuses, and faster tallies.
3B Purple Rock This object will receive push and break functionality in the future.
3C Smashable Wall This object has been slightly optimized, and breaks for Flame Shields and Super Sonic.
3D Boss - Green Hill Zone Code cleanup and dynamic art incoming.
3E Prison Capsule ...
3F Boss Explosion This could be a subtype of Obj27 if you need an object index.
40 Moto Bug Badnik This object might(?) receive a new subtype in the future.
41 Springs Air Collision for side springs will be added as a mod. Diagonal Springs MIGHT be added.
42 Newtron badnik Green badnik will be fixed so that it can reappear.
43 Roller badnik ...
44 GHZ Edge Walls ...
45 MZ Sideways Stompers Absent in the final game; Restored from leftover code. Still incomplete.
46 MZ Bricks Slight improvements will be made to their falling movement to remove popping.
47 SYZ Bumper ...
48 Eggman's Swinging Ball The ball now rotates as it swings (Credit: RetroKoH).
49 Waterfall Sound ...
4A Special Stage Entry Effect Unused in the final game.
4B Giant Ring This object now includes the Flash as a routine, and loads art dynamically.
4C MZ Lava Geyser ...
4D MZ Lava Fall ...
4E MZ Wall of Lava This will be improved upon.
4F Splats Badnik Absent in the final game; Restored from the prototype. Still incomplete.
50 Yadrin badnik ...
51 Smashable Green Block Smashing has been optimized.
52 Moving Blocks (MZ, LZ, SBZ) ...
53 Collapsing Floors (MZ, SLZ, SBZ) Optimized by RetroKoH. Shares some functionality with Obj1A.
54 Lava Tag I might make an alternate version of this object that triggers death.
55 Basaran badnik ...
56 Floating Blocks (SYZ/SLZ) and Doors (LZ) I might split this into different objects.
57 Spiked Ball and Chain (SYZ and LZ) ...
58 SYZ Big Spiked Ball ...
59 SLZ Elevator ...
5A SLZ Circular Platforms ...
5B SLZ Staircase Blocks ...
5C SLZ Pylon ...
5D SLZ Fan ...
5E SLZ Seesaw ...
5F Bomb badnik Shrapnel spawning optimized by RetroKoH.
60 Orbinaut badnik ...
61 LZ Blocks ...
62 LZ Gargoyle (and Fireball) ...
63 LZ Conveyor Platforms Might make the wheel a Scenery subtype.
64 Bubbler and Air Bubbles ...
65 LZ Waterfalls ...
66 SBZ Rotating Junction ...
67 SBZ Convex Wheel (Running Disc) ...
68 SBZ Conveyor Belt ...
69 SBZ Spinning Platforms ...
6A SBZ Buzzsaws ...
6B SBZ Stomper and Door ...
6C SBZ Vanishing Platforms ...
6D SBZ Flamethrower ...
6E SBZ Electric Orb ...
6F SBZ Spinning Conveyor Platforms ...
70 SBZ Girder ...
71 Invisible Solid Barrier ...
72 SBZ Teleporter ...
73 Boss - Marble Zone Code cleanup and dynamic art incoming.
74 Marble Boss Fire ...
75 Boss - Spring Yard Zone Code cleanup and dynamic art incoming.
76 Spring Yard Boss Blocks ...
77 Boss - Labyrinth Zone Code cleanup and dynamic art incoming.
78 Caterkiller badnik Collision bugs fixed
79 Lamppost ...
7A Boss - Star Light Zone Code cleanup and dynamic art incoming.
7B Star Light Boss Spikeballs ...
7C Unused This object slot was once for the Ring Flash object, which has been integrated into Obj4B, and no longer exists.
7D Hidden Bonus Flags ...
7E Special Stage Results ...
7F SS Results Chaos Emeralds Could this be subsprites of Obj7E?
80 Continue Screen Elements ...
81 Continue Screen Sonic Add alternate Peelout version if the move is enabled.
82 SBZ2 Eggman ...
83 SBZ2 Eggman's Crumbling Floor ...
84 Final Boss Cylinders ...
85 Boss - Final Code cleanup and dynamic art incoming.
86 Final Boss Plasma Launcher ...
87 Ending Sonic ...
88 Ending Emeralds ...
89 Ending Logo ...
8A Credits Text This will be removed in favor of either Enigma mappings or ASCII text.
8B Post-Credits Eggman ...
8C Post-Credits Emeralds ...
8D Super Sonic Stars Backported from Sonic 2
8E After-Images Backported from S3K
8F Goggles In-progress restoration.

Object Status Table (Object RAM)

Every object in RAM in stock Sonic 1 (and S1Fixed) consists of $40 (64) bytes of data. Each byte serves a different purpose as it relates to how the object functions in the game. Some of the bytes' purposes are identical across all objects, while others are not. Many objects also have a slew of "scratch RAM" that you can make use of to add greater functionality to them.

Universal OST Bytes

These OST bytes serve the same purpose across every object in the game, and shouldn't be used outside of their intended purpose. Doing so can cause significant issues.

$00 - obID

This is the index number of the object. In sonic.asm, there is a function called ExecuteObjects that goes through the entirety of Object RAM and runs the code of every object loaded in RAM. To do so, it checks for these ID numbers. If it reads 00, it assumes there is no object, and moves on to the next space. Otherwise it pulls this value and runs a check against the Object Pointers list noted above, to find the address of the code to run. If an objects index number is changed at any point, either through a coded instruction or in real-time via RAM modding, it will begin running completely different code. In some instances, this may be desired, but in most cases, this can be troublesome.

$01 - obRender

This is a bitfield which tells the game whether or not this object's sprite should be rendered on-screen or not, and how it should do so. Each bit provides a different function. I'll break this down in the future, but do note that this changes across games, and functionality in S1Fixed is slightly different than in stock Sonic 1.

$02-03 - obGfx

These two bytes tell the game where the object's starting art tile offset is in VRAM, as well as which palette line it should utilize for rendering. A full breakdown of this, along with the accompanying macro, is coming soon.

$04-07 - obMap

These four bytes contain a full address pointing to the location of this object's sprite mappings. Unless your object will NEVER be displayed on screen in some manner, it's not advised to change this value during runtime. Only set this in the object's code to a label pointing to sprite mappings.

$08-09 - obX

These two bytes are the integer portion of the object's current x-position within the "room" you are currently in. Said "room" almost always refers to the stage you're currently playing in, though there are exceptions.

$0A-0B - obXSub

These two bytes are the fraction, or subpixel, portion of the object's current x-position. Now, numbers in this game do not use actual floating point numbers, so the way that fractions work is like so. Let's say obX is set to $0014 (20 in decimal), and obXSub is set to $4000. Because obXSub is 2 bytes, it ranges from $0000 to $FFFF, for $10000 possible values. Now, $4000/$10000 is equal to 1/4, or 0.25. Therefore, when we combine all 4 bytes into $00144000, that means the object's x-position is equal to 20.25. The subpixel value is not used when placing the object on screen, but as it is incremented and it overflows, the integer portion gets incremented as a result, allowing it to work like a fraction.

$0C-0D - obY

These two bytes are the integer portion of the object's current y-position within the "room".

$0E-0F - obYSub

These two bytes are the fraction, or subpixel, portion of the object's current y-position. The prior explanation applies here too.

$18 - obPriority

This OST is only one byte in stock Sonic 1, but has been extended to 2 bytes in S1Fixed ($18-19)
This byte is used by DisplaySprite to determine the drawing priority level of the object's sprite. Sprites with a lower priority value will be drawn ABOVE sprites with a higher priority value.

$19 - obActWid

This OST has been moved to $23 in S1Fixed (due to obPriority being extended to 2 bytes)
Labeled as "action width" in the disassembly. I'll provide more info later.

$1A - obFrame

The current sprite mapping frame being displayed.

OST Bytes used by most objects, including Sonic

These OST bytes serve the same purpose with most objects, including Sonic. In some cases, they can be repurposed, but this usually shouldn't be done unless you know what you are doing.

$10 - obVelX

These two bytes are used by the subroutines SpeedToPos or ObjectFall (or other similar subroutines) to adjust the object's x-position smoothly, giving it a sense of horizontal "motion".

$12 - obVelY

These two bytes are used by the subroutines SpeedToPos or ObjectFall (or other similar subroutines) to adjust the object's y-position smoothly, giving it a sense of vertical "motion". In ObjectFall, this value gets increased gradually as a means to emulate gravity.

$16 - obHeight

This byte determines the object's vertical radius, used primarily for terrain collision. Despite its name, it only determines half of the object's collision height. To be more accurate, it determines the length of the object's vertical collision detection sensor line.

$17 - obWidth

This byte determines the object's horizontal radius, used primarily for terrain collision. Despite its name, it only determines half of the object's collision width. To be more accurate, it determines the length of the object's horizontal collision detection sensor line.

$1B - obAniFrame

This byte is used by objects that utilize the AnimateSprite subroutine to determine the object's current frame index in its animation script. It's important to note that this doesn't directly determine the object's sprite mapping frame to be displayed. It is instead used to find the sprite mapping frame to be displayed, in accordance with the object's animation script.

$1C - obAnim

This byte is used by objects that utilize the AnimateSprite subroutine to determine which of the object's loaded animations should be executed.

$1D - obPrevAni

This byte is used by objects that utilize the AnimateSprite subroutine. It stores the previously run animation, allowing the subroutine to determine whether or not a new animation is being run or not. If so, it usually resets the object's animation variables accordingly.

$1E - obTimeFrame

$1F - obDelayAni

$22 - obStatus

This byte is a bitfield used by most objects to determine various aspects about its current in-game state. The manner in which the bits are utilized depends on the object in question.

$24 - obRoutine

This byte is used by nearly all objects as a means of program control. When an object's code is run by ExecuteObjects, it's commonplace that this byte will be used in a lookup table to determine which segment of the object's code should be run at that moment. As a result of being utilized in lookup tables that use word-length pointers, obRoutine is always set to an even value.

$26 - obAngle

This byte is used by many objects in subroutines involving angular movement. The most obvious example of this is Sonic, who runs and rolls along all manner of slopes throughout the game. Some objects use this byte in different manners.

OST Bytes used by many non-player objects

These OST bytes serve the same purpose with various objects, but NOT Sonic. Again, there are some cases where they can be repurposed, but this usually shouldn't be done unless you know what you are doing. Sonic uses equivalent OST bytes for different purposes.

$20 - obColType

This OST byte is used by ReactToItem to determine the type of collision hitbox an object has, as well as its bounding box size (via a lookup table). If this byte is set to 00, then Sonic is unable to collide with this item in a meaningful way.

$21 - obColProp

This OST byte is an additional collision-based property. The most common use of this byte is by boss objects as hit points. Every boss takes 8 hits before being defeated, so this byte is set to 8 when the boss battle starts, and the battle ends when this ticks down to 0.

$23 - obRespawnNo

This OST has been moved to $14 in S1Fixed, due to being extended to 2 bytes for use with the S3K Object Manager This OST variable serves the same purpose in stock Sonic 1, and S1Fixed, but its execution is slightly different. I'll cover this more in-depth later.

$25 - ob2ndRout

This byte is used by some objects as a secondary routine counter. It works exactly like obRoutine, and is always set to an even value.

$25 - obSolid

Removed in S1Fixed This byte was used in stock Sonic 1 as a status flag for solid objects. S1Fixed does away with this variable completely, following suit with Sonic 2 and Sonic 3K.

$28 - obSubtype

This byte is like a secondary obID value, so much so that this value is stored next to the ID value in an object's data in the object layout data. It denotes which variation of an object that this instance is. For example, Green Hill Zone's Newtron badnik makes use of this byte to determine whether it is a blue Newtron (which glides along the ground) or a green Newtron (which fires a shot, then disappears).

More to come later.

Porting Objects to/from S1Fixed

There are a few things to make note of when porting objects to/from S1Fixed, and other engines, especially those built off of Sonic 2 or Sonic 3:

  • S1Fixed uses Sonic 2 format sprites (as of the time of writing). Adjustments to certain object mechanics such as crumbling ledges, as well as object sprite mappings, will need to be adjusted accordingly.
  • Aspects like Solidity only account for one player character, as opposed to two, like in Sonic 2 or Sonic 3K.
  • Objects use a single byte ID instead of an address. Certain aspects like subtypes and routine handling will be different in this engine compared to Sonic 3K.