diff --git a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs index 9d932fc376..49ec5df467 100644 --- a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs +++ b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.Render.cs @@ -25,8 +25,14 @@ namespace BizHawk.Emulation.Consoles.Sega // TODO: I would like to be able to render Scroll A before Scroll B, in order to minimize overdraw. // But at the moment it complicates priority stuff. - RenderScrollB(); - RenderScrollA(); + if (CellBasedVertScroll == false) + { + RenderScrollB(); + RenderScrollA(); + } else { + RenderScrollBTwoCellVScroll(); + RenderScrollATwoCellVScroll(); + } RenderSpritesScanline(); } @@ -88,6 +94,47 @@ namespace BizHawk.Emulation.Consoles.Sega } } + void RenderScrollAScanlineTwoCellVScroll(int xScroll, int nameTableBase, int startPixel, int endPixel, bool window) + { + const int lowPriority = 2; + const int highPriority = 5; + + int fineHScroll = xScroll & 15; + int nameTableWidth = NameTableWidth; + if (window) + nameTableWidth = (DisplayWidth == 40) ? 64 : 32; + + for (int x = startPixel; x < endPixel; x++) + { + int vsramUnitOffset = ((x - fineHScroll) / 16) % 40; + int yScroll = VSRAM[vsramUnitOffset * 2] & 0x3FF; + int yTile = ((ScanLine + yScroll) / 8) % NameTableHeight; + + int xTile = Math.Abs(((x + (1024 - xScroll)) / 8) % nameTableWidth); + int xOfs = Math.Abs((x + (1024 - xScroll)) & 7); + int yOfs = (ScanLine + yScroll) % 8; + int cellOfs = nameTableBase + (yTile * nameTableWidth * 2) + (xTile * 2); + int nameTableEntry = VRAM[cellOfs] | (VRAM[cellOfs + 1] << 8); + int patternNo = nameTableEntry & 0x7FF; + bool hFlip = ((nameTableEntry >> 11) & 1) != 0; + bool vFlip = ((nameTableEntry >> 12) & 1) != 0; + bool priority = ((nameTableEntry >> 15) & 1) != 0; + int palette = (nameTableEntry >> 13) & 3; + + if (priority && PriorityBuffer[x] >= highPriority) continue; + if (!priority && PriorityBuffer[x] >= lowPriority) continue; + + if (vFlip) yOfs = 7 - yOfs; + if (hFlip) xOfs = 7 - xOfs; + + int texel = PatternBuffer[(patternNo * 64) + (yOfs * 8) + (xOfs)]; + if (texel == 0) continue; + int pixel = Palette[(palette * 16) + texel]; + FrameBuffer[(ScanLine * FrameWidth) + x] = pixel; + PriorityBuffer[x] = (byte)(priority ? highPriority : lowPriority); + } + } + void CalculateWindowScanlines(out int startScanline, out int endScanline) { int data = Registers[0x12]; @@ -182,6 +229,44 @@ namespace BizHawk.Emulation.Consoles.Sega } } + void RenderScrollATwoCellVScroll() + { + // Calculate scroll offsets + + int hscroll = CalcHScrollPlaneA(ScanLine); + + // Calculate window dimensions + + int startWindowScanline, endWindowScanline; + int startWindowPixel, endWindowPixel; + CalculateWindowScanlines(out startWindowScanline, out endWindowScanline); + CalculateWindowPosition(out startWindowPixel, out endWindowPixel); + + // Render scanline + + if (ScanLine >= startWindowScanline && ScanLine < endWindowScanline) // Window takes up whole scanline + { + RenderScrollAScanline(0, 0, NameTableAddrWindow, 0, FrameWidth, true); + } + else if (startWindowPixel != -1) // Window takes up partial scanline + { + if (startWindowPixel == 0) // Window grows from left side + { + RenderScrollAScanline(0, 0, NameTableAddrWindow, 0, endWindowPixel, true); + RenderScrollAScanlineTwoCellVScroll(hscroll, NameTableAddrA, endWindowPixel, FrameWidth, false); + } + else // Window grows from right side + { + RenderScrollAScanlineTwoCellVScroll(hscroll, NameTableAddrA, 0, startWindowPixel, false); + RenderScrollAScanline(0, 0, NameTableAddrWindow, startWindowPixel, FrameWidth, true); + } + } + else // No window this scanline + { + RenderScrollAScanlineTwoCellVScroll(hscroll, NameTableAddrA, 0, FrameWidth, false); + } + } + void RenderScrollB() { int bgColor = BackgroundColor; @@ -222,9 +307,51 @@ namespace BizHawk.Emulation.Consoles.Sega { FrameBuffer[(ScanLine * FrameWidth) + x] = pixel; PriorityBuffer[x] = (byte)(priority ? highPriority : lowPriority); + } else { + FrameBuffer[(ScanLine * FrameWidth) + x] = bgColor; } - else + } + } + + void RenderScrollBTwoCellVScroll() + { + int bgColor = BackgroundColor; + int xScroll = CalcHScrollPlaneB(ScanLine); + int fineHScroll = xScroll & 15; + + const int lowPriority = 1; + const int highPriority = 4; + + for (int x = 0; x < FrameWidth; x++) + { + int vsramUnitOffset = ((x - fineHScroll) / 16) % 40; + int yScroll = VSRAM[(vsramUnitOffset*2)+1] & 0x3FF; + int yTile = ((ScanLine + yScroll) / 8) % NameTableHeight; + + int xTile = Math.Abs(((x + (1024 - xScroll)) / 8) % NameTableWidth); + int xOfs = Math.Abs((x + (1024 - xScroll)) & 7); + int yOfs = (ScanLine + yScroll) % 8; + int cellOfs = NameTableAddrB + (yTile * NameTableWidth * 2) + (xTile * 2); + int nameTableEntry = VRAM[cellOfs] | (VRAM[cellOfs + 1] << 8); + int patternNo = nameTableEntry & 0x7FF; + bool hFlip = ((nameTableEntry >> 11) & 1) != 0; + bool vFlip = ((nameTableEntry >> 12) & 1) != 0; + bool priority = ((nameTableEntry >> 15) & 1) != 0; + int palette = (nameTableEntry >> 13) & 3; + + if (priority && PriorityBuffer[x] >= highPriority) continue; + if (!priority && PriorityBuffer[x] >= lowPriority) continue; + + if (vFlip) yOfs = 7 - yOfs; + if (hFlip) xOfs = 7 - xOfs; + + int texel = PatternBuffer[(patternNo * 64) + (yOfs * 8) + (xOfs)]; + int pixel = Palette[(palette * 16) + texel]; + if (texel != 0) { + FrameBuffer[(ScanLine * FrameWidth) + x] = pixel; + PriorityBuffer[x] = (byte)(priority ? highPriority : lowPriority); + } else { FrameBuffer[(ScanLine * FrameWidth) + x] = bgColor; } } diff --git a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs index 5c8c5f3659..a2774eb85b 100644 --- a/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs +++ b/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs @@ -25,7 +25,7 @@ namespace BizHawk.Emulation.Consoles.Sega public bool DisplayEnabled { get { return (Registers[1] & 0x40) != 0; } } public bool VInterruptEnabled { get { return (Registers[1] & 0x20) != 0; } } public bool DmaEnabled { get { return (Registers[1] & 0x10) != 0; } } - public bool CellBasedVertScroll { get { return (Registers[11] & 0x08) != 0; } } + public bool CellBasedVertScroll { get { return (Registers[11] & 0x04) != 0; } } public bool InDisplayPeriod { get { return ScanLine < 224 && DisplayEnabled; } }