5

There's a glitch in the original Super Mario Bros on the NES. That causes Mario to become an unused sprite(Little Fire-Mario). I'm unsure how the glitch works, but I'll show the instructions on how to pull it off to add some information that might be crucial in explaining the glitch(Little Fire-Mario).

Instructions

You have to enter any castle level except for 8-4(as it will end the game) as Big or Fire Mario. To pull off the glitch, you have to hit the axe and Bowser at the same time. This will cause Mario to enter the next level as Big Mario. However, upon touching a Mushroom, Big Mario will become small, and touching a fire flower will cause Small Fire Mario to appear.

How does all of this work? And why would taking damage and hitting the axe at the same time open up unused sprites like Little Fire Mario?

12

Without analyzing the code

I'd risk to say that the state of Mario consists of 2 different variables:

  • big or small mario state
  • big or small mario image

When you're hit and you end a level, the game only updates one of the variables: mario state, and doesn't have time to update the other one, resulting in an inconsistency. This should not happen but it did.

Now when you pick the mushroom, since the state is known as "small mario", the game probably toggles the sprite (by EORing it), so it swaps it, which can explain why mario doesn't remain big, but swaps to small.

And note that there is no firing frame as small. In this video https://www.youtube.com/watch?v=v2lVgHaRDf8 we can witness that firing mario is small unless when he fires. The frame where he fires is the one from a big mario.

Now why are there 2 variables/states for the same thing? Because there's a transition from big to small and reverse. When the level is completed, the transition isn't done / is aborted before it can happen, and Mario remains big.

With analysis of the code

Now you can find a comprehensive SMB 6502 disassembly on github. It seems to confirm what I suspected:

PlayerSize            = $0754
PlayerStatus          = $0756

as you see, there is one variable for size and another one for status.

The status variable is changed here:

Shroom_Flower_PUp:
      lda PlayerStatus    ;if player status = small, branch
      beq UpToSuper
      cmp #$01            ;if player status not super, leave
      bne NoPUp
      ldx ObjectOffset    ;get enemy offset, not necessary
      lda #$02            ;set player status to fiery
      sta PlayerStatus
      jsr GetPlayerColors ;run sub to change colors of player
      ldx ObjectOffset    ;get enemy offset again, and again not necessary
      lda #$0c            ;set value to be used by subroutine tree (fiery)
      jmp UpToFiery       ;jump to set values accordingly

The size isn't changed there. It's done somewhere else.

The transition code uses a third variable PlayerChangeSizeFlag, and as predicted, when the flag reaches a given value, an inversion of the size variable is done (using eor #$01 on it), which explains that if the size is big, it becomes small:

InitChangeSize:
          ldy PlayerChangeSizeFlag  ;if growing/shrinking flag already set
          bne ExitBoth              ;then branch to leave
          sty PlayerAnimCtrl        ;otherwise initialize player's animation frame control
          inc PlayerChangeSizeFlag  ;set growing/shrinking flag
          lda PlayerSize
          eor #$01                  ;invert player's size
          sta PlayerSize
ExitBoth: rts                       ;leave

Note that InitChangeSize is the same routine used to make Mario bigger when he gets a mushroom, or smaller when he's hit.

When Mario is hit just when the level ends, PlayerState is updated immediately to "small", but InitChangeSize is probably not called or not called enough times to reach the state where it changes PlayerSize

The fix is rather easy if one wants to patch the game: at the start of the level, propagate PlayerState to PlayerSize to correct the desynchronization.

| improve this answer | |
  • 2
    FWIW, the disassembly has been ported to a SourceGen project: 6502disassembly.com/nes-smb . It loses a little of the original, e.g. the list of constants is a bit of a mess, but gains cross-references. The visualizations are a bit iffy at this point. – fadden May 24 at 22:22
  • 2
    Wow. Just wow. This is a super elaborate answer that I thought I would never see on a question like this. Thanks for explaining it in such detail. – Nip Dip May 25 at 0:16
  • As a person who used (and still sometimes does) to create cheat-tables, can confirm the 0x754 and 0x756 thing ! Interestingly, the same thing applies to the world type - one address for visuals, another for physics. By the way, I encourage you guys to use some ram editor and try inputting the invalid values, and seeinmg what happens. I couldn't find anything too interesting in SMB1, but in SMB3, the "invalid" upgrade type results in interestingly glitchy model and behaviour! – tabdiukov May 25 at 11:10
  • something you can do for all console games, because you virtually cannot crash a cartridge console game (the code of which resides in ROM), but you can make the program do strange & interesting things. – Jean-François Fabre May 25 at 12:08
  • @Jean Exactly! A good way to have both some fun, and learn something new :) – tabdiukov May 25 at 13:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.