// File: mindbeast[P]rnes/ppu.cpp
// Function: Rnes::Ppu::render(unsigned int)
void Ppu::render(uint32_t scanline) {
  bool spriteSize8x8 = isSpriteSize8x8();
  uint32_t spriteSize = spriteSize8x8 ? 8 : 16;

  // Indicate that all bg elements are transparent
  memset(pixelWritten, 0, sizeof(pixelWritten));
  memset(scanlineBuffer, 0, sizeof(scanlineBuffer));

  // render bg
  if (renderBackgroundEnabled()) {
    uint32_t patternTableAddr = getBgPatternTableAddr();
    uint32_t tempFineXScroll = vramFineXScroll;

    // render background
    for (uint32_t xBgOffset = 0; xBgOffset < renderWidth;) {
      // Get next tile and attribute addresses
      uint16_t nameAddr = getTileAddr(vramCurrentAddr);
      uint16_t attrAddr = getAttrAddr(vramCurrentAddr);

      // Load attribute and pattern
      uint16_t patternAddr = load(nameAddr) * 16 + patternTableAddr + getFineY(vramCurrentAddr);
      assert(patternAddr >= patternTableAddr and
             patternAddr < (patternTableAddr + patternTableSize));
      uint16_t pattern = loadPatternTile(patternAddr);
      uint8_t attr = load(attrAddr);
      uint32_t tileOffsetY = ((nameAddr & 0x3ff) >> 5 >> 1) % 2;

      // Render the pattern
      for (uint32_t tileXOffset = tempFineXScroll; tileXOffset < 8 and xBgOffset < renderWidth;
           tileXOffset++, xBgOffset++) {

        uint32_t color = (pattern >> ((7 - tileXOffset) * 2)) & 0x3;
        uint32_t tileOffsetX = (((nameAddr & 0x3ff) & 0x1f) >> 1) % 2;
        uint32_t subNibble = tileOffsetX + tileOffsetY * 2;
        assert(subNibble >= 0 and subNibble < 4);
        uint32_t palette = (attr >> (2 * subNibble)) & 0x3;
        bool transparent = (color == 0);
        uint32_t pixel = getColor(palette, color, false);

        scanlineBuffer[xBgOffset] = pixel;
        if (!transparent) {
          pixelWritten[xBgOffset] = true;
        }
        tempFineXScroll++;
        tempFineXScroll &= 0x7;
      }
      vramCoarseXInc();
    }
  }

  // render sprites
  if (renderSpritesEnabled()) {
    // Render up to 8 colliding sprites
    uint32_t patternTableAddr = getSpritePatternTableAddr();
    uint32_t renderedSprites = 0;
    for (uint32_t i = 0; i < maxSpriteCount; i++) {

      // Determine if the provide collides with the scanline
      uint32_t sprite = maxSpriteCount - 1 - i;
      if ((scanline >= (spriteRam[sprite].yCoordMinus1 + 1u)) and
          (scanline <= (spriteRam[sprite].yCoordMinus1 + 1u + spriteSize - 1u))) {
      } else {
        continue;
      }

      // Render the sprite
      bool spriteVerticalFlip = (spriteRam[sprite].attr & (1u << 7)) != 0;
      bool spriteHorizontalFlip = (spriteRam[sprite].attr & (1u << 6)) != 0;
      for (int j = 0; j < 8; j++) {
        uint32_t spriteLine = scanline - (spriteRam[sprite].yCoordMinus1 + 1);
        if (spriteVerticalFlip) {
          spriteLine = spriteSize - 1 - spriteLine;
        }

        uint32_t xOffset = j;
        if (spriteHorizontalFlip) {
          xOffset = 8 - 1 - xOffset;
        }
        uint32_t color;
        if (spriteSize8x8) {
          color = getColorFromPatternTable<true>(patternTableAddr, spriteRam[sprite].tileIndex,
                                                 xOffset, spriteLine);
        } else {
          color = getColorFromPatternTable<false>(patternTableAddr, spriteRam[sprite].tileIndex,
                                                  xOffset, spriteLine);
        }

        // Need to do some bounds checking bullshit here
        uint32_t xCoordinate = spriteRam[sprite].xCoord + j;
        if ((color != 0) and (xCoordinate < renderWidth)) {
          bool bgPixelInFront = (spriteRam[sprite].attr & (1u << 5)) != 0;
          if ((bgPixelInFront and !pixelWritten[xCoordinate]) or !bgPixelInFront) {
            scanlineBuffer[xCoordinate] = getColor(spriteRam[sprite].attr & 0x3, color, true);
          }
        }
        // Do sprite 0 collision detection
        if ((sprite == 0) and (color != 0) and (xCoordinate < renderWidth - 1)) {
          if (pixelWritten[xCoordinate]) {
            setSprite0Hit();
          }
        }
      }
      renderedSprites++;
      if (renderedSprites > maxRenderedSpritePerScanline) {
        setLostSprites();
        break;
      }
    }
  }

  // dump pixels to display
  for (uint32_t i = 0; i < renderWidth; i++) {
    setPixel(i, scanline, scanlineBuffer[i]);
  }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xa0, %rsp
movq %rdi, -0x8(%rbp)
movl %esi, -0xc(%rbp)
movq -0x8(%rbp), %rdi
movq %rdi, -0x88(%rbp)
callq 0x24f60
movq -0x88(%rbp), %rdi
andb $0x1, %al
movb %al, -0xd(%rbp)
movb -0xd(%rbp), %dl
movl $0x10, %eax
movl $0x8, %ecx
testb $0x1, %dl
cmovnel %ecx, %eax
movl %eax, -0x14(%rbp)
addq $0x14c, %rdi            # imm = 0x14C
xorl %esi, %esi
movl $0x100, %edx            # imm = 0x100
callq 0xe420
movq -0x88(%rbp), %rdi
addq $0x24c, %rdi            # imm = 0x24C
xorl %esi, %esi
movl $0x400, %edx            # imm = 0x400
callq 0xe420
movq -0x88(%rbp), %rdi
callq 0x24f80
testb $0x1, %al
jne 0x243b6
jmp 0x24656
movq -0x88(%rbp), %rdi
callq 0x24fa0
movw %ax, %cx
movq -0x88(%rbp), %rax
movzwl %cx, %ecx
movl %ecx, -0x18(%rbp)
movl 0x134(%rax), %eax
movl %eax, -0x1c(%rbp)
movl $0x0, -0x20(%rbp)
cmpl $0x100, -0x20(%rbp)     # imm = 0x100
jae 0x24654
movq -0x88(%rbp), %rax
movq %rax, %rdi
movzwl 0x138(%rax), %esi
callq 0x24fd0
movw %ax, %cx
movq -0x88(%rbp), %rax
movw %cx, -0x22(%rbp)
movq %rax, %rdi
movzwl 0x138(%rax), %esi
callq 0x24ff0
movq -0x88(%rbp), %rdi
movw %ax, -0x24(%rbp)
movzwl -0x22(%rbp), %esi
callq 0x23410
movb %al, %cl
movq -0x88(%rbp), %rax
movzbl %cl, %ecx
shll $0x4, %ecx
addl -0x18(%rbp), %ecx
movl %ecx, -0x90(%rbp)
movq %rax, %rdi
movzwl 0x138(%rax), %esi
callq 0x25030
movl %eax, %ecx
movl -0x90(%rbp), %eax
addl %ecx, %eax
movw %ax, -0x26(%rbp)
movzwl -0x26(%rbp), %ecx
xorl %eax, %eax
cmpl -0x18(%rbp), %ecx
movb %al, -0x89(%rbp)
jb 0x24494
movzwl -0x26(%rbp), %eax
movl -0x18(%rbp), %ecx
addl $0x1000, %ecx           # imm = 0x1000
cmpl %ecx, %eax
setb %al
movb %al, -0x89(%rbp)
movb -0x89(%rbp), %al
testb $0x1, %al
jne 0x244a0
jmp 0x244a2
jmp 0x244c1
leaq 0x44d9d(%rip), %rdi     # 0x69246
leaq 0x44ccc(%rip), %rsi     # 0x6917c
movl $0x163, %edx            # imm = 0x163
leaq 0x44de2(%rip), %rcx     # 0x6929e
callq 0xe240
movq -0x88(%rbp), %rdi
movzwl -0x26(%rbp), %esi
callq 0x23720
movq -0x88(%rbp), %rdi
movw %ax, -0x28(%rbp)
movzwl -0x24(%rbp), %esi
callq 0x23410
movb %al, -0x29(%rbp)
movzwl -0x22(%rbp), %eax
andl $0x3ff, %eax            # imm = 0x3FF
sarl $0x5, %eax
sarl %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %edx, -0x30(%rbp)
movl -0x1c(%rbp), %eax
movl %eax, -0x34(%rbp)
xorl %eax, %eax
cmpl $0x8, -0x34(%rbp)
movb %al, -0x91(%rbp)
jae 0x24525
cmpl $0x100, -0x20(%rbp)     # imm = 0x100
setb %al
movb %al, -0x91(%rbp)
movb -0x91(%rbp), %al
testb $0x1, %al
jne 0x24534
jmp 0x24643
movzwl -0x28(%rbp), %eax
movl $0x7, %ecx
subl -0x34(%rbp), %ecx
shll %ecx
sarl %cl, %eax
andl $0x3, %eax
movl %eax, -0x38(%rbp)
movzwl -0x22(%rbp), %eax
andl $0x3ff, %eax            # imm = 0x3FF
andl $0x1f, %eax
sarl %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %edx, -0x3c(%rbp)
movl -0x3c(%rbp), %eax
movl -0x30(%rbp), %ecx
shll %ecx
addl %ecx, %eax
movl %eax, -0x40(%rbp)
xorl %eax, %eax
cmpl $0x0, -0x40(%rbp)
movb %al, -0x92(%rbp)
jb 0x2458b
cmpl $0x4, -0x40(%rbp)
setb %al
movb %al, -0x92(%rbp)
movb -0x92(%rbp), %al
testb $0x1, %al
jne 0x24597
jmp 0x24599
jmp 0x245b8
leaq 0x44d1f(%rip), %rdi     # 0x692bf
leaq 0x44bd5(%rip), %rsi     # 0x6917c
movl $0x16f, %edx            # imm = 0x16F
leaq 0x44ceb(%rip), %rcx     # 0x6929e
callq 0xe240
movq -0x88(%rbp), %rdi
movzbl -0x29(%rbp), %eax
movl -0x40(%rbp), %ecx
shll %ecx
sarl %cl, %eax
andl $0x3, %eax
movl %eax, -0x44(%rbp)
cmpl $0x0, -0x38(%rbp)
sete %al
andb $0x1, %al
movb %al, -0x45(%rbp)
movl -0x44(%rbp), %esi
movl -0x38(%rbp), %edx
xorl %ecx, %ecx
callq 0x23440
movl %eax, %ecx
movq -0x88(%rbp), %rax
movl %ecx, -0x4c(%rbp)
movl -0x4c(%rbp), %edx
movl -0x20(%rbp), %ecx
movl %edx, 0x24c(%rax,%rcx,4)
testb $0x1, -0x45(%rbp)
jne 0x2461a
movq -0x88(%rbp), %rax
movl -0x20(%rbp), %ecx
movb $0x1, 0x14c(%rax,%rcx)
movl -0x1c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x1c(%rbp)
movl -0x1c(%rbp), %eax
andl $0x7, %eax
movl %eax, -0x1c(%rbp)
movl -0x34(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x34(%rbp)
movl -0x20(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x20(%rbp)
jmp 0x24507
movq -0x88(%rbp), %rdi
callq 0x23560
jmp 0x243e2
jmp 0x24656
movq -0x88(%rbp), %rdi
callq 0x25050
testb $0x1, %al
jne 0x2466b
jmp 0x248cb
movq -0x88(%rbp), %rdi
callq 0x25070
movzwl %ax, %eax
movl %eax, -0x50(%rbp)
movl $0x0, -0x54(%rbp)
movl $0x0, -0x58(%rbp)
cmpl $0x40, -0x58(%rbp)
jae 0x248c9
movq -0x88(%rbp), %rcx
movl $0x3f, %eax
subl -0x58(%rbp), %eax
movl %eax, -0x5c(%rbp)
movl -0xc(%rbp), %eax
movl -0x5c(%rbp), %edx
movzbl 0x30(%rcx,%rdx,4), %ecx
addl $0x1, %ecx
cmpl %ecx, %eax
jb 0x246da
movq -0x88(%rbp), %rcx
movl -0xc(%rbp), %eax
movl -0x5c(%rbp), %edx
movzbl 0x30(%rcx,%rdx,4), %ecx
addl $0x1, %ecx
addl -0x14(%rbp), %ecx
subl $0x1, %ecx
cmpl %ecx, %eax
ja 0x246da
jmp 0x246df
jmp 0x248bb
movq -0x88(%rbp), %rax
movl -0x5c(%rbp), %ecx
movzbl 0x32(%rax,%rcx,4), %ecx
andl $0x80, %ecx
cmpl $0x0, %ecx
setne %cl
andb $0x1, %cl
movb %cl, -0x5d(%rbp)
movl -0x5c(%rbp), %ecx
movzbl 0x32(%rax,%rcx,4), %eax
andl $0x40, %eax
cmpl $0x0, %eax
setne %al
andb $0x1, %al
movb %al, -0x5e(%rbp)
movl $0x0, -0x64(%rbp)
cmpl $0x8, -0x64(%rbp)
jge 0x2489c
movq -0x88(%rbp), %rcx
movl -0xc(%rbp), %eax
movl -0x5c(%rbp), %edx
movzbl 0x30(%rcx,%rdx,4), %ecx
addl $0x1, %ecx
subl %ecx, %eax
movl %eax, -0x68(%rbp)
testb $0x1, -0x5d(%rbp)
je 0x24753
movl -0x14(%rbp), %eax
subl $0x1, %eax
subl -0x68(%rbp), %eax
movl %eax, -0x68(%rbp)
movl -0x64(%rbp), %eax
movl %eax, -0x6c(%rbp)
testb $0x1, -0x5e(%rbp)
je 0x2476a
movl $0x7, %eax
subl -0x6c(%rbp), %eax
movl %eax, -0x6c(%rbp)
testb $0x1, -0xd(%rbp)
je 0x24799
movq -0x88(%rbp), %rdi
movl -0x50(%rbp), %eax
movl -0x5c(%rbp), %ecx
movzbl 0x31(%rdi,%rcx,4), %edx
movl -0x6c(%rbp), %ecx
movl -0x68(%rbp), %r8d
movzwl %ax, %esi
callq 0x250a0
movzbl %al, %eax
movl %eax, -0x70(%rbp)
jmp 0x247c0
movq -0x88(%rbp), %rdi
movl -0x50(%rbp), %eax
movl -0x5c(%rbp), %ecx
movzbl 0x31(%rdi,%rcx,4), %edx
movl -0x6c(%rbp), %ecx
movl -0x68(%rbp), %r8d
movzwl %ax, %esi
callq 0x251a0
movzbl %al, %eax
movl %eax, -0x70(%rbp)
movq -0x88(%rbp), %rax
movl -0x5c(%rbp), %ecx
movzbl 0x33(%rax,%rcx,4), %eax
addl -0x64(%rbp), %eax
movl %eax, -0x74(%rbp)
cmpl $0x0, -0x70(%rbp)
je 0x24855
cmpl $0x100, -0x74(%rbp)     # imm = 0x100
jae 0x24855
movq -0x88(%rbp), %rax
movl -0x5c(%rbp), %ecx
movzbl 0x32(%rax,%rcx,4), %eax
andl $0x20, %eax
cmpl $0x0, %eax
setne %al
andb $0x1, %al
movb %al, -0x75(%rbp)
testb $0x1, -0x75(%rbp)
je 0x2481b
movq -0x88(%rbp), %rax
movl -0x74(%rbp), %ecx
testb $0x1, 0x14c(%rax,%rcx)
je 0x24821
testb $0x1, -0x75(%rbp)
jne 0x24853
movq -0x88(%rbp), %rdi
movl -0x5c(%rbp), %eax
movzbl 0x32(%rdi,%rax,4), %esi
andl $0x3, %esi
movl -0x70(%rbp), %edx
movl $0x1, %ecx
callq 0x23440
movl %eax, %edx
movq -0x88(%rbp), %rax
movl -0x74(%rbp), %ecx
movl %edx, 0x24c(%rax,%rcx,4)
jmp 0x24855
cmpl $0x0, -0x5c(%rbp)
jne 0x2488c
cmpl $0x0, -0x70(%rbp)
je 0x2488c
cmpl $0xff, -0x74(%rbp)
jae 0x2488c
movq -0x88(%rbp), %rax
movl -0x74(%rbp), %ecx
testb $0x1, 0x14c(%rax,%rcx)
je 0x2488a
movq -0x88(%rbp), %rdi
callq 0x252d0
jmp 0x2488c
jmp 0x2488e
movl -0x64(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x64(%rbp)
jmp 0x2471d
movl -0x54(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x54(%rbp)
cmpl $0x8, -0x54(%rbp)
jbe 0x248b9
movq -0x88(%rbp), %rdi
callq 0x252f0
jmp 0x248c9
jmp 0x248bb
movl -0x58(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x58(%rbp)
jmp 0x2468b
jmp 0x248cb
movl $0x0, -0x7c(%rbp)
cmpl $0x100, -0x7c(%rbp)     # imm = 0x100
jae 0x24902
movq -0x88(%rbp), %rdi
movl -0x7c(%rbp), %esi
movl -0xc(%rbp), %edx
movl -0x7c(%rbp), %eax
movl 0x24c(%rdi,%rax,4), %ecx
callq 0x24910
movl -0x7c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x7c(%rbp)
jmp 0x248d2
addq $0xa0, %rsp
popq %rbp
retq
nopl (%rax,%rax)