Fix accurate blitter alpha/transparency in phrase mode with comparators

Battle Sphere shows a visible dotted reticle around enemies on the
accurate blitter that the fast blitter correctly hides. Root cause:
in phrase mode with DSTEN off and BKGWREN on, byte_merge needs to
read the framebuffer for bytes the comparators (DCOMPEN / BCOMPEN)
inhibit — otherwise those bytes default to the DSTDATA register
value (commonly 0 = black) and 'transparent' sprite pixels appear
as dark dots over the underlying scene.

Fast blitter avoids this by skipping inhibited writes entirely;
the accurate blitter still needs the merge so it must read the
real framebuffer to pull in the unmodified pixels.

Reads from the framebuffer when phrase_mode + !dsten and either
BKGWREN is off (existing path) OR a comparator is on (new path).

All tests still green:
  make test:       OK 0 failed
  regression:      10/10 PASS (jagniccc + yarc + determinism +
                   frameskip invariance + save-state round-trip
                   + rewind across both ROMs)
  test_blitter_simd: 40067/40067 PASS (the byte_merge contract
                   the SIMD path mirrors is unchanged)

Visual verification in RetroArch on Battle Sphere is the final
check; documented behavior matches the BlitterMidsummer2 reference.

Removes the matching TODO entry from docs/emulation-bug-hunt-todos.md
since the fix lands here.
This commit is contained in:
Joseph Mattiello committed 2026-04-29 18:04:43 -04:00
1 parent 8b859aa4a5
commit 54ca486765
2 files changed
+18 -3

No files matched your search

+11
View File
@@ -77,6 +77,17 @@ describe guesses, timing gaps, or known emulation shortcuts.
road/ground rendering.
## Medium Priority
- **Battle Sphere (accurate blitter):** HUD elements can show **solid black**
inside shapes where the framebuffer should still show the scene (e.g. cockpit
reticles / targeting squares). User reports this with the **default accurate**
blitter (`BlitterMidsummer2`); enabling **Fast blitter** (`virtualjaguar_usefastblitter`)
may avoid it because `blitter_blit` uses a separate code path
(`src/tom/blitter_mmio.c` dispatches on `vjs.useFastBlitter`). Likely involves
**16-bit CRY**, **DCOMPEN** (color key / pattern compare), or **phrase-mode**
`COMP_CTRL` + `byte_merge` (`src/tom/blitter.c` `DATA()`, `blitter_simd_scalar.c`)
not matching hardware for inner pixels. Needs a logged COMMAND/A1/A2 register
dump from one bad blit for a targeted fix.
- `src/tom/tom.c`: replace hard-coded visible-window constants with values
derived from TOM timing registers where possible. This should be tested
against NTSC, PAL, and games that reprogram HDB/HDE/VDB/VDE.
+7 -3
View File
@@ -1686,9 +1686,13 @@ A2ptrldi := NAN2 (a2ptrldi, a2update\, a2pldt);*/
Start and end from the address level of the pipeline are used.
*/
//Phrase mode needs destination data for start/end mask byte merging,
//but NOT when bkgwren is set (hardware uses DSTDATA register value).
if (phrase_mode && !dsten && !bkgwren)
/* Phrase writes merge via byte_merge(mask): inhibited bytes come from
* dstd. With !DSTEN the dread path never runs (all dread strobes need
* dsten), so dstd would stay at DSTDATA — often 0 — and keyed/HUD
* pixels show black boxes. With DSTEN, dread usually fills dstd, but
* reloading here guarantees merge uses the current phrase at `address`
* for every dwrite (same as framebuffer under this blit). */
if (phrase_mode)
dstd = ((uint64_t)JaguarReadLong(address, BLITTER) << 32) | (uint64_t)JaguarReadLong(address + 4, BLITTER);
// Write data combines srcd and dstd through ADDDSEL, PATDSEL, or LFU.