docs
Physics reference
Every physics constant in the engine, with the disassembly address it came from.
This is the engine's provenance registry, rendered as-is. Every constant that governs movement, collision, sprites, or timing appears here with its value, its unit, and the address in the reference game's disassembly that it was traced to.
It exists because the claim this project makes — that the physics are exact — is only worth something if it is checkable. A number in a table is an assertion; a number with an address next to it is evidence.
How to read a row
- Value — as the engine stores it. Speeds and accelerations are integers in subpixels: 1 px = 16 subpx, and velocities are 16-bit with the low byte a 1/256 fraction, so
$0180means 1.5 subpx per frame per frame. Arrays are tables indexed exactly as the original indexes them; where that indexing is unobvious, the note says how. - Status —
verified: traced to a disassembly address, which is in the row.corroborated: no single address, but agreed by community documentation (TAS resources, forum research) from more than one direction.verify: still open. The engine uses it, a source suggests it, nobody has confirmed it against the disassembly yet. These are marked in the source with a[VERIFY]tag, counted by CI, and the count has to reach zero before launch.
- Note — why the number is what it is, and any quirk that comes with it. The notes are where the actually surprising behaviour is documented.
Every constant in the engine source carries the same citation as a comment, and a CI check fails the build if a physics constant appears without one. The registry and the code cannot drift apart without the build going red.
The table below shows the address; the full source list behind each constant — which disassembly, which forum thread, which TAS resource — lives in packages/engine/physics-provenance.json, alongside the comment on the constant itself.
Found a disagreement?
That is the most valuable bug report this project can receive. If you can show a case where the engine and the reference game diverge — a frame count, a subpixel, an edge case in a collision — open an issue with the reproduction. During the competition it is also a community prize entry, and a demonstration that lands as a failing test in the repo is the strongest form of it.
100 of 971 shown · registry updated 2026-07-24
| Constant | Value | Unit | Status | Address |
|---|---|---|---|---|
AIR_CATCH_WINDOW_OPTIMAL Optimal (TAS) window. Preserve exactly. | 3 | frames | corroborated | — |
AIR_CATCH_WINDOW_STANDARD Standard input window to regain height during flight. | 11 | frames | corroborated | — |
AIR_HORIZONTAL_ACCEL Identical to ground: the same MarioAccel_ row 0 is used airborne. PlayerInAir only gates the ice-table substitution at $00D753 (ice accel applies on the ground only). IMPORTANT companion fact: releasing the d-pad in mid-air applies NO deceleration - CODE_00D764 drains the P-meter then returns early when PlayerInAir != 0. Air friction of -1.0 applies only while holding a direction at or past the target. | 384 | 1/256 subpx/frame^2 | verified | $00D345 |
BABY_YOSHI_GROWTH_COUNT INC SpriteMisc1570 / CMP #$05. Eating any powerup ($74-$77) grows it in one bite instead ($01A2AD). | 5 | sprites eaten | verified | $01A2F7 |
BABY_YOSHI_GROWTH_FREEZE_FRAMES YoshiGrowingTimer := $40. SpriteLock and PlayerIsFrozen are both held non-zero for the whole window - the world stops. Pose from GrowingAniSequence ($01EBC2), index (timer-1)>>3. | 64 | frames | verified | $01A2F1 |
BABY_YOSHI_GROWTH_Y_RISE SpriteYPos -= $10 when the baby becomes sprite $35. | 16 | px | verified | $01A2C7 |
BALLNCHAIN_ANGULAR_STEP A 9-bit angle in $1602 + $151C stepped by +2 or -2 depending on bit4 of the anchor's X position. | 2 | units/frame | verified | $02D62A |
BALLNCHAIN_RADIUS InitBallNChain: $187B = $38. The grey chained platform $A3 uses $30 = 48. | 56 | px | verified | $018533 |
BALLNCHAIN_RADIUS_GREY_PLATFORM InitGreyChainPlat: $187B = $30. | 48 | px | verified | $018537 |
BALLNCHAIN_ROTATION_PERIOD 512 angle units at 2 per frame. Position = anchor + (CircleCoords[angle] * radius) >> 8 on each axis. | 256 | frames | verified | $02D62A |
BANZAI_BILL_SPAWN_SIDE_RULE InitBanzai erases the sprite unless SubHorizPos returns Y = 1, i.e. unless the player is to its LEFT. | 1 | flag | verified | $018525 |
BANZAI_BILL_SPEED LDA #$E8 every frame; no gravity, no level collision. Drawn as a 4x4 tile block (64 x 64 px). | -24 | subpx/frame | verified | $02D590 |
BASEBALL_XSPEED BaseballSpeed db $18,$E8, spawned as extended sprite $0D at x + $10 (facing right) / x - $08 (left) from BaseballTileDispX ($02C460, db $10,$F8) with DATA_02C462 (db $00,$FF) as the high bytes, same Y. Baseball ($02A254) calls ONLY the X integrator - no Y integration and no gravity - so the ball flies perfectly horizontally until the screen-edge erase. snesrev ExtSpr0D_Baseball (// 02a254) agrees. | 24 | subpx/frame | verified | $02C464 |
BIG_BOO_AIM_OFFSET_ABOVE_PLAYER BigBoo's alternate entry point loads $20 instead of $10. | 32 | px | verified | $01F8DC |
BLOCKED_ABOVE The $77 bit the head sensor sets; tryJump and the rising-Vy cancel both read it. | 8 | bitmask | verified | $00EB77 |
BLOCKED_BELOW The $77 bit the foot sensors set. Separate arm of RunPlayerBlockCode_EB77 from the side bits. | 4 | bitmask | verified | $00EB77 |
BLOCKED_LEFT DATA_00E90A db $01,$02 — the $77 PlayerBlockedDirs bit a left-side sensor sets. | 2 | bitmask | verified | $00E90A |
BLOCKED_RIGHT DATA_00E90A db $01,$02 — the $77 PlayerBlockedDirs bit a right-side sensor sets. Engine-native grouping; SMW never names the set. | 1 | bitmask | verified | $00E90A |
BLOCK_BOUNCE_DURATION bounce_spr_timer ($16C5) = 8 at spawn; the replacement tile is written when it reaches 0. | 8 | frames | verified | $028752 |
BLOCK_BOUNCE_PLAYER_XPUSH DATA_0290DA db $80,$E0,$20,$80; $80 is the sentinel for 'do not touch the player'. Direction 3 sets player yspeed to 0 via DATA_0290D6. | [null, -32, 32, null] | subpx/frame | verified | $0290DA |
BLOCK_BOUNCE_TYPE_TABLE DATA_00F05C. Value 6 spawns bounce-sprite id 7 (spinning turn block); value 7 short-circuits to the brick-shatter path. | [1, 5, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 2, 2, 2, 1, 1, 7, 17, 16] | code | verified | $00F05C |
BLOCK_BOUNCE_XSPEED BlockBounceXSpeed db $00,$40,$C0,$00. | [0, 64, -64, 0] | subpx/frame | verified | $0290D2 |
BLOCK_BOUNCE_YACCEL kBounceSpr01_TurnBlock_YAcceleration db $10,$00,$00,$F0. | [16, 0, 0, -16] | subpx/frame^2 | verified | $0290DE |
BLOCK_BOUNCE_YSPEED BlockBounceYSpeed db $C0,$00,$00,$40 indexed by hit direction. | [-64, 0, 0, 64] | subpx/frame | verified | $0290D2 |
BLOCK_CONTENTS_TABLE DATA_00F080. item = v>>1, powerup-dependent = v&1. Negative values index DATA_00F100 by block X position. item 1=mushroom $74, 2=flower $75, 3=star $76, 4=feather $77, 5=1-up $78, 6=coin, 7=multi-coin, 8=vine $79, 10=P-switch $3E. | [128, 0, 0, 30, 0, 0, 5, 9, 6, 129, 14, 12, 20, 0, 5, 9, 6, 7, 14, 12, 22, 24, 26, 26, 0, 9, 0, 0, 255, 12, 10, 0, 0, 0, 8, 2] | code | verified | $00F080 |
BLOCK_GENERATE_TABLE_PAGE0 TileToGeneratePg0, generate indices 0-8. Index 2 = erase ($25), index 3 = vine ($06), index 5 = the spinning-turn-block tile ($48). | 37,37,37,6,73,72,43,162,198 | map16 low byte | verified | $00C08D |
BLOCK_GENERATE_TABLE_PAGE1 TileToGeneratePg1, generate indices 9-23. Index 9 = invisible solid ($52), 10 = multi-coin ($1B), 12 = turn block ($1E), 13 and 22 = used block ($32), 19 = ON/OFF block ($12). | 82,27,35,30,50,19,21,22,43,44,18,104,105,50,94 | map16 low byte | verified | $00C0E4 |
BLOCK_HIT_DIRECTION_MASK DATA_00F0EC indexed by hit direction j: 0 = from below, 1 = from the right, 2 = from the left, 3 = from above. ANDed with DATA_00F0A4[block]. | [8, 1, 2, 4] | bitmask | verified | $00F0EC |
BLOCK_REPLACEMENT_TILE_TABLE DATA_00F0C8; index into TileToGeneratePg1 ($00C0E4 = $52,$1B,$23,$1E,$32,$13,$15,$16,$2B,$2C,$12,$68,$69,$32,$5E) offset by 9. Generate-index 13 -> tile $32, the used block. | [14, 19, 14, 13, 14, 16, 13, 13, 13, 13, 10, 13, 13, 12, 13, 13, 13, 13, 11, 13, 13, 22, 13, 13, 13, 17, 17, 18, 13, 13, 13, 14, 15, 12, 13, 13] | generate-index | verified | $00F0C8 |
BLOCK_RESPONSE_MASK_TABLE DATA_00F0A4, 36 entries indexed by (tile_lo - $11). Index 33 (mask 4 = from above only) is the spin-jump turn-block break. | [12, 8, 12, 8, 12, 15, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 3, 3, 8, 8, 8, 8, 8, 8, 4, 8, 8] | bitmask | verified | $00F0A4 |
BLURP_VERTICAL_PERIOD Derived: 8 speed steps x 4 frames each way. Peak-to-peak excursion is 4 px. | 64 | frames | verified | $0384EF |
BLURP_XSPEED db $08,$F8. Direction fixed at spawn by FaceMario; Blurp never re-reads Mario's position. | 8 | subpx/frame | verified | $0384C6 |
BLURP_YACCEL db $01,$FF, applied when EffFrame & 3 == 0. | 1 | subpx/frame per 4 frames | verified | $0384C8 |
BLURP_YSPEED_MAX db $04,$FC. Direction flips on exact equality. | 4 | subpx/frame | verified | $0384C4 |
BOBOMB_BLAST_DURATION $1534 = 1 and $1540 = 64 at fuse end; BobOmbExplosion runs until $1540 reaches 0, then status 0. | 64 | frames | verified | $019624 |
BOBOMB_BLAST_HITBOX_GROUP RESOLVED: $1662 is forced to $11 for the whole explosion, and clipping group $11 is (dispX -24, width 64, dispY -24, height 64) - a 64x64 px box centred on the sprite. Previous [VERIFY] cleared. | 17 | clipping-index | verified | $02808A |
BOBOMB_BLAST_PLAYER_HIT_FRAMES The player check additionally requires (uint8)($1540 - 16) < $20, so only 8 of the 16 sprite-hit frames can hurt Mario. | [47, 46, 39, 38, 31, 30, 23, 22] | timer-units | verified | $02808A |
BOBOMB_BLAST_SPRITE_HIT_FRAMES Sprites are killed on the frames where (($1540 >> 1) & 3) == 3. | [63, 62, 55, 54, 47, 46, 39, 38, 31, 30, 23, 22, 15, 14, 7, 6] | timer-units | verified | $02808A |
BOBOMB_FLASH_THRESHOLD $15F6 ^= (2*$1540) & $0E once $1540 < $40. | 64 | timer-units | verified | $019624 |
BOBOMB_FUSE_EXPIRY_ACTIONS At $1540 == 1 the handler plays SFX_KAPOW, sets $1534 = 1, $1540 = $40 and SpriteStatus = 8 (back to NORMAL), and clears $1686 bit3 so the blast can hit other sprites. The Bobomb main ($0189D8) then sees $1534 and jumps to BobOmbExplosion ($02808A). Because the carried-sprite handler also calls CODE_019624 every frame, this happens identically in the player's hands - the status change to 8 drops it out of the carry state. Below $1540 == $40 the sprite flashes by XOR-ing (($1540 << 1) & $0E) into SpriteOBJAttribute ($15F6). | null | — | verified | $019630 |
BOBOMB_FUSE_TICK_DIRECTION The Bob-omb branch of the status-9 handler returns before CODE_01969C, so it never runs IncrmntStunTimer (++$1540 every 2 frames) or UnstunSprite. $1540 is only ever decremented, by the generic per-slot timer block CODE_0180D2 ($0180D2), which runs for every non-zero status when sprites are not locked - so the fuse ticks 1 per frame, in status 9 and while carried alike. | -1 | units/frame | verified | $019624 |
BOBOMB_SELF_FUSE_TIMER When the walk timer expires the Bob-omb sets status 9 and $1540 = $40. | 64 | frames | verified | $0189E4 |
BOBOMB_STOMP_FUSE_TIMER SetStunnedTimer gives sprite $0D the 255 value, and the id-13 branch of $019624 skips IncrmntStunTimer, so it ticks 1 per FRAME rather than 1 per 2 frames. | 255 | frames | verified | $019624 |
BOBOMB_WALK_TIMER InitBomb sets $1540 = $FF. An untouched Bob-omb walks 255 frames then sits down by itself. | 255 | frames | verified | $018566 |
BONYBEETLE_SPIKE_CYCLE_FRAMES $1528 advances once per two frames (odd frames only) and wraps at 256, arming $1540 = $A0 each wrap. | 512 | frames | verified | $01E58B |
BONYBEETLE_SPIKE_WINDOW SBC #$08 / CMP #$60 - while $1540 is in [8,104) a stomp HURTS instead of crumbling. 96 frames of exposed spikes per 512-frame cycle. | [8, 104] | timer-units | verified | $01E5E0 |
BOOSTREAM_REFLECT_RULE Sprite $B0 negates Xspeed on $1588 & $03 and Yspeed on $1588 & $0C - a perfect reflection with no gravity or acceleration. The ring layout routine CODE_039020 (called every 8 frames, phase = (slot ^ EffFrame) & 7) is [VERIFY]. | 1 | flag | verified | $038FA4 |
BOO_AIM_OFFSET_ABOVE_PLAYER TileGenerateTrackB ($18C1) is subtracted from the player's Y before the vertical distance test. $10 for the Boo $37 and Boo Block $AF, $20 for the Big Boo $28. | 16 | px | verified | $01F8E5 |
BOO_CHASE_ACCEL_PERIOD TrueFrame & 7 == 0 gates the +-1 step on each axis, so 64 frames from rest to full speed. | 8 | frames | verified | $01F989 |
BOO_CHASE_SPEED DATA_01F8CF db $08,$F8. Applies to both axes and to $37, $28 and $AF alike. | 8 | subpx/frame | verified | $01F8CF |
BOO_DECEL_PERIOD_MASK TrueFrame mask for [Boo $37, Big Boo $28, Boo Block $AF]; the shy Boo sheds 1 subpx per axis on each unmasked frame. 16 / 32 / 8 frames to stop from full speed. | [1, 3, 0] | frame-mask | verified | $01F94A |
BOO_FREEZE_EXEMPT_RANGE CORRECTED NOTE. ADC #$0A / CMP #$14 is a +-10 px horizontal window, but it does NOT make a near Boo keep moving. Both range tests branch to CODE_01F92F, which freezes when the shy latch $C2 is set. The window's real jobs are (a) at $01F906, to keep an already-shy Boo shy without re-evaluating the player's facing on its 32-frame beat, and (b) at $01F914, to bypass the turn logic. A shy Boo freezes at ANY distance once it faces the player; only a far Boo that still has to turn keeps chasing during the turn. | 10 | px | verified | $01F914 |
BOO_FREEZE_RECHECK_PERIOD $1540 = $20. The 'is Mario facing me' test is SAMPLED, not continuous - turning around does not stop a Boo until the next multiple of 32 frames. | 32 | frames | verified | $01F8FA |
BOO_SHY_LATCH_RULE $C2 = 1 (shy) iff SubHorizPos's Y equals PlayerDirection ($76), i.e. the player is facing the Boo. Re-evaluated only when $1540 hits 0, then reset to $20 (32 frames). Decision matrix: $C2 == 0 -> always chase; $C2 == 1 and near -> freeze; $C2 == 1, far, already facing the player -> freeze; $C2 == 1, far, not yet facing -> set $15AC = $1F and chase while turning. | null | — | verified | $01F8E1 |
BOO_STREAM_TRAIL_PERIOD CODE_039020 is a TRAIL EMITTER, not a ring re-form. Every 8 frames (per-slot phase, ((slot ^ EffFrame) & 7) == 0) sprite $B0 spawns MINOR extended sprite $0A at its own position with MinExtSpriteTimer = $30 (48 frames) and its own X speed. There are 12 minor slots, round-robined via MinExtSpriteSlotIdx when full. The stream Boo itself is a billiard ball: no gravity, XSpeed negated on horizontal tile contact, YSpeed negated on vertical. | 8 | frames | verified | $039020 |
BOO_TURN_ANIMATION_FRAMES $15AC = $1F; the facing actually flips at $15AC == $10, half-way through. | 31 | frames | verified | $01F92A |
BOUNCE_SPRITE_SLOTS Searched 3 down to 0; overwrites via $18CD when all four are busy. | 4 | slots | verified | $028752 |
BOUNCE_SPRITE_TYPE_NAMES BounceSpriteNumber = DATA_00F05C value + 1 ($02882A). The dispatch table at $02904D names each. | turn-no-turn,music,question,sideways,translucent,onoff,turnblock | BounceSpriteNumber 1..7 | verified | $02904D |
BRICK_SHATTER_PLAYER_YSPEED Bounce type 7 (spin-jump break of tile $1E): player_yspeed = -48, 50 points, brick pieces spawned, tile blanked via generate index 2. | -48 | subpx/frame | verified | $028752 |
BULLET_BILL_SPEED_TABLE_X BulletSpeedX, indexed by $C2. No gravity, no acceleration, no wall reaction. | [32, -32, 0, 0, 24, 24, -24, -24] | subpx/frame | verified | $018FD7 |
BULLET_BILL_SPEED_TABLE_Y BulletSpeedY, same index. | [0, 0, -32, 32, -24, 24, 24, -24] | subpx/frame | verified | $018FDF |
BULLET_BLASTER_EDGE_SUPPRESS_RANGE No shot when the blaster is within 16 px of the camera's left edge. | 16 | px | verified | $02B39C |
BULLET_BLASTER_PERIOD ShooterTimer = $60 after every attempt, whether or not the shot happened. | 96 | frames | verified | $02B37C |
BULLET_BLASTER_PLAYER_SUPPRESS_RANGE ADC #$11 / CMP #$22 - no shot while |playerX - shooterX| <= 17. This is what lets the player stand on a blaster. | 17 | px | verified | $02B3A6 |
BULLET_DIAGONAL_GENERATOR_PERIOD DiagonalBulletTimer advances on odd frames only and fires at $A0 = 160 counts. | 320 | frames | verified | $02B0CD |
BULLET_GENERATOR_PERIOD GenerateBullet: EffFrame & $7F == 0. Y = (camY + (rand & $7F) + $20) & $F0, side and direction from bit0 of the same random byte. | 128 | frames | verified | $02B08A |
BUOYANT_PLATFORM_GRAVITY Platforms2 ($01B563, reached from FloatingSpikeBall $01B559; the brief's $01B99F is not this code) uses if (YSpeed < $40) ++YSpeed - gravity +1, not the shared +3. Shared by $5C, $5D, $A4 and $C4. | 1 | subpx/frame^2 | verified | $01B563 |
BUOYANT_PLATFORM_RISE_CAP While $164A SpriteInLiquid is set, YSpeed -= 2 each frame until it reaches the cap: $F8 (-8) for ids below $5D (the falling checkerboard $5C) and $FC (-4) for $5D and above (orange water platform $5D, floating spike ball $A4, falling grey platform $C4). Net in-liquid acceleration is -1/frame. Only $A4 integrates X; the platforms are vertical-only. | [-8, -4] | subpx/frame | verified | $01B58A |
CAMERA_H_WINDOW_HALF_WIDTH CameraLeftBuffer = trigger - $0C, CameraRightBuffer = trigger + $0C. Total window 24 px, not the 16 px commonly quoted from RGME. | 12 | px | verified | $00F6DB |
CAMERA_TRIGGER_FACING_LEFT DATA_00F6B3 dw $0090,$0060 indexed by $13FF = PlayerDirection*2. Facing left keeps the player right-of-centre. | 144 | px | verified | $00F6B3 |
CAMERA_TRIGGER_FACING_RIGHT Second entry of DATA_00F6B3. | 96 | px | verified | $00F6B3 |
CAMERA_TRIGGER_INITIAL LDA #$0080 / STA CameraMoveTrigger ($142A) on level init. | 128 | px | verified | $00A7B9 |
CAMERA_TRIGGER_SLIDE_RATE DATA_00F6BF entries at +8/+10 are dw $FFFE/$0002; applied in HandleLRScrolling ($00CE4C) when $1400 CameraProperMove is set. 144->96 takes 24 frames. | 2 | px/frame | verified | $00F6BF |
CAMERA_TURNAROUND_DRAG DATA_00F6CF entries dw $0001/$FFFF reduce the camera's horizontal move by 1 px while the trigger is sliding toward its target. | 1 | px/frame | verified | $00F8AB |
CAMERA_V_BOTTOM_CLAMP_HORIZ_LEVEL LDA #$00C0 passed into CODE_00F7F4 for non-vertical levels; vertical levels pass (LastScreenVert-1)<<8. | 192 | px | verified | $00A28A |
CAMERA_V_FLOOR_CLAMP DATA_00F6AD dw $0000,$0000,$00C0, indexed by the same scroll-direction index. | [0, 0, 192] | px | verified | $00F6AD |
CAMERA_V_SCROLL_DOWN_MAX Index 1. Downward scroll is never gated by the grounded check. | 5 | px/frame | verified | $00F6A7 |
CAMERA_V_SCROLL_FREE_UP_MAX Index 2, used when VerticalScrollEnabled ($13F1) is 0 and the level's vertical setting is not 1. | 6 | px/frame | verified | $00F6A7 |
CAMERA_V_SCROLL_UP_MAX DATA_00F6A7 dw $FFFD,$0005,$FFFA. Index 0 = normal upward scroll. | 3 | px/frame | verified | $00F6A7 |
CAMERA_V_WINDOW_BOTTOM Second entry. Screen-Y above 124 scrolls the camera down. | 124 | px | verified | $00F69F |
CAMERA_V_WINDOW_SPLIT CMP #$0070 selects which of the two window edges is used. | 112 | px | verified | $00F7F4 |
CAMERA_V_WINDOW_TOP DATA_00F69F dw $0064,$007C. Screen-Y below 100 scrolls the camera up. | 100 | px | verified | $00F69F |
CAPE_AIRCATCH_BUDGET_TABLE Indexed by (falling Vy >> 3); signed minimum is retained in MaxStageOfFlight while FlightPhase >= 2. Table entries 9-10 are unreachable. | [0, 0, 0, -8, -8, -8, -12, -16, -56] | subpx/frame | verified | $00D7D9 |
CAPE_AIRCATCH_CLIMB_GRAVITY | -12 | subpx/frame^2 | verified | $00D7A5 |
CAPE_AIRCATCH_DEAD_YSPEED_WRITE STX PlayerYSpeed+1 at $00D8B4 is overwritten by the STA at $00D94C in the same frame. Only the STZ MaxStageOfFlight matters. Do not model the write. | true | — | verified | $00D8AF |
CAPE_AIRCATCH_FAST_FRAMES back / neutral / back. The neutral frame changes NextFlightPhase so the second press bypasses CapePumpTimer. This is an input PATTERN, not a tolerance window - the spec's phrase '3-frame window' is a mis-framing. | 3 | frames | corroborated | $00D849 |
CAPE_AIRCATCH_FLOAT_GRAVITY Used at phase 1 once the budget is spent or the X-speed sign disagrees with facing. | 1 | subpx/frame^2 | verified | $00D7A5 |
CAPE_AIRCATCH_FULL_RISE 114.75 px over 62 rising frames, from budget -56 (requires sustained flight phase 6, Vy >= 64). | 1836 | subpx | verified | $00D8AF |
CAPE_AIRCATCH_PARTIAL_RISE 34.75 px over 34 rising frames. Budgets -8, -12 and -16 all give this because the -12/frame ramp overshoots them on the same frame. | 556 | subpx | verified | $00D8AF |
CAPE_AIRCATCH_STANDARD_FRAMES Frames from the first backward input to the first rising frame when the input is HELD: 1 immediate step + 8 timer frames + 2 frames for the phase-1 clamp/gravity to turn Vy negative. Not a tolerance window. | 11 | frames | corroborated | $00D856 |
CAPE_FLIGHT_BACKWARD_ACCEL MarioAccel_ row 14 L columns; only applied on frames with a NEW B press (see CAPE_FLIGHT_BACKWARD_NEEDS_B_PRESS). | -6 | subpx/frame^2 | verified | $00D345 |
CAPE_FLIGHT_BACKWARD_NEEDS_B_PRESS Backward d-pad during flight falls through to $00D764 (no friction while airborne) unless byetudlrFrame bit7 is set. TASVideos: 'pressing left and B will slow Mario down; press B repeatedly to slow down more'. | true | — | verified | $00D6B1 |
CAPE_FLIGHT_BACKWARD_SPEED_RANGE Attractor of accel -6.0 / target -8 / friction +1.0; cycle -13,-12,-11,-10,-9,-8,-7. | [-7, -13] | subpx/frame | corroborated | $00D742 |
CAPE_FLIGHT_BACKWARD_TARGET | -8 | subpx/frame | verified | $00D535 |
CAPE_FLIGHT_CEILING_BEHAVIOUR Rising + PlayerBlockedDir & $08 zeroes Vy at the top of the player update, before integration. Flight is not cancelled. | Vy=0, FlightPhase unchanged | — | verified | $00CD24 |
CAPE_FLIGHT_FACING_LOCKED The flight branch never writes PlayerDirection and skips the skid/turnaround block entirely. | true | — | verified | $00D6C3 |
CAPE_FLIGHT_FORWARD_ACCEL MarioAccel_ row 14 R-walk/R-run = $0400. With target 48 and friction -1.0 this produces the 51,50,49,48,47 cycle. | 4 | subpx/frame^2 | verified | $00D345 |
CAPE_FLIGHT_FORWARD_TARGET DATA_00D535 row 14; identical for all four run levels. | 48 | subpx/frame | verified | $00D535 |
CAPE_FLIGHT_GRAVITY_TABLE DATA_00D7A5[phase+3] for phase 1..6. Phase 1 uses +1 instead of -12 when MaxStageOfFlight==0, when PlayerXSpeed+1==0, or when X-speed sign disagrees with facing. | [-12, 1, 3, 4, 5, 6] | subpx/frame^2 | verified | $00D7A5 |
CAPE_FLIGHT_PHASE_COUNT Phases 0-6; phase 0 means not in the dive/swoop cycle. | 7 | phases | verified | $00D7C8 |