Super Mario Bros.: The Lost Levels
Nintendo's 1986 sequel to Super Mario Bros. for the Famicom Disk System, released in Japan as Super Mario Bros. 2.
Super Mario Bros.: The Lost Levels is Nintendo’s sequel to Super Mario Bros., released in Japan on 3 June 19861 as Super Mario Bros. 2 for the Famicom Disk System1. The manual presents it as another Mario journey in a parallel world, eight new worlds, with a Mario game and a Luigi game2. It was aimed at players who had mastered the first. Miyamoto recalls a “For Super Players” sticker on the package, and a Nintendo interview’s note calls it a tougher challenge developed for advanced players3. Nintendo counts Nintendo's page counts 52 courses, including those opened by meeting conditions, one and a half times the first game's. It names the poison mushroom and a tailwind that carries the player's body along as new tricks, and says Luigi jumps higher than Mario and slips more.4 courses, including those opened by meeting conditions4.
Miyamoto calls it Takashi Tezuka’s directorial debut, and Tezuka says they made it by changing the first game’s difficulty and stage design5. Miyamoto recalls that the team changed Super Mario’s levels using the VS. System, an arcade system based on the Famicom, and, as they played, made “very challenging levels”6. In North America, the Super Mario Bros. 2 that Nintendo released was a different game, a remake of Yume Kojo: Doki Doki Panic7. Retrospectives say this one was held back from North America for its difficulty, but they disagree on who decided8.
Famicom Tsūshin’s chart had it second in its 1 August 1986 issue, a week after it entered at the top9. Famicom Hisshoubon’s three reviewers gave it 3, 2, and 2 of 510. Nasubi Kamen asked whether it had only got harder and saw a push to sell the Disk System; Tokashiki Minoru wanted it on a cartridge, for friends without the drive; Goinkyo missed the first game’s fresh surprise10.
In North America it came out in August 1993, as The Lost Levels in Super Mario All-Stars for the Super NES, which added a save after each level11,12. Nintendo Power warned that it was “waaayy harder” than the original13. On the Wii’s Virtual Console, GameSpot’s Frank Provo gave the original game 6.5 of 1014 in 2007 and called it a level hack of the first14,15. IGN’s 2010 history says it looked exactly like its predecessor but was much harder, with poison mushrooms, warps that send the player back, and wind16.
#How it plays
Mario runs and jumps exactly as in Super Mario Bros.: the same take-off speeds, gravities, frictions, and top speeds17. As there, a jump can start only from the ground, not after walking off a ledge18, and a hit that shrinks him leaves him blinking and safe for the same few seconds19.
Luigi is the other choice on the title screen. He takes off at Mario’s speeds, but under lighter gravity, so the same jump carries him higher. His weaker friction makes him slippery: he takes longer to speed up, slow down, and turn around20,21. Turning against his motion doesn’t brake him twice as hard the way it does Mario. After a castle’s axe, he walks on with Mario’s physics until the next area20.
#Wind and the poison mushroom
From World 5 on, some outdoor stretches blow the player to the right, with leaves and a sound while the wind lasts. On average the push is a quarter of Mario’s walking speed, in the air as well as on the ground22,23,24. It adds to the player’s own speed, so running into it is slower and running with it faster, at any speed22.
The poison mushroom comes from blocks of its own and slides along like a Super Mushroom. Touching it is a hit: small Mario dies, and a bigger Mario drops straight to small. During the blink after a hit, or under a star’s invincibility, it does nothing25. It is drawn as the mushroom is, in its own colors26.
#Springboards and warps
A springboard pushes the player down and up over four steps, then launches them at 7 px a frame, faster than any jump from the ground27,28. Pressing A fresh while the spring is pressed down raises the launch to 12 px a frame. Green springboards, in worlds 2, 3, and 7 and in B and C, launch at 32 px a frame. The flight up and down is the same whether A is held or not, for both brothers27.
A warp zone sends the player to one world, whichever pipe they enter. Some go back: World 3’s leads to World 1, and the warps of worlds 6 to 8 lead to World 529.
#A harder second half
From 4-4 on, and throughout worlds A to D, every level runs in a hard mode30. Enemies marked for it appear, cannons fire more often, and up to four flying Cheep Cheeps come at once instead of three. Hammer bros throw more often, and Bowser breathes fire sooner. The large lifts are narrower31.
Piranha plants are green in worlds 1 to 3. From World 4 on they turn red: they move twice as fast and come out of their pipes even when the player stands closer32. Once no Lakitu is on screen, a new one flies in after a while; from World 7 on, it flies lower33.
#Game over, World 9, and worlds A to D
At a game over, the player can continue or retry. Continue restarts the current world at its first level, with three lives and the score at zero. Retry ends the game34.
Clearing the castle of every world from 1 to 8, without a retry, opens World 9 after 8-4’s ending. A warp that skips a world shuts it unless the player warps back and clears that world’s castle; a continue keeps the record35. World 9 gives one life and no continue, and its four levels loop36.
Each ending after 8-4 is saved to the disk as a star on the title screen. With eight stars, holding A while pressing Start begins at World A, and worlds A to D follow. Their own ending adds no star37.
The flagpole gives an extra life when the coin count’s two digits match the timer’s last digit38. Fireworks go off over the castle when the timer’s last digit matches the coin count’s: three for an odd digit, six for an even one39.
#Across the series
Against Super Mario Bros., most of the code is the same game’s engine, changed in places; the Code lineage table below lists each stretch. The movement and gravity routines are the same instructions40, and so are Mario’s numbers17. The player’s sprites are the same tiles41, and the songs heard in the levels have the same notes42. The levels are new: one short pipe intro is all the level data the two share43.
What changed for play:
- Two players become two brothers. The menu drops the two-player game and the world select, and Select picks Mario or Luigi44. In Super Mario Bros., Luigi is the second player’s character45; here he has his own physics20.
- The wind and the poison mushroom are new. The wind runs in the player’s movement code and the game’s frame loop46,47, and the poison mushroom comes from its own blocks48.
- A star now blocks a poison mushroom. The injury routine also does nothing while a star lasts49.
- Hard mode starts at 4-4, where Super Mario Bros.’s starts at 5-350. Super Mario Bros.’s other hard mode, which turns Goombas into Buzzy Beetles, never switches on: its checks remain, but nothing sets its flag51.
- Warps lead by world. Super Mario Bros. picks the destination by which pipe the player enters; here the world, level, and area decide, and every pipe leads to the same place49,52.
- The springboard changes. Landing on one now sets the falling gravity too, so holding A no longer matters to the flight49, and the green springs are new53.
- Stomps bounce higher, at one speed for every stomp, and higher still off paratroopas49.
- Lakitu returns after three checks, where Super Mario Bros. waits for seven54. The red piranha plants are new55, and so are upside-down ones56.
- Game over offers a continue, where Super Mario Bros.’s game over ends the game50.
- The flagpole and the fireworks follow the coins. The flagpole life is new53. Super Mario Bros. sets off fireworks when the timer ends in 1, 3, or 6; here the coins’ last digit decides55.
Cartridge
Code lineage
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Jump | Speed from the run | Super Mario Bros.'s jumps: the faster the run-up, the higher the jump |
| Gravity | Switch on release | Super Mario Bros.'s light pull while A is held on the way up, lighter still for Luigi |
| Variable jump height | Gravity switch | Super Mario Bros.'s held jump; Luigi's lighter gravity carries him higher |
| Running | Run button | Super Mario Bros.'s top speeds; Luigi takes longer to speed up, stop, and turn |
| Invincibility frames | Slow timer | Super Mario Bros.'s blink after a hit |
| Wind | Area push | a steady push to the right in some outdoor stretches, on average a quarter of Mario's walking speed |
| Springboard | Timed press | a fresh press of A on the spring launches higher, and green springs launch far higher |
| Difficulty ramp | Level flag | from 4-4 on, and in worlds A to D, extra enemies appear and hammer bros, Bowser, and the cannons get more aggressive |
| Enemy spawning | Generated | a new Lakitu flies in sooner than in Super Mario Bros. once none is on screen |
| Continue | Unlimited | at every game over outside World 9, Continue restarts the world from its first level |
| Extra life | Other condition | grabbing the flagpole gives a life when the coin count's two digits match the timer's last digit |
| Coyote time | None | a jump starts only from the ground, as in Super Mario Bros. |
Every claim about this game, by concept (65)
References
- ^ abThe game came out in Japan on 3 June 1986, as Super Mario Bros. 2 for the Famicom Disk System.PublisherNintendo, Virtual Console SMB 2 · Iwata Asks, Mario 25th · Super Mario Bros. 2 (FDS) manual
How we know
1986年6月3日にディスクシステム用ソフトとして発売されました
Virtual Console page, introduction · archived copy ↗ Super Mario Bros. 2: The second Super Mario action game released for the Family Computer Disk System on June 3, 1986.
footnote 5 · archived copy ↗ To Super Players June 3, 1986
p. 2, "To Super Players" · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^The manual opens with a letter, "To Super Players", that presents the game as another Mario journey in a parallel world, eight new worlds of varied courses. It says the game has a Mario game and a Luigi game, with Luigi leaping more recklessly than Mario, and warns players not to eat the poison mushroom when it appears.ManualSuper Mario Bros. 2 (FDS) manual · Nintendo, Virtual Console SMB 2
How we know
つのマリオの旅(パラレルワールド)を創ってみました。 … この変化に富んだコースの新しい8ワールド … マリオゲームとルイージゲームがあり … ルイージはマリオより無鉄砲に飛び上がります。 … 食べないようにしてください
p. 2, "To Super Players" · archived copy ↗ 本作は『スーパーマリオブラザーズ』のパラレルワールドを舞台にした、マリオとルイージの新たな冒険のお話です。
Virtual Console page, introduction · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^The game was aimed at players who had mastered the first. Miyamoto recalls a sticker on the package that said "For Super Players", and a note to Nintendo's NES Classic interview describes it as a tougher challenge developed for advanced players. Nintendo's page today pitches it at people who played Super Mario Bros. to exhaustion, with a higher difficulty. Iwata, interviewing the All-Stars developers in 2010, described it the same way: he said they made it for advanced players, and Sugiyama agreed.WebNintendo Channel Miyamoto interview (TMK transcript) · Iwata Asks, Super Mario All-Stars · NES Classic developer interview · Nintendo, Famicom SMB 2 pagerevised
How we know
But we thought it would be hard for first time players so we put a sticker on the package that said "For Super Players".
Super Mario Bros.: The Lost Levels · archived copy ↗ Some might not have been fully satisfied by the first Super Mario, so we made The Lost Levels for advanced players.
Iwata · archived copy ↗ It looked and played very similar to the first Super Mario Bros., but with a tougher challenge level developed for advanced players.
footnote 6 · archived copy ↗ 『スーパーマリオブラザーズ』を遊び尽くした人に向けた、前作よりも高い難易度!
introduction · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abNintendo's page counts 52 courses, including those opened by meeting conditions, one and a half times the first game's. It names the poison mushroom and a tailwind that carries the player's body along as new tricks, and says Luigi jumps higher than Mario and slips more.PublisherNintendo, Famicom SMB 2 page
How we know
条件を満たすと遊べるものも含めて全52コース。 前作の1.5倍ものコースが待ち受けている。 … 取るとダメージを受ける「毒キノコ」や体が勝手に流される「追い風」など … ルイージはマリオよりジャンプが高く、滑りやすい。
introduction · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Miyamoto calls the game Takashi Tezuka's directorial debut. Tezuka says they made it by changing the first game's difficulty and stage design, and Miyamoto that work on Super Mario Bros. 3 began after it, in the spring of 1986.InterviewNES Classic developer interview
How we know
had been Tezuka-san's directorial debut. That went well, so we decided to make a completely new game.
Miyamoto · archived copy ↗ We made Super Mario Bros.: The Lost Levels by changing the original game's difficulty and stage design
Tezuka · archived copy ↗ We began after Super Mario Bros.: The Lost Levels in the spring of 1986
Miyamoto · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Miyamoto recalls that the team used the arcade VS. System, based on the Famicom, to make Super Mario, started changing its levels because they could, and, as they played, made "very challenging levels", and that the game gave infinite 1-ups early to prepare players for what came. IGN's 2010 history says its later stages came from Vs. Super Mario Bros.WebNintendo Channel Miyamoto interview (TMK transcript) · IGN, History of Super Mario Bros.revised
How we know
There was an arcade system called the VS system that was based off of the Famicom. We decided to use that to make Super Mario and we started changing the levels because we could. As we played with it we made very challenging levels. We thought it would be fun if the levels got even harder, and we worked on it. … We made it so you could get infinite 1-UPs from the beginning to prepare you for the levels to come
Super Mario Bros.: The Lost Levels · archived copy ↗ Latter stages were cannibalized from "Vs. Super Mario Bros.", a largely redesigned arcade port of the original.
Super Mario Bros. 2 · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^In America, the Super Mario Bros. 2 that Nintendo released was a different game. Koji Kondo says the Japanese Super Mario Bros. 2 continued the first game directly, while in America it was a remake of Yume Kojo: Doki Doki Panic.InterviewIwata Asks, Super Mario All-Starsrevised
How we know
What we called Super Mario Bros. 2 in Japan … was a direct continuation to the original Super Mario Bros., but in America it was a remake of Yume Kojo: Doki Doki Panic.
Kondo · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abRetrospectives say the game was kept from North America for its difficulty. IGN's 2010 history says Nintendo judged it beyond North American players and canceled its release there; GameSpot's 2007 review puts the call on the head of Nintendo of America; Den of Geek's 2024 article names Howard Phillips.WebIGN, History of Super Mario Bros. · GameSpot, The Lost Levels review · Den of Geek, the "real" Super Mario Bros. 2
How we know
Nintendo decided Mario 2's difficulty level exceeded North American skill level. Rather than risk the franchise's popularity, they canceled its stateside release and looked for an alternative.
Super Mario Bros. 2 · archived copy ↗ However, the head honcho of Nintendo of America at the time felt the game would be too challenging for players in the west.
review, first paragraph · archived copy ↗ It was famed Nintendo Power creator Howard Phillips who made the decision to not import the Japanese version of the game for that reason.
I'm Sorry, You're Just Too Darn Difficult · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abFamicom Tsūshin's shop chart in its 1 August 1986 issue placed the game second, behind Makaimura, and its note said the game had appeared at the top only the week before.MagazineFamitsu 4
How we know
今週堂々の1位は『魔界村』だ。
p. 4, Famitsu TOP 30 · archived copy ↗ おやっ、先週トップで登場したばかりなのに、もう1ランクダウン
p. 4, Famitsu TOP 30, no. 2 · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abFamicom Hisshoubon's three reviewers gave it 3, 2, and 2 hearts of 5. Nasubi Kamen asked whether it had only got harder, thought it a cut job, and saw a plain push to sell the Disk System; Tokashiki Minoru, 15, wanted it on a cartridge for friends without a Disk System; Goinkyo no longer felt the first game's fresh surprise and fun, and wondered whether that was because it came across as a pack of bugs and tricks.MagazineFamicom Hisshoubon 6
How we know
ねえ、これただムズかしくなっただけ?他に変化はないの? … 手抜きだと思う … ディスクシステムを買わせようというのもみえみえ … 絶対カセットで出してほしかった。ボクはディスクシステムを持ってるからうれしかったけど、持ってない友だちはかわいそうだ。 … しかし前作マリオの様な、新鮮な驚きや楽しさはもう感じなかった … バグ・裏ワザパックという印象が強いからだろうか。
p. 119, cross review, Super Mario Bros. 2 · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abSuper Mario All-Stars for the Super Famicom collected the game in July 1993, as The Lost Levels, with Super Mario Bros. 1 to 3; it came out in North America in August 1993. Sugiyama, who says he worked on it as an assistant director and designer under another main director, recalls, if he remembers correctly, that Miyamoto proposed the collection; he thinks the idea was to include The Lost Levels to give a game few had played on the Disk System another chance. He says the team added saving, after each level in The Lost Levels, as significant help.InterviewIwata Asks, Super Mario All-Stars · Nintendo, Super NES Games · Nintendo Power 52revised
How we know
Super Mario All-Stars: Released for the Super Famicom system in July 1993. It includes four games: Super Mario Bros., Super Mario Bros. 2, Super Mario Bros. 3 and Super Mario Bros.: The Lost Levels.
footnote 1 · archived copy ↗ Super Mario All-Stars Nintendo August 1993
Super Mario All-Stars · archived copy ↗ Release Date August 1993
p. 100, Pak Watch · archived copy ↗ Someone else was the main director. I lent support as an assistant director and designer. … If I remember correctly, Miyamoto-san brought it up a little while after development of Super Mario Kart … I think the idea was to include the Lost Levels … a game not many had played on the Disk System, thereby giving people another opportunity to play it. … That made it possible to save after each level in The Lost Levels. The intention was to offer some significant help.
Sugiyama · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abThe name The Lost Levels is the one the game carries in English. The earliest use read here is Nintendo Power's September 1993 issue, whose editor explained that the game had been released only in Japan, as Super Mario Bros. 2; who coined the name isn't known.MagazineNintendo Power 52 · Iwata Asks, Mario 25th
How we know
Levels was originally released only in Japan … and was titled Super Mario Bros. 2, while the SMB 2 that you all know is called Super Mario Bros. USA in Japan.
p. 7, Editor's Corner · archived copy ↗ This game is known as Super Mario Bros.: The Lost Levels in the US.
footnote 5 · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Nintendo Power's September 1993 review of Super Mario All-Stars warned that The Lost Levels was "waaayy harder" than the first game, recommended starting with Luigi for his higher jump, and ran a World 9 Challenge: a photo of World 9, reached without warping, by 31 October 1993 earned an iron-on patch.MagazineNintendo Power 52
How we know
This game is waaayy harder than the original SMB.
p. 16 · archived copy ↗ Luigi can jump higher, so we recommend playing with Luigi when you first begin to play the game.
p. 18 · archived copy ↗ you can only reach World 9 of the Lost Levels if you play every single torturous level. Absolutely no warping! … Send us a photo of your accomplishment, and we'll send you this great iron-on patch. … The deadline is October 31, 1993.
p. 22, World 9 Challenge · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abGameSpot's Frank Provo scored the 2007 Wii Virtual Console release 6.5 of 10, calling it a level hack of the first game whose levels are "infinitely more sadistic".WebGameSpot, The Lost Levels review
How we know
Basically just a level hack for the original Super Mario Bros.
The Bad · archived copy ↗ the level layouts aren't as memorable, and they're infinitely more sadistic
review · archived copy ↗ If you want a tough Super Mario Bros., here you go
The Good · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^GameSpot's October 2007 review says the Wii Virtual Console sold the original, unedited Disk System game for 600 Wii points.WebGameSpot, The Lost Levels review
How we know
At 600 Wii points, the original, unedited rendition of Super Mario Bros. 2 is a decent buy
review, last paragraph · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^IGN's 2010 history describes the game as looking exactly like its predecessor but much harder, with poisonous mushrooms, warps that send the player back, and weather that blows Mario off course.WebIGN, History of Super Mario Bros.
How we know
Visually, it looked exactly like its predecessor, but it was harder... much harder. … some mushrooms were poisonous, some warps sent you back instead of forward, and inclement weather regularly kicked Mario off-course in mid-chasm jump.
Super Mario Bros. 2 · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abThe 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.Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1
How we know
4955JumpMForceData: 4956 .byte $20, $20, $1e, $28, $28, $0d, $04 ⋮ 4958FallMForceData: 4959 .byte $70, $70, $60, $90, $90, $0a, $09 ⋮ 4961FrictionData: 4962 .byte $e4, $98, $d0 ⋮ 4964PlayerYSpdData: 4965 .byte $fc, $fc, $fc, $fb, $fb, $fe, $ff ⋮ 4967InitMForceData: 4968 .byte $00, $00, $00, $00, $00, $80, $00 ⋮ 4970MaxLeftXSpdData: 4971 .byte $d8, $e8, $f0 ⋮
sm2main.asm lines 4955–4974 at 9c40114 · upstream ↗ 4858JumpMForceData: 4859 .db $20, $20, $1e, $28, $28, $0d, $04 ⋮ 4861FallMForceData: 4862 .db $70, $70, $60, $90, $90, $0a, $09 ⋮ 4864PlayerYSpdData: 4865 .db $fc, $fc, $fc, $fb, $fb, $fe, $ff ⋮ 4867InitMForceData: 4868 .db $00, $00, $00, $00, $00, $80, $00 ⋮ 4870MaxLeftXSpdData: 4871 .db $d8, $e8, $f0 ⋮ 4873MaxRightXSpdData: 4874 .db $28, $18, $10 ⋮
src/prg.asm lines 4858–4878 at 1cc3c92 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^
ProcJumpingstarts a jump only whilePlayer_Stateis 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:PlayerBGCollisionsets 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 (GameEngineSubroutine0 to 3 and$0B).Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1revisedHow we know
5015ProcJumping: 5016 lda Player_State ;check player state 5017 beq InitJS ;if on the ground, branch 5018 lda SwimmingFlag ;if swimming flag not set, jump to do something else 5019 beq NoJump ;to prevent midair jumping, otherwise continue
sm2main.asm lines 5015–5019 at 9c40114 · upstream ↗ 10936 lda GameEngineSubroutine 10937 cmp #$0b ;if running routine #11 or $0b 10938 beq ExPBGCol ;branch to leave 10939 cmp #$04 10940 bcc ExPBGCol ;if running routines $00-$03 branch to leave 10941 lda #$01 ;load default player state for swimming 10942 ldy SwimmingFlag ;if swimming flag set, 10943 bne SetPSte ;branch ahead to set default state 10944 lda Player_State ;if player in normal state, 10945 beq SetFallS ;branch to set default state for falling 10946 cmp #$03 10947 bne ChkOnScr ;if in any other state besides climbing, skip to next part ⋮
sm2main.asm lines 10936–10949 at 9c40114 · upstream ↗ 11046LandPlyr: jsr ChkForLandJumpSpring ;do sub to check for jumpspring metatiles and deal with it 11047 lda #$f0 11048 and Player_Y_Position ;mask out lower nybble of player's vertical position 11049 sta Player_Y_Position ;and store as new vertical position to land player properly 11050 jsr HandlePipeEntry ;do sub to process potential pipe entry 11051 lda #$00 11052 sta Player_Y_Speed ;initialize vertical speed and fractional 11053 sta Player_Y_MoveForce ;movement force to stop player's vertical movement 11054 sta StompChainCounter ;initialize enemy stomp counter 11055InitSteP: lda #$00 11056 sta Player_State ;set player's state to normal
sm2main.asm lines 11046–11056 at 9c40114 · upstream ↗ 4921ProcJumping: 4922 LDA Player_State ; check player state 4923 BEQ InitJS ; if on the ground, branch 4924 LDA SwimmingFlag ; if swimming flag not set, jump to do something else 4925 BEQ NoJump ; to prevent midair jumping, otherwise continue
src/prg.asm lines 4921–4925 at 1cc3c92 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-26.
- ^A hit on big Mario sets
InjuryTimerto 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.Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1revisedHow we know
10424 lda InjuryTimer ;check again to see if either of the two 10425 ora StarInvincibleTimer ;invincibility timers have expired, branch if not 10426 bne ExInjColRoutines ⋮ 10428ForceInjury: 10429 ldx PlayerStatus ;check player's status 10430 beq KillPlayer ;branch if small 10431 sta PlayerStatus ;otherwise set player's status to small 10432 lda #$08 10433 sta InjuryTimer ;set injured invincibility timer
sm2main.asm lines 10424–10433 at 9c40114 · upstream ↗ 928 ldx #$14 ;set offset to decrement only frame timers 929 dec IntervalTimerControl ;if interval timer control not expired, branch 930 bpl DecrTheTimers ;to skip and thus decrement only frame timers 931 lda #$14 932 sta IntervalTimerControl ;otherwise reset interval timer control to 20 frames 933 ldx #$23 ;and load offset to decrement frame and interval timers
sm2main.asm lines 928–933 at 9c40114 · upstream ↗ 81Timers = $0780 ⋮ 99InjuryTimer = $079e
sm2main.asm lines 81–99 at 9c40114 · upstream ↗ 10583InjurePlayer: 10584 LDA InjuryTimer ; check again to see if injured invincibility timer is 10585 BNE ExInjColRoutines ; at zero, and branch to leave if so ⋮ 10587ForceInjury: 10588 LDX PlayerStatus ; check player's status 10589 BEQ KillPlayer ; branch if small 10590 STA PlayerStatus ; otherwise set player's status to small 10591 LDA #$08 10592 STA InjuryTimer ; set injured invincibility timer
src/prg.asm lines 10583–10592 at 1cc3c92 · upstream ↗ 126DecTimers: 127 LDX #$14 ; load end offset for end of frame timers 128 DEC IntervalTimerControl ; decrement interval timer control, 129 BPL DecTimersLoop ; if not expired, only frame timers will decrement 130 LDA #$14 131 STA IntervalTimerControl ; if control for interval timers expired, 132 LDX #$23 ; interval timers will decrement along with frame timers 133DecTimersLoop: 134 LDA Timers,x ; check current timer 135 BEQ SkipExpTimer ; if current timer expired, branch to skip, 136 DEC Timers,x ; otherwise decrement the current timer 137SkipExpTimer: ⋮
src/prg.asm lines 126–139 at 1cc3c92 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-26.
- ^ abcPicking Luigi copies his own gravities and frictions over Mario's: held gravity
$18–$22for the five jump rows against Mario's$1E–$28, falling gravity$3E–$5Dagainst$60–$90, and friction$B4,$68,$A0against$E4,$98,$D0. It also writes anRTSover theASLthat 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 andASLback for the walk that follows, whoever is playing, until the next area loads the player's own again.Codedoppelganger's SMB2J disassemblyHow we know
14477;jump, fall and friction physics data used with mario and luigi 14478;note luigi's higher jump, but with lesser friction 14479JumpFrictionData: 14480 .byte $20, $20, $1e, $28, $28, $0d, $04 14481 .byte $70, $70, $60, $90, $90, $0a, $09 ⋮ 14483 .byte $e4, $98, $d0 ⋮ 14485 .byte $18, $18, $18, $22, $22, $0d, $04 14486 .byte $42, $42, $3e, $5d, $5d, $0a, $09 ⋮ 14488 .byte $b4, $68, $a0
sm2main.asm lines 14477–14488 at 9c40114 · upstream ↗ 14490LoadPhysicsData: 14491 ldx #$60 ;use luigi's offsets and RTS opcode 14492 ldy #$21 14493 lda SelectedPlayer ;if selected luigi, branch to continue 14494 bne ModifyPhysics 14495LoadMarioPhysics: 14496 ldx #$0e ;otherwise use mario's offsets and ASL opcode 14497 ldy #$10 14498ModifyPhysics: 14499 stx PhyOpcode ;load opcode into friction code to modify it 14500 ldx #$10 14501MPhyLoop: ⋮
sm2main.asm lines 14490–14507 at 9c40114 · upstream ↗ 5124 lda PlayerFacingDir 5125 cmp Player_MovingDir ;check facing direction against moving direction 5126 beq ExitPhy ;if the same, branch to leave 5127PhyOpcode: asl FrictionAdderLow ;otherwise multiply friction by 2 5128 rol FrictionAdderHigh ;then leave (or just leave, if code was modified earlier 5129ExitPhy: rts
sm2main.asm lines 5124–5129 at 9c40114 · upstream ↗ 2605DoneInitArea: lda #Silence ;silence music 2606 sta AreaMusicQueue 2607 lda #$01 ;disable screen output 2608 sta DisableScreenFlag 2609 jsr LoadPhysicsData 2610 inc OperMode_Task ;increment task for this mode 2611 rts
sm2main.asm lines 2605–2611 at 9c40114 · upstream ↗ 11158HandleAxeMetatile: 11159 lda #$00 11160 sta OperMode_Task ;reset secondary mode 11161 lda #$02 11162 sta OperMode ;set primary mode to victory mode 11163 jsr LoadMarioPhysics 11164 lda #$18 11165 sta Player_X_Speed ;set horizontal speed and continue to erase axe metatile
sm2main.asm lines 11158–11165 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-26.
- ^
FrictionDatagives Mario's acceleration and braking per frame:$E4running,$98walking, and$D0walking while still fast from a run, in 1/4096 px/frame.CodeXkeeper0/smb1How we know
4877FrictionData: 4878 .db $e4, $98, $d0
src/prg.asm lines 4877–4878 at 1cc3c92 · upstream ↗ 5037 LDY $00 ; get other value in memory 5038 LDA FrictionData,y ; get value using value in memory as offset 5039 STA FrictionAdderLow 5040 LDA #$00 5041 STA FrictionAdderHigh ; init something here
src/prg.asm lines 5037–5041 at 1cc3c92 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^ abWind 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,FileListNumberis 0 and both calls are skipped.Codedoppelganger's SMB2J disassemblyrevisedHow we know
179BlowPlayerAround: 180 lda WindFlag ;if wind is turned off, just exit 181 beq ExBlow 182 lda AreaType ;don't blow the player around unless 183 cmp #$01 ;the area is ground type 184 bne ExBlow 185 ldy #$01 186 lda FrameCounter ;branch to set d0 if on an odd frame 187 asl 188 bcs BThr ;otherwise wind will only blow 189 ldy #$03 ;one out of every four frames 190BThr: sty $00 ⋮
sm2data2.asm lines 179–202 at 9c40114 · upstream ↗ 322BlowPlayerAround: 323 lda WindFlag ;if wind is turned off, just exit 324 beq ExBlow 325 lda AreaType ;don't blow the player around unless 326 cmp #$01 ;the area is ground type 327 bne ExBlow 328 ldy #$01 329 lda FrameCounter 330 asl 331 bcs BThr 332 ldy #$03 333BThr: sty $00 ⋮
sm2data4.asm lines 322–345 at 9c40114 · upstream ↗ 4848JmpMove: jsr MovePlayerHorizontally ;do another sub to move player horizontally 4849 sta Player_X_Scroll ;set returned value as player's movement speed for scroll 4850 lda FileListNumber ;if in worlds 1-4 don't bother checking for wind 4851 beq ExOGSS 4852 jsr BlowPlayerAround 4853ExOGSS: rts
sm2main.asm lines 4848–4853 at 9c40114 · upstream ↗ 4892JSMove: jsr JmpMovesm2main.asm line 4892 at 9c40114 · upstream ↗ 4830MoveSubs: jsr JumpEngine ⋮ 4832 .word OnGroundStateSub 4833 .word JumpSwimSub 4834 .word FallingSub 4835 .word ClimbingSub
sm2main.asm lines 4830–4835 at 9c40114 · upstream ↗ 4273 lda FileListNumber 4274 beq NoWind ;if in worlds 1-4, skip ahead 4275 jsr SimulateWind ;otherwise, simulate wind where needed
sm2main.asm lines 4273–4275 at 9c40114 · upstream ↗ 217SimulateWind: 218 lda WindFlag ;if no wind, branch to leave 219 beq ExSimW 220 lda #$04 ;play wind sfx 221 sta NoiseSoundQueue
sm2data2.asm lines 217–221 at 9c40114 · upstream ↗ 264WindOn: 265 lda #$01 ;branch to turn the wind on 266 bne WOn 267WindOff: 268 lda #$00 ;turn the wind off 269WOn: sta WindFlag 270 rts
sm2data2.asm lines 264–270 at 9c40114 · upstream ↗ 4236 .word InitializeArea 4237 .word ScreenRoutines 4238 .word SecondaryGameSetup 4239 .word GameCoreRoutine
sm2main.asm lines 4236–4239 at 9c40114 · upstream ↗ 2615SecondaryGameSetup: 2616 lda #$00 2617 sta DisableScreenFlag ;reenable screen, reset some flags 2618 sta WindFlag
sm2main.asm lines 2615–2618 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Mario's top ground speed is
$18walking and$28running (B held).CodeXkeeper0/smb1 · Jdaster64 n.d.How we know
4873MaxRightXSpdData: 4874 .db $28, $18, $10
src/prg.asm lines 4873–4874 at 1cc3c92 · upstream ↗ 5009 AND #B_Button 5010 BNE SetRTmr ; if pressed, skip ahead to set timer ⋮ 5014 INY ; if running timer not set or level type is water,
src/prg.asm lines 5000–5014 at 1cc3c92 · upstream ↗ Maximum walk speed (normal, castle cutscene) 01900 | Maximum running speed 02900
SMB PHYSICS 101 / GROUND PHYSICS, Mario table · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-27.
- ^Horizontal speeds are 4.4 fixed point, whole pixels and sixteenths per frame;
MoveObjectHorizontallyshifts the sixteenths into the subpixel byte.CodeXkeeper0/smb1How we know
6495 ASL ; and move low nybble to high 6496 ASL 6497 ASL ⋮ 6500 LSR ; move high nybble to low 6501 LSR 6502 LSR 6503 LSR
src/prg.asm lines 6492–6503 at 1cc3c92 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-27.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
6308BlockCode: jsr JumpEngine ;run appropriate subroutine depending on block number ⋮ 6310 .word MushFlowerBlock 6311 .word PoisonMushBlock 6312 .word CoinBlock 6313 .word CoinBlock 6314 .word ExtraLifeMushBlock 6315 .word PoisonMushBlock 6316 .word MushFlowerBlock 6317 .word MushFlowerBlock 6318 .word PoisonMushBlock 6319 .word VineBlock 6320 .word StarBlock ⋮
sm2main.asm lines 6308–6339 at 9c40114 · upstream ↗ 6123 lda PowerUpType 6124 cmp #$02 ;check currently loaded power-up type 6125 bcs PutBehind ;if star or 1-up, branch ahead 6126 lda PlayerStatus ;otherwise check player's current status 6127 cmp #$02 6128 bcc StrType ;if player not fiery, use status as power-up type 6129 lsr ;otherwise shift right to force fire flower type 6130StrType: sta PowerUpType ;store type here
sm2main.asm lines 6123–6130 at 9c40114 · upstream ↗ 6148 lda PowerUpType ;check power-up type 6149 beq ShroomM ;if normal mushroom, branch ahead to move it 6150 cmp #$03 6151 beq ShroomM ;if 1-up mushroom, branch ahead to move it 6152 cmp #$04 6153 beq ShroomM 6154 cmp #$05 6155 beq ShroomM
sm2main.asm lines 6148–6155 at 9c40114 · upstream ↗ 6161ShroomM: jsr MoveNormalEnemy ;do sub to make mushrooms movesm2main.asm line 6161 at 9c40114 · upstream ↗ 10253HandlePowerUpCollision: 10254 jsr EraseEnemyObject ;erase the power-up object 10255 lda PowerUpType 10256 cmp #$04 ;check power-up type 10257 bne Safe ;if not a poison shroom, branch 10258 jmp InjurePlayer ;otherwise injure the player properly
sm2main.asm lines 10253–10258 at 9c40114 · upstream ↗ 10423InjurePlayer: 10424 lda InjuryTimer ;check again to see if either of the two 10425 ora StarInvincibleTimer ;invincibility timers have expired, branch if not 10426 bne ExInjColRoutines ⋮ 10428ForceInjury: 10429 ldx PlayerStatus ;check player's status 10430 beq KillPlayer ;branch if small 10431 sta PlayerStatus ;otherwise set player's status to small 10432 lda #$08 10433 sta InjuryTimer ;set injured invincibility timer 10434 asl 10435 sta Square1SoundQueue ;play pipedown/injury sound ⋮
sm2main.asm lines 10423–10436 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassemblyHow we know
12527PowerUpGfxTable: 12528 .byte $d8, $da, $db, $ff ;regular mushroom 12529 .byte $d6, $d6, $d9, $d9 ;fire flower 12530 .byte $8d, $8d, $e4, $e4 ;star 12531 .byte $d8, $da, $db, $ff ;1-up mushroom 12532 .byte $d8, $da, $db, $ff ;poison mushroom ⋮ 12534PowerUpAttributes: 12535 .byte $02, $01, $02, $01, $03
sm2main.asm lines 12527–12535 at 9c40114 · upstream ↗ 12565 ldy Enemy_SprDataOffset+5 ;get sprite data offset again 12566 pla ;pull saved power-up type from the stack 12567 beq PUpOfs ;if regular mushroom, 1-up mushroom 12568 cmp #$03 ;or poison mushroom, branch 12569 beq PUpOfs ;do not change colors or flip them 12570 cmp #$04 12571 beq PUpOfs
sm2main.asm lines 12565–12571 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abLanding on a springboard sets both the held and the falling gravity to
$70and 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.Codedoppelganger's SMB2J disassemblyHow we know
11291ChkForLandJumpSpring: 11292 jsr ChkJumpspringMetatiles ;do sub to check if player landed on jumpspring 11293 bcc ExCJSp ;if carry not set, jumpspring not found, therefore leave 11294 lda #$70 11295 sta VerticalForce ;otherwise set vertical movement force for player 11296 sta VerticalForceDown 11297 lda #$f9 11298 sta JumpspringForce ;set default jumpspring force 11299 lda #$03 11300 sta JumpspringTimer ;set jumpspring timer to be used later 11301 lsr 11302 sta JumpspringAnimCtrl ;set jumpspring animation control to start animating ⋮
sm2main.asm lines 11291–11303 at 9c40114 · upstream ↗ 5574JumpspringHandler: 5575 jsr GetEnemyOffscreenBits ;get offscreen information 5576 lda TimerControl ;check master timer control 5577 bne DrawJSpr ;branch to last section if set 5578 lda JumpspringAnimCtrl ;check jumpspring frame control 5579 beq DrawJSpr ;branch to last section if not set 5580 tay 5581 dey ;subtract one from frame control in A, 5582 tya ;the only way a poor NMOS 6502 can 5583 and #%00000010 ;mask out all but d1, original value still in Y 5584 bne DownJSpr ;if set, branch to move player up 5585 inc Player_Y_Position ⋮
sm2main.asm lines 5574–5620 at 9c40114 · upstream ↗ 12672 lda #$02 12673 ldy WorldNumber ;if the world number is not 2, 3 or 7 12674 cpy #$01 ;then use regular attributes for jumpsprings 12675 beq GrnJS ;which will paint them red 12676 cpy #$02 12677 beq GrnJS ;otherwise use alternate attributes 12678 cpy #$06 ;to get the green superhigh jumpsprings 12679 bne RedJS 12680GrnJS: lsr 12681RedJS: sta EnemyAttributeData+$18 ;set jumpspring gfx attributes in the lookup table 12682 sta EnemyAttributeData+$19 12683 sta EnemyAttributeData+$1a
sm2main.asm lines 12672–12683 at 9c40114 · upstream ↗ 6575MovePlayerVertically: 6576 ldx #$00 ;set X for player offset 6577 lda TimerControl 6578 bne NoJSChk ;if master timer control set, branch ahead 6579 lda JumpspringAnimCtrl ;otherwise check to see if jumpspring is animating 6580 bne ExXMove ;branch to leave if so 6581NoJSChk: lda VerticalForce ;dump vertical force 6582 sta $00 6583 lda #$04 ;set maximum vertical speed here 6584 jmp ImposeGravitySprObj ;then jump to move player vertically
sm2main.asm lines 6575–6584 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^
PlayerYSpdDatastarts a jump at −4 px/frame (rows 0–2) or −5 px/frame (rows 3–4).CodeXkeeper0/smb1 · Jdaster64 n.d.How we know
4864PlayerYSpdData: 4865 .db $fc, $fc, $fc, $fb, $fb, $fe, $ff
src/prg.asm lines 4864–4865 at 1cc3c92 · upstream ↗ 4972 LDA PlayerYSpdData,y 4973 STA Player_Y_Speed
src/prg.asm lines 4972–4973 at 1cc3c92 · upstream ↗ 04000 | 04000 | 05000
JUMPING/VERTICAL PHYSICS, Mario table, Initial Velocity (−Y) · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-27.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
3425ScrollLockObject_Warp: 3426 ldx #$80 ;use base number for warp to world 2 3427 lda HardWorldFlag ;if on worlds A-D, skip ahead to next part 3428 bne WarpWorldsAThruD ;note d7 is set in all entries to prevent zero condition 3429 lda WorldNumber ;from happening in warp zone code elsewhere 3430 bne WarpWorlds2Thru8 ;if not on world 1, branch to handle a different way 3431 ldy AreaType ;check to see if on ground level type 3432 dey ;branch if so to add one to the number 3433 beq W1Warp2 3434 lda AreaAddrsLOffset ;if on first underground level, branch to use base number 3435 beq W1Warp1 3436 inx ;otherwise add two to the number and use it ⋮
sm2main.asm lines 3425–3468 at 9c40114 · upstream ↗ 1689WarpZoneNumbers: 1690 .byte $02, $03, $04, $01, $06, $07, $08, $05, $0b, $0c, $0d
sm2main.asm lines 1689–1690 at 9c40114 · upstream ↗ 11332 lda WarpZoneControl ;check warp zone control 11333 beq ExPipeE ;branch to leave if none found 11334 and #%00001111 ;mask out all but lower nybble 11335 tax ;save as offset, then use to load warp zone destination 11336 lda WarpZoneNumbers,x 11337 ldy HardWorldFlag ;if playing worlds A-D, branch to skip this part 11338 beq SetWDest 11339 sec 11340 sbc #$09 ;otherwise subtract 9 to get correct world number 11341SetWDest: tay 11342 dey ;decrement for use as world number 11343 sty WorldNumber ;store as world number and offset
sm2main.asm lines 11332–11343 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
2557InitializeArea: 2558 ldy #$4b ;clear all memory again, only as far as $074b 2559 jsr InitializeMemory ;this is only necessary in game mode
sm2main.asm lines 2557–2559 at 9c40114 · upstream ↗ 2591 lda HardWorldFlag ;check to see if we're in worlds A-D 2592 bne SetSecHard ;if so, activate the secondary no matter where we're at 2593 lda WorldNumber ;otherwise check world number 2594 cmp #World4 ;if less than 4, do not activate secondary 2595 bcc CheckHalfway 2596 bne SetSecHard ;if not equal to, then world > 4, thus activate 2597 lda LevelNumber ;otherwise, world 4, so check level number 2598 cmp #Level4 ;if not 4, do not set secondary hard mode flag 2599 bcc CheckHalfway 2600SetSecHard: inc SecondaryHardMode ;set secondary hard mode flag for areas 4-4 and beyond
sm2main.asm lines 2591–2600 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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 (
$1Cagainst$30), can take the longer of two jump timers ($37against$20), and in worlds 1 to 6 wait a shorter time before walking left ($50against$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 ($60subtracted 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.Codedoppelganger's SMB2J disassemblyrevisedHow we know
6968 iny 6969 lda (EnemyData),y ;get second byte of object 6970 and #%01000000 ;check to see if hard mode bit is set 6971 beq CheckForEnemyGroup ;if not, branch to check for group enemy objects 6972 lda SecondaryHardMode ;if set, check to see if secondary hard mode flag 6973 beq Inc2B ;is on, and if not, branch to skip this object completely
sm2main.asm lines 6968–6973 at 9c40114 · upstream ↗ 5753CannonBitmasks: 5754 .byte %00001111, %00000111
sm2main.asm lines 5753–5754 at 9c40114 · upstream ↗ 5763 lda PseudoRandomBitReg+1,x ;otherwise get part of LSFR 5764 ldy SecondaryHardMode ;get secondary hard mode flag, use as offset 5765 and CannonBitmasks,y ;mask out bits of LSFR as decided by flag 5766 cmp #$06 ;check to see if lower nybble is above certain value 5767 bcs Chk_BB ;if so, branch to check enemy
sm2main.asm lines 5763–5767 at 9c40114 · upstream ↗ 7408 ldy #$03 ;load Y with default value 7409 lda SecondaryHardMode 7410 beq MaxCC ;if secondary hard mode flag not set, do not increment Y 7411 iny ;otherwise, increment Y to allow as many as four onscreen 7412MaxCC: sty $00 ;store whatever pseudorandom bits are in Y 7413 cpx $00 ;compare enemy object buffer offset with Y 7414 bcs ChpChpEx ;if X => Y, branch to leave
sm2main.asm lines 7408–7414 at 9c40114 · upstream ↗ 8207HammerThrowTmrData: 8208 .byte $30, $1c
sm2main.asm lines 8207–8208 at 9c40114 · upstream ↗ 8227 lda HammerThrowingTimer,x ;check hammer throwing timer 8228 bne DecHT ;if not expired, skip ahead, do not throw hammer 8229 ldy SecondaryHardMode ;otherwise get secondary hard mode flag 8230 lda HammerThrowTmrData,y ;get timer data using flag as offset 8231 sta HammerThrowingTimer,x ;set as new timer
sm2main.asm lines 8227–8231 at 9c40114 · upstream ↗ 8241HammerBroJumpLData: 8242 .byte $20, $37
sm2main.asm lines 8241–8242 at 9c40114 · upstream ↗ 8267 lda $00 ;load preset value here to use as bitmask 8268 and PseudoRandomBitReg+2,x ;and do bit-wise comparison with part of LSFR 8269 tay ;then use as offset 8270 lda SecondaryHardMode ;check secondary hard mode flag 8271 bne HJump 8272 tay ;if secondary hard mode flag clear, set offset to 0 8273HJump: lda HammerBroJumpLData,y ;get jump length timer data using offset from before
sm2main.asm lines 8267–8273 at 9c40114 · upstream ↗ 7166HBroWalkingTimerData: 7167 .byte $80, $50
sm2main.asm lines 7166–7167 at 9c40114 · upstream ↗ 7173 lda WorldNumber ;if on worlds 7-9, branch to skip the walk delay 7174 cmp #World7 7175 bcs NoHBI 7176 ldy SecondaryHardMode ;get secondary hard mode flag 7177 lda HBroWalkingTimerData,y 7178 sta EnemyIntervalTimer,x ;set value as delay for hammer bro to walk left
sm2main.asm lines 7173–7178 at 9c40114 · upstream ↗ 8469BlooberBitmasks: 8470 .byte %00111111, %00000011
sm2main.asm lines 8469–8470 at 9c40114 · upstream ↗ 8476 ldy SecondaryHardMode ;use secondary hard mode flag as offset 8477 lda PseudoRandomBitReg+1,x ;get LSFR 8478 and BlooberBitmasks,y ;mask out bits in LSFR using bitmask loaded with offset 8479 bne BlooberSwim ;if any bits set, skip ahead to make swim 8480 txa 8481 lsr ;check to see if on second or fourth slot (1 or 3) 8482 bcc FBLeft ;if not, branch to figure out moving direction 8483 ldy Player_MovingDir ;otherwise, load player's moving direction and 8484 bcs SBMDir ;do an unconditional branch to set 8485FBLeft: ldy #$02 ;set left moving direction by default 8486 jsr PlayerEnemyDiff ;get horizontal difference between player and bloober 8487 bpl SBMDir ;if enemy to the right of player, keep left ⋮
sm2main.asm lines 8476–8488 at 9c40114 · upstream ↗ 9257 jsr SetFlameTimer ;get timing for bowser's flame 9258 ldy SecondaryHardMode 9259 beq SetFBTmr ;if secondary hard mode flag not set, skip this 9260 sec 9261 sbc #$10 ;otherwise subtract from value in A 9262SetFBTmr: sta BowserFireBreathTimer ;set value as timer here
sm2main.asm lines 9257–9262 at 9c40114 · upstream ↗ 7554 jsr SetFlameTimer ;get timer data based on flame counter 7555 clc 7556 adc #$20 ;add 32 frames by default 7557 ldy SecondaryHardMode 7558 beq SetFrT ;if secondary mode flag not set, use as timer setting 7559 sec 7560 sbc #$10 ;otherwise subtract 16 frames for secondary hard mode 7561SetFrT: sta FrenzyEnemyTimer ;set timer accordingly
sm2main.asm lines 7554–7561 at 9c40114 · upstream ↗ 9331 lda #$40 ;load default movement force 9332 ldy SecondaryHardMode 9333 beq SFlmX ;if secondary hard mode flag not set, use default 9334 lda #$60 ;otherwise load alternate movement force to go faster 9335SFlmX: sta $00 ;store value here
sm2main.asm lines 9331–9335 at 9c40114 · upstream ↗ 12395 ldy AreaType 12396 cpy #$03 ;check for castle-type level 12397 beq ShrinkPlatform 12398 ldy SecondaryHardMode ;check for secondary hard mode flag set 12399 beq SetLast2Platform ;branch if not set elsewhere ⋮ 12401ShrinkPlatform: 12402 lda #$f8 ;load offscreen coordinate if flag set or castle-type level
sm2main.asm lines 12395–12402 at 9c40114 · upstream ↗ 7935SPBBox: lda #$05 ;set default bounding box size control 7936 ldy AreaType 7937 cpy #$03 ;check for castle-type level 7938 beq CasPBB ;use default value if found 7939 ldy SecondaryHardMode ;otherwise check for secondary hard mode flag 7940 bne CasPBB ;if set, use default value 7941 lda #$06 ;use alternate value if not castle or secondary not set 7942CasPBB: sta Enemy_BoundBoxCtrl,x ;set bounding box size control here and leave
sm2main.asm lines 7935–7942 at 9c40114 · upstream ↗ 7885 ldy SecondaryHardMode ;if secondary hard mode flag not set, 7886 bne AlignP ;branch ahead 7887 ldy #$02 ;otherwise set value here 7888 jsr PosPlatform ;do a sub to add or subtract pixels
sm2main.asm lines 7885–7888 at 9c40114 · upstream ↗ 7985PlatPosDataLow: 7986 .byte $08,$0c,$f8
sm2main.asm lines 7985–7986 at 9c40114 · upstream ↗ 9813SetupPlatformRope: 9814 pha ;save second/third copy to stack 9815 lda Enemy_X_Position,y ;get horizontal coordinate 9816 clc 9817 adc #$08 ;add eight pixels 9818 ldx SecondaryHardMode ;if secondary hard mode flag set, 9819 bne GetLRp ;use coordinate as-is 9820 clc 9821 adc #$10 ;otherwise add sixteen more pixels
sm2main.asm lines 9813–9821 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
7805InitPiranhaPlant: 7806 lda #$22 ;set default attribute and range data here 7807 sta $00 ;range data is used to detect how close player is 7808 lda #$13 ;to the piranha plant to keep it inside the pipe 7809 sta $01 ;default data makes red piranha plants 7810 lda HardWorldFlag 7811 bne PatchPP ;use default data if in worlds A-D 7812 lda WorldNumber 7813 cmp #$03 7814 bcs PatchPP ;use default data if in worlds 4-8 7815 dec $00 7816 lda #$21 ;otherwise make green piranha plant instead ⋮
sm2main.asm lines 7805–7821 at 9c40114 · upstream ↗ 9593ChkPlayerNearPipe: 9594 lda $00 ;get saved horizontal difference 9595 cmp #$21 9596 bcc PutinPipe ;if player within a certain distance, branch to leave
sm2main.asm lines 9593–9596 at 9c40114 · upstream ↗ 9612RiseFallPiranhaPlant: 9613 sta $00 ;save vertical coordinate here 9614 lda EnemyAttributeData+PiranhaPlant 9615 cmp #$22 ;check for red piranha plants 9616 beq RedPP ;if found, skip to next part to execute code on every frame 9617 lda FrameCounter ;get frame counter 9618 lsr 9619 bcc PutinPipe ;branch to leave if d0 set (execute code every other frame)
sm2main.asm lines 9612–9619 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassemblyHow we know
7251LakituAndSpinyHandler: 7252 lda FrenzyEnemyTimer ;if timer here not expired, leave 7253 bne ExLSHand 7254 cpx #$05 ;if we are on the special use slot, leave 7255 bcs ExLSHand 7256 lda #$80 ;set timer 7257 sta FrenzyEnemyTimer 7258 ldy #$04 ;start with the last enemy slot 7259ChkLak: lda Enemy_ID,y ;check all enemy slots to see 7260 cmp #Lakitu ;if lakitu is on one of them 7261 beq CreateSpiny ;if so, branch out of this loop 7262 dey ;otherwise check another slot ⋮
sm2main.asm lines 7251–7288 at 9c40114 · upstream ↗ 7234SetupLakitu: 7235 lda #$00 ;erase counter for lakitu's reappearance 7236 sta LakituReappearTimer
sm2main.asm lines 7234–7236 at 9c40114 · upstream ↗ 928 ldx #$14 ;set offset to decrement only frame timers 929 dec IntervalTimerControl ;if interval timer control not expired, branch 930 bpl DecrTheTimers ;to skip and thus decrement only frame timers 931 lda #$14 932 sta IntervalTimerControl ;otherwise reset interval timer control to 20 frames 933 ldx #$23 ;and load offset to decrement frame and interval timers
sm2main.asm lines 928–933 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
14431GameOverMenu: 14432 lda SavedJoypadBits ;if player pressed the start button 14433 and #Start_Button ;then either continue or start over 14434 bne ContinueOrRetry 14435 lda SavedJoypadBits 14436 and #Select_Button ;if player pressed the select button 14437 beq ChgSel ;then branch to select "continue" or "retry" 14438 ldx SelectTimer ;if select timer not expired while 14439 bne ChgSel ;pressing select, skip this 14440 lsr 14441 sta SelectTimer ;otherwise set the select timer 14442 lda ContinueMenuSelect ⋮
sm2main.asm lines 14431–14444 at 9c40114 · upstream ↗ 14455ContinueOrRetry: 14456 lda ContinueMenuSelect ;if player selected "continue" 14457 beq Continue ;then branch to continue 14458 lda #$00 14459 sta CompletedWorlds ;otherwise init completed worlds flags 14460 jmp TerminateGame ;and end the game ⋮ 14462Continue: 14463 ldy #$02 14464 sty NumberofLives ;give three lives 14465 sta LevelNumber 14466 sta AreaNumber ;put at x-1 of the current world 14467 sta CoinTally ⋮
sm2main.asm lines 14455–14473 at 9c40114 · upstream ↗ 2869ContinueGame: 2870 jsr LoadAreaPointer ;update level pointer with 2871 lda #$01 ;actual world and area numbers, then 2872 sta PlayerSize ;reset player's size, status, and 2873 inc FetchNewGameTimerFlag ;set game timer flag to reload 2874 lda #$00 ;game timer from header 2875 sta TimerControl ;also set flag for timers to count again 2876 sta PlayerStatus 2877 sta GameEngineSubroutine ;reset task for game core 2878 sta OperMode_Task ;set modes and leave 2879 lda #$01 ;if in game over mode, switch back to 2880 sta OperMode ;game mode, because game is still on ⋮
sm2main.asm lines 2869–2881 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
1185WorldBits: 1186 .byte $01, $02, $04, $08, $10, $20, $40, $80 ⋮ 1188SetupVictoryMode: 1189 ldx ScreenRight_PageLoc ;get page location of right side of screen 1190 inx ;increment to next page 1191 stx DestinationPageLoc 1192 ldy WorldNumber 1193 lda WorldBits,y 1194 ora CompletedWorlds ;set bit according to the world the player was in 1195 sta CompletedWorlds
sm2main.asm lines 1185–1195 at 9c40114 · upstream ↗ 428BackToNormal: 429 lda #>SoundEngine ;reset sound engine vector 430 sta SoundEngineJSRCode+2 ;to run the original one 431 lda #<SoundEngine 432 sta SoundEngineJSRCode+1 433 lda #$00 434 sta DiskIOTask ;erase task numbers 435 sta OperMode_Task 436 lda HardWorldFlag ;if in world D, branch to end the game 437 bne EndTheGame 438 lda CompletedWorlds ;if completed all worlds without skipping over any 439 cmp #$ff ;then branch elsewhere (note warping backwards may ⋮
sm2data3.asm lines 428–451 at 9c40114 · upstream ↗ 14692 lda SavedJoypadBits ;check to see if the player pressed start 14693 and #Start_Button 14694 beq ChkSelect ;if not, branch to check other buttons 14695 lda #$00 14696 sta CompletedWorlds 14697 sta DiskIOTask 14698 sta HardWorldFlag
sm2main.asm lines 14692–14698 at 9c40114 · upstream ↗ 14455ContinueOrRetry: 14456 lda ContinueMenuSelect ;if player selected "continue" 14457 beq Continue ;then branch to continue 14458 lda #$00 14459 sta CompletedWorlds ;otherwise init completed worlds flags 14460 jmp TerminateGame ;and end the game
sm2main.asm lines 14455–14460 at 9c40114 · upstream ↗ 14462Continue: 14463 ldy #$02 14464 sty NumberofLives ;give three lives 14465 sta LevelNumber 14466 sta AreaNumber ;put at x-1 of the current world 14467 sta CoinTally 14468 ldy #$0b 14469ISCont: sta ScoreAndCoinDisplay,y ;reset score 14470 dey 14471 bpl ISCont 14472 inc Hidden1UpFlag ;allow 1-up to be found again 14473 jmp ContinueGame
sm2main.asm lines 14462–14473 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
446GoToWorld9: 447 lda #$00 448 sta CompletedWorlds ;init completed worlds flag 449 sta NumberofLives ;give the player one life 450 sta FantasyW9MsgFlag 451 jmp NextWorld ;run world 9
sm2data3.asm lines 446–451 at 9c40114 · upstream ↗ 2850RunGameOver: 2851 lda #$00 2852 sta DisableScreenFlag 2853 lda WorldNumber ;if on world 9, branch on to end the game 2854 cmp #World9 2855 beq W9End 2856 jmp GameOverMenu ;otherwise run game over menu 2857W9End: lda ScreenTimer 2858 bne ExRGO ⋮ 2860TerminateGame: 2861 lda #Silence ;silence music 2862 sta EventMusicQueue ⋮
sm2main.asm lines 2850–2867 at 9c40114 · upstream ↗ 4793NextArea: inc AreaNumber ;increment area number used for address loader 4794 lda WorldNumber 4795 cmp #$08 4796 bne NotW9 ;if not at end of world 9-4, branch 4797 lda LevelNumber ;otherwise reset level and area numbers properly 4798 cmp #$04 4799 bne NotW9 4800 lda #$00 4801 sta LevelNumber 4802 sta AreaNumber 4803NotW9: jsr LoadAreaPointer ;get new level pointer
sm2main.asm lines 4793–4803 at 9c40114 · upstream ↗ 1303NextWorld: lda #$00 1304 sta AreaNumber ;reset area/level numbers to start the next world 1305 sta LevelNumber 1306 sta OperMode_Task 1307 lda WorldNumber 1308 clc 1309 adc #$01 ;add one, but only up to world 9 1310 cmp #World9 1311 bcc NoPast9 1312 lda #World9 ;make world 9 loop forever (or until game is over) 1313NoPast9: sta WorldNumber ;update the world number
sm2main.asm lines 1303–1313 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
14261LoadEnding: 14262 lda #$02 ;set filelist number to load SM2DATA3, SM2CHAR2 and SM2SAVE 14263 sta FileListNumber 14264 jsr LoadFiles ;load them now 14265 bne ReturnedError 14266 jsr CheckFileCount ;check to see if loaded the correct number of files 14267 beq AddBeatenGame ;if so, continue 14268 lda #$00 ;otherwise reset count of games beaten 14269 sta GamesBeatenCount 14270AddBeatenGame: 14271 lda GamesBeatenCount ;get the new count of games beaten we loaded (or reset) 14272 clc ;add one to it, to a maximum of 24/$18 ⋮
sm2main.asm lines 14261–14280 at 9c40114 · upstream ↗ 391RunMushroomRetainers: 392 jsr MushroomRetainersForW8 ;draw and flash the seven mushroom retainers 393 lda AltMusicBuffer ;if still playing victory music, branch to leave 394 bne ExRMR 395 lda HardWorldFlag ;if on world D, branch elsewhere 396 bne BackToNormal 397 inc OperMode_Task ;otherwise just move onto the last task 398ExRMR: rts ⋮ 400EndingDiskRoutines: 401 lda DiskIOTask 402 jsr JumpEngine ⋮ 404 .word DiskScreen ⋮
sm2data3.asm lines 391–426 at 9c40114 · upstream ↗ 14180LoadWorlds1Thru4: 14181 lda NotColdFlag ;if not set, just cold booted, thus no need to check world info 14182 beq InitWorldPos 14183 lda HardWorldFlag ;if player was playing worlds A-D, skip ahead 14184 bne LW14Files ;otherwise check the world number 14185 lda WorldNumber 14186 cmp #World5 ;if world number was less than 5, files still in memory 14187 bcc InitWorldPos ;thus skip to the end, no need to load them again 14188LW14Files: lda #$00 ;set filelist number to reload SM2MAIN, SM2CHAR1 and SM2SAVE 14189 sta FileListNumber 14190 jsr LoadFiles ;now reload those files 14191 bne ReturnedError ;if returned with error number, branch ⋮
sm2main.asm lines 14180–14193 at 9c40114 · upstream ↗ 14301World14List: 14302 .byte $01, $05, $0f, $ff 14303World58List: 14304 .byte $20, $ff 14305EndingList: 14306 .byte $10, $30, $0f, $ff 14307WorldADList: 14308 .byte $40, $ff
sm2main.asm lines 14301–14308 at 9c40114 · upstream ↗ 14832 ldy #$33 ;set up offset in the title screen tiles 14833 lda #$0c ;set up counter to print up to 12 stars per row 14834 sta $00 14835 ldx #$00 ;init star counter 14836PrintStars: lda #$26 ;print blank by default 14837 cpx GamesBeatenCount ;check star counter against games beaten 14838 bcs PrintToTS ;if counted up to games beaten, print the blank 14839 lda #$f1 ;otherwise print a star for a beaten game 14840PrintToTS: sta TitleScreenGfxData,y ;print to title screen 14841 iny 14842 dec $00 ;decrement until done printing a row 14843 bne NextStarR ⋮
sm2main.asm lines 14832–14847 at 9c40114 · upstream ↗ 14692 lda SavedJoypadBits ;check to see if the player pressed start 14693 and #Start_Button 14694 beq ChkSelect ;if not, branch to check other buttons 14695 lda #$00 14696 sta CompletedWorlds 14697 sta DiskIOTask 14698 sta HardWorldFlag 14699 lda GamesBeatenCount ;check to see if player has beaten 14700 cmp #$08 ;the game at least 8 times 14701 bcc StG ;if not, start the game as usual at world 1 14702 lda SavedJoypadBits 14703 and #A_Button ;check if the player pressed A + start ⋮
sm2main.asm lines 14692–14706 at 9c40114 · upstream ↗ 14147LoadHardWorlds: 14148 lda HardWorldFlag ;if this is not set, skip this 14149 beq NoLoadHW 14150 lda #$03 14151 sta FileListNumber ;set filelist number to load SM2DATA4 14152 jsr LoadFiles 14153 bne ReturnedError ;if returned with error number, branch 14154 jsr CheckFileCount ;check to see if loaded the correct number of files 14155 bne BadFileCount ;if not, branch to set error code 14156NoLoadHW: 14157 jsr LoadAreaPointer ;update area pointer to get world 1 or A 14158 lda HardWorldFlag ;if this is not set, skip this ⋮
sm2main.asm lines 14147–14160 at 9c40114 · upstream ↗ 1196 lda HardWorldFlag ;if not playing worlds A-D, branch to skip this 1197 beq W1Thru8 1198 lda WorldNumber ;otherwise, if not on world D, branch to skip this 1199 cmp #World4 ;(note worlds A-D use values 0-3 in this variable) 1200 bcc W1Thru8 1201 lda #World8 ;if on world D, set world number to 8 to satisfy 1202 sta WorldNumber ;end of game condition in later victory mode subs
sm2main.asm lines 1196–1202 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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).Codedoppelganger's SMB2J disassembly
How we know
11219ChkFlagpoleYPosLoop: 11220 cmp FlagpoleYPosData,x ;compare with current vertical coordinate data 11221 bcs MtchF ;if player's => current, branch to use current offset 11222 dex ;otherwise decrement offset to use 11223 bne ChkFlagpoleYPosLoop ;do this until all data is checked (use last one if all checked) 11224MtchF: stx FlagpoleScore ;store offset here to be used later 11225 lda CoinDisplay 11226 cmp CoinDisplay+1 ;check to see if coin tally digits are the same 11227 bne RunFR ;if not, branch to use flagpole score data as-is 11228 cmp GameTimerDisplay+2 ;check to see if the last digit of game timer matches 11229 bne RunFR ;the two digits, if not, branch to use data as-is 11230 lda #$05 ⋮
sm2main.asm lines 11219–11231 at 9c40114 · upstream ↗ 5551GiveFPScr: ldy FlagpoleScore ;get score offset from earlier (when player touched flagpole) 5552 cpy #$05 5553 bne NoEL4F ;if set to give player an extra life, do so now 5554 inc NumberofLives 5555 lda #$40 5556 sta $fe 5557 jmp NoSc4F 5558NoEL4F: lda FlagpoleScoreMods,y ;get amount to award player points 5559 ldx FlagpoleScoreDigits,y ;get digit with which to award points
sm2main.asm lines 5551–5559 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^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.Codedoppelganger's SMB2J disassembly
How we know
9470GameTimerFireworks: 9471 lda GameTimerDisplay+2 ;check to see if last digit of timer matches 9472 cmp CoinDisplay+1 ;the last digit in the coin tally 9473 bne NoFWks ;if not, skip the fireworks 9474 and #$01 9475 beq EvenDgs ;if so, check to see if they are both odd or even 9476 ldy #$03 9477 lda #$03 ;if they are both odd, set state and counter 9478 bne SetFWC ;for 3 fireworks to go off 9479EvenDgs: ldy #$00 ;if they are both even, set state and counter 9480 lda #$06 ;for 6 fireworks to go off 9481 bne SetFWC ⋮
sm2main.asm lines 9470–9485 at 9c40114 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^The movement and gravity routines (
MoveObjectHorizontallythroughImposeGravity) are Super Mario Bros.'s, instruction for instruction:ImposeGravityandMoveDropPlatformare byte for byte the same, and the other seven differ only in the addresses of their calls and jumps.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
6487MovePlayerHorizontally:src/prg.asm lines 6482–6722 at 1cc3c92 · upstream ↗ 6525MovePlayerHorizontally:sm2main.asm lines 6520–6748 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $BF02-$C046. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $8AD3-$8C17. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^The player's 80 sprite tiles,
$00-$4Fof the sprite pattern table, are Super Mario Bros.'s byte for byte.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
33.incbin "smb1.chr"smb1.asm line 33 at 1cc3c92 · upstream ↗ 61.incbin "SM2CHAR1.CHR"fdswrap.asm line 61 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): CHR $0000-$04FF. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2CHAR1 CHR $0000-$04FF. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^1,299 bytes of The Lost Levels' music are Super Mario Bros.'s byte for byte: the last bytes of the music headers and the notes of every song SM2MAIN plays, from the star music (
Star_CloudMData) through the ground music's parts, death, castle, game over, time running out, level end, underground, water and the castle-clear music (EndOfCastleMusData).CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
125GroundM_P1Data:src/music-data.asm lines 114–347 at 1cc3c92 · upstream ↗ 16352GroundM_P1Data:sm2main.asm lines 16341–16574 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $F9B5-$FEC7. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $D9EC-$DEFE. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^
LoadAreaPointer,FindAreaPointerandGetAreaDataAddrsare Super Mario Bros.'s, with the area data address tables made tables of words (indexed with an ASL) where Super Mario Bros. keeps separate low and high byte tables; the tables themselves hold The Lost Levels' worlds, andAreaDataOfsLoopbackits 12 loop points. Of the level data, only the 9-byte pipe intro area (L_GroundArea10) is byte for byte Super Mario Bros.'s.Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1 · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
14565 aslsm2main.asm line 14565 at 9c40114 · upstream ↗ lda EnemyDataAddrs+1,y
sm2main.asm#L14567-L14567 · upstream ↗ 3948 .db $12, $36, $0e, $0e, $0e, $32, $32, $32, $0a, $26, $40src/prg.asm line 3948 at 1cc3c92 · upstream ↗ 14526L_GroundArea10:sm2main.asm line 14526 at 9c40114 · upstream ↗ 247L_GroundArea10:src/levels/objects.asm line 247 at 1cc3c92 · upstream ↗ 3952LoadAreaPointer:src/prg.asm lines 3948–4054 at 1cc3c92 · upstream ↗ 9AreaAddrOffsets:src/levels/levels.asm lines 4–71 at 1cc3c92 · upstream ↗ GetAreaType: and #%01100000
sm2main.asm#L14536-L14686 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $9BF8-$9D6F. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $C2A4-$C46B. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^
GameMenuRoutineandDemoEngineare Super Mario Bros.'s menu and demo, rewritten in part: the menu drops the one- or two-player choice and the world select, and starts the game at world 1, or at world A when A is held and the game has been beaten 8 times; Select switches between Mario and Luigi; the demo plays new button presses and durations through the same code.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
CMP #A_Button+Start_Button
src/prg.asm#L336-L337 · upstream ↗ LDX WorldSelectEnableFlag
src/prg.asm#L350-L350 · upstream ↗ inc HardWorldFlag
sm2main.asm#L14699-L14705 · upstream ↗ 14729 sta SelectedPlayersm2main.asm lines 14727–14729 at 9c40114 · upstream ↗ 460 .db $01, $80, $02, $81, $41, $80, $01src/prg.asm line 460 at 1cc3c92 · upstream ↗ 14783 .byte $01, $81, $01, $81, $01, $81, $02, $01sm2main.asm line 14783 at 9c40114 · upstream ↗ 330GameMenuRoutine:src/prg.asm lines 328–485 at 1cc3c92 · upstream ↗ 14706StG: jmp StartGamesm2main.asm lines 14692–14806 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $823F-$831C; $8340-$838A. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $C46E-$C572. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^A second player plays Luigi on controller 2, taking turns with the first.ManualSuper Mario Bros. instruction booklet · Computer Entertainer, June 1986
How we know
Controller 2 — for second player in 2 player game … moves Mario (Luigi
2. Names of Controller Parts · archived copy ↗ (Solo or 2-player alternating; Pause.)
review of Super Mario Bros. · archived copy ↗ The quote matches our archived copy.
Checked by a second AI model, 2026-10-26.
- ^The player's control and movement code (
GameRoutinesthroughImposeFriction) is Super Mario Bros.'s, instruction for instruction in 14 of its 19 regions, with these changes:OnGroundStateSuband the jump and swim code move the player through a newJmpMove, which lets the wind push them (BlowPlayerAround) once a disk file beyond worlds 1 to 4 is loaded;PlayerPhysicsSub's friction table moves up to follow the fall forces, inside the 17 bytes thatLoadPhysicsDataoverwrites with Mario's or Luigi's set;PlayerEndLevelqueues the level-end music once, guarded by a newFlagpoleMusicFlag, where Super Mario Bros. queues it wheneverScrollLockis set, and sets the hidden 1-up's flag on 10 coins in any world, where Super Mario Bros. reads a per-world table, and starts world 9 over after 9-4;PlayerDeathshares one RTS. The entrances, pipes, size change, injury blink, flagpole slide, falling, climbing, animation speed andImposeFrictiondiffer only in their addresses.Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1 · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”revisedHow we know
4852 jsr BlowPlayerAroundsm2main.asm lines 4848–4852 at 9c40114 · upstream ↗ JSR MovePlayerHorizontally
src/prg.asm#L4788-L4789 · upstream ↗ 4892JSMove: jsr JmpMovesm2main.asm line 4892 at 9c40114 · upstream ↗ 4878 .db $e4, $98, $d0src/prg.asm line 4878 at 1cc3c92 · upstream ↗ 4962 .byte $e4, $98, $d0sm2main.asm line 4962 at 9c40114 · upstream ↗ CMP Hidden1UpCoinAmts,y
src/prg.asm#L4686-L4686 · upstream ↗ inc FlagpoleMusicFlag
sm2main.asm#L4768-L4772 · upstream ↗ LDA ScrollLock
src/prg.asm#L4660-L4663 · upstream ↗ cmp #$0a
sm2main.asm#L4790-L4790 · upstream ↗ 4795 cmp #$08sm2main.asm lines 4794–4802 at 9c40114 · upstream ↗ 14503 sta JumpMForceData,xsm2main.asm lines 14490–14504 at 9c40114 · upstream ↗ 4286PlayerEntrance:src/prg.asm lines 4267–5135 at 1cc3c92 · upstream ↗ 4428PlayerEntrance:sm2main.asm lines 4411–5207 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $B04A-$B623. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $7BA2-$818F. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^
GameCoreRoutineandScrollHandlerare Super Mario Bros.'s with three changes: the game mode's task list gainsGameModeDiskRoutinesat its head and the core no longer reads the current player's saved joypad bits; each frame of play callsSimulateWindonce a disk file beyond worlds 1 to 4 is loaded;ScrollHandlerwaits for the status bar's timer IRQ before it scrolls and keeps the name table inNameTableSelect, where Super Mario Bros. sets it in its PPU control mirror.GetScreenPositionis byte for byte the same.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
LDA SavedJoypadBits,x
src/prg.asm#L4081-L4083 · upstream ↗ 4235 .word GameModeDiskRoutinessm2main.asm line 4235 at 9c40114 · upstream ↗ jsr SimulateWind
sm2main.asm#L4275-L4275 · upstream ↗ 4341 lda IRQAckFlagsm2main.asm line 4341 at 9c40114 · upstream ↗ LDA Mirror_PPU_CTRL_REG1
src/prg.asm#L4206-L4209 · upstream ↗ sta NameTableSelect
sm2main.asm#L4357-L4357 · upstream ↗ 4090GameEngine:src/prg.asm lines 4081–4262 at 1cc3c92 · upstream ↗ 4241GameCoreRoutine:sm2main.asm lines 4232–4406 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $AEEA-$B049. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $7A37-$7BA1. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Cannons, hammers, coins, power-ups and blocks (
ProcessCannonsthroughBlockObjMT_Updater) are Super Mario Bros.'s with the poison mushroom added and one player:BumpBlock's contents table gains poison mushroom blocks andMushFlowerBlockgainsPoisonMushBlock, which sets power-up type 4;PowerUpObjHandlerwalks types 4 and 5 like a mushroom;BlockBumpedChkchecks 19 block metatiles where Super Mario Bros. checks 14;GiveOneCoinandAddToScoreuse fixed offsets where Super Mario Bros. indexes byCurrentPlayer; the head-collision and coin checks use the renumbered metatiles.SetupPowerUpis byte for byte Super Mario Bros.'s, though the poison mushroom now passes through it, and so areInitBlock_XY_PosandSpawnBrickChunks; the cannons, hammers, jumping coins, brick shatter and bouncing blocks differ only in their addresses.Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1 · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
cmp #$0d
sm2main.asm#L6305-L6305 · upstream ↗ 6319 .word VineBlocksm2main.asm lines 6319–6322 at 9c40114 · upstream ↗ lda #$04
sm2main.asm#L6333-L6333 · upstream ↗ 6152 cmp #$04sm2main.asm lines 6152–6155 at 9c40114 · upstream ↗ LDY #$0d
src/prg.asm#L6312-L6312 · upstream ↗ ldy #$12
sm2main.asm#L6358-L6358 · upstream ↗ LDX CurrentPlayer
src/prg.asm#L6011-L6012 · upstream ↗ ldy #$11
sm2main.asm#L6071-L6071 · upstream ↗ lda #$c5
sm2main.asm#L6227-L6227 · upstream ↗ 5665ProcessCannons:src/prg.asm lines 5663–6474 at 1cc3c92 · upstream ↗ 5756ProcessCannons:sm2main.asm lines 5754–6512 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $B9BA-$BF01. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $8585-$8AD2. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcdThe collision code (
FireballEnemyCollisionthroughBlockBufferCollision) is Super Mario Bros.'s with the upside-down piranha plant, the poison mushroom and the renumbered metatiles added: the fireball, enemy, background and stomp checks treat the upside-down plant like a piranha plant;HandlePowerUpCollisioninjures the player on power-up type 4;InjurePlayeralso does nothing while the star'sStarInvincibleTimerruns;HandlePECollisionschecks for the cannon's Bullet Bill before the piranha plants and Podoboo, where Super Mario Bros. checks it after them;PlayerEnemyCollisiondecides a stomp with LDY, DEY, BPL on the vertical speed where Super Mario Bros. uses BMI and BNE (the two disagree only at speed $80); every stomp bounces the player through a newSetBounce, at vertical speed $FA, or $F8 off red paratroopas and flying green paratroopas, where Super Mario Bros. sets $FD after a stomp that defeats an enemy and $FC after one that demotes a paratroopa or knocks an enemy into its shell;HandleClimbinggives a life for grabbing the flagpole when the coin tally's digits and the timer's last digit match;HandlePipeEntrysends the player to the warp zone's one destination whichever pipe they take, where Super Mario Bros. picks by the pipe's x position;HandleCoinMetatile's axe restores Mario's physics;ChkForLandJumpSpringsetsVerticalForceDownto $70 as well asVerticalForce;PlayerHammerCollisionalso skips while the player is off screen;HandleEToBGCollisionbounces a power-up on a bumped block without stunning it and never demotes a piranha plant;CheckPlayerVerticaldrops its test of how far down the screen the player is; one bounding box grows from $14 to $16. The platform collisions,LandEnemyProperly, the side checks andBlockBufferCollisiondiffer only in their addresses, and three regions,PlayerCollisionCoreamong them, are byte for byte the same.Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1 · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”revisedHow we know
cpy #UpsideDownPiranhaP
sm2main.asm#L10190-L10190 · upstream ↗ jmp InjurePlayer
sm2main.asm#L10255-L10258 · upstream ↗ ora StarInvincibleTimer
sm2main.asm#L10424-L10425 · upstream ↗ cpy #BulletBill_CannonVar
sm2main.asm#L10356-L10361 · upstream ↗ CPY #Podoboo
src/prg.asm#L10515-L10520 · upstream ↗ 11296 sta VerticalForceDownsm2main.asm lines 11294–11296 at 9c40114 · upstream ↗ LDA Player_Y_Speed
src/prg.asm#L10556-L10558 · upstream ↗ ldy Player_Y_Speed
sm2main.asm#L10399-L10401 · upstream ↗ LDA #$fd
src/prg.asm#L10661-L10662 · upstream ↗ LDA #$fc
src/prg.asm#L10695-L10697 · upstream ↗ cmp #RedParatroopa
sm2main.asm#L10533-L10541 · upstream ↗ sta FlagpoleScore
sm2main.asm#L11225-L11231 · upstream ↗ LDA Player_X_Position
src/prg.asm#L11521-L11529 · upstream ↗ 11336 lda WarpZoneNumbers,xsm2main.asm lines 11334–11336 at 9c40114 · upstream ↗ 11163 jsr LoadMarioPhysicssm2main.asm line 11163 at 9c40114 · upstream ↗ ora Misc_OffscreenBits
sm2main.asm#L10224-L10224 · upstream ↗ 11511NoDemote: cmp #PowerUpObjectsm2main.asm lines 11501–11515 at 9c40114 · upstream ↗ LDY Player_Y_HighPos
src/prg.asm#L11066-L11072 · upstream ↗ 12036 .db $03, $0e, $0d, $14src/prg.asm line 12036 at 1cc3c92 · upstream ↗ 11827 .byte $03, $0e, $0d, $16sm2main.asm line 11827 at 9c40114 · upstream ↗ 10242FireballEnemyCDLoop:src/prg.asm lines 10227–12360 at 1cc3c92 · upstream ↗ 10085FireballEnemyCDLoop:sm2main.asm lines 10070–12141 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $D6D9-$E431. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $A319-$B0CF. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abThe status bar numbers, game setup, lives and game over (
PrintStatusBarNumbersthroughRunGameOver) are Super Mario Bros.'s with a newGameOverSubsdispatch among them and these changes: the status bar's digit tables lose the second player's entries;UpdateTopScorechecks one score where Super Mario Bros. checks two;DigitsMathRoutinedrops a compare with zero before its BEQ;DefaultSprOffsetsloses the sprite 0 data;InitializeArealoads the player's physics (LoadPhysicsData) and sets the secondary hard mode flag from 4-4 on and in all of worlds A to D (HardWorldFlag), where Super Mario Bros. sets it from 5-3 on or once the primary hard mode flag is set;SecondaryGameSetupsets the name table and switches on the status bar's IRQ split, where Super Mario Bros. sets the second player's lives and switches on the sprite 0 check;InitializeMemoryalso spares $0100-$0108;PlayerLoseLifereads its own halfway pages and no longer swaps players;RunGameOveropens the continue-or-retry menu (or, in world 9, ends the game) where Super Mario Bros. ends the game on Start or when the screen timer runs out and then hands over to the other player if they have lives left.GetAreaMusicandEntrance_GameTimerSetupkeep every instruction, though the level timer's digits moved from $07F8 to $07EC.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”revisedHow we know
1990 .db $06, $0c, $12, $18, $1e, $24src/prg.asm line 1990 at 1cc3c92 · upstream ↗ 2087 JSR TopScoreChecksrc/prg.asm line 2087 at 1cc3c92 · upstream ↗ 2050 CMP #TitleScreenModeValuesrc/prg.asm line 2050 at 1cc3c92 · upstream ↗ 2119 .db $18, $ff, $23, $58src/prg.asm line 2119 at 1cc3c92 · upstream ↗ 2444 .byte $06, $0c, $12, $18sm2main.asm line 2444 at 9c40114 · upstream ↗ lda HardWorldFlag
sm2main.asm#L2591-L2598 · upstream ↗ CMP #Level3
src/prg.asm#L2172-L2179 · upstream ↗ 2609 jsr LoadPhysicsDatasm2main.asm line 2609 at 9c40114 · upstream ↗ 2631 sta NameTableSelectsm2main.asm line 2631 at 9c40114 · upstream ↗ INC Sprite0HitDetectFlag
src/prg.asm#L2245-L2245 · upstream ↗ 2645 inc IRQUpdateFlagsm2main.asm line 2645 at 9c40114 · upstream ↗ cpy #$09
sm2main.asm#L2660-L2661 · upstream ↗ JSR TransposePlayers
src/prg.asm#L2441-L2441 · upstream ↗ 2472 AND #Start_Buttonsrc/prg.asm lines 2472–2473 at 1cc3c92 · upstream ↗ jmp GameOverMenu
sm2main.asm#L2854-L2856 · upstream ↗ sta GameTimerDisplay
sm2main.asm#L2751-L2751 · upstream ↗ 1989StatusBarOffset:src/prg.asm lines 1982–2534 at 1cc3c92 · upstream ↗ 2443StatusBarOffset:sm2main.asm lines 2438–2881 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $8EF4-$92AF. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $6D6D-$70BA. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^The code keeps the primary hard mode flag's checks, which turn Goombas into Buzzy Beetles, start walking enemies faster (
$F4against$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.Codedoppelganger's SMB2J disassembly · Xkeeper0/smb1How we know
6983BuzzyBeetleMutate: 6984 cmp #Goomba ;if below $37, check for goomba 6985 bne StrID ;value ($3f or more always fails) 6986 ldy PrimaryHardMode ;check if primary hard mode flag is set 6987 beq StrID ;and if so, change goomba to buzzy beetle 6988 lda #BuzzyBeetle 6989StrID: sta Enemy_ID,x ;store enemy object number into buffer
sm2main.asm lines 6983–6989 at 9c40114 · upstream ↗ 7751 pha ;save another copy to stack 7752 ldy #Goomba ;load value for goomba enemy 7753 lda PrimaryHardMode ;if primary hard mode flag not set, 7754 beq PullID ;branch, otherwise change to value 7755 ldy #BuzzyBeetle ;for buzzy beetle 7756PullID: pla ;get second copy from stack
sm2main.asm lines 7751–7756 at 9c40114 · upstream ↗ 7148NormalXSpdData: 7149 .byte $f8, $f4 ⋮ 7151InitNormalEnemy: 7152 ldy #$01 ;load offset of 1 by default 7153 lda PrimaryHardMode ;check for primary hard mode flag set 7154 bne GetESpd 7155 dey ;if not set, decrement offset 7156GetESpd: lda NormalXSpdData,y ;get appropriate horizontal speed 7157SetESpd: sta Enemy_X_Speed,x ;store as speed for enemy object
sm2main.asm lines 7148–7157 at 9c40114 · upstream ↗ 8213RevivedXSpeed: 8214 .byte $08, $f8, $0c, $f4
sm2main.asm lines 8213–8214 at 9c40114 · upstream ↗ 8351 lda PrimaryHardMode ;check primary hard mode flag 8352 beq SetRSpd ;if not set, use pointer as-is 8353 iny 8354 iny ;otherwise increment 2 bytes to next data 8355SetRSpd: lda RevivedXSpeed,y ;load and store new horizontal speed 8356 sta Enemy_X_Speed,x ;and leave
sm2main.asm lines 8351–8356 at 9c40114 · upstream ↗ 10518RevivalRateData: 10519 .byte $10, $0b
sm2main.asm lines 10518–10519 at 9c40114 · upstream ↗ 10530 ldy PrimaryHardMode ;check primary hard mode flag 10531 lda RevivalRateData,y ;load timer setting according to flag 10532 sta EnemyIntervalTimer,x ;set as enemy timer to revive stomped enemy
sm2main.asm lines 10530–10532 at 9c40114 · upstream ↗ 14848 ldy #$6f ;clear all memory as in initialization procedure, 14849 jsr InitializeMemory ;but this time, clear only as far as $076f
sm2main.asm lines 14848–14849 at 9c40114 · upstream ↗ 415 LDA WorldSelectEnableFlag ; if world select flag is on, then primary 416 STA PrimaryHardMode ; hard mode must be on as well
src/prg.asm lines 415–416 at 1cc3c92 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^The area parser and its object builders (
AreaParserTaskHandlerthroughGetBlockBufferAddr) are Super Mario Bros.'s, with most changes in the metatile numbers they write, which shift with the renumbered metatiles (bridges, flagpole, coins, bricks, cannons, stairs, blocks, water pipe). Beyond those: the object jump table gains entries, the wind on and off commands among them, so three offsets into it change;ScrollLockObject_Warppicks the warp zone's destination by world, level and area; the third ledge style uses metatiles $8A-$8C and draws no stem, where Super Mario Bros.'s mushroom ledge uses $19-$1B with a stem (andRenderUnderPartdrops its stem checks); the vertical pipes place a piranha plant through a sharedSetupPiranhaPlant, with no exception for world 1-1;Jumpspringplaces nothing when no enemy slot is free.IncrementColumnPos,AlterAreaAttributes,KillEnemiesand the object position helpers are byte for byte the same.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
LDA #$2e
src/prg.asm#L2925-L2925 · upstream ↗ lda #$36
sm2main.asm#L3246-L3246 · upstream ↗ .word WindOn
sm2main.asm#L3385-L3386 · upstream ↗ lda WorldNumber
sm2main.asm#L3429-L3429 · upstream ↗ LDA #$19
src/prg.asm#L3215-L3215 · upstream ↗ 3229 LDA #$4fsrc/prg.asm line 3229 at 1cc3c92 · upstream ↗ lda #$8a
sm2main.asm#L3550-L3550 · upstream ↗ 3560 rtssm2main.asm line 3560 at 9c40114 · upstream ↗ 3852 CPY #$54src/prg.asm lines 3852–3855 at 1cc3c92 · upstream ↗ 4145 cpy #$8bsm2main.asm line 4145 at 9c40114 · upstream ↗ 3428 LDA AreaNumbersrc/prg.asm lines 3428–3430 at 1cc3c92 · upstream ↗ 3772SetupPiranhaPlant:sm2main.asm lines 3772–3773 at 9c40114 · upstream ↗ bcs NoJs
sm2main.asm#L4040-L4040 · upstream ↗ 2543DoAPTasks:src/prg.asm lines 2539–3938 at 1cc3c92 · upstream ↗ 2897DoAPTasks: dey ⋮ 3759 dey ⋮ 4083 dey ⋮ 4156 dey
sm2main.asm lines 2893–4227 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $92B0-$9BF5. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $70C1-$7A36. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abFireballs, the level timer and the stage objects (
ProcFireballsthroughVineObjectHandler) are Super Mario Bros.'s with these changes:RunGameTimertests the player's height with BPL instead of BCS and points at the timer digits' new place;WarpZoneObjectno longer counts upWarpZoneControl;FlagpoleRoutinegives a life for the new sixth flagpole score;JumpspringHandlerlaunches with the green springs' force on world numbers 2, 3 and 7;VineObjectHandlererases the vine and its metatiles once it goes off the left of the screen.ProcFireballsandProcessWhirlpoolsdiffer only in their addresses, andSetupVineis byte for byte the same.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
BCS ExGTimer
src/prg.asm#L5324-L5324 · upstream ↗ bpl ExGTimer
sm2main.asm#L5387-L5387 · upstream ↗ LDY #$23
src/prg.asm#L5342-L5342 · upstream ↗ ldy #$17
sm2main.asm#L5404-L5404 · upstream ↗ INC WarpZoneControl
src/prg.asm#L5364-L5364 · upstream ↗ 5554 inc NumberofLivessm2main.asm lines 5552–5557 at 9c40114 · upstream ↗ cpy #World7
sm2main.asm#L5604-L5611 · upstream ↗ 5714ChkVOffscr: lda Enemy_X_Position+5sm2main.asm lines 5714–5724 at 9c40114 · upstream ↗ 5174ProcFireballs:src/prg.asm lines 5143–5658 at 1cc3c92 · upstream ↗ 5246ProcFireballs:sm2main.asm lines 5215–5749 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $B624-$B9B9. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $8190-$8584. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Enemy spawning and set-up (
EnemiesAndLoopsCorethroughInitJumpGPTroopa) are Super Mario Bros.'s with these changes:ProcLoopCommandreads 12 loop entries where Super Mario Bros. reads 11, and takes each loop's number of checks from a newMultiLoopCounttable where Super Mario Bros. special-cases world 7;ProcessEnemyDataskips a check in world 9 and gives one enemy code a piranha plant set-up;InitHammerBrochecks the world;LakituAndSpinyHandlerbrings Lakitu back onceLakituReappearTimerreaches 3, where Super Mario Bros. waits until it reaches 7, and brings him back lower on the screen in worlds 7 to 9 and A to D;InitBowserfirst erases any other Bowser;InitPiranhaPlantpatches the plant's palette and pipe distance, green in worlds 1 to 3 and red from world 4 and in A to D;InitJumpGPTroopastarts the jumping paratroopa at x speed $F4 where Super Mario Bros. uses $F8. Of its 27 regions, 15 differ only in their addresses and 5 are byte for byte the same.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”revisedHow we know
LDY #$0b
src/prg.asm#L6797-L6797 · upstream ↗ ldy #$0c
sm2main.asm#L6832-L6832 · upstream ↗ CMP #World7
src/prg.asm#L6813-L6815 · upstream ↗ 6850 cmp MultiLoopCount,ysm2main.asm line 6850 at 9c40114 · upstream ↗ cmp #World9
sm2main.asm#L7018-L7020 · upstream ↗ 7174 cmp #World7sm2main.asm lines 7173–7175 at 9c40114 · upstream ↗ CMP #$07
src/prg.asm#L7275-L7276 · upstream ↗ cmp #$03
sm2main.asm#L7265-L7266 · upstream ↗ 7280 ldy HardWorldFlagsm2main.asm lines 7280–7285 at 9c40114 · upstream ↗ KKCheck: cpy ObjectOffset
sm2main.asm#L7483-L7493 · upstream ↗ sta EnemyAttributeData+PiranhaPlant
sm2main.asm#L7810-L7821 · upstream ↗ LDA #$f8
src/prg.asm#L7895-L7895 · upstream ↗ lda #$f4
sm2main.asm#L7874-L7874 · upstream ↗ 6734ChkAreaTsk:src/prg.asm lines 6727–7901 at 1cc3c92 · upstream ↗ ChkAreaTsk: lda AreaParserTaskNum
sm2main.asm#L6758-L7878 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $C047-$C7DE. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $8C23-$9413. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ ab
RunStarFlagObjthroughOffscreenBoundsCheckare Super Mario Bros.'s with these changes:RunStarFlagObjsets off fireworks when the timer's last digit matches the coin tally's, where Super Mario Bros. matches the timer's last digit against 1, 3 and 6, and adds its points to the one player's score;MovePiranhaPlantmoves red piranha plants (the patched palette) every frame;BalancePlatformleaves a platform alone when its partner is no balance platform;OffscreenBoundsChecktreats upside-down piranha plants like upright ones.FirebarSpinis byte for byte the same, and the moving platforms differ only in their addresses.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”How we know
9610 CMP #$06src/prg.asm lines 9602–9611 at 1cc3c92 · upstream ↗ cmp CoinDisplay+1
sm2main.asm#L9472-L9472 · upstream ↗ LDA CurrentPlayer
src/prg.asm#L9643-L9643 · upstream ↗ cmp #$22
sm2main.asm#L9615-L9615 · upstream ↗ cmp #$24
sm2main.asm#L9682-L9684 · upstream ↗ cpy #PiranhaPlant
sm2main.asm#L10023-L10023 · upstream ↗ 9581StarFlagXPosAdder:src/prg.asm lines 9579–10216 at 1cc3c92 · upstream ↗ 9450StarFlagXPosAdder:sm2main.asm lines 9448–10064 at 9c40114 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $D2CD-$D6D5. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $9F02-$A318. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^The platforms' set-up, the enemy object loop, the enemies' movers, Bowser and the fireworks (
InitBalPlatformthroughRunFireworks, 32 regions) are Super Mario Bros.'s instruction for instruction, with two changes: the enemy movers' jump table (EnemyMovementSubs) sends enemy 4, the upside-down piranha plant, to the newMoveUpsideDownPiranhaP, where Super Mario Bros. hasMoveNormalEnemy;MoveSwimmingCheepCheepadds $40 to a swimming Cheep Cheep's vertical fraction each frame where Super Mario Bros. adds $20. Three of the regions are byte for byte the same, and the rest differ only in the addresses of their calls, jumps and tables.CodeXkeeper0/smb1 · doppelganger's SMB2J disassembly · “Super Mario Bros. (World)” · “Super Mario Bros. 2 (Japan) (En)”revisedHow we know
MoveNormalEnemy
src/prg.asm#L8124-L8124 · upstream ↗ MoveUpsideDownPiranhaP
sm2main.asm#L8093-L8093 · upstream ↗ 8682 LDA #$20src/prg.asm lines 8682–8683 at 1cc3c92 · upstream ↗ 8621 lda #$40sm2main.asm lines 8621–8622 at 9c40114 · upstream ↗ ADC $02
src/prg.asm#L8689-L8691 · upstream ↗ 7912AlignP:src/prg.asm lines 7906–9574 at 1cc3c92 · upstream ↗ AlignP: ldy #$ff
sm2main.asm#L7883-L9443 · upstream ↗ In the cartridge (“Super Mario Bros. (World)”): $C7DF-$D2CC. We don't reproduce the bytes.
In the cartridge (“Super Mario Bros. 2 (Japan) (En)”): SM2MAIN $9414-$9F01. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
Sources
Code
- doppelganger and threecreepio. 2025. “SMB2J disassembly for ca65 (threecreepio/smb2j-disassembly).” Commit 9c40114. threecreepio/smb2j-disassembly @ 9c40114 · licence, all uses
- doppelganger and Xkeeper. 2022. “smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1).” Commit 1cc3c92. Xkeeper0/smb1 @ 1cc3c92 · licence, all uses
- 1985. “Super Mario Bros. (World).” · licence, all uses
- 1986. “Super Mario Bros. 2 (Japan) (En).” · licence, all uses
Primary
- Nintendo. n.d. “Nintendo, Virtual Console: Super Mario Bros. 2 (Famicom Disk System).” Nintendo. original · archived · licence, all uses
- Co., Nintendo and Ltd. 2010. “Iwata Asks: Super Mario 25th Anniversary.” Nintendo Co., Ltd. original · archived · licence, all uses
- Nintendo. 1986. “Super Mario Bros. 2 (FMC-SMB) Famicom Disk System manual and jacket.” Nintendo. original · archived · licence, all uses
- Co., Nintendo and Ltd. 2010. “Iwata Asks: Super Mario All-Stars (Wii).” Nintendo Co., Ltd. original · archived · licence, all uses
- Nintendo. 2016. “Nintendo, NES Classic Edition developer interview: Super Mario Bros. and Super Mario Bros. 3.” Nintendo. original · archived · licence, all uses
- Nintendo. n.d. “Nintendo, Famicom software: Super Mario Bros. 2.” Nintendo. original · archived · licence, all uses
- America, Nintendo of. n.d. “Super NES Games (Nintendo of America release list).” Nintendo of America. original · archived · licence, all uses
- Nintendo. 1985. “Super Mario Bros. instruction booklet (NES).” Super Mario Bros. instruction booklet, pp. 1-16. Nintendo of America. original · archived · licence, all uses
Secondary
- Kingdom, The Mushroom. 2010. “Nintendo Channel: Interview with Shigeru Miyamoto, volumes 1 and 2 (December 2010), transcript.” The Mushroom Kingdom. original · archived · licence, all uses
- McLaughlin, Rus. 2010. “IGN Presents: The History of Super Mario Bros.” IGN. original · archived · licence, all uses
- Provo, Frank. 2007. “Super Mario Bros.: The Lost Levels Review (GameSpot).” GameSpot. original · archived · licence, all uses
- Byrd, Matthew. 2024. “Why We've Always Been Wrong About the 'Real' Super Mario Bros. 2.” Den of Geek. original · archived · licence, all uses
- ASCII editors. 1986. “BIWEEKLY Famicom Tsushin (Famitsu) no. 4, 1 August 1986.” Famicom Tsushin, vol. 1 no. 4 (whole no. 4). ASCII. original · archived · licence, all uses
- Shuppankyoku, JICC. 1986. “Famicom Hisshoubon no. 6, September 1986.” Famicom Hisshoubon, no. 6 (September 1986). JICC Shuppankyoku. original · archived · licence, all uses
- Nintendo Power editors. 1993. “Nintendo Power, issue 52 (1993-09).” Nintendo Power, vol. 52. Nintendo of America. original · archived · licence, all uses
- Jdaster64. n.d. “A Complete Guide to SMB's Physics Engine.” original · archived · licence, all uses
- Computer Entertainer editors. 1986. “Computer Entertainer, Vol. 5, No. 3 (June 1986).” Computer Entertainer, vol. 5, no. 3 (June 1986). Computer Entertainer. original · archived · licence, all uses