Super Mario Bros. 1 (NES) -- Handle Pipe Entry Fix?

Started by SMB2J-2Q, July 15, 2022, 11:49:04 AM

Previous topic - Next topic

SMB2J-2Q

In Super Mario All-Stars, I found some new code instructions that tell the game to send the player to the three intended worlds, if one tried doing the Minus World trick at the end of 1-2 where the goal pipe is:

This code I found is an extension of the existing HandlePipeEntry code in the original NES Super Mario Bros. 1, and would be written starting at line 12290 in Doppelganger's SMB1 disassembly file, with the rest of the code afterward continuing at line 12303 under the label CalculateWarpPipeOffset.

12290          cmp #$04                          ;if WarpZoneControl >= 4, continue
12291          bcs CalculateWarpPipeOffset
12292          ldx #$04
12293          lda WorldNumber
12294          beq RecalculateWarpPipeOffsetSave ;are we at World 1-2 (World 4-3-2 pipes)?
12295          inx                               ;increment to World 4-2 underground pipe (to World 5)
12296          lda AreaType
12297          dec a                             ;are we above ground?
12298          bne RecalculateWarpPipeOffsetSave ;branch if World 5 pipe
12299          inx                               ;otherwise, World 8-7-6 pipe
12300 RecalculateWarpPipeOffsetSave:
12301          txa
12302          sta WarpZoneControl               ;override invalid value
12303 CalculateWarpPipeOffset:
12304          and #$00000011                    ;mask out all but 2 LSB
12305          asl
12306          asl                               ;multiply by four
12307          tax                               ;save as offset to warp zone numbers (starts at left pipe)
12308          lda Player_X_Position             ;get player's horizontal position
12309          cmp #$60
12310          bcc GetWNum                       ;if player at left, but not near middle, use offset and skip ahead
12311          inx                               ;otherwise increment for last pipe
12312          cmp #$a0                         
12313          bcc GetWNum                       ;if player at middle, but not too far right, use offset and skip ahead
12314          inx
12315 GetWNum: ldy WarpZoneNumbers,x             ;get warp zone numbers
12316          dey                               ;decrement for use as world number
12317          sty WorldNumber                   ;store as world number and offset
12318          ldx WorldAddrOffsets,y            ;get offset to where this world's area offsets are
12319          lda AreaAddrOffsets,x             ;get area offset based on world offset
12320          sta AreaPointer                   ;store area offset here to be used to change areas
12321          lda #Silence
12322          sta EventMusicQueue               ;silence music
12323          lda $00
12324          sta EntrancePage                  ;initialize starting page number
12325          sta AreaNumber                    ;initialize area number used for area address offset
12326          sta LevelNumber                   ;initialize level number used for world display
12327          sta AltEntranceControl            ;initialize mode of entry
12328          inc Hidden1UPFlag                 ;set flag for hidden 1-UP blocks
12329          inc FetchNewGameTimerFlag         ;set flag to load new game timer
12330 ExPipeE: rts                               ;leave!!!       

But, there is one instruction not present in the original 6502 that is in the 65816 (and 65C02): DEC A ($3A), or decrement accumulator. Thanks to Cyneprepou4uk, he provided me with the correct replacement opcodes, now included below.

12296          lda AreaType
12297          sec
12298          sbc #$01                          ;are we above ground?
12299          bne RecalculateWarpPipeOffsetSave ;branch if World 5 pipe
12300          inx                               ;otherwise, World 8-7-6 pipe

Thank you,



Ben

Cyneprepou4uk

You can't TAX + DEX because X is already used here. Instead you can SEC + SBC #$01.

SMB2J-2Q

#2
Quote from: Cyneprepou4uk on July 15, 2022, 02:21:26 PMYou can't TAX + DEX because X is already used here. Instead you can SEC + SBC #$01.
Thank you.

12296       lda AreaType
12297       sec
12298       sbc #$01                          ;are we above ground?
12299       bne RecalculateWarpPipeOffsetSave ;branch if World 5 pipe
12300       inx                               ;otherwise, World 8-7-6 pipe

These modifications, and those listed above, better correlate with the instructions within the ScrollLockObject_Warp routine:
  3566 ScrollLockObject_Warp:
  3567          ldx #$04            ;load value of 4 for game text routine as default
  3568          lda WorldNumber     ;warp zone (4-3-2), then check world number
  3569          beq WarpNum
  3570          inx                 ;if world number > 1, increment for next warp zone (5)
  3571          ldy AreaType        ;check area type
  3572          dey
  3573          bne WarpNum         ;if ground area type, increment for last warp zone
  3574          inx                 ;(8-7-6) and move on
  3575 WarpNum: txa
  3576          sta WarpZoneControl ;store number here to be used by warp zone routine
                [...]               ;more code follows, but only this part is relevant.

~Ben

SMB2J-2Q

I wonder if there is any way that I can copy/paste the newfound code into the ROM using any hex editor (Translhextion, Hexecute), once I know what the correct bytes (hexdump) are to connect the dots of the code?

~Ben

Cyneprepou4uk


SMB2J-2Q

#5
Quote from: Cyneprepou4uk on July 16, 2022, 07:09:34 AMLiteral copy/pasting sounds like a good way.
I got started doing this using HxD hex editor, but I have a problem after loading the saved game, as in it won't start. What do I do next? Thank you!

I do understand that any new code I intend to add to this will enlarge the existing file size.

What I would really like to do, instead of hex edit, is use an ASM assembler after adding in the new lines of code to doppelganger's existing SMB1 ASM file. What ASM to ROM assembler do you use?

What I was trying to do was add in the extra code I mention above for the HandlePipeEntry routine, and I am aware of such branching opcodes which have these variable numbers depending on where the branch to is, and that is one of the reasons a hex editor can be difficult to use.

~Ben

Jorpho

FYI, it so happens there was a recent video documenting the various fixes made to the minus world "bug" over the years (except, alas, for SMB Deluxe).
https://www.youtube.com/watch?v=61m5MiyC17s

In brief, SMAS includes more than one code patch, and the fix it uses is considerably more complex than the one used in SMB2J.
This signature is an illusion and is a trap devisut by Satan. Go ahead dauntlessly! Make rapid progres!

SMB2J-2Q

Quote from: Jorpho on July 16, 2022, 11:06:11 PMFYI, it so happens there was a recent video documenting the various fixes made to the minus world "bug" over the years (except, alas, for SMB Deluxe).
https://www.youtube.com/watch?v=61m5MiyC17s

In brief, SMAS includes more than one code patch, and the fix it uses is considerably more complex than the one used in SMB2J.
Yes, I just saw that one recently, hence why I first started this topic. I do understand about him not including the Game Boy Color version you mention as the GB uses Z-80 programming logic rather than 6502.

But because it was for the SNES, it was written in the modified 65816 code, so to duplicate the DEC A instruction for the classic 6502 logic, I was told a few replies ago to use the combination of SEC and SBC #$01. I also included "SMAS bug fix" in my notes as to the added code so as to make clear this additional code was found in Super Mario All-Stars.

Therefore, when I re-write doppelganger's SMB1 disassembly to include this extension, this is how the fix would be applied:
HandlePipeEntry:
         lda Up_Down_Buttons       ;check saved controller bits from earlier
         and #%00000100            ;for pressing down
         beq ExPipeE               ;if not pressing down, branch to leave
         lda $00
         cmp #$11                  ;check right foot metatile for warp pipe right metatile
         bne ExPipeE               ;branch to leave if not found
         lda $01
         cmp #$10                  ;check left foot metatile for warp pipe left metatile
         bne ExPipeE               ;branch to leave if not found
         lda #$30
         sta ChangeAreaTimer       ;set timer for change of area
         lda #$03
         sta GameEngineSubroutine  ;set to run vertical pipe entry routine on next frame
         lda #Sfx_PipeDown_Injury
         sta Square1SoundQueue     ;load pipedown/injury sound
         lda #%00100000
         sta Player_SprAttrib      ;set background priority bit in player's attributes
         lda WarpZoneControl       ;check warp zone control
         beq ExPipeE               ;branch to leave if none found
         cmp #$04                  ;SMAS bug fix: if current value in WarpZoneControl >= 4, continue
         bcs CalculateWarpPOffset
         ldx #$04
         lda WorldNumber
         beq RecalcWPOffsetSave    ;are we at World 1-2 (4-3-2 pipes)?
         inx                       ;increment to World 4-2 underground warp pipe (5)
         lda AreaType              ;check area type
         sec
         sbc #$01                  ;are we above ground?
         bne RecalcWPOffsetSave    ;branch if we're still inside
         inx                       ;otherwise increment to World 4-2 outside warp pipes (8-7-6)
RecalcWPOffsetSave:
         txa
         sta WarpZoneControl       ;SMAS bug fix: override invalid value
CalculateWarpPOffset:
         and #%00000011            ;mask out all but 2 LSB
         asl
         asl                       ;multiply by four
         tax                       ;save as offset to warp zone numbers (starts at left pipe)
         lda Player_X_Position     ;get player's horizontal position
         cmp #$60     
         bcc GetWNum               ;if player at left, not near middle, use offset and skip ahead
         inx                       ;otherwise increment for middle pipe
         cmp #$a0     
         bcc GetWNum               ;if player at middle, but not too far right, use offset and skip
         inx                       ;otherwise increment for last pipe
GetWNum: ldy WarpZoneNumbers,x     ;get warp zone numbers
         dey                       ;decrement for use as world number
         sty WorldNumber           ;store as world number and offset
         ldx WorldAddrOffsets,y    ;get offset to where this world's area offsets are
         lda AreaAddrOffsets,x     ;get area offset based on world offset
         sta AreaPointer           ;store area offset here to be used to change areas
         lda #Silence
         sta EventMusicQueue       ;silence music
         lda #$00
         sta EntrancePage          ;initialize starting page number
         sta AreaNumber            ;initialize area number used for area address offset
         sta LevelNumber           ;initialize level number used for world display
         sta AltEntranceControl    ;initialize mode of entry
         inc Hidden1UpFlag         ;set flag for hidden 1-up blocks
         inc FetchNewGameTimerFlag ;set flag to load new game timer
ExPipeE: rts                       ;leave!!!

~Ben

Cyneprepou4uk

#8
QuoteI got started doing this using HxD hex editor, but I have a problem after loading the saved game, as in it won't start. What do I do next? Thank you!

I have no idea what exactly did you do, so I don't know what you should do next.

QuoteI do understand that any new code I intend to add to this will enlarge the existing file size.

If by enlarging you mean inserting (not overwriting) your bytes somewhere in the middle of the file, or adding bytes to the end of the file, then it's not how it works. Basically you need to create a subroutine at some existing free space and call it when needed.

QuoteWhat ASM to ROM assembler do you use?

ca65. But xkas-plus is better for this task.

SMB2J-2Q

Quote from: Cyneprepou4uk on July 17, 2022, 05:18:14 AMI have no idea what exactly did you do, so I don't know what you should do next.

If by enlarging you mean inserting (not overwriting) your bytes somewhere in the middle of the file, or adding bytes to the end of the file, then it's not how it works. Basically you need to create a subroutine at some existing free space and call it when needed.

ca65. But xkas-plus is better for this task.
Yes, the second paragraph matches exactly why my attempt failed.

And, I may try CA65 soon.

~Ben