Files
psxrecomp/CONTRIBUTING.md
2026-10-22 14:23:14 -07:00

308 lines
15 KiB
Markdown

# Contributing to PSXRecomp
Thanks for your interest! PSXRecomp is a static recompiler, not an emulator, and
it holds itself to a high correctness bar. This guide covers how the project is
organized, the rules that keep it faithful, and how to get a change merged.
Contributions are welcome — AI-assisted or not — as long as they are reviewed,
tested, and keep the core game-agnostic.
New to the codebase? Read [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and
[`docs/EXECUTION_MODEL.md`](docs/EXECUTION_MODEL.md) first, then
[`docs/BUILDING.md`](docs/BUILDING.md) to get a build going.
## Repository layout
- **`mstan/psxrecomp`** (this repo) — the framework: the recompiler tool
(`recompiler/`), the runtime engine (`runtime/`), shared tools, and docs.
Framework changes go here.
- **Per-game repos** (`mstan/TombaRecomp`, `mstan/MegaManX6Recomp`,
`mstan/ApeEscapeRecomp`, …) — one per title. Each keeps **game code at the
repo root** and pins this framework (and usually `recomp-ui`) as **root-level
submodules** (`psxrecomp/`, `recomp-ui/`). Game-specific work goes in the game
repo; nothing game-specific belongs in the framework.
Each game repo pins an **exact framework commit** — the git submodule pointer at
`psxrecomp/` (some older game repos also carry a human-readable `psxrecomp-v4.pin`
record; the submodule pointer is the source of truth). The framework evolves on
its own cadence; a game only moves to a newer framework when someone deliberately
bumps that submodule pointer.
**Starting a new title or shipping a release?** Follow
[`docs/GAME_PROJECT_SETUP.md`](docs/GAME_PROJECT_SETUP.md) (layout, CI template
under `docs/ci/templates/game-release.yml`, release checklist). Also see
[Linking the framework](docs/BUILDING.md#linking-the-framework) and
[Framework changes and the pin](#framework-changes-and-the-pin) below.
## The core rules
These are non-negotiable because violating them is how past efforts turned into
un-debuggable emulators. The full engineering constitution is
[`CLAUDE.md`](CLAUDE.md); the debugging philosophy is
[`docs/internal/PRINCIPLES.md`](docs/internal/PRINCIPLES.md). The short version:
1. **Build the faithful core; no per-game hacks in the foundation.** The correct
fix is the general, hardware-accurate one, not a surgical workaround for one
title. No title checks in the runtime, no magic PC addresses, no spoofed
return values for one game. (Per-game *enhancements* — widescreen, faster
loads — are legitimate once the faithful core is proven; see
[`docs/ENHANCEMENTS.md`](docs/ENHANCEMENTS.md).)
2. **No stubs.** A function is fully implemented or it fails loudly. No
`return 0;` placeholders, no `// TODO` behavior, no hand-delivered fake
events. If execution reaches code we can't handle, we stop and fix
discovery/codegen — we don't fake the result.
3. **Never hand-edit generated code.** Files under `generated/` and the
recompiler's output are build artifacts. If the emitted C is wrong, fix the
recompiler (`recompiler/src/`), then regenerate. (The one exception: a PR
whose explicit purpose is to *inspect or prove* a generator bug.)
4. **LLE is the baseline; HLE is a validated subsystem replacement, never a
starting point.** The recompiled BIOS is the reference and oracle. An HLE tier
is allowed only where the LLE path has a genuine landmine, must be general
(every game), must operate on the real guest structures, and must be
continuously checked against the Beetle oracle — never a "produce the answer
the BIOS would have" shim.
5. **No `printf`/log-file debugging.** Runtime inspection goes through the TCP
debug server ([`docs/TCP_COMMANDS.md`](docs/TCP_COMMANDS.md)) and always-on ring
buffers, not `fprintf`.
6. **Fix broken tooling immediately** — don't route around it with indirect
evidence, and don't infer correctness from two implementations sharing a bug.
## AI-assisted and human contributions
Both are welcome. Using AI does not reduce your responsibility for the result:
review, understand, and test any code before submitting it, and don't submit a
large AI-generated rewrite you can't explain or validate. A PR — however it was
written — is likely to be rejected if it:
- Introduces broad, unfocused changes, or combines unrelated refactors with
behavioral fixes.
- Hardcodes game-specific behavior into the framework, or adds a title check to
the core runtime.
- Modifies generated output instead of fixing the generator/source.
- Introduces regressions in known-working games.
- Contains code the contributor cannot reasonably explain, or whose verification
cannot be stated.
### Commit messages
Use a subject only by default. Add a body only when the reason is not clear
from the diff, and keep it to 1-3 lines. Do not add per-file changelogs or
repeat the diff or PR description.
### BIOS emitter changes
After changing a BIOS fingerprint input, run
`bash tools/regen_bios.sh --config bios/OpenBIOS.toml` and commit the regenerated
OpenBIOS output and stamp together. PR CI checks the committed stamps without
requiring a retail BIOS dump. Never update a stamp without regenerating.
Retail-dump users must also regenerate each linked retail stem, for example
`bash tools/regen_bios.sh --config bios/SCPH1001.toml`. Retail dumps and generated
retail C/stamps stay local and gitignored. In the PR validation notes, name the
BIOS stems checked and whether their output changed; explicitly say when a
retail stem was not checked. Unchanged OpenBIOS output does not establish
unchanged SCPH1001 output.
## Game-specific work belongs in game repos
The framework stays game-agnostic. Per-game patches, launcher UI, assets, memory
maps, metadata, config, and workarounds live in the appropriate game repo, not
here. If a behavior differs by game, disc, executable, region, BIOS, or runtime
configuration, it should be **parameterized** (config option, per-game manifest
value, a parameter passed in from the game repo) or **modeled** as the real
hardware/runtime behavior — never special-cased in the core.
Known game repos:
- [TombaRecomp](https://github.com/mstan/TombaRecomp) — *Tomba!*
- [MegaManX6Recomp](https://github.com/mstan/MegaManX6Recomp) — *Mega Man X6*
- [ApeEscapeRecomp](https://github.com/mstan/ApeEscapeRecomp) — *Ape Escape*
Maintainers record evaluated external branches, game repos, exact revisions,
and reusable upstream candidates in the
[`ecosystem watch`](docs/ecosystem-watch.md). Check it before porting work from
an older framework fork.
### Framework changes and the pin
Because each game pins an exact framework commit (its `psxrecomp/` submodule
pointer), a framework fix does **not** reach a game until that game bumps that
pointer. When your framework change is meant to fix or improve a specific title:
- **Link the game repo** in your PR (and a matching game-repo branch/commit/PR if
you have one). This lets maintainers build and test the fix in its real
integration environment.
- **Build the game against your framework branch** to prove it. The game's
`CMakeLists.txt` points `PSXRECOMP_ROOT` at the submodule; you can build
against a working checkout via the junction/symlink dev setup, or override
`PSXRECOMP_ROOT` at configure time to point at your branch — no submodule
surgery required. See [`docs/BUILDING.md`](docs/BUILDING.md#linking-the-framework).
- **Say whether a pin bump is required to consume the fix**, and whether it
requires a **regen** (codegen/emitter changes do; runtime-only changes usually
don't — note which). Bumping the submodule pointer is a deliberate, reviewable
change made in the game repo, not something a framework PR does on its own.
## Verifying a change
Correctness is demonstrated, not asserted:
- **Oracle-check against Beetle.** Run `psx-beetle` alongside `psx-runtime` and
compare via the shared TCP protocol; for timing/codegen, use the co-sim build.
See [`docs/internal/COSIM_ORACLE.md`](docs/internal/COSIM_ORACLE.md).
- **Verify visually, end to end.** A milestone is "the pixels appear on screen"
— take a screenshot, don't trust a counter or a ring-buffer flag alone.
- **A decoder / codegen change requires a playthrough check**, not just a clean
build: cosim/lockstep first, then run the affected area.
### Automated tests — run these first
```sh
cmake -S recompiler -B recompiler/build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build recompiler/build
cd recompiler/build && ctest --output-on-failure
```
38 tests, under five seconds, no BIOS dump or disc image required. See
[`docs/TESTING.md`](docs/TESTING.md) for running individual tests, what the
suite covers, and the three known-failing tests that are deliberately not
registered.
If you add a test, register it with `ctest` in the same commit. An unregistered
test cannot fail, and a test that cannot fail is not a test.
### Regression checklist
The automated suite does not cover whether a game still runs; only playing one
does. A framework change should not break a game that worked before it. When
practical, build against the known-working game repos above and confirm, for
each:
- The game **boots**.
- The **attract demo** plays without issue.
- A save file can be **created**.
- A save file can be **loaded**.
- **Gameplay is reachable** after saving/loading.
Perfect compatibility across every game isn't required for every PR, but the PR
must clearly document **what was tested and on which games**. Useful notes: host
OS, compiler/toolchain, game repo + version/region, whether it boots / reaches
gameplay / save-load works, and whether memory cards, audio, video, input, CD
loading, or overlays were touched. Call out any known regression or uncertain
area explicitly — a documented uncertainty is fine; a silent one is not.
## No copyrighted assets
Do not submit copyrighted binary content to this repository. This includes, but
is not limited to:
- PlayStation BIOS files
- Game ROMs, ISOs, BIN/CUE files, or disc images
- Extracted game assets — music, sound, textures, models, video, fonts, artwork
- Proprietary SDK files
- Decompiled copyrighted game source
Small factual references — addresses, symbols, hashes, structure descriptions,
compatibility notes — are acceptable when used for tooling, debugging, or
documentation. The local artifacts above are gitignored in every repo for exactly
this reason (copyright + size); keep them out of commits.
## Adding a new game
At a high level: create a game repo from the template, add this framework as the
`psxrecomp/` submodule, seed function starts (typically from a Ghidra export in
`seeds/`), write `game.toml`, generate the game C with
`psxrecomp-game --config game.toml`, then build and bring the title up boot →
menu → gameplay, oracle-checking as you go. The existing game repos
(TombaRecomp, MegaManX6Recomp) are the working references.
## Pull requests
Keep PRs focused — one thing: one bug, one feature, one diagnostic, one isolated
refactor, or docs for one topic. Avoid combining unrelated refactors, formatting
churn, and behavioral fixes in the same PR.
- Branch off `master`; keep framework changes and game changes in their
respective repos.
- **Do not commit** local artifacts: BIOS images, disc images, generated C
(`generated/`), memory cards, Ghidra databases, build outputs, or overlay
capture files.
- Explain *what hardware behavior* the change matches and *how you verified it*
(oracle diff, screenshot, playthrough). Changes that can't state their
verification are hard to accept.
- Keep the fix minimal and at the right layer (recompiler vs runtime vs game
config). If you find yourself editing generated code or adding a stub, step
back — the real fix is upstream of that.
When opening a PR, please include: what changed and why; whether it's
game-agnostic or needs per-game configuration; what games/tests were run; any
risks or known limitations; links to affected game repos/branches/commits when
the change targets a specific game; and screenshots, logs, or traces when useful.
## Bug reports
Good bug reports are extremely helpful. Please include:
- The game repo being used, and version/region if known.
- Your OS and build type/configuration.
- Steps to reproduce, expected behavior, and actual behavior.
- Logs, traces, screenshots, or video if available.
- Whether it happens in **LLE (recompiled BIOS)**, **HLE**, and/or the
**dirty-RAM interpreter** path — and in the software vs OpenGL vs Vulkan
renderer, if relevant.
- Whether it's a **regression** from a previous commit, and the last good one if
you know it.
For gameplay bugs, include the nearest save file or a reproduction path when
possible. For build failures, include `gcc -v` / your toolchain / the generator.
## Dependencies and portability
Avoid unnecessary dependencies. When adding one, explain why it's needed and how
it affects portability, build complexity, licensing, or distribution. The project
should stay practical to build and run for contributors.
## Style and maintainability
Prefer clear, boring, maintainable code that reads like the code around it: match
the surrounding style and naming, keep changes localized, and comment non-obvious
hardware behavior or timing assumptions. Prefer diagnostics that can be compiled
out when not needed. Avoid cleverness unless it's clearly justified.
## Licensing
By contributing, you agree your contribution is licensed under the same license as
the project. Do not submit code you don't have the right to contribute, and do not
copy from incompatible licenses or proprietary sources.
Runtime comments cite hardware facts to PSX-SPX, and emulators are used only as
black-box references whose outputs we compare. See
[`docs/PROVENANCE.md`](docs/PROVENANCE.md) for the rules and for the passages
still to be replaced.
### Upstream-derived work and credit
When a change adapts code or behavior from another branch or repository, add a
short record under `docs/internal/upstream/`. The record must include:
- clickable links to the source repository or PR and the exact source commit;
- the original author identity and how commit/co-author credit is preserved;
- the behavior or files actually adapted;
- related source material deliberately excluded from the focused change; and
- the validation performed after rebuilding the change on current `master`.
Prefer preserving the source commit's author when a clean cherry-pick is
appropriate. For a reconstructed or partial port, add the human contributor's
exact `Co-authored-by` trailer and cite the source in the commit or PR body.
Attribution does not replace license compatibility or permission.
## Be respectful
Contributors use different tools, workflows, and levels of AI assistance. Review
the contribution, not the person. Technical criticism is welcome; personal attacks
are not.
## Questions / community
Development happens with the **R.A.I.D. (Retro AI Development)** community —
Discord invite in the [README](README.md). Open a GitHub issue for bugs and
build problems (include `gcc -v` / OS / generator for build failures).