Veran Warp
Cause
When a tile on the map is selected, it will usually just read a byte from a table, and tell the game to print the text corresponding to it. If out-of-bounds on the past map, the text index given may be invalid. (This won't be the case in the present, since it will just continue to read the past map's data.)
This isn't enough to make the game go haywire, even if the index is invalid. However, if the byte read has bit 7 set, the game performs some "special behaviour". This is meant for screens whose text can change (ie. moblin's keep).
In this code snippet from the _mapGetRoomTextOrReturn (02:619d)
function, register 'c' is the aforementioned byte:
ld c,(hl) ; $61c5
bit 7,c ; $61c6
ret z ; $61c8
ld a,c ; $61c9
and $07 ; $61ca
rst_jumpTable ; $61cc
.dw $61d7 ; 0
.dw $61f4 ; 1
.dw $6208 ; 2
.dw $6210 ; 3
.dw $6219 ; 4
Each ".dw" line is an address the game will jump to if 'c'&7 equals the corresponding value. There are exactly 5 special-behaviour functions defined (values 0-4). However, since 'c' was ANDed with 7, the possible values it may have are 0-7. Here's what it looks like if values 5-7 are interpreted as addresses:
ld a,c ; $61c9
and $07 ; $61ca
rst_jumpTable ; $61cc
.dw $61d7 ; 0
.dw $61f4 ; 1
.dw $6208 ; 2
.dw $6210 ; 3
.dw $6219 ; 4
.dw $fad5 ; 5
.dw $cc34 ; 6
.dw $1107 ; 7
So, if one of the invalid tiles in the past has bit 7 set, and has bits 0-2 equal a value between 5-7, the game will jump somewhere unexpected.
Here is a table of the values which come after the past map's last tile. Tiles with interesting behaviour are noted. (Note: tiles $ee and $ef reference the same data as $f0 and $f1.)
Index | Value | Behaviour |
---|---|---|
$e0 | $48 | |
$e1 | $88 | |
$e2 | $8d | Jump to $fad5 |
$e3 | $88 | |
$e4 | $83 | |
$e5 | $88 | |
$e6 | $ba | |
$e7 | $88 | |
$e8 | $03 | |
$e9 | $88 | |
$ea | $0a | |
$eb | $88 | |
$ec | $3c | |
$ed | $a8 | |
$f0 | $90 | |
$f1 | $98 | |
$f2 | $03 | |
$f3 | $88 | |
$f4 | $ac | |
$f5 | $ff | Jump to $1107 |
$f6 | $13 | |
$f7 | $af | Jump to $1107 |
$f8 | $78 | |
$f9 | $ff | Jump to $1107 |
$fa | $c1 | |
$fb | $ff | Jump to $1107 |
$fc | $0b | |
$fd | $aa | |
$fe | $05 | |
$ff | $99 |
Unfortunately, the jump to $1107 is uninteresting. It lands in the middle of the "setDeathRespawnPoint" function, but it does not have the effect of changing Link's respawn point, since HL is still pointing to ROM. It ends up writing several values to ROM (also starting at $1107), which only has the effect of writing to the "SRAM Enable" register of the MBC5.
The japanese version is the same as the US version for this jump, but the EU version actually tends to crash on these tiles.
As speedrunners know, though, the jump to $fad5 is very interesting indeed.
Veran Warp tile (Index $e2)
As explained above, the game jumps to $fad5. This falls into the gameboy's WRAM mirror ($f000-$fdff
mirrors the values of $d000-$ddff
, which contains the game's object data). Since there tend to be very few objects on-screen when this glitch is done, the game runs NOP opcodes until it reaches $fe00
, OAM memory.
OAM memory is inaccessible except at hblank and vblank. If it happens to be accessible at the time, the OAM can be manipulated to perform arbitrary code execution. This is extremely finicky since the OAM is usually inaccessible in this situation.
If the OAM is inaccessible, it will immediately read $ff
, or the "rst $38" opcode, which is the same as "call $0038". For some reason, the game has this apparently unused code at address $38:
nop ; $0038
nop ; $0039
nop ; $003a
pop hl ; $003b
pop de ; $003c
pop bc ; $003d
pop af ; $003e
reti ; $003f
Here's what happens next:
- After above pop opcodes run, it returns to 01f8 (getLogA_v2)
- After that function, it returns to 02:5b6e (middle of
_inventorySubmenu0_drawStoredItems
function)- This is in the middle of a loop which overwrites a lot of memory. Contents of bc, de, hl are irrelevant; however, the value of
$ff8d
is abnormally high when it jumps here, and this causes unexpected memory overwriting to occur. The value of$ff8d
is the value of a sprite's X-position when you first open the map.
- This is in the middle of a loop which overwrites a lot of memory. Contents of bc, de, hl are irrelevant; however, the value of
Address | Value | Use in glitch | Set from |
---|---|---|---|
c212 | fe01 | pop hl | |
c214 | 6192 | pop de | |
c216 | 6124 | pop bc | |
c218 | 1a62 | pop af | |
c21a | 01f8 | reti (from 003f) | |
c21c | 5b6e | ret (from getLogA_v2) | Call to "updateMenus" function. Supposed to return to bank 1, but returns to bank 2 instead. |
c21e | 33c4 | ret (from _inventorySubmenu0_drawStoredItems) | Call to "runGameLogic" function. If the game fails to return here, it will almost surely crash. (This is the end of the stack.) |
Memory overwrite loop
The value of $ff8d
- or, the first sprite's X position - will determine what memory gets overwritten. The function it ends up in was originally written to draw the items in your inventory. The original logic of the function works like this:
- Prior to calling this function, wram bank 4 is expected to be loaded.
- For values of
ff8d
from$10
to$01
:- The item index equals the value of
[wInventoryStorage + [$ff8d] - 1]
. - Load that item's "treasure display data" to $cec0 by calling
loadTreasureDisplayData
. - Read an address from
$5b80+[$ff8d]*2
. This is where to write the item's "graphics map" to. - It proceeds to call
_drawTreasureDisplayDataToBg
, with parameters 'de' (the address from the last step) and 'hl' ($cec1, where the tresure display data was loaded).- This writes to 'de', which is expected to reference
w4TileMap
. It also ORs de with $400 and writes there, under the assumption that this will write to w4AttributeMap.
- This writes to 'de', which is expected to reference
- The item index equals the value of
The details are rather complicated, but in summary: the addresses it writes to depend solely on $ff8d
and no other variables. The values it writes depends on what "item" it thinks it's writing there.
Second A-press (re-opened map)
$ff8d cycles from about $50 to $3d, then loops back to $64 up to $5e. Then it turns to $00 and the player gains control.
Third A-press (immediately after 2nd without re-opening map)
$ff8d cycles from $00 to $f3, then control returns to the player. After pressing A again, it then cycles from $f3 to $bc. Along the way, it hits $c9, which may spawn Veran.
Memory overwrite table
The list of addresses it writes to in the below table is not necessarily exhaustive. It usually also writes to the bytes proceeding the ones listed. It should not deviate far from the given values, though (unless they cause other effects like changing the rom bank, then anything goes).
The memory-overwriting loop would be stable, if it weren't for the fact that it sometimes overwrites the ROM bank number. This causes the code being executed to change the moment it's written to. This can cause crashes and other abnormal effects, but sometimes this just has the effect of ending the loop and returning control to the player.
From $ff8d's starting value, the game keeps decrementing it until it reaches 0, or the ROM bank gets overwritten. If this happens, with some luck, the game will manage to return to the main thread and not crash.
FF8D value | "Inventory slot" (source of data) | Writes to addresses | Notes |
---|---|---|---|
$01
|
$c68a (wInventoryStorage+$0)
|
$d063,$d083,$d463,$d483
| |
$02
|
$c68b (wInventoryStorage+$1)
|
$d067,$d087,$d467,$d487
| |
$03
|
$c68c (wInventoryStorage+$2)
|
$d06b,$d08b,$d46b,$d48b
| |
$04
|
$c68d (wInventoryStorage+$3)
|
$d06f,$d08f,$d46f,$d48f
| |
$05
|
$c68e (wInventoryStorage+$4)
|
$d0c3,$d0e3,$d4c3,$d4e3
| |
$06
|
$c68f (wInventoryStorage+$5)
|
$d0c7,$d0e7,$d4c7,$d4e7
| |
$07
|
$c690 (wInventoryStorage+$6)
|
$d0cb,$d0eb,$d4cb,$d4eb
| |
$08
|
$c691 (wInventoryStorage+$7)
|
$d0cf,$d0ef,$d4cf,$d4ef
| |
$09
|
$c692 (wInventoryStorage+$8)
|
$d123,$d143,$d523,$d543
| |
$0a
|
$c693 (wInventoryStorage+$9)
|
$d127,$d147,$d527,$d547
| |
$0b
|
$c694 (wInventoryStorage+$a)
|
$d12b,$d14b,$d52b,$d54b
| |
$0c
|
$c695 (wInventoryStorage+$b)
|
$d12f,$d14f,$d52f,$d54f
| |
$0d
|
$c696 (wInventoryStorage+$c)
|
$d183,$d1a3,$d583,$d5a3
| |
$0e
|
$c697 (wInventoryStorage+$d)
|
$d187,$d1a7,$d587,$d5a7
| |
$0f
|
$c698 (wInventoryStorage+$e)
|
$d18b,$d1ab,$d58b,$d5ab
| |
$10
|
$c699 (wInventoryStorage+$f)
|
$d18f,$d1af,$d58f,$d5af
| |
$11
|
$c69a (wObtainedTreasureFlags+$0)
|
$9221,$9241,$9621,$9641
| |
$12
|
$c69b (wObtainedTreasureFlags+$1)
|
$2a5e,$2a7e,$2e5e,$2e7e
| |
$13
|
$c69c (wObtainedTreasureFlags+$2)
|
$28b7,$28d7,$2cb7,$2cd7
| |
$14
|
$c69d (wObtainedTreasureFlags+$3)
|
$e026,$e046,$e426,$e446
|
Possible music corruption here |
$15
|
$c69e (wObtainedTreasureFlags+$4)
|
$c9ac,$cd8c,$cdac
|
$c9ac+: rooms in d5 (keyblock room, bridge room) $cd8c+: wStaticObjects |
$16
|
$c69f (wObtainedTreasureFlags+$5)
|
$1368,$1748,$1768
| |
$17
|
$c6a0 (wObtainedTreasureFlags+$6)
|
$1b30,$1b50,$1f30,$1f50
| |
$18
|
$c6a1 (wObtainedTreasureFlags+$7)
|
$8be0,$8fe0,$9000
| |
$19
|
$c6a2 (wObtainedTreasureFlags+$8)
|
$c94a,$cd2a,$cd4a
|
$c94a+: Rooms in d3 (boss room and prior rooms) $cd2a+: Area / tileset stuff |
$1a
|
$c6a3 (wObtainedTreasureFlags+$9)
|
$583e,$5c1e,$5c3e
| |
$1b
|
$c6a4 (wObtainedTreasureFlags+$a)
|
$f0e5,$f105,$f4e5,$f505
| |
$1c
|
$c6a5 (wObtainedTreasureFlags+$b)
|
$c9ac,$cd8c,$cdac
|
Same as $15 |
$1d
|
$c6a6 (wObtainedTreasureFlags+$c)
|
$12f6,$16d6,$16f6
| |
$1e
|
$c6a7 (wObtainedTreasureFlags+$d)
|
$c943,$cd23,$cd43
|
$c943+: Early rooms in d2 $cd23+: Area / tileset stuff |
$1f
|
$c6a8 (wObtainedTreasureFlags+$e)
|
$593c,$5d1c,$5d3c
| |
$20
|
$c6a9 (wObtainedTreasureFlags+$f)
|
$e14e,$e16e,$e54e,$e56e
| |
$21
|
$c6aa (wLinkHealth)
|
$113a,$115a,$153a,$155a
| |
$22
|
$c6ab (wLinkMaxHealth)
|
$d3e0,$d7e0,$d800
| |
$23
|
$c6ac (wNumHeartPieces)
|
$68cd,$68ed,$6ccd,$6ced
| |
$24
|
$c6ad (wNumRupees)
|
$7900,$7920,$7d00,$7d20
| |
$25
|
$c6ae (wNumRupees+1)
|
$2312,$2332,$2712,$2732
| |
$26
|
$c6af (wShieldLevel)
|
$1823,$1843,$1c23,$1c43
| |
$27
|
$c6b0 (wNumBombs)
|
$fad6,$faf6,$fed6,$fef6
| |
$28
|
$c6b1 (wMaxBombs)
|
$c2ec,$c6cc,$c6ec
|
$c6cc = wRingBoxLevel $c6cd = wNumUnappraisedRingsBcd $c6ce = wNumRingsAppraised $c6ec = wPirateShipRoom $c6ed = wPirateShipY |
$29
|
$c6b2 (wSwordLevel)
|
$03fe,$07fe,$081e
| |
$2a
|
$c6b3 (wNumBombchus)
|
$0a48,$0e28,$0e48
| |
$2b
|
$c6b4 (wSeedSatchelLevel)
|
$3341,$3721,$3741
| |
$2c
|
$c6b5 (wFluteIcon)
|
$db7c,$df5c,$df7c
| |
$2d
|
$c6b6 (wSwitchHookLevel)
|
$4a4a,$4e2a,$4e4a
| |
$2e
|
$c6b7 (wSelectedHarpSong)
|
$0306,$0326,$0706,$0726
| |
$2f
|
$c6b8 (wBraceletLevel)
|
$228f,$266f,$268f
| |
$30
|
$c6b9 (wNumEmberSeeds)
|
$c9f1,$cdd1,$cdf1
|
$c9f1+: Black tower rooms $cdd1: wNumEnemies $cdd2: wToggleBlocksState $cdd3: wSwitchState |
$31
|
$c6ba (wNumScentSeeds)
|
$5925,$5d05,$5d25
| |
$32
|
$c6bb (wNumPegasusSeeds)
|
$f1ed,$f5cd,$f5ed
| |
$33
|
$c6bc (wNumGaleSeeds)
|
$c85c,$c87c,$cc5c,$cc7c
|
$c85c+: D8 entrance $c87c+: Sea of no return entrance $cc5c: wLinkInAir $cc5d: wLinkSwimmingState $cc5e: ? $cc7c+: Part of wGrabbableObjectBuffer |
$34
|
$c6bd (wNumMysterySeeds)
|
$7847,$7867,$7c47,$7c67
| |
$35
|
$c6be (wNumGashaSeeds)
|
$3121,$3141,$3521,$3541
| |
$36
|
$c6bf (wEssencesObtained)
|
$d37c,$d75c,$d77c
| |
$37
|
$c6c0 (wTradeItem)
|
$127e,$165e,$167e
| |
$38
|
$c6c1 (wc6c1)
|
$78d1,$78f1,$7cd1,$7cf1
| |
$39
|
$c6c2 (wTuniNutState)
|
$c141,$c521,$c541
| |
$3a
|
$c6c3 (wNumSlates)
|
$d3e6,$d7c6,$d7e6
| |
$3b
|
$c6c4 (wSatchelSelectedSeeds)
|
$fa9e,$fe7e,$fe9e
| |
$3c
|
$c6c5 (wShooterSelectedSeeds)
|
$28ff,$291f,$2cff,$2d1f
|
Changes the ROM bank, so this can crash. Equipping ember or pegasus seeds appears to prevent the crash. (Gambatte doesn't seem to crash at all, though.) |
$3d
|
$c6c6 (wRingBoxContents+0)
|
$c130,$c510,$c530
| |
$3e
|
$c6c7 (wRingBoxContents+1)
|
$784f,$786f,$7c4f,$7c6f
| |
$3f
|
$c6c8 (wRingBoxContents+2)
|
$eb41,$ef21,$ef41
| |
$40
|
$c6c9 (wRingBoxContents+3)
|
$d3f3,$d7d3,$d7f3
| |
$41
|
$c6ca (wRingBoxContents+4)
|
$f299,$f679,$f699
| |
$42
|
$c6cb (wActiveRing)
|
$73e0,$77c0,$77e0
| |
$43
|
$c6cc (wRingBoxLevel)
|
$c999,$cd79,$cd99
|
$c999+: D5 rooms (Includes d5 boss keys room and others leading to it) |
$44
|
$c6cd (wNumUnappraisedRingsBcd)
|
$72fe,$731e,$76fe,$771e
| |
$45
|
$c6ce (wc6ce)
|
$01e1,$05c1,$05e1
| |
$46
|
$c6cf (wc6cf)
|
$db20,$db40,$df20,$df40
| |
$47
|
$c6d0 (wGlobalFlags+0)
|
$c91a,$ccfa,$cd1a
|
$c91a+: D1 rooms |
$48
|
$c6d1 (wGlobalFlags+1)
|
$c2e6,$c6c6,$c6e6
|
$c6c6 and $c6c7 are the first 2 rings in the ring box.
$c6e6: Maku tree text index |
$49
|
$c6d2 (wGlobalFlags+2)
|
$ea1c,$ea3c,$ee1c,$ee3c
|
$ca1c+: D6 BK room and others $ca3c+: D6 past rooms |
$4a
|
$c6d3 (wGlobalFlags+3)
|
$d3ef,$d7ef,$d80f
| |
$4b
|
$c6d4 (wGlobalFlags+4)
|
$8211,$8231,$8611,$8631
| |
$4c
|
$c6d5 (wGlobalFlags+5)
|
$3af1,$3ed1,$3ef1
| |
$4d
|
$c6d6 (wGlobalFlags+6)
|
$c3fe,$c7fe,$c81e
| |
$4e
|
$c6d7 (wGlobalFlags+7)
|
$72fe,$731e,$76fe,$771e
| |
$4f
|
$c6d8 (wGlobalFlags+8)
|
$e277,$e657,$e677
|
Gives small keys for Dungeon 5, Dungeon 6 present, and (sometimes) Dungeon 7. See here for more information. |
$50
|
$c6d9 (wGlobalFlags+9)
|
$cbf0,$cff0,$d010
| |
$51
|
$c6da (wGlobalFlags+10)
|
$8357,$8737,$8757
| |
$52
|
$c6db (wGlobalFlags+11)
|
$a8ed,$accd,$aced
|
Savefile corruption? |
$53
|
$c6dc (wGlobalFlags+12)
|
$7a01,$7a21,$7e01,$7e21
| |
$54
|
$c6dd (wGlobalFlags+13)
|
$0fe6,$1006
| |
$55
|
$c6de (wGlobalFlags+14)
|
$1181,$11a1,$1581,$15a1
| |
$56
|
$c6df (wGlobalFlags+15)
|
$d062,$d082,$d462,$d482
| |
$57
|
$c6e0 (wc6e0)
|
$68cd,$68ed,$6ccd,$6ced
| |
$58
|
$c6e1 (wc6e1)
|
$c900,$c920,$cd00,$cd20
|
$c900+: Maku path rooms $c920+: D1 rooms |
$59
|
$c6e2 (wc6e2)
|
$83a4,$8784,$87a4
| |
$5a
|
$c6e3 (wc6e3)
|
$89aa,$8d8a,$8daa
| |
$5b
|
$c6e4 (wc6e4)
|
$8190,$81b0,$8590,$85b0
| |
$5c
|
$c6e5 (wc6e5)
|
$8332,$8712,$8732
| |
$5d
|
$c6e6 (wc6e6)
|
$892c,$8d0c,$8d2c
| |
$5e
|
$c6e7 (wc6e7)
|
$2106,$2126,$2506,$2526
| |
$5f
|
$c6e8 (wMakuTreeState)
|
$590a,$5cea,$5d0a
| |
$60
|
$c6e9 (wJabuWaterLevel)
|
$e011,$e031,$e411,$e431
| |
$61
|
$c6ea (wc6ea)
|
$02f3,$06d3,$06f3
| |
$62
|
$c6eb (wc6eb)
|
$c92b,$cd0b,$cd2b
|
$c92b = d2 boss room $c92c = d2 room underneath swoop $cd0b = wRoomHeight (but this is rarely used) $cd0c = wScreenTransitionBoundaryX; this is what causes the glitch that makes Link oscillate and allows him to always screen transition. $cd0d = wScreenTransitionBoundaryY $cd2b = wLoadedAreaAnimation $cd2c = wLastToggleBlocksState |
$63
|
$c6ec (wPirateShipRoom)
|
$00a6,$0486,$04a6
| |
$64
|
$c6ed (wPirateShipY)
|
$0806,$0826,$0c06,$0c26
| |
$65
|
$c6ee (wPirateShipX)
|
$3998,$3d78,$3d98
| |
$66
|
$c6ef (wPirateShipAngle)
|
$bb41,$bf21,$bf41
|
Savefile corruption? |
$67
|
$c6f0 (wc6f0)
|
$c9e6,$cdc6,$cde6
|
$c9e6+: Black tower rooms |
$68
|
$c6f1
|
$0205,$0225,$0605,$0625
| |
$69
|
$c6f2
|
$1a20,$1a40,$1e20,$1e40
| |
$6a
|
$c6f3
|
$78c5,$78e5,$7cc5,$7ce5
| |
$6b
|
$c6f4
|
$b221,$b241,$b621,$b641
| |
$6c
|
$c6f5
|
$db7c,$df5c,$df7c
| |
$6d
|
$c6f6
|
$624a,$662a,$664a
| |
$6e
|
$c6f7
|
$016f,$018f,$056f,$058f
| |
$6f
|
$c6f8
|
$0202,$0222,$0602,$0622
| |
$70
|
$c6f9
|
$0331,$0711,$0731
| |
$71
|
$c6fa
|
$c920,$cd00,$cd20
|
Similar to $58 |
$72
|
$c6fb
|
$5928,$5d08,$5d28
| |
$73
|
$c6fc
|
$78c1,$78e1,$7cc1,$7ce1
| |
$74
|
$c6fd
|
$db41,$df21,$df41
| |
$75
|
$c6fe
|
$d3f3,$d7d3,$d7f3
| |
$76
|
$c6ff
|
$0036,$0056,$0436,$0456
| |
$77
|
$c700 (present map $00)
|
$2005,$2025,$2405,$2425
| |
$78
|
$c701 (present map $01)
|
$fad9,$faf9,$fed9,$fef9
| |
$79
|
$c702 (present map $02)
|
$c2cc,$c6ac,$c6cc
|
$c2cc is part of a stack... $c6ac = wNumHeartPieces $c6ad = wNumRupees (low byte) $c6cc = wRingBoxLevel $c6cd = wNumUnappraisedRingsBcd $c6ce = wNumRingsAppraised |
$7a
|
$c703 (present map $03)
|
$214f,$216f,$254f,$256f
| |
$7b
|
$c704 (present map $04)
|
$d3e9,$d7e9,$d809
| |
$7c
|
$c705 (present map $05)
|
$73a6,$7786,$77a6
| |
$7d
|
$c706 (present map $06)
|
$b399,$b779,$b799
| |
$7e
|
$c707 (present map $07)
|
$1348,$1728,$1748
| |
$7f
|
$c708 (present map $08)
|
$10c6,$10e6,$14c6,$14e6
| |
$80
|
$c709 (present map $09)
|
$4fea,$500a
| |
$81
|
$c70a (present map $0a)
|
$21d1,$21f1,$25d1,$25f1
| |
$82
|
$c70b (present map $0b)
|
$58f8,$5cd8,$5cf8
| |
$83
|
$c70c (present map $0c)
|
$2ac5,$2ae5,$2ec5,$2ee5
| |
$84
|
$c70d (present map $0d)
|
$ca31,$ce11,$ce31
|
$ca31+: D6 past underwater rooms |
$85
|
$c70e (present map $0e)
|
$c9f0,$cdd0,$cdf0
|
$c9f0+: Ganon's tower rooms
$cdd0: wEnemiesKilledListTail $cdd1: wNumEnemiesKilles $cdd2: wToggleBlocksState |
$86
|
$c70f (present map $0f)
|
$0068,$0088,$0468,$0488
| |
$87
|
$c710 (present map $10)
|
$18ed,$1ccd,$1ced
| |
$88
|
$c711 (present map $11)
|
$c15d,$c17d,$c55d,$c57d
| |
$89
|
$c712 (present map $12)
|
$200d,$202d,$240d,$242d
| |
$8a
|
$c713 (present map $13)
|
$faf1,$fb11,$fef1,$ff11
| |
$8b
|
$c714 (present map $14)
|
$c854,$cc34,$cc54
|
$c854+: Past overworld rooms
$cc34: wAreaFlags $cc35: wActiveMusic $cc36: wGrassAnimationModifier |
$8c
|
$c715 (present map $15)
|
$80e6,$8106,$84e6,$8506
| |
$8d
|
$c716 (present map $16)
|
$4b27,$4f07,$4f27
| |
$8e
|
$c717 (present map $17)
|
$e821,$e841,$ec21,$ec41
|
$c821+: Past Talus Peaks rooms
$c841+: More Past Talus Peaks rooms $cc21: wLinkLocalRespawnY $cc22: wLinkLocalRespawnX $cc23: wLinkLocalRespawnDir $cc41+: Dungeon-related vars |
$8f
|
$c718 (present map $18)
|
$82f3,$86d3,$86f3
| |
$90
|
$c719 (present map $19)
|
$7977,$7997,$7d77,$7d97
| |
$91
|
$c71a (present map $1a)
|
$83a7,$8787,$87a7
| |
$92
|
$c71b (present map $1b)
|
$2181,$21a1,$2581,$25a1
| |
$93
|
$c71c (present map $1c)
|
$58e4,$5cc4,$5ce4
| |
$94
|
$c71d (present map $1d)
|
$11df,$11ff,$15df,$15ff
| |
$95
|
$c71e (present map $1e)
|
$d06e,$d08e,$d46e,$d48e
| |
$96
|
$c71f (present map $1f)
|
$18ed,$1ccd,$1ced
| |
$97
|
$c720 (present map $20)
|
$1a7d,$1e5d,$1e7d
| |
$98
|
$c721 (present map $21)
|
$c370,$c390,$c770,$c790
|
$c770+: Fairy's woods screens |
$99
|
$c722 (present map $22)
|
$593c,$5d1c,$5d3c
| |
$9a
|
$c723 (present map $23)
|
$d084,$d0a4,$d484,$d4a4
| |
$9b
|
$c724 (present map $24)
|
$d087,$d0a7,$d487,$d4a7
| |
$9c
|
$c725 (present map $25)
|
$d0c9,$d0e9,$d4c9,$d4e9
| |
$9d
|
$c726 (present map $26)
|
$d129,$d149,$d529,$d549
| |
$9e
|
$c727 (present map $27)
|
$d167,$d187,$d567,$d587
| |
$9f
|
$c728 (present map $28)
|
$d164,$d184,$d564,$d584
| |
$a0
|
$c729 (present map $29)
|
$d122,$d142,$d522,$d542
| |
$a1
|
$c72a (present map $2a)
|
$d0c2,$d0e2,$d4c2,$d4e2
| |
$a2
|
$c72b (present map $2b)
|
$0118,$0138,$0518,$0538
| |
$a3
|
$c72c (present map $2c)
|
$0119,$0139,$0519,$0539
| |
$a4
|
$c72d (present map $2d)
|
$1aff,$1b1f,$1eff,$1f1f
| |
$a5
|
$c72e (present map $2e)
|
$1b01,$1b21,$1f01,$1f21
| |
$a6
|
$c72f (present map $2f)
|
$fb21,$ff01,$ff21
| |
$a7
|
$c730 (present map $30)
|
$031c,$033c,$071c,$073c
| |
$a8
|
$c731 (present map $31)
|
$031d,$033d,$071d,$073d
| |
$a9
|
$c732 (present map $32)
|
$1b1f,$1eff,$1f1f
| |
$aa
|
$c733 (present map $33)
|
$1b23,$1f03,$1f23
| |
$ab
|
$c734 (present map $34)
|
$fb23,$ff03,$ff23
| |
$ac
|
$c735 (present map $35)
|
$7800,$7820,$7c00,$7c20
| |
$ad
|
$c736 (present map $36)
|
$7905,$7925,$7d05,$7d25
| |
$ae
|
$c737 (present map $37)
|
$fb25,$ff05,$ff25
| |
$af
|
$c738 (present map $38)
|
$7a40,$7a60,$7e40,$7e60
| |
$b0
|
$c739 (present map $39)
|
$7b05,$7b25,$7f05,$7f25
| |
$b1
|
$c73a (present map $3a)
|
$fb25,$ff05,$ff25
| |
$b2
|
$c73b (present map $3b)
|
$7b42,$7b62,$7f42,$7f62
| |
$b3
|
$c73c (present map $3c)
|
$7a25,$7a45,$7e25,$7e45
| |
$b4
|
$c73d (present map $3d)
|
$fb45,$ff25,$ff45
| |
$b5
|
$c73e (present map $3e)
|
$0201,$0221,$0601,$0621
| |
$b6
|
$c73f (present map $3f)
|
$0023,$0403,$0423
| |
$b7
|
$c740 (present map $40)
|
$0225,$0605,$0625
| |
$b8
|
$c741 (present map $41)
|
$0807,$0827,$0c07,$0c27
| |
$b9
|
$c742 (present map $42)
|
$6165,$6185,$6565,$6585
| |
$ba
|
$c743 (present map $43)
|
$e180,$e560,$e580
| |
$bb
|
$c744 (present map $44)
|
$c91a,$ccfa,$cd1a
|
Similar to $47 |
$bc
|
$c745 (present map $45)
|
$21c6,$21e6,$25c6,$25e6
|
Writes to ROM bank number. Veran Warp has a tendency to crash here.
This is typically where veran warp ends. When successful, the ROM bank number changes, but the game still manages to return properly. Although veran warp does crash here sometimes, it seems that this doesn't actually cause the crash; the actual cause appears to be related to the timer interrupt being corrupted from when |
$bd
|
$c746 (present map $46)
|
$5921,$5d01,$5d21
|
Writes to RAM bank number. |
$be
|
$c747 (present map $47)
|
$7af7,$7ed7,$7ef7
| |
$bf
|
$c748 (present map $48)
|
$e1b7,$e1d7,$e5b7,$e5d7
|
Writes to thread 1's stack; could cause a crash? (not likely though, since it's pretty far down in the stack)
$c5b7+ = wSavefileString; corruption shouldn't matter since it's rewritten upon saving $c5d7+ = wUnappraisedRings |
$c0
|
$c749 (present map $49)
|
$00c9,$00e9,$04c9,$04e9
| |
$c1
|
$c74a (present map $4a)
|
$0301,$0321,$0701,$0721
| |
$c2
|
$c74b (present map $4b)
|
$1105,$1125,$1505,$1525
| |
$c3
|
$c74c (present map $4c)
|
$e321,$e701,$e721
|
$c701+ = Talus Peaks screens (Note: $c702 is used as an address for ring box corruption)
$c721+ = Talus Peaks present screens |
$c4
|
$c74d (present map $4d)
|
$79e5,$7a05,$7de5,$7e05
| |
$c5
|
$c74e (present map $4e)
|
$cb72,$cb92,$cf72,$cf92
| |
$c6
|
$c74f (present map $4f)
|
$73d4,$73f4,$77d4,$77f4
| |
$c7
|
$c750 (present map $50)
|
$90eb,$94cb,$94eb
| |
$c8
|
$c751 (present map $51)
|
$3943,$3d23,$3d43
|
Writes to high bit of ROM bank number. Veran warp has a tendency to crash here. |
$c9
|
$c752 (present map $52)
|
$f240,$f620,$f640
|
Spawns an interaction. |
$ca
|
$c753 (present map $53)
|
$3b01,$3ee1,$3f01
|
Writes to high bit of ROM bank number. I'm not sure if this is an issue or not, since Ages doesn't have enough ROM space for this to matter normally? |
$cb
|
$c754 (present map $54)
|
$d340,$d720,$d740
|
Writes to interaction slots. Spawns veran, possibly other objects under other circumstances. In order for veran to appear, it must write: nonzero to $dx40, and $2d to $dx41. ($dx40 enables it, and $dx41 sets the "id" to "veran".) |
$cc
|
$c755 (present map $55)
|
$2005,$2025,$2405,$2425
|
Writes to ROM bank number. |
$cd
|
$c756 (present map $56)
|
$c9ed,$ca0d,$cded,$ce0d
|
$c9ed+: Black Tower screens
$ca0d+: Unused screens |
$ce
|
$c757 (present map $57)
|
$4b4a,$4f2a,$4f4a
| |
$cf
|
$c758 (present map $58)
|
$434a,$472a,$474a
| |
$d0
|
$c759 (present map $59)
|
$39cd,$39ed,$3dcd,$3ded
|
Writes to high bit of ROM bank. |
$d1
|
$c75a (present map $5a)
|
$187d,$1c5d,$1c7d
| |
$d2
|
$c75b (present map $5b)
|
$4b4a,$4f2a,$4f4a
| |
$d3
|
$c75c (present map $5c)
|
$434a,$472a,$474a
| |
$d4
|
$c75d (present map $5d)
|
$39cd,$39ed,$3dcd,$3ded
|
Writes to high bit of ROM bank. |
$d5
|
$c75e (present map $5e)
|
$3a7d,$3e5d,$3e7d
|
Writes to high bit of ROM bank. |
$d6
|
$c75f (present map $5f)
|
$c940,$cd20,$cd40
|
$c940+: D2 rooms
$cd20+: Area / tileset vars |
$d7
|
$c760 (present map $60)
|
$0068,$0088,$0468,$0488
| |
$d8
|
$c761 (present map $61)
|
$8bf0,$8ff0,$9010
| |
$d9
|
$c762 (present map $62)
|
$0a67,$0e47,$0e67
| |
$da
|
$c763 (present map $63)
|
$2a07,$2a27,$2e07,$2e27
|
Writes to ROM bank. Has a tendency to crash veran warp.
On its first call to @writeTile, the ROM bank gets overwritten, but it manages to return. On its second call, de has been changed from the previous ROM bank switch. In at least one crash case, de = $e00b; so the RAM values to be written to are $e00b, $e02b, $e40b, $e42b, and the bytes immediately after those. The write to $e00b and $e00c is particularly bad since this is a function in RAM; this can cause the timer interrupt to crash. (It almost certainly will, since that happens to be on the opcode which restores the ROM bank...) In one non-crashing case, de = $8901; an innocent value pointing to vram. $e42b ($c42b) is part of wVBlankFunctionQueue; this could also cause a crash if it overwrites something currently being queued. Not sure if this happens in practice. |
$db
|
$c764 (present map $64)
|
$a0c3,$a0e3,$a4c3,$a4e3
|
Savefile corruption? |
$dc
|
$c765 (present map $65)
|
$d173,$d553,$d573
| |
$dd
|
$c766 (present map $66)
|
$b399,$b779,$b799
|
Savefile corruption? |
$de
|
$c767 (present map $67)
|
$0948,$0d28,$0d48
| |
$df
|
$c768 (present map $68)
|
$0024,$0404,$0424
| |
$e0
|
$c769 (present map $69)
|
$3087,$30a7,$3487,$34a7
| |
$e1
|
$c76a (present map $6a)
|
$cb02,$cb22,$cf02,$cf22
| |
$e2
|
$c76b (present map $6b)
|
$4bf8,$4fd8,$4ff8
| |
$e3
|
$c76c (present map $6c)
|
$60cd,$60ed,$64cd,$64ed
| |
$e4
|
$c76d (present map $6d)
|
$d15d,$d17d,$d55d,$d57d
| |
$e5
|
$c76e (present map $6e)
|
$3ae9,$3ec9,$3ee9
| |
$e6
|
$c76f (present map $6f)
|
$1202,$1222,$1602,$1622
| |
$e7
|
$c770 (present map $70)
|
$d2cb,$d2eb,$d6cb,$d6eb
| |
$e8
|
$c771 (present map $71)
|
$123d,$125d,$163d,$165d
| |
$e9
|
$c772 (present map $72)
|
$203e,$205e,$243e,$245e
|
Writes to ROM bank. |
$ea
|
$c773 (present map $73)
|
$68cd,$68ed,$6ccd,$6ced
| |
$eb
|
$c774 (present map $74)
|
$3a20,$3e00,$3e20
| |
$ec
|
$c775 (present map $75)
|
$1201,$1221,$1601,$1621
| |
$ed
|
$c776 (present map $76)
|
$92cb,$92eb,$96cb,$96eb
| |
$ee
|
$c777 (present map $77)
|
$123c,$125c,$163c,$165c
| |
$ef
|
$c778 (present map $78)
|
$c9d1,$c9f1,$cdd1,$cdf1
|
$c9d1+: Black tower turret screens
The rest is similar to $30. |
$f0
|
$c779 (present map $79)
|
$1279,$1299,$1679,$1699
| |
$f1
|
$c77a (present map $7a)
|
$d2cb,$d2eb,$d6cb,$d6eb
| |
$f2
|
$c77b (present map $7b)
|
$1278,$1298,$1678,$1698
| |
$f3
|
$c77c (present map $7c)
|
$203e,$205e,$243e,$245e
|
Writes to the ROM bank. Doesn't seem to cause crashes, but if you've traded to get the poe clock, control returns to the player here. You can just press A again to continue. (So, there are 3 A presses in total, instead of 2.) |
$f4
|
$c77d (present map $7d)
|
$68cd,$68ed,$6ccd,$6ced
| |
$f5
|
$c77e (present map $7e)
|
$7800,$7820,$7c00,$7c20
| |
$f6
|
$c77f (present map $7f)
|
$cb12,$cb32,$cf12,$cf32
| |
$f7
|
$c780 (present map $80)
|
$7992,$79b2,$7d92,$7db2
| |
$f8
|
$c781 (present map $81)
|
$123c,$125c,$163c,$165c
| |
$f9
|
$c782 (present map $82)
|
$c9e9,$cdc9,$cde9
|
$c9e9+: Black tower rooms (including the room where you present the essences) |
$fa
|
$c783 (present map $83)
|
$59e0,$5dc0,$5de0
| |
$fb
|
$c784 (present map $84)
|
$325e,$363e,$365e
| |
$fc
|
$c785 (present map $85)
|
$48cd,$48ed,$4ccd,$4ced
| |
$fd
|
$c786 (present map $86)
|
$d017,$d037,$d417,$d437
| |
$fe
|
$c787 (present map $87)
|
$6801,$6821,$6c01,$6c21
| |
$ff
|
$c788 (present map $88)
|
$fa20,$fa40,$fe20,$fe40
|
Crash cases
$ff8d = $bc,$cc,$da
These values for $ff8d all have a similar effect:
- ROM bank switches to 1
- Jumps to 03:6306 -> 03:6318
- Jumps to "updateAllObjects"
- Calls many functions including "updateAnimations"
- Rets to 2a07 (or somewhere else depending on which index this is?), then to 5d23 (this resumes the memory-overwriting loop).
Now, it will proceed to overwrite the next few bytes, as normal. The value of 'de' after the above shenanigans will determine where it writes to. If de=$e00a, as sometimes occurs, this causes a crash later on due to corrupting code in RAM.
The value of 'de' is determined by the call to "updateAnimations". So, this very common cause of crashing is ultimately determined by the state of the animations on-screen. Note that animations reset when paused; so, it may be possible to avoid crashes by being very precise with timing the second pause during the veran warp setup.
Potential memory corruptions
- The black tower entrance (address $c9ea) can be corrupted with veran warp. In theory it could cause the room's state to be set such that it thinks the essence cutscene was already done. However it doesn't appear possible to manipulate it in this particular way.
Ring boxes
There are two ways to corrupt the ring box level; either by standing at position $7a or $29 (triggering $79 or $28, respectively).
$79 depends on address $c702 (top-left of symmetry city).
- Level 10: Don't visit symmetry top-left.
- Level 15: Do visit symmetry top-left.
$28 depends on wMaxBombs.
- Level 1: Have 10 or 30 max bombs.
- Level 13: Have 50 max bombs.
$c702 can be written to with $ff8d=$c3
. However, it seems only a value of $01
can be written, which is useless.
Sadly, all of these ring boxes crash when opening the ring list. Other levels may not crash and would allow us to overwrite memory past the ring list.
wRingBoxLevel
is also close to wNumRingsAppraised
. Corruption could be helpful for getting the 100th ring. Unfortunately, only the sequence for obtaining the level 10 ringbox corrupts this, in which case it becomes 7, which is not very useful.