- Introduced comprehensive documentation covering internal hook architecture, memory management, graphics loading pipeline, sprite loading system, cross-namespace integration, performance considerations, and debugging strategies. - Detailed sections on adding custom features and modifying existing behaviors, including examples and code snippets. - Included a complete hook list and memory map quick reference for developers.
9.3 KiB
RAM Analysis: The Engine's State
This document provides a high-level analysis of how Work RAM (WRAM) and Save RAM (SRAM) are used to manage the game's state. For a raw list of addresses, see Core/ram.asm and Core/sram.asm.
1. The Core Game Loop: WRAM in Motion
The entire game is driven by a master state machine whose state is stored in a single WRAM variable:
MODE($7E0010): This is the game's primary state index. The main loop inbank_00.asmreads this value every frame and jumps to the corresponding module in theModule_MainRoutingtable.SUBMODE($7E0011): Many modules have their own internal state machines. This variable holds the sub-state for the currentMODE.
This MODE/SUBMODE pattern is the fundamental driver of the game's flow. For example:
- When Link opens the menu, the game sets
MODEto0x0E(Interface), which gives control to the menu engine. - When Link talks to a character,
Interface_PrepAndDisplayMessageis called, which saves the current game state toMODECACHE($7E010C) and then setsMODEto0x0Eto display the text box. When the dialogue is finished, the previous state is restored from the cache. - Transitioning between the overworld and underworld involves setting
MODEto0x08(Overworld Load) or0x06(Underworld Load), respectively.
2. Defining the World: Location and Environment
The player's location and the properties of their environment are controlled by a handful of key WRAM variables.
-
INDOORS($7E001B): A simple but powerful flag (0x01for indoors,0x00for outdoors). This variable is checked by numerous systems to alter their behavior. For instance, theZSCustomOverworldsystem reads this flag to determine whether to apply day/night palettes, and the audio engine uses it to select the appropriate music track. -
OWSCR($7E008A) andROOM($7E00A0): These variables store the player's current location.OWSCRholds the Overworld screen ID, whileROOMholds the Underworld room ID.
The interaction between these variables is central to world traversal. When Link enters a cave on OWSCR 0x35, the following happens:
- The game looks up the entrance data for that tile in
Overworld/entrances.asm. - This data specifies the destination
ROOMID (e.g., 0x0104). - The
INDOORSflag is set to0x01. - The main game
MODEis set to0x06(Underworld Load). - The dungeon engine in
bank_01.asmtakes over. It reads theROOMID and uses it to look up the room's header inALTTP/rooms.asm. This header contains pointers to all the data needed to draw the room, including its layout, objects, and sprites.
3. Room-Specific Behavior
Once a room is loaded, its specific behavior is governed by tags and flags.
-
TAG1/TAG2($7E00AE/$AF): These are "Room Effect" tags loaded from the room's header. They trigger special behaviors like kill rooms, shutter doors, or custom events defined inDungeons/custom_tag.asm. For example, a kill room tag will cause theUnderworld_HandleRoomTagsroutine to check if all sprites in the room ($7E0E20+) have been defeated. -
UWDEATH($7FDF80) andOWDEATH($7FEF80): These are large bitfields in SRAM that track the state of every overworld screen and underworld room. When a kill room is cleared or a key is taken from a chest, a bit is set in this array. This ensures that the state persists permanently in the save file, preventing enemies from respawning or chests from reappearing.
4. The Player and Entities
-
Link: The player's state is managed by its own state machine in
bank_07.asm, with the current state held inLINKDO($7E005D). This is covered in detail inDocs/Link.md. -
Sprites and Ancillae: The WRAM regions from
$7E0D00onwards are large arrays that hold the state of all active entities in the game (16 sprites, ~40 ancillae). These are defined asstructsinCore/structs.asm. While there are dozens of variables for each sprite, the most important for general game logic are:SprState($7E0DD0,X): The sprite's main state (e.g.,0x09for active,0x0Bfor stunned).SprType($7E0E20,X): The sprite's ID number.SprX/SprY($0D10,X/$0D00,X): The sprite's coordinates.
The sprite engine in
bank_06.asmiterates through these arrays each frame, executing the logic for each active sprite.
4.5. Custom WRAM Region ($7E0730+)
Oracle of Secrets adds a custom WRAM region starting at $7E0730, utilizing the MAP16OVERFLOW free RAM space. All custom variables documented here have been verified against Core/ram.asm and are actively used by the project's custom systems.
Verified Custom WRAM Variables
| Address | Label | Description | Verified |
|---|---|---|---|
$7E0730 |
MenuScrollLevelV |
Vertical scroll position for the custom menu system | ✓ |
$7E0731 |
MenuScrollLevelH |
Horizontal scroll position for the custom menu system | ✓ |
$7E0732 |
MenuScrollHDirection |
Direction flag for horizontal menu scrolling (2 bytes) | ✓ |
$7E0734 |
MenuItemValueSpoof |
Temporary override for displayed menu item values (2 bytes) | ✓ |
$7E0736 |
ShortSpoof |
Shorter version of the spoof value (1 byte) | ✓ |
$7E0737 |
MusicNoteValue |
Current music note value for Ocarina system (2 bytes) | ✓ |
$7E0739 |
GoldstarOrHookshot |
Differentiates Hookshot (0) from Goldstar (1) mode | ✓ |
$7E073A |
Neck_Index |
Index for multi-part sprite body tracking (e.g., bosses) | ✓ |
$7E073B |
Neck1_OffsetX |
X-offset for first neck/body segment | ✓ |
$7E073C |
Neck1_OffsetY |
Y-offset for first neck/body segment | ✓ |
$7E073D |
Neck2_OffsetX |
X-offset for second neck/body segment | ✓ |
$7E073E |
Neck2_OffsetY |
Y-offset for second neck/body segment | ✓ |
$7E073F |
Neck3_OffsetX |
X-offset for third neck/body segment | ✓ |
$7E0740 |
Neck3_OffsetY |
Y-offset for third neck/body segment | ✓ |
$7E0741 |
Offspring1_Id |
Sprite ID of first child sprite (for boss mechanics) | ✓ |
$7E0742 |
Offspring2_Id |
Sprite ID of second child sprite (for boss mechanics) | ✓ |
$7E0743 |
Offspring3_Id |
Sprite ID of third child sprite (for boss mechanics) | ✓ |
$7E0744 |
Kydreeok_Id |
Sprite ID for Kydreeok boss entity | ✓ |
$7E0745 |
FishingOrPortalRod |
Differentiates Fishing Rod (1) from Portal Rod (2) | ✓ |
Usage Notes
-
Menu System: Variables
$7E0730-$7E0736are exclusively used by the custom menu system (Menu/menu.asm) to manage smooth scrolling between the Items and Quest Status pages. -
Item Differentiation:
GoldstarOrHookshotandFishingOrPortalRodare critical for shared inventory slots. These allow two distinct items to occupy a single menu slot, with the player able to switch between them using the L/R shoulder buttons. -
Boss Mechanics: The
Neck_*andOffspring_*variables enable complex multi-part boss sprites (e.g., Kydreeok with multiple heads, Manhandla with independent parts). The parent sprite uses these to track and coordinate its child sprite components. -
Memory Safety: All variables in this region are placed within the MAP16OVERFLOW free RAM area, which is guaranteed to be unused by the vanilla game engine. This prevents conflicts with vanilla systems.
5. Long-Term Progression: SRAM and Custom Flags
SRAM ($7EF000+) stores the player's save file and is the key to managing long-term quest progression. Oracle of Secrets heavily expands the vanilla save format to support its new data-driven systems.
-
OOSPROG($7EF3D6) andOOSPROG2($7EF3C6): These are the primary bitfields for tracking major and minor quest milestones. They are the heart of the game's custom progression.- Example Flow:
- The player talks to the
village_elderNPC for the first time. - The NPC's code in
Sprites/NPCs/village_elder.asmsets a specific bit inOOSPROG(e.g.,ORA.b #$10 : STA.l OOSPROG). - Later, the world map code in
Overworld/world_map.asmchecks this bit (LDA.l OOSPROG : AND.b #$10). If it's set, a new icon is displayed on the map.
- The player talks to the
- Example Flow:
-
Other Custom SRAM: The project adds many other custom variables to SRAM to track new systems, such as:
- New Inventory:
ZoraMask($7EF347),RocsFeather($7EF34D), etc. - Side-Quests:
MagicBeanProg($7EF39B) tracks the growth of a magic bean over time. - New Collectibles: A block starting at
$7EF38Btracks items likeBananasandSeashells.
- New Inventory:
This data-driven approach, centered on modifying and checking flags in SRAM, allows for complex, stateful quest design that persists across play sessions.