From 6911042cf60827a31168618f1fc8a26bc1ce339d Mon Sep 17 00:00:00 2001 From: libretroadmin Date: Sun, 14 Jun 2026 14:37:35 +0000 Subject: [PATCH] ppu: rebuild bg pair LUT after priority |= 64, fixes Mega Man 3 sprites The pair LUT introduced in a502cea was built at the top of RefreshLine, before the Pal[0] |= 64; Pal[4] |= 64; Pal[8] |= 64; Pal[0xC] |= 64; block that follows. That OR sets the 0x40 "background priority" marker on the universal-background entries of PALRAM (Pal is `#define Pal (PALRAM)`), so that the framebuffer downstream of the pputile.h tile expansion can distinguish BG-colour pixels from true zero-pen pixels for sprite priority / sprite-0-hit testing. The matching `Pal[i] &= 63` at the bottom of the function strips the bit again after rendering. Building the LUT before the |= 64 meant entries 0/4/8/0xC carried the PALRAM value WITHOUT the priority bit, so every tile pixel that expanded to one of those four indices wrote a plain palette index to the framebuffer instead of (palette | 0x40). Sprite compositing then could not tell which BG pixels were the universal background, and broke priority for games that exercise Pal[0x04/0x08/0x0C] -- Mega Man 3 sprites were reported. Fix: move the FCEU_BuildBgPairLUT() call to just after the `Pal[i] |= 64` block. The LUT is now built with the priority bit present in entries 0/4/8/0xC and bit-exactly matches what the original eight-load scalar code reads from PALRAM during the pputile.h includes. No second rebuild is needed before the `Pal[i] &= 63` cleanup because the LUT is only consumed inside the includes, which sit between the OR and the AND. Mid-scanline RefreshLine entries via FCEUPPU_LineUpdate continue to work: each entry walks the same |= 64 -> BuildLUT -> includes -> &= 63 sequence, so any $3F00..$3F0F write the CPU made since the previous span is folded into the LUT before the resumed tile span renders. Codegen unchanged: still one leaq fceu_bg_pair_lut at function entry, 48 indexed LUT loads across the 12 pputile.h expansion sites, one movq per site to P. --- src/ppu.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/ppu.c b/src/ppu.c index 9bad747..65551ff 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -138,12 +138,16 @@ uint8_t UPALRAM[0x03];/* for 0x4/0x8/0xC addresses in palette, the ones in * emit four pair-lookups and a single 64-bit store instead of eight * serial nibble-load-store iterations. * - * Rebuilt at the start of every RefreshLine call so that mid-frame - * raster effects writing to $3F00-$3F0F see their pen changes on the - * very next scanline. 256 entries x ~3 ops = ~768 ops per rebuild; - * RefreshLine runs once per scanline plus the occasional mid-scanline - * call from FCEUPPU_LineUpdate, so the worst-case rebuild cost is - * well under 1% of frame budget. */ + * Rebuilt INSIDE RefreshLine just before the pputile.h includes, so + * that: + * (a) any PALRAM[0x00..0x0F] change since the previous RefreshLine + * is reflected on the next tile span, and + * (b) crucially, the 0x40 "background priority" bit that RefreshLine + * OR's into Pal[0/4/8/0xC] right before rendering -- and ANDs + * off again after -- is captured in the LUT. Without that the + * sprite compositor cannot tell BG-color pixels apart from the + * universal background, breaking sprite priority for any game + * that hits Pal[0x04/0x08/0x0C] (e.g. Mega Man 3 sprites). */ static uint16_t fceu_bg_pair_lut[256]; static void FCEU_BuildBgPairLUT(void) @@ -468,13 +472,6 @@ static void FASTAPASS(1) RefreshLine(int lastpixel) { if (numtiles <= 0) return; - /* Rebuild the bg pair LUT once per RefreshLine call. This covers - * both per-scanline calls from FCEUPPU_Loop and mid-scanline calls - * from FCEUPPU_LineUpdate (raster effects); any PALRAM[0x00..0x0F] - * change done since the previous RefreshLine is reflected for the - * about-to-be-rendered tile span. */ - FCEU_BuildBgPairLUT(); - P = Pline; vofs = 0; @@ -512,6 +509,17 @@ static void FASTAPASS(1) RefreshLine(int lastpixel) { Pal[8] |= 64; Pal[0xC] |= 64; + /* Rebuild the bg pair LUT *after* the priority |= 64 modification + * above so that the LUT entries for PALRAM[0/4/8/0xC] carry the + * 0x40 bit while the pputile.h includes below run. The matching + * Pal[i] &= 63 cleanup at the bottom of this function does NOT + * require a second rebuild: the LUT is only consumed by the + * pputile.h block, which runs between the |= 64 and the &= 63. + * The LUT is also rebuilt at every mid-scanline RefreshLine call + * via FCEUPPU_LineUpdate, picking up any $3F00..$3F0F write the + * CPU made since the previous tile span. */ + FCEU_BuildBgPairLUT(); + /* This high-level graphics MMC5 emulation code was written for MMC5 carts in "CL" mode. * It's probably not totally correct for carts in "SL" mode. */