SMB1 Hacking

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

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

SMB1 Hacking

Post by SMB2J-2Q »

I've been getting better at learning how to use X816 to hack SMB1.

Most recently, I "cleaned up" the code that deals with the enemy and area object databases. Instead of the cumbersome .db low and high byte labeling, I did a .dw of the areas, grouped by area type.

My work so far:

Code: Select all

EnemyDataHOffsets:
    .db (E_WaterAreas-EnemyDataOffsets)>>1, (E_GroundAreas-EnemyDataOffsets)>>1
    .db (E_UndergroundAreas-EnemyDataOffsets)>>1, (E_CastleAreas-EnemyDataOffsets)>>1
    
EnemyDataOffsets:
    E_CastleAreas:
        .dw E_CastleArea1, E_CastleArea2, E_CastleArea3, E_CastleArea4, E_CastleArea5, E_CastleArea6
    E_GroundAreas:
        .dw E_GroundArea1, E_GroundArea2, E_GroundArea3, E_GroundArea4, E_GroundArea5, E_GroundArea6
        .dw E_GroundArea7, E_GroundArea8, E_GroundArea9, E_GroundArea10, E_GroundArea11, E_GroundArea12
        .dw E_GroundArea13, E_GroundArea14, E_GroundArea15, E_GroundArea16, E_GroundArea17, E_GroundArea18
        .dw E_GroundArea19, E_GroundArea20, E_GroundArea21, E_GroundArea22
    E_UndergroundAreas:
        .dw E_UndergroundArea1, E_UndergroundArea2, E_UndergroundArea3
    E_WaterAreas:
        .dw E_WaterArea1, E_WaterArea2, E_WaterArea3

Code: Select all

        
AreaDataHOffsets:
    .db (L_WaterAreas-AreaDataOffsets)>>1, (L_GroundAreas-AreaDataOffsets)>>1
    .db (L_UndergroundAreas-AreaDataOffsets)>>1, (L_CastleAreas-AreaDataOffsets)>>1
    
AreaDataOffsets:
    L_WaterAreas:
        .dw L_WaterArea1, L_WaterArea2, L_WaterArea3
    L_GroundAreas:
        .dw L_GroundArea1, L_GroundArea2, L_GroundArea3, L_GroundArea4, L_GroundArea5, L_GroundArea6
        .dw L_GroundArea7, L_GroundArea8, L_GroundArea9, L_GroundArea10, L_GroundArea11, L_GroundArea12
        .dw L_GroundArea13, L_GroundArea14, L_GroundArea15, L_GroundArea16, L_GroundArea17, L_GroundArea18
        .dw L_GroundArea19, L_GroundArea20, L_GroundArea21, L_GroundArea22
    L_UndergroundAreas:
        .dw L_UndergroundArea1, L_UndergroundArea2, L_UndergroundArea3
    L_CastleAreas:
        .dw L_CastleArea1, L_CastleArea2, L_CastleArea3, L_CastleArea4, L_CastleArea5, L_CastleArea6
I then changed the code under GetAreaDataAddrs in order to redirect it to the new labels:

Code: Select all

GetAreaDataAddrs:
            lda AreaPointer          ; use 2 MSB for Y
            jsr GetAreaType
            tay
            lda AreaPointer          ; mask out all but 5 LSB
            and #%00011111
            sta AreaAddrsOffset      ; save as low offset
            lda EnemyAddrHOffsets,y  ; load base value with 2 altered MSB,
            clc                      ; then add base value to 5 MSB, result
            adc AreaAddrsOffset      ; becomes offset for level data
            tay
            lda EnemyDataOffsets,y   ; use offset to load pointer
            sta EnemyDataLow
            lda EnemyDataOffsets+1,y
            sta EnemyDataHigh
            ldy AreaType             ; use area type as offset
            lda AreaAddrHOffsets,y   ; do the same thing but with different base value
            clc
            adc AreaAddrsOffset
            tay
            lda AreaDataOffsets,y
            sta AreaDataLow
            lda AreaDataOffsets+1,y
            sta AreaDataHigh
            [...]
However, when I last compiled this code (using the Github version of the SMB1 disassembly file), after taking out the two DoNothing instructions and then extending the HandlePipeEntry code found by DisplacedGamers (referring to what he found in Super Mario All-Stars), it was 13 bytes too long for comfort. This is the reason why I wish to split up the program into two banks, one covering Worlds 1-4 and the second covering Worlds 5-8, just as in Super Mario Bros. 2 (The Lost Levels) and All Night Nippon Super Mario Bros.

Thank you,



Ben (SMB2J-2Q)
Bavi_H
Posts: 158
Joined: Sun Mar 03, 2013 1:52 am
Location: Texas, USA
Contact:

Re: SMB1 Hacking

Post by Bavi_H »

Here is some context:
SMB2J-2Q
Posts: 25
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

Bavi_H wrote: Tue Aug 30, 2022 9:13 am Here is some context:
Yes, I am aware of all of that for what I did.

~Ben
Oziphantom
Posts: 1350
Joined: Tue Feb 07, 2017 2:03 am

Re: SMB1 Hacking

Post by Oziphantom »

yeah but nobody else was :D

looking at the code it would be trivial to port it to 64tass so you can avoid having to use doxbox and assemble at full speed with a lot more powerful assembler.
SMB2J-2Q
Posts: 25
Joined: Thu Jul 27, 2017 5:13 pm

Code Cleanups/Optimizations for SMB1/SMB2J

Post by SMB2J-2Q »

I have been fascinated with ShaneM's work on his hybrid SMB1/2J hack, and therefore I thus ask: may I please find out what the latest build/download is for this hack, so that I can try to find all the cleanups/optimizations to date that he'd found and used for it, that he took from both the PAL version of SMB1 as well as from Super Mario All-Stars for the SNES???
viewtopic.php?t=11576

autoreverse's SMB1 EU disassembly topic is another thing of interest to me regarding these code cleanups/optimizations:
viewtopic.php?t=9519

For example, I did find one thing odd with the ForceInjury code for our NTSC version of SMB1:

Code: Select all

ForceInjury:
    ldx PlayerStatus
    beq KillPlayer
    sta PlayerStatus       ;otherwise, set player's status to small
    lda #$08
    sta InjuryTimer        ;set injury timer
    asl
    sta Square1MusicQueue  ;play pipedown/injury sound
    [...]

What the ASL instruction in the NTSC version is doing: it multiplies the #$08 stored in A by two, thus becoming #$10 and therefore allowing the pipedown/injury sound to play, but saving one byte.

UPDATE: I found this topic on the old ACMLM board, in particular the last reply by ShaneM, to be quite handy in cleaning up the code for SMB1. The first instruction he tells us, which frees up to 200+ bytes, was what allowed me to incorporate Hamtaro126's 99 lives code and the extension of the HandlePipeEntry code from SMAS (by DisplacedGamers) perfectly, along with many of the fixes present in the PAL version of SMB1 and in SMAS, without the need for a second bank.
http://acmlm.kafuka.org/board/thread.ph ... 724#158724
But, it's when he talks about the cluttered-up level data, in the form of four examples in pictures (using SMB Utility to describe where each of these objects are), that gets cloudy because he used to have a Photobucket account where he'd hosted them, and obviously they are no longer available. I wonder exactly what he's referring to (removal of unnecessary bricks and things other in certain levels, that in this case takes up to 150+ bytes)?

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

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

Post by SMB2J-2Q »

Does anyone know how I can fix the 1-Up get sound so that it does not play the first few notes of the power-up get sound, which in this case is whenever you collect a 1-UP Mushroom? This was fixed in Super Mario All-Stars.

Code: Select all

HandlePowerUpCollision:
    jsr EraseEnemyObject    ;erase the power-up object
    lda #$06
    jsr SetupFloateyNumber  ;award 1000 points to player by default
    lda #Sfx_PowerUpGrab
    sta Square2SoundQueue   ;play the power-up sound
    lda PowerUpType         ;check power-up type
    cmp #$02
    bcc Shroom_Flower_PUp   ;if mushroom or fire flower, branch
    cmp #$03
    beq SetFor1Up           ;if 1-up mushroom, 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

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

SetFor1Up:
      lda #$0b                 ;change 1000 points into 1-up instead
      sta FloateyNum_Control,x ;and then leave
      rts
You can see here there that in this particular routine the specific instruction to play the 1-UP collection sound is not present, which would be given out as:

Code: Select all

lda #SFX_ExtraLife
sta Square2SoundQueue
UPDATE: I finally got this to work right, by inserting the above two instructions after the sta FloateyNum_Control,x and before the rts under the SetFor1Up routine:

Code: Select all

SetFor1Up:
    lda #$0b                 ;change 1000 points into 1-up instead
    sta FloateyNum_Control,x
    lda #SFX_ExtraLife
    sta Square2SoundQueue ;play 1-up sound and then leave
    rts


~Ben
User avatar
Dwedit
Posts: 4687
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

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

Post by Dwedit »

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.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
SMB2J-2Q
Posts: 25
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.
I found two such notes there, one for turning into Super Mario and the other for turning into Fire Mario. I believe the first one should remain because the first indicates Mario growing from small to Super.

Per your suggestions, the modified code would be:

Code: Select all

HandlePowerUpCollision:
    jsr EraseEnemyObject
    lda #$06
    jsr SetupFloateyNumber
    lda PowerUpType
    cmp #$03
    beq SetFor1Up
    lda #SFX_PowerUpGrab
    sta Square2SoundQueue
    lda PowerUpType
    cmp #$02
    bcc Shroom_Flower_PUp
    lda #$23
    sta StarInvincibleTimer
    lda #StarPowerMusic
    sta AreaMusicQueue
    rts
    
Shroom_Flower_PUp:
    lda PlayerStatus
    beq UpToSuper
    cmp #$01
    bne NoPUp
    ldx ObjectOffset
    lda #$02
    sta PlayerStatus
    jsr GetPlayerColors
    lda #$0c
    jmp UpToFiery
    
SetFor1Up:
    lda #$0b
    sta FloateyNum_Control,x
    rts
UPDATE: Your suggestion also worked, and as with my suggestion, the 1-UP Mushroom does not have any residual notes of the PowerUpGrab jingle playing.

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

SMB1 - Add Time Bonus for Castle?

Post by SMB2J-2Q »

I wonder if the designers of Super Mario Bros. had intended for each of the x-4 (castle) levels to have a time bonus award as well, but that due to the program's 32K ROM size, it was cut out?

Here's the specific data I found for the x-4 time bonus award for Super Mario Bros. 2 (FDS), which comes after the PrintVictoryMessages routine:

Code: Select all

EndCastleAward:
  lda WorldEndTimer
  cmp #$06
  bcs ExEWA
  jsr AwardTimerCastle
  lda GameTimerDisplay
  ora GameTimerDisplay+1
  ora GameTimerDisplay+2
  bne ExEWA
  lda #$30
  sta SelectTimer        ;set select timer (used for World 8's ending only)
  lda #$06
  sta WorldEndTimer
  inc OperMode_Task
ExEWA:
  rts
Under AwardGameTimerPoints, a label for AwardTimerCastle is placed before the "lda FrameCounter" instruction:

Code: Select all

AwardGameTimerPoints:
  lda GameTimerDisplay
  ora GameTimerDisplay+1
  ora GameTimerDisplay+2
  beq IncrementSFTask1
AwardTimerCastle:
  lda FrameCounter
  and #%00000100
  beq NoTTick
  lda #SFX_TimerTick
  sta Square2SoundQueue
NoTTick:
  ldy #$17
  lda #$ff
  sta DigitModifier+5
  jsr DigitsMathRoutine
  lda #$05
  sta DigitModifier+5
I wonder if these modifications alone might make it work for the NES SMB1?

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

SMB1 - Starman Music vs. Flagpole Music Fix

Post by SMB2J-2Q »

https://www.youtube.com/watch?v=SJ7Or3MfgRI

How would I be able to correct this particular glitch, please?

The above video details this glitch, and how I wish to get it fixed (a la Super Mario All-Stars).

I am going to paste some relevant code here in order to determine where this check should go.

Code: Select all

ProcELoop:    stx ObjectOffset           ;put incremented offset in X as enemy object offset
              jsr EnemiesAndLoopsCore    ;process enemy objects
              jsr FloateyNumbersRoutine  ;process floatey numbers
              inx
              cpx #$06                   ;do these two subroutines until the whole buffer is done
              bne ProcELoop
              jsr GetPlayerOffscreenBits ;get offscreen bits for player object
              jsr RelativePlayerPosition ;get relative coordinates for player object
              jsr PlayerGfxHandler       ;draw the player
              jsr BlockObjMT_Updater     ;replace block objects with metatiles if necessary
              ldx #$01
              stx ObjectOffset           ;set offset for second
              jsr BlockObjectsCore       ;process second block object
              dex
              stx ObjectOffset           ;set offset for first
              jsr BlockObjectsCore       ;process first block object
              jsr MiscObjectsCore        ;process misc objects (hammer, jumping coins)
              jsr ProcessCannons         ;process bullet bill cannons
              jsr ProcessWhirlpools      ;process whirlpools
              jsr FlagpoleRoutine        ;process the flagpole
              jsr RunGameTimer           ;count down the game timer
              jsr ColorRotation          ;cycle one of the background colors
              lda Player_Y_HighPos
              cmp #$02                   ;if player is below the screen, don't bother with the music
              bpl NoChgMus
              lda StarInvincibleTimer    ;if star mario invincibility timer at zero,
              beq ClrPlrPal              ;skip this part
              cmp #$04
              bne NoChgMus               ;if not yet at a certain point, continue
              lda IntervalTimerControl   ;if interval timer not yet expired,
              bne NoChgMus               ;branch ahead, don't bother with the music
              jsr GetAreaMusic           ;to re-attain appropriate level music
NoChgMus:     ldy StarInvincibleTimer    ;get invincibility timer
              lda FrameCounter           ;get frame counter
              cpy #$08                   ;if timer still above certain point,
              bcs CycleTwo               ;branch to cycle player's palette quickly
              lsr                        ;otherwise, divide by 8 to cycle every eighth frame
              lsr

Code: Select all

FlagpoleCollision:
      lda GameEngineSubroutine
      cmp #$05                  ;check for end-of-level routine running
      beq PutPlayerOnVine       ;if running, branch to end of climbing code
      lda #$01
      sta PlayerFacingDir       ;set player's facing direction to right
      inc ScrollLock            ;set scroll lock flag
      lda GameEngineSubroutine
      cmp #$04                  ;check for flagpole slide routine running
      beq RunFR                 ;if running, branch to end of flagpole code here
      lda #BulletBill_CannonVar ;load identifier for bullet bills (cannon variant)
      jsr KillEnemies           ;get rid of them
      lda #Silence
      sta EventMusicQueue       ;silence music
      lsr
      sta FlagpoleSoundQueue    ;load flagpole sound into flagpole sound queue
      ldx #$04                  ;start at end of vertical coordinate data
      lda Player_Y_Position
      sta FlagpoleCollisionYPos ;store player's vertical coordinate here to be used later
Thank you,



Ben
frantik
Posts: 373
Joined: Tue Mar 03, 2009 3:56 pm

Re: SMB1 - Starman Music vs. Flagpole Music Fix

Post by frantik »

you can probably get away with just adding this to the top of FlagpoleCollision:

Code: Select all

   lda #$04
   sta StarInvincibleTimer
when the star timer reaches 4 it disables the color cycling and turns off the starman music

In Super Mario Special I added this near the top of FlagpoleSlide: but I don't even think you need to check if the timer is 0

Code: Select all

   lda StarInvincibleTimer
   beq +
   lda #$04
   sta StarInvincibleTimer
+
btw you might consider just making a single thread for all your SMB questions
SMB2J-2Q
Posts: 25
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 - Starman Music vs. Flagpole Music Fix

Post by SMB2J-2Q »

frantik wrote: Fri Sep 23, 2022 11:49 pm you can probably get away with just adding this to the top of FlagpoleCollision:

Code: Select all

   lda #$04
   sta StarInvincibleTimer
when the star timer reaches 4 it disables the color cycling and turns off the starman music

In Super Mario Special I added this near the top of FlagpoleSlide: but I don't even think you need to check if the timer is 0

Code: Select all

   lda StarInvincibleTimer
   beq +
   lda #$04
   sta StarInvincibleTimer
+
btw you might consider just making a single thread for all your SMB questions
@frantik,

I tried the first one and it didn't quite work, going straight to the current area music upon getting on the pole. What I want to do is for it to not switch back to the area music when the Starman wears off, meaning once Mario reached the flagpole.

For your information, here's the FlagpoleSlide routine, as originally coded:

Code: Select all

    lda Enemy_ID+5
    cmp #FlagpoleFlagObject
    bne NoFPObj
    lda FlagpoleSoundQueue
    sta Square1SoundQueue
    lda #$00
    sta FlagpoleSoundQueue
    ldy Player_Y_Position
    cpy #$9e
    bcs SlidePlayer
    lda #$04
SlidePlayer:
    jmp AutoControlPlayer
NoFPObj:
    inc GameEngineSubroutine
    rts
I saw your code where you included it within SMB Special 35th; however, there is one LDX instruction (before the "LDA Enemy_ID,x") and two STA instructions containing out-of-bounds addresses ($7fec, $7ff9, $7ffa, etc.) coming after the "STA StarInvincibleTimer" and before the "LDA FlagpoleSoundQueue"; may I ask you what this LDX and the two STAs are supposed to do?

Code: Select all

    lda Enemy_ID,x
    cmp #FlagpoleFlagObject
    beq KillStarman
    jsr ResetPalStar
    lda StarInvincibleTimer
    beq $adbb
    lda #$04
    sta StarInvincibleTimer
$adbb: sta $7ffa
$adbe: sta $7ff9
    sta FlagpoleMusicQueue
Image


~Ben
Last edited by SMB2J-2Q on Sat Sep 24, 2022 3:31 pm, edited 5 times in total.
SMB2J-2Q
Posts: 25
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

At @frantik's suggestion, could a moderator please merge the following topics I'd created into this particular thread?

SMB1 - Starman Music vs. Flagpole Music Fix:
viewtopic.php?t=24158

SMB1 - Add Time Bonus for Castle?:
viewtopic.php?t=24154

Super Mario Bros. (NES) -- 1-Up Mushroom Sound Fix:
viewtopic.php?t=24153

SMB1 -- 5-3, 7-3 Change to Winter Graphics?:
viewtopic.php?t=24146

Code Cleanups/Optimizations for SMB1/SMB2J:
viewtopic.php?t=24143

Thank you,



Ben
Fiskbit
Posts: 518
Joined: Sat Nov 18, 2017 9:15 pm

Re: SMB1 Hacking

Post by Fiskbit »

I've merged them, except for Change to Winter Graphics? because the posts wouldn't be contiguous in this thread. That topic looks finished, though, so I think there's no harm in leaving it separate.
SMB2J-2Q
Posts: 25
Joined: Thu Jul 27, 2017 5:13 pm

Re: SMB1 Hacking

Post by SMB2J-2Q »

Fiskbit wrote: Sat Sep 24, 2022 8:33 am I've merged them, except for Change to Winter Graphics? because the posts wouldn't be contiguous in this thread. That topic looks finished, though, so I think there's no harm in leaving it separate.
I appreciate that.

~Ben
Post Reply