This page details bugs of Super Mario Bros..
Spiny Egg Bug#
Here's how the Spiny egg's speed is calculated and stored.
DifLoop: lda PRDiffAdjustData,y ;get three values and save them
sta $01,x ;to $01-$03
iny
iny ;increment Y four bytes for each value
iny
iny
dex ;decrement X for each one
bpl DifLoop ;loop until all three are written
ldx ObjectOffset ;get enemy object buffer offset
jsr PlayerLakituDiff ;move enemy, change direction, get value - difference
ldy Player_X_Speed ;check player's horizontal speed
cpy #$08
bcs SetSpSpd ;if moving faster than a certain amount, branch elsewhere
tay ;otherwise save value in A to Y for now
lda PseudoRandomBitReg+1,x
and #%00000011 ;get one of the LSFR parts and save the 2 LSB
beq UsePosv ;branch if neither bits are set
tya
eor #%11111111 ;otherwise get two's compliment of Y
tay
iny
UsePosv: tya ;put value from A in Y back to A A=SpeedVar
SetSpSpd: jsr SmallBBox ;set bounding box control, etc. A=SpeedVar
...
Doing good so far. The A register stores the appropriate speed value.
SmallBBox: lda #$09 ;set specific bounding box size control A=09
bne SetBBox ;unconditional branch A=09
Oops, jumping to the bounding box subroutine overwrote A! All that work for nothing.
SetBBox: sta Enemy_BoundBoxCtrl,x ;set bounding box control here A=09
lda #$02 ;set moving direction for left A=02
sta Enemy_MovingDir,x A=02
InitVStf: lda #$00 ;initialize vertical speed A=00
sta Enemy_Y_Speed,x ;and movement force A=00
sta Enemy_Y_MoveForce,x A=00
rts A=00
Now A ends up equaling 00. Now the game jumps back to the SetSpSpd subroutine...
...
ldy #$02 ;Set Y to 02 (Leftwards) A=00
sta Enemy_X_Speed,x ;Set Spiny egg speed to A (00) A=00
cmp #$00 ;Now check if speed is negative... A=00
bmi SpinyRte ;...but A is always 00. No branch. A=00
dey ;Set Y to 01 (Rightwards)
SpinyRte: sty Enemy_MovingDir,x ;Set moving direction.
...and speed is set to 0. Moving direction is set to Rightwards, but since the egg has no horizontal movement and the direction is reset when the Spiny enemy spawns, this doesn't mean much.
To fix this bug, simply move the "jsr SmallBBox" to after the dey opcode, which is what this patch does:
Download Spiny Egg Speed Patch File: SMBSpinyEggPatch.ips (25 B) (info) |
(Source: doppelganger's SMBDis)
Warp Zone Scroll bug#
|
|
To do: |
Unless you're walking on the ceiling, Level 1-2 is supposed to stop scrolling as soon as the mundane return-to-surface pipe comes onto the right edge of the screen. However, due to a programming error, it keeps scrolling anyway, revealing that the warp zone room is there. It was originally intended to be a much more hidden secret than it turned out to be.
Download “No Minus World” patch File: SMBNoMinus.7z (353 B) (info) |
Shell vs. Hammer Brother bug#
|
|
To do: |
In World 8-3, due to a programming error, if Mario stomps on a Koopa shell and uses it to hit a Hammer Brother, it will often fail to destroy the enemy. The few times it does actually destroy a Hammer Brother, though, it will only award the player 500 points for the first and 800 for the second, instead of the flat 1000 points Hammer Brothers usually award the player when defeated.
ProcEnemyCollisions:
lda Enemy_State,y ;check both enemy states for d5 set
ora Enemy_State,x
and #%00100000 ;if d5 is set in either state, or both, branch
bne ExitProcessEColl ;to leave and do nothing else at this point
lda Enemy_State,x
cmp #$06 ;if second enemy state < $06, branch elsewhere
bcc ProcSecondEnemyColl
lda Enemy_ID,x ;check second enemy identifier for hammer bro
cmp #HammerBro ;if hammer bro found in alt state, branch to leave
beq ExitProcessEColl
lda Enemy_State,y ;check first enemy state for d7 set
asl
bcc ShellCollisions ;branch if d7 is clear
lda #$06
jsr SetupFloateyNumber ;award 1000 points for killing enemy
jsr ShellOrBlockDefeat ;then kill enemy, then load
ldy $01 ;original offset of second enemy
ShellCollisions:
tya ;move Y to X
tax
jsr ShellOrBlockDefeat ;kill second enemy
ldx ObjectOffset
lda ShellChainCounter,x ;get chain counter for shell
clc
adc #$04 ;add four to get appropriate point offset
ldx $01
jsr SetupFloateyNumber ;award appropriate number of points for second enemy
ldx ObjectOffset ;load original offset of first enemy
inc ShellChainCounter,x ;increment chain counter for additional enemies
ExitProcessEColl:
rts ;leave!!!
ProcSecondEnemyColl:
lda Enemy_State,y ;if first enemy state < $06, branch elsewhere
cmp #$06
bcc MoveEOfs
lda Enemy_ID,y ;check first enemy identifier for hammer bro
cmp #HammerBro ;if hammer bro found in alt state, branch to leave
beq ExitProcessEColl
jsr ShellOrBlockDefeat ;otherwise, kill first enemy
ldy $01
lda ShellChainCounter,y ;get chain counter for shell
clc
adc #$04 ;add four to get appropriate point offset
ldx ObjectOffset
jsr SetupFloateyNumber ;award appropriate number of points for first enemy
ldx $01 ;load original offset of second enemy
inc ShellChainCounter,x ;increment chain counter for additional enemies
rts ;leave!!!
In Super Mario All-Stars, it has been corrected so that the Hammer Brother is now the first enemy in the queue to be hit by a shell, hence the shell always hits a Hammer Brother perfectly and to award the player 1000 points as with all other means of defeating a Hammer Brother.
ProcEnemyCollisions:
lda Enemy_State,y ;check both enemy states for d5 set
ora Enemy_State,x
and #%00100000 ;if d5 is set in either state, or both, branch
bne ExitProcessEColl ;to leave and do nothing else at this point
lda Enemy_ID,x ;SMAS bugfix: check enemy identifier for hammer bro first
cmp #HammerBro ;if hammer bro found in alt state, branch to leave
bne PEC2
lda #$00 ;SMAS bugfix: reset hammer bro state if defeated (perfect hit from shell every time)
sta Enemy_State,x
PEC2: lda Enemy_State,x
cmp #$06 ;if second enemy state < $06, branch elsewhere
bcc ProcSecondEnemyColl
lda Enemy_State,y ;check first enemy state for d7 set
asl
bcc ShellCollisions ;branch if d7 is clear
lda #$06
jsr SetupFloateyNumber ;award 1000 points for killing enemy
jsr ShellOrBlockDefeat ;then kill enemy, then load
ldy $01 ;original offset of second enemy
According to ShaneM, the SMAS bugfix used for this was also flawed because it prevented the player from earning a couple hundred to thousand points during the stomp/shell chain routine, as well as not being given points for demoting a Paratroopa. The old SetupFloateyNumber routine in the remake was at 0x3E084, while the initialization was at 0x3E07E, and except for the 'ShellCollisions' and 'ProcSecondEnemyColl' all the JSR and JMP calls should point to 0x3E084 instead.
InitSetupFloatey:
cmp FloateyNum_Control,x
bcc ExSFN
SetupFloateyNumber:
and then change the two instances of
jsr SetupFloateyNumber
under 'ShellCollisions' and 'ProcSecondEnemyColl' to
jsr InitSetupFloatey