Refactor project structure and enhance documentation for "Oracle of Secrets" development. Introduced new epics for core infrastructure, system integration, dungeon polish, quest implementation, and boss enhancements. Improved technical debt tracking with actionable tasks, including restructuring code for better maintainability and readability. Updated known bugs and conflicts, and outlined new features and content for dungeons and quests. Enhanced overall organization and clarity of project documentation.

This commit is contained in:
scawful
2025-10-02 12:20:15 -04:00
parent 90a4a44e72
commit 27ffaf16d8
4 changed files with 555 additions and 427 deletions

View File

@@ -49,7 +49,7 @@ The 65816 is an 8/16-bit microprocessor used in the Super Nintendo Entertainment
### 2.2. Scoping and Style
- **Guideline:** The established code style uses labels followed by `{}` brackets to define scope for new blocks of logic. This convention must be followed.
- **Guideline:** The established code style uses labels followed by `{}` brackets to define scope for new blocks of logic. This convention must be followed. The `subroutine`/`endsubroutine` keywords are explicitly *not* to be used in this project.
- **Rationale:** The `subroutine`/`endsubroutine` keywords are not used in this project. Maintaining a consistent style is crucial for readability.
### 2.3. Data Organization
@@ -62,6 +62,19 @@ The 65816 is an 8/16-bit microprocessor used in the Super Nintendo Entertainment
- **Guideline:** Avoid hardcoding numerical values. Use `!` or `define()` to create named constants for RAM/SRAM addresses, item IDs, sprite states, tile IDs, etc.
- **Rationale:** This makes the code self-documenting and significantly easier to maintain and debug.
### 2.5. Opcode Size Suffixes (.b, .w, .l)
`asar` can often infer operand sizes, but relying on this can lead to bugs when the processor state (M and X flags) is not what you expect. To write robust, readable, and safe code, you should use explicit size suffixes.
- **`.b` (byte):** Forces an 8-bit operation. Use this when you are certain you are working with a single byte.
- Example: `LDA.b $7E0010` will correctly load a single byte into the accumulator, regardless of the M flag's state.
- **`.w` (word):** Forces a 16-bit operation. Use this when working with two bytes (a word).
- Example: `LDA.w $7E0022` will load a 16-bit value. This is essential for correctness if the M flag is 1 (8-bit mode).
- **`.l` (long):** Forces a 24-bit operation, typically for addresses in `JML` or `JSL`.
- Example: `JSL.l SomeRoutineInAnotherBank`
**Golden Rule:** A mismatch between the M/X flags and the intended operation size is a primary cause of crashes. When in doubt, wrap your code in `REP`/`SEP` to explicitly set the processor state, and use size suffixes to make your intent clear to both the assembler and future developers.
## 3. Project-Specific Conventions
### 3.1. File & Directory Structure
@@ -82,7 +95,14 @@ The 65816 is an 8/16-bit microprocessor used in the Super Nintendo Entertainment
- When hooking or modifying vanilla code, it is essential to understand the original context. The `usdasm` disassembly is the primary reference for this.
- To find the original code for a patch at a given address (e.g., `$07A3DB`), you can search for the SNES address in the `usdasm` files (e.g., `#_07A3DB:`).
## 4. Debugging Tips for BRKs and Crashes
## 4. Build Process and ROM Management
- **Clean ROM**: The clean, unmodified "The Legend of Zelda: A Link to the Past" ROM should be placed at `Roms/oos169.sfc`. This path is included in `.gitignore`, so the ROM file will not be committed to the repository.
- **Build Script**: A `build.sh` script is provided to automate the build process. For detailed usage, see `Docs/AsarUsage.md`.
- **Workflow**: The build script creates a fresh copy of the clean ROM and applies the `Oracle_main.asm` patch to it using `asar`.
- **Important**: Never apply patches directly to `Roms/oos169.sfc`. Always use the build script to create a new, patched ROM. This ensures the clean ROM remains untouched for future builds.
## 5. Debugging Tips for BRKs and Crashes
When encountering unexpected crashes (often indicated by a `BRK` instruction in emulators), especially after modifying code, consider the following:
@@ -92,6 +112,10 @@ When encountering unexpected crashes (often indicated by a `BRK` instruction in
- **Check `PHP`/`PLP`:** These save/restore the entire Processor Status Register. Use them when a routine needs a specific P state and you want to restore the caller's state afterwards.
- **Stack Corruption:** JSL/JSR push the return address onto the stack. If a called routine pushes too much data onto the stack without popping it, or if the stack pointer (`S`) is corrupted, the return address can be overwritten, leading to a crash when `RTL`/`RTS` is executed.
- **`JSR`/`RTS` vs `JSL`/`RTL` Mismatch:** This is a critical and common error.
- `JSR` (Jump to Subroutine) pushes a 2-byte return address. It **must** be paired with `RTS` (Return from Subroutine), which pulls 2 bytes.
- `JSL` (Jump to Subroutine Long) pushes a 3-byte return address (including the bank). It **must** be paired with `RTL` (Return from Subroutine Long), which pulls 3 bytes.
- Using `RTL` with `JSR` (or `RTS` with `JSL`) will corrupt the stack and almost certainly lead to a crash. Always verify that your subroutine calls and returns are correctly paired.
- **Balance Pushes and Pops:** Every `PHA`, `PHX`, `PHY`, `PHP` should ideally have a corresponding `PLA`, `PLX`, `PLY`, `PLP` within the same routine.
- **Bank Switching with Stack:** Be extremely careful when performing bank switches (`PHB`/`PLB`, `PHK`/`PLK`) around stack operations, as the stack is in WRAM (bank $7E/$7F).
@@ -113,7 +137,12 @@ When encountering unexpected crashes (often indicated by a `BRK` instruction in
## 5. Memory and Symbol Analysis
## 6. Verification Policy
- **Bugs and Features:** Never mark a bug fix or feature implementation as `DONE` until it has been thoroughly tested and verified in an emulator. This ensures stability and prevents regressions.
## 7. Memory and Symbol Analysis
This section details the layout and purpose of critical memory regions (WRAM and SRAM) and the symbol definition files that give them context.
@@ -160,7 +189,7 @@ This file acts as a central header, defining constants and labels for memory add
* **Memory Maps:** It contains the definitive memory maps for WRAM structures, most notably for sprites and ancillae.
* **Readability:** Its primary purpose is to replace "magic numbers" (raw addresses) with human-readable labels, which is essential for a project of this scale.
## 6. Disassembly Analysis and Search Guide
## 8. Disassembly Analysis and Search Guide
This section provides a high-level analysis of key banks in the Link to the Past disassembly. Use this guide to quickly locate relevant code and understand the overall structure of the game.
@@ -694,7 +723,7 @@ This bank is another major collection of advanced sprite and boss logic, complem
#### 6.19. Bank $1F: Dungeon Room Data
## 7. ZScream expanded feature ROM map
## 9. ZScream expanded feature ROM map
> **Last Updated:** 02/28/2025
> **Note:** All addresses are in PC format unless otherwise stated.

131
Docs/Link.md Normal file
View File

@@ -0,0 +1,131 @@
# Bank $07: Core Player (Link) Engine Analysis
**File**: `ALTTP/bank_07.asm`
**Address Range**: `$078000` - `$07FFFF`
This bank is dedicated entirely to the player character, Link. It contains his core state machine, which governs everything from movement and physics to item usage and interaction with the world. It is executed every frame that the player has control and is not in a cutscene.
---
### 1. Main Entry Point: `Link_Main`
* **Routine**: `Link_Main` (`#_078000`)
* **Purpose**: This is the top-level function for all player-related code, called once per frame from the main game loop when the game is in a playable state (e.g., Overworld or Underworld).
* **Functionality**:
1. It first checks if the player is in a state that prevents control (e.g., a cutscene, indicated by `$02E4` being non-zero).
2. If the player has control, it calls `Link_ControlHandler`, which is the heart of the player engine.
3. After the main handler runs, it calls `HandleSomariaAndGraves` to process interactions with those specific objects, which need to be checked every frame.
---
### 2. The Player State Machine: `Link_ControlHandler`
* **Routine**: `Link_ControlHandler` (`#_07807F`)
* **Purpose**: This function acts as a state machine dispatcher. It reads Link's current state from a single, critical WRAM variable and jumps to the appropriate logic handler for that state.
* **Critical WRAM Variable**: `$7E005D` (`LINKDO`) - This byte holds Link's current state ID. Modifying this value directly forces Link into a different state.
* **Execution Flow**:
1. **Damage Check**: Before any other action, the handler checks if Link has taken damage (`$7E0373`, `HURTME`). If so, it processes the damage, checks for the Magic Cape (`$7E0055`), reduces health, and can trigger a state change to `LinkState_Recoil` or the death sequence.
2. **State Dispatch**: It reads the value of `LINKDO`, multiplies it by two (since each entry is a 2-byte address), and uses it as an index into the `.vectors` jump table (`#_078041`). It then performs a `JMP` to the corresponding state handler routine.
---
### 3. Link State Vector Table
This table at `#_078041` defines all 31 possible states for Link. Understanding this is key to modifying player behavior.
| State ID | Label (`#_07....`) | Description |
|:---:|---|---|
| `0x00` | `LinkState_Default` | The normal on-foot state for walking, standing, and most basic interactions. |
| `0x01` | `LinkState_Pits` | Handles the logic for falling into a pit. |
| `0x02` | `LinkState_Recoil` | Handles being knocked back by an enemy or obstacle. |
| `0x03` | `LinkState_SpinAttack` | Manages the spin attack animation and hitbox. |
| `0x04` | `LinkState_Swimming` | The state for swimming in water. |
| `0x05` | `LinkState_OnIce` | Handles movement physics for icy surfaces. |
| `0x06` | `LinkState_Recoil` | A duplicate pointer to the recoil state, likely for a different impact type. |
| `0x07` | `LinkState_Zapped` | A special recoil state for electrical damage. |
| `0x08` | `LinkState_UsingEther` | Handles the animation and logic for using the Ether medallion. |
| `0x09` | `LinkState_UsingBombos` | Handles the animation and logic for using the Bombos medallion. |
| `0x0A` | `LinkState_UsingQuake` | Handles the animation and logic for using the Quake medallion. |
| `0x0B` | `LinkState_HoppingSouthOW` | Manages the multi-frame action of hopping off a ledge to the south. |
| `0x0C` | `LinkState_HoppingHorizontallyOW` | Manages hopping off a ledge to the east or west. |
| `0x0D` | `LinkState_HoppingDiagonallyUpOW` | Manages hopping off a ledge diagonally up-left or up-right. |
| `0x0E` | `LinkState_HoppingDiagonallyDownOW`| Manages hopping off a ledge diagonally down-left or down-right. |
| `0x0F` | `LinkState_0F` | A generic ledge-hopping state. |
| `0x10` | `LinkState_0F` | (Duplicate) A generic ledge-hopping state. |
| `0x11` | `LinkState_Dashing` | The state for running with the Pegasus Boots. |
| `0x12` | `LinkState_ExitingDash` | The brief turn-around animation after a dash collides with a wall. |
| `0x13` | `LinkState_Hookshotting` | Manages Link's state while the hookshot is extended. |
| `0x14`| `LinkState_CrossingWorlds` | Handles the Magic Mirror animation and world transition. |
| `0x15` | `LinkState_ShowingOffItem` | The "item get" pose when Link holds an item above his head. |
| `0x16` | `LinkState_Sleeping` | For the beginning of the game when Link is in bed. |
| `0x17` | `LinkState_Bunny` | The state for when Link is transformed into a bunny in the Dark World. |
| `0x18` | `LinkState_HoldingBigRock` | The state for lifting a heavy, dark-colored rock (requires Titan's Mitt). |
| `0x19` | `LinkState_ReceivingEther` | The cutscene for receiving the Ether medallion from the tablet. |
| `0x1A` | `LinkState_ReceivingBombos` | The cutscene for receiving the Bombos medallion from the tablet. |
| `0x1B` | `LinkState_ReadingDesertTablet` | The cutscene for reading the Desert Palace tablet. |
| `0x1C` | `LinkState_TemporaryBunny` | The brief bunny transformation sequence when entering the Dark World. |
| `0x1D` | `LinkState_TreePull` | The state for pulling on the tree in the haunted grove for the race game. |
| `0x1E` | `LinkState_SpinAttack` | (Duplicate) A second entry for the spin attack state. |
---
### 4. Analysis of Core States
#### `LinkState_Default` (`#_078109`)
This is the most complex state and serves as the foundation for player control. It is a large routine that dispatches to numerous sub-handlers.
* **Initial Checks**:
* `Link_HandleBunnyTransformation`: Checks if Link should transform into a bunny.
* Checks for recoil/damage (`$7E004D`) and branches to a simplified physics handler if necessary.
* **Action Dispatching**: If not recoiling, it checks for player input and calls the appropriate action handler.
* `Link_HandleToss`: Checks if Link is throwing a carried object.
* `Link_HandleAPress`: Handles context-sensitive actions for the A button (talk, read, lift, open, dash).
* `Link_HandleYItem`: Manages using the currently selected item (bow, boomerang, rods, etc.).
* `Link_HandleSwordCooldown`: Manages sword swings and charging a spin attack.
* **Physics and Collision**: If no other action is taken, it processes movement.
* `ResetAllAcceleration`: Clears speed values if Link is standing still.
* `Link_HandleDiagonalCollision` & `Link_HandleCardinalCollision`: Check for collisions with walls and objects.
* `JSL Link_HandleVelocity`: The main physics engine. Applies acceleration, deceleration, and the final movement vector to Link's coordinates.
* **Animation & Camera**:
* `JSL Link_HandleMovingAnimation_FullLongEntry`: Updates Link's sprite graphics based on his direction and action.
* `HandleIndoorCameraAndDoors`: Manages camera scrolling and door transitions indoors.
#### `LinkState_Recoil` (`#_0786B5`)
This state demonstrates how control is temporarily taken from the player.
* **Z-Axis Movement**: It uses `$7E0024` (Link's Z-position) and `$7E0029` (knockback Z-velocity) to handle Link being knocked into the air and falling back down.
* **Timer-Based**: The duration of the recoil is controlled by a countdown timer in `$7E0046` (`INPAIN`). Once the timer reaches zero, Link's state is typically returned to `LinkState_Default`.
* **Collision & Landing**: While in recoil, it still checks for collisions. It also has special checks for landing, such as `Link_SplashUponLanding` if he falls into water, which can change his state to `LinkState_Swimming`.
#### `LinkState_Bunny` (`#_0783A1`)
This state shows a persistent change in abilities.
* **Simplified Controls**: The bunny state has a much simpler control handler. It still allows for movement but disables all item and sword usage.
* **State Check**: It constantly checks for the condition that allows Link to transform back: the presence of the Moon Pearl (`$7EF357`). If the pearl is obtained or Link leaves the Dark World, it triggers the transformation back to the default state.
---
### 5. Key WRAM Variables for Link
This bank relies on a large number of WRAM addresses to function. Understanding these is critical to debugging or modifying player logic.
| Address | Label | Description |
|:---:|---|---|
| `$7E005D` | `LINKDO` | **Link's State**: The primary state ID, used as an index for the state machine. |
| `$7E0020/21`| `POSY` | Link's 16-bit Y-coordinate. |
| `$7E0022/23`| `POSX` | Link's 16-bit X-coordinate. |
| `$7E0024` | `POSZ` | Link's 8-bit Z-coordinate (height). |
| `$7E0026` | - | Link's facing direction. |
| `$7E0027/28`| - | Link's Y/X velocity. |
| `$7E002A/2B`| - | Link's Y/X sub-pixel position. |
| `$7E003A` | - | Action flags (bitfield for sword charging, etc.). |
| `$7E0046` | `INPAIN` | Recoil/invincibility timer after taking damage. |
| `$7E004D` | - | A flag indicating Link is in a recoil state. |
| `$7E0303` | - | The ID of the currently selected Y-button item. |
| `$7E0373` | `HURTME` | Damage value to be applied to Link on the next frame. |
| `$7E037B` | - | A flag that temporarily disables taking damage. |
| `$7E0372` | - | A flag indicating Link is currently dashing. |

66
Docs/Ram.md Normal file
View File

@@ -0,0 +1,66 @@
# 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 in `bank_00.asm` reads this value every frame and jumps to the corresponding module in the `Module_MainRouting` table.
- **`SUBMODE` (`$7E0011`):** Many modules have their own internal state machines. This variable holds the sub-state for the current `MODE`.
This `MODE`/`SUBMODE` pattern is the fundamental driver of the game's flow. For example:
- When Link opens the menu, the game sets `MODE` to `0x0E` (Interface), which gives control to the menu engine.
- When Link talks to a character, `Interface_PrepAndDisplayMessage` is called, which saves the current game state to `MODECACHE` (`$7E010C`) and then sets `MODE` to `0x0E` to 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 `MODE` to `0x08` (Overworld Load) or `0x06` (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 (`0x01` for indoors, `0x00` for outdoors). This variable is checked by numerous systems to alter their behavior. For instance, the `ZSCustomOverworld` system 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`) and `ROOM` (`$7E00A0`):** These variables store the player's current location. `OWSCR` holds the Overworld screen ID, while `ROOM` holds 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:
1. The game looks up the entrance data for that tile in `Overworld/entrances.asm`.
2. This data specifies the destination `ROOM` ID (e.g., 0x0104).
3. The `INDOORS` flag is set to `0x01`.
4. The main game `MODE` is set to `0x06` (Underworld Load).
5. The dungeon engine in `bank_01.asm` takes over. It reads the `ROOM` ID and uses it to look up the room's header in `ALTTP/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 in `Dungeons/custom_tag.asm`. For example, a kill room tag will cause the `Underworld_HandleRoomTags` routine to check if all sprites in the room (`$7E0E20+`) have been defeated.
- **`UWDEATH` (`$7FDF80`) and `OWDEATH` (`$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 in `LINKDO` (`$7E005D`). This is covered in detail in `Docs/Link.md`.
- **Sprites and Ancillae:** The WRAM regions from `$7E0D00` onwards are large arrays that hold the state of all active entities in the game (16 sprites, ~40 ancillae). These are defined as `structs` in `Core/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., `0x09` for active, `0x0B` for 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.asm` iterates through these arrays each frame, executing the logic for each active sprite.
## 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`) and `OOSPROG2` (`$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:**
1. The player talks to the `village_elder` NPC for the first time.
2. The NPC's code in `Sprites/NPCs/village_elder.asm` sets a specific bit in `OOSPROG` (e.g., `ORA.b #$10 : STA.l OOSPROG`).
3. Later, the world map code in `Overworld/world_map.asm` checks this bit (`LDA.l OOSPROG : AND.b #$10`). If it's set, a new icon is displayed on the map.
- **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 `$7EF38B` tracks items like `Bananas` and `Seashells`.
This data-driven approach, centered on modifying and checking flags in SRAM, allows for complex, stateful quest design that persists across play sessions.