Illegal ancillae are those with IDs greater $43
. Through various means, these values can be placed into the ancilla array; however, there is no vector corresponding to these values. As such, they begin executing mostly garbage code.
The following section headers will name each entry as IAx##, where IA abbreviates Illegal Ancilla; x indicates a hexadecimal value; ## is the hexadecimal ID of the ancilla.
The results are fully unpredictable.
This entry points to a location in WRAM. The address is seemingly unused, so it will most likely have $0000
, which will execute the break vector. This vector jumps to $00FFFF
, which has a BRL
that wraps around and reads $0000
as the argument to the opcode, which is a relative distance to branch from the byte after the argument. $0000
is expected to have the original illegal pointer, so execution resumes at $000704
, more unused space. This begins a long loop of repeated break vectors. Eventually, the stack will overwrite $0000
, resulting in unpredictable effects.
This entry points to a location in WRAM. The low byte of the address is unused, so it will contain the value $00
. The next byte is not predictable, but the argument to BRK
is generally ignorable. The break vector leads to a jump to $000108
. From here, the results are not predictable.
This entry points to a location in WRAM. The high byte of the address is technically used, but always has a value of $00
. This leads to a similar BRK
loop as IAx44.
This entry points to a location in WRAM. The results are fully unpredictable.
This entry points to a location in WRAM. The low byte (which will determine the first opcode used) is technically predictable, as it is determined by Link's coordinates; however, with 7 consecutive $00
bytes following, there is not much useful that can be done besides returning from the routine safely with an RTS
($60
).
This entry points to a location in WRAM. The results are fully unpredictable.
This entry points to a location in WRAM, specifically the OAM buffer. The results are fully unpredictable.
This entry points to a location in ROM. Specifically, it is executing data that is part of the quake draw routine's tables. Depending on the slot, different code from another table will be executed. Regardless, the game will crash.
This entry points to a location in WRAM. The address $0302
only takes on values of $00
or $01
. In the case of $00
, the BRK
vector will lead to unpredictable results. When ORA (dp,X)
is executed, the inevitable crash is merely delayed.
This entry points to a location in WRAM. The results are fully unpredictable.
This entry points to a location in WRAM. The results are fully unpredictable.
This entry points to a location in WRAM. The addresses containing the opcode is unused, resulting in a BRK
, but it is followed by overworld screen properters, resulting in unpredictable code.
This entry points to a location in WRAM. The high byte of the address will be the opcode (BRK
); after the break vector, the results are unpredictable.
This entry points to a location in WRAM, specifically the OAM buffer. The results are fully unpredictable.
This entry points to a location in WRAM, specifically the last slot in the torch timer array. This array has 16 slots, but in practice, this slot is never used. A BRK
will occur, but it will then jump to $0501
for execution. The results are fully unpredictable.
This entry points to a location in WRAM, specifically the high byte of the quadrant visits flag for the current underworld tile. This byte is unused, so it will result in a BRK
. The address branched to after the BRK
is also unused, resulting in a BRK
loop.
IAx54 and IAx55 share the same entry point.
This entry points to a location in ROM. Funnily enough, it is pointing straight to another pointer from the jump table we just used, specifically the pointer for the unused ancilla with an ID of $03
. Quite quickly, this routine converges into actual code; in fact, it's the code just run to find out which ancilla subroutine to execute. Unfortunately, this returns to a point in the code after a stack push was meant to occur. When a value assumed to be the ancilla ID is recovered from the stack, it will rip off the low byte of our return address ($37
). This results in it executing the routine for the ancilla with ID $02
, the fire rod shot. When it's finished, the unbalanced stack will eventually cause the game to crash. Forever.
IAx56 and IAx57 share the same entry point.
This entry points to a location in ROM, specifically the routine AncillaAdd_FireRodShot
; however, its entry point does not align with the code, and it treats the parameter of an STA dp
as the opcode, resulting in a BRK
being executed. The break vector leads to the middle of the topmost subroutine, RunModule
. The entry point does not align with the code, but it recovers after one instruction. From here, the result is mostly unpredictable.
However, $80
is in the middle of some unused RAM. If this illegal ancilla is in slots 0, 1, or 2, then the ADC
will look at the word in addresses $80
, $81
, or $82
, respectively, all of which contain the value $0000
. In these cases, the accumulator, which currently contains the high byte of our pointer, will have the value of $0000
added to it, which contains the low byte of our pointer. The carry is always clear, so we know that we will get $36
as our result, with the carry flag set. This results in a jump to a known address in bank83, of which bank03 is a mirror. This bank is filled entirely with overworld tile32 data, so, needless to say, the results are garbage. But we can follow along for a bit:
The game crashes because the stack is unbalanced after this RTL
.
For all other indices, the results are fully unpredictable.
IAx58, IAx59, IAx5A, and IAx5B all share the same entry point.
This entry points to a location in ROM, specifically one of the exit points of Ancilla_QuakeSpell
. The entry point aligns perfectly with the code, an RTS
that allows the ancilla to exit safely, without doing anything.
This entry points to a location in open bus. The results are fully unpredictable.
This entry points to a location in ROM, specifically part of the flute ancilla's consumption code. The entry point aligns perfectly with the code. The branch is always taken, as the Z
flag will always be unset from reading the high byte of the pointer.
This creates a glitchy object (often the item get sprite) on screen, but beyond that, it does not appear to have any interesting consequences in and of itself. If the object ends up being off screen, it will just delete itself from the array.
This entry points to a location in ROM, specifically part of the bomb draw routine's data table. The first instruction is a BRK
. Eventually, this leads to open bus, making the results crashdictable.
This entry points to a location in WRAM, specifically the start of the ancilla ID array. This array is easily manipulable; it can, and has, been used for arbitrary code execution.
This entry points to a location in WRAM, specifically a general purpose DMA buffer. The results are fully unpredictable.
This entry points to a location in WRAM, specifically an array of shutter door location values. Results will vary based on the position and order of doors, but most of the results are fully unpredictable..
This entry points to a location in ROM, specifically the Ancilla_SFX1_Pan
routine. The entry point aligns with the code perfectly and results in a stable return. Because the sound effect attempted is $80
, this will only cause the ambient sound effect (if any) to fade away, as negative values flag this behavior for those sound effects specifically.
This entry points to a location in open bus. The results are fully unpredictable. And then some.
This entry points to a location in ROM, specifically the Ancilla_Move_Y
routine. The entry point is exactly the beginning of the routine, so the code runs perfectly and without fault. Thus, this ancilla will be in a constant state of vertical movement. Every array used by this routine is 10 entries long, so there is no potential for corruption.
This entry points to a location in open bus. In general, the results are fully unpredictable.
In many cases, the same instructions are used ad nauseum, but eventually, execution lands on the controller ports, allowing for full control arbitrary code execution in a TAS. This can make this illegal ancilla predictable and usable; however, other CPU operations will clobber the value in open bus.
This entry points to a location in ROM, specifically part of the door debris draw routine's data table. It just so happens to land on the value $60
, which is the opcode RTS
, causing the ancilla's routine to exit immediately.
This entry points to a location in ROM, specifically the end of the jump splash draw routine. The entry point does not align perfectly with the code, but it recovers quickly. This code takes the value of the accumulator, which currently holds the high byte of the pointer ($A9
), and stores it with an indirect write to the 16-bit address present in $FA
. This address holds the player 1 joypad input from the previous frame: AXLR....
. $FB
is the same for player 2, but the code for writing it is unreachable; thus, the high byte of the indirect address is always $00
.
Effectively, this means we can write $A9
to 16 different addresses using a standard SNES controller; within the address range $00
–$F0
, for low nibbles of $0
. The bottom nibble is the controller's signature, which is %0000
for a regular controller.
Controller input | Address | Effect | ||
---|---|---|---|---|
%0000 |
|
$00 |
Scratch space | Useless |
%0001 |
R |
$10 |
Game module | Crash |
%0010 |
L |
$20 |
Link's Y-coordinate low byte | Funny teleport |
%0011 |
LR |
$30 |
Link's Y velocity | Nothing; overwritten before used |
%0100 |
X |
$40 |
Temp variable for coordinates | Nothing; overwritten before used |
%0101 |
X R |
$50 |
Change direction flag | Link cannot turn anymore |
%0110 |
XL |
$60 |
Attract mode sequence counter | Nothing; zeroed every frame as collateral damage |
%0111 |
XLR |
$70 |
Free RAM | Nothing |
%1000 |
A |
$80 |
Free RAM | Nothing |
%1001 |
A R |
$90 |
OAM buffer location pointer | Seemingly nothing; at worst, broken graphics for 1 frame |
%1010 |
A L |
$A0 |
Room ID | Repointed to EP big chest room or a nonexistent room ID $01A9 corresponds to cape and mirror in SRAM |
%1011 |
A LR |
$B0 |
Subsubmodule | Game will crash during transitions |
%1100 |
AX |
$C0 |
Tilemap buffer pointer | Nothing? |
%1101 |
AX R |
$D0 |
Room load pointers | No effect; overwritten when required by the CPU |
%1110 |
AXL |
$E0 |
BG1 horizontal scroll low byte | Seemingly nothing; fixed every frame |
%1111 |
AXLR |
$F0 |
Joypad 1 inputs | Changes input for joypad 1 to B+Select+Up+Right |
This entry points to a location in ROM, specifically the end of a routine that transmutes ancilla $2A
into ancilla $2B
. The low byte of the Y-coordinate will remain at its current value, and the high byte will take the value $D9
. The low and high bytes of the X-coordinate will be garbage data.
After the coordinates are set each frame, this illegal ancilla will behave like a normal spin spark. The ID rewrite of the routine is not performed, so this ancilla will remain with the illegal ID.
This illegal ancilla shares the same pointer and effect as IAx5F.
This entry points to a location in location in WRAM, specifically the flute cooldown timer. This results in fully unpredictable results or a BRK
(followed by fully unpredictable results).
This entry points to a location in WRAM, specifically the arbitrary DMA buffer. The results are fully unpredictable.
This entry points to a location in ROM, specifically part of the magic powder draw routine's table. After doing a bunch of subtraction, a BRK
is hit and execution resumes in the middle of a large data table. Given the onslaught of indirect addressing, results are fully unpredictable. A number of branch instructions can potentially occur, but overall almost all outcomes will be a crash.
This entry points to a location in WRAM, specifically an array of ancillae OAM priority values. Results are, you guessed it, fully unpredictable.
This entry points to a location in WRAM, specifically the torch timer array. Unlike IAx52, this points to the beginning of the array, which is actually used in practice. Unfortunately, this still results in unpredictable code or a BRK
.
This entry points to a location in open bus. It will constantly not branch on negatives until it reaches CPU registers. Afterwards, the results are fully unpredictable.
This entry points to a location in WRAM, specifically the last byte in an array that counts the number spiral staircases. The results are fully unpredictable.
This entry points to a location in WRAM, specifically part of the arbitrary DMA buffer. The results are fully unpredictable.
This entry points to a location in ROM, specifically part of the bombos spell draw routine. The entry point does not align perfectly with the code, but it recovers after one instruction. Unfortunately, the entry is after where a stack push was meant to occur, so at the end of this routine, the stack will become unbalanced, resulting in a crash. I mean... The results are fully unpredictable.
This entry points to a location in ROM, specifically part of the Skull Woods flame routine. The entry point does not align with the code and ends up executing a BRK
. Interestingly, the SEP
instruction sets the interrupt-disable flag; however, this flag has no effect on a software interrupt.
The location branched to does not align perfectly with the code. It recovers after 1 instruction; however, the routine entered expects both the accumulator and index registers to be in 16-bit mode. Having entered with both in 8-bit mode, the CPU fails to stay aligned with the intended code. This is irrelevant, as the code eventually enters a BRK
loop.
This entry points to a location in ROM, specifically the end of the dash tremor code. The break vector hit leads to a data table, where endless direct page instructions are executed. They're not worth documenting, and I hope the game eventually crashes.
This entry points to a location in WRAM, specifically the ancilla layer array. Without using other misslots to write different values here, only $00
or $01
will be present. The results are fully unpredictable.
This illegal ancilla shares the same pointer and effect as IAx61.
This entry points to a location in ROM, specifically part of an ancilla hitbox routine. The entry point aligns perfectly with the code; however, it skips over several stack pushes that were meant to occur. This leaves the stack unbalanced, causing the game to eventually crash.
This entry points to a location in WRAM, specifically a general purpose property of sprites. Predictably, the results are fully unpredictable.
This entry points to a location in WRAM, specifically part of the OAM buffer. The results are fully unpredictable.
This entry points to a location in ROM, specifically the repulse spark draw routine. The entry point does not align perfectly with the code, but it recovers after 1 instruction. Eventually the routine returns cleanly.
Of particular interest is the instruction at $08:905A
, which loads the X
register with the value of $0FAC
. The repulse spark is not a slotted ancilla, so it does not have any recovery of slot after this. Thus, this instruction will determine the next slot executed −1. Normally, this address only takes on values from 0
–5; however, if we found a way to modify this address, we could execute ancillae in the illegal slots from $0A
–$7F
.
This also comes with the potential to hardlock the game in an infinite loop. For example, if IAx7A is in slot 0 and $0FAC
has a value of $02
, then the next ancilla executed will be slot 1, followed by slot 0 again. This will repeat ad infinitum.
Less interestingly, this illegal ancilla should also result in some broken objects on screen.
This illegal ancilla shares the same pointer and effect as IAx78.
This entry points to a location in WRAM, specifically the an array that counts the number spiral staircases. The results are fully unpredictable.
This entry points to a location in ROM, specifically an ancilla sound effects routine. The entry point does not align with code, causing it to branch to open bus. It will keep branching backwards until it reaches CPU registers or the A bus is clobbered. These results are fully unpredictable.
Under most circumstances, HDMA will indeed clobber the A register, and the CPU should eventually reach $08:8000
, where it will put a broken sound effect into SFX set 1 and allow this ancilla to recover cleanly.
This illegal ancilla shares the same pointer and effect as IAx78.
This entry points to a location in WRAM, specifically the current underworld room ID. The top byte located in $A1
an only ever contain $00
or $01
. These results are fully unpredictable, though being in room $60
can return safely.
This entry points to a location in WRAM, specifically a general purpose property of F-slot ancillae. The results are fully unpredictable.
Due to the left shift when calculating the index into the pointer table, illegal ancillae with an ID of $81
and higher will behave as the ancilla with an ID $80
lower. This is because the top bit is discarded after decrementing, resulting in the same index.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.
The results are fully unpredictable.