data/claims/smb1-av.yamlSuper Mario Bros. graphics and sound (doppelganger's SMBDIS via Xkeeper0/smb1)
Game: Super Mario Bros..
Each frame the NMI handler waits for the sprite 0 flag to clear, spins until the sprite 0 hit, waits a short loop for horizontal blank, and only then writes the level's scroll, so the status bar above the hit stays still. smb1-av/sprite-0-split
About: Super Mario Bros. Status bar split
Codesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) ·src/prg.asm#L159-L183LDA Sprite0HitDetectFlag ; check for flag here
…
LDA PPU_STATUS ; wait for sprite 0 flag to clear, which will
AND #%01000000 ; not happen until vblank has ended
…
Sprite0Hit:
LDA PPU_STATUS ; do sprite #0 hit detection
AND #%01000000
BEQ Sprite0Hit
LDY #$14 ; small delay, to wait until we hit horizontal blank time
…
LDA HorizontalScroll ; set scroll registers from variables
…Checked by claude, 2026-10-21 — read the NMI handler's sprite 0 wait at the pinned commitCited by: Status bar split
The game sets sprite 0 to OAM bytes $18, $FF, $23, $58, so tile $FF sits at Y $18 and X $58 behind the background in sprite palette 3. smb1-av/sprite-0-placement
About: Super Mario Bros. Status bar split
Value
{
"y": 24,
"tile": 255,
"attributes": 35,
"x": 88
}src/prg.asm#L2118-L2119Sprite0Data:
.db $18, $ff, $23, $58smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L2236-L2245LDY #$03 ; set up sprite #0
ISpr0Loop:
LDA Sprite0Data,y
STA Sprite_Data,y
…
INC Sprite0HitDetectFlag ; set sprite #0 check flagChecked by claude, 2026-10-21 — read Sprite0Data and the loop that copies it into OAMThe OAM bytes are Y, tile, attributes, X (nesdev-ppu-oam). Attribute $23 sets bit 5 (behind the background) and palette bits 3. Which status-bar tile the hit lands on was not checked; a probe should name it.
Cited by: Status bar split
Every unpaused frame SpriteShuffler adds one of three amounts, $58, $48, or $38 in turn, to each object's OAM offset at or above $28, wrapping within $28-$FF, so the objects' sprites change OAM slots each frame and a ninth sprite on a line drops out on different frames. smb1-av/sprite-shuffle
About: Super Mario Bros. Sprite flicker
Value
{
"amounts": [
88,
72,
56
],
"floor": 40,
"phases": 3
}src/prg.asm#L164-L169LDA GamePauseStatus ; if in pause mode, do not bother with sprites at all
…
JSR MoveSpritesOffscreen
JSR SpriteShufflersmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L240-L266SpriteShuffler:
…
LDA #$28 ; load preset value which will put it at
STA $00 ; sprite #10
LDX #$0e ; start at the end of OAM data offsets
ShuffleLoop:
LDA SprDataOffset,x ; check for offset value against
CMP $00 ; the preset value
BCC NextSprOffset ; if less, skip this part
LDY SprShuffleAmtOffset ; get current offset to preset value we want to add
CLC
ADC SprShuffleAmt,y ; get shuffle amount, add to current sprite offset
…smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L2225-L2230LDA #$38 ; load sprite shuffle amounts to be used later
STA SprShuffleAmt+2
LDA #$48
STA SprShuffleAmt+1
LDA #$58
STA SprShuffleAmtChecked by claude, 2026-10-21 — read SpriteShuffler, its call in the NMI handler, and the amounts set at area startThe claim that a dropped sprite moves between frames follows from the PPU's in-order search (nesdev-ppu-oam); the atlas has not measured which sprites flicker in play.
Cited by: Sprite flicker
Every 8th frame ColorRotation rewrites background palette 3 at $3F0C, putting the next of six colors, $27, $27, $27, $17, $07, $17, in its second entry, so the question blocks and coins shimmer on a 48-frame loop. smb1-av/color-rotation
About: Super Mario Bros. Palette cycling
Value
{
"palette": "$3F0C",
"entry": 1,
"colors": [
39,
39,
39,
23,
7,
23
],
"framesPerStep": 8,
"loopFrames": 48
}src/prg.asm#L1386-L1390ColorRotatePalette:
.db $27, $27, $27, $17, $07, $17
…
.db $3f, $0c, $04, $ff, $ff, $ff, $ff, $00smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L1399-L1402ColorRotation:
LDA FrameCounter ; get frame counter
AND #$07 ; mask out all but three LSB
BNE ExitColorRot ; branch if not set to zero to do this every eighth framesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/prg.asm#L1429-L1441LDY ColorRotateOffset ; get color cycling offset
LDA ColorRotatePalette,y
STA VRAM_Buffer1+4,x ; get and store current color in second slot of palette
…
CMP #$06 ; check to see if it's still in rangeChecked by claude, 2026-10-21 — read ColorRotation, its tables, and its call from the area's per-frame routinesBlankPalette is a VRAM buffer header (address $3F0C, 4 bytes); the area-type row of Palette3Data fills it and the cycled color overwrites entry 1. Which tiles use palette 3 is a T0 reading from play, not checked in the level data.
Cited by: CHR bank switching, Palette cycling
The cartridge header declares two 16 KB PRG ROM pages and one 8 KB CHR ROM page, so the game's tile set never changes. smb1-av/chr-fixed
About: Super Mario Bros. CHR bank switching
Value
{
"prgPages": 2,
"chrPages": 1,
"chrKB": 8
}smb1.asm#L4-L10.db "NES", $1a ; identification of the iNES header
…
.db 2 ; number of 16KB PRG-ROM pages
.db 1 ; number of 8KB CHR-ROM pages
…
.db 1 ; mapper/mirroring/whateverChecked by claude, 2026-10-21 — read the header bytes at the pinned commitByte 6 = 1 with byte 7 = 0 is mapper 0 (NROM) with vertical mirroring in the iNES format; the atlas has no pinned iNES reference yet, so the mapper number rests on that reading.
Cited by: Super Mario Bros.
The NMI handler calls the sound engine once a frame; its music handler steps square 2, square 1, the triangle, and the noise channel in turn, and each channel counts its note length down by 1 per frame and reads its next byte at 0. smb1-av/music-loop
About: Super Mario Bros. Music driver
Value
{
"order": [
"square2",
"square1",
"triangle",
"noise"
],
"tick": "frame"
}src/prg.asm#L115JSR SoundEngine ; play soundsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-engine.asm#L691-L698HandleSquare2Music:
DEC Squ2_NoteLenCounter ; decrement square 2 note length
BNE MiscSqu2MusicTasks ; is it time for more data? if not, branch to end tasks
LDY MusicOffset_Square2 ; increment square 2 music offset and fetch data
INC MusicOffset_Square2
LDA (MusicData),y
BEQ EndOfMusicData ; if zero, the data is a null terminator
BPL Squ2NoteHandler ; if non-negative, data is a notesmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-engine.asm#L763-L766HandleSquare1Music:
LDY MusicOffset_Square1 ; is there a nonzero offset here?
BEQ HandleTriangleMusic ; if not, skip ahead to the triangle channel
DEC Squ1_NoteLenCounter ; decrement square 1 note lengthChecked by claude, 2026-10-21 — read MusicHandler and each channel's handler at the pinned commitCited by: Music driver
A song's header picks an 8-entry row of MusicLengthLookupTbl, and a length code of 0-7 indexes it in frames; the overworld theme's row is 54, 3, 9, 6, 18, 27, 36, 12, a quarter note of 18 frames, and the hurry-up version adds 8 to the row offset for 36, 2, 6, 4, 12, 18, 24, 8. smb1-av/note-lengths
About: Super Mario Bros. Music driver
Value
{
"overworld": [
54,
3,
9,
6,
18,
27,
36,
12
],
"hurry": [
36,
2,
6,
4,
12,
18,
24,
8
],
"table": [
5,
10,
20,
40,
80,
30,
60,
2,
4,
8,
16,
32,
64,
24,
48,
12,
3,
6,
12,
24,
48,
18,
36,
8,
54,
3,
9,
6,
18,
27,
36,
12,
36,
2,
6,
4,
12,
18,
24,
8,
18,
1,
3,
2,
6,
9,
12,
4
]
}src/music-data.asm#L34-L39; header format is as follows:
; 1 byte - length byte offsetsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-engine.asm#L662-L666LoadHeader:
LDA MusicHeaderOffsetData,y ; load offset for header
TAY
LDA MusicHeaderData,y ; now load the header
STA NoteLenLookupTblOfssmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/ram.asm#L521NoteLenLookupTblOfs = $f0smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-data.asm#L63-L64GroundLevelPart1Hdr:
.db $18, <GroundM_P1Data, >GroundM_P1Data, $2d, $1c, $b8smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-data.asm#L377-L383MusicLengthLookupTbl:
.db $05, $0a, $14, $28, $50, $1e, $3c, $02
.db $04, $08, $10, $20, $40, $18, $30, $0c
.db $03, $06, $0c, $18, $30, $12, $24, $08
.db $36, $03, $09, $06, $12, $1b, $24, $0c
.db $24, $02, $06, $04, $0c, $12, $18, $08
.db $12, $01, $03, $02, $06, $09, $0c, $04smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-engine.asm#L925-L933ProcessLengthData:
AND #%00000111 ; clear all but the three LSBs
CLC
ADC $f0 ; add offset loaded from first header byte
ADC NoteLengthTblAdder ; add extra if time running out musicsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-engine.asm#L625-L627CMP #TimeRunningOutMusic ; is it time running out music?
BNE FindEventMusicHeader
LDX #$08 ; load offset to be added to length byte of headerChecked by claude, 2026-10-25 — read the header format, the ground headers, the length table, and ProcessLengthData; LoadHeader (L662-L666) stores the header's first byte in NoteLenLookupTblOfs, $f0Every overworld part's header starts with $18. Reading 18 frames as a quarter note gives about 200 beats a minute at 60.1 frames a second; the note-value reading is the atlas's, not the disassembly's.
Cited by: Music driver
A note byte is an offset into FreqRegLookupTbl, a table of 51 two-byte timer periods that the driver writes to the channel's timer registers; a recreation turns period into Hz on a square channel and half that on the triangle. smb1-av/period-table
About: Super Mario Bros. Music driver
Value
[ 136, 47, 0, 678, 640, 604, 570, 538, 479, 452, 427, 403, 380, 359, 339, 320, 302, 285, 269, 254, 239, 226, 213, 201, 190, 179, 169, 160, 151, 142, 134, 119, 126, 113, 84, 100, 95, 89, 80, 71, 67, 59, 53, 42, 35, 1141, 855, 761, 719, 508, 106 ]
src/music-engine.asm#L107-L115Dump_Freq_Regs:
TAY
LDA FreqRegLookupTbl+1,y ; use previous contents of A for sound reg offset
BEQ NoTone ; if zero, then do not load
STA SND_REGISTER+2,x ; first byte goes into LSB of frequency divider
LDA FreqRegLookupTbl,y ; second byte goes into 3 MSB plus extra bit forsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-data.asm#L362-L375FreqRegLookupTbl:
.db $00, $88, $00, $2f, $00, $00
.db $02, $a6, $02, $80, $02, $5c, $02, $3a
…
.db $02, $f9, $02, $cf, $01, $fc, $00, $6aAPU Pulse · Pulse channel output to mixerfpulse = fCPU/(16*(t+1))APU Triangle · leadf = fCPU ÷ (32 × (tval + 1))Checked by claude, 2026-10-21 — read Dump_Freq_Regs and decoded the table's high/low byte pairs at the pinned commitPairs are stored high byte first. Period 254 gives 438.7 Hz on a square channel and 508 gives 219.8 Hz, so the table sits close to A = 440 Hz equal temperament.
Cited by: Music driver
A new area-music square note sets duty and volume byte $82 and an envelope index of 8; each frame the driver writes AreaMusicEnvData[index] to the channel and counts the index down, so the note plays at 50% duty with volumes 0, 8, 7, 6, 5, 5, 4, 4 and then 0 until the next note. smb1-av/square-envelope
About: Super Mario Bros. Music driver
Value
{
"duty": 0.5,
"volumes": [
0,
8,
7,
6,
5,
5,
4,
4,
0
],
"table": [
144,
148,
148,
149,
149,
150,
151,
152
]
}src/music-engine.asm#L934-L952LoadControlRegs:
…
LDA #$08 ; this is the default value for all other music
…
LDX #$82 ; load contents of other sound regs for square 2
LDY #$7fsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-engine.asm#L754-L759LDY Squ2_EnvelopeDataCtrl ; check for contents saved from LoadControlRegs
BEQ NoDecEnv1
DEC Squ2_EnvelopeDataCtrl ; decrement unless already zero
NoDecEnv1:
JSR LoadEnvelopeData ; do a load of envelope data to replace default
STA SND_SQUARE2_REG ; based on offset set by first load unless playingsmb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1) · src/music-data.asm#L388-L392AreaMusicEnvData:
.db $90, $94, $94, $95, $95, $96, $97, $98
…
.db $90, $91, $92, $92, $93, $93, $93, $94Checked by claude, 2026-10-21 — traced Squ2NoteHandler into MiscSqu2MusicTasks and LoadEnvelopeData at the pinned commitOn the note's first frame the index is still 8 when it is read (the DEC is to memory, after LDY), so it reads one byte past the 8-byte table, the first byte of WaterEventMusEnvData, $90: volume 0 for one frame. Bits 7-6 of $9x are duty 2 (50%) and bit 4 is constant volume (nesdev-apu-pulse). Water music uses WaterEventMusEnvData and an index of $28 instead. A Mesen probe of $4004 would confirm the per-frame trace.
Cited by: Music driver
The noise channel plays three drum sounds, all at constant volume 12 ($400C = $1C), a short beat at period index 3, a strong beat at period index 12, and a long beat at period index 3 with a longer length-counter load ($58 instead of $18). smb1-av/noise-beats
About: Super Mario Bros. Music driver
Value
{
"short": [
28,
3,
24
],
"strong": [
28,
12,
24
],
"long": [
28,
3,
88
],
"silent": [
16
]
}src/music-engine.asm#L888-L913LDA #$1c ; short beat data
LDX #$03
LDY #$18
BNE PlayBeat
StrongBeat:
LDA #$1c ; strong beat data
LDX #$0c
LDY #$18
BNE PlayBeat
LongBeat:
LDA #$1c ; long beat data
LDX #$03
…Checked by claude, 2026-10-21 — read NoiseBeatHandler at the pinned commitPeriod indexes 3 and 12 are 32 and 762 CPU cycles on NTSC (nes-hardware/noise). The underground and castle songs skip the noise routine.
Cited by: Music driver
During the overworld and underwater songs the sound engine raises a counter by 1 a frame up to $30 and writes it to the DMC's output level at $4011; other songs count it back down to 0, and the game plays no samples. smb1-av/dac-level
About: Super Mario Bros. DPCM samples
Value
{
"max": 48,
"step": 1
}src/music-engine.asm#L79-L91LDY DAC_Counter ; load some sort of counter
LDA AreaMusicBuffer
AND #%00000011 ; check for specific music
BEQ NoIncDAC
INC DAC_Counter ; increment and check counter
CPY #$30
BCC StrWave ; if not there yet, just store it
NoIncDAC:
TYA
BEQ StrWave ; if we are at zero, do not decrement
DEC DAC_Counter ; decrement counter
StrWave:
…Checked by claude, 2026-10-21 — read the end of SoundEngine at the pinned commitArea music bits 0 and 1 are the ground and water songs (AreaMusicBuffer). The audible effect of the raised level on the other channels is not sourced here; the NESdev APU Mixer page would be the anchor. "No samples": the disassembly never writes the sample address or length registers ($4012, $4013); its only other DMC write resets $4011 at power-on (src/prg.asm line 31).
Cited by: DPCM samples