shithub: zelda3

Download patch

ref: 2f012a3c3d26fa0a62b1608d6dd9ac6acf8d18cb
parent: 442cc396eff66eabcafb12afb43f6061ef70f106
author: Snesrev <[email protected]>
date: Mon Sep 5 21:03:44 EDT 2022

Remove previous ppu optimizations that are now obsolete

--- a/snes/ppu.c
+++ b/snes/ppu.c
@@ -17,10 +17,11 @@
 
 static void ppu_handlePixel(Ppu* ppu, int x, int y);
 static int ppu_getPixel(Ppu* ppu, int x, int y, bool sub, int* r, int* g, int* b);
+static int ppu_getPixelForBgLayer(Ppu *ppu, int x, int y, int layer, bool priority);
 static void ppu_calculateMode7Starts(Ppu* ppu, int y);
 static int ppu_getPixelForMode7(Ppu* ppu, int x, int layer, bool priority);
-static inline bool ppu_getWindowState(Ppu* ppu, int layer, int x);
 static void ppu_evaluateSprites(Ppu* ppu, int line);
+static bool ppu_getWindowState(Ppu* ppu, int layer, int x);
 static uint16_t ppu_getVramRemap(Ppu* ppu);
 
 Ppu* ppu_init(Snes* snes) {
@@ -175,236 +176,245 @@
   }
 }
 
-static FORCEINLINE bool ppu_checkmodecondition(uint8_t mode, bool state) {
-  return mode & (state + 1);
-  return mode == 3 || mode == 2 && state || mode == 1 && !state;
-  return (mode >> 1) & state | mode & (state ^ 1);
-}
-
-static FORCEINLINE int ppu_clamp0_31(int v) {
-  if (v < 0) v = 0;
-  if (v > 31) v = 31;
-  return v;
-}
-
 static void ppu_handlePixel(Ppu* ppu, int x, int y) {
-  int r = 0, g = 0, b = 0;
-  
-  if(!ppu->forcedBlank) {
+  int r = 0, r2 = 0;
+  int g = 0, g2 = 0;
+  int b = 0, b2 = 0;
+  if (!ppu->forcedBlank) {
     int mainLayer = ppu_getPixel(ppu, x, y, false, &r, &g, &b);
 
     bool colorWindowState = ppu_getWindowState(ppu, 5, x);
-    if(ppu_checkmodecondition(ppu->clipMode, colorWindowState)) {
+    if (
+      ppu->clipMode == 3 ||
+      (ppu->clipMode == 2 && colorWindowState) ||
+      (ppu->clipMode == 1 && !colorWindowState)
+      ) {
       r = g = b = 0;
     }
     int secondLayer = 5; // backdrop
-    bool mathEnabled = mainLayer < 6 && ppu->mathEnabled[mainLayer] && !ppu_checkmodecondition(ppu->preventMathMode, colorWindowState);
+    bool mathEnabled = mainLayer < 6 && ppu->mathEnabled[mainLayer] && !(
+      ppu->preventMathMode == 3 ||
+      (ppu->preventMathMode == 2 && colorWindowState) ||
+      (ppu->preventMathMode == 1 && !colorWindowState)
+      );
+    if ((mathEnabled && ppu->addSubscreen) || ppu->pseudoHires_always_zero || ppu->mode == 5 || ppu->mode == 6) {
+      secondLayer = ppu_getPixel(ppu, x, y, true, &r2, &g2, &b2);
+    }
     // TODO: subscreen pixels can be clipped to black as well
     // TODO: math for subscreen pixels (add/sub sub to main)
-    if(mathEnabled) {
-      int r2 = 0, g2 = 0, b2 = 0;
-      if (ppu->addSubscreen && (secondLayer = ppu_getPixel(ppu, x, y, true, &r2, &g2, &b2)) != 5) {
-        if (ppu->subtractColor) {
-          r -= r2;
-          g -= g2;
-          b -= b2;
-        } else {
-          r += r2;
-          g += g2;
-          b += b2;
-        }
+    if (mathEnabled) {
+      if (ppu->subtractColor) {
+        r -= (ppu->addSubscreen && secondLayer != 5) ? r2 : ppu->fixedColorR;
+        g -= (ppu->addSubscreen && secondLayer != 5) ? g2 : ppu->fixedColorG;
+        b -= (ppu->addSubscreen && secondLayer != 5) ? b2 : ppu->fixedColorB;
       } else {
-        if (ppu->subtractColor) {
-          r -= ppu->fixedColorR;
-          g -= ppu->fixedColorG;
-          b -= ppu->fixedColorB;
-        } else {
-          r += ppu->fixedColorR;
-          g += ppu->fixedColorG;
-          b += ppu->fixedColorB;
-        }
+        r += (ppu->addSubscreen && secondLayer != 5) ? r2 : ppu->fixedColorR;
+        g += (ppu->addSubscreen && secondLayer != 5) ? g2 : ppu->fixedColorG;
+        b += (ppu->addSubscreen && secondLayer != 5) ? b2 : ppu->fixedColorB;
       }
-      if(ppu->halfColor && (secondLayer != 5 || !ppu->addSubscreen)) {
+      if (ppu->halfColor && (secondLayer != 5 || !ppu->addSubscreen)) {
         r >>= 1;
         g >>= 1;
         b >>= 1;
       }
-      r = ppu_clamp0_31(r);
-      g = ppu_clamp0_31(g);
-      b = ppu_clamp0_31(b);
+      if (r > 31) r = 31;
+      if (g > 31) g = 31;
+      if (b > 31) b = 31;
+      if (r < 0) r = 0;
+      if (g < 0) g = 0;
+      if (b < 0) b = 0;
     }
+    if (!(ppu->pseudoHires_always_zero || ppu->mode == 5 || ppu->mode == 6)) {
+      r2 = r; g2 = g; b2 = b;
+    }
   }
   int row = (y - 1) + (ppu->evenFrame ? 0 : 239);
-  uint8_t *dst = &ppu->pixelBuffer[row * 2048 + x * 8];
-  uint8_t ppu_brightness = ppu->brightness;
-  dst[1] = dst[5] = ((b << 3) | (b >> 2)) * ppu_brightness / 15;
-  dst[2] = dst[6] = ((g << 3) | (g >> 2)) * ppu_brightness / 15;
-  dst[3] = dst[7] = ((r << 3) | (r >> 2)) * ppu_brightness / 15;
+  ppu->pixelBuffer[row * 2048 + x * 8 + 1] = ((b2 << 3) | (b2 >> 2)) * ppu->brightness / 15;
+  ppu->pixelBuffer[row * 2048 + x * 8 + 2] = ((g2 << 3) | (g2 >> 2)) * ppu->brightness / 15;
+  ppu->pixelBuffer[row * 2048 + x * 8 + 3] = ((r2 << 3) | (r2 >> 2)) * ppu->brightness / 15;
+  ppu->pixelBuffer[row * 2048 + x * 8 + 5] = ((b << 3) | (b >> 2)) * ppu->brightness / 15;
+  ppu->pixelBuffer[row * 2048 + x * 8 + 6] = ((g << 3) | (g >> 2)) * ppu->brightness / 15;
+  ppu->pixelBuffer[row * 2048 + x * 8 + 7] = ((r << 3) | (r >> 2)) * ppu->brightness / 15;
 }
 
+static const int bitDepthsPerMode[10][4] = {
+  {2, 2, 2, 2},
+  {4, 4, 2, 5},
+  {4, 4, 5, 5},
+  {8, 4, 5, 5},
+  {8, 2, 5, 5},
+  {4, 2, 5, 5},
+  {4, 5, 5, 5},
+  {8, 5, 5, 5},
+  {4, 4, 2, 5},
+  {8, 7, 5, 5}
+};
 
-static bool FORCEINLINE ppu_islayeractive(Ppu *ppu, int curLayer, bool sub, int x) {
-  Layer *layerp = &ppu->layer[curLayer];
-  return layerp->screenEnabled[sub] && (!layerp->screenWindowed[sub] || !ppu_getWindowState(ppu, curLayer, x));
-}
+static int ppu_getPixel(Ppu *ppu, int x, int y, bool sub, int *r, int *g, int *b) {
+  // array for layer definitions per mode:
+//   0-7: mode 0-7; 8: mode 1 + l3prio; 9: mode 7 + extbg
 
-typedef struct GetPixelRV {
-  uint8_t pixel;
-  uint8_t layer;
-} GetPixelRV;
+//   0-3; layers 1-4; 4: sprites; 5: nonexistent
+  static const int layersPerMode[10][12] = {
+    {4, 0, 1, 4, 0, 1, 4, 2, 3, 4, 2, 3},
+    {4, 0, 1, 4, 0, 1, 4, 2, 4, 2, 5, 5},
+    {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5},
+    {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5},
+    {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5},
+    {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5},
+    {4, 0, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5},
+    {4, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5, 5},
+    {2, 4, 0, 1, 4, 0, 1, 4, 4, 2, 5, 5},
+    {4, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5, 5}
+  };
 
-typedef struct TileAndXY {
-  uint16_t tile;
-  uint8_t x, y;
-} TileAndXY;
+  static const int prioritysPerMode[10][12] = {
+    {3, 1, 1, 2, 0, 0, 1, 1, 1, 0, 0, 0},
+    {3, 1, 1, 2, 0, 0, 1, 1, 0, 0, 5, 5},
+    {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5},
+    {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5},
+    {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5},
+    {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5},
+    {3, 1, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5},
+    {3, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5, 5},
+    {1, 3, 1, 1, 2, 0, 0, 1, 0, 0, 5, 5},
+    {3, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5, 5}
+  };
 
-static GetPixelRV ppu_getPixel_Mode7(Ppu *ppu, int x, int y, bool sub);
-static GetPixelRV ppu_getPixel_Mode1(Ppu *ppu, int x, int y, bool sub);
+  static const int layerCountPerMode[10] = {
+    12, 10, 8, 8, 8, 8, 6, 5, 10, 7
+  };
 
-static int FORCEINLINE ppu_getPixel(Ppu *ppu, int x, int y, bool sub, int *r, int *g, int *b) {
-  GetPixelRV rv;
-  if (ppu->mode == 1) {
-    rv = ppu_getPixel_Mode1(ppu, x, y, sub);
-  } else {
-    rv = ppu_getPixel_Mode7(ppu, x, y, sub);
-  }
-  uint16_t color = ppu->cgram[rv.pixel];
-  *r = color & 0x1f;
-  *g = (color >> 5) & 0x1f;
-  *b = (color >> 10) & 0x1f;
-  return rv.layer;
-}
-
-static GetPixelRV ppu_getPixel_Mode7(Ppu *ppu, int x, int y, bool sub) {
+  
   // figure out which color is on this location on main- or subscreen, sets it in r, g, b
   // returns which layer it is: 0-3 for bg layer, 4 or 6 for sprites (depending on palette), 5 for backdrop
-  int actMode = ppu->mode == 1 ? 8 : ppu->mode;
-  static const uint8_t kLayersMode7[] = { 4, 4, 4, 0, 4 };
-  static const uint8_t kPrioritysMode7[] = { 3, 2, 1, 0, 0, };
-  for (int i = 0; i < 5; i++) {
-    int curLayer = kLayersMode7[i];
-    int curPriority = kPrioritysMode7[i];
-    int pixel;
-    if (ppu_islayeractive(ppu, curLayer, sub, x)) {
+  int actMode = ppu->mode == 1 && ppu->bg3priority ? 8 : ppu->mode;
+  actMode = ppu->mode == 7 && ppu->m7extBg_always_zero ? 9 : actMode;
+  int layer = 5;
+  int pixel = 0;
+  for (int i = 0; i < layerCountPerMode[actMode]; i++) {
+    int curLayer = layersPerMode[actMode][i];
+    int curPriority = prioritysPerMode[actMode][i];
+    bool layerActive = false;
+    if (!sub) {
+      layerActive = ppu->layer[curLayer].screenEnabled[0] && (
+        !ppu->layer[curLayer].screenWindowed[0] || !ppu_getWindowState(ppu, curLayer, x)
+        );
+    } else {
+      layerActive = ppu->layer[curLayer].screenEnabled[1] && (
+        !ppu->layer[curLayer].screenWindowed[1] || !ppu_getWindowState(ppu, curLayer, x)
+        );
+    }
+    if (layerActive) {
       if (curLayer < 4) {
-        pixel = ppu_getPixelForMode7(ppu, x, curLayer, curPriority);
+        // bg layer
+        int lx = x;
+        int ly = y;
+        if (ppu->bgLayer[curLayer].mosaicEnabled && ppu->mosaicSize > 1) {
+          lx -= lx % ppu->mosaicSize;
+          ly -= (ly - ppu->mosaicStartLine) % ppu->mosaicSize;
+        }
+        if (ppu->mode == 7) {
+          pixel = ppu_getPixelForMode7(ppu, lx, curLayer, curPriority);
+        } else {
+          lx += ppu->bgLayer[curLayer].hScroll;
+          if (ppu->mode == 5 || ppu->mode == 6) {
+            lx *= 2;
+            lx += (sub || ppu->bgLayer[curLayer].mosaicEnabled) ? 0 : 1;
+            if (ppu->interlace_always_zero) {
+              ly *= 2;
+              ly += (ppu->evenFrame || ppu->bgLayer[curLayer].mosaicEnabled) ? 0 : 1;
+            }
+          }
+          ly += ppu->bgLayer[curLayer].vScroll;
+          pixel = ppu_getPixelForBgLayer(
+            ppu, lx & 0x3ff, ly & 0x3ff,
+            curLayer, curPriority
+          );
+        }
       } else {
-        pixel = (ppu->objPriorityBuffer[x] == curPriority) ? ppu->objPixelBuffer[x] : 0;
-        if (pixel < 0xc0)
-          curLayer = 6;
+        // get a pixel from the sprite buffer
+        pixel = 0;
+        if (ppu->objPriorityBuffer[x] == curPriority) pixel = ppu->objPixelBuffer[x];
       }
-      if (pixel != 0)
-        return (GetPixelRV) { pixel, curLayer };
     }
+    if (pixel > 0) {
+      layer = curLayer;
+      break;
+    }
   }
-  return (GetPixelRV) { 0, 5 };
-}
+  if (ppu->directColor_always_zero && layer < 4 && bitDepthsPerMode[actMode][layer] == 8) {
+    *r = ((pixel & 0x7) << 2) | ((pixel & 0x100) >> 7);
+    *g = ((pixel & 0x38) >> 1) | ((pixel & 0x200) >> 8);
+    *b = ((pixel & 0xc0) >> 3) | ((pixel & 0x400) >> 8);
+  } else {
+    uint16_t color = ppu->cgram[pixel & 0xff];
+    *r = color & 0x1f;
+    *g = (color >> 5) & 0x1f;
+    *b = (color >> 10) & 0x1f;
+  }
+  if (layer == 4 && pixel < 0xc0) layer = 6; // sprites with palette color < 0xc0
+  return layer;
 
-static FORCEINLINE TileAndXY ppu_GetBgTileAndXy(Ppu *ppu, int x, int y, int layer);
-static FORCEINLINE int ppu_GetPixelFromTileAndXY_2bpp(Ppu *ppu, TileAndXY txy, int tileAdr);
-static FORCEINLINE int ppu_GetPixelFromTileAndXY_4bpp(Ppu *ppu, TileAndXY txy, int tileAdr);
-
-static FORCEINLINE GetPixelRV ppu_getSpritePixelRV(uint8_t pixel) {
-  // sprites with palette color < 0xc0 are layer 6 instead of 4.
-  return (GetPixelRV) { pixel, pixel < 0xc0 ? 6 : 4 };
 }
 
-static GetPixelRV ppu_getPixel_Mode1(Ppu *ppu, int x, int y, bool sub) {
-  // figure out which color is on this location on main- or subscreen
-  // returns which layer it is: 0-3 for bg layer, 4 or 6 for sprites (depending on palette), 5 for backdrop
-  TileAndXY BG1, BG2, BG3;
-  uint8_t pixel;
 
-  // BG3 tiles with priority 1, 2bpp
-  bool layer3active = ppu_islayeractive(ppu, 2, sub, x);
-  if (layer3active) {
-    BG3 = ppu_GetBgTileAndXy(ppu, x, y, 2);
-    if (BG3.tile & 0x2000) {
-      if ((pixel = ppu_GetPixelFromTileAndXY_2bpp(ppu, BG3, ppu->bgLayer[2].tileAdr)) != 0)
-        return (GetPixelRV) { pixel, 2 };
-      layer3active = false;
-    }
+static int ppu_getPixelForBgLayer(Ppu *ppu, int x, int y, int layer, bool priority) {
+  BgLayer *layerp = &ppu->bgLayer[layer];
+  // figure out address of tilemap word and read it
+  bool wideTiles = layerp->bigTiles_always_zero || ppu->mode == 5 || ppu->mode == 6;
+  int tileBitsX = wideTiles ? 4 : 3;
+  int tileHighBitX = wideTiles ? 0x200 : 0x100;
+  int tileBitsY = layerp->bigTiles_always_zero ? 4 : 3;
+  int tileHighBitY = layerp->bigTiles_always_zero ? 0x200 : 0x100;
+  uint16_t tilemapAdr = layerp->tilemapAdr + (((y >> tileBitsY) & 0x1f) << 5 | ((x >> tileBitsX) & 0x1f));
+  if ((x & tileHighBitX) && layerp->tilemapWider) tilemapAdr += 0x400;
+  if ((y & tileHighBitY) && layerp->tilemapHigher) tilemapAdr += layerp->tilemapWider ? 0x800 : 0x400;
+  uint16_t tile = ppu->vram[tilemapAdr & 0x7fff];
+  // check priority, get palette
+  if (((bool)(tile & 0x2000)) != priority) return 0; // wrong priority
+  int paletteNum = (tile & 0x1c00) >> 10;
+  // figure out position within tile
+  int row = (tile & 0x8000) ? 7 - (y & 0x7) : (y & 0x7);
+  int col = (tile & 0x4000) ? (x & 0x7) : 7 - (x & 0x7);
+  int tileNum = tile & 0x3ff;
+  if (wideTiles) {
+    // if unflipped right half of tile, or flipped left half of tile
+    if (((bool)(x & 8)) ^ ((bool)(tile & 0x4000))) tileNum += 1;
   }
-  // Sprites with priority 3
-  uint8_t obj_prio = ppu->objPriorityBuffer[x];
-  if (obj_prio == 3 && ppu_islayeractive(ppu, 4, sub, x))
-    return ppu_getSpritePixelRV(ppu->objPixelBuffer[x]);
-  
-  // BG1 tiles with priority 1
-  bool layer1active = ppu_islayeractive(ppu, 0, sub, x);
-  if (layer1active) {
-    BG1 = ppu_GetBgTileAndXy(ppu, x, y, 0);
-    if (BG1.tile & 0x2000) {
-      if ((pixel = ppu_GetPixelFromTileAndXY_4bpp(ppu, BG1, ppu->bgLayer[0].tileAdr)) != 0)
-        return (GetPixelRV) { pixel, 0 };
-      layer1active = false;
-    }
+  if (layerp->bigTiles_always_zero) {
+    // if unflipped bottom half of tile, or flipped upper half of tile
+    if (((bool)(y & 8)) ^ ((bool)(tile & 0x8000))) tileNum += 0x10;
   }
-  // BG2 tiles with priority 1
-  bool layer2active = ppu_islayeractive(ppu, 1, sub, x);
-  if (layer2active) {
-    BG2 = ppu_GetBgTileAndXy(ppu, x, y, 1);
-    if (BG2.tile & 0x2000) {
-      if ((pixel = ppu_GetPixelFromTileAndXY_4bpp(ppu, BG2, ppu->bgLayer[1].tileAdr)) != 0)
-        return (GetPixelRV) { pixel, 1 };
-      layer2active = false;
-    }
+  // read tiledata, ajust palette for mode 0
+  int bitDepth = bitDepthsPerMode[ppu->mode][layer];
+  if (ppu->mode == 0) paletteNum += 8 * layer;
+  // plane 1 (always)
+  int paletteSize = 4;
+  uint16_t plane1 = ppu->vram[(layerp->tileAdr + ((tileNum & 0x3ff) * 4 * bitDepth) + row) & 0x7fff];
+  int pixel = (plane1 >> col) & 1;
+  pixel |= ((plane1 >> (8 + col)) & 1) << 1;
+  // plane 2 (for 4bpp, 8bpp)
+  if (bitDepth > 2) {
+    paletteSize = 16;
+    uint16_t plane2 = ppu->vram[(layerp->tileAdr + ((tileNum & 0x3ff) * 4 * bitDepth) + 8 + row) & 0x7fff];
+    pixel |= ((plane2 >> col) & 1) << 2;
+    pixel |= ((plane2 >> (8 + col)) & 1) << 3;
   }
-  // Sprites with priority 2
-  if (obj_prio == 2 && ppu_islayeractive(ppu, 4, sub, x))
-    return ppu_getSpritePixelRV(ppu->objPixelBuffer[x]);
-  // BG1 tiles with priority 0
-  if (layer1active && (pixel = ppu_GetPixelFromTileAndXY_4bpp(ppu, BG1, ppu->bgLayer[0].tileAdr)))
-    return (GetPixelRV) { pixel, 0 };
-  // BG2 tiles with priority 0
-  if (layer2active && (pixel = ppu_GetPixelFromTileAndXY_4bpp(ppu, BG2, ppu->bgLayer[1].tileAdr)))
-    return (GetPixelRV) { pixel, 1 };
-  // Sprites with priority 1
-  // Sprites with priority 0
-  if (obj_prio <= 1 && ppu_islayeractive(ppu, 4, sub, x))
-    return ppu_getSpritePixelRV(ppu->objPixelBuffer[x]);
-  // BG3 tiles with priority 0
-  if (layer3active && (pixel = ppu_GetPixelFromTileAndXY_2bpp(ppu, BG3, ppu->bgLayer[2].tileAdr)))
-    return (GetPixelRV) { pixel, 2 };
-  // backdrop
-  return (GetPixelRV) { 0, 5 };
-}
-
-static FORCEINLINE TileAndXY ppu_GetBgTileAndXy(Ppu *ppu, int x, int y, int layer) {
-  BgLayer *layerp = &ppu->bgLayer[layer];
-  if (layerp->mosaicEnabled && ppu->mosaicSize > 1) {
-    x -= x % ppu->mosaicSize;
-    y -= (y - ppu->mosaicStartLine) % ppu->mosaicSize;
+  // plane 3 & 4 (for 8bpp)
+  if (bitDepth > 4) {
+    paletteSize = 256;
+    uint16_t plane3 = ppu->vram[(layerp->tileAdr + ((tileNum & 0x3ff) * 4 * bitDepth) + 16 + row) & 0x7fff];
+    pixel |= ((plane3 >> col) & 1) << 4;
+    pixel |= ((plane3 >> (8 + col)) & 1) << 5;
+    uint16_t plane4 = ppu->vram[(layerp->tileAdr + ((tileNum & 0x3ff) * 4 * bitDepth) + 24 + row) & 0x7fff];
+    pixel |= ((plane4 >> col) & 1) << 6;
+    pixel |= ((plane4 >> (8 + col)) & 1) << 7;
   }
-  x += layerp->hScroll;
-  y += layerp->vScroll;
-  // figure out address of tilemap word and read it
-  int tilemapAdr = layerp->tilemapAdr + (((y >> 3) & 0x1f) << 5 | ((x >> 3) & 0x1f));
-  if (layerp->tilemapWider)
-    tilemapAdr += (x & 0x100) << 2;
-  if ((y & 0x100) && layerp->tilemapHigher) tilemapAdr += layerp->tilemapWider ? 0x800 : 0x400;
-  return (TileAndXY) { ppu->vram[tilemapAdr & 0x7fff], x, y };
+  // return cgram index, or 0 if transparent, palette number in bits 10-8 for 8-color layers
+  return pixel == 0 ? 0 : paletteSize * paletteNum + pixel;
 }
 
-static FORCEINLINE int ppu_GetPixelFromTileAndXY_2bpp(Ppu *ppu, TileAndXY txy, int tileAdr) {
-  int paletteNum = (txy.tile & 0x1c00) >> 10;
-  int row = (txy.tile & 0x8000) ? 7 - (txy.y & 0x7) : (txy.y & 0x7);
-  int col = (txy.tile & 0x4000) ? (txy.x & 0x7) : 7 - (txy.x & 0x7);
-  int plane1 = ppu->vram[(tileAdr + ((txy.tile & 0x3ff) * 4 * 2) + row) & 0x7fff] >> col;
-  int pixel = plane1 & 1 | (plane1 >> 7) & 2;
-  return pixel == 0 ? 0 : 4 * paletteNum + pixel;
-}
-
-static FORCEINLINE int ppu_GetPixelFromTileAndXY_4bpp(Ppu *ppu, TileAndXY txy, int tileAdr) {
-  int paletteNum = (txy.tile & 0x1c00) >> 10;
-  int row = (txy.tile & 0x8000) ? 7 - (txy.y & 0x7) : (txy.y & 0x7);
-  int col = (txy.tile & 0x4000) ? (txy.x & 0x7) : 7 - (txy.x & 0x7);
-  uint16_t *addr = &ppu->vram[(tileAdr + ((txy.tile & 0x3ff) * 4 * 4) + row) & 0x7fff];
-  uint16_t plane1 = addr[0] >> col, plane2 = addr[8] >> col;
-  int pixel = plane1 & 1 | (plane1 >> 7) & 2 | (plane2 << 2) & 4 | (plane2 >> 5) & 8;
-  return pixel == 0 ? 0 : 16 * paletteNum + pixel;
-}
-
 static void ppu_calculateMode7Starts(Ppu* ppu, int y) {
   // expand 13-bit values to signed values
   int hScroll = ((int16_t) (ppu->m7matrix[6] << 3)) >> 3;
@@ -453,13 +463,29 @@
   return pixel;
 }
 
-static inline bool ppu_getWindowState(Ppu* ppu, int layer, int x) {
-  WindowLayer *wl = &ppu->windowLayer[layer];
-  if(!(wl->window1enabled | wl->window2enabled))
+static bool ppu_getWindowState(Ppu* ppu, int layer, int x) {
+  if (!ppu->windowLayer[layer].window1enabled && !ppu->windowLayer[layer].window2enabled) {
     return false;
+  }
+  if (ppu->windowLayer[layer].window1enabled && !ppu->windowLayer[layer].window2enabled) {
+    bool test = x >= ppu->window1left && x <= ppu->window1right;
+    return ppu->windowLayer[layer].window1inversed ? !test : test;
+  }
+  if (!ppu->windowLayer[layer].window1enabled && ppu->windowLayer[layer].window2enabled) {
+    bool test = x >= ppu->window2left && x <= ppu->window2right;
+    return ppu->windowLayer[layer].window2inversed ? !test : test;
+  }
   bool test1 = x >= ppu->window1left && x <= ppu->window1right;
   bool test2 = x >= ppu->window2left && x <= ppu->window2right;
-  return ((test1 ^ wl->window1inversed) & wl->window1enabled) | ((test2 ^ wl->window2inversed) & wl->window2enabled);
+  if (ppu->windowLayer[layer].window1inversed) test1 = !test1;
+  if (ppu->windowLayer[layer].window2inversed) test2 = !test2;
+  switch (ppu->windowLayer[layer].maskLogic_always_zero) {
+  case 0: return test1 || test2;
+  case 1: return test1 && test2;
+  case 2: return test1 != test2;
+  case 3: return test1 == test2;
+  }
+  return false;
 }
 
 static void ppu_evaluateSprites(Ppu* ppu, int line) {