SMB1 Hacking

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

I just experimented with ShaneM's code for the lives cap he implemented within the "DecNumTimer" subroutine, but I did it differently:

Code: Select all

Lives:    lda NumberOfLives     ; check number of lives
          cmp #$13              ; 19 lives yet?
          bcs EndLives          ; if yes, skip this routine
          inc NumberOfLives     ; otherwise, give player one extra life
          lda #SFX_ExtraLife  
          sta Square2SoundQueue ; and play the 1-up sound
EndLives: rts
Therefore, once the cap for the amount of extra lives has been met, the 1-UP sound will no longer play. What I want to do next is change the function of the 1-UP Mushroom to give Mario 2,000 points after the lives cap has been reached, and to play the power-up sound instead (this being akin to what the 1-UP Mushroom does in Super Mario Bros. Deluxe's Challenge mode, which gives the player 2,000 points). Here's the routine for the 1-UP Mushroom:

Code: Select all

SetFor1Up:
    lda #$0b                 ; change 1,000 points into 1-UP instead
    sta FloateyNum_Control,x ; and then leave
    rts
My proposed modified code. Please let me know what I should do to make it right, how best to optimize it.

Code: Select all

SetFor1Up:
    lda #$0b                   ; change 1,000 points into 1-UP instead
    ldy NumberOfLives
    cpy #$13                   ; does player have 19 lives yet?
    bcc EndSetFor1Up           ; if not, skip 
    lda #$07                   ; otherwise, change 1-UP into 2,000 points instead
    ldy #SFX_PowerUpGrab
    sty Square2SoundQueue      ; and play the power-up sound instead
EndSetFor1Up:
    sta FloateyNum_Control,x   ; and then leave
    rts
I would also like to know what to do to make the game kill a Koopa Troopa or Buzzy Beetle automatically if the player's lives reach 19 (or are at 19) if by turtle tipping, and instead give the player a final 8,000 points, just as what happens in Super Mario Bros. Deluxe's Challenge mode.

~Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

I have a question about the PAL-A version of SMB1. I know that the movement for the flying Cheep Cheep animation (MoveFlyingCheepCheep) has been greatly simplified, but unless you also change the related attributes (under FlyCCXSpeedData and InitFlyingCheepCheep) to the PAL-A version, then the Cheep Cheeps will only fly just above the bridge. Even for the original PAL-A release, the Cheep Cheeps never fly near the heads-up display attributes.

Original NTSC version:
https://www.youtube.com/watch?v=Gx5--eK2k6Y&t=462s
(the flying Cheep Cheeps will fly as high as the score display)

Original PAL-A version:
https://www.youtube.com/watch?v=APe83KBXf9M&t=465s
(the flying Cheep Cheeps only fly as high as the bottom of the topmost clouds)

Super Mario Bros. Enhanced (by Dintonen):
https://www.youtube.com/watch?v=dCpvqPC6Gkc&t=454s
(the flying Cheep Cheeps only fly as high as the top of the bridge, when combined with the original NTSC flying Cheep Cheep speed attributes)

Hence I ask: with the simplified animation coding for the flying Cheep Cheeps, may I please find out which attributes should I change under FlyCCXSpeedData and InitFlyingCheepCheep to make them fly as high as touching the score display (as in the NTSC release)?

Here's the PAL-A specific attributes:

Code: Select all

FlyCCXSpeedData:
      .byte $0e, $05, $06, $0e      ;original NTSC attributes
      .byte $1c, $20, $10, $0c
      .byte $1e, $22, $18, $14
         
      .byte $11, $07, $08, $0a      ;PAL diff: Faster speed to compensate FPS difference
      .byte $23, $28, $15, $10
      .byte $22, $2c, $1f, $1b
and

Code: Select all

InitFlyingCheepCheep:
         lda FrenzyEnemyTimer       ;if timer here not expired yet, branch to leave
         bne ChpChpEx
         jsr SmallBBox              ;jump to set bounding box size $09 and init other values
         lda PseudoRandomBitReg+1,x
         and #%00000011             ;set pseudorandom offset here
         tay
         lda FlyCCTimerData,y       ;load timer with pseudorandom offset
         sta FrenzyEnemyTimer
         ldy #$03                   ;load Y with default value
         lda SecondaryHardMode
         beq MaxCC                  ;if secondary hard mode flag not set, do not increment Y
         iny                        ;otherwise, increment Y to allow as many as four onscreen
MaxCC:   sty $00                    ;store whatever pseudorandom bits are in Y
         cpx $00                    ;compare enemy object buffer offset with Y
         bcs ChpChpEx               ;if X => Y, branch to leave
         lda PseudoRandomBitReg,x
         and #%00000011             ;get last two bits of LSFR, first part
         sta $00                    ;and store in two places
         sta $01
         lda #$fa                   ;set vertical speed for cheep-cheep;PAL diff: Faster speed to compensate FPS difference (was $fb for NTSC)
         sta Enemy_Y_Speed,x
         lda #$00                   ;load default value
         ldy Player_X_Speed         ;check player's horizontal speed
         beq GSeed                  ;if player not moving left or right, skip this part
         lda #$04
         cpy #$1d                   ;PAL diff: Faster speed cutoffs to compensate FPS difference (was $19 for NTSC)
         bcc GSeed                  ;if moving to the right but not very quickly, do not change A
         asl                        ;otherwise, multiply A by 2

Thank you,



Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

In Super Mario Bros. 2 (FDS), the ChkToStunEnemies routine has been updated slightly to this.

Code: Select all

ChkToStunEnemies:
           lda Enemy_ID,x
           cmp #$09                   ;perform many comparisons on enemy object identifier
           bcc NoDemote               ;if the enemy object identifier is equal to the values
           cmp #$11                   ;$0e-$10 it will be demoted, in practice $0e and $10
           bcs NoDemote               ;are values used by green paratroopas
           cmp #PiranhaPlant          
           beq NoDemote               ;enemy objects $0a-$0d will not be demoted
           cmp #UpsideDownPiranhaP
           beq NoDemote
           cmp #$0a                   ;demote enemy object $09 even though it is not used
           bcc Demote                 
           cmp #PiranhaPlant
           bcc NoDemote
Demote:    and #%00000001             ;erase all but LSB, essentially turning enemy object
           sta Enemy_ID,x             ;into green or red koopa troopa to demote them
NoDemote:  cmp #PowerUpObject
           beq BounceOff              ;if power-up object, branch to bounce it
           cmp #Goomba
           beq BounceOff              ;redundant, already checked for goomba
           lda #$02                   
           sta Enemy_State,x          ;set enemy state to 2 (stunned)
BounceOff: dec Enemy_Y_Position,x
           dec Enemy_Y_Position,x     ;subtract two pixels from enemy's vertical position
           lda Enemy_ID,x
           cmp #Bloober               ;check for bloober object
           beq SetWYSpd
           lda #$fd                   ;set default vertical speed
           ldy AreaType
           bne SetNotW                ;if area type not water, set as speed, otherwise
SetWYSpd:  lda #$ff                   ;change the vertical speed
SetNotW:   sta Enemy_Y_Speed,x        ;set vertical speed now
           ldy #$01
           jsr PlayerEnemyDiff        ;get horizontal difference between player and enemy object
           bpl ChkBBill               ;branch if enemy is to the right of player
           iny                        ;increment Y if not
ChkBBill:  lda Enemy_ID,x      
           cmp #BulletBill_CannonVar  ;check for bullet bill (cannon variant)
           beq NoCDirF
           cmp #BulletBill_FrenzyVar  ;check for bullet bill (frenzy variant)
           beq NoCDirF                ;branch if either found, direction does not change
           sty Enemy_MovingDir,x      ;store as moving direction
NoCDirF:   dey                        ;decrement and use as offset
           lda EnemyBGCXSpdData,y     ;get proper horizontal speed
           sta Enemy_X_Speed,x        ;and store, then leave
ExEBGChk:  rts

To improve SMB1's original version to match, do this:

Code: Select all

ChkToStunEnemies:
           lda Enemy_ID,x
           cmp #$09                   ;perform many comparisons on enemy object identifier
           bcc SetStun                ;if the enemy object identifier is equal to the values
           cmp #$11                   ;$0e, $0f or $10 it will be demoted, in practice $0e and $10
           bcs SetStun                ;are values used by green paratroopas
           cmp #PiranhaPlant          
           beq SetStun                ;enemy objects $0a-$0d will not be demoted
           cmp #$0a                   ;demote enemy object $09 even though it is not used
           bcc Demote
           cmp #PiranhaPlant
           bcc SetStun
Demote:    and #%00000001             ;erase all but LSB, essentially turning enemy object
           sta Enemy_ID,x             ;into green or red koopa troopa to demote them
SetStun:   cmp #PowerUpObject
           beq BounceOff              ;if power-up object, branch to bounce it  
           lda #$02
           sta Enemy_State,x          ;set enemy state to 2 (stunned)
BounceOff: dec Enemy_Y_Position,x
           dec Enemy_Y_Position,x     ;subtract two pixels from enemy's vertical position
           lda Enemy_ID,x
           cmp #Bloober               ;check for bloober object
           beq SetWYSpd
           lda #$fd                   ;set default vertical speed
           ldy AreaType
           bne SetNotW                ;if area type not water, set as speed, otherwise
SetWYSpd:  lda #$ff                   ;change the vertical speed
SetNotW:   sta Enemy_Y_Speed,x        ;set vertical speed now
           ldy #$01
           jsr PlayerEnemyDiff        ;get horizontal difference between player and enemy object
           bpl ChkBBill               ;branch if enemy is to the right of player
           iny                        ;increment Y if not
ChkBBill:  lda Enemy_ID,x      
           cmp #BulletBill_CannonVar  ;check for bullet bill (cannon variant)
           beq NoCDirF
           cmp #BulletBill_FrenzyVar  ;check for bullet bill (frenzy variant)
           beq NoCDirF                ;branch if either found, direction does not change
           sty Enemy_MovingDir,x      ;store as moving direction
NoCDirF:   dey                        ;decrement and use as offset
           lda EnemyBGCXSpdData,y     ;get proper horizontal speed
           sta Enemy_X_Speed,x        ;and store, then leave
ExEBGChk:  rts
~Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

I finally corrected the end castle glitches for both Worlds 6-1 and 8-3, which means no more flagpole cut-offs. Screenshots included:

World 6-1:
Image

World 8-3:
Image

To fix these two, respectively, change these bytes (last two in 6-1, last four in 8-3) before the $fd.

Under L_GroundArea15 (world 6-1), change from:

Code: Select all

$0f, $a6
to

Code: Select all

$ef, $26
Under L_GroundArea2 (world 8-3), change from:

Code: Select all

$be, $42, $ef, $20
to

Code: Select all

$7e, $42, $af, $20
~Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

I would like to tweak SMB1 to add the Red Piranha Plants from The Lost Levels, for Worlds 6, 7 and 8 as well as in Worlds 1 thru 5 after you beat the game which triggers the hard mode for all eight Worlds. Could you please review the following modifications I made in the code below, to see if the changes I describe will actually work?

Code: Select all

;--------------------------------
;$00 - used to store piranha plant attribute data
;$01 - used to store piranha plant range data for player

InitPiranhaPlant:
         lda #$22                     ;set default attribute and range data here
         sta $00                      ;range data is used to detect how close player is
         lda #$13                     ;to the piranha plant to keep it inside the pipe
         sta $01                      ;default data makes red piranha plants
         lda PrimaryHardMode          ;check to see if primary hard mode has been activated
         bne PatchPP                  ;if so, use default data no matter where we're at
         lda WorldNumber              ;otherwise check world number
         cmp #World6                  ;if less than 6, make green piranha plant instead
         bcc InitEnemyFrenzy
         bne PatchPP                  ;if not equal to, then world > 6, thus activate
         dec $00
         lda #$21                     
         sta $01
PatchPP: lda $00
         sta EnemyAttributeData+PiranhaPlant  ;patch over attribute and range data
         lda $01
         sta ChkPlayerNearPipe+3
         lda #$01                     ;set initial speed
         sta PiranhaPlant_Y_Speed,x
         lsr
         sta Enemy_State,x            ;initialize enemy state and what would normally
         sta PiranhaPlant_MoveFlag,x  ;be used as vertical speed, but not in this case
         lda Enemy_Y_Position,x
         sta PiranhaPlantDownYPos,x   ;save original vertical coordinate here
         sec
         sbc #$18
         sta PiranhaPlantUpYPos,x     ;save original vertical coordinate - 24 pixels here
         lda #$09
         jmp SetBBox2                 ;set specific value for bounding box control
Thank you,


Ben (SMB2J-2Q)
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: SMB1 Hacking

Post by Pokun »

Heh, reminds me of All Night Nippon Super Mario Bros, except in that case all flowers (or "Pakkun Okapii") behaves like the red Pakkun regardless of world, difficulty and palette used for it. It almost makes me think it was a mistake when Nintendo modified SMB2 by inserting SMB1 levels to create ANNSMB, by accidentally using the red Pakkun instead of the green one, but it could be deliberate.
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

Pokun wrote: Mon Dec 05, 2022 11:59 am Heh, reminds me of All Night Nippon Super Mario Bros, except in that case all flowers (or "Pakkun Okapii") behaves like the red Pakkun regardless of world, difficulty and palette used for it. It almost makes me think it was a mistake when Nintendo modified SMB2 by inserting SMB1 levels to create ANNSMB, by accidentally using the red Pakkun instead of the green one, but it could be deliberate.
Yes, that is the behavior I want the plants to have for Worlds 6, 7 and 8 plus the entire Hard Mode setting (remember that the first hard mode appears in World 5-3, but the first level with the Piranhas following this is 6-1)... for them to be speedy and to only appear until Mario stands next to their pipes.

~Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

Just to give you folks an update as to what I tried to do... besides trying to add a flag for secondary hard mode to make them go faster while still green, but when I changed the value for the speed from LDA #$01 to LDA #$02 (under "InitPiranhaPlant"), nothing happened (they just disappeared).

Therefore I ask: what is really making them go up and down, and what would I change to make them go faster after the hard mode flag is set?

This is really bad for the PAL 50 Hz version (Europe, U.K., Republic of Ireland and Australia), since as there was no speed correction applied to the green Piranhas, they are consequently slower.

UPDATE 2 (12/8/22): I found out I can make the plants move faster by changing the following:

Code: Select all

ChkPlayerNearPipe:
      lda $00                     ;get saved horizontal difference
      cmp #$13
      bcc PutinPipe               ;if player within a certain distance, branch to leave
and

Code: Select all

RiseFallPiranhaPlant:
      sta $00                     ;save vertical coordinate here
      lda FrameCounter            ;get frame counter
      lsr
      nop
      nop
      lda TimerControl            ;get master timer control
      bne PutinPipe               ;branch to leave if set (likely not necessary)
      lda Enemy_Y_Position,x      ;get current vertical coordinate
      clc
      adc PiranhaPlant_Y_Speed,x  ;add vertical speed to move up or down
      sta Enemy_Y_Position,x      ;save as new vertical coordinate
      cmp $00                     ;compare against low or high coordinate
      bne PutinPipe               ;branch to leave if not yet reached
      lda #$00
      sta PiranhaPlant_MoveFlag,x ;otherwise clear movement flag
      lda #$40
      sta EnemyFrameTimer,x       ;set timer to delay piranha plant movement
The first part of the changed code under ChkPlayerNearPipe will not work unless the bcc under RiseFallPiranhaPlant is changed to something else.

To make the green Piranha Plants in the PAL version closely match the NTSC version's normal speed, change the cmp #$21 under ChkPlayerNearPipe to cmp #$1b. For the faster speed associated with the red plants in SMB2J, you would change this from cmp #$1b to cmp #$0f.

~Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: Super Mario Bros. (NES) -- 1-Up Mushroom Sound Fix

Post by SMB2J-2Q »

Dwedit wrote: Wed Sep 21, 2022 10:17 pm Just change the code? You have everything in front of you there.

Right before the "lda #Sfx_PowerUpGrab" line, you add another "lda PowerUpType", then move "cmp #$03", "beq SetFor1Up" immediately after that.

Some of the comments in "Shroom_Flower_PUp" that say that "ldx ObjectOffset" was not necessary, if that's true, you can remove one of those, and get the 2 bytes back.

----

As for how it's making the 1up sound play in the first place, "SetFor1Up" is writing byte 0B to somewhere in FloateyNum to change the 1000 point bonus to a 1up.
Then there is code elsewhere (look at DecNumTimer) that is checking that byte, adding a life, then making it play Sfx_ExtraLife.
While I know what you had suggested worked perfectly (to write another Check Power Up instruction), I also wonder if it might be possible to modify the code like this?

That is, it would start off with the check for the 1-UP Mushroom first (CMP #3) as you suggested initially, but I would follow that by subtracting 1 from that particular value in the accumulator (SEC + SBC #1) to compare it against the value for the normal Mushroom or Fire Flower (CMP #2) and, if valid, to BCS it to the Shroom_Flower_PUp routine.

Code: Select all

HandlePowerUpCollision:
      jsr EraseEnemyObject    ;erase the power-up object
      lda #$06
      jsr SetupFloateyNumber  ;award 1000 points to player by default
      lda PowerUpType         ;check power-up type
      cmp #$03
      beq SetFor1Up           ;if 1-up mushroom, branch
      sec
      sbc #$01                ;otherwise subtract 1 from accumulator to get value for power-up mushroom or fire flower
      lda #Sfx_PowerUpGrab
      sta Square2SoundQueue   ;play the power-up sound
      cmp #$02                
      bcs Shroom_Flower_PUp   ;if found, branch
      lda #$23                ;otherwise set star mario invincibility
      sta StarInvincibleTimer ;timer, and load the star mario music
      lda #StarPowerMusic     ;into the area music queue, then leave
      sta AreaMusicQueue
      rts
~Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

OK. I was playing Super Mario All-Stars, and I found out that the invisible platform bug that was present in the NES and FDS versions of SMB1 and the FDS version of The Lost Levels has indeed been fixed in both respective titles in the SNES version, which means the TMK website got it wrong about SMAS not fixing the invisible platform bug ("World 4-3 Glitch Fest"). When Mario stands on one set of these balance platforms, the other set of platforms no longer moves. Screenshots to be provided here for confirmation.

In World 4-3:
Image

Image

In World 6-3:
Image

Image

Another bug in the NES/FDS versions that was fixed in SMAS was the Koopa shell to Hammer Brother collision, meaning the shell actually hits the Hammer Brother from the front. In the NES/FDS version, the shell doesn't hit the Hammer Brother from the front, but only in the back.
Image

May I ask you how I would correct these flaws in the NES SMB1, please? What parts of doppelganger's code would I be changing, respectively?

~Ben
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: SMB1 Hacking

Post by Pokun »

I'm not familiar with the platform bugs, but I'm pretty sure the Hammer Bros bug is fixed in SMB2 and ANNSMB, as it's easier to defeat them with shells in those two FDS games. Maybe you could check SMB2 and see what it does differently.
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

Pokun wrote: Sun Jan 01, 2023 12:48 pm I'm not familiar with the platform bugs, but I'm pretty sure the Hammer Bros bug is fixed in SMB2 and ANNSMB, as it's easier to defeat them with shells in those two FDS games. Maybe you could check SMB2 and see what it does differently.
I've been told that the invisible platform bug was due to sprite limitations associated with the original long lifts. Perhaps, this code will need to be tweaked to make it better comply within these boundaries.

~Ben
SMB2J-2Q
Posts: 152
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

I finally was able to fix the problem of Fire Mario dying by time-up and still in the Fire Mario colors:

Code: Select all

KillPlayer:
      stx Player_X_Speed   ;halt player's horizontal movement by initializing speed
      lda #$01
      sta PlayerSize       ;set player's size to small
      jsr GetPlayerColors  ;when player dies by time-up, if fiery, jump to revert back to normal Mario color palette
      inx
      stx EventMusicQueue  ;set event music queue to death music
      lda #$fc
      sta Player_Y_Speed   ;set new vertical speed
      lda #$0b             ;set subroutine to run on next frame
      bne SetKRout         ;branch to set player's state and other things
What I did was to add these three lines of code after the leading STX instruction and before the INX:

Code: Select all

lda #$01
sta PlayerSize       ;set player's size to small
jsr GetPlayerColors  ;when player dies by time-up, if fiery, jump to revert back to normal Mario color palette
UPDATE: The possessed pulley/invisible platform bug in World 4-3 has partially been fixed. For now, refer to the modified code below.
https://www.romhacking.net/forum/index. ... #msg439539

To make it work for the NES, go to PlatformFall and do this:

Code: Select all

PlatformFall:
      tya                         ;save offset for other platform to stack
      pha
      jsr MoveFallingPlatform     ;make current platform fall
      pla
      tax                         ;pull offset from stack and save to X
      lda Enemy_State,x
      bpl RightPlatformFall
      jsr MoveFallingPlatform     ;make other platform fall
      
RightPlatformFall:
      ldx ObjectOffset
      lda PlatformCollisionFlag,x ;if player not standing on either platform,
      bmi ExPF                    ;skip this part
      tax                         ;transfer collision flag offset as offset to X
      jsr PositionPlayerOnVPlat   ;and position player appropriately
ExPF: ldx ObjectOffset            ;get enemy object buffer offset and leave
      rts
~Ben
User avatar
TakuikaNinja
Posts: 87
Joined: Mon Jan 09, 2023 6:42 pm
Location: New Zealand
Contact:

Re: SMB1 Hacking

Post by TakuikaNinja »

SMB2J-2Q wrote: Sun Jan 01, 2023 8:08 pm I've been told that the invisible platform bug was due to sprite limitations associated with the original long lifts. Perhaps, this code will need to be tweaked to make it better comply within these boundaries.

~Ben
I'm the one who said that. However, I seem to have misunderstood what the bug is referring to. Sprites disappearing is an issue, but it's not related to the hitboxes of the falling platforms doing weird things to the player. My bad.
User avatar
TakuikaNinja
Posts: 87
Joined: Mon Jan 09, 2023 6:42 pm
Location: New Zealand
Contact:

Re: SMB1 Hacking

Post by TakuikaNinja »

I've been working on my own bugfix hack with a few others using the asm6 version of the disassembly. Most of the work was done independently from this thread but I plan on implementing the changes discussed here.
https://github.com/TakuikaNinja/smb1-bugfix

The most substantial change by far is the overhaul of the NMI handler. Now the game logic is handled outside of the NMI handler and lag frames are properly accounted for. Glitched scanlines and HUD flickering is no more.
Post Reply