// File: /workspace/github_cpp_makefile_star2_O2/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
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
movq %rdi, %rbx
movq (%rdi), %rax
movq 0x8(%rax), %rax
movb 0xff41(%rax), %r14b
cmpb $0x0, 0xff40(%rax)
js 0x31da2
movabsq $0x200000000, %rcx      # imm = 0x200000000
movq %rcx, 0x8(%rbx)
movb $0x0, 0x10(%rbx)
andb $-0x4, %r14b
orb $0x2, %r14b
movb %r14b, 0xff41(%rax)
jmp 0x31ede
movzbl %sil, %ecx
addl 0x8(%rbx), %ecx
movl %ecx, 0x8(%rbx)
movl 0xc(%rbx), %edx
cmpq $0x3, %rdx
ja 0x31e95
leaq 0xf734(%rip), %rsi      # 0x414f4
movslq (%rsi,%rdx,4), %rdx
addq %rsi, %rdx
jmpq *%rdx
cmpl $0xcc, %ecx
jl 0x31e95
andl $0x0, 0x8(%rbx)
movb 0x10(%rbx), %cl
incb %cl
movb %cl, 0x10(%rbx)
cmpb $-0x71, %cl
jne 0x31ef9
testb $0x10, 0xff41(%rax)
movl $0x1, 0xc(%rbx)
movb $0x1, 0x1c(%rbx)
jne 0x31e8a
jmp 0x31e95
cmpl $0x50, %ecx
jl 0x31e95
movabsq $0x300000000, %rax      # imm = 0x300000000
movq %rax, 0x8(%rbx)
jmp 0x31e95
cmpl $0xab, %ecx
jle 0x31e95
andq $0x0, 0x8(%rbx)
movb 0xff41(%rax), %r15b
movq %rbx, %rdi
callq 0x31f0c
testb $0x8, %r15b
jne 0x31e8a
jmp 0x31e95
cmpb $-0x70, 0x10(%rbx)
jne 0x31e57
movq %rbx, %rdi
xorl %esi, %esi
callq 0x31f54
movl 0x8(%rbx), %ecx
cmpl $0x1c8, %ecx            # imm = 0x1C8
jl 0x31e95
andl $0x0, 0x8(%rbx)
movb 0x10(%rbx), %al
incb %al
movb %al, 0x10(%rbx)
cmpb $-0x66, %al
jb 0x31e95
movl $0x2, 0xc(%rbx)
movq (%rbx), %rax
movq 0x8(%rax), %rax
testb $0x20, 0xff41(%rax)
movb $0x0, 0x10(%rbx)
je 0x31e95
pushq $0x1
popq %rsi
movq %rbx, %rdi
callq 0x31f54
movb 0x10(%rbx), %cl
movq (%rbx), %rax
movq 0x8(%rax), %rax
cmpb 0xff45(%rax), %cl
jne 0x31ec8
movl %r14d, %r15d
orb $0x4, %r15b
testb $0x40, %r14b
je 0x31ecf
pushq $0x1
popq %rsi
movq %rbx, %rdi
callq 0x31f54
movq (%rbx), %rax
movq 0x8(%rax), %rax
jmp 0x31ecf
andb $-0x5, %r14b
movl %r14d, %r15d
andb $-0x4, %r15b
orb 0xc(%rbx), %r15b
movb %r15b, 0xff41(%rax)
movb 0x10(%rbx), %al
movq (%rbx), %rcx
movq 0x8(%rcx), %rcx
movb %al, 0xff44(%rcx)
addq $0x8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
movl $0x2, 0xc(%rbx)
testb $0x20, 0xff41(%rax)
jne 0x31e8a
jmp 0x31e95
nop