Mirror Block Erase

Erasing pushable blocks with the mirror is something everyone knows how to do. Just use the mirror while the block is moving, and POOF! the block is gone.

Blocks

Let's start by understanding how pushable blocks move in the first place. Everyone is likely familiar with sprites and ancillae, but blocks are neither of these. Blocks are their own, unique class of self-acting entities.

Only two blocks can be active at any one time. They all "exist" in RAM when exploring the underworld, but they spend the majority of that existence doing nothing. A two-item array exists to identify active blocks with their manipulables index: $05FC and $05FD. These entries normally hold $00, which means they don't identify an active block. When a block becomes active, it will store its manipulables index+1 into an available active block slot to identify itself.

Take note that I am saying "identify", rather than another word. This is because this array doesn't actually control which block is active. Every manipulable room object—push blocks, pots, rocks, bombable floors, hammer pegs—have a property listed in the 16 item array at $0500. Six of these values correspond to push block behavior:

This array is populated during the room building module. Its size is then saved to $042C. During player control, that address is used to loop through every manipulable object in the list. Any object in states 1 through 4 will run special push block code for that object. In practice, this tends to be actual push blocks.

The active block array is populated upon pushing a block. To find an empty slot in this array, $05FD is checked for a value of $00. If it is zero, then that activity slot is used. If not, $05FC is checked. If it too is occupied, then pushing fails. The slot chosen is used to index other two-item arrays containing properties of a live block—position, speed, target, etc.—from addresses $05E0 and $05FB. These properties are derived from the block's primary properties as they appear in the manipulables arrays the moment the block is initially pushed.

The manipulables array and the active blocks array are completely unaware of each other, so how do blocks know which slot to use? There's no remembering of the slot a block initially took. Instead, blocks will first check $05FD to see if it contains their index. If it doesn't, they use $05FC. This code is weird not only because it makes this bad assumption that the remaining slot must be correct, but also because it's written as if it were meant to loop without making any attempt to actually do so.

So… can we mess with these indices to confuse currently active blocks?

Amazing Mirror

We sure can!

Only a few things reset those activity addresses:

The developers made an itsy-bitsy mistake in JP1.0: they had the mirror clear the slots every time it is used. Later versions fixed this by moving those clears to be inside of the "successfully mirrored" branch. Since these indices are only used after the block's actual state is checked, active blocks can be confused into using slot 0, regardless of it matching the block's index. All operations which refer back to the manipulables index will be broken as well. With the slot holding a value of $00, accounting for the +1 from before makes the block use $FF, which is shifted left once to $FE (the most significant bit is lost). This results in any actions with the manipulables array being applied to $05FE and $05FF, which are unused memory. This just ends up being incremented indefinitely.

In the most precise sense, you're not actually deleting the block; you're just making it behave as if it's somewhere else. In fact, the block remains active after you mirror erase it. It will continue its routine forever, because there's no one telling it to stop. The values it's being told to use are all wrong, and it won't reach its expected destination anymore. Even if it does, it can't tell itself to stop. It just chugs on, oblivious to the damnation cast upon it.

Its index is no longer in the active block array, but it doesn't need to be. That's only used because the developers also wanted to keep the velocities and other values for moving blocks in a smaller, segregated portion of memory. A block runs code regardless when its state in the manipulables array is nonzero.

BONK

So now it's time to answer the big question: Why does bonking weird?

Every frame that these blocks move, they also calculate and save a recoil velocity based on the direction they're moving. This determines its movement as well as Link's, should he do something recoily. This doesn't just include bonking; it includes other actions that use the recoil speeds, such as taking damage or jumping.

These values are set constantly, overriding previous actions that only set them on initialization, such as the aforementioned. The reason push blocks always seem to make you recoil upwards is because they're fast, and it's really hard to make two go at once. In fact, it seems almost impossible. The active slot 0 seems virtually impossible to reach, so it will pretty much always have 0 as its direction, which goes up. Were you to get a new block in that slot moving in a different direction at the same time as a block is in slot 1 (which is populated first), you would recoil in whatever direction it was pushed instead.

As it turns out, we can actually make this happen by manipulating our blocks just slightly differently!

Mirror block… remember?

A funny thing thing happens if you mirror inside a dungeon on the same frame you begin pushing a block. Instead of being lost to the infinity of travel, the block sticks around forever. What gives?

This is actually easy to piece together with what we know already. The mirror still resets both active block indices to 0, but it does this before the code for pushing a block is run. So what happens is the block sticks its index into the active block array, but the room or a new room are loaded before it can actually do anything. After a room load, the manipulables array doesn't contain any valid blocks to find. This means that no block is ever trying to operate on the active slot with a live index. However! The block draw code is completely agnostic to the state of that array. It only looks for the live slots. Thus, we get a veritable ghost block that can't do anything, but still haunts your screen.

Because this apparition occupies a real slot, it means any block you push while it is visible will be forced to occupy the other slot—the one that's normally impossible to need. If the real block you push happens to occupy the same slot as a spectre (perhaps you used the same block for both steps), then the action routines will operate on the ghost block (if it's in slot 1, because then it's found first), turning it real. At the end, you'll have a ghost block in slot 0, the one that's hard to use.

The last oddity with both slots being drawn is that the variables used to control the block falling animation are not slotted. There is only a single shared step and timer, so if one block looks like it's falling, the other will too, regardless of their indices.

Blockbuster

It's now time to combine everything we know into some brand new tech.

We can then ask ourselves: "What if a mirror erased block is operating on real data?" And the answer is that the block will behave like a normal block with 2 differences—this block will be invisible, and it will never stop sliding.

We start by creating a ghost block to occupy slot 1 (we'll call this ghostbusting). Once we've done that, we'll find a useful block to mirror erase. When we do this, that block will seem to be gone, but we'll notice that we can keep pushing something. If we can somehow get on the otherside of whatever this is (you can probably already deduce that this is an invisible block), it will push us instead. And it does! So long as we push against it.

This is no different from a normal mirror erase, but we've managed to manipulate the slot 0 data to our advantage. The pushblock's inertia overpowers all, and it can push us through any other obstacle. The only real limit is that there are very few places to utilize it fully. Even when we assume every pushable block can be reached with cavestate, there are just not that many of them. And even fewer of them can give us a useful clip. It's extremely unfortunate.

Summary

When blocks are pushed, the index of their data is put into a slot in a 2 item array. This slot is then used to update their coordinates and velocity. When the mirror is used, both values in that index array are set to 0, which is meant to indicate the slot is free. This happens in caves, but it doesn't affect the blocks' actual statuses; the blocks will keep running, but a coding mistake will have them assume that they should use the slot 0 value in the array as an index if the second value is not correct. This bad assumption results in -1, an invalid index, being used for some of the block data.

The block continues operating, and is not technically erased. This makes this glitch a form of misslotting, but a slightly different species from other known misslots. In versions after JP1.0, a check is performed to see if the beep sound effect was played, and doesn't reset the array if it was.

A ghost block that is drawn without acting can be created by pushing a block inside a dungeon and mirroring on the same frame the block is activated. This puts it in slot 1 indefinitely without any way to operate.

A ghost block can be used to place real block data in slot 0. A subsequent mirror erase will cause that block to operate indefinitely without being drawn. This invisible block can be used to push Link through any collision.