auto PPU::Background::clip(int n) -> int {
  //13-bit sign extend: --s---nnnnnnnnnn -> ssssssnnnnnnnnnn
  return n & 0x2000 ? (n | ~1023) : (n & 1023);
}

//H = 28
auto PPU::Background::beginMode7() -> void {
  latch.hoffset = ppu.io.hoffsetMode7;
  latch.voffset = ppu.io.voffsetMode7;
}

auto PPU::Background::runMode7() -> void {
  int a = (int16)ppu.io.m7a;
  int b = (int16)ppu.io.m7b;
  int c = (int16)ppu.io.m7c;
  int d = (int16)ppu.io.m7d;

  int cx = (int13)ppu.io.m7x;
  int cy = (int13)ppu.io.m7y;
  int hoffset = (int13)latch.hoffset;
  int voffset = (int13)latch.voffset;

  if(Background::x++ & ~255) return;
  uint x = mosaic.hoffset;
  uint y = ppu.bg1.mosaic.voffset;  //BG2 vertical mosaic uses BG1 mosaic size

  if(--mosaic.hcounter == 0) {
    mosaic.hcounter = io.mosaic + 1;
    mosaic.hoffset += io.mosaic + 1;
  }

  if(ppu.io.hflipMode7) x = 255 - x;
  if(ppu.io.vflipMode7) y = 255 - y;

  int psx = ((a * clip(hoffset - cx)) & ~63) + ((b * clip(voffset - cy)) & ~63) + ((b * y) & ~63) + (cx << 8);
  int psy = ((c * clip(hoffset - cx)) & ~63) + ((d * clip(voffset - cy)) & ~63) + ((d * y) & ~63) + (cy << 8);

  int px = psx + (a * x);
  int py = psy + (c * x);

  //mask pseudo-FP bits
  px >>= 8;
  py >>= 8;

  uint tile;
  uint palette;
  switch(ppu.io.repeatMode7) {
  //screen repetition outside of screen area
  case 0:
  case 1:
    px &= 1023;
    py &= 1023;
    tile = ppu.vram[(py >> 3) * 128 + (px >> 3)].byte(0);
    palette = ppu.vram[(tile << 6) + ((py & 7) << 3) + (px & 7)].byte(1);
    break;

  //palette color 0 outside of screen area
  case 2:
    if((px | py) & ~1023) {
      palette = 0;
    } else {
      px &= 1023;
      py &= 1023;
      tile = ppu.vram[(py >> 3) * 128 + (px >> 3)].byte(0);
      palette = ppu.vram[(tile << 6) + ((py & 7) << 3) + (px & 7)].byte(1);
    }
    break;

  //character 0 repetition outside of screen area
  case 3:
    if((px | py) & ~1023) {
      tile = 0;
    } else {
      px &= 1023;
      py &= 1023;
      tile = ppu.vram[(py >> 3) * 128 + (px >> 3)].byte(0);
    }
    palette = ppu.vram[(tile << 6) + ((py & 7) << 3) + (px & 7)].byte(1);
    break;
  }

  uint priority;
  if(id == ID::BG1) {
    priority = io.priority[0];
  } else if(id == ID::BG2) {
    priority = io.priority[bool(palette & 0x80)];
    palette &= 0x7f;
  }

  if(palette == 0) return;

  if(io.aboveEnable) {
    output.above.palette = palette;
    output.above.priority = priority;
    output.above.tile = 0;
  }

  if(io.belowEnable) {
    output.below.palette = palette;
    output.below.priority = priority;
    output.below.tile = 0;
  }
}
