// File: donfreiday[P]gb/gpu.cpp
// Function: GPU::step(unsigned char)
void GPU::step(u8 cycles) {
  u8 status = mmu->memory[STAT];

  // If the LCD is disabled:
  if (!(bitTest(mmu->memory[LCDC], LCDC_DISPLAY_ENABLE))) {
    modeclock = 0;
    scanline = 0;
    mode = 2;               // todo: hack to match BGB LCD timings
    status &= (0xFF << 2);  // clear mode bits in LCD status register
    status |= mode;
    mmu->memory[STAT] = status;
    mmu->memory[LY] = scanline;  // writes to this address are trapped in MMU
    return;
  }

  modeclock += cycles;
  bool interrupt = false;

  switch (mode) {
    // OAM read mode, 80 cycles
    case 2:
      if (modeclock >= 80) {
        modeclock = 0;
        mode = 3;
      }
      break;

    // VRAM read mode, 172 cycles. End of mode 3 is end of scanline
    case 3:
      if (modeclock >= 172) {
        modeclock = 0;
        mode = 0;  // hblank
        interrupt = bitTest(mmu->memory[STAT], STAT_MODE0_INT_ENABLE);
        renderScanline();
      }
      break;

    // hblank, 204 cycles
    case 0:
      if (modeclock >= 204) {
        modeclock = 0;
        scanline++;
        if (scanline == 143) {
          interrupt = bitTest(mmu->memory[STAT], STAT_MODE1_INT_ENABLE);
          mode = 1;  // vblank
          renderScreen();
        } else {
          mode = 2;
          interrupt = bitTest(mmu->memory[STAT], STAT_MODE2_INT_ENABLE);
        }
      }
      break;

    // vblank, 456 cycles (10 scanlines)
    case 1:
      if (scanline == 144) {
        requestInterrupt(0);
      }
      if (modeclock >= 456) {
        modeclock = 0;
        scanline++;
        if (scanline > 153) {
          mode = 2;  // restart scanning mode
          interrupt = bitTest(mmu->memory[STAT], STAT_MODE2_INT_ENABLE);
          scanline = 0;
        }
      }
      break;
  }

  if (interrupt) {
    requestInterrupt(1);
  }

  // Handle coincidence flag and check for interrupt enabled
  if (scanline == mmu->memory[LYC]) {
    bitSet(status, STAT_LYC_FLAG);
    if (bitTest(status, STAT_LYC_INT_ENABLE)) {
      requestInterrupt(1);
    }
  } else {
    bitClear(status, STAT_LYC_FLAG);
  }

  status &= (0xFF << 2);  // clear the mode flag bits
  status |= mode;
  mmu->memory[STAT] = status;
  mmu->memory[LY] = scanline;  // writes to this address are trapped in mmu
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x30, %rsp
movb %sil, %al
movq %rdi, -0x8(%rbp)
movb %al, -0x9(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0x18(%rbp)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff41, %esi           # imm = 0xFF41
callq 0x4f3d0
movq %rax, %rcx
movq -0x18(%rbp), %rax
movb (%rcx), %cl
movb %cl, -0xa(%rbp)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff40, %esi           # imm = 0xFF40
callq 0x4f3d0
movl $0x7, %esi
movzbl (%rax), %edi
callq 0x4e910
testb $0x1, %al
jne 0x5135c
movq -0x18(%rbp), %rax
movl $0x0, 0x8(%rax)
movb $0x0, 0x10(%rax)
movl $0x2, 0xc(%rax)
movzbl -0xa(%rbp), %ecx
andl $0x3fc, %ecx            # imm = 0x3FC
movb %cl, -0xa(%rbp)
movl 0xc(%rax), %edx
movzbl -0xa(%rbp), %ecx
orl %edx, %ecx
movb %cl, -0xa(%rbp)
movb -0xa(%rbp), %cl
movb %cl, -0x1a(%rbp)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff41, %esi           # imm = 0xFF41
callq 0x4f3d0
movb -0x1a(%rbp), %dl
movq %rax, %rcx
movq -0x18(%rbp), %rax
movb %dl, (%rcx)
movb 0x10(%rax), %cl
movb %cl, -0x19(%rbp)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff44, %esi           # imm = 0xFF44
callq 0x4f3d0
movb -0x19(%rbp), %cl
movb %cl, (%rax)
jmp 0x51600
movq -0x18(%rbp), %rax
movzbl -0x9(%rbp), %edx
movl 0x8(%rax), %ecx
addl %edx, %ecx
movl %ecx, 0x8(%rax)
movb $0x0, -0xb(%rbp)
movl 0xc(%rax), %eax
movq %rax, -0x28(%rbp)
subq $0x3, %rax
ja 0x51527
movq -0x28(%rbp), %rax
leaq 0x14cd8(%rip), %rcx     # 0x66064
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq -0x18(%rbp), %rax
cmpl $0x50, 0x8(%rax)
jl 0x513b1
movq -0x18(%rbp), %rax
movl $0x0, 0x8(%rax)
movl $0x3, 0xc(%rax)
jmp 0x51527
movq -0x18(%rbp), %rax
cmpl $0xac, 0x8(%rax)
jl 0x51401
movq -0x18(%rbp), %rax
movl $0x0, 0x8(%rax)
movl $0x0, 0xc(%rax)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff41, %esi           # imm = 0xFF41
callq 0x4f3d0
movl $0x3, %esi
movzbl (%rax), %edi
callq 0x4e910
movq -0x18(%rbp), %rdi
andb $0x1, %al
movb %al, -0xb(%rbp)
callq 0x51610
jmp 0x51527
movq -0x18(%rbp), %rax
cmpl $0xcc, 0x8(%rax)
jl 0x5149f
movq -0x18(%rbp), %rax
movl $0x0, 0x8(%rax)
movb 0x10(%rax), %cl
addb $0x1, %cl
movb %cl, 0x10(%rax)
movzbl 0x10(%rax), %eax
cmpl $0x8f, %eax
jne 0x5146f
movq -0x18(%rbp), %rax
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff41, %esi           # imm = 0xFF41
callq 0x4f3d0
movl $0x4, %esi
movzbl (%rax), %edi
callq 0x4e910
movq -0x18(%rbp), %rdi
andb $0x1, %al
movb %al, -0xb(%rbp)
movl $0x1, 0xc(%rdi)
callq 0x51680
jmp 0x5149d
movq -0x18(%rbp), %rax
movl $0x2, 0xc(%rax)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff41, %esi           # imm = 0xFF41
callq 0x4f3d0
movl $0x5, %esi
movzbl (%rax), %edi
callq 0x4e910
andb $0x1, %al
movb %al, -0xb(%rbp)
jmp 0x5149f
jmp 0x51527
movq -0x18(%rbp), %rax
movzbl 0x10(%rax), %eax
cmpl $0x90, %eax
jne 0x514be
movq -0x18(%rbp), %rdi
xorl %esi, %esi
callq 0x516a0
movq -0x18(%rbp), %rax
cmpl $0x1c8, 0x8(%rax)       # imm = 0x1C8
jl 0x51525
movq -0x18(%rbp), %rax
movl $0x0, 0x8(%rax)
movb 0x10(%rax), %cl
addb $0x1, %cl
movb %cl, 0x10(%rax)
movzbl 0x10(%rax), %eax
cmpl $0x99, %eax
jle 0x51523
movq -0x18(%rbp), %rax
movl $0x2, 0xc(%rax)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff41, %esi           # imm = 0xFF41
callq 0x4f3d0
movl $0x5, %esi
movzbl (%rax), %edi
callq 0x4e910
movb %al, %cl
movq -0x18(%rbp), %rax
andb $0x1, %cl
movb %cl, -0xb(%rbp)
movb $0x0, 0x10(%rax)
jmp 0x51525
jmp 0x51527
testb $0x1, -0xb(%rbp)
je 0x5153b
movq -0x18(%rbp), %rdi
movl $0x1, %esi
callq 0x516a0
movq -0x18(%rbp), %rax
movzbl 0x10(%rax), %ecx
movl %ecx, -0x2c(%rbp)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff45, %esi           # imm = 0xFF45
callq 0x4f3d0
movq %rax, %rcx
movl -0x2c(%rbp), %eax
movzbl (%rcx), %ecx
cmpl %ecx, %eax
jne 0x51596
leaq -0xa(%rbp), %rdi
movl $0x2, %esi
callq 0x4e8e0
movl $0x6, %esi
movzbl -0xa(%rbp), %edi
callq 0x4e910
testb $0x1, %al
jne 0x51586
jmp 0x51594
movq -0x18(%rbp), %rdi
movl $0x1, %esi
callq 0x516a0
jmp 0x515a4
leaq -0xa(%rbp), %rdi
movl $0x2, %esi
callq 0x4e8b0
movq -0x18(%rbp), %rax
movzbl -0xa(%rbp), %ecx
andl $0x3fc, %ecx            # imm = 0x3FC
movb %cl, -0xa(%rbp)
movl 0xc(%rax), %edx
movzbl -0xa(%rbp), %ecx
orl %edx, %ecx
movb %cl, -0xa(%rbp)
movb -0xa(%rbp), %cl
movb %cl, -0x2e(%rbp)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff41, %esi           # imm = 0xFF41
callq 0x4f3d0
movb -0x2e(%rbp), %dl
movq %rax, %rcx
movq -0x18(%rbp), %rax
movb %dl, (%rcx)
movb 0x10(%rax), %cl
movb %cl, -0x2d(%rbp)
movq (%rax), %rdi
addq $0x8, %rdi
movl $0xff44, %esi           # imm = 0xFF44
callq 0x4f3d0
movb -0x2d(%rbp), %cl
movb %cl, (%rax)
addq $0x30, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)