(This week's article comes from a question asked in the RGMechEx Discord server. You can join it here!)
So here's a thing that me and a bunch of other people have noticed before: the breakable bricks in the Super Mario All-Stars version of Super Mario Bros. seem less firm than in the original NES release. That is, they don't seem to pack as big of a punch to Mario's noggin when he hits one from below. In fact, if you just barely bump a block at the apex of your jump in the Super NES version, it will send Mario upwards for some reason.
Mario's head hitting bricks in this game is like a hot knife cutting through butter.
I've heard that the developer's accidentally reversed Mario's impulse velocity when breaking a brick somehow, but that doesn't seem right. We've known that most of the code for the All-Stars releases of games was just a copy-paste version of the NES versions, so it sounds unlikely that a random byte would be changed like this. Time to investigate!
I'll walk you through my process here. First, we can check if the velocity of Mario was inverted or not. We need to find the code that runs when a brick is broken, and where Mario's velocity gets set.
I'm going to go to this Super Mario Bros. disassembly by doppelganger and Ctrl+F for things like 'brick', 'break', 'shatter', etc. This looks promising:
BrickShatter:
jsr CheckTopOfBlock ;check to see if there's a coin directly above this block
lda #Sfx_BrickShatter
sta Block_RepFlag,x ;set flag for block object to immediately replace metatile
sta NoiseSoundQueue ;load brick shatter sound
jsr SpawnBrickChunks ;create brick chunk objects
lda #$fe
sta Player_Y_Speed ;set vertical speed for player
lda #$05
sta DigitModifier+5 ;set digit modifier to give player 50 points
jsr AddToScore ;do sub to update the score
ldx SprDataOffset_Ctrl ;load control bit and leave
rts
Okay, so there it is! Mario's velocity gets set to #$FE, or -2 when he breaks a brick. Positive velocities point downwards, so this means... Mario is set to move upwards. Well that's interesting! Because that's exactly what happens in the All-Stars version, but it doesn't feel like it in the NES version. Let's check the SNES version real quick.
This is from leaked source code so I will only be posting images instead of text.
Yeah, this routine is here, with only a small change regarding the sound effect that plays. Mario's velocity is set to -2 in both games. So that disproves that! The date on this file within the Super Mario All-Stars source is August 12, 1985, so I would be surprised if there were any tiny changes like that.
So, what's the deal then? Why do the two games differ? And why is Mario's velocity being set in the upwards direction when he should be moving downwards? More investigation needed...
I'll put the two instances of the game next to each other and step frame by frame as I break a block, and see when the difference occurs.
The brick breaks on the same frame in the same exact way, but Mario hits an invisible force in the NES version.
It looks like the change in velocity gets applied way after the block breaks, so I don't think this will help much. However, I did notice something when I did this. In the NES version, there are two sound effects that get played when you break a break--the brick shattering sound, and the bump sound that plays when Mario hits his head on any ceiling. In the Super NES version however, there is only the brick shattering sound. It's quite hollow sounding because there is no thud sound (and the bonk sound in this game has some oomf to it too). That gives me an idea.
Instead of looking at the game, I'm going to watch the memory. The memory region from $0500 - $069F (in both versions--the code was copy-pasted afterall) holds the tile IDs of all the blocks currently loaded. Let's see what happens as I step frame by frame again.
NES version on the left, SNES on the right. The orange byte is the tile of the brick block.
There it is, just as I suspected! In the NES version, it takes two frames for the brick block to disappear. Mario actually bonks his head on this mystery block that appears after the brick disappears for a single frame. This mystery block does not appear in the Super NES version, so Mario has nothing to bonk his head against. When Mario bonks his head on a ceiling, his velocity is set to positive 1. So that means after breaking a brick, he will have a velocity of +1 in the NES version, but -2 in the Super NES version. Solved!
But what is this mystery block?? Well, the mystery block is just the invisible solid block that appears after hitting any bumpable block. When a block plays its little animation of being hit, it turns into a sprite object temporarily. The background becomes blank in that time, but that tile still has to act solid during the animation. This is the purpose of the mystery block.
Sometimes this animation can glitch out which will break the illusion that the blocks are always a single entity.
But why does it not appear in the Super NES version? Let's find out. I'm going to set a break point in my emulator (which is Mesen2, I highly recommend, link here) to pause when this specific memory location gets written to in both versions.
The ID of the mystery block (and a lot of blocks) changed, but the code is identical.
Okay wait, the mystery block is there in the Super NES version too? Huh. If I let the games run again I should get the blank tile next.
You can see the location of the code in the ROM is different too, which changes all the jump and call targets.
Yep, there they are. But something happened that gives us another clue! In between these break points, the NES version of the game advanced a frame. So on one frame, the mystery block was written, and then cleared on the next frame. In the Super NES version, these writes happened on the same frame. This would explain why Mario never interacts with the mystery block--it appears and disappears before any physics code runs! But why??
Let's look at that function that runs to clear the mystery block. If I set a break point at the start of this function on the NES version, I find that it runs once per frame.
I need to get a symbols file so that all these memory addresses have labels...
If I go to the disassembly, it looks like this is responsible for drawing various blocks to the screen.
BlockObjMT_Updater:
ldx #$01 ;set offset to start with second block object
UpdateLoop:
stx ObjectOffset ;set offset here
lda VRAM_Buffer1 ;if vram buffer already being used here,
bne NextBUpd ;branch to move onto next block object
lda Block_RepFlag,x ;if flag for block object already clear,
beq NextBUpd ;branch to move onto next block object
lda Block_BBuf_Low,x ;get low byte of block buffer
sta $06 ;store into block buffer address
lda #$05
sta $07 ;set high byte of block buffer address
lda Block_Orig_YPos,x ;get original vertical coordinate of block object
sta $02 ;store here and use as offset to block buffer
tay
lda Block_Metatile,x ;get metatile to be written
sta ($06),y ;write it to the block buffer
jsr ReplaceBlockMetatile ;do sub to replace metatile where block object is
lda #$00
sta Block_RepFlag,x ;clear block object flag
NextBUpd:
dex ;decrement block object offset
bpl UpdateLoop ;do this until both block objects are dealt with
rts ;then leave
Every frame it makes its way through the entire function. However, I notice that on the frame that the brick is hit, the branch at $BED8 is taken, which basically skips an entry in the buffer for that frame. From the comments on this code here, it looks like maybe that the VRAM buffer is being used. It looks like maybe the game can only draw one kind of block at a time. If more than one is queued up, then they are drawn on successive frames instead. This lines up with what we saw here. What about the Super NES version?
Well this function exists over here as well, and it runs once per frame too. However, it runs through on every frame without a hiccup. Interesting. Just curious, what does the original source for this function look like?
I honestly think the comments from the disassembly are better than the original source.
Oh! Well look at that, I didn't even notice. The branch that the NES was taking was commented out in the Super NES version! I presume maybe because the Super NES has the capability to be able to draw more tiles at once without causing graphical glitches or whatnot. The entire VRAM buffer that the NES references here is completely absent from the Super NES version of the game. This seems to be the root cause for why Mario interacts differently with bricks between the two versions. There's even proof with the commented out lines!
Just out of curiosity, what would happen if we commented out these lines in the NES version? Maybe we'll see why that check was put there in the first place.
Well I tried it and nothing bad seems to happen. I'm sure with more testing or a deeper analysis of the code would reveal why that check was done, but at a first glance it seems like everything works just fine. We can even see Mario bounce upwards when he just barely grazes a brick block:
This just feels wrong.
So why is Mario's velocity set to be an upwards direction when he hits a block? Who knows. Maybe it was an accident, and the temporary solid block just prevented him from moving upwards anyway and so it was never noticed. Or maybe this was done on purpose to get the 'bump' sound to play too? Or perhaps this was originally the intended way that Mario would break bricks. It will forever be a mystery.
You can support Retro Game Mechanics Explained on Patreon here! Any and all support is greatly appreciated!