refactor: Consolidate documentation and add git workflow guide
This commit is contained in:
@@ -1,98 +0,0 @@
|
||||
# APU Timing and Handshake Bug Analysis & Refactoring Plan
|
||||
|
||||
## 1. Problem Statement
|
||||
|
||||
The emulator's Audio Processing Unit (APU) currently fails to load and play music. Analysis of the execution flow indicates that the SPC700 processor gets "stuck" during the initial handshake sequence with the main CPU. This handshake is responsible for uploading the sound driver from the ROM to the APU's RAM. The failure of this timing-sensitive process prevents the sound driver from ever running, thus no music is played.
|
||||
|
||||
This document outlines the cause of this timing failure and proposes a refactoring plan to achieve cycle-accurate emulation required for a stable APU.
|
||||
|
||||
## 2. Analysis of the CPU-APU Handshake
|
||||
|
||||
The process of starting the APU and loading a sound bank is not a simple data transfer; it is a tightly synchronized conversation between the main CPU (65816) and the APU's CPU (SPC700).
|
||||
|
||||
### 2.1. The Conversation Protocol
|
||||
|
||||
The main CPU, executing the `LoadSongBank` routine (`#_008888` in `bank_00.asm`), and the SPC700, executing its internal IPL ROM (`bootRom` in `apu.cc`), follow a strict protocol:
|
||||
|
||||
1. **APU Ready**: The SPC700 boots, initializes itself, and signals it is ready by writing `$AA` to its port `$F4` and `$BB` to port `$F5`.
|
||||
2. **CPU Waits**: The main CPU waits in a tight loop, reading the combined 16-bit value from its I/O ports until it sees `$BBAA`. This confirms the APU is alive.
|
||||
3. **CPU Initiates**: The CPU writes the command code `$CC` to the APU's input port.
|
||||
4. **APU Acknowledges**: The SPC700, which was waiting for this command, sees `$CC` and prepares to receive a block of data.
|
||||
5. **Synchronized Byte Transfer**: The CPU and APU then enter a lock-step loop to transfer the sound driver byte-by-byte. For each byte:
|
||||
* The CPU sends the data.
|
||||
* The CPU waits for the APU to read the data and echo back a confirmation value.
|
||||
* Only upon receiving the confirmation does the CPU send the next byte.
|
||||
|
||||
### 2.2. Point of Failure
|
||||
|
||||
The "stuck" behavior occurs because one side of this conversation fails to meet the other's expectation. Due to timing desynchronization, either:
|
||||
* The SPC700 is waiting for a byte that the CPU has not yet sent (or sent too early).
|
||||
* The CPU is waiting for an acknowledgment that the SPC700 has already sent (or has not yet sent).
|
||||
|
||||
The result is an infinite loop on the SPC700, which is what the watchdog timer in `Apu::RunCycles` detects.
|
||||
|
||||
## 3. Root Cause: Cycle Inaccuracy in SPC700 Emulation
|
||||
|
||||
The handshake's reliance on precise timing exposes inaccuracies in the current SPC700 emulation model. The core issue is that the emulator does not calculate the *exact* number of cycles each SPC700 instruction consumes.
|
||||
|
||||
### 3.1. Incomplete Opcode Timing
|
||||
|
||||
The emulator uses a static lookup table (`spc700_cycles.h`) for instruction cycle counts. This provides a *base* value but fails to account for critical variations:
|
||||
* **Addressing Modes**: Different addressing modes have different cycle costs.
|
||||
* **Page Boundaries**: Memory accesses that cross a 256-byte page boundary take an extra cycle.
|
||||
* **Branching**: Conditional branches take a different number of cycles depending on whether the branch is taken or not.
|
||||
|
||||
While some of this is handled (e.g., `DoBranch`), it is not applied universally, leading to small, cumulative errors.
|
||||
|
||||
### 3.2. Fragile Multi-Step Execution Model
|
||||
|
||||
The `step`/`bstep` mechanism in `Spc700::RunOpcode` is a significant source of fragility. It attempts to model complex instructions by spreading their execution across multiple calls. This means the full cycle cost of an instruction is not consumed atomically. An off-by-one error in any step, or an incorrect transition, can corrupt the timing of the entire APU, causing the handshake to fail.
|
||||
|
||||
### 3.3. Floating-Point Precision
|
||||
|
||||
The use of a `double` for the `apuCyclesPerMaster` ratio can introduce minute floating-point precision errors. Over the thousands of cycles required for the handshake, these small errors can accumulate and contribute to the overall timing drift between the CPU and APU.
|
||||
|
||||
## 4. Proposed Refactoring Plan
|
||||
|
||||
To resolve this, the APU emulation must be refactored from its current instruction-based timing model to a more robust **cycle-accurate model**. This is the standard approach for emulating timing-sensitive hardware.
|
||||
|
||||
### Step 1: Implement Cycle-Accurate Instruction Execution
|
||||
|
||||
The `Spc700::RunOpcode` function must be refactored to calculate and consume the *exact* cycle count for each instruction *before* execution.
|
||||
|
||||
* **Calculate Exact Cost**: Before running an opcode, determine its precise cycle cost by analyzing its opcode, addressing mode, and potential page-boundary penalties. The `spc700_cycles.h` table should be used as a base, with additional cycles added as needed.
|
||||
* **Atomic Execution**: The `bstep` mechanism should be removed. An instruction, no matter how complex, should be fully executed within a single call to a new `Spc700::Step()` function. This function will be responsible for consuming the exact number of cycles it calculated.
|
||||
|
||||
### Step 2: Centralize the APU Execution Loop
|
||||
|
||||
The main `Apu::RunCycles` loop should be the sole driver of APU time.
|
||||
|
||||
* **Cycle Budget**: At the start of a frame, `Apu::RunCycles` will calculate the total "budget" of APU cycles it needs to execute.
|
||||
* **Cycle-by-Cycle Stepping**: It will then loop, calling `Spc700::Step()` and `Dsp::Cycle()` and decrementing its cycle budget, until the budget is exhausted. This ensures the SPC700 and DSP are always perfectly synchronized.
|
||||
|
||||
**Example of the new loop in `Apu::RunCycles`:**
|
||||
```cpp
|
||||
void Apu::RunCycles(uint64_t master_cycles) {
|
||||
// 1. Calculate cycle budget for this frame
|
||||
const uint64_t target_apu_cycles = ...;
|
||||
|
||||
// 2. Run the APU until the budget is met
|
||||
while (cycles_ < target_apu_cycles) {
|
||||
// 3. Execute one SPC700 cycle/instruction and get its true cost
|
||||
int spc_cycles_consumed = spc700_.Step();
|
||||
|
||||
// 4. Advance DSP and Timers for each cycle consumed
|
||||
for (int i = 0; i < spc_cycles_consumed; ++i) {
|
||||
Cycle(); // This ticks the DSP and timers
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Use Integer-Based Cycle Ratios
|
||||
|
||||
To eliminate floating-point errors, the `apuCyclesPerMaster` ratio should be converted to a fixed-point integer ratio. This provides perfect, drift-free conversion between main CPU and APU cycles over long periods.
|
||||
|
||||
## 5. Conclusion
|
||||
|
||||
The APU handshake failure is a classic and challenging emulation problem that stems directly from timing inaccuracies. By refactoring the SPC700 execution loop to be cycle-accurate, we can ensure the emulated CPU and APU remain perfectly synchronized, allowing the handshake to complete successfully and enabling music playback.
|
||||
539
docs/B4-git-workflow.md
Normal file
539
docs/B4-git-workflow.md
Normal file
@@ -0,0 +1,539 @@
|
||||
# Git Workflow and Branching Strategy
|
||||
|
||||
**Last Updated:** October 10, 2025
|
||||
**Status:** Active
|
||||
**Current Phase:** Pre-1.0 (Relaxed Rules)
|
||||
|
||||
## ⚠️ Pre-1.0 Workflow (Current)
|
||||
|
||||
**TLDR for now:** Since yaze is pre-1.0 and actively evolving, we use a **simplified workflow**:
|
||||
|
||||
- ✅ **Documentation changes**: Commit directly to `master` or `develop`
|
||||
- ✅ **Small bug fixes**: Can go direct to `develop`, no PR required
|
||||
- ✅ **Solo work**: Push directly when you're the only one working
|
||||
- ⚠️ **Breaking changes**: Use feature branches and document in changelog
|
||||
- ⚠️ **Major refactors**: Use feature branches for safety (can always revert)
|
||||
|
||||
**Why relaxed?**
|
||||
- Small team / solo development
|
||||
- Pre-1.0 means breaking changes are expected
|
||||
- Documentation needs to be public quickly
|
||||
- Overhead of PRs/reviews slows down experimentation
|
||||
|
||||
**When to transition to strict workflow:**
|
||||
- Multiple active contributors
|
||||
- Stable API (post-1.0)
|
||||
- Large user base depending on stability
|
||||
- Critical bugs need rapid hotfixes
|
||||
|
||||
---
|
||||
|
||||
## 📚 Full Workflow Reference (Future/Formal)
|
||||
|
||||
The sections below document the **formal Git Flow model** that yaze will adopt post-1.0 or when the team grows. For now, treat this as aspirational best practices.
|
||||
|
||||
## Branch Structure
|
||||
|
||||
### Main Branches
|
||||
|
||||
#### `master`
|
||||
- **Purpose**: Production-ready release branch
|
||||
- **Protection**: Protected, requires PR approval
|
||||
- **Versioning**: Tagged with semantic versions (e.g., `v0.3.2`, `v0.4.0`)
|
||||
- **Updates**: Only via approved PRs from `develop` or hotfix branches
|
||||
|
||||
#### `develop`
|
||||
- **Purpose**: Main development branch, integration point for all features
|
||||
- **Protection**: Protected, requires PR approval
|
||||
- **State**: Should always build and pass tests
|
||||
- **Updates**: Merges from feature branches, releases merge back after tagging
|
||||
|
||||
### Supporting Branches
|
||||
|
||||
#### Feature Branches
|
||||
**Naming Convention:** `feature/<short-description>`
|
||||
|
||||
**Examples:**
|
||||
- `feature/overworld-editor-improvements`
|
||||
- `feature/dungeon-room-painter`
|
||||
- `feature/add-sprite-animations`
|
||||
|
||||
**Rules:**
|
||||
- Branch from: `develop`
|
||||
- Merge back to: `develop`
|
||||
- Lifetime: Delete after merge
|
||||
- Naming: Use kebab-case, be descriptive but concise
|
||||
|
||||
**Workflow:**
|
||||
```bash
|
||||
# Create feature branch
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
git checkout -b feature/my-feature
|
||||
|
||||
# Work on feature
|
||||
git add .
|
||||
git commit -m "feat: add new feature"
|
||||
|
||||
# Keep up to date with develop
|
||||
git fetch origin
|
||||
git rebase origin/develop
|
||||
|
||||
# Push and create PR
|
||||
git push -u origin feature/my-feature
|
||||
```
|
||||
|
||||
#### Bugfix Branches
|
||||
**Naming Convention:** `bugfix/<issue-number>-<short-description>`
|
||||
|
||||
**Examples:**
|
||||
- `bugfix/234-canvas-scroll-regression`
|
||||
- `bugfix/fix-dungeon-crash`
|
||||
|
||||
**Rules:**
|
||||
- Branch from: `develop`
|
||||
- Merge back to: `develop`
|
||||
- Lifetime: Delete after merge
|
||||
- Reference issue number when applicable
|
||||
|
||||
#### Hotfix Branches
|
||||
**Naming Convention:** `hotfix/<version>-<description>`
|
||||
|
||||
**Examples:**
|
||||
- `hotfix/v0.3.3-memory-leak`
|
||||
- `hotfix/v0.3.2-crash-on-startup`
|
||||
|
||||
**Rules:**
|
||||
- Branch from: `master`
|
||||
- Merge to: BOTH `master` AND `develop`
|
||||
- Creates new patch version
|
||||
- Used for critical production bugs only
|
||||
|
||||
**Workflow:**
|
||||
```bash
|
||||
# Create hotfix from master
|
||||
git checkout master
|
||||
git pull origin master
|
||||
git checkout -b hotfix/v0.3.3-critical-fix
|
||||
|
||||
# Fix the issue
|
||||
git add .
|
||||
git commit -m "fix: critical production bug"
|
||||
|
||||
# Merge to master
|
||||
git checkout master
|
||||
git merge --no-ff hotfix/v0.3.3-critical-fix
|
||||
git tag -a v0.3.3 -m "Hotfix: critical bug"
|
||||
git push origin master --tags
|
||||
|
||||
# Merge to develop
|
||||
git checkout develop
|
||||
git merge --no-ff hotfix/v0.3.3-critical-fix
|
||||
git push origin develop
|
||||
|
||||
# Delete hotfix branch
|
||||
git branch -d hotfix/v0.3.3-critical-fix
|
||||
```
|
||||
|
||||
#### Release Branches
|
||||
**Naming Convention:** `release/<version>`
|
||||
|
||||
**Examples:**
|
||||
- `release/v0.4.0`
|
||||
- `release/v0.3.2`
|
||||
|
||||
**Rules:**
|
||||
- Branch from: `develop`
|
||||
- Merge to: `master` AND `develop`
|
||||
- Used for release preparation (docs, version bumps, final testing)
|
||||
- Only bugfixes allowed, no new features
|
||||
|
||||
**Workflow:**
|
||||
```bash
|
||||
# Create release branch
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
git checkout -b release/v0.4.0
|
||||
|
||||
# Prepare release (update version, docs, changelog)
|
||||
# ... make changes ...
|
||||
git commit -m "chore: prepare v0.4.0 release"
|
||||
|
||||
# Merge to master and tag
|
||||
git checkout master
|
||||
git merge --no-ff release/v0.4.0
|
||||
git tag -a v0.4.0 -m "Release v0.4.0"
|
||||
git push origin master --tags
|
||||
|
||||
# Merge back to develop
|
||||
git checkout develop
|
||||
git merge --no-ff release/v0.4.0
|
||||
git push origin develop
|
||||
|
||||
# Delete release branch
|
||||
git branch -d release/v0.4.0
|
||||
```
|
||||
|
||||
#### Experimental Branches
|
||||
**Naming Convention:** `experiment/<description>`
|
||||
|
||||
**Examples:**
|
||||
- `experiment/vulkan-renderer`
|
||||
- `experiment/wasm-build`
|
||||
|
||||
**Rules:**
|
||||
- Branch from: `develop` or `master`
|
||||
- May never merge (prototypes, research)
|
||||
- Document findings in docs/experiments/
|
||||
- Delete when concluded or merge insights into features
|
||||
|
||||
## Commit Message Conventions
|
||||
|
||||
Follow **Conventional Commits** specification:
|
||||
|
||||
### Format
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
### Types
|
||||
- `feat`: New feature
|
||||
- `fix`: Bug fix
|
||||
- `docs`: Documentation only
|
||||
- `style`: Code style (formatting, semicolons, etc.)
|
||||
- `refactor`: Code refactoring
|
||||
- `perf`: Performance improvements
|
||||
- `test`: Adding or updating tests
|
||||
- `build`: Build system changes (CMake, dependencies)
|
||||
- `ci`: CI/CD configuration changes
|
||||
- `chore`: Maintenance tasks
|
||||
|
||||
### Scopes (optional)
|
||||
- `overworld`: Overworld editor
|
||||
- `dungeon`: Dungeon editor
|
||||
- `graphics`: Graphics editor
|
||||
- `emulator`: Emulator core
|
||||
- `canvas`: Canvas system
|
||||
- `gui`: GUI/ImGui components
|
||||
|
||||
### Examples
|
||||
```bash
|
||||
# Good commit messages
|
||||
feat(overworld): add tile16 quick-select palette
|
||||
fix(canvas): resolve scroll regression after refactoring
|
||||
docs: update build instructions for SDL3
|
||||
refactor(emulator): extract APU timing to cycle-accurate model
|
||||
perf(dungeon): optimize room rendering with batched draw calls
|
||||
|
||||
# With body and footer
|
||||
feat(overworld): add multi-tile selection tool
|
||||
|
||||
Allows users to select and copy/paste rectangular regions
|
||||
of tiles in the overworld editor. Supports undo/redo.
|
||||
|
||||
Closes #123
|
||||
```
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
### PR Title
|
||||
Follow commit message convention:
|
||||
```
|
||||
feat(overworld): add new feature
|
||||
fix(dungeon): resolve crash
|
||||
```
|
||||
|
||||
### PR Description Template
|
||||
```markdown
|
||||
## Description
|
||||
Brief description of changes
|
||||
|
||||
## Type of Change
|
||||
- [ ] Bug fix (non-breaking change)
|
||||
- [ ] New feature (non-breaking change)
|
||||
- [ ] Breaking change (fix or feature that breaks existing functionality)
|
||||
- [ ] Documentation update
|
||||
|
||||
## Testing
|
||||
- [ ] All tests pass
|
||||
- [ ] Added new tests for new features
|
||||
- [ ] Manual testing completed
|
||||
|
||||
## Checklist
|
||||
- [ ] Code follows style guidelines
|
||||
- [ ] Self-review completed
|
||||
- [ ] Documentation updated
|
||||
- [ ] No new warnings
|
||||
- [ ] Dependent changes merged
|
||||
```
|
||||
|
||||
### PR Review Process
|
||||
1. **Author**: Create PR, fill out template, request reviewers
|
||||
2. **CI**: Automatic build and test on all platforms
|
||||
3. **Reviewers**: Code review, suggest changes
|
||||
4. **Author**: Address feedback, push updates
|
||||
5. **Approval**: At least 1 approval required for merge
|
||||
6. **Merge**: Squash or merge commit (case-by-case)
|
||||
|
||||
## Version Numbering
|
||||
|
||||
Follow **Semantic Versioning (SemVer)**: `MAJOR.MINOR.PATCH`
|
||||
|
||||
### MAJOR (e.g., 1.0.0)
|
||||
- Breaking API changes
|
||||
- Major architectural changes
|
||||
- First stable release
|
||||
|
||||
### MINOR (e.g., 0.4.0)
|
||||
- New features (backward compatible)
|
||||
- Significant improvements
|
||||
- Major dependency updates (SDL3)
|
||||
|
||||
### PATCH (e.g., 0.3.2)
|
||||
- Bug fixes
|
||||
- Minor improvements
|
||||
- Documentation updates
|
||||
|
||||
### Pre-release Tags
|
||||
- `v0.4.0-alpha.1` - Early testing
|
||||
- `v0.4.0-beta.1` - Feature complete
|
||||
- `v0.4.0-rc.1` - Release candidate
|
||||
|
||||
## Release Process
|
||||
|
||||
### For Minor/Major Releases (0.x.0, x.0.0)
|
||||
|
||||
1. **Create release branch**
|
||||
```bash
|
||||
git checkout -b release/v0.4.0
|
||||
```
|
||||
|
||||
2. **Update version numbers**
|
||||
- `CMakeLists.txt`
|
||||
- `docs/H1-changelog.md`
|
||||
- `README.md`
|
||||
|
||||
3. **Update documentation**
|
||||
- Review all docs for accuracy
|
||||
- Update migration guides if breaking changes
|
||||
- Finalize changelog
|
||||
|
||||
4. **Create release commit**
|
||||
```bash
|
||||
git commit -m "chore: prepare v0.4.0 release"
|
||||
```
|
||||
|
||||
5. **Merge and tag**
|
||||
```bash
|
||||
git checkout master
|
||||
git merge --no-ff release/v0.4.0
|
||||
git tag -a v0.4.0 -m "Release v0.4.0"
|
||||
git push origin master --tags
|
||||
```
|
||||
|
||||
6. **Merge back to develop**
|
||||
```bash
|
||||
git checkout develop
|
||||
git merge --no-ff release/v0.4.0
|
||||
git push origin develop
|
||||
```
|
||||
|
||||
7. **Create GitHub Release**
|
||||
- Draft release notes
|
||||
- Attach build artifacts (CI generates these)
|
||||
- Publish release
|
||||
|
||||
### For Patch Releases (0.3.x)
|
||||
|
||||
1. **Collect fixes on develop**
|
||||
- Merge all bugfix PRs to develop
|
||||
- Ensure tests pass
|
||||
|
||||
2. **Create release branch**
|
||||
```bash
|
||||
git checkout -b release/v0.3.2
|
||||
```
|
||||
|
||||
3. **Follow steps 2-7 above**
|
||||
|
||||
## Long-Running Feature Branches
|
||||
|
||||
For large features (e.g., v0.4.0 modernization), use a **feature branch with sub-branches**:
|
||||
|
||||
```
|
||||
develop
|
||||
└── feature/v0.4.0-modernization (long-running)
|
||||
├── feature/v0.4.0-sdl3-core
|
||||
├── feature/v0.4.0-sdl3-graphics
|
||||
└── feature/v0.4.0-sdl3-audio
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Long-running branch stays alive during development
|
||||
- Sub-branches merge to long-running branch
|
||||
- Long-running branch periodically rebases on `develop`
|
||||
- Final merge to `develop` when complete
|
||||
|
||||
## Tagging Strategy
|
||||
|
||||
### Release Tags
|
||||
- Format: `v<MAJOR>.<MINOR>.<PATCH>[-prerelease]`
|
||||
- Examples: `v0.3.2`, `v0.4.0-rc.1`, `v1.0.0`
|
||||
- Annotated tags with release notes
|
||||
|
||||
### Internal Milestones
|
||||
- Format: `milestone/<name>`
|
||||
- Examples: `milestone/canvas-refactor-complete`
|
||||
- Used for tracking major internal achievements
|
||||
|
||||
## Best Practices
|
||||
|
||||
### DO ✅
|
||||
- Keep commits atomic and focused
|
||||
- Write descriptive commit messages
|
||||
- Rebase feature branches on develop regularly
|
||||
- Run tests before pushing
|
||||
- Update documentation with code changes
|
||||
- Delete branches after merging
|
||||
|
||||
### DON'T ❌
|
||||
- Commit directly to master or develop
|
||||
- Force push to shared branches
|
||||
- Mix unrelated changes in one commit
|
||||
- Merge without PR review
|
||||
- Leave stale branches
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Start new feature
|
||||
git checkout develop
|
||||
git pull
|
||||
git checkout -b feature/my-feature
|
||||
|
||||
# Update feature branch with latest develop
|
||||
git fetch origin
|
||||
git rebase origin/develop
|
||||
|
||||
# Finish feature
|
||||
git push -u origin feature/my-feature
|
||||
# Create PR on GitHub → Merge → Delete branch
|
||||
|
||||
# Start hotfix
|
||||
git checkout master
|
||||
git pull
|
||||
git checkout -b hotfix/v0.3.3-fix
|
||||
# ... fix, commit, merge to master and develop ...
|
||||
|
||||
# Create release
|
||||
git checkout develop
|
||||
git pull
|
||||
git checkout -b release/v0.4.0
|
||||
# ... prepare, merge to master, tag, merge back to develop ...
|
||||
```
|
||||
|
||||
## Emergency Procedures
|
||||
|
||||
### If master is broken
|
||||
1. Create hotfix branch immediately
|
||||
2. Fix critical issue
|
||||
3. Fast-track PR review
|
||||
4. Hotfix deploy ASAP
|
||||
|
||||
### If develop is broken
|
||||
1. Identify breaking commit
|
||||
2. Revert if needed
|
||||
3. Fix in new branch
|
||||
4. Merge fix with priority
|
||||
|
||||
### If release needs to be rolled back
|
||||
1. Tag current state as `v0.x.y-broken`
|
||||
2. Revert master to previous tag
|
||||
3. Create hotfix branch
|
||||
4. Fix and release as patch version
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Current Simplified Workflow (Pre-1.0)
|
||||
|
||||
### Daily Development Pattern
|
||||
|
||||
```bash
|
||||
# For documentation or small changes
|
||||
git checkout master # or develop, your choice
|
||||
git pull
|
||||
# ... make changes ...
|
||||
git add docs/
|
||||
git commit -m "docs: update workflow guide"
|
||||
git push origin master
|
||||
|
||||
# For experimental features
|
||||
git checkout -b feature/my-experiment
|
||||
# ... experiment ...
|
||||
git push -u origin feature/my-experiment
|
||||
# If it works: merge to develop
|
||||
# If it doesn't: delete branch, no harm done
|
||||
```
|
||||
|
||||
### When to Use Branches (Pre-1.0)
|
||||
|
||||
**Use a branch for:**
|
||||
- Large refactors that might break things
|
||||
- Experimenting with new ideas
|
||||
- Features that take multiple days
|
||||
- SDL3 migration or other big changes
|
||||
|
||||
**Don't bother with branches for:**
|
||||
- Documentation updates
|
||||
- Small bug fixes
|
||||
- Typo corrections
|
||||
- README updates
|
||||
- Adding comments or tests
|
||||
|
||||
### Current Branch Usage
|
||||
|
||||
For now, treat `master` and `develop` interchangeably:
|
||||
- `master`: Latest stable-ish code + docs
|
||||
- `develop`: Optional staging area for integration
|
||||
|
||||
When you want docs public, just push to `master`. The GitHub Pages / docs site will update automatically.
|
||||
|
||||
### Commit Message (Simplified)
|
||||
|
||||
Still try to follow the convention, but don't stress:
|
||||
|
||||
```bash
|
||||
# Good enough
|
||||
git commit -m "docs: reorganize documentation structure"
|
||||
git commit -m "fix: dungeon editor crash on load"
|
||||
git commit -m "feat: add overworld sprite editor"
|
||||
|
||||
# Also fine for now
|
||||
git commit -m "update docs"
|
||||
git commit -m "fix crash"
|
||||
```
|
||||
|
||||
### Releases (Pre-1.0)
|
||||
|
||||
Just tag and push:
|
||||
|
||||
```bash
|
||||
# When you're ready for v0.3.2
|
||||
git tag -a v0.3.2 -m "Release v0.3.2"
|
||||
git push origin v0.3.2
|
||||
# GitHub Actions builds it automatically
|
||||
```
|
||||
|
||||
No need for release branches or complex merging until you have multiple contributors.
|
||||
|
||||
---
|
||||
|
||||
**References:**
|
||||
- [Git Flow](https://nvie.com/posts/a-successful-git-branching-model/)
|
||||
- [Conventional Commits](https://www.conventionalcommits.org/)
|
||||
- [Semantic Versioning](https://semver.org/)
|
||||
|
||||
203
docs/E6-emulator-improvements.md
Normal file
203
docs/E6-emulator-improvements.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Emulator Core Improvements Roadmap
|
||||
|
||||
**Last Updated:** October 10, 2025
|
||||
**Status:** Active Planning
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines improvements, refactors, and optimizations for the yaze emulator core. These changes aim to enhance accuracy, performance, and code maintainability.
|
||||
|
||||
Items are presented in order of descending priority, from critical accuracy fixes to quality-of-life improvements.
|
||||
|
||||
---
|
||||
|
||||
## Critical Priority: APU Timing Fix
|
||||
|
||||
### Problem Statement
|
||||
|
||||
The emulator's Audio Processing Unit (APU) currently fails to load and play music. Analysis shows that the SPC700 processor gets "stuck" during the initial handshake sequence with the main CPU. This handshake is responsible for uploading the sound driver from ROM to APU RAM. The failure of this timing-sensitive process prevents the sound driver from running.
|
||||
|
||||
### Root Cause: CPU-APU Handshake Timing
|
||||
|
||||
The process of starting the APU and loading a sound bank requires tightly synchronized communication between the main CPU (65816) and the APU's CPU (SPC700).
|
||||
|
||||
#### The Handshake Protocol
|
||||
|
||||
1. **APU Ready**: SPC700 boots, initializes, signals ready by writing `$AA` to port `$F4` and `$BB` to port `$F5`
|
||||
2. **CPU Waits**: Main CPU waits in tight loop, reading combined 16-bit value from I/O ports until it sees `$BBAA`
|
||||
3. **CPU Initiates**: CPU writes command code `$CC` to APU's input port
|
||||
4. **APU Acknowledges**: SPC700 sees `$CC` and prepares to receive data block
|
||||
5. **Synchronized Byte Transfer**: CPU and APU enter lock-step loop to transfer sound driver byte-by-byte:
|
||||
* CPU sends data
|
||||
* CPU waits for APU to read data and echo back confirmation
|
||||
* Only upon receiving confirmation does CPU send next byte
|
||||
|
||||
#### Point of Failure
|
||||
|
||||
The "stuck" behavior occurs because one side fails to meet the other's expectation. Due to timing desynchronization:
|
||||
* The SPC700 is waiting for a byte that the CPU has not yet sent (or sent too early), OR
|
||||
* The CPU is waiting for an acknowledgment that the SPC700 has already sent (or has not yet sent)
|
||||
|
||||
The result is an infinite loop on the SPC700, detected by the watchdog timer in `Apu::RunCycles`.
|
||||
|
||||
### Technical Analysis
|
||||
|
||||
The handshake's reliance on precise timing exposes inaccuracies in the current SPC700 emulation model.
|
||||
|
||||
#### Issue 1: Incomplete Opcode Timing
|
||||
|
||||
The emulator uses a static lookup table (`spc700_cycles.h`) for instruction cycle counts. This provides a *base* value but fails to account for:
|
||||
* **Addressing Modes**: Different addressing modes have different cycle costs
|
||||
* **Page Boundaries**: Memory accesses crossing 256-byte page boundaries take an extra cycle
|
||||
* **Branching**: Conditional branches take different cycle counts depending on whether branch is taken
|
||||
|
||||
While some of this is handled (e.g., `DoBranch`), it is not applied universally, leading to small, cumulative errors.
|
||||
|
||||
#### Issue 2: Fragile Multi-Step Execution Model
|
||||
|
||||
The `step`/`bstep` mechanism in `Spc700::RunOpcode` is a significant source of fragility. It attempts to model complex instructions by spreading execution across multiple calls. This means the full cycle cost of an instruction is not consumed atomically. An off-by-one error in any step corrupts the timing of the entire APU.
|
||||
|
||||
#### Issue 3: Floating-Point Precision
|
||||
|
||||
The use of `double` for the `apuCyclesPerMaster` ratio can introduce minute floating-point precision errors. Over thousands of cycles required for the handshake, these small errors accumulate and contribute to timing drift between CPU and APU.
|
||||
|
||||
### Proposed Solution: Cycle-Accurate Refactoring
|
||||
|
||||
#### Step 1: Implement Cycle-Accurate Instruction Execution
|
||||
|
||||
The `Spc700::RunOpcode` function must be refactored to calculate and consume the *exact* cycle count for each instruction *before* execution.
|
||||
|
||||
* **Calculate Exact Cost**: Before running an opcode, determine its precise cycle cost by analyzing opcode, addressing mode, and potential page-boundary penalties
|
||||
* **Atomic Execution**: Remove the `bstep` mechanism. An instruction, no matter how complex, should be fully executed within a single call to a new `Spc700::Step()` function
|
||||
|
||||
#### Step 2: Centralize the APU Execution Loop
|
||||
|
||||
The main `Apu::RunCycles` loop should be the sole driver of APU time.
|
||||
|
||||
* **Cycle Budget**: At the start of a frame, calculate the total "budget" of APU cycles needed
|
||||
* **Cycle-by-Cycle Stepping**: Loop, calling `Spc700::Step()` and `Dsp::Cycle()`, decrementing cycle budget until exhausted
|
||||
|
||||
**Example of the new loop in `Apu::RunCycles`:**
|
||||
```cpp
|
||||
void Apu::RunCycles(uint64_t master_cycles) {
|
||||
// 1. Calculate cycle budget for this frame
|
||||
const uint64_t target_apu_cycles = ...;
|
||||
|
||||
// 2. Run the APU until the budget is met
|
||||
while (cycles_ < target_apu_cycles) {
|
||||
// 3. Execute one SPC700 cycle/instruction and get its true cost
|
||||
int spc_cycles_consumed = spc700_.Step();
|
||||
|
||||
// 4. Advance DSP and Timers for each cycle consumed
|
||||
for (int i = 0; i < spc_cycles_consumed; ++i) {
|
||||
Cycle(); // This ticks the DSP and timers
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 3: Use Integer-Based Cycle Ratios
|
||||
|
||||
To eliminate floating-point errors, convert the `apuCyclesPerMaster` ratio to a fixed-point integer ratio. This provides perfect, drift-free conversion between main CPU and APU cycles over long periods.
|
||||
|
||||
---
|
||||
|
||||
## High Priority: Core Architecture & Timing Model
|
||||
|
||||
### CPU Cycle Counting
|
||||
|
||||
* **Issue:** The main CPU loop in `Snes::RunCycle()` advances the master cycle counter by a fixed amount (`+= 2`). Real 65816 instructions have variable cycle counts. The current workaround of scattering `callbacks_.idle()` calls is error-prone and difficult to maintain.
|
||||
* **Recommendation:** Refactor `Cpu::ExecuteInstruction` to calculate and return the *precise* cycle cost of each instruction, including penalties for addressing modes and memory access speeds. The main `Snes` loop should then consume this exact value, centralizing timing logic and dramatically improving accuracy.
|
||||
|
||||
### Main Synchronization Loop
|
||||
|
||||
* **Issue:** The main loop in `Snes::RunFrame()` is state-driven based on the `in_vblank_` flag. This can be fragile and makes it difficult to reason about component state at any given cycle.
|
||||
* **Recommendation:** Transition to a unified main loop driven by a single master cycle counter. In this model, each component (CPU, PPU, APU, DMA) is "ticked" forward based on the master clock. This is a more robust and modular architecture that simplifies component synchronization.
|
||||
|
||||
---
|
||||
|
||||
## Medium Priority: PPU Performance
|
||||
|
||||
### Rendering Approach Optimization
|
||||
|
||||
* **Issue:** The PPU currently uses a "pixel-based" renderer (`Ppu::RunLine` calls `HandlePixel` for every pixel). This is highly accurate but can be slow due to high function call overhead and poor cache locality.
|
||||
* **Optimization:** Refactor the PPU to use a **scanline-based renderer**. Instead of processing one pixel at a time, process all active layers for an entire horizontal scanline, compose them into a temporary buffer, and then write the completed scanline to the framebuffer. This is a major architectural change but is a standard and highly effective optimization technique in SNES emulation.
|
||||
|
||||
**Benefits:**
|
||||
- Reduced function call overhead
|
||||
- Better cache locality
|
||||
- Easier to vectorize/SIMD
|
||||
- Standard approach in accurate SNES emulators
|
||||
|
||||
---
|
||||
|
||||
## Low Priority: Code Quality & Refinements
|
||||
|
||||
### APU Code Modernization
|
||||
|
||||
* **Issue:** The code in `dsp.cc` and `spc700.cc`, inherited from other projects, is written in a very C-like style, using raw pointers, `memset`, and numerous "magic numbers."
|
||||
* **Refactor:** Gradually refactor this code to use modern C++ idioms:
|
||||
- Replace raw arrays with `std::array`
|
||||
- Use constructors with member initializers instead of `memset`
|
||||
- Define `constexpr` variables or `enum class` types for hardware registers and flags
|
||||
- Improve type safety, readability, and long-term maintainability
|
||||
|
||||
### Audio Subsystem & Buffering
|
||||
|
||||
* **Issue:** The current implementation in `Emulator::Run` queues audio samples directly to the SDL audio device. If the emulator lags for even a few frames, the audio buffer can underrun, causing audible pops and stutters.
|
||||
* **Improvement:** Implement a **lock-free ring buffer (or circular buffer)** to act as an intermediary. The emulator thread would continuously write generated samples into this buffer, while the audio device (in its own thread) would continuously read from it. This decouples the emulation speed from the audio hardware, smoothing out performance fluctuations and preventing stutter.
|
||||
|
||||
### Debugger & Tooling Optimizations
|
||||
|
||||
#### DisassemblyViewer Data Structure
|
||||
* **Issue:** `DisassemblyViewer` uses a `std::map` to store instruction traces. For a tool that handles frequent insertions and lookups, this can be suboptimal.
|
||||
* **Optimization:** Replace `std::map` with `std::unordered_map` for faster average-case performance.
|
||||
|
||||
#### BreakpointManager Lookups
|
||||
* **Issue:** The `ShouldBreakOn...` functions perform a linear scan over a `std::vector` of all breakpoints. This is O(n) and could become a minor bottleneck if a very large number of breakpoints are set.
|
||||
* **Optimization:** For execution breakpoints, use a `std::unordered_set<uint32_t>` for O(1) average lookup time. This would make breakpoint checking near-instantaneous, regardless of how many are active.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
1. **Critical (v0.4.0):** APU timing fix - Required for music playback
|
||||
2. **High (v0.5.0):** CPU cycle counting accuracy - Required for game compatibility
|
||||
3. **High (v0.5.0):** Main synchronization loop refactor - Foundation for accuracy
|
||||
4. **Medium (v0.6.0):** PPU scanline renderer - Performance optimization
|
||||
5. **Low (ongoing):** Code quality improvements - Technical debt reduction
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### APU Timing Fix Success
|
||||
- [ ] Music plays in all tested games
|
||||
- [ ] Sound effects work correctly
|
||||
- [ ] No audio glitches or stuttering
|
||||
- [ ] Handshake completes within expected cycle count
|
||||
|
||||
### Overall Emulation Accuracy
|
||||
- [ ] CPU cycle accuracy within ±1 cycle per instruction
|
||||
- [ ] APU synchronized within ±1 cycle with CPU
|
||||
- [ ] PPU timing accurate to scanline level
|
||||
- [ ] All test ROMs pass
|
||||
|
||||
### Performance Targets
|
||||
- [ ] 60 FPS on modest hardware (2015+ laptops)
|
||||
- [ ] PPU optimizations provide 20%+ speedup
|
||||
- [ ] Audio buffer never underruns in normal operation
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- `docs/E4-Emulator-Development-Guide.md` - Implementation details
|
||||
- `docs/E1-emulator-enhancement-roadmap.md` - Feature roadmap
|
||||
- `docs/E5-debugging-guide.md` - Debugging techniques
|
||||
|
||||
---
|
||||
|
||||
**Status:** Active Planning
|
||||
**Next Steps:** Begin APU timing refactoring for v0.4.0
|
||||
|
||||
151
docs/E7-debugging-startup-flags.md
Normal file
151
docs/E7-debugging-startup-flags.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# YAZE Startup Debugging Flags
|
||||
|
||||
This guide explains how to use command-line flags to quickly open specific editors and cards during development for faster debugging workflows.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
./yaze [flags]
|
||||
```
|
||||
|
||||
## Available Flags
|
||||
|
||||
### `--rom_file`
|
||||
Load a specific ROM file on startup.
|
||||
|
||||
```bash
|
||||
./yaze --rom_file=/path/to/zelda3.sfc
|
||||
```
|
||||
|
||||
### `--debug`
|
||||
Enable debug logging with verbose output.
|
||||
|
||||
```bash
|
||||
./yaze --debug --log_file=yaze_debug.log
|
||||
```
|
||||
|
||||
### `--editor`
|
||||
Open a specific editor on startup. This saves time by skipping manual navigation through the UI.
|
||||
|
||||
**Available editors:**
|
||||
- `Assembly` - Assembly code editor
|
||||
- `Dungeon` - Dungeon/underworld editor
|
||||
- `Graphics` - Graphics and tile editor
|
||||
- `Music` - Music and sound editor
|
||||
- `Overworld` - Overworld map editor
|
||||
- `Palette` - Palette editor
|
||||
- `Screen` - Screen editor
|
||||
- `Sprite` - Sprite editor
|
||||
- `Message` - Message/text editor
|
||||
- `Hex` - Hex/memory editor
|
||||
- `Agent` - AI agent interface
|
||||
- `Settings` - Settings editor
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon
|
||||
```
|
||||
|
||||
### `--cards`
|
||||
Open specific cards/panels within an editor. Most useful with the Dungeon editor.
|
||||
|
||||
**Dungeon Editor Cards:**
|
||||
- `Rooms List` - Shows the list of all dungeon rooms
|
||||
- `Room Matrix` - Shows the dungeon room layout matrix
|
||||
- `Entrances List` - Shows dungeon entrance configurations
|
||||
- `Room Graphics` - Shows room graphics settings
|
||||
- `Object Editor` - Shows the object placement editor
|
||||
- `Palette Editor` - Shows the palette editor
|
||||
- `Room N` - Opens a specific room by ID (e.g., `Room 0`, `Room 105`)
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Rooms List,Room 0"
|
||||
```
|
||||
|
||||
## Common Debugging Scenarios
|
||||
|
||||
### 1. Quick Dungeon Room Testing
|
||||
Open a specific dungeon room for testing:
|
||||
|
||||
```bash
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room Graphics"
|
||||
```
|
||||
|
||||
### 2. Multiple Room Comparison
|
||||
Compare multiple rooms side-by-side:
|
||||
|
||||
```bash
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
|
||||
```
|
||||
|
||||
### 3. Full Dungeon Editor Workspace
|
||||
Open all dungeon editor tools:
|
||||
|
||||
```bash
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
|
||||
--cards="Rooms List,Room Matrix,Room Graphics,Object Editor,Palette Editor"
|
||||
```
|
||||
|
||||
### 4. Debug Mode with Logging
|
||||
Enable full debug output while working:
|
||||
|
||||
```bash
|
||||
./yaze --rom_file=zelda3.sfc --debug --log_file=debug.log \
|
||||
--editor=Dungeon --cards="Room 0"
|
||||
```
|
||||
|
||||
### 5. Quick Overworld Editing
|
||||
Jump straight to overworld editing:
|
||||
|
||||
```bash
|
||||
./yaze --rom_file=zelda3.sfc --editor=Overworld
|
||||
```
|
||||
|
||||
## gRPC Test Harness (Developer Feature)
|
||||
|
||||
If compiled with `YAZE_WITH_GRPC=ON`, you can enable automated GUI testing:
|
||||
|
||||
```bash
|
||||
./yaze --enable_test_harness --test_harness_port=50051
|
||||
```
|
||||
|
||||
This allows remote control via gRPC for automated testing and AI agent interaction.
|
||||
|
||||
## Combining Flags
|
||||
|
||||
All flags can be combined for powerful debugging setups:
|
||||
|
||||
```bash
|
||||
# Full debugging setup for room 105
|
||||
./yaze \
|
||||
--rom_file=/path/to/zelda3.sfc \
|
||||
--debug \
|
||||
--log_file=room_105_debug.log \
|
||||
--editor=Dungeon \
|
||||
--cards="Room 105,Room Graphics,Palette Editor,Object Editor"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Card names are case-sensitive and must match exactly
|
||||
- Use quotes around comma-separated card lists
|
||||
- Invalid editor or card names will be logged as warnings but won't crash the application
|
||||
- The `--cards` flag is currently only implemented for the Dungeon editor
|
||||
- Room IDs range from 0-319 in the vanilla game
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Editor doesn't open:**
|
||||
- Check spelling (case-sensitive)
|
||||
- Verify ROM loaded successfully
|
||||
- Check log output with `--debug`
|
||||
|
||||
**Cards don't appear:**
|
||||
- Ensure editor is set (e.g., `--editor=Dungeon`)
|
||||
- Check card name spelling
|
||||
- Some cards require a loaded ROM
|
||||
|
||||
**Want to add more card support?**
|
||||
See `EditorManager::OpenEditorAndCardsFromFlags()` in `src/app/editor/editor_manager.cc`
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
# Emulator Core Improvements & Optimization Roadmap
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
This document outlines potential long-term improvements, refactors, and optimizations for the `yaze` emulator core. These suggestions aim to enhance accuracy, performance, and code maintainability.
|
||||
|
||||
For a detailed analysis of the critical, high-priority bug affecting audio, please first refer to the separate document:
|
||||
|
||||
- **[APU Timing and Handshake Bug Analysis & Refactoring Plan](./APU_Timing_Fix_Plan.md)**
|
||||
|
||||
The items below are presented in order of descending priority, from critical accuracy fixes to quality-of-life code improvements.
|
||||
|
||||
## 2. Core Architecture & Timing Model (High Priority)
|
||||
|
||||
The most significant improvements relate to the fundamental emulation loop and cycle timing, which are the root cause of the current audio bug and affect overall system stability.
|
||||
|
||||
* **CPU Cycle Counting:**
|
||||
* **Issue:** The main CPU loop in `Snes::RunCycle()` advances the master cycle counter by a fixed amount (`+= 2`). Real 65816 instructions have variable cycle counts. The current workaround of scattering `callbacks_.idle()` calls is error-prone and difficult to maintain.
|
||||
* **Recommendation:** Refactor `Cpu::ExecuteInstruction` to calculate and return the *precise* cycle cost of each instruction, including penalties for addressing modes and memory access speeds. The main `Snes` loop should then consume this exact value, centralizing timing logic and dramatically improving accuracy.
|
||||
|
||||
* **Main Synchronization Loop:**
|
||||
* **Issue:** The main loop in `Snes::RunFrame()` is state-driven based on the `in_vblank_` flag. This can be fragile and makes it difficult to reason about the state of all components at any given cycle.
|
||||
* **Recommendation:** Transition to a unified main loop that is driven by a single master cycle counter. In this model, each component (CPU, PPU, APU, DMA) is "ticked" forward based on the master clock. This is a more robust and modular architecture that simplifies component synchronization.
|
||||
|
||||
## 3. PPU (Video Rendering) Performance
|
||||
|
||||
The Picture Processing Unit (PPU) is often a performance bottleneck. The following change could provide a significant speed boost.
|
||||
|
||||
* **Rendering Approach:**
|
||||
* **Issue:** The PPU currently uses a "pixel-based" renderer (`Ppu::RunLine` calls `HandlePixel` for every pixel). This is highly accurate but can be slow due to high function call overhead and poor cache locality.
|
||||
* **Optimization:** Refactor the PPU to use a **scanline-based renderer**. Instead of processing one pixel at a time, this approach processes all active layers for an entire horizontal scanline, composes them into a temporary buffer, and then writes the completed scanline to the framebuffer. This is a major architectural change but is a standard and highly effective optimization technique in SNES emulation.
|
||||
|
||||
## 4. APU (Audio) Code Quality & Refinements
|
||||
|
||||
Beyond the critical timing fixes, the APU core would benefit from modernization.
|
||||
|
||||
* **Code Style:**
|
||||
* **Issue:** The code in `dsp.cc` and `spc700.cc`, inherited from other projects, is written in a very C-like style, using raw pointers, `memset`, and numerous "magic numbers."
|
||||
* **Refactor:** Gradually refactor this code to use modern C++ idioms. Replace raw arrays with `std::array`, use constructors with member initializers instead of `memset`, and define `constexpr` variables or `enum class` types for hardware registers and flags. This will improve type safety, readability, and long-term maintainability.
|
||||
|
||||
## 5. Audio Subsystem & Buffering
|
||||
|
||||
To ensure smooth, stutter-free audio, the interface between the emulator and the host audio API can be improved.
|
||||
|
||||
* **Audio Buffering Strategy:**
|
||||
* **Issue:** The current implementation in `Emulator::Run` queues audio samples directly to the SDL audio device. If the emulator lags for even a few frames, the audio buffer can underrun, causing audible pops and stutters.
|
||||
* **Improvement:** Implement a **lock-free ring buffer (or circular buffer)** to act as an intermediary. The emulator thread would continuously write generated samples into this buffer, while the audio device (in its own thread) would continuously read from it. This decouples the emulation speed from the audio hardware, smoothing out performance fluctuations and preventing stutter.
|
||||
|
||||
## 6. Debugger & Tooling Optimizations (Lower Priority)
|
||||
|
||||
These are minor optimizations that would improve the performance of the debugging tools, especially under heavy use.
|
||||
|
||||
* **`DisassemblyViewer` Data Structure:**
|
||||
* **Issue:** `DisassemblyViewer` uses a `std::map` to store instruction traces. For a tool that handles frequent insertions and lookups, this can be suboptimal.
|
||||
* **Optimization:** Replace `std::map` with `std::unordered_map` for faster average-case performance.
|
||||
|
||||
* **`BreakpointManager` Lookups:**
|
||||
* **Issue:** The `ShouldBreakOn...` functions perform a linear scan over a `std::vector` of all breakpoints. This is O(n) and could become a minor bottleneck if a very large number of breakpoints are set.
|
||||
* **Optimization:** For execution breakpoints, use a `std::unordered_set<uint32_t>` for O(1) average lookup time. This would make breakpoint checking near-instantaneous, regardless of how many are active.
|
||||
401
docs/G4-canvas-coordinate-fix.md
Normal file
401
docs/G4-canvas-coordinate-fix.md
Normal file
@@ -0,0 +1,401 @@
|
||||
# Canvas Coordinate Synchronization and Scroll Fix
|
||||
|
||||
**Date**: October 10, 2025
|
||||
**Issues**:
|
||||
1. Overworld map highlighting regression after canvas refactoring
|
||||
2. Overworld canvas scrolling unexpectedly when selecting tiles
|
||||
3. Vanilla Dark/Special World large map outlines not displaying
|
||||
**Status**: ✅ Fixed
|
||||
|
||||
## Problem Summary
|
||||
|
||||
After the canvas refactoring (commits f538775954, 60ddf76331), two critical bugs appeared:
|
||||
|
||||
1. **Map highlighting broken**: The overworld editor stopped properly highlighting the current map when hovering. The map highlighting only worked during active tile painting, not during normal mouse hover.
|
||||
|
||||
2. **Wrong canvas scrolling**: When right-clicking to select tiles (especially on Dark World), the overworld canvas would scroll unexpectedly instead of the tile16 blockset selector.
|
||||
|
||||
## Root Cause
|
||||
|
||||
The regression had **FIVE** issues:
|
||||
|
||||
### Issue 1: Wrong Coordinate System (Line 1041)
|
||||
**File**: `src/app/editor/overworld/overworld_editor.cc:1041`
|
||||
|
||||
**Before (BROKEN)**:
|
||||
```cpp
|
||||
const auto mouse_position = ImGui::GetIO().MousePos; // ❌ Screen coordinates!
|
||||
const auto canvas_zero_point = ow_map_canvas_.zero_point();
|
||||
int map_x = (mouse_position.x - canvas_zero_point.x) / kOverworldMapSize;
|
||||
```
|
||||
|
||||
**After (FIXED)**:
|
||||
```cpp
|
||||
const auto mouse_position = ow_map_canvas_.hover_mouse_pos(); // ✅ World coordinates!
|
||||
int map_x = mouse_position.x / kOverworldMapSize;
|
||||
```
|
||||
|
||||
**Why This Was Wrong**:
|
||||
- `ImGui::GetIO().MousePos` returns **screen space** coordinates (absolute position on screen)
|
||||
- The canvas may be scrolled, scaled, or positioned anywhere on screen
|
||||
- Screen coordinates don't account for canvas scrolling/offset
|
||||
- `hover_mouse_pos()` returns **canvas/world space** coordinates (relative to canvas content)
|
||||
|
||||
### Issue 2: Hover Position Not Updated (Line 416)
|
||||
**File**: `src/app/gui/canvas.cc:416`
|
||||
|
||||
**Before (BROKEN)**:
|
||||
```cpp
|
||||
void Canvas::DrawBackground(ImVec2 canvas_size) {
|
||||
// ... setup code ...
|
||||
ImGui::InvisibleButton(canvas_id_.c_str(), scaled_size, kMouseFlags);
|
||||
|
||||
// ❌ mouse_pos_in_canvas_ only updated in DrawTilePainter() during painting!
|
||||
|
||||
if (config_.is_draggable && IsItemHovered()) {
|
||||
// ... pan handling ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`mouse_pos_in_canvas_` was only updated inside painting methods:
|
||||
- `DrawTilePainter()` at line 741
|
||||
- `DrawSolidTilePainter()` at line 860
|
||||
- `DrawTileSelector()` at line 929
|
||||
|
||||
**After (FIXED)**:
|
||||
```cpp
|
||||
void Canvas::DrawBackground(ImVec2 canvas_size) {
|
||||
// ... setup code ...
|
||||
ImGui::InvisibleButton(canvas_id_.c_str(), scaled_size, kMouseFlags);
|
||||
|
||||
// ✅ CRITICAL FIX: Always update hover position when hovering
|
||||
if (IsItemHovered()) {
|
||||
const ImGuiIO& io = GetIO();
|
||||
const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
|
||||
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
|
||||
mouse_pos_in_canvas_ = mouse_pos; // ✅ Updated every frame during hover
|
||||
}
|
||||
|
||||
if (config_.is_draggable && IsItemHovered()) {
|
||||
// ... pan handling ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Coordinate Spaces
|
||||
|
||||
yaze has three coordinate spaces:
|
||||
|
||||
1. **Screen Space**: Absolute pixel coordinates on the monitor
|
||||
- `ImGui::GetIO().MousePos` returns this
|
||||
- Never use this for canvas operations!
|
||||
|
||||
2. **Canvas/World Space**: Coordinates relative to canvas content
|
||||
- Accounts for canvas scrolling and offset
|
||||
- `Canvas::hover_mouse_pos()` returns this
|
||||
- Use this for map calculations, entity positioning, etc.
|
||||
|
||||
3. **Tile/Grid Space**: Coordinates in tile units (not pixels)
|
||||
- `Canvas::CanvasToTile()` converts from canvas to grid space
|
||||
- Used by automation API
|
||||
|
||||
### Usage Patterns
|
||||
|
||||
**For Hover/Highlighting** (CheckForCurrentMap):
|
||||
```cpp
|
||||
auto hover_pos = canvas.hover_mouse_pos(); // ✅ Updates continuously
|
||||
int map_x = hover_pos.x / kOverworldMapSize;
|
||||
```
|
||||
|
||||
**For Active Painting** (DrawOverworldEdits):
|
||||
```cpp
|
||||
auto paint_pos = canvas.drawn_tile_position(); // ✅ Updates only during drag
|
||||
int map_x = paint_pos.x / kOverworldMapSize;
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Visual Testing
|
||||
|
||||
**Map Highlighting Test**:
|
||||
1. Open overworld editor
|
||||
2. Hover mouse over different maps (without clicking)
|
||||
3. Verify current map highlights correctly
|
||||
4. Test with different scale levels (0.25x - 4.0x)
|
||||
5. Test with scrolled canvas
|
||||
|
||||
**Scroll Regression Test**:
|
||||
1. Open overworld editor
|
||||
2. Switch to Dark World (or any world)
|
||||
3. Right-click on overworld canvas to select a tile
|
||||
4. ✅ **Expected**: Tile16 blockset selector scrolls to show the selected tile
|
||||
5. ✅ **Expected**: Overworld canvas does NOT scroll
|
||||
6. ❌ **Before fix**: Overworld canvas would scroll unexpectedly
|
||||
|
||||
### Unit Tests
|
||||
Created `test/unit/gui/canvas_coordinate_sync_test.cc` with regression tests:
|
||||
- `HoverMousePos_IndependentFromDrawnPos`: Verifies hover vs paint separation
|
||||
- `CoordinateSpace_WorldNotScreen`: Ensures world coordinates used
|
||||
- `MapCalculation_SmallMaps`: Tests 512x512 map boundaries
|
||||
- `MapCalculation_LargeMaps`: Tests 1024x1024 v3 ASM maps
|
||||
- `OverworldMapHighlight_UsesHoverNotDrawn`: Critical regression test
|
||||
- `OverworldMapIndex_From8x8Grid`: Tests all three worlds (Light/Dark/Special)
|
||||
|
||||
Run tests:
|
||||
```bash
|
||||
./build/bin/yaze_test --unit
|
||||
```
|
||||
|
||||
## Impact Analysis
|
||||
|
||||
### Files Changed
|
||||
1. `src/app/editor/overworld/overworld_editor.cc` (line 1041-1049)
|
||||
- Changed from screen coordinates to canvas hover coordinates
|
||||
- Removed incorrect `canvas_zero_point` subtraction
|
||||
|
||||
2. `src/app/gui/canvas.cc` (line 414-421)
|
||||
- Added continuous hover position tracking in `DrawBackground()`
|
||||
- Now updates `mouse_pos_in_canvas_` every frame when hovering
|
||||
|
||||
3. `src/app/editor/overworld/overworld_editor.cc` (line 2344-2360)
|
||||
- Removed fallback scroll code that scrolled the wrong canvas
|
||||
- Now only uses `blockset_selector_->ScrollToTile()` which targets the correct canvas
|
||||
|
||||
4. `src/app/editor/overworld/overworld_editor.cc` (line 1403-1408)
|
||||
- Changed from `ImGui::IsItemHovered()` (checks last drawn item)
|
||||
- To `ow_map_canvas_.IsMouseHovering()` (checks canvas hover state directly)
|
||||
|
||||
5. `src/app/editor/overworld/overworld_editor.cc` (line 1133-1151)
|
||||
- Added world offset subtraction for vanilla large map parent coordinates
|
||||
- Now properly accounts for Dark World (0x40-0x7F) and Special World (0x80-0x9F)
|
||||
|
||||
### Affected Functionality
|
||||
- ✅ **Fixed**: Overworld map highlighting during hover (all worlds, all ROM types)
|
||||
- ✅ **Fixed**: Vanilla Dark World large map highlighting (was drawing off-screen)
|
||||
- ✅ **Fixed**: Vanilla Special World large map highlighting (was drawing off-screen)
|
||||
- ✅ **Fixed**: Overworld canvas no longer scrolls when selecting tiles
|
||||
- ✅ **Fixed**: Tile16 selector properly scrolls to show selected tile (via blockset_selector_)
|
||||
- ✅ **Fixed**: Entity renderer using `hover_mouse_pos()` (already worked correctly)
|
||||
- ✅ **Preserved**: Tile painting using `drawn_tile_position()` (unchanged)
|
||||
- ✅ **Preserved**: Multi-area map support (512x512, 1024x1024)
|
||||
- ✅ **Preserved**: All three worlds (Light 0x00-0x3F, Dark 0x40-0x7F, Special 0x80+)
|
||||
- ✅ **Preserved**: ZSCustomOverworld v3 large maps (already worked correctly)
|
||||
|
||||
### Related Code That Works Correctly
|
||||
These files already use the correct pattern:
|
||||
- `src/app/editor/overworld/overworld_entity_renderer.cc:68-69` - Uses `hover_mouse_pos()` for entity placement ✅
|
||||
- `src/app/editor/overworld/overworld_editor.cc:664` - Uses `drawn_tile_position()` for painting ✅
|
||||
|
||||
## Multi-Area Map Support
|
||||
|
||||
The fix properly handles all area sizes:
|
||||
|
||||
### Standard Maps (512x512)
|
||||
```cpp
|
||||
int map_x = hover_pos.x / 512; // 0-7 range
|
||||
int map_y = hover_pos.y / 512; // 0-7 range
|
||||
int map_index = map_x + map_y * 8; // 0-63 (8x8 grid)
|
||||
```
|
||||
|
||||
### ZSCustomOverworld v3 Large Maps (1024x1024)
|
||||
```cpp
|
||||
int map_x = hover_pos.x / 1024; // Large map X
|
||||
int map_y = hover_pos.y / 1024; // Large map Y
|
||||
// Parent map calculation handled in lines 1073-1190
|
||||
```
|
||||
|
||||
The existing multi-area logic (lines 1068-1190) remains unchanged and works correctly with the fix.
|
||||
|
||||
## Issue 3: Wrong Canvas Being Scrolled (Line 2344-2366)
|
||||
|
||||
**File**: `src/app/editor/overworld/overworld_editor.cc:2344`
|
||||
|
||||
**Problem**: When selecting tiles with right-click on the overworld canvas, `ScrollBlocksetCanvasToCurrentTile()` was calling `ImGui::SetScrollX/Y()` which scrolls **the current ImGui window**, not a specific canvas.
|
||||
|
||||
**Call Stack**:
|
||||
```
|
||||
DrawOverworldCanvas() // Overworld canvas is current window
|
||||
└─ CheckForOverworldEdits() (line 1401)
|
||||
└─ CheckForSelectRectangle() (line 793)
|
||||
└─ ScrollBlocksetCanvasToCurrentTile() (line 916)
|
||||
└─ ImGui::SetScrollX/Y() (lines 2364-2365) // ❌ Scrolls CURRENT window!
|
||||
```
|
||||
|
||||
**Before (BROKEN)**:
|
||||
```cpp
|
||||
void OverworldEditor::ScrollBlocksetCanvasToCurrentTile() {
|
||||
if (blockset_selector_) {
|
||||
blockset_selector_->ScrollToTile(current_tile16_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: maintain legacy behavior when the selector is unavailable.
|
||||
constexpr int kTilesPerRow = 8;
|
||||
constexpr int kTileDisplaySize = 32;
|
||||
|
||||
int tile_col = current_tile16_ % kTilesPerRow;
|
||||
int tile_row = current_tile16_ / kTilesPerRow;
|
||||
float tile_x = static_cast<float>(tile_col * kTileDisplaySize);
|
||||
float tile_y = static_cast<float>(tile_row * kTileDisplaySize);
|
||||
|
||||
const ImVec2 window_size = ImGui::GetWindowSize();
|
||||
float scroll_x = tile_x - (window_size.x / 2.0F) + (kTileDisplaySize / 2.0F);
|
||||
float scroll_y = tile_y - (window_size.y / 2.0F) + (kTileDisplaySize / 2.0F);
|
||||
|
||||
// ❌ BUG: This scrolls whatever ImGui window is currently active!
|
||||
// When called from overworld canvas, it scrolls the overworld instead of tile16 selector!
|
||||
ImGui::SetScrollX(std::max(0.0f, scroll_x));
|
||||
ImGui::SetScrollY(std::max(0.0f, scroll_y));
|
||||
}
|
||||
```
|
||||
|
||||
**After (FIXED)**:
|
||||
```cpp
|
||||
void OverworldEditor::ScrollBlocksetCanvasToCurrentTile() {
|
||||
if (blockset_selector_) {
|
||||
blockset_selector_->ScrollToTile(current_tile16_); // ✅ Correct: Targets specific canvas
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ CRITICAL FIX: Do NOT use fallback scrolling from overworld canvas context!
|
||||
// The fallback code uses ImGui::SetScrollX/Y which scrolls the CURRENT window,
|
||||
// and when called from CheckForSelectRectangle() during overworld canvas rendering,
|
||||
// it incorrectly scrolls the overworld canvas instead of the tile16 selector.
|
||||
//
|
||||
// The blockset_selector_ should always be available in modern code paths.
|
||||
// If it's not available, we skip scrolling rather than scroll the wrong window.
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Fixes It**:
|
||||
- The modern `blockset_selector_->ScrollToTile()` targets the specific tile16 selector canvas
|
||||
- The fallback `ImGui::SetScrollX/Y()` has no context - it just scrolls the active window
|
||||
- By removing the fallback, we prevent scrolling the wrong canvas
|
||||
- If `blockset_selector_` is null (shouldn't happen in modern builds), we safely do nothing instead of breaking user interaction
|
||||
|
||||
## Issue 4: Wrong Hover Check (Line 1403)
|
||||
|
||||
**File**: `src/app/editor/overworld/overworld_editor.cc:1403`
|
||||
|
||||
**Problem**: The code was using `ImGui::IsItemHovered()` to check if the mouse was over the canvas, but this checks the **last drawn ImGui item**, which could be entities, overlays, or anything drawn after the canvas's InvisibleButton. This meant hover detection was completely broken.
|
||||
|
||||
**Call Stack**:
|
||||
```
|
||||
DrawOverworldCanvas()
|
||||
└─ DrawBackground() at line 1350 // Creates InvisibleButton (item A)
|
||||
└─ DrawExits/Entrances/Items/Sprites() // Draws entities (items B, C, D...)
|
||||
└─ DrawOverlayPreviewOnMap() // Draws overlay (item E)
|
||||
└─ IsItemHovered() at line 1403 // ❌ Checks item E, not item A!
|
||||
```
|
||||
|
||||
**Before (BROKEN)**:
|
||||
```cpp
|
||||
if (current_mode == EditingMode::DRAW_TILE) {
|
||||
CheckForOverworldEdits();
|
||||
}
|
||||
if (IsItemHovered()) // ❌ Checks LAST item (overlay/entity), not canvas!
|
||||
status_ = CheckForCurrentMap();
|
||||
```
|
||||
|
||||
**After (FIXED)**:
|
||||
```cpp
|
||||
if (current_mode == EditingMode::DRAW_TILE) {
|
||||
CheckForOverworldEdits();
|
||||
}
|
||||
// ✅ CRITICAL FIX: Use canvas hover state, not ImGui::IsItemHovered()
|
||||
// IsItemHovered() checks the LAST drawn item, which could be entities/overlay,
|
||||
// not the canvas InvisibleButton. ow_map_canvas_.IsMouseHovering() correctly
|
||||
// tracks whether mouse is over the canvas area.
|
||||
if (ow_map_canvas_.IsMouseHovering()) // ✅ Checks canvas hover state directly
|
||||
status_ = CheckForCurrentMap();
|
||||
```
|
||||
|
||||
**Why This Fixes It**:
|
||||
- `IsItemHovered()` is context-sensitive - it checks whatever the last `ImGui::*()` call was
|
||||
- After drawing entities and overlays, the "last item" is NOT the canvas
|
||||
- `Canvas::IsMouseHovering()` tracks the hover state from the InvisibleButton in `DrawBackground()`
|
||||
- This state is set correctly when the InvisibleButton is hovered (line 416 in canvas.cc)
|
||||
|
||||
## Issue 5: Vanilla Large Map World Offset (Line 1132-1136)
|
||||
|
||||
**File**: `src/app/editor/overworld/overworld_editor.cc:1132-1136`
|
||||
|
||||
**Problem**: For vanilla ROMs, the large map highlighting logic wasn't accounting for world offsets when calculating parent map coordinates. Dark World maps (0x40-0x7F) and Special World maps (0x80-0x9F) use map IDs with offsets, but the display grid coordinates are 0-7.
|
||||
|
||||
**Before (BROKEN)**:
|
||||
```cpp
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map() ||
|
||||
overworld_.overworld_map(current_map_)->large_index() != 0) {
|
||||
const int highlight_parent =
|
||||
overworld_.overworld_map(current_highlighted_map)->parent();
|
||||
const int parent_map_x = highlight_parent % 8; // ❌ Wrong for Dark/Special!
|
||||
const int parent_map_y = highlight_parent / 8;
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize,
|
||||
large_map_size, large_map_size);
|
||||
}
|
||||
```
|
||||
|
||||
**Example Bug**:
|
||||
- Dark World map 0x42 (parent) → `0x42 % 8 = 2`, `0x42 / 8 = 8`
|
||||
- This draws the outline at grid position (2, 8) which is **off the screen**!
|
||||
- Correct position should be (2, 0) in the Dark World display grid
|
||||
|
||||
**After (FIXED)**:
|
||||
```cpp
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map() ||
|
||||
overworld_.overworld_map(current_map_)->large_index() != 0) {
|
||||
const int highlight_parent =
|
||||
overworld_.overworld_map(current_highlighted_map)->parent();
|
||||
|
||||
// ✅ CRITICAL FIX: Account for world offset when calculating parent coordinates
|
||||
int parent_map_x;
|
||||
int parent_map_y;
|
||||
if (current_world_ == 0) {
|
||||
// Light World (0x00-0x3F)
|
||||
parent_map_x = highlight_parent % 8;
|
||||
parent_map_y = highlight_parent / 8;
|
||||
} else if (current_world_ == 1) {
|
||||
// Dark World (0x40-0x7F) - subtract 0x40 to get display coordinates
|
||||
parent_map_x = (highlight_parent - 0x40) % 8;
|
||||
parent_map_y = (highlight_parent - 0x40) / 8;
|
||||
} else {
|
||||
// Special World (0x80-0x9F) - subtract 0x80 to get display coordinates
|
||||
parent_map_x = (highlight_parent - 0x80) % 8;
|
||||
parent_map_y = (highlight_parent - 0x80) / 8;
|
||||
}
|
||||
|
||||
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
|
||||
parent_map_y * kOverworldMapSize,
|
||||
large_map_size, large_map_size);
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Fixes It**:
|
||||
- Map IDs are **absolute**: Light World 0x00-0x3F, Dark World 0x40-0x7F, Special 0x80-0x9F
|
||||
- Display coordinates are **relative**: Each world displays in an 8x8 grid (0-7, 0-7)
|
||||
- Without subtracting the world offset, coordinates overflow the display grid
|
||||
- This matches the same logic used for v3 large maps (lines 1084-1096) and small maps (lines 1141-1172)
|
||||
|
||||
## Commit Reference
|
||||
|
||||
**Canvas Refactoring Commits**:
|
||||
- `f538775954` - Organize Canvas Utilities and BPP Format Management
|
||||
- `60ddf76331` - Integrate Canvas Automation API and Simplify Overworld Editor Controls
|
||||
|
||||
These commits moved canvas utilities to modular components but introduced the regression by not maintaining hover position tracking.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
1. **Canvas Mode System**: Complete the interaction handler modes (tile paint, select, etc.)
|
||||
2. **Persistent Context Menus**: Implement mode switching through context menu popups
|
||||
3. **Debugging Visualization**: Add canvas coordinate overlay for debugging
|
||||
4. **E2E Tests**: Create end-to-end tests for overworld map highlighting workflow
|
||||
|
||||
## Related Documentation
|
||||
- `docs/G1-canvas-guide.md` - Canvas system architecture
|
||||
- `docs/E5-debugging-guide.md` - Debugging techniques
|
||||
- `docs/debugging-startup-flags.md` - CLI flags for editor testing
|
||||
@@ -6,28 +6,65 @@ This document outlines the development roadmap for yaze. The project has achieve
|
||||
|
||||
## Current Focus: AI & Editor Polish
|
||||
|
||||
With the core systems stable, the immediate priority is to enhance the `z3ed` AI agent, finalize the Tile16 editor, and improve the user experience.
|
||||
With the core systems stable, the immediate priority is to finish the overworld editor features, fix dungeon object rendering, finalize the Tile16 editor, enhance the `z3ed` AI agent, and improve the user experience.
|
||||
|
||||
---
|
||||
|
||||
## 0.4.X (Next Major Release) - Advanced Tooling & UX
|
||||
## 0.4.0 (Next Major Release) - SDL3 Modernization & Core Improvements
|
||||
|
||||
### Priority 1: Editor Features & UX
|
||||
- **Tile16 Palette System**: Resolve the remaining color consistency issues in the Tile16 editor's source tile view and implement the palette-switching functionality.
|
||||
- **Overworld Sprite Editing**: Complete the workflow for adding, removing, and moving sprites directly on the overworld canvas.
|
||||
- **Dungeon Editor UI**: Add human-readable labels for rooms/entrances and implement tab management for a better multi-room workflow.
|
||||
- **Performance**: Address the slow initial load time (~2.6 seconds) by implementing lazy loading for rooms.
|
||||
**Status:** Planning
|
||||
**Type:** Major Breaking Release
|
||||
**Timeline:** 6-8 weeks
|
||||
|
||||
### Priority 2: `z3ed` AI Agent
|
||||
- ✅ **Vim Mode**: Implemented vim-style line editing in simple-chat with full modal editing support
|
||||
- ✅ **Autocomplete**: Added intelligent command completion with fuzzy matching in FTXUI chat
|
||||
- **Live LLM Hardening**: Finalize testing of the native Gemini function-calling loop and the proactive v3 system prompt.
|
||||
- **AI-Driven Editing**: Integrate the AI with the GUI test harness to allow for automated, mouse-driven edits based on natural language commands.
|
||||
- **Expand Agent Toolset**: Add new read-only tools for inspecting dialogue, music data, and sprite properties.
|
||||
### 🎯 Primary Goals
|
||||
|
||||
### Priority 3: Testing & Stability
|
||||
- **Windows Validation**: Perform a full testing cycle on Windows to validate the `z3ed` CLI, GUI test harness, and collaboration features.
|
||||
- **Expand E2E Coverage**: Create comprehensive end-to-end smoke tests for the Dungeon and Overworld editors.
|
||||
1. **SDL3 Migration** - Modernize graphics/audio/input backend
|
||||
2. **Dependency Reorganization** - Consolidate `src/lib/` + `third_party/` → `external/`
|
||||
3. **Backend Abstraction** - Clean separation of graphics/audio/input backends
|
||||
4. **Editor Polish** - Complete remaining UX improvements
|
||||
|
||||
### Phase 1: Infrastructure (Week 1-2)
|
||||
- **Dependency Consolidation**: Merge `src/lib/` and `third_party/` into unified `external/` directory
|
||||
- **CMake Modernization**: Update all build files, submodules, and CI workflows
|
||||
- **Build Validation**: Ensure all platforms (Windows/macOS/Linux) build cleanly
|
||||
|
||||
### Phase 2: SDL3 Core Migration (Week 3-4)
|
||||
- **SDL3 Update**: Migrate from SDL2 to SDL3 with GPU-based rendering
|
||||
- **Graphics Backend**: Create abstraction layer (`GraphicsBackend` interface)
|
||||
- **Basic Rendering**: Get window creation and basic editor rendering working
|
||||
- **ImGui Integration**: Update ImGui SDL3 backend integration
|
||||
|
||||
### Phase 3: Complete SDL3 Integration (Week 5-6)
|
||||
- **All Editors**: Port all 6 editors (Overworld, Dungeon, Graphics, Palette, Screen, Music) to new backend
|
||||
- **Audio Backend**: Implement SDL3 audio backend for emulator
|
||||
- **Input Backend**: Implement SDL3 input backend with improved gamepad support
|
||||
- **Performance**: Optimize rendering performance, benchmark against v0.3.x
|
||||
|
||||
### Phase 4: Editor Features & UX (Week 7-8)
|
||||
- **Tile16 Palette System**: Resolve color consistency issues in source tile view
|
||||
- **Overworld Sprite Editing**: Complete sprite add/remove/move workflow
|
||||
- **Dungeon Editor UI**: Add human-readable room labels and improved tab management
|
||||
- **Performance**: Implement lazy loading for rooms (~2.6s → <1s load time)
|
||||
|
||||
### Phase 5: AI Agent Enhancements (Throughout)
|
||||
- ✅ **Vim Mode**: Implemented vim-style line editing in simple-chat
|
||||
- ✅ **Autocomplete**: Added intelligent command completion with fuzzy matching
|
||||
- **Live LLM Hardening**: Finalize Gemini function-calling and proactive v3 prompts
|
||||
- **AI-Driven Editing**: Integrate AI with GUI test harness for automated edits
|
||||
- **Expand Toolset**: Add tools for dialogue, music data, sprite properties
|
||||
|
||||
### Success Criteria
|
||||
- [ ] All platforms build and run with SDL3
|
||||
- [ ] No performance regression vs v0.3.x
|
||||
- [ ] All editors functional with new backend
|
||||
- [ ] Emulator audio/input working
|
||||
- [ ] Documentation updated
|
||||
- [ ] Migration guide complete
|
||||
|
||||
**Breaking Changes:**
|
||||
- SDL2 → SDL3 (requires recompilation)
|
||||
- Directory restructure (requires submodule re-init)
|
||||
- API changes in graphics backend (for extensions)
|
||||
|
||||
---
|
||||
|
||||
|
||||
379
docs/I2-future-improvements.md
Normal file
379
docs/I2-future-improvements.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# Future Improvements & Long-Term Vision
|
||||
|
||||
**Last Updated:** October 10, 2025
|
||||
**Status:** Living Document
|
||||
|
||||
This document outlines potential improvements, experimental features, and long-term vision for yaze. Items here are aspirational and may or may not be implemented depending on community needs, technical feasibility, and development resources.
|
||||
|
||||
---
|
||||
|
||||
## Architecture & Performance
|
||||
|
||||
### Emulator Core Improvements
|
||||
See `docs/E6-emulator-improvements.md` for detailed emulator improvement roadmap.
|
||||
|
||||
**Priority Items:**
|
||||
- **APU Timing Fix**: Cycle-accurate SPC700 execution for reliable music playback
|
||||
- **CPU Cycle Accuracy**: Variable instruction timing for better game compatibility
|
||||
- **PPU Scanline Renderer**: Replace pixel-based renderer for 20%+ performance boost
|
||||
- **Audio Buffering**: Lock-free ring buffer to eliminate stuttering
|
||||
|
||||
### Plugin Architecture (v0.5.x+)
|
||||
Enable community extensions and custom tools.
|
||||
|
||||
**Features:**
|
||||
- C API for plugin development
|
||||
- Hot-reload capability for rapid iteration
|
||||
- Plugin registry and discovery system
|
||||
- Example plugins (custom exporters, automation tools)
|
||||
|
||||
**Benefits:**
|
||||
- Community can extend without core changes
|
||||
- Experimentation without bloating core
|
||||
- Custom workflow tools per project needs
|
||||
|
||||
### Multi-Threading Improvements
|
||||
Parallelize heavy operations for better performance.
|
||||
|
||||
**Opportunities:**
|
||||
- Background ROM loading
|
||||
- Parallel graphics decompression
|
||||
- Asynchronous file I/O
|
||||
- Worker thread pool for batch operations
|
||||
|
||||
---
|
||||
|
||||
## Graphics & Rendering
|
||||
|
||||
### Advanced Graphics Editing
|
||||
Full graphics sheet import/export workflow.
|
||||
|
||||
**Features:**
|
||||
- Import modified PNG graphics sheets
|
||||
- Automatic palette extraction and optimization
|
||||
- Tile deduplication and compression
|
||||
- Preview impact on ROM size
|
||||
|
||||
**Use Cases:**
|
||||
- Complete graphics overhauls
|
||||
- HD texture packs (with downscaling)
|
||||
- Art asset pipelines
|
||||
|
||||
### Alternative Rendering Backends
|
||||
Support beyond SDL3 for specialized use cases.
|
||||
|
||||
**Potential Backends:**
|
||||
- **OpenGL**: Maximum compatibility, explicit control
|
||||
- **Vulkan**: High-performance, low-overhead (Linux/Windows)
|
||||
- **Metal**: Native macOS/iOS performance
|
||||
- **WebGPU**: Browser-based editor
|
||||
|
||||
**Benefits:**
|
||||
- Platform-specific optimization
|
||||
- Testing without hardware dependencies
|
||||
- Future-proofing for new platforms
|
||||
|
||||
### High-DPI / 4K Support
|
||||
Perfect rendering on modern displays.
|
||||
|
||||
**Improvements:**
|
||||
- Retina/4K-aware canvas rendering
|
||||
- Scalable UI elements
|
||||
- Crisp text at any zoom level
|
||||
- Per-monitor DPI awareness
|
||||
|
||||
---
|
||||
|
||||
## AI & Automation
|
||||
|
||||
### Multi-Modal AI Input
|
||||
Enhance `z3ed` with visual understanding.
|
||||
|
||||
**Features:**
|
||||
- Screenshot → context for AI
|
||||
- "Fix this room" with image reference
|
||||
- Visual diff analysis
|
||||
- Automatic sprite positioning from mockups
|
||||
|
||||
### Collaborative AI Sessions
|
||||
Shared AI context in multiplayer editing.
|
||||
|
||||
**Features:**
|
||||
- Shared AI conversation history
|
||||
- AI-suggested edits visible to all users
|
||||
- Collaborative problem-solving
|
||||
- Role-based AI permissions
|
||||
|
||||
### Automation & Scripting
|
||||
Python/Lua scripting for batch operations.
|
||||
|
||||
**Use Cases:**
|
||||
- Batch room modifications
|
||||
- Automated testing scripts
|
||||
- Custom validation rules
|
||||
- Import/export pipelines
|
||||
|
||||
---
|
||||
|
||||
## Content Editors
|
||||
|
||||
### Music Editor UI
|
||||
Visual interface for sound and music editing.
|
||||
|
||||
**Features:**
|
||||
- Visual SPC700 music track editor
|
||||
- Sound effect browser and editor
|
||||
- Import custom SPC files
|
||||
- Live preview while editing
|
||||
|
||||
### Dialogue Editor
|
||||
Comprehensive text editing system.
|
||||
|
||||
**Features:**
|
||||
- Visual dialogue tree editor
|
||||
- Text search across all dialogues
|
||||
- Translation workflow support
|
||||
- Character count warnings
|
||||
- Preview in-game font rendering
|
||||
|
||||
### Event Editor
|
||||
Visual scripting for game events.
|
||||
|
||||
**Features:**
|
||||
- Node-based event editor
|
||||
- Trigger condition builder
|
||||
- Preview event flow
|
||||
- Debug event sequences
|
||||
|
||||
### Hex Editor Enhancements
|
||||
Power-user tool for low-level editing.
|
||||
|
||||
**Features:**
|
||||
- Structure definitions (parse ROM data types)
|
||||
- Search by data pattern
|
||||
- Diff view between ROM versions
|
||||
- Bookmark system for addresses
|
||||
- Disassembly view integration
|
||||
|
||||
---
|
||||
|
||||
## Collaboration & Networking
|
||||
|
||||
### Real-Time Collaboration Improvements
|
||||
Enhanced multi-user editing.
|
||||
|
||||
**Features:**
|
||||
- Conflict resolution strategies
|
||||
- User presence indicators (cursor position)
|
||||
- Chat integration
|
||||
- Permission system (read-only, edit, admin)
|
||||
- Rollback/version control
|
||||
|
||||
### Cloud ROM Storage
|
||||
Optional cloud backup and sync.
|
||||
|
||||
**Features:**
|
||||
- Encrypted cloud storage
|
||||
- Automatic backups
|
||||
- Cross-device sync
|
||||
- Shared project workspaces
|
||||
- Version history
|
||||
|
||||
---
|
||||
|
||||
## Platform Support
|
||||
|
||||
### Web Assembly Build
|
||||
Browser-based yaze editor.
|
||||
|
||||
**Benefits:**
|
||||
- No installation required
|
||||
- Cross-platform by default
|
||||
- Shareable projects via URL
|
||||
- Integrated with cloud storage
|
||||
|
||||
**Challenges:**
|
||||
- File system access limitations
|
||||
- Performance considerations
|
||||
- WebGPU renderer requirement
|
||||
|
||||
### Mobile Support (iOS/Android)
|
||||
Touch-optimized editor for tablets.
|
||||
|
||||
**Features:**
|
||||
- Touch-friendly UI
|
||||
- Stylus support
|
||||
- Cloud sync with desktop
|
||||
- Read-only preview mode for phones
|
||||
|
||||
**Use Cases:**
|
||||
- Tablet editing on the go
|
||||
- Reference/preview on phone
|
||||
- Show ROM to players on mobile
|
||||
|
||||
---
|
||||
|
||||
## Quality of Life
|
||||
|
||||
### Undo/Redo System Enhancement
|
||||
More granular and reliable undo.
|
||||
|
||||
**Improvements:**
|
||||
- Per-editor undo stacks
|
||||
- Undo history viewer
|
||||
- Branching undo (tree structure)
|
||||
- Persistent undo across sessions
|
||||
|
||||
### Project Templates
|
||||
Quick-start templates for common ROM hacks.
|
||||
|
||||
**Templates:**
|
||||
- Vanilla+ (minimal changes)
|
||||
- Graphics overhaul
|
||||
- Randomizer base
|
||||
- Custom story framework
|
||||
|
||||
### Asset Library
|
||||
Shared library of community assets.
|
||||
|
||||
**Features:**
|
||||
- Import community sprites/graphics
|
||||
- Share custom rooms/dungeons
|
||||
- Tag-based search
|
||||
- Rating and comments
|
||||
- License tracking
|
||||
|
||||
### Accessibility
|
||||
Make yaze usable by everyone.
|
||||
|
||||
**Features:**
|
||||
- Screen reader support
|
||||
- Keyboard-only navigation
|
||||
- Colorblind-friendly palettes
|
||||
- High-contrast themes
|
||||
- Adjustable font sizes
|
||||
|
||||
---
|
||||
|
||||
## Testing & Quality
|
||||
|
||||
### Automated Regression Testing
|
||||
Catch bugs before they ship.
|
||||
|
||||
**Features:**
|
||||
- Automated UI testing framework
|
||||
- Visual regression tests (screenshot diffs)
|
||||
- Performance regression detection
|
||||
- Automated ROM patching tests
|
||||
|
||||
### ROM Validation
|
||||
Ensure ROM hacks are valid.
|
||||
|
||||
**Features:**
|
||||
- Detect common errors (invalid pointers, etc.)
|
||||
- Warn about compatibility issues
|
||||
- Suggest fixes for problems
|
||||
- Export validation report
|
||||
|
||||
### Continuous Integration Enhancements
|
||||
Better CI/CD pipeline.
|
||||
|
||||
**Improvements:**
|
||||
- Build artifacts for every commit
|
||||
- Automated performance benchmarks
|
||||
- Coverage reports
|
||||
- Security scanning
|
||||
|
||||
---
|
||||
|
||||
## Documentation & Community
|
||||
|
||||
### API Documentation Generator
|
||||
Auto-generated API docs from code.
|
||||
|
||||
**Features:**
|
||||
- Doxygen → web docs pipeline
|
||||
- Example code snippets
|
||||
- Interactive API explorer
|
||||
- Versioned documentation
|
||||
|
||||
### Video Tutorial System
|
||||
In-app video tutorials.
|
||||
|
||||
**Features:**
|
||||
- Embedded tutorial videos
|
||||
- Step-by-step guided walkthroughs
|
||||
- Context-sensitive help
|
||||
- Community-contributed tutorials
|
||||
|
||||
### ROM Hacking Wiki Integration
|
||||
Link editor to wiki documentation.
|
||||
|
||||
**Features:**
|
||||
- Context-sensitive wiki links
|
||||
- Inline documentation for ROM structures
|
||||
- Community knowledge base
|
||||
- Translation support
|
||||
|
||||
---
|
||||
|
||||
## Experimental / Research
|
||||
|
||||
### Machine Learning Integration
|
||||
AI-assisted ROM hacking.
|
||||
|
||||
**Possibilities:**
|
||||
- Auto-generate room layouts
|
||||
- Suggest difficulty curves
|
||||
- Detect similar room patterns
|
||||
- Generate sprite variations
|
||||
|
||||
### VR/AR Visualization
|
||||
Visualize SNES data in 3D.
|
||||
|
||||
**Use Cases:**
|
||||
- 3D preview of overworld
|
||||
- Virtual dungeon walkthrough
|
||||
- Spatial room editing
|
||||
|
||||
### Symbolic Execution
|
||||
Advanced debugging technique.
|
||||
|
||||
**Features:**
|
||||
- Explore all code paths
|
||||
- Find unreachable code
|
||||
- Detect potential bugs
|
||||
- Generate test cases
|
||||
|
||||
---
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
These improvements are **not scheduled** and exist here as ideas for future development. Priority will be determined by:
|
||||
|
||||
1. **Community demand** - What users actually need
|
||||
2. **Technical feasibility** - What's possible with current architecture
|
||||
3. **Development resources** - Available time and expertise
|
||||
4. **Strategic fit** - Alignment with project vision
|
||||
|
||||
---
|
||||
|
||||
## Contributing Ideas
|
||||
|
||||
Have an idea for a future improvement?
|
||||
|
||||
- Open a GitHub Discussion in the "Ideas" category
|
||||
- Describe the problem it solves
|
||||
- Outline potential implementation approach
|
||||
- Consider technical challenges
|
||||
|
||||
The best ideas are:
|
||||
- **Specific**: Clear problem statement
|
||||
- **Valuable**: Solves real user pain points
|
||||
- **Feasible**: Realistic implementation
|
||||
- **Scoped**: Can be broken into phases
|
||||
|
||||
---
|
||||
|
||||
**Note:** This is a living document. Ideas may be promoted to the active roadmap (`I1-roadmap.md`) or removed as project priorities evolve.
|
||||
|
||||
@@ -10,7 +10,7 @@ Welcome to the official documentation for yaze, a comprehensive ROM editor for T
|
||||
- [B1: Build Instructions](B1-build-instructions.md) - How to build yaze on Windows, macOS, and Linux.
|
||||
- [B2: Platform Compatibility](B2-platform-compatibility.md) - Cross-platform support details.
|
||||
- [B3: Build Presets](B3-build-presets.md) - A guide to the CMake preset system.
|
||||
- [B4: Release Workflows](B4-release-workflows.md) - GitHub Actions release pipeline documentation.
|
||||
- [B4: Git Workflow](B4-git-workflow.md) - Branching strategy, commit conventions, and release process.
|
||||
- [B5: Architecture and Networking](B5-architecture-and-networking.md) - System architecture, gRPC, and networking.
|
||||
|
||||
## C: `z3ed` CLI
|
||||
@@ -20,26 +20,55 @@ Welcome to the official documentation for yaze, a comprehensive ROM editor for T
|
||||
- [E1: Assembly Style Guide](E1-asm-style-guide.md) - 65816 assembly coding standards.
|
||||
- [E2: Development Guide](E2-development-guide.md) - Core architectural patterns, UI systems, and best practices.
|
||||
- [E3: API Reference](E3-api-reference.md) - C/C++ API documentation for extensions.
|
||||
- [E4: Emulator Development Guide](E4-Emulator-Development-Guide.md)
|
||||
- [E5: Debugging and Testing Guide](E5-debugging-guide.md) - A master guide to the SNES emulator subsystem.
|
||||
- [E4: Emulator Development Guide](E4-Emulator-Development-Guide.md) - SNES emulator subsystem implementation guide.
|
||||
- [E5: Debugging Guide](E5-debugging-guide.md) - Debugging techniques and workflows.
|
||||
- [E6: Emulator Improvements](E6-emulator-improvements.md) - Core accuracy and performance improvements roadmap.
|
||||
- [E7: Debugging Startup Flags](E7-debugging-startup-flags.md) - CLI flags for quick editor access and testing.
|
||||
- [E8: Emulator Debugging Vision](E8-emulator-debugging-vision.md) - Long-term vision for Mesen2-level debugging features.
|
||||
|
||||
## F: Technical Documentation
|
||||
- [F1: Dungeon Editor Guide](F1-dungeon-editor-guide.md) - A master guide to the dungeon editing system.
|
||||
- [F1: Dungeon Editor Guide](F1-dungeon-editor-guide.md) - Master guide to the dungeon editing system.
|
||||
- [F2: Tile16 Editor Palette System](F2-tile16-editor-palette-system.md) - Design of the palette system.
|
||||
- [F3: Overworld Loading](F3-overworld-loading.md) - How vanilla and ZSCustomOverworld maps are loaded.
|
||||
- [F4: Overworld Agent Guide](F4-overworld-agent-guide.md) - AI agent integration for overworld editing.
|
||||
|
||||
## G: GUI Guides
|
||||
## G: Graphics & GUI Systems
|
||||
- [G1: Canvas System and Automation](G1-canvas-guide.md) - The core GUI drawing and interaction system.
|
||||
- [G2: Renderer Migration Plan](G2-renderer-migration-plan.md) - Historical plan for renderer refactoring.
|
||||
- [G3: Palette System Overview](G3-palete-system-overview.md) - SNES palette system and editor integration.
|
||||
- [G3: Renderer Migration Complete](G3-renderer-migration-complete.md) - Post-migration analysis and results.
|
||||
- [G4: Canvas Coordinate Fix](G4-canvas-coordinate-fix.md) - Technical deep-dive on canvas coordinate synchronization bug fix.
|
||||
|
||||
## H: Project Info
|
||||
- [H1: Changelog](H1-changelog.md)
|
||||
|
||||
## I: Roadmap
|
||||
- [I1: Roadmap](I1-roadmap.md)
|
||||
## I: Roadmap & Vision
|
||||
- [I1: Roadmap](I1-roadmap.md) - Current development roadmap and planned releases.
|
||||
- [I2: Future Improvements](I2-future-improvements.md) - Long-term vision and aspirational features.
|
||||
|
||||
## R: ROM Reference
|
||||
- [R1: A Link to the Past ROM Reference](R1-alttp-rom-reference.md) - Technical reference for ALTTP ROM structures, graphics, palettes, and compression.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: October 2025 - Version 0.3.3 (In Flux)*
|
||||
## Documentation Standards
|
||||
|
||||
### Naming Convention
|
||||
- **A-series**: Getting Started & Testing
|
||||
- **B-series**: Build, Platform & Git Workflow
|
||||
- **C-series**: CLI Tools (`z3ed`)
|
||||
- **E-series**: Development, API & Emulator
|
||||
- **F-series**: Feature-Specific Technical Docs
|
||||
- **G-series**: Graphics & GUI Systems
|
||||
- **H-series**: Project Info (Changelog, etc.)
|
||||
- **I-series**: Roadmap & Vision
|
||||
- **R-series**: ROM Technical Reference
|
||||
|
||||
### File Naming
|
||||
- Use descriptive, kebab-case names
|
||||
- Prefix with series letter and number (e.g., `E4-emulator-development-guide.md`)
|
||||
- Keep filenames concise but clear
|
||||
|
||||
---
|
||||
|
||||
*Last updated: October 10, 2025 - Version 0.3.2 (Preparing for Release)*
|
||||
Reference in New Issue
Block a user