The Legend of Zelda: Oracle of Seasons and Oracle of Ages
Capcom's 2001 pair of Game Boy Color Zeldas for Nintendo, built on Link's Awakening's engine and read through Stewmath's disassembly of both US releases.
The Legend of Zelda: Oracle of Seasons and Oracle of Ages are two Game Boy Color Zeldas that Capcom made for Nintendo in 2001. The atlas reads them through Stewmath’s disassembly, which builds the US release of each from one tree, with per-game code behind ROM_AGES and ROM_SEASONS. Every claim here covers both games unless it names one. Nothing has been measured in an emulator yet.
#Hearts
Health counts in quarter hearts, by the annotators’ comment on the starting value, where Link’s Awakening’s annotators give eighths1. The fourth Piece of Heart clears the count and gives Link a Heart Container1. Every Heart Container adds one heart to his maximum and refills sixteen hearts’ worth (computed), capped at the maximum2.
#Getting hit
A collision table decides what each enemy’s touch does. For a standard enemy such as the Octorok, Link gets 34 frames of invincibility, against Link’s Awakening’s 80, counting one object update as a frame3. The same effect pushes him for 15 frames at 1.25 pixels a frame along one axis (computed), and the player can’t steer4. The push points away from the enemy by the atlas’s reading of a function’s header comment4. Damage counts in eighths of a heart (computed), the unit Link’s Awakening’s annotators give its health5. The one ring Link wears can add to it, cut it, halve it, or double it5.
#Drops
Each enemy has a drop chance, from 1 in 16 to always, and a set of 32 entries. One random value decides whether anything drops and a second picks the entry6. A drop Link can’t use yet, such as bombs or seeds he hasn’t found, becomes no drop. In Seasons, a drop in Subrosia is first mapped through a Subrosia set, which holds the ore chunks6. A Link to the Past steps through prize packs in order instead7.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Health upgrade | Pieces | four Pieces of Heart make a Heart Container, which adds a heart and a 16-heart refill |
| Invincibility frames | Frame timer | 34 frames after a standard enemy's touch, read from a collision table |
| Knockback | Fixed shove | 15 frames at 1.25 pixels a frame for a standard enemy, halved by the Steadfast Ring, with no steering |
| Item drop table | Chance, then set | a per-enemy chance from 1 in 16 to always, then one of 32 entries; a drop Link can't use becomes none |
| Damage formula | Fixed table | the enemy's own damage, changed by the one ring Link wears |
Every claim about this game, by concept (6)
References
- ^ abIn both US releases Link's health counts in quarter hearts, the annotators' reading in a comment that calls the starting
$0c"3 hearts". The fourth Piece of Heart resets the count to 0 and gives Link a Heart Container with a parameter of 4, one heart. Link's Awakening DX also takes four pieces, but there the fourth adds a heart to a maximum kept in whole hearts and queues$40of health in eighths, eight hearts' worth.CodeStewmath/oracles-disasm · zladx/LADX-DisassemblyrevisedHow we know
389initialFileVariables_standardGame:code/fileManagement.s lines 389–391 at 21c924a · upstream ↗ 251 ; TREASURE_HEART_PIECE (0x2b) 252 .db <wNumHeartPieces 253 .db $02
data/ages/treasureCollectionBehaviours.s lines 251–254 at 21c924a · upstream ↗ 219 ; TREASURE_HEART_PIECE (0x2b) 220 .db <wNumHeartPieces 221 .db $02
data/seasons/treasureCollectionBehaviours.s lines 219–222 at 21c924a · upstream ↗ 2018@func: 2019 ld a,(wNumHeartPieces) 2020 add b 2021 add a 2022 push af 2023 sub $08 2024 jr nz,+ ⋮ 2026 ld (wNumHeartPieces),a 2027 dec a ⋮ 2029 ld (w7d0ef),a
code/textbox.s lines 2018–2029 at 21c924a · upstream ↗ 308 ; You got 4 pieces of heart. That's 1 heart container 309.ifdef ROM_AGES ⋮ 322 ld a,SND_FILLED_HEART_CONTAINER 323 call playSound 324 ld a,TREASURE_HEART_CONTAINER 325 ld c,$04 326 jp giveTreasure
code/textbox.s lines 308–326 at 21c924a · upstream ↗ 2922 ; If found 4 heart pieces… ⋮ 2929 ; Clear heart pieces count ⋮ 2935 ; Increase the maximum number of hearts
src/code/entities/bank3.asm lines 2922–2937 at 2a5c2c0 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^In both US releases a Heart Container adds its parameter to Link's maximum health, 4 quarters (one heart) for every Heart Container treasure object the data defines and for the one the fourth Piece of Heart gives. It always brings a heart refill of
$40, which is sixteen hearts (computed, in the quarter hearts a comment gives), capped at the maximum. Link's Awakening DX's boss container adds a heart and queues$FFof health instead.CodeStewmath/oracles-disasm · zladx/LADX-DisassemblyrevisedHow we know
246 ; TREASURE_HEART_CONTAINER (0x2a) 247 .db <wLinkMaxHealth 248 .db $8a
data/ages/treasureCollectionBehaviours.s lines 246–249 at 21c924a · upstream ↗ 214 ; TREASURE_HEART_CONTAINER (0x2a) 215 .db <wLinkMaxHealth 216 .db $8a
data/seasons/treasureCollectionBehaviours.s lines 214–217 at 21c924a · upstream ↗ 21; a: Add [param] to [b0]. (Heart container) ⋮ 23; c: Add [param] to [b0], using [b0+1] as a cap. (Health refill)
data/ages/treasureCollectionBehaviours.s lines 1–23 at 21c924a · upstream ↗ 265 m_BeginTreasureSubids TREASURE_HEART_CONTAINER 266 m_TreasureSubid $1a, $04, $16, $3b, TREASURE_OBJECT_HEART_CONTAINER_00
data/ages/treasureObjectData.s lines 265–268 at 21c924a · upstream ↗ 230 m_BeginTreasureSubids TREASURE_HEART_CONTAINER 231 m_TreasureSubid $1a, $04, $16, $3b, TREASURE_OBJECT_HEART_CONTAINER_00
data/seasons/treasureObjectData.s lines 230–233 at 21c924a · upstream ↗ 13; b1: Parameter (value of 'c' to pass to "giveTreasure")data/ages/treasureObjectData.s lines 8–13 at 21c924a · upstream ↗ 117 call @giveTreasure ⋮ 119 ; Check if adding this item requires adding another item. 120 push af 121 ld hl,@extraItemsToAddTable 122 call @findItemInTable 123 call nz,@giveTreasure
code/treasureAndDrops.s lines 115–123 at 21c924a · upstream ↗ 199; When Link obtains any item in the first column, he will obtain the item in the second 200; column with the parameter in the third column. ⋮ 204 .db TREASURE_HEART_CONTAINER TREASURE_HEART_REFILL $40
code/treasureAndDrops.s lines 199–204 at 21c924a · upstream ↗ 319; [de] += c. 320@modea: 321 ld a,(de) 322 add c 323 ld (de),a 324 ret ⋮ 326; [de] += c, and [de+1] is the cap for this value. ⋮ 328@modec: ⋮ 339 cp (hl) ⋮ 347 add c 348 ld (de),a 349 jr ++ ⋮
code/treasureAndDrops.s lines 319–362 at 21c924a · upstream ↗ 2768 ; Increase max health, and fully restore healthsrc/code/entities/bank3.asm lines 2768–2772 at 2a5c2c0 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^In both US releases what a touch does to Link comes from a collision table indexed by the enemy's collision mode. A standard enemy (Octorok's collision mode) uses the plain damage effect, which sets Link's invincibility counter to
$22, so he can't be hit for 34 frames, counted one per object update. The low- and high-knockback effects set 25 and 42. Link's Awakening DX sets$50, 80 frames, on every hit.CodeStewmath/oracles-disasm · zladx/LADX-DisassemblyHow we know
15; [objectCollisionTable + Enemy.enemyCollisionMode * $20 + Item.collisionType] ⋮ 21; The value of the byte itself is a CollisionEffect.
data/ages/objectCollisionTable.s lines 14–21 at 21c924a · upstream ↗ 20 /* 0x09 */ m_EnemyData $8f $90 enemy09SubidDatadata/ages/enemyData.s line 20 at 21c924a · upstream ↗ 13 /* 0x09 */ m_EnemyData $74 $90 enemy09SubidDatadata/seasons/enemyData.s line 13 at 21c924a · upstream ↗ 97 ; ENEMYCOLLISION_STANDARD_ENEMY (0x10)data/ages/objectCollisionTable.s lines 97–98 at 21c924a · upstream ↗ 68 ; ENEMYCOLLISION_STANDARD_ENEMY (0x10)data/seasons/objectCollisionTable.s lines 68–69 at 21c924a · upstream ↗ 469; COLLISIONEFFECT_DAMAGE_LINK_LOW_KNOCKBACK 470collisionEffect01: 471 ld e,LINKDMG_00 ⋮ 475; COLLISIONEFFECT_DAMAGE_LINK 476collisionEffect02: 477 ld e,LINKDMG_04 ⋮ 481; COLLISIONEFFECT_DAMAGE_LINK_HIGH_KNOCKBACK 482collisionEffect03: 483 ld e,LINKDMG_08 ⋮ 491 call applyDamageToLink_paramE
code/collisionEffects.s lines 468–492 at 21c924a · upstream ↗ 1465; bit 5: whether to give invincibility frames ⋮ 1469; b1: Value to write to Object.invincibilityFrames (if applicable) ⋮ 1473@damageTypeTable:
code/collisionEffects.s lines 1462–1476 at 21c924a · upstream ↗ 1427 ; bc = invincibilityCounter 1428 inc c 1429 ldi a,(hl) 1430 bit 5,e 1431 jr z,+ 1432 ld (bc),a
code/collisionEffects.s lines 1427–1432 at 21c924a · upstream ↗ 6310checkLinkVulnerable: 6311 ; Check var2a, invincibilityCounter, knockbackCounter ⋮ 6317 jr nz,checkLinkCollisionsEnabled@noCarry
code/bank0.s lines 6310–6317 at 21c924a · upstream ↗ 108updateLinkInvincibilityCounter: 109 ld hl,w1Link.invincibilityCounter ⋮ 114 ; If $80 or higher, invincibilityCounter goes up and Link doesn't flash red ⋮ 118 ; Otherwise it goes down, and Link flashes red 119 dec (hl)
object_code/common/specialObjects/commonCode.s lines 108–119 at 21c924a · upstream ↗ 54 call @updateSpecialObject ⋮ 56 call updateLinkInvincibilityCounter
code/specialObjects.s lines 53–56 at 21c924a · upstream ↗ ld a, $50 … ld [wInvincibilityCounter], a
src/code/entities/bank3.asm#L5347-L5348 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abIn both US releases the plain damage effect sets Link's knockback counter to 15 (7 and 21 for the low- and high-knockback effects), and the Steadfast Ring halves it. The counter drops by one per object update on the ground, which is a frame if the objects update once a frame (not read). While it runs Link moves at
SPEED_140, 1.25 pixels a frame along one axis (computed from the speed table's comment thatSPEED_100is one pixel a frame on one axis, not diagonally), and his top-down normal state skips the d-pad. The angle is taken from the enemy toward a position for Link; that it points away from the enemy is the atlas's reading ofobjectGetRelativeAngleWithTempVars's header comment, and the position passed for Link wasn't traced. Link's Awakening DX's knockback countdown runs 16 frames.CodeStewmath/oracles-disasm · zladx/LADX-DisassemblyrevisedHow we know
1466; bit 4: whether to give knockback ⋮ 1470; b2: Value to write to Object.knockbackCounter (if applicable) ⋮ 1473@damageTypeTable:
code/collisionEffects.s lines 1466–1476 at 21c924a · upstream ↗ 1434 ; bc = knockbackAngle 1435 inc c 1436 ldh a,(<hFF8A) 1437 ld (bc),a ⋮ 1439 ; bc = knockbackCounter 1440 inc c 1441 ldi a,(hl) 1442 bit 4,e 1443 jr z,+ 1444 ld (bc),a
code/collisionEffects.s lines 1434–1444 at 21c924a · upstream ↗ 330 ld c,a 331 call objectGetRelativeAngleWithTempVars 332 ldh (<hFF8A),a
code/collisionEffects.s lines 330–332 at 21c924a · upstream ↗ 6762; @param bc YX position to get the direction toward 6763; @param d Current object
code/bank0.s lines 6761–6766 at 21c924a · upstream ↗ 575 ; Steadfast ring halves knockback 576 ld a,STEADFAST_RING 577 call cpActiveRing 578 jr nz,+ 579 ld l,SpecialObject.knockbackCounter 580 srl (hl)
code/specialObjectAnimationsAndDamage.s lines 575–580 at 21c924a · upstream ↗ 4481linkUpdateKnockback: ⋮ 4489 jr c,@cancelKnockback ⋮ 4491 ; Set c to the amount to decrement knockback by. 4492 ; $01 normally, $02 if in the air? ⋮ 4504 ; Decrement knockback 4505 sub c 4506 jr c,@cancelKnockback ⋮ 4513 ; Get speed and knockback angle (de = w1Link.knockbackAngle) ⋮ 4516 ld b,SPEED_140
object_code/common/specialObjects/link.s lines 4481–4516 at 21c924a · upstream ↗ 7; SPEED_100 is a speed of 1 pixel per frame, if moving directly on one axis (not 8; diagonal).
constants/common/objectSpeeds.s lines 7–8 at 21c924a · upstream ↗ 2769 call specialObjectUpdateAdjacentWallsBitset 2770 call linkUpdateKnockback
object_code/common/specialObjects/link.s lines 2769–2770 at 21c924a · upstream ↗ 2823 ld e,SpecialObject.knockbackCounter 2824 ld a,(de) 2825 or a 2826 jp nz,func_5631
object_code/common/specialObjects/link.s lines 2823–2826 at 21c924a · upstream ↗ 2950func_5631: 2951 ld hl,wIsTileSlippery 2952 bit 6,(hl) 2953 ret z ⋮ 2958 ret
object_code/common/specialObjects/link.s lines 2950–2958 at 21c924a · upstream ↗ ld a, $10 … ld [wIgnoreLinkCollisionsCountdown], a
src/code/entities/bank3.asm#L5378-L5379 · upstream ↗ 588func_002_44C2::src/code/bank2.asm lines 588–614 at 2a5c2c0 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^ abIn both US releases a hit copies the enemy's damage value to Link as a negative number, and Link's one active ring changes it: Power Rings L1–L3 add 2, 4, and 8 units, Armor Rings L1–L3 take 1, 2, and 3 off, the Blue Ring halves it with an arithmetic shift that rounds toward more damage (3 units become 2), the Green Ring takes three quarters (rounded toward less damage), and the Cursed Ring doubles it. A hit always costs at least one unit, and the Protection Ring makes every hit 8. Health then drops a quarter heart per 2 units, so a unit is an eighth of a heart (computed, from the quarter hearts a comment gives). Link's Awakening DX's health is in eighths by its annotators' comment (
$08 = 1 heart), and its Blue Tunic also halves damage.CodeStewmath/oracles-disasm · zladx/LADX-DisassemblyrevisedHow we know
1407 bit 7,(hl) 1408 jr z,++ ⋮ 1410 ldh a,(<hActiveObjectType) 1411 add Object.damage 1412 ld e,a 1413 ld a,(de) 1414 ld c,Item.damageToApply 1415 ld (bc),a
code/collisionEffects.s lines 1407–1415 at 21c924a · upstream ↗ 789wActiveRing: ; $c6cb/$c6c5 790; When bit 6 is set, the ring is disabled?
include/wram.s lines 789–791 at 21c924a · upstream ↗ 463; Updates Link's damageToApply variable to account for damage-modifying rings. ⋮ 473 ld a,(wActiveRing) ⋮ 496; Cursed ring: damage *= 2 ⋮ 501; Blue ring: damage /= 2 ⋮ 507; Green ring: damage *= 0.75 ⋮ 525@writeDamageToApply: 526 bit 7,a 527 jr nz,+ 528 ld a,$ff ⋮ 534; This is a table of values to add to any amount of damage that Link takes. 535@ringDamageModifierTable: 536 .db POWER_RING_L1 $fe ⋮
code/specialObjectAnimationsAndDamage.s lines 462–542 at 21c924a · upstream ↗ 556 ; Protection ring does fixed damage on each hit 557 ld b,a 558 ld a,PROTECTION_RING 559 call cpActiveRing 560 jr nz,+ 561 ld b,$f8
code/specialObjectAnimationsAndDamage.s lines 556–561 at 21c924a · upstream ↗ 582 ld hl,wLinkHealth ⋮ 588 ld a,(de) ⋮ 592 ; Apply the damage (finally update wLinkHealth) 593 ld a,(de) ⋮ 595 dec (hl) 596 add $02 597 jr nc,-- ⋮ 599 ld (de),a
code/specialObjectAnimationsAndDamage.s lines 582–599 at 21c924a · upstream ↗ 100updateLinkDamageTaken: 101 callab bank6.linkUpdateDamageToApplyForRings ⋮ 103linkApplyDamage_b5: 104 callab bank6.linkApplyDamage
object_code/common/specialObjects/commonCode.s lines 100–104 at 21c924a · upstream ↗ 390 .db <wLinkHealth, $0c ; 3 heartscode/fileManagement.s line 390 at 21c924a · upstream ↗ 5322 ; If using the Blue Tunic, take half damagessrc/code/entities/bank3.asm lines 5322–5325 at 2a5c2c0 · upstream ↗ 3382; Amount of health ($08 = 1 heart) 3383wHealth::
src/constants/memory/wram.asm lines 3382–3384 at 2a5c2c0 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^ abIn both US releases a killed enemy's byte in
itemDropTablespicks a chance and a drop set, and two random values decide the drop. The first picks one bit of a 64-bit chance mask (computed, 1 in 16 to always); the second picks one of the set's 32 entries, which Seasons first maps through a Subrosia set when Link is in Subrosia. A drop Link can't use yet, such as bombs or seeds he hasn't found, becomes no drop. An Octorok drops something 24 times in 64; its set holds 16 hearts, 12 single rupees, and 4 five-rupee pieces.CodeStewmath/oracles-disasmHow we know
28 xor a 29 call decideItemDrop 30 jp z,partDelete 31 ld b,PART_ITEM_DROP
object_code/common/parts/enemyDestroyed.s lines 28–31 at 21c924a · upstream ↗ 761; Decides what an enemy will drop. ⋮ 766decideItemDrop_body: ⋮ 780 ld hl,itemDropTables 781 rst_addAToHl ⋮ 790 ld hl,itemDropProbabilityTable ⋮ 795 call getRandomNumber 796 and $3f 797 call checkFlag ⋮ 801 and $1f 802 ld hl,itemDropSetTable ⋮ 807 call getRandomNumber 808 and $1f ⋮
code/treasureAndDrops.s lines 761–811 at 21c924a · upstream ↗ 814; Checks whether an item drop of a given type can spawn. ⋮ 817; @param[out] c $ff if item cannot spawn (Link doesn't have it), otherwise the item itself ⋮ 819.ifdef ROM_SEASONS 820 ; different drop table for subrosia
code/treasureAndDrops.s lines 813–845 at 21c924a · upstream ↗ 896; Byte 1: Value to AND with that variable to check availability; if nonzero, the item 897; can drop. 898itemDropAvailabilityTable:
code/treasureAndDrops.s lines 894–920 at 21c924a · upstream ↗ 421wc608: ; $c608 422; This is always 1. Used as a dummy value in various places?
include/wram.s lines 421–423 at 21c924a · upstream ↗ 923; Each entry in this table is a bitset. A random bit is chosen. If the bit is 0, no item drops. 924itemDropProbabilityTable: ⋮ 934@probability0: 935@probability1: ⋮ 953@probability7:
code/treasureAndDrops.s lines 923–956 at 21c924a · upstream ↗ 1061itemDropSetE:code/treasureAndDrops.s lines 1061–1065 at 21c924a · upstream ↗ 1074; Bits 5-7: Index for itemDropProbabilityTable 1075; Bits 0-4: Index for itemDropSetTable 1076; Or it can be $ff for no item drop. 1077; Comments show changes in Seasons 1078itemDropTables: 1079.ifdef ROM_AGES
code/treasureAndDrops.s lines 1073–1081 at 21c924a · upstream ↗ 1098.elsecode/treasureAndDrops.s lines 1098–1100 at 21c924a · upstream ↗ 39.define ENEMY_OCTOROK $09constants/common/enemies.s line 39 at 21c924a · upstream ↗ 5 ITEM_DROP_FAIRY db ; $00 6 ITEM_DROP_HEART db ; $01 7 ITEM_DROP_1_RUPEE db ; $02 8 ITEM_DROP_5_RUPEES db ; $03
constants/common/itemDrops.s lines 4–8 at 21c924a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^A defeated monster's prize pack (1–7) gives a drop half the time, and pack 4 always. Each pack keeps its own counter that steps through 8 prizes in order; pack 1's row is heart, heart, heart, heart, green rupee, heart, heart, green rupee.Codespannerisms/usdasm
How we know
24136|06F9B2| JSL GetRandomNumber ⋮ 24138|06F9B6| PLY ⋮ 24140|06F9B7| AND.w PrizePackRarities,Y 24141|06F9BA| BNE .you_dont_get_a_drop ⋮ 24152|06F9C0| ORA.w $0FC7,Y ⋮ 24155|06F9C4| LDA.w $0FC7,Y 24156|06F9C7| INC A 24157|06F9C8| AND.b #$07 24158|06F9CA| STA.w $0FC7,Y ⋮ 24162|06F9CE| LDA.w PrizePackPrizes,Y
bank_06.asm lines 24107–24162 at bcdd96e · upstream ↗ 24276|06FA5C| db $01 ; pack 1 : 50% ⋮ 24279|06FA5F| db $00 ; pack 4 : 100%
bank_06.asm lines 24275–24283 at bcdd96e · upstream ↗ 24307.pack_1 24308|06FA72| db $D8 ; SPRITE D8 - heart ⋮ 24312|06FA76| db $D9 ; SPRITE D9 - green rupee
bank_06.asm lines 24305–24315 at bcdd96e · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
Sources
Code
- Stewmath and oracles-disasm contributors. 2026. “oracles-disasm: a disassembly of Oracle of Ages and Oracle of Seasons (Stewmath/oracles-disasm).” Commit 21c924a. Stewmath/oracles-disasm @ 21c924a · licence, all uses
- zladx contributors. 2026. “LADX-Disassembly: Disassembly of Link's Awakening DX.” zladx. Commit 2a5c2c0. zladx/LADX-Disassembly @ 2a5c2c0 · licence, all uses
- (spannerisms), kan. 2026. “usdasm: A Link to the Past US Disassembly.” Commit bcdd96e. spannerisms/usdasm @ bcdd96e · licence, all uses