Claim set · data/claims/smb2j.yaml

Super Mario Bros.: The Lost Levels (doppelganger's SMB2J disassembly, threecreepio/smb2j-disassembly)

Game: Super Mario Bros.: The Lost Levels.

The Lost Levels keeps Super Mario Bros.'s movement tables for Mario byte for byte. Take-off speeds, held and falling gravities, frictions, and top speeds all match. smb2j/mario-tables

About: Super Mario Bros.: The Lost Levels Jump

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L4955-L4974JumpMForceData: .byte $20, $20, $1e, $28, $28, $0d, $04 … FallMForceData: .byte $70, $70, $60, $90, $90, $0a, $09 … FrictionData: .byte $e4, $98, $d0 … PlayerYSpdData: .byte $fc, $fc, $fc, $fb, $fb, $fe, $ff … …smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L4858-L4878JumpMForceData: .db $20, $20, $1e, $28, $28, $0d, $04 … FallMForceData: .db $70, $70, $60, $90, $90, $0a, $09 … PlayerYSpdData: .db $fc, $fc, $fc, $fb, $fb, $fe, $ff … InitMForceData: .db $00, $00, $00, $00, $00, $80, $00 … …Checked by claude, 2026-10-21 — compared the seven tables byte by byte in both disassemblies; LoadMarioPhysics copies the same Mario bytes back in for each game

Cited by: Super Mario Bros.: The Lost Levels

Picking Luigi copies his own gravities and frictions over Mario's: held gravity $18–$22 for the five jump rows against Mario's $1E–$28, falling gravity $3E–$5D against $60–$90, and friction $B4, $68, $A0 against $E4, $98, $D0. It also writes an RTS over the ASL that doubles Mario's friction when he faces away from his motion. His take-off speeds are Mario's. Each area's setup loads the chosen player's set; the axe at the end of a castle loads Mario's set and ASL back for the walk that follows, whoever is playing, until the next area loads the player's own again. smb2j/luigi-physics

About: Super Mario Bros.: The Lost Levels Jump

Value
{
 "heldGravity": [
  24,
  24,
  24,
  34,
  34
 ],
 "fallGravity": [
  66,
  66,
  62,
  93,
  93
 ],
 "friction": [
  180,
  104,
  160
 ]
}
CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14477-L14488;jump, fall and friction physics data used with mario and luigi ;note luigi's higher jump, but with lesser friction JumpFrictionData: .byte $20, $20, $1e, $28, $28, $0d, $04 .byte $70, $70, $60, $90, $90, $0a, $09 .byte $e4, $98, $d0 .byte $18, $18, $18, $22, $22, $0d, $04 .byte $42, $42, $3e, $5d, $5d, $0a, $09 .byte $b4, $68, $a0SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14490-L14507LoadPhysicsData: ldx #$60 ;use luigi's offsets and RTS opcode ldy #$21 lda SelectedPlayer ;if selected luigi, branch to continue bne ModifyPhysics LoadMarioPhysics: ldx #$0e ;otherwise use mario's offsets and ASL opcode ldy #$10 ModifyPhysics: stx PhyOpcode ;load opcode into friction code to modify it ldx #$10 MPhyLoop: …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L5124-L5129lda PlayerFacingDir cmp Player_MovingDir ;check facing direction against moving direction beq ExitPhy ;if the same, branch to leave PhyOpcode: asl FrictionAdderLow ;otherwise multiply friction by 2 rol FrictionAdderHigh ;then leave (or just leave, if code was modified earlier ExitPhy: rtsSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L2605-L2611DoneInitArea: lda #Silence ;silence music sta AreaMusicQueue lda #$01 ;disable screen output sta DisableScreenFlag jsr LoadPhysicsData inc OperMode_Task ;increment task for this mode rtsSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L11158-L11165HandleAxeMetatile: lda #$00 sta OperMode_Task ;reset secondary mode lda #$02 sta OperMode ;set primary mode to victory mode jsr LoadMarioPhysics lda #$18 sta Player_X_Speed ;set horizontal speed and continue to erase axe metatileChecked by claude, 2026-10-26 — read LoadPhysicsData; the loop copies 17 bytes (X = $10 down to 0) ending at JumpMForceData+$10, which spans JumpMForceData, FallMForceData, and FrictionData but not PlayerYSpdData; $60 is the RTS opcode and $0E is ASL absolute. InitializeArea calls LoadPhysicsData for every area, and HandleAxeMetatile calls LoadMarioPhysics (Y = $10, Mario's rows) on the axe

Cited by: Super Mario Bros. 2, Super Mario Bros.: The Lost Levels

ProcJumping starts a jump only while Player_State is 0, on the ground, or while the player is swimming, as Super Mario Bros.'s does. Walking off a ledge doesn't keep that state: PlayerBGCollision sets a player in the normal or climbing state to falling (or, swimming, to state 1) on each frame of play before it looks for ground, and only landing on a block sets the state back to 0, so out of water a jump can't start after walking off a ledge. The background check skips that step only during the game's scripted routines (GameEngineSubroutine 0 to 3 and $0B). smb2j/no-coyote-time

About: Super Mario Bros.: The Lost Levels Coyote time

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L5015-L5019ProcJumping: lda Player_State ;check player state beq InitJS ;if on the ground, branch lda SwimmingFlag ;if swimming flag not set, jump to do something else beq NoJump ;to prevent midair jumping, otherwise continueSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L10936-L10949lda GameEngineSubroutine cmp #$0b ;if running routine #11 or $0b beq ExPBGCol ;branch to leave cmp #$04 bcc ExPBGCol ;if running routines $00-$03 branch to leave lda #$01 ;load default player state for swimming ldy SwimmingFlag ;if swimming flag set, bne SetPSte ;branch ahead to set default state lda Player_State ;if player in normal state, beq SetFallS ;branch to set default state for falling cmp #$03 bne ChkOnScr ;if in any other state besides climbing, skip to next part …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L11046-L11056LandPlyr: jsr ChkForLandJumpSpring ;do sub to check for jumpspring metatiles and deal with it lda #$f0 and Player_Y_Position ;mask out lower nybble of player's vertical position sta Player_Y_Position ;and store as new vertical position to land player properly jsr HandlePipeEntry ;do sub to process potential pipe entry lda #$00 sta Player_Y_Speed ;initialize vertical speed and fractional sta Player_Y_MoveForce ;movement force to stop player's vertical movement sta StompChainCounter ;initialize enemy stomp counter InitSteP: lda #$00 sta Player_State ;set player's state to normalsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L4921-L4925ProcJumping: LDA Player_State ; check player state BEQ InitJS ; if on the ground, branch LDA SwimmingFlag ; if swimming flag not set, jump to do something else BEQ NoJump ; to prevent midair jumping, otherwise continueChecked by claude, 2026-10-26 — read ProcJumping in both disassemblies, PlayerBGCollision's state set and LandPlyr's reset at the pinned commits

Cited by: Coyote time, Super Mario Bros.: The Lost Levels

A hit on big Mario sets InjuryTimer to 8, as in Super Mario Bros., and the timer counts down once every 21 frames with the interval timers, through the same timer loop as Super Mario Bros.'s. smb2j/injury-timer

About: Super Mario Bros.: The Lost Levels Invincibility frames

Value
{
 "ticks": 8,
 "framesPerTick": 21
}
CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L10424-L10433lda InjuryTimer ;check again to see if either of the two ora StarInvincibleTimer ;invincibility timers have expired, branch if not bne ExInjColRoutines ForceInjury: ldx PlayerStatus ;check player's status beq KillPlayer ;branch if small sta PlayerStatus ;otherwise set player's status to small lda #$08 sta InjuryTimer ;set injured invincibility timerSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L928-L933ldx #$14 ;set offset to decrement only frame timers dec IntervalTimerControl ;if interval timer control not expired, branch bpl DecrTheTimers ;to skip and thus decrement only frame timers lda #$14 sta IntervalTimerControl ;otherwise reset interval timer control to 20 frames ldx #$23 ;and load offset to decrement frame and interval timersSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L81-L99Timers = $0780 … InjuryTimer = $079esmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L10583-L10592InjurePlayer: LDA InjuryTimer ; check again to see if injured invincibility timer is BNE ExInjColRoutines ; at zero, and branch to leave if so ForceInjury: LDX PlayerStatus ; check player's status BEQ KillPlayer ; branch if small STA PlayerStatus ; otherwise set player's status to small LDA #$08 STA InjuryTimer ; set injured invincibility timersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L126-L139DecTimers: LDX #$14 ; load end offset for end of frame timers DEC IntervalTimerControl ; decrement interval timer control, BPL DecTimersLoop ; if not expired, only frame timers will decrement LDA #$14 STA IntervalTimerControl ; if control for interval timers expired, LDX #$23 ; interval timers will decrement along with frame timers DecTimersLoop: LDA Timers,x ; check current timer BEQ SkipExpTimer ; if current timer expired, branch to skip, DEC Timers,x ; otherwise decrement the current timer SkipExpTimer: …Checked by claude, 2026-10-21 — read ForceInjury and the timer loop; InjuryTimer is Timers+$1E, an interval timer as in Super Mario Bros. (the comment's "20 frames" is the reload value; the period is 21)

Cited by: Super Mario Bros.: The Lost Levels

Wind blows the player to the right. An area object turns it on (WindOn) and another off (WindOff), and the setup that runs at the start of every area clears it. While it is on, and only in ground-type areas (underground, water, and castle areas stay calm), it moves the player 1 px right on every fourth frame while the frame counter is below 128 and on every second frame from 128 to 255: 96 px every 256 frames, 0.375 px a frame on average. The push is added to the position after the player's own move, on the ground and in the air but not while climbing, so it adds to a rightward speed and takes from a leftward one whatever the speed, and the screen scrolls with it. Its leaves and sound play while it is on, in every type of area. None of it runs until the game has loaded a disk file beyond worlds 1 to 4 (worlds 5 to 8, the ending, or worlds A to D): before that, FileListNumber is 0 and both calls are skipped. smb2j/wind

About: Super Mario Bros.: The Lost Levels Wind

Value
{
 "push": 1,
 "everyFrames": [
  4,
  2
 ],
 "per256Frames": 96,
 "average": 0.375
}
CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2data2.asm#L179-L202BlowPlayerAround: lda WindFlag ;if wind is turned off, just exit beq ExBlow lda AreaType ;don't blow the player around unless cmp #$01 ;the area is ground type bne ExBlow ldy #$01 lda FrameCounter ;branch to set d0 if on an odd frame asl bcs BThr ;otherwise wind will only blow ldy #$03 ;one out of every four frames BThr: sty $00 …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2data4.asm#L322-L345BlowPlayerAround: lda WindFlag ;if wind is turned off, just exit beq ExBlow lda AreaType ;don't blow the player around unless cmp #$01 ;the area is ground type bne ExBlow ldy #$01 lda FrameCounter asl bcs BThr ldy #$03 BThr: sty $00 …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L4848-L4853JmpMove: jsr MovePlayerHorizontally ;do another sub to move player horizontally sta Player_X_Scroll ;set returned value as player's movement speed for scroll lda FileListNumber ;if in worlds 1-4 don't bother checking for wind beq ExOGSS jsr BlowPlayerAround ExOGSS: rtsSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L4892-L4892JSMove: jsr JmpMoveSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L4830-L4835MoveSubs: jsr JumpEngine .word OnGroundStateSub .word JumpSwimSub .word FallingSub .word ClimbingSubSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L4273-L4275lda FileListNumber beq NoWind ;if in worlds 1-4, skip ahead jsr SimulateWind ;otherwise, simulate wind where neededSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2data2.asm#L217-L221SimulateWind: lda WindFlag ;if no wind, branch to leave beq ExSimW lda #$04 ;play wind sfx sta NoiseSoundQueueSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2data2.asm#L264-L270WindOn: lda #$01 ;branch to turn the wind on bne WOn WindOff: lda #$00 ;turn the wind off WOn: sta WindFlag rtsSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L4236-L4239.word InitializeArea .word ScreenRoutines .word SecondaryGameSetup .word GameCoreRoutineSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L2615-L2618SecondaryGameSetup: lda #$00 sta DisableScreenFlag ;reenable screen, reset some flags sta WindFlagChecked by claude, 2026-10-26 — read BlowPlayerAround in both overlay files, the two callers (JmpMove, reached from OnGroundStateSub and from JumpSwimSub and FallingSub through LRAir; ClimbingSub never calls it) and the game core's SimulateWind call, at the pinned commit; AreaType 1 is ground, as in Super Mario Bros.

ASL of FrameCounter puts its bit 7 in the carry, so the mask is 1 (every second frame) while the counter is 128-255 and 3 (every fourth) while it is 0-127; the disassembly's comment ("set d0 if on an odd frame") misreads it. SM2DATA2 (worlds 5 to 8) and SM2DATA4 (worlds A to D) each carry the routine (the ROM map's regions blow-player-around and blow-player-around-2). Which areas place WindOn is level data, not read here.

Cited by: Super Mario Bros.: The Lost Levels

A poison mushroom comes out of a block of its own kind, and stays a poison mushroom whatever the player's size, where an ordinary mushroom block gives a fire flower to a big or fiery player. It slides along like a mushroom. Touching it removes it and runs the injury routine: a small player dies, and a big or fiery one drops straight to small, with the blink that follows any hit. During that blink, or while a star's invincibility lasts, it is removed and does nothing. smb2j/poison-mushroom

About: Super Mario Bros.: The Lost Levels Power-up

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L6308-L6339BlockCode: jsr JumpEngine ;run appropriate subroutine depending on block number .word MushFlowerBlock .word PoisonMushBlock .word CoinBlock .word CoinBlock .word ExtraLifeMushBlock .word PoisonMushBlock .word MushFlowerBlock .word MushFlowerBlock .word PoisonMushBlock .word VineBlock .word StarBlock …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L6123-L6130lda PowerUpType cmp #$02 ;check currently loaded power-up type bcs PutBehind ;if star or 1-up, branch ahead lda PlayerStatus ;otherwise check player's current status cmp #$02 bcc StrType ;if player not fiery, use status as power-up type lsr ;otherwise shift right to force fire flower type StrType: sta PowerUpType ;store type hereSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L6148-L6155lda PowerUpType ;check power-up type beq ShroomM ;if normal mushroom, branch ahead to move it cmp #$03 beq ShroomM ;if 1-up mushroom, branch ahead to move it cmp #$04 beq ShroomM cmp #$05 beq ShroomMSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L6161-L6161ShroomM: jsr MoveNormalEnemy ;do sub to make mushrooms moveSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L10253-L10258HandlePowerUpCollision: jsr EraseEnemyObject ;erase the power-up object lda PowerUpType cmp #$04 ;check power-up type bne Safe ;if not a poison shroom, branch jmp InjurePlayer ;otherwise injure the player properlySMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L10423-L10436InjurePlayer: lda InjuryTimer ;check again to see if either of the two ora StarInvincibleTimer ;invincibility timers have expired, branch if not bne ExInjColRoutines ForceInjury: ldx PlayerStatus ;check player's status beq KillPlayer ;branch if small sta PlayerStatus ;otherwise set player's status to small lda #$08 sta InjuryTimer ;set injured invincibility timer asl sta Square1SoundQueue ;play pipedown/injury sound …Checked by claude, 2026-10-26 — read BumpBlock's jump table, SetupPowerUp, PowerUpObjHandler, HandlePowerUpCollision, and InjurePlayer through ForceInjury at the pinned commit

Power-up type 4. ForceInjury stores A, which is 0 after the timer check falls through, as the player's status, so fiery goes to small in one step. The metatile tables label poison-mushroom versions of the ? block, the hidden block, and two bricks (sm2main.asm lines 2149, 2155, 2162, 2197); which blocks hold one is level data. Super Mario Bros.'s InjurePlayer checks only the blink timer (super-mario-code-lineage/smb1-smb2j-collisions).

Cited by: Super Mario Bros.: The Lost Levels

The poison mushroom is drawn from the same four tiles as the mushroom and the 1-up mushroom ($D8, $DA, $DB, $FF) in sprite palette 3, where the mushroom uses palette 2 and the 1-up palette 1. Like them it keeps its colors, where the fire flower and the star cycle theirs. smb2j/poison-mushroom-look

About: Super Mario Bros.: The Lost Levels Power-up

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L12527-L12535PowerUpGfxTable: .byte $d8, $da, $db, $ff ;regular mushroom .byte $d6, $d6, $d9, $d9 ;fire flower .byte $8d, $8d, $e4, $e4 ;star .byte $d8, $da, $db, $ff ;1-up mushroom .byte $d8, $da, $db, $ff ;poison mushroom PowerUpAttributes: .byte $02, $01, $02, $01, $03SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L12565-L12571ldy Enemy_SprDataOffset+5 ;get sprite data offset again pla ;pull saved power-up type from the stack beq PUpOfs ;if regular mushroom, 1-up mushroom cmp #$03 ;or poison mushroom, branch beq PUpOfs ;do not change colors or flip them cmp #$04 beq PUpOfsChecked by claude, 2026-10-26 — read PowerUpGfxTable, PowerUpAttributes, and DrawPowerUp's color-cycling skip at the pinned commit

Which colors palette 3 holds in each kind of area isn't read here.

Cited by: Super Mario Bros.: The Lost Levels

Every area from World 4-4 on, and every area of worlds A to D, starts with the secondary hard mode flag set; the areas of worlds 1 to 3 and 4-1 to 4-3 start without it. The memory clear at the start of each area resets it first. smb2j/hard-mode-from-4-4

About: Super Mario Bros.: The Lost Levels Difficulty ramp

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L2557-L2559InitializeArea: ldy #$4b ;clear all memory again, only as far as $074b jsr InitializeMemory ;this is only necessary in game modeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L2591-L2600lda HardWorldFlag ;check to see if we're in worlds A-D bne SetSecHard ;if so, activate the secondary no matter where we're at lda WorldNumber ;otherwise check world number cmp #World4 ;if less than 4, do not activate secondary bcc CheckHalfway bne SetSecHard ;if not equal to, then world > 4, thus activate lda LevelNumber ;otherwise, world 4, so check level number cmp #Level4 ;if not 4, do not set secondary hard mode flag bcc CheckHalfway SetSecHard: inc SecondaryHardMode ;set secondary hard mode flag for areas 4-4 and beyondChecked by claude, 2026-10-26 — read InitializeArea at the pinned commit

SecondaryHardMode is $06CC, under the $074B limit of the clear. World numbers 4 and up (worlds 5 to 9) branch straight to the set.

Cited by: Super Mario Bros.: The Lost Levels

With the secondary hard mode flag set: enemies the level data marks for hard mode appear, where otherwise they are skipped; a Bullet Bill cannon is picked to fire when a pseudorandom number masked to three bits is below 6 (6 of 8 values, against 6 of 16 with four bits); up to four flying Cheep Cheeps at once instead of three; hammer bros throw on a shorter timer ($1C against $30), can take the longer of two jump timers ($37 against $20), and in worlds 1 to 6 wait a shorter time before walking left ($50 against $80; in worlds 7 to 9 they don't wait either way, and worlds A to D, which share world numbers 1 to 4 and always have the flag, take $50); Bloopers pick a new direction when two pseudorandom bits are clear rather than six, and then a Blooper in an even enemy slot turns toward the player, one in an odd slot takes the player's moving direction; Bowser's fire breath and the flames that fly in on their own come 16 timer ticks sooner, and the flames move faster ($60 subtracted from their fractional position each frame, against $40); and the large lifts are drawn four sprites wide instead of six with a smaller box, as they always are in castles, with balance platforms and their ropes placed for the shorter width. smb2j/hard-mode-effects

About: Super Mario Bros.: The Lost Levels Difficulty ramp

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L6968-L6973iny lda (EnemyData),y ;get second byte of object and #%01000000 ;check to see if hard mode bit is set beq CheckForEnemyGroup ;if not, branch to check for group enemy objects lda SecondaryHardMode ;if set, check to see if secondary hard mode flag beq Inc2B ;is on, and if not, branch to skip this object completelySMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L5753-L5754CannonBitmasks: .byte %00001111, %00000111SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L5763-L5767lda PseudoRandomBitReg+1,x ;otherwise get part of LSFR ldy SecondaryHardMode ;get secondary hard mode flag, use as offset and CannonBitmasks,y ;mask out bits of LSFR as decided by flag cmp #$06 ;check to see if lower nybble is above certain value bcs Chk_BB ;if so, branch to check enemySMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7408-L7414ldy #$03 ;load Y with default value lda SecondaryHardMode beq MaxCC ;if secondary hard mode flag not set, do not increment Y iny ;otherwise, increment Y to allow as many as four onscreen MaxCC: sty $00 ;store whatever pseudorandom bits are in Y cpx $00 ;compare enemy object buffer offset with Y bcs ChpChpEx ;if X => Y, branch to leaveSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8207-L8208HammerThrowTmrData: .byte $30, $1cSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8227-L8231lda HammerThrowingTimer,x ;check hammer throwing timer bne DecHT ;if not expired, skip ahead, do not throw hammer ldy SecondaryHardMode ;otherwise get secondary hard mode flag lda HammerThrowTmrData,y ;get timer data using flag as offset sta HammerThrowingTimer,x ;set as new timerSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8241-L8242HammerBroJumpLData: .byte $20, $37SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8267-L8273lda $00 ;load preset value here to use as bitmask and PseudoRandomBitReg+2,x ;and do bit-wise comparison with part of LSFR tay ;then use as offset lda SecondaryHardMode ;check secondary hard mode flag bne HJump tay ;if secondary hard mode flag clear, set offset to 0 HJump: lda HammerBroJumpLData,y ;get jump length timer data using offset from beforeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7166-L7167HBroWalkingTimerData: .byte $80, $50SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7173-L7178lda WorldNumber ;if on worlds 7-9, branch to skip the walk delay cmp #World7 bcs NoHBI ldy SecondaryHardMode ;get secondary hard mode flag lda HBroWalkingTimerData,y sta EnemyIntervalTimer,x ;set value as delay for hammer bro to walk leftSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8469-L8470BlooberBitmasks: .byte %00111111, %00000011SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8476-L8488ldy SecondaryHardMode ;use secondary hard mode flag as offset lda PseudoRandomBitReg+1,x ;get LSFR and BlooberBitmasks,y ;mask out bits in LSFR using bitmask loaded with offset bne BlooberSwim ;if any bits set, skip ahead to make swim txa lsr ;check to see if on second or fourth slot (1 or 3) bcc FBLeft ;if not, branch to figure out moving direction ldy Player_MovingDir ;otherwise, load player's moving direction and bcs SBMDir ;do an unconditional branch to set FBLeft: ldy #$02 ;set left moving direction by default jsr PlayerEnemyDiff ;get horizontal difference between player and bloober bpl SBMDir ;if enemy to the right of player, keep left …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L9257-L9262jsr SetFlameTimer ;get timing for bowser's flame ldy SecondaryHardMode beq SetFBTmr ;if secondary hard mode flag not set, skip this sec sbc #$10 ;otherwise subtract from value in A SetFBTmr: sta BowserFireBreathTimer ;set value as timer hereSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7554-L7561jsr SetFlameTimer ;get timer data based on flame counter clc adc #$20 ;add 32 frames by default ldy SecondaryHardMode beq SetFrT ;if secondary mode flag not set, use as timer setting sec sbc #$10 ;otherwise subtract 16 frames for secondary hard mode SetFrT: sta FrenzyEnemyTimer ;set timer accordinglySMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L9331-L9335lda #$40 ;load default movement force ldy SecondaryHardMode beq SFlmX ;if secondary hard mode flag not set, use default lda #$60 ;otherwise load alternate movement force to go faster SFlmX: sta $00 ;store value hereSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L12395-L12402ldy AreaType cpy #$03 ;check for castle-type level beq ShrinkPlatform ldy SecondaryHardMode ;check for secondary hard mode flag set beq SetLast2Platform ;branch if not set elsewhere ShrinkPlatform: lda #$f8 ;load offscreen coordinate if flag set or castle-type levelSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7935-L7942SPBBox: lda #$05 ;set default bounding box size control ldy AreaType cpy #$03 ;check for castle-type level beq CasPBB ;use default value if found ldy SecondaryHardMode ;otherwise check for secondary hard mode flag bne CasPBB ;if set, use default value lda #$06 ;use alternate value if not castle or secondary not set CasPBB: sta Enemy_BoundBoxCtrl,x ;set bounding box size control here and leaveSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7885-L7888ldy SecondaryHardMode ;if secondary hard mode flag not set, bne AlignP ;branch ahead ldy #$02 ;otherwise set value here jsr PosPlatform ;do a sub to add or subtract pixelsSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7985-L7986PlatPosDataLow: .byte $08,$0c,$f8SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L9813-L9821SetupPlatformRope: pha ;save second/third copy to stack lda Enemy_X_Position,y ;get horizontal coordinate clc adc #$08 ;add eight pixels ldx SecondaryHardMode ;if secondary hard mode flag set, bne GetLRp ;use coordinate as-is clc adc #$10 ;otherwise add sixteen more pixelsChecked by claude, 2026-10-26 — read every reader of SecondaryHardMode at the pinned commit (14 sites, grep for the label in all four source files) and the tables they index

The hammer bro's jump-timer pick is the pseudorandom bits masked with $00, which the code before it sets to 1 only while the hammer bro is above the middle of the screen (Y below $70), so the longer timer comes only up there. Without the flag, balance platforms move 8 px left (PlatPosDataLow's $F8) and their rope 16 px further right. Which enemies carry the hard-mode bit is level data, not read here.

Cited by: Super Mario Bros.: The Lost Levels

The code keeps the primary hard mode flag's checks, which turn Goombas into Buzzy Beetles, start walking enemies faster ($F4 against $F8), and revive stomped enemies sooner and send them off faster. Nothing in The Lost Levels' code sets the flag, and starting a game clears it, so none of these takes effect. Super Mario Bros. sets it from its world select flag when a game starts. smb2j/primary-hard-mode-unset

About: Super Mario Bros.: The Lost Levels Difficulty ramp

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L6983-L6989BuzzyBeetleMutate: cmp #Goomba ;if below $37, check for goomba bne StrID ;value ($3f or more always fails) ldy PrimaryHardMode ;check if primary hard mode flag is set beq StrID ;and if so, change goomba to buzzy beetle lda #BuzzyBeetle StrID: sta Enemy_ID,x ;store enemy object number into bufferSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7751-L7756pha ;save another copy to stack ldy #Goomba ;load value for goomba enemy lda PrimaryHardMode ;if primary hard mode flag not set, beq PullID ;branch, otherwise change to value ldy #BuzzyBeetle ;for buzzy beetle PullID: pla ;get second copy from stackSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7148-L7157NormalXSpdData: .byte $f8, $f4 InitNormalEnemy: ldy #$01 ;load offset of 1 by default lda PrimaryHardMode ;check for primary hard mode flag set bne GetESpd dey ;if not set, decrement offset GetESpd: lda NormalXSpdData,y ;get appropriate horizontal speed SetESpd: sta Enemy_X_Speed,x ;store as speed for enemy objectSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8213-L8214RevivedXSpeed: .byte $08, $f8, $0c, $f4SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L8351-L8356lda PrimaryHardMode ;check primary hard mode flag beq SetRSpd ;if not set, use pointer as-is iny iny ;otherwise increment 2 bytes to next data SetRSpd: lda RevivedXSpeed,y ;load and store new horizontal speed sta Enemy_X_Speed,x ;and leaveSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L10518-L10519RevivalRateData: .byte $10, $0bSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L10530-L10532ldy PrimaryHardMode ;check primary hard mode flag lda RevivalRateData,y ;load timer setting according to flag sta EnemyIntervalTimer,x ;set as enemy timer to revive stomped enemySMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14848-L14849ldy #$6f ;clear all memory as in initialization procedure, jsr InitializeMemory ;but this time, clear only as far as $076fsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L415-L416LDA WorldSelectEnableFlag ; if world select flag is on, then primary STA PrimaryHardMode ; hard mode must be on as wellChecked by claude, 2026-10-26 — grep for PrimaryHardMode and $076a in every source file at the pinned commit (five reads, no writes); read InitializeGame and InitializeMemory; read Super Mario Bros.'s StartWorld1 at xkeeper-smb1's pin

PrimaryHardMode is $076A. A search of sm2main.asm, sm2data2-4.asm and fdswrap.asm finds it only as a source (LDA, LDY) and no store to it by name or by its address; InitializeGame's InitializeMemory call clears $0000-$076F (the stack's $0100-$0108 and $0160-$01FF spared), and the cold boot clears more.

Cited by: Super Mario Bros.: The Lost Levels

A warp zone leads to one world, whichever of its pipes the player takes. In world 1 it leads to world 2 from the first underground area, to world 3 from a ground area, and to world 4 from any other; in world 3, back to world 1; in world 5, to world 6 from one ground area (the 12th in the area table, which the disassembly's comment places at 5-1), to world 8 from any other ground area, and to world 7 from any other area; in worlds 2, 4, and 6 to 9, to world 5, which from 6 to 8 is back. In worlds A to D it goes by level: from a second level to world B, a third to world C, a fourth to world D; the table holds no world for a first level. smb2j/warp-destinations

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L3425-L3468ScrollLockObject_Warp: ldx #$80 ;use base number for warp to world 2 lda HardWorldFlag ;if on worlds A-D, skip ahead to next part bne WarpWorldsAThruD ;note d7 is set in all entries to prevent zero condition lda WorldNumber ;from happening in warp zone code elsewhere bne WarpWorlds2Thru8 ;if not on world 1, branch to handle a different way ldy AreaType ;check to see if on ground level type dey ;branch if so to add one to the number beq W1Warp2 lda AreaAddrsLOffset ;if on first underground level, branch to use base number beq W1Warp1 inx ;otherwise add two to the number and use it …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L1689-L1690WarpZoneNumbers: .byte $02, $03, $04, $01, $06, $07, $08, $05, $0b, $0c, $0dSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L11332-L11343lda WarpZoneControl ;check warp zone control beq ExPipeE ;branch to leave if none found and #%00001111 ;mask out all but lower nybble tax ;save as offset, then use to load warp zone destination lda WarpZoneNumbers,x ldy HardWorldFlag ;if playing worlds A-D, branch to skip this part beq SetWDest sec sbc #$09 ;otherwise subtract 9 to get correct world number SetWDest: tay dey ;decrement for use as world number sty WorldNumber ;store as world number and offsetChecked by claude, 2026-10-26 — read ScrollLockObject_Warp's branches entry by entry against WarpZoneNumbers and HandlePipeEntry at the pinned commit

The low nybble of WarpZoneControl indexes WarpZoneNumbers ($02, $03, $04, $01, $06, $07, $08, $05, $0B, $0C, $0D); in worlds A to D the entry less 9 is the letter world's number, so $87 plus level 0 gives $05 - 9, no world. Which areas hold warp zones is level data, not read here.

Cited by: Super Mario Bros.: The Lost Levels

Clearing a world's castle sets that world's bit in a record of completed worlds. After the ending that follows 8-4, the game goes on to World 9 if all eight bits are set, and back to the title screen if any is missing; after D-4 it always goes back to the title. The record is cleared when a game starts and when the player picks Retry at a game over, and Continue keeps it, so a world skipped by a warp counts only if the player warps back and clears its castle. smb2j/world-9-condition

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L1185-L1195WorldBits: .byte $01, $02, $04, $08, $10, $20, $40, $80 SetupVictoryMode: ldx ScreenRight_PageLoc ;get page location of right side of screen inx ;increment to next page stx DestinationPageLoc ldy WorldNumber lda WorldBits,y ora CompletedWorlds ;set bit according to the world the player was in sta CompletedWorldsSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2data3.asm#L428-L451BackToNormal: lda #>SoundEngine ;reset sound engine vector sta SoundEngineJSRCode+2 ;to run the original one lda #<SoundEngine sta SoundEngineJSRCode+1 lda #$00 sta DiskIOTask ;erase task numbers sta OperMode_Task lda HardWorldFlag ;if in world D, branch to end the game bne EndTheGame lda CompletedWorlds ;if completed all worlds without skipping over any cmp #$ff ;then branch elsewhere (note warping backwards may …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14692-L14698lda SavedJoypadBits ;check to see if the player pressed start and #Start_Button beq ChkSelect ;if not, branch to check other buttons lda #$00 sta CompletedWorlds sta DiskIOTask sta HardWorldFlagSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14455-L14460ContinueOrRetry: lda ContinueMenuSelect ;if player selected "continue" beq Continue ;then branch to continue lda #$00 sta CompletedWorlds ;otherwise init completed worlds flags jmp TerminateGame ;and end the gameSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14462-L14473Continue: ldy #$02 sty NumberofLives ;give three lives sta LevelNumber sta AreaNumber ;put at x-1 of the current world sta CoinTally ldy #$0b ISCont: sta ScoreAndCoinDisplay,y ;reset score dey bpl ISCont inc Hidden1UpFlag ;allow 1-up to be found again jmp ContinueGameChecked by claude, 2026-10-26 — read SetupVictoryMode's bit set, BackToNormal's three ways out, and the clears in GameMenuRoutine and ContinueOrRetry (Continue leaves CompletedWorlds alone) at the pinned commit

Cited by: Super Mario Bros.: The Lost Levels

World 9 begins with one life. A game over there ends the game, with no continue menu. Its four levels loop: after 9-4 the game starts 9-1 again. smb2j/world-9-rules

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2data3.asm#L446-L451GoToWorld9: lda #$00 sta CompletedWorlds ;init completed worlds flag sta NumberofLives ;give the player one life sta FantasyW9MsgFlag jmp NextWorld ;run world 9SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L2850-L2867RunGameOver: lda #$00 sta DisableScreenFlag lda WorldNumber ;if on world 9, branch on to end the game cmp #World9 beq W9End jmp GameOverMenu ;otherwise run game over menu W9End: lda ScreenTimer bne ExRGO TerminateGame: lda #Silence ;silence music sta EventMusicQueue …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L4793-L4803NextArea: inc AreaNumber ;increment area number used for address loader lda WorldNumber cmp #$08 bne NotW9 ;if not at end of world 9-4, branch lda LevelNumber ;otherwise reset level and area numbers properly cmp #$04 bne NotW9 lda #$00 sta LevelNumber sta AreaNumber NotW9: jsr LoadAreaPointer ;get new level pointerSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L1303-L1313NextWorld: lda #$00 sta AreaNumber ;reset area/level numbers to start the next world sta LevelNumber sta OperMode_Task lda WorldNumber clc adc #$01 ;add one, but only up to world 9 cmp #World9 bcc NoPast9 lda #World9 ;make world 9 loop forever (or until game is over) NoPast9: sta WorldNumber ;update the world numberChecked by claude, 2026-10-26 — read GoToWorld9, RunGameOver, PlayerEndLevel's world 9 wrap, and NextWorld's cap at the pinned commit

The lives counter holds lives less one, as Continue's "ldy

Cited by: Super Mario Bros.: The Lost Levels

Each time the ending loads, after 8-4 or D-4, the count of games beaten goes up by one, to at most 24 (a short read of the ending's files resets it to 0 first). After 8-4 the game writes the count to the disk's save file (a disk error aside); after D-4 it doesn't, and the title screen, which reloads the disk's first files after a game in worlds A to D or from world 5 on, reads the saved count back, so D-4 adds nothing. The title screen shows a star for each game beaten, in two rows of 12. With 8 or more, holding A while pressing Start begins the game at World A, loading worlds A to D, with their own halfway points, from the disk. smb2j/letter-worlds

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14261-L14280LoadEnding: lda #$02 ;set filelist number to load SM2DATA3, SM2CHAR2 and SM2SAVE sta FileListNumber jsr LoadFiles ;load them now bne ReturnedError jsr CheckFileCount ;check to see if loaded the correct number of files beq AddBeatenGame ;if so, continue lda #$00 ;otherwise reset count of games beaten sta GamesBeatenCount AddBeatenGame: lda GamesBeatenCount ;get the new count of games beaten we loaded (or reset) clc ;add one to it, to a maximum of 24/$18 …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2data3.asm#L391-L426RunMushroomRetainers: jsr MushroomRetainersForW8 ;draw and flash the seven mushroom retainers lda AltMusicBuffer ;if still playing victory music, branch to leave bne ExRMR lda HardWorldFlag ;if on world D, branch elsewhere bne BackToNormal inc OperMode_Task ;otherwise just move onto the last task ExRMR: rts EndingDiskRoutines: lda DiskIOTask jsr JumpEngine .word DiskScreen …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14180-L14193LoadWorlds1Thru4: lda NotColdFlag ;if not set, just cold booted, thus no need to check world info beq InitWorldPos lda HardWorldFlag ;if player was playing worlds A-D, skip ahead bne LW14Files ;otherwise check the world number lda WorldNumber cmp #World5 ;if world number was less than 5, files still in memory bcc InitWorldPos ;thus skip to the end, no need to load them again LW14Files: lda #$00 ;set filelist number to reload SM2MAIN, SM2CHAR1 and SM2SAVE sta FileListNumber jsr LoadFiles ;now reload those files bne ReturnedError ;if returned with error number, branch …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14301-L14308World14List: .byte $01, $05, $0f, $ff World58List: .byte $20, $ff EndingList: .byte $10, $30, $0f, $ff WorldADList: .byte $40, $ffSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14832-L14847ldy #$33 ;set up offset in the title screen tiles lda #$0c ;set up counter to print up to 12 stars per row sta $00 ldx #$00 ;init star counter PrintStars: lda #$26 ;print blank by default cpx GamesBeatenCount ;check star counter against games beaten bcs PrintToTS ;if counted up to games beaten, print the blank lda #$f1 ;otherwise print a star for a beaten game PrintToTS: sta TitleScreenGfxData,y ;print to title screen iny dec $00 ;decrement until done printing a row bne NextStarR …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14692-L14706lda SavedJoypadBits ;check to see if the player pressed start and #Start_Button beq ChkSelect ;if not, branch to check other buttons lda #$00 sta CompletedWorlds sta DiskIOTask sta HardWorldFlag lda GamesBeatenCount ;check to see if player has beaten cmp #$08 ;the game at least 8 times bcc StG ;if not, start the game as usual at world 1 lda SavedJoypadBits and #A_Button ;check if the player pressed A + start …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14147-L14160LoadHardWorlds: lda HardWorldFlag ;if this is not set, skip this beq NoLoadHW lda #$03 sta FileListNumber ;set filelist number to load SM2DATA4 jsr LoadFiles bne ReturnedError ;if returned with error number, branch jsr CheckFileCount ;check to see if loaded the correct number of files bne BadFileCount ;if not, branch to set error code NoLoadHW: jsr LoadAreaPointer ;update area pointer to get world 1 or A lda HardWorldFlag ;if this is not set, skip this …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L1196-L1202lda HardWorldFlag ;if not playing worlds A-D, branch to skip this beq W1Thru8 lda WorldNumber ;otherwise, if not on world D, branch to skip this cmp #World4 ;(note worlds A-D use values 0-3 in this variable) bcc W1Thru8 lda #World8 ;if on world D, set world number to 8 to satisfy sta WorldNumber ;end of game condition in later victory mode subsChecked by claude, 2026-10-26 — read LoadEnding, the ending's task lists, the title's star loop and Start handling, and the two disk loaders at the pinned commit

The count is GamesBeatenCount, the byte that the save file SM2SAVE loads over at $D29F (sm2main.asm line 15279: "this is overwritten by the contents of SM2SAVE"). D-4 reaches the ending because SetupVictoryMode sets the world number to 8 on world D. After D-4, RunMushroomRetainers skips the disk task list that writes the save, and LoadWorlds1Thru4, with HardWorldFlag still set, reloads file list 0 (SM2MAIN, SM2CHAR1, SM2SAVE). A disk error is handled by DiskErrorHandler, not read here.

Cited by: Super Mario Bros.: The Lost Levels

Landing on a springboard sets both the held and the falling gravity to $70 and a launch speed of −7 px a frame ($F9). Over the spring's four steps it carries the player down 2 px a step, then up 2 px a step, and on the last step it launches them. A press of A, not held from before, in the second step or later raises the launch to −12 px a frame ($F4), or to −32 px a frame ($E0) on the green springboards of world numbers 2, 3, and 7, which the letter worlds share as B and C, since the test reads the world number without the letter-world flag. With both gravities at $70, the bounce rises and falls under one pull whether A is held or not, for Mario and Luigi alike. While the game's timers are stopped the spring waits. smb2j/springboard

About: Super Mario Bros.: The Lost Levels Springboard

Value
{
 "gravity": 112,
 "launch": -7,
 "pressed": -12,
 "green": -32
}
CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L11291-L11303ChkForLandJumpSpring: jsr ChkJumpspringMetatiles ;do sub to check if player landed on jumpspring bcc ExCJSp ;if carry not set, jumpspring not found, therefore leave lda #$70 sta VerticalForce ;otherwise set vertical movement force for player sta VerticalForceDown lda #$f9 sta JumpspringForce ;set default jumpspring force lda #$03 sta JumpspringTimer ;set jumpspring timer to be used later lsr sta JumpspringAnimCtrl ;set jumpspring animation control to start animating …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L5574-L5620JumpspringHandler: jsr GetEnemyOffscreenBits ;get offscreen information lda TimerControl ;check master timer control bne DrawJSpr ;branch to last section if set lda JumpspringAnimCtrl ;check jumpspring frame control beq DrawJSpr ;branch to last section if not set tay dey ;subtract one from frame control in A, tya ;the only way a poor NMOS 6502 can and #%00000010 ;mask out all but d1, original value still in Y bne DownJSpr ;if set, branch to move player up inc Player_Y_Position …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L12672-L12683lda #$02 ldy WorldNumber ;if the world number is not 2, 3 or 7 cpy #$01 ;then use regular attributes for jumpsprings beq GrnJS ;which will paint them red cpy #$02 beq GrnJS ;otherwise use alternate attributes cpy #$06 ;to get the green superhigh jumpsprings bne RedJS GrnJS: lsr RedJS: sta EnemyAttributeData+$18 ;set jumpspring gfx attributes in the lookup table sta EnemyAttributeData+$19 sta EnemyAttributeData+$1aSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L6575-L6584MovePlayerVertically: ldx #$00 ;set X for player offset lda TimerControl bne NoJSChk ;if master timer control set, branch ahead lda JumpspringAnimCtrl ;otherwise check to see if jumpspring is animating bne ExXMove ;branch to leave if so NoJSChk: lda VerticalForce ;dump vertical force sta $00 lda #$04 ;set maximum vertical speed here jmp ImposeGravitySprObj ;then jump to move player verticallyChecked by claude, 2026-10-26 — read ChkForLandJumpSpring, JumpspringHandler, the spring's color patch in EnemyGfxHandler, and MovePlayerVertically at the pinned commit

The step is JumpspringAnimCtrl, 1 to 4; the handler reads it less one (0 to 3). The player's vertical move is skipped while the spring animates, and its gravity code caps only the fall (ImposeGravitySprObj passes no upward limit). Worlds A to D use world numbers 0 to 3 (the comment at sm2main.asm line 1199).

Cited by: Super Mario Bros.: The Lost Levels

Piranha plants are green in worlds 1 to 3 and red in worlds 4 to 9 and A to D. A green one stays in its pipe while the player is less than 33 px from it across, and moves on every second frame; a red one stays in only while the player is less than 19 px away, and moves on every frame. smb2j/piranha-plants

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7805-L7821InitPiranhaPlant: lda #$22 ;set default attribute and range data here sta $00 ;range data is used to detect how close player is lda #$13 ;to the piranha plant to keep it inside the pipe sta $01 ;default data makes red piranha plants lda HardWorldFlag bne PatchPP ;use default data if in worlds A-D lda WorldNumber cmp #$03 bcs PatchPP ;use default data if in worlds 4-8 dec $00 lda #$21 ;otherwise make green piranha plant instead …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L9593-L9596ChkPlayerNearPipe: lda $00 ;get saved horizontal difference cmp #$21 bcc PutinPipe ;if player within a certain distance, branch to leaveSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L9612-L9619RiseFallPiranhaPlant: sta $00 ;save vertical coordinate here lda EnemyAttributeData+PiranhaPlant cmp #$22 ;check for red piranha plants beq RedPP ;if found, skip to next part to execute code on every frame lda FrameCounter ;get frame counter lsr bcc PutinPipe ;branch to leave if d0 set (execute code every other frame)Checked by claude, 2026-10-26 — read InitPiranhaPlant and MovePiranhaPlant at the pinned commit

InitPiranhaPlant patches the operand of the cmp #$21 at ChkPlayerNearPipe+3: $13 for red, $21 for green. The palette patch is in EnemyAttributeData, which RiseFallPiranhaPlant reads back to tell red from green.

Cited by: Super Mario Bros.: The Lost Levels

While Lakitu's stretch of a level runs, the game checks every 128 frames whether a Lakitu is on screen. When none is, it counts the check, and on the third it sends a new Lakitu if an enemy slot is free (if none is, it tries again at the next check); the count starts over when a Lakitu is set up. The new one flies high in worlds 1 to 6 ($20) and lower in worlds 7 to 9 and A to D ($60). When one is on screen, the same check leads to the Spiny throw, not covered here. smb2j/lakitu-return

About: Super Mario Bros.: The Lost Levels Enemy spawning

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7251-L7288LakituAndSpinyHandler: lda FrenzyEnemyTimer ;if timer here not expired, leave bne ExLSHand cpx #$05 ;if we are on the special use slot, leave bcs ExLSHand lda #$80 ;set timer sta FrenzyEnemyTimer ldy #$04 ;start with the last enemy slot ChkLak: lda Enemy_ID,y ;check all enemy slots to see cmp #Lakitu ;if lakitu is on one of them beq CreateSpiny ;if so, branch out of this loop dey ;otherwise check another slot …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L7234-L7236SetupLakitu: lda #$00 ;erase counter for lakitu's reappearance sta LakituReappearTimerSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L928-L933ldx #$14 ;set offset to decrement only frame timers dec IntervalTimerControl ;if interval timer control not expired, branch bpl DecrTheTimers ;to skip and thus decrement only frame timers lda #$14 sta IntervalTimerControl ;otherwise reset interval timer control to 20 frames ldx #$23 ;and load offset to decrement frame and interval timersChecked by claude, 2026-10-26 — read LakituAndSpinyHandler and SetupLakitu at the pinned commit

FrenzyEnemyTimer is $078F, one of the frame timers ($0780-$0794) that count down every frame while the game's timers run.

Cited by: Super Mario Bros.: The Lost Levels

At a game over outside World 9, Select switches between CONTINUE and RETRY and Start picks. Continue restarts the current world at its first level, small, with three lives, the score and coins at zero, and the hidden 1-up available again. Retry ends the game and clears the record of completed worlds. smb2j/continue-or-retry

About: Super Mario Bros.: The Lost Levels Continue

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14431-L14444GameOverMenu: lda SavedJoypadBits ;if player pressed the start button and #Start_Button ;then either continue or start over bne ContinueOrRetry lda SavedJoypadBits and #Select_Button ;if player pressed the select button beq ChgSel ;then branch to select "continue" or "retry" ldx SelectTimer ;if select timer not expired while bne ChgSel ;pressing select, skip this lsr sta SelectTimer ;otherwise set the select timer lda ContinueMenuSelect …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L14455-L14473ContinueOrRetry: lda ContinueMenuSelect ;if player selected "continue" beq Continue ;then branch to continue lda #$00 sta CompletedWorlds ;otherwise init completed worlds flags jmp TerminateGame ;and end the game Continue: ldy #$02 sty NumberofLives ;give three lives sta LevelNumber sta AreaNumber ;put at x-1 of the current world sta CoinTally …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L2869-L2881ContinueGame: jsr LoadAreaPointer ;update level pointer with lda #$01 ;actual world and area numbers, then sta PlayerSize ;reset player's size, status, and inc FetchNewGameTimerFlag ;set game timer flag to reload lda #$00 ;game timer from header sta TimerControl ;also set flag for timers to count again sta PlayerStatus sta GameEngineSubroutine ;reset task for game core sta OperMode_Task ;set modes and leave lda #$01 ;if in game over mode, switch back to sta OperMode ;game mode, because game is still on …Checked by claude, 2026-10-26 — read GameOverMenu, ContinueOrRetry, Continue, and ContinueGame at the pinned commit

Cited by: Super Mario Bros.: The Lost Levels

Grabbing the flagpole gives an extra life in place of the height's points when the coin count's two digits are the same and match the timer's last digit (11 coins with the timer ending in 1, for example). smb2j/flagpole-1up

About: Super Mario Bros.: The Lost Levels Extra life

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L11219-L11231ChkFlagpoleYPosLoop: cmp FlagpoleYPosData,x ;compare with current vertical coordinate data bcs MtchF ;if player's => current, branch to use current offset dex ;otherwise decrement offset to use bne ChkFlagpoleYPosLoop ;do this until all data is checked (use last one if all checked) MtchF: stx FlagpoleScore ;store offset here to be used later lda CoinDisplay cmp CoinDisplay+1 ;check to see if coin tally digits are the same bne RunFR ;if not, branch to use flagpole score data as-is cmp GameTimerDisplay+2 ;check to see if the last digit of game timer matches bne RunFR ;the two digits, if not, branch to use data as-is lda #$05 …SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L5551-L5559GiveFPScr: ldy FlagpoleScore ;get score offset from earlier (when player touched flagpole) cpy #$05 bne NoEL4F ;if set to give player an extra life, do so now inc NumberofLives lda #$40 sta $fe jmp NoSc4F NoEL4F: lda FlagpoleScoreMods,y ;get amount to award player points ldx FlagpoleScoreDigits,y ;get digit with which to award pointsChecked by claude, 2026-10-26 — read HandleClimbing's flagpole score and FlagpoleRoutine's award at the pinned commit

Cited by: Super Mario Bros.: The Lost Levels

Fireworks go off over the castle when the timer's last digit matches the coin count's last digit as the level ends: three when the digit is odd, six when it is even, and none when they differ. smb2j/fireworks

CodeSMB2J disassembly for ca65 (threecreepio/smb2j-disassembly) · sm2main.asm#L9470-L9485GameTimerFireworks: lda GameTimerDisplay+2 ;check to see if last digit of timer matches cmp CoinDisplay+1 ;the last digit in the coin tally bne NoFWks ;if not, skip the fireworks and #$01 beq EvenDgs ;if so, check to see if they are both odd or even ldy #$03 lda #$03 ;if they are both odd, set state and counter bne SetFWC ;for 3 fireworks to go off EvenDgs: ldy #$00 ;if they are both even, set state and counter lda #$06 ;for 6 fireworks to go off bne SetFWC …Checked by claude, 2026-10-26 — read GameTimerFireworks at the pinned commit

Cited by: Super Mario Bros.: The Lost Levels