Misslot Dossier

This document aims to catalog and understand the behavior and potential of misslotting every ancillae and the potential corruption they can effect in any situation.

Table of contents

Glossary

This glossary seeks to establish accurate and comprehensible terminology for describing the various concepts pertinent to misslotting.

Ancilla
A self-acting entity whose routines and existence are established and controlled from the 10 item array at $0C4A.
Arbitrary misslot
Putting an ancilla into an illegal slot (10+). Ancillae in these slot are not executed, but their placement can corrupt memory during their initialization.
Former slot/Front slot
The first 5 slots (0–4) used by ancillae. This is where ancillae with more significant effects are placed. An ancillae designed to be in this section can be declared a front half, etc. ancilla. This may be abbreviated to F-Slot in disassembly comments.
(General) Misslot
Placing an ancilla into a slot which it is not designed to be in. This includes bombs (ID: $07) in slots 2+ and front slot ancillae in the latter slots.
Illegal ancilla/object
An ancilla with an ID greater than $43. IDs of $44 and higher do not have a defined entry in the routine jump table.
Illegal slot
An ancilla slot (10–255) not recognized by the general ancilla execution routine.
Latter slot/Back slot
The last 5 slots (5–9) used by ancillae. Ancillae with less significance usually begin here for placement. This may be abbreviated to L-Slot in disassembly comments.
Overload
Causing either the former slots or every slot to be occupied, often intentionally.
Particle
One of the three ancillae (IDs: $0A, $13, $3C) that can be overwritten to force the creation of a new ancilla.

Ancilla creation

Ancillae are created by calling various subroutines. F-Slot ancillae generally use a more fine-tuned search routine. L-Slot ancillae tend to find a slot themselves and ignore extra precautions. Though, ultimately, it is those precautions that give rise to misslotting.

db $00, $08, $0C, $10
Data in used for below
db $10, $04, $10, $18
Appears to be number of OAM objects<<2
db $08, $08, $08, $00
db $14, $00, $10, $28
db $18, $10, $10, $10
db $10, $0C, $08, $08
db $50, $00, $10, $08
db $40, $00, $0C, $24
db $10, $0C, $08, $10
db $10, $04, $0C, $1C
db $00, $10, $14, $14
db $10, $08, $20, $10
db $10, $10, $04, $00
db $80, $10, $04, $30
db $14, $10, $00, $10
db $00, $00, $08, $00
db $10, $08, $78, $80
PHA
A contains Ancilla ID at this time
JSL $1CF537
Ancilla_CheckForAvailableSlot
PLA
Get ID back
TYX
If no slot found
BMI fail
STA $0C4A, X
Save ID to slot
TAY
To save ID
LDA $EE
Get Link's layer
STA $0C7C, X
Give it to the ancilla
LDA $0476
Another layer flag
STA $03CA, X
Saved
STZ $0C22, X
Clears Y velocity
STZ $0C2C, X
Clears X velocity
STZ $0280, X
Clears OAM properties
STZ $028A, X
Clears collision flag
PHX
TYX
LDA $08806F, X
Get OAM size
STA $0E
PLX
LDA $0E
It's already there...
STA $0C90, X
Save as a property
CLC
Clear carry to indicate success
RTS
fail
SEC
Set carry to indicate failure
RTS
FindAncillaSlot
STY $0F
Save the number of type allowed
INY
+1 for later comparison
STY $0E
Save comparison value
LDY #$00
Y will be our counter
LDX #$04
Slot index
tally
CMP $0C4A, X
Is it the ancilla we're making?
BNE diff
If not, keep looking
INY
If so, count up
diff
DEX
Next slot
BPL tally
Stop after 0
CPY $0E
Compare tally to quota
BEQ too_many
Except this doesn't work if we have more…
LDY #$01
Prep Y with slot 1
CMP #$07
Only allow bombs and door debris
BEQ find_slot
Into the first 2 slots
CMP #$08
BEQ find_slot
Continue with slot 1 start if those
LDY #$04
Otherwise, start with slot 4
find_slot
LDA $0C4A, Y
Check the slot
BEQ open
Is it empty?
DEY
Next slot
BPL find_slot
Stop after 0
find_harder
DEC $03C4
Decrement our search index
BPL positive
If our search index is positive
LDA $0F
Leave it; otherwise, load our quota
STA $03C4
positive
LDY $03C4
Search index to register
LDA $0C4A, Y
Check that slot
CMP #$3C
Sword sparkle
BEQ open
These ancillae are all considered "replacable"
CMP #$13
Ice rod sparkle
BEQ open
CMP #$0A
Lobbed arrow
BEQ open
DEY
BPL find_harder
Stop after slot 0
open
RTL
Return with our index
too_many
TXY
Returns with −1
RTL

Ancilla quota
Ancilla Quota Ancilla Quota
$00 Nothing $22 Item get 5
$01 Somaria missile $23 Poof 5
$02 Fire rod shot 2 $24 Overworld gravestone 5
$03 Unused $25 Unused
$04 Beam wall hit $26 Sword swing sparkle
$05 Boomerang 1 $27 Duck 5
$06 Wall hit 2 $28 Fairy pond toss 5
$07 Bomb 2 $29 Falling item get 5
$08 Door debris 1 $2A Spin sparkle 1
$09 Arrow 3 $2B Spin sparkle
$0A Wall arrow $2C Somaria block 1*
$0B Ice shot 2 $2D Somaria block dying
$0C Sword beam 1 $2E Somaria block exploding
$0D Max charge sparkle $2F Lamp flame 3
$0E Unused $30 Byrna charge up 1
$0F Unused $31 Byrna spark
$10 Unused $32 Blast wall explosion —*
$11 Ice shot wall hit $33 Blast wall explosion 2^
$12 Unused $34 Skull Woods fire
$13 Ice shot sparkle $35 Master Sword cutscene 5
$14 Unused $36 Flute from ground 1
$15 Splash 1 $37 Flute spot debris 1
$16 Stars 1 $38 Flute cutscene bird 1†
$17 Dirt 1 $39 Somaria platform poof
$18 Ether 1 $3A Super Bomb explosion 1
$19 Bombos 1 $3B Sword up sparkle 1
$1A Powder 1 $3C Sword charge sparkle —*
$1B Wall poke spark 2 $3D Item splash
$1C Quake 1 $3E Crystal rising
$1D Bonk screen shake 2 $3F Bush powder poof 5
$1E Dash dust 1 $40 Smithy poof 5
$1F Hookshot 4 $41 Waterfall splash 1
$20 Link's blanky —* $42 Upgrade rupees 10
$21 Link snoring 2/5** $43 Ganon's Tower cutscene 1†
  • * Unique initialization routine
  • ** Varies depending on routine caller
  • † Limited to 1 by a different search routine of the first 5 slots
  • ^ The blast wall is hardcoded to always be in the first 2 slots

The Ancilla_CheckForAvailableSlot routine is extremely similar to another routine, Ancilla_GetRidOfArrowInWall, which searches for lobbed arrows and removes them if there are no open slots, or if exactly 3 are already present. The table to the right documents the quota of each ancilla, based on the code that creates it.

The critical fault that allows for misslotting is the use of $03C4 as a search index. This address is only ever written to in 4 locations. 2 of these locations are in Ancilla_GetRidOfArrowInWall, where it is decremented, or set to 4, similar to the logic at $1CF561 in Ancilla_CheckForAvailableSlot, the other 2 instances of writing to this address.

Both of these routines follow the path of "decrement search index; set it to <value> if it goes negative" (although, the arrows routine is much more aggressive; it won't take no for an answer). In the arrow routine, this value is 4; for the general routine, it is the ancilla-specific quota. The entire purpose of this in the general routine is questionable. All F-slot ancillae use one of these prebuilt routines.

At least by design. Both of these search routines assume that this address has been pre-initialized elsewhere, but that is never the case. If, by any means, $03C4 already has a value, this will determine where a secondary search begins. If the value is greater than $7F after decrementing, then the ancilla's quota will overwrite it.

The foremost curiosity is why the individual initialization routines don't take care of ID and quota themselves. In many cases, these are parameters loaded before the initialization is called.

The table here should not be taken at face value, as other factors may limit when, where, and how many of any particular ancilla can spawn.

Ancillae execution

Ancillae_DoAll:
LDX #$09
Start at slot 9
next
STX $0FA0
Save index
LDA $0C4A, X
Get ancilla ID
BEQ inactive
ID 0 is no ancilla
JSR Ancilla_Do
inactive
DEX
Next slot
BPL next
Stop after 0
RTS
Ancilla_Do
PHA
A contains Ancilla ID at this time
CPX #$06
L-Slot ancillae have limited OAM
BCS skip_oam
Likely irrelevant beyond graphical glitches.
LDA $0C90, X
Allocation>>2 of ancilla
LDY $0FB3
Room flag for object display
BEQ sorted
LDY $0C7C, X
Layer of ancilla
BNE layer2
Only 2 layers recognized
JSL $0DBA8C
Unsorted layer 1 objects to OAM-D
BRA finish_oam
layer2
JSL $0DBA94
Unsorted layer 2 objects to OAM-F
BRA finish_oam
sorted
JSL $0DBA80
Sorted objects to OAM-A
finish_oam
TYA
Save allocated slot as a property
STA $0C86, X
skip_oam
LDY $11
Check for default submodule
BNE skip_timer
Only tick when default
LDY $0C68, X
Check if timer is already zero
BEQ skip_timer
Don't tick at 0
DEC $0C68, X
Tick tock
skip_timer
PLA
Recover ancilla ID
DEC A
Decrement to account for invalid ID 0
ASL A
Shift left to index words
TAY
LDA jump_table+0, Y
That DEC A could have been removed
STA $00
By just indexing from before the table
LDA jump_table+1, Y
STA $01
JMP ($0000)
At least it's not a NES jump
jump_table
dw $851B
ID: $01 - Somaria missile
dw $86D2
ID: $02 - Fire rod shot
dw $8852
ID: $03 - Unused
dw $8D19
ID: $04 - Beam wall hit
dw $90FC
ID: $05 - Boomerang
dw $93E8
ID: $06 - Wall hit
dw $955A
ID: $07 - Bomb
dw $9FDA
ID: $08 - Door debris
dw $A155
ID: $09 - Arrow
dw $A47F
ID: $0A - Wall arrow
dw $A501
ID: $0B - Ice shot
dw $DDE9
ID: $0C - Sword beam
dw $DDEE
ID: $0D - Max sword charge sparkle
dw $A632
ID: $0E - Unused
dw $A632
ID: $0F - Unused
dw $A632
ID: $10 - Unused
dw $A55A
ID: $11 - Ice rod shot wall hit
dw $A632
ID: $12 - Unused
dw $8435
ID: $13 - Ice shot sparkle
dw $A831
ID: $14 - Unused
dw $A833
ID: $15 - Splash
dw $A909
ID: $16 - Stars
dw $A9CD
ID: $17 - Dirt
dw $AAC4
ID: $18 - Ether
dw $B0F2
ID: $19 - Bombos
dw $BAD4
ID: $1A - Powder
dw $93FF
ID: $1B - Wall poke spark
dw $B68E
ID: $1C - Quake
dw $BBE0
ID: $1D - Bonk screen shake
dw $BCB6
ID: $1E - Dash dust
dw $BD98
ID: $1F - Hookshot
dw $C037
ID: $20 - Link's blanky
dw $C0B8
ID: $21 - Link snoring
dw $C3AE
ID: $22 - Item get
dw $D3E0
ID: $23 - Poof
dw $EE13
ID: $24 - Overworld gravestone
dw $A907
ID: $25 - Unused
dw $D67E
ID: $26 - Sword swing sparkle
dw $DE0C
ID: $27 - Duck
dw $C716
ID: $28 - Fairy pond toss item
dw $CAB0
ID: $29 - Pendant/crystal/medallion item get
dw $D7D6
ID: $2A - Spin attack sparkle
dw $D921
ID: $2B - Spin attack sparkle
dw $E377
ID: $2C - Somaria block
dw $E9FA
ID: $2D - Somaria block dying
dw $EB50
ID: $2E - Somaria block exploding
dw $EC25
ID: $2F - Lamp flame
dw $DB48
ID: $30 - Byrna charge up spark
dw $DC94
ID: $31 - Byrna spark
dw $AA59
ID: $32 - Blast wall explosion
dw $A632
ID: $33 - Blast wall explosion
dw $EFAC
ID: $34 - Skull Woods fire
dw $C283
ID: $35 - Master Sword cutscene
dw $CFCE
ID: $36 - Flute from ground
dw $D061
ID: $37 - Flute spot debris
dw $D1FC
ID: $38 - Flute cutscene bird
dw $EA95
ID: $39 - Somaria platform poof
dw $F19F
ID: $3A - Super Bomb explosion
dw $C18B
ID: $3B - Sword up sparkle
dw $C20E
ID: $3C - Sword charge sparkle
dw $CA25
ID: $3D - Item splash
dw $CC16
ID: $3E - Crystal rising
dw $D53D
ID: $3F - Bush powder poof
dw $D4BE
ID: $40 - Smithy poof
dw $ECC1
ID: $41 - Waterfall splash
dw $C802
ID: $42 - Upgrade rupees
dw $CCC4
ID: $43 - Ganon's Tower cutscene
illegal_ancillae
dw $0702
ID: $44
dw $0106
ID: $45
dw $0701
ID: $46
dw $0107
ID: $47
dw $0700
ID: $48
dw $0108
ID: $49
dw $0904
ID: $4A
dw $FF04
ID: $4B
dw $0302
ID: $4C
dw $0708
ID: $4D
dw $0101
ID: $4E
dw $0707
ID: $4F
dw $0001
ID: $50
dw $0807
ID: $51
dw $04FF
ID: $52
dw $0409
ID: $53
dw $8383
ID: $54
dw $8383
ID: $55
dw $80B6
ID: $56
dw $80B6
ID: $57
dw $B6B7
ID: $58
dw $B6B7
ID: $59
dw $B6B7
ID: $5A
dw $B6B7
ID: $5B
dw $68BD
ID: $5C
dw $D00C
ID: $5D
dw $9E03
ID: $5E
dw $0C4A
ID: $5F
dw $11A5
ID: $60
dw $06D0
ID: $61
dw $8020
ID: $62
dw $2090
ID: $63
dw $908B
ID: $64
dw $2A20
ID: $65
dw $A086
ID: $66
dw $A904
ID: $67
dw $D90B
ID: $68
dw $0C4A
ID: $69
dw $03F0
ID: $6A
dw $1088
ID: $6B
dw $B9F8
ID: $6C
dw $0280
ID: $6D
dw $04F0
ID: $6E
dw $30A9
ID: $6F
dw $0485
ID: $70
dw $10A9
ID: $71
dw $B3AC
ID: $72
dw $F00F
ID: $73
dw $BC11
ID: $74
dw $0C7C
ID: $75
dw $06D0
ID: $76
dw $8C22
ID: $77
dw $0DBA
ID: $78
dw $0A80
ID: $79
dw $9022
ID: $7A
dw $0DBA
ID: $7B
dw $0480
ID: $7C
dw $8022
ID: $7D
dw $0DBA
ID: $7E
dw $00A0
ID: $7F
dw $03A9
ID: $80

Shared ancillae routines

Addresses of interest

Valid ancillae

These are ancillae with valid pointers that were intended to exist.

The following section headers will name each entry as Ax##, where A abbreviates Ancilla; x indicates a hexadecimal value; ## is the hexadecimal ID of the ancilla. In less technical contexts, a descriptive English name will be used.

Illegal ancillae

Illegal IDs can be placed into the execution array; however, the addresses they jump to are not necessarily valid pointers.

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.

IAx44

IAx44_entry
BRK #$00
brk_vector
BRL $0702
BRK #$00
brk_vector
BRL $0702

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.

IAx45

IAx45_entry
BRK ??
brk_vector
BRL $0106
????

This entry points to a location in WRAM. The low byte of the address is seemingly unused, so it will most likely have $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.

IAx46

IAx46_entry
BRK #$00
brk_vector
BRL $0701
BRK #$00
brk_vector
BRL $0701

This entry points to a location in WRAM. The high byte of the address is technically used, but seemingly always has a value of $00. This leads to a similar BRK loop as IAx44.

IAx47

IAx47_entry
????

This entry points to a location in WRAM. The results are fully unpredictable.

IAx48

IAx48_entry
????

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).

IAx49

IAx49_entry
????

This entry points to a location in WRAM. The results are fully unpredictable.

IAx4A

IAx4A_entry
????
IAx4A_entry
BRK #$F0
brk_vector
BRL $0904
BRK #$F0
brk_vector
BRL $0904

This entry points to a location in WRAM, specifically the OAM buffer. The results are most likely unpredictable. In specific circumstances, this location of the buffer may not be written to, meaning it will have the repeated value $00F0. In these cases, a BRK loop will occur.

IAx4B

IAx4B_entry
PHD
Doctor to stack
JSR ($FC0E, X)
X<10

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. Based on a quick glance, nothing good seems to be possible; I consider this a lost cause, but may revisit it later.

IAx4C

IAx4C_entry
BRK ??
IAx4C_entry
ORA (??,X)

This entry points to a location in WRAM. The address $0302 is not well documented, but it only appears to take 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 is only being delayed.

IAx4D

IAx4D_entry
????

This entry points to a location in WRAM. The address will contain a value based on the current overworld area, but the results are unpredictable.

IAx4E

IAx4E_entry
????

This entry points to a location in WRAM. The results are fully unpredictable.

IAx4F

IAx4F_entry
BRK ??
brk_vector
BRL $0707
????

This entry points to a location in WRAM. The addresses containing the opcode seems unused, resulting in a BRK, but is followed by overworld-related data, resulting in unpredictable code.

IAx50

IAx50_entry
BRK ??
brk_vector
BRL $0001
????

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.

IAx51

IAx51_entry
????

This entry points to a location in WRAM, specifically the OAM buffer. This section of the buffer generally has values written to it. If this section is somehow clear, a BRK loop similar to IAx4A will occur.

IAx52

IAx52_entry
BRK ??
brk_vector
BRL $04FF
????

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. This is the beginning of a table for replacement objects. In the underworld, this will be fairly predictable. In the overworld, it will be predictable, but more robust, as this table updates as Link moves. In both cases, the end result is likely a crash.

IAx53

IAx53_entry
BRK ??
brk_vector
BRL $0409
BRK ??
brk_vector
BRL $0409

This entry points to a location in WRAM, specifically the high byte of the quadrant visits flag for the current underworld tile. This high byte seems unused, so it will result in a BRK. The address branched to after the BRK also appears unused, resulting in a BRK loop.

IAx54

IAx55

IAx54_entry
EOR ($88)
ORA $FC8D, Y
Y=$A6 so [$08FD33]:$C4
BCC −24
Carry will be cleared
LDY $0C86, X
We've been here before

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 the ID of $02, the fire rod shot. When it's finished, the unbalanced stack will eventually cause the game to crash. Forever.

IAx56

IAx57

IAx56_entry
BRK #$22
BRL $80B6
ADC ($80, X)
STA $03
LDA $807D, Y
Y=$AA; so [$8127]:$8D
STA $04
LDA $8099, Y
Y=$AA; so [$8143]:$83
STA $05
JMP [$0003]

IAx56 and IAx57 share the same entry point.

This entry points to a location in ROM, specifically the routine AddFireRodShot; 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, Module_MainRouting. 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 seemingly 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:

ORA ($00, X)
CMP ($2D, X)
LDY #$0C
AND ($00, X)
ROR $122D, X
CPY $0201
CPY $4CCC
Open bus
LSR $4422
More open bus
EOR $514D4E
MVP $FF, $44
Nice
LDX #$57
EOR $4423, Y
Speed 5: Open Bus
EOR $5D5B, Y
EOR $4444
EOR $376058, X
MVP $37, $42
TAY
RTL

The game crashes because the stack is unbalanced after this RTL.

For all other indices, the results are unpredictable.

IAx58

IAx59

IAx5A

IAx5B

IAx58_entry
RTS

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.

IAx5C

IAx5C_entry
????

This entry points to a location in open bus. The results are completely unpredictable.

IAx5D

IAx5D_entry
BNE $13
Z=0 because we last loaded $D0
JSR $F6B6
Ancilla_PrepAdjustedOamCoord
Adjust_OAM
LDY $0C7C, X
LDA $F67F, Y
Y=$B8; [$08F737]:$05
STA $65
Expected legal values: $10, $20, $30
STZ $64
LDA $0BFA, X
All values of X when this code runs
STA $00
should be <10
LDA $0C0E, X
So I wouldn't expect any corruption
STA $01
LDA $0C04, X
STA $02
LDA $0C18, X
STA $03
REP #$20
LDA $00
SEC
SBC $0122
STA $00
LDA $02
SEC
SBC $011E
STA $02
STA $04
SEP #$20
RTS

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.

The routine most likely results in 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.

IAx5E

IAx5E_entry
BRK #$00
brk_vector
BRL $9E03
ORA $78, X
ORA $79, X
ORA $76, X
ORA $77, X
ORA $76, X
ORA $77, X
ORA $78, X
ORA $79, X
ORA $78, X
ORA $79, X
ORA $92, X
PHP
TYA
PHP
LDY $08
LDA $930C
DB is still $08
PHP
STA $A508, Y
This does nothing
PHP
LDA $AD8C
JMP $48A4
Open bus
????

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 results unpredictable.

IAx5F

IAx5F_entry
????

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.

IAx60

IAx60_entry
????

This entry points to a location in WRAM, specifically the general purpose DMA buffer. With determination, the results can be predictable, though do note that the select menu makes use of this buffer. Most possibile circumstances are likely to crash.

IAx61

IAx61_entry
????

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 results are likely to crash.

IAx62

IAx62_entry
JSR $8035
Ancilla_SetSfxPan
STA $012D
RTS

This entry points to a location in ROM, specifically the Ancilla_DoSfx1 routine. The entry point aligns with the code perfectly and results in a stable return. This will take a sound effect and apply panning to it based on the location of the player; however, in this case, only a broken blip of noise will be played. This is because the sound effect attempted is $80. For sound effects in general, only the bottom 6 bits constitute the ID; the top 2 bytes are used to control panning. For SFX1, panning isn't actually taken into account; negative values actually indicate an instruction to fade the volume of this sound effect.

IAx63

IAx63_entry
????
Open bus

This entry points to a location in open bus. The results are unpredictable.

IAx64

IAx64_entry
LDA $0C22, X
Ancilla_MoveVert

This entry points to a location in ROM, specifically the Ancilla_MoveVert routine. The entry point is exactly the beginning of the routine, so the code runs perfectly and without fault. Technically, 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.

IAx65

IAx65_entry
????
IAx65_entry
ROL A
ROL A
ROL A
ROL A
Let's skip ahead
SED
SED
SED
SED
Let's skip ahead again…
SED
SED
SED
Just a little more…
SED
SED
????

This entry points to a location in open bus. In general, the results are unpredictable.

In some 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, but only when the HDMA transfers are known.

IAx66

IAx66_entry
RTS

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 subroutine to exit immediately.

IAx67

IAx67_entry
STA ($FA)
A=$A9
RTS

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.

Address Effect
$00 Scratch space Useless
$10 Game module Crash
$20 Link's Y-coordinate low byte Funny teleport
$30 Link's Y velocity Nothing; overwritten before used
$40 Temp variable for coordinates Nothing; overwritten before used
$50 Change direction flag Link cannot turn anymore
$60 Attract mode sequence counter Nothing; zeroed every frame as collateral damage
$70 Free RAM Nothing
$80 Free RAM Nothing
$90 OAM buffer location pointer Seemingly nothing; at worst, broken graphics for 1 frame
$A0 Room ID Repointed to EP big chest room or a nonexistent room
ID $01A9 corresponds to cape and mirror in SRAM
$B0 Subsubmodule Game will crash during transitions
$C0 Tilemap buffer pointer Will affect the exact results of somaria jukes
$D0 Room load pointers No effect; overwritten when required by the CPU
$E0 BG1 horizontal scroll low byte Seemingly nothing; fixed every frame
$F0 Joypad 1 inputs Changes input for joypad 1 to B+Select+Up+Right

IAx68

IAx68_entry
STA $0C0E, X
A=$D9
LDA $02
Could be anything
STA $0C04, X
LDA $03
STA $0C18, X
BRA +7
Ancilla_SpinSpark

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.

IAx69

This illegal ancilla shares the same pointer and effect as IAx5F.

IAx6A

IAx6A_entry
????

This entry points to a location in location in WRAM, specifically the flute cooldown timer. This results in unpredictable code or a BRK (followed by unpredictable code).

IAx6B

IAx6B_entry

This entry points to a location in WRAM, specifically the arbitrary DMA buffer. Results are fully unpredictable.

IAx6C

IAx6C_entry
ORA ($00, X)
PLX
SBC $F4FFFA, X
SBC $FA0001, X
SBC $F4FFFA, X
SBC $FA0001, X
SBC $F4FFFA, X
SBC $FA0001, X
boring…
SBC $F3FFFD, X
SBC $F8FFFF, X
SBC $0F0009, X
BRK #$06
brk_vector
BRL $B9F8
ADC ($71, X)
TAX
ADC ($EC), Y
ORA ($EC, X)
ORA ($70, X)
AND ($71), Y
AND ($72), Y
AND ($7E), Y
Not worth looking at…
ADC ($AB), Y
ADC ($AA), Y
ADC ($CF), Y
ORA $8DCF
CMP $CDCF4D
ORA $8D1F0D, X
ORA $CD1F4D, X
ORA ($0D, X)
ORA ($8D, X)
ORA ($4D, X)
ORA ($CD, X)
BNE +25
CMP ($19)
CMP ($19), Y
CMP ($19, S), Y
PEI ($0D)
DEC $0D, X
CMP $0D, X
CMP [$0D], Y
STA ($09, S), Y
BRL $840D
????
Open bus

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 unpredictable. A number of branch instructions can potentially occur, but overall results are likely to be a crash.

IAx6D

IAx6D_entry
????

This entry points to a location in WRAM, specifically an array of ancillae OAM priority values. Results are, you guessed it, unpredictable.

IAx6E

IAx6E_entry
????

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.

IAx6F

IAx6F_entry
????

This entry points to a location in open Bus. The results are unpredictable.

IAx70

IAx70_entry
????

This entry points to a location in WRAM, specifically the last byte in an array that counts the number spiral staircases. Results are completely unpredictable.

IAx71

IAx71_entry
????

This entry points to a location in WRAM, specifically a point of the arbitrary DMA buffer. Results are completely unpredictable.

IAx72

PHX
Code before entry for reference
LDA $7F5810, X
CMP #$0D
BEQ +118
Does not align with below
IAx72_entry
ROR $0A, X
CLC
ADC $7F5810, X
CLC
ADC #$02

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.

IAx73

IAx73_entry
SEP #$85
BRK #$E2
brk_vector
BRL $F00F
CMP $7E
TAY
AND #$E0
ORA $F0, S
ASL $98
SEC
SBC #$20
BRK #$A8
brk_vector
BRL $F00F

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.

IAx74

IAx74_entry
BRK #$99
brk_vector
BRL $BC11
EOR ($85), Y
etc.

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.

IAx75

IAx75_entry
????

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.

IAx76

This illegal ancilla shares the same pointer and effect as IAx61.

IAx77

IAx77_entry
STA $0C0E, X
PLA
STA $0BFA, X
PLA
STA $0C18, X
PLA
STA $0C04, X

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.

IAx78

IAx78_entry
????

This entry points to a location in WRAM, specifically a general purpose property of sprites. Results are unpredictable.

IAx79

IAx79_entry
????

This entry points to a location in WRAM, specifically part of the OAM buffer. Results will be similar to IAx4A.

IAx7A

IAx7A_entry
TSB $9091
Write to ROM ignored
LDA $01
SEC
SBC #$04
LDX $0FAC
LDA $8F82, X
LDY #$02
RTS

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.

IAx7B

This illegal ancilla shares the same pointer and effect as IAx78.

IAx7C

IAx7C_entry
????

This entry points to a location in WRAM, specifically the an array that counts the number spiral staircases. Results are completely unpredictable.

IAx7D

IAx7D_entry
BRA −115
????

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. The results are unpredictable.

Under certain circumstances, the CPU may eventually reach $08:8000, where it will put a broken sound effect into SFX set 1 and recover cleanly.

IAx7E

This illegal ancilla shares the same pointer and effect as IAx78.

IAx7F

IAx7F_entry
????

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. Results will generally be unpredictable, though being in room $60 can return safely.

IAx80

IAx80_entry
????

This entry points to a location in WRAM, specifically a general purpose property of F-slot ancillae. Results are unpredictable.

Further illegal ancilla

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.