data/claims/smb1-demo.yamlSuper Mario Bros. title-screen demo (doppelganger's SMBDIS via Xkeeper0/smb1)
Game: Super Mario Bros..
Super Mario Bros.' demo is a script of 21 controller bytes in DemoActionData, each held for the number of frames at the same place in DemoTimingData; DemoEngine moves to the next byte when the count runs out. The 21 counts add up to 1,119 frames (computed), about 18.6 seconds at the NES's 60.1 frames a second (computed). smb1-demo/script
About: Super Mario Bros. Demo playback
Value
[ [ 1, 155 ], [ 128, 16 ], [ 2, 24 ], [ 129, 5 ], [ 65, 44 ], [ 128, 32 ], [ 1, 36 ], [ 66, 21 ], [ 194, 90 ], [ 2, 16 ], [ 128, 32 ], [ 65, 40 ], [ 193, 48 ], [ 65, 32 ], [ 193, 16 ], [ 1, 128 ], [ 193, 32 ], [ 1, 48 ], [ 2, 48 ], [ 128, 1 ], [ 0, 255 ] ]
src/prg.asm#L459-L467DemoActionData:
.db $01, $80, $02, $81, $41, $80, $01
.db $42, $c2, $02, $80, $41, $c1, $41, $c1
.db $01, $c1, $01, $02, $80, $00
DemoTimingData:
.db $9b, $10, $18, $05, $2c, $20, $24
.db $15, $5a, $10, $20, $28, $30, $20, $10
.db $80, $20, $30, $30, $01, $ff, $00smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L469-L485LDX DemoAction ; load current demo action
LDA DemoActionTimer ; load current action timer
BNE DoAction ; if timer still counting down, skip
INX
INC DemoAction ; if expired, increment action, X, and
…
LDA DemoTimingData-1,x ; get next timer
STA DemoActionTimer ; store as current timer
…
LDA DemoActionData-1,x ; get and perform action (current or next)
STA SavedJoypad1Bits
DEC DemoActionTimer ; decrement action timersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/defs.asm#L104-L111A_Button = %10000000
B_Button = %01000000
…
Left_Dir = %00000010
Right_Dir = %00000001Checked by claude, 2026-10-22 — read DemoEngine and both tables at the pinned commit; the first call with a timer of 0 loads a count and applies the byte once, so each byte is applied for its full count; summed the 21 counts before the final $00 (1,119); the frame rate (60.0988 Hz NTSC) is outside the source and only scales the computed secondsCited by: Demo playback
The demo writes each byte into SavedJoypad1Bits, the byte the interrupt routine fills from controller 1 every frame, and then calls GameCoreRoutine, the same engine a game in play runs, so Mario moves by the ordinary player code. smb1-demo/input-path
About: Super Mario Bros. Demo playback
Codesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) ·src/prg.asm#L74NonMaskableInterrupt:smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L116JSR ReadJoypads ; read joypadssmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L1895STA SavedJoypadBits,x ; save controller status here alwayssmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L480-L481LDA DemoActionData-1,x ; get and perform action (current or next)
STA SavedJoypad1Bitssmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L343-L348LDX DemoTimer ; otherwise check demo timer
BNE ChkWorldSel ; if demo timer not expired, branch to check world selection
STA SelectTimer ; set controller bits here if running demo
JSR DemoEngine ; run through the demo actions
BCS ResetTitle ; if carry flag set, demo over, thus branch
JMP RunDemo ; otherwise, run game engine for demosmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L390-L391RunDemo:
JSR GameCoreRoutine ; run game enginesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/ram.asm#L31-L32SavedJoypadBits = $06fc
SavedJoypad1Bits = $06fcChecked by claude, 2026-10-22 — read the NMI's ReadJoypads call, ReadPortBits' store, GameMenuRoutine's demo branch, and RunDemo at the pinned commit; SavedJoypadBits,x with x = 0 is SavedJoypad1BitsCited by: Demo playback
Each time the title screen starts, InitializeGame sets DemoTimer to $18 (24). It is one of the interval timers, which the interrupt routine counts down once every 21 frames, so the demo starts about 500 frames after the title appears (computed: 24 × 21 = 504, less up to 20 for the first tick). Pressing Select, or B once world select is enabled, puts the timer back to $18. smb1-demo/start
About: Super Mario Bros. Demo playback
Value
{
"ticks": 24,
"framesPerTick": 21
}src/prg.asm#L316-L323TitleScreenMode:
LDA OperMode_Task
JSR JumpEngine
.dw InitializeGamesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L2131-L2132LDA #$18 ; set demo timer
STA DemoTimersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L74NonMaskableInterrupt:smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L126-L140LDX #$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
…
DEC Timers,x ; otherwise decrement the current timersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/ram.asm#L60-L82Timers = $0780
…
DemoTimer = $07a2smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L355-L359LDA DemoTimer ; if select or B pressed, check demo timer one last time
BEQ ResetTitle ; if demo timer expired, branch to reset title screen mode
LDA #$18 ; otherwise reset demo timer
STA DemoTimersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L348-L354ChkWorldSel:
LDX WorldSelectEnableFlag ; check to see if world selection has been enabled
BEQ NullJoypad
CMP #B_Button ; if so, check to see if the B button was pressedChecked by claude, 2026-10-22 — DemoTimer at $07A2 is offset $22 of Timers, inside the interval range (X = $23) and outside the frame range (X = $14); IntervalTimerControl reloads $14 when it goes negative, so the interval timers tick every 21st frame; read the select/B branch at the pinned commitCited by: Demo playback
The demo ends, and the title screen restarts from InitializeGame, in three ways: the script reaches its closing count of 0; the engine reaches its lose-life routine (GameEngineSubroutine 6); or a player presses Start (alone or with A) or Select on either controller; B, which the title screen checks for world select, isn't tested while the demo runs. smb1-demo/end
About: Super Mario Bros. Demo playback
Codesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) ·src/prg.asm#L474-L478INC DemoAction ; if expired, increment action, X, and
SEC ; set carry by default for demo over
LDA DemoTimingData-1,x ; get next timer
STA DemoActionTimer ; store as current timer
BEQ DemoOver ; if timer already at zero, skipsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L390-L400JSR GameCoreRoutine ; run game engine
LDA GameEngineSubroutine ; check to see if we're running lose life routine
CMP #$06
BNE ExitMenu ; if not, do not do all the resetting below
ResetTitle:
LDA #$00 ; reset game modes, disable
STA OperMode ; sprite 0 check and disable
STA OperMode_Task ; screen outputsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L4266-L4276GameRoutines:
…
.dw PlayerLoseLifesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L330-L342LDA SavedJoypad1Bits ; check to see if either player pressed
ORA SavedJoypad2Bits ; only the start button (either joypad)
CMP #Start_Button
BEQ StartGame
CMP #A_Button+Start_Button ; check to see if A + start was pressed
…
CMP #Select_Button ; check to see if the select button was pressed
BEQ SelectBLogic ; if so, branch reset demo timersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L343-L344LDX DemoTimer ; otherwise check demo timer
BNE ChkWorldSel ; if demo timer not expired, branch to check world selectionsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L348-L357ChkWorldSel:
LDX WorldSelectEnableFlag ; check to see if world selection has been enabled
…
CMP #B_Button ; if so, check to see if the B button was pressed
…
SelectBLogic:
LDA DemoTimer ; if select or B pressed, check demo timer one last time
BEQ ResetTitle ; if demo timer expired, branch to reset title screen modesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L402-L404ChkContinue:
LDY DemoTimer ; if timer for demo has expired, reset modes
BEQ ResetTitleChecked by claude, 2026-10-22 — read DemoEngine's end branch, RunDemo's lose-life test (entry 6 of GameRoutines is PlayerLoseLife), and GameMenuRoutine's Start, A+Start, and Select tests, which run before the demo branch and reach ResetTitle while DemoTimer is 0; ResetTitle sets OperMode_Task to 0, TitleScreenMode's first task, InitializeGame; ChkWorldSel's B test is reached only while DemoTimer is not 0Cited by: Demo playback
The demo doesn't reseed the random generator. PseudoRandomBitReg at $07A7 gets its seed $A5 only at reset, and the interrupt routine shifts it every frame; InitializeGame clears memory only up to $076F. The generator's state when the demo starts therefore depends on the number of frames since reset. smb1-demo/rng-not-reset
About: Super Mario Bros. Demo playback
Codesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) ·src/prg.asm#L29-L35ColdBoot:
JSR InitializeMemory ; clear memory using pointer in Y
…
LDA #$a5 ; set warm boot flag
STA WarmBootValidation
STA PseudoRandomBitReg ; set seed for pseudorandom registersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L74NonMaskableInterrupt:smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L142-L158PauseSkip:
…
LDA PseudoRandomBitReg ; get first memory location of LSFR bytes
…
ROR PseudoRandomBitReg,x ; rotate carry into d7, and rotate last bit into carrysmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L2124-L2125LDY #$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/ram.asm#L237PseudoRandomBitReg = $07a7Checked by claude, 2026-10-22 — the only store of a seed into PseudoRandomBitReg is at ColdBoot (grep); PauseSkip follows the pause branch, so the shift runs every NMI; InitializeMemory clears page 7 from Y down to 0, so Y = $6F leaves $0770-$07FF; the statement makes no claim about whether the demo's run uses the generatorCited by: Demo playback