// File: /workspace/github_cpp_makefile_star2_O1/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), %rdi
movq 0x8(%rdi), %rax
movb 0xff41(%rax), %r14b
cmpb $0x0, 0xff40(%rax)
js 0x389b8
movabsq $0x200000000, %rcx      # imm = 0x200000000
movq %rcx, 0x8(%rbx)
movb $0x0, 0x10(%rbx)
andb $-0x4, %r14b
orb $0x2, %r14b
movb %r14b, 0xff41(%rax)
jmp 0x38b3c
movzbl %sil, %ecx
addl 0x8(%rbx), %ecx
movl %ecx, 0x8(%rbx)
movl 0xc(%rbx), %edx
cmpq $0x3, %rdx
ja 0x38aea
leaq 0xf8e6(%rip), %rsi      # 0x482bc
movslq (%rsi,%rdx,4), %rdx
addq %rsi, %rdx
jmpq *%rdx
cmpl $0xcc, %ecx
jl 0x38aea
movl $0x0, 0x8(%rbx)
movb 0x10(%rbx), %cl
incb %cl
movb %cl, 0x10(%rbx)
cmpb $-0x71, %cl
jne 0x38ab8
movb 0xff41(%rax), %r15b
andb $0x10, %r15b
movl $0x1, 0xc(%rbx)
movb $0x1, 0x1c(%rbx)
jmp 0x38aca
cmpl $0x50, %ecx
jl 0x38aea
movabsq $0x300000000, %rax      # imm = 0x300000000
movq %rax, 0x8(%rbx)
jmp 0x38aea
cmpl $0xac, %ecx
jl 0x38aea
movq $0x0, 0x8(%rbx)
movb 0xff41(%rax), %r15b
andb $0x8, %r15b
movq %rbx, %rdi
callq 0x38b58
jmp 0x38aca
cmpb $-0x70, 0x10(%rbx)
jne 0x38a7d
movzbl 0xff0f(%rax), %edx
orl $0x1, %edx
movl $0xff0f, %esi           # imm = 0xFF0F
callq 0x383ba
cmpl $0x1c8, 0x8(%rbx)       # imm = 0x1C8
jl 0x38aea
movl $0x0, 0x8(%rbx)
movb 0x10(%rbx), %al
incb %al
movb %al, 0x10(%rbx)
cmpb $-0x66, %al
jb 0x38aea
movl $0x2, 0xc(%rbx)
movq (%rbx), %rax
movq 0x8(%rax), %rax
movb 0xff41(%rax), %r15b
andb $0x20, %r15b
movb $0x0, 0x10(%rbx)
jmp 0x38aca
movl $0x2, 0xc(%rbx)
movb 0xff41(%rax), %r15b
andb $0x20, %r15b
testb %r15b, %r15b
je 0x38aea
movq (%rbx), %rdi
movq 0x8(%rdi), %rax
movzbl 0xff0f(%rax), %edx
orl $0x2, %edx
movl $0xff0f, %esi           # imm = 0xFF0F
callq 0x383ba
movb 0x10(%rbx), %cl
movq (%rbx), %rdi
movq 0x8(%rdi), %rax
cmpb 0xff45(%rax), %cl
jne 0x38b1f
movl %r14d, %r15d
orb $0x4, %r15b
testb $0x40, %r14b
je 0x38b26
movzbl 0xff0f(%rax), %edx
orl $0x2, %edx
movl $0xff0f, %esi           # imm = 0xFF0F
callq 0x383ba
jmp 0x38b26
andb $-0x5, %r14b
movl %r14d, %r15d
andb $-0x4, %r15b
orb 0xc(%rbx), %r15b
movq (%rbx), %rax
movq 0x8(%rax), %rax
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
nop