// File: /workspace/github_cpp_makefile_star2_O1/thanoskoutr[P]Chip-8-Emulator/chip8.cpp
// Function: Chip8::emulateCycle()
void Chip8::emulateCycle()
{
  /* Fetch next Opcode from memory */
  unsigned short opcode = memory[pc] << 8 | memory[pc + 1];  // opcode is 2 bytes, memory stores 1 byte

  /* Decode Opcode */
  switch (opcode & 0xF000)  // Switch on MSB
  {
    case 0x0000:
      switch (opcode & 0x000F)
      {
        case 0x0000:  // 00E0 - CLS: Clear the display.
          for (int i = 0; i < SCREEN_SIZE; i++)
            gfx[i] = 0x0;
          drawFlag = true;
          pc += 2;
        break;

        case 0x000E:  // 00EE - RET: Return from a subroutine.
          sp--;
          pc = stack[sp];
          pc += 2;
        break;

        default:
          printf ("Unknown opcode [0x0000]: 0x%X\n", opcode);
      }
    break;

    case 0x1000:  // 1nnn - JP addr: Jump to location nnn.
      pc = opcode & 0x0FFF;
    break;

    case 0x2000:  // 2nnn - CALL addr: Call subroutine at nnn.
      stack[sp] = pc;
      sp++;
      pc = opcode & 0x0FFF;
    break;

    case 0x3000:  // 3xkk - SE Vx, byte: Skip next instruction if Vx = kk.
      if (V[(opcode & 0x0F00) >> 8] == (opcode & 0x00FF))
        pc += 4;
      else
        pc += 2;
    break;

    case 0x4000:  // 4xkk - SNE Vx, byte: Skip next instruction if Vx != kk.
      if (V[(opcode & 0x0F00) >> 8] != (opcode & 0x00FF))
        pc += 4;
      else
        pc += 2;
    break;

    case 0x5000:  // 5xy0 - SE Vx, Vy: Skip next instruction if Vx = Vy.
      if (V[(opcode & 0x0F00) >> 8] == V[(opcode & 0x00F0) >> 4])
        pc += 4;
      else
        pc += 2;
    break;

    case 0x6000:  // 6xkk - LD Vx, byte: Set Vx = kk.
      V[(opcode & 0x0F00) >> 8] = opcode & 0x00FF;
      pc += 2;
    break;

    case 0x7000:  // 7xkk - ADD Vx, byte: Set Vx = Vx + kk.
      V[(opcode & 0x0F00) >> 8] += opcode & 0x00FF;
      pc += 2;
    break;

    case 0x8000:
      switch (opcode & 0x000F)
      {
        case 0x0000:  // 8xy0 - LD Vx, Vy: Set Vx = Vy.
          V[(opcode & 0x0F00) >> 8] = V[(opcode & 0x00F0) >> 4];
          pc += 2;
        break;

        case 0x0001:  // 8xy1 - OR Vx, Vy: Set Vx = Vx OR Vy.
          V[(opcode & 0x0F00) >> 8] |= V[(opcode & 0x00F0) >> 4];
          pc += 2;
        break;

        case 0x0002:  // 8xy2 - AND Vx, Vy: Set Vx = Vx AND Vy.
          V[(opcode & 0x0F00) >> 8] &= V[(opcode & 0x00F0) >> 4];
          pc += 2;
        break;

        case 0x0003:  // 8xy3 - XOR Vx, Vy: Set Vx = Vx XOR Vy.
          V[(opcode & 0x0F00) >> 8] ^= V[(opcode & 0x00F0) >> 4];
          pc += 2;
        break;

        case 0x0004:  //  8xy4 - ADD Vx, Vy: Set Vx = Vx + Vy, set VF = carry.
          if (V[(opcode & 0x0F00) >> 8] + V[(opcode & 0x00F0) >> 4] > 0xFF) // Check for overflow
            V[0xF] = 1;
          else
            V[0xF] = 0;
          V[(opcode & 0x0F00) >> 8] += V[(opcode & 0x00F0) >> 4];
          pc += 2;
        break;

        case 0x0005:  //  8xy5 - SUB Vx, Vy: Set Vx = Vx - Vy, set VF = NOT borrow.
          if (V[(opcode & 0x0F00) >> 8] > V[(opcode & 0x00F0) >> 4])  // check Vx-Vy
            V[0xF] = 1;
          else
            V[0xF] = 0;
          V[(opcode & 0x0F00) >> 8] -= V[(opcode & 0x00F0) >> 4];
          pc += 2;
        break;

        case 0x0006:  //  8xy6 - SHR Vx {, Vy}: Set Vx = Vx SHR 1.
          if ((V[(opcode & 0x0F00) >> 8] & 0x1) == 1) // Check if LSB == 1
            V[0xF] = 1;
          else
            V[0xF] = 0;
          V[(opcode & 0x0F00) >> 8] >>= 1;  // divide by 2 == bitwise right shift with 1
          pc += 2;
        break;

        case 0x0007:  // 8xy7 - SUBN Vx, Vy: Set Vx = Vy - Vx, set VF = NOT borrow.
          if (V[(opcode & 0x0F00) >> 8] < V[(opcode & 0x00F0) >> 4])  // check Vy-Vx
            V[0xF] = 1;
          else
            V[0xF] = 0;
          V[(opcode & 0x0F00) >> 8] = V[(opcode & 0x00F0) >> 4] - V[(opcode & 0x0F00) >> 8];
          pc += 2;
        break;

        case 0x000E:  // 8xyE - SHL Vx {, Vy}: Set Vx = Vx SHL 1.
          if (V[(opcode & 0x0F00) >> 8] >> 7 == 1) // Check if MSB == 1
            V[0xF] = 1;
          else
            V[0xF] = 0;
          V[(opcode & 0x0F00) >> 8] <<= 1;  // multiply by 2 == bitwise left shift with 1
          pc += 2;
        break;

        default:
          printf ("Unknown opcode [0x8000]: 0x%X\n", opcode);
      }
    break;

    case 0x9000:  // 9xy0 - SNE Vx, Vy: Skip next instruction if Vx != Vy.
      if (V[(opcode & 0x0F00) >> 8] != V[(opcode & 0x00F0) >> 4])
        pc += 4;
      else
        pc += 2;
    break;

    case 0xA000:  // Annn - LD I, addr: Set I = nnn.
      I = opcode & 0x0FFF;
      pc += 2;
    break;

    case 0xB000:  // Bnnn - JP V0, addr: Jump to location nnn + V0.
      pc = (opcode & 0x0FFF) + V[0];
    break;

    case 0xC000:  // Cxkk - RND Vx, byte: Set Vx = random byte AND kk.
      V[(opcode & 0x0F00) >> 8] = (rand() % 0xFF) & (opcode & 0x00FF);  // generate random number between 0-255
      pc += 2;
    break;

    case 0xD000:  // Dxyn - DRW Vx, Vy, nibble: Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
    {
      /* A sprite is a set of 8 pixels = 8 bits = 1 byte,
       * Each row of 8 pixels is read as bit-coded starting from memory location I;
       * I value doesn't change after the execution of this instruction.
       * VF is set to 1 if any screen pixels are flipped from set to unset when the sprite is drawn,
         and to 0 if that doesn't happen */

      unsigned short x = V[(opcode & 0x0F00) >> 8];
      unsigned short y = V[(opcode & 0x00F0) >> 4];
      unsigned short height = opcode & 0x000F;
      unsigned short sprite;

      V[0xF] = 0;
      for (int yLine = 0; yLine < height; yLine++)  // read for n-byte sprites
      {
        sprite = memory[I + yLine];  // sprite represents 1 hex value e.g. 0xF2 (= 1 byte = 8 bits = 8 pixels)
        for (int xLine = 0; xLine < 8; xLine++) // each row is 8 pixels
        {
          if ((sprite & (0x80 >> xLine)) != 0 ) // For each bit of the sprite that is 1, 0x80 = 1000 0000
          {
            if (gfx[((y + yLine) * SCREEN_WIDTH) + (x + xLine)] == 1) // Check if the pixel on the display is set to 1
              V[0xF] = 1;
            gfx[((y + yLine) * SCREEN_WIDTH) + (x + xLine)] ^= 1; // Set the pixel value by using XOR
          }
        }
      }
      drawFlag = true;
      pc += 2;
    }
    break;

    case 0xE000:
      switch (opcode & 0x00FF)
      {
        case 0x009E:  // Ex9E - SKP Vx: Skip next instruction if key with the value of Vx is pressed.
          if (key[V[(opcode & 0x0F00) >> 8]] != 0)
            pc += 4;
          else
            pc += 2;
        break;

        case 0x00A1:  // ExA1 - SKNP Vx: Skip next instruction if key with the value of Vx is not pressed.
          if (key[V[(opcode & 0x0F00) >> 8]] == 0)
            pc += 4;
          else
            pc += 2;
        break;

        default:
          printf ("Unknown opcode [0xE000]: 0x%X\n", opcode);
      }
    break;

    case 0xF000:
      switch (opcode & 0x00FF)
      {
        case 0x0007:  // Fx07 - LD Vx, DT: Set Vx = delay timer value.
          V[(opcode & 0x0F00) >> 8] = delay_timer;
          pc += 2;
        break;

        case 0x000A:  // Fx0A - LD Vx, K: Wait for a key press, store the value of the key in Vx.
        {
          bool isPressed = false;
          for (int i = 0; i < KEY_SIZE; i++)
          {
            if (key[i] != 0)
            {
              V[(opcode & 0x0F00) >> 8] = key[i]; // or Vx = i
              isPressed = true;
            }
          }
          if (!isPressed) // If no key pressed, skip this cycle and try again
            return;
          pc += 2;
        }
        break;

        case 0x0015:  // Fx15 - LD DT, Vx: Set delay timer = Vx.
          delay_timer = V[(opcode & 0x0F00) >> 8];
          pc += 2;
        break;

        case 0x0018:  // Fx18 - LD ST, Vx: ST is set equal to the value of Vx.
          sound_timer = V[(opcode & 0x0F00) >> 8];
          pc += 2;
        break;

        case 0x001E:  // Fx1E - ADD I, Vx: Set I = I + Vx.
          if (I + V[(opcode & 0x0F00) >> 8] > 0xFFF) // Check for overflow, undocumented feature
            V[0xF] = 1;
          else
            V[0xF] = 0;
          I += V[(opcode & 0x0F00) >> 8];
          pc += 2;
        break;

        case 0x0029:  // Fx29 - LD F, Vx: Set I = location of sprite for digit Vx.
          I = V[(opcode & 0x0F00) >> 8] * 0x5;  // Each character in font takes 5 bytes of memory
          pc += 2;
        break;

        case 0x0033:  // Fx33 - LD B, Vx: Store BCD representation of Vx in memory locations I, I+1, and I+2.
          memory[I]   = (V[(opcode & 0x0F00) >> 8] / 100);       // hundreds
          memory[I+1] = (V[(opcode & 0x0F00) >> 8] / 10) % 10;   // tens
          memory[I+2] = (V[(opcode & 0x0F00) >> 8]) % 10;        // ones
          pc += 2;
        break;

        case 0x0055:  // Fx55 - LD [I], Vx: Store registers V0 through Vx in memory starting at location I.
          for (int i = 0; i <= ((opcode & 0x0F00) >> 8); i++)
            memory[I + i] = V[i];
          /* On the original interpreter, when the operation is done, I = I + X + 1 (I is incremented).
             In SCHIP, I is left unmodified. */
					I += ((opcode & 0x0F00) >> 8) + 1;
          pc += 2;
        break;

        case 0x0065:  // Fx65 - LD Vx, [I]: Read registers V0 through Vx from memory starting at location I.
          for (int i = 0; i <= ((opcode & 0x0F00) >> 8); i++)
            V[i] = memory[I + i];
          /* On the original interpreter, when the operation is done, I = I + X + 1 (I is incremented).
             In SCHIP, I is left unmodified. */
					I += ((opcode & 0x0F00) >> 8) + 1;
          pc += 2;
        break;

        default:
          printf ("Unknown opcode [0xF000]: 0x%X\n", opcode);
      }
    break;

    default:
      printf ("Unknown opcode: 0x%X\n", opcode);
  }

  /* Update Timers */
  if (delay_timer > 0)
    delay_timer--;

  if (sound_timer > 0)
  {
    if (sound_timer == 1)
      sound_timer--;
  }

}

// Assembly:
pushq %rbp
pushq %r14
pushq %rbx
movq %rdi, %rbx
movzwl 0x1826(%rdi), %eax
movzbl 0x814(%rdi,%rax), %r14d
movl %r14d, %esi
shll $0x8, %esi
movb 0x815(%rdi,%rax), %bpl
movzbl %bpl, %edx
orl %edx, %esi
movl %r14d, %ecx
shrl $0x4, %ecx
leaq 0x18c4(%rip), %rdi      # 0x4004
movslq (%rdi,%rcx,4), %rcx
addq %rdi, %rcx
jmpq *%rcx
andl $0xf, %edx
cmpl $0xe, %edx
je 0x2a51
testl %edx, %edx
jne 0x2a74
leaq 0x1(%rbx), %rdi
movl $0x800, %edx            # imm = 0x800
xorl %esi, %esi
callq 0x2110
jmp 0x2971
cmpl $0xa1, %edx
je 0x2a22
cmpl $0x9e, %edx
jne 0x2a45
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %ecx
cmpb $0x0, 0x801(%rbx,%rcx)
jne 0x2a3d
jmp 0x2d28
andl $0xfff, %esi            # imm = 0xFFF
movzbl 0x1814(%rbx), %eax
addl %esi, %eax
jmp 0x2d2b
andl $0xf, %r14d
cmpb %bpl, 0x1814(%rbx,%r14)
jne 0x2a3d
jmp 0x2d28
callq 0x20b0
cltq
imulq $-0x7f7f7f7f, %rax, %rcx # imm = 0x80808081
shrq $0x20, %rcx
addl %eax, %ecx
movl %ecx, %edx
shrl $0x1f, %edx
shrl $0x7, %ecx
addl %edx, %ecx
addl %eax, %ecx
andb %cl, %bpl
andl $0xf, %r14d
movb %bpl, 0x1814(%rbx,%r14)
jmp 0x2974
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
shrl $0x4, %edx
cmpb 0x1814(%rbx,%rdx), %cl
jne 0x2a3d
jmp 0x2d28
movzwl 0x1848(%rbx), %ecx
movw %ax, 0x1828(%rbx,%rcx,2)
incw 0x1848(%rbx)
andl $0xfff, %esi            # imm = 0xFFF
movw %si, 0x1826(%rbx)
jmp 0x2d32
cmpl $0x1d, %edx
jle 0x29d1
cmpl $0x32, %edx
jle 0x2aa5
cmpl $0x33, %edx
je 0x2b45
cmpl $0x55, %edx
je 0x2c79
cmpl $0x65, %edx
jne 0x2d5b
andl $0xf, %r14d
leaq 0x1814(%rbx), %rax
movq %r14, %rcx
incq %rcx
movzwl 0x1824(%rbx), %edx
movb -0x1000(%rax,%rdx), %dl
movb %dl, (%rax)
incq %rax
decq %rcx
jne 0x2890
jmp 0x2ca2
andl $0xf, %r14d
cmpb %bpl, 0x1814(%rbx,%r14)
je 0x2a3d
jmp 0x2d28
andl $0xf, %r14d
addb %bpl, 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
shrl $0x4, %edx
cmpb 0x1814(%rbx,%rdx), %cl
je 0x2a3d
jmp 0x2d28
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %eax
movl %edx, %ecx
shrl $0x4, %ecx
movzbl 0x1814(%rbx,%rcx), %ecx
movb $0x0, 0x1823(%rbx)
andq $0xf, %rdx
je 0x2971
shll $0x6, %ecx
addq %rax, %rcx
leaq (%rbx,%rcx), %rax
incq %rax
xorl %esi, %esi
movzwl 0x1824(%rbx), %ecx
addq %rsi, %rcx
movzbl 0x814(%rbx,%rcx), %edi
xorl %ecx, %ecx
movl %edi, %r8d
shll %cl, %r8d
testb %r8b, %r8b
jns 0x295c
cmpb $0x1, (%rax,%rcx)
jne 0x2958
movb $0x1, 0x1823(%rbx)
xorb $0x1, (%rax,%rcx)
incq %rcx
cmpq $0x8, %rcx
jne 0x2940
incq %rsi
addq $0x40, %rax
cmpq %rdx, %rsi
jne 0x292c
movb $0x1, (%rbx)
addw $0x2, 0x1826(%rbx)
jmp 0x2d32
andl $0xf, %r14d
movb %bpl, 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xfff, %esi            # imm = 0xFFF
movw %si, 0x1824(%rbx)
jmp 0x2d28
movl %edx, %ecx
andl $0xf, %ecx
cmpl $0xe, %ecx
ja 0x2d64
leaq 0x168b(%rip), %rdi      # 0x4044
movslq (%rdi,%rcx,4), %rcx
addq %rdi, %rcx
jmpq *%rcx
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
jmp 0x2af4
cmpl $0x14, %edx
jg 0x2a80
cmpl $0x7, %edx
je 0x2aee
cmpl $0xa, %edx
jne 0x2d5b
andl $0xf, %r14d
xorl %ecx, %ecx
xorl %edx, %edx
movb 0x801(%rbx,%rcx), %sil
testb %sil, %sil
je 0x2a0b
movb %sil, 0x1814(%rbx,%r14)
movb $0x1, %dl
incq %rcx
cmpq $0x10, %rcx
jne 0x29f4
testb $0x1, %dl
jne 0x2d28
jmp 0x2d56
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %ecx
cmpb $0x0, 0x801(%rbx,%rcx)
jne 0x2d28
addl $0x4, %eax
jmp 0x2d2b
leaq 0x1721(%rip), %rdi      # 0x416d
jmp 0x2d6b
movzwl 0x1848(%rbx), %eax
decl %eax
movw %ax, 0x1848(%rbx)
movzwl %ax, %eax
movzwl 0x1828(%rbx,%rax,2), %eax
addl $0x2, %eax
jmp 0x2d2b
leaq 0x16b4(%rip), %rdi      # 0x412f
jmp 0x2d6b
cmpl $0x15, %edx
je 0x2afd
cmpl $0x18, %edx
jne 0x2d5b
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
movb %cl, 0x184b(%rbx)
jmp 0x2d28
cmpl $0x1e, %edx
je 0x2bc9
cmpl $0x29, %edx
jne 0x2d5b
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %ecx
leal (%rcx,%rcx,4), %ecx
movw %cx, 0x1824(%rbx)
jmp 0x2d28
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
andl $0xf, %r14d
andb %cl, 0x1814(%rbx,%r14)
jmp 0x2d28
movb 0x184a(%rbx), %cl
andl $0xf, %r14d
jmp 0x2c6c
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
movb %cl, 0x184a(%rbx)
jmp 0x2d28
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
shrl $0x4, %edx
cmpb 0x1814(%rbx,%rdx), %cl
seta 0x1823(%rbx)
movb 0x1814(%rbx,%rdx), %cl
subb %cl, 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %eax
leal (%rax,%rax,4), %ecx
leal (%rax,%rcx,8), %eax
shrl $0xc, %eax
movzwl 0x1824(%rbx), %ecx
movb %al, 0x814(%rbx,%rcx)
movzbl 0x1814(%rbx,%r14), %eax
imull $0xcd, %eax, %eax
shrl $0xb, %eax
leal (%rax,%rax,4), %ecx
leal (%rcx,%rcx,4), %ecx
addl %eax, %ecx
shrl $0x7, %ecx
andl $0x6, %ecx
leal (%rcx,%rcx,4), %ecx
subb %cl, %al
movzwl 0x1824(%rbx), %ecx
movb %al, 0x815(%rbx,%rcx)
movzbl 0x1814(%rbx,%r14), %eax
imull $0xcd, %eax, %ecx
shrl $0xa, %ecx
andl $-0x2, %ecx
leal (%rcx,%rcx,4), %ecx
subb %cl, %al
movzwl 0x1824(%rbx), %ecx
movb %al, 0x816(%rbx,%rcx)
jmp 0x2974
movzwl 0x1824(%rbx), %ecx
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %edx
addl %ecx, %edx
cmpl $0x1000, %edx           # imm = 0x1000
setae 0x1823(%rbx)
movzbl 0x1814(%rbx,%r14), %edx
addl %ecx, %edx
movw %dx, 0x1824(%rbx)
jmp 0x2d28
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
andl $0xf, %r14d
orb %cl, 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
andb $0x1, %cl
movb %cl, 0x1823(%rbx)
shrb 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
shrl $0x4, %edx
cmpb 0x1814(%rbx,%rdx), %cl
setb 0x1823(%rbx)
movb 0x1814(%rbx,%rdx), %cl
subb 0x1814(%rbx,%r14), %cl
movb %cl, 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xf, %r14d
leaq 0x1814(%rbx), %rax
movq %r14, %rcx
incq %rcx
movb (%rax), %dl
movzwl 0x1824(%rbx), %esi
movb %dl, -0x1000(%rax,%rsi)
incq %rax
decq %rcx
jne 0x2c8a
movzwl 0x1824(%rbx), %eax
addw %r14w, %ax
incl %eax
movw %ax, 0x1824(%rbx)
jmp 0x2974
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
andl $0xf, %r14d
xorb %cl, 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %ecx
shrl $0x4, %edx
movzbl 0x1814(%rbx,%rdx), %esi
addl %ecx, %esi
cmpl $0x100, %esi            # imm = 0x100
setae 0x1823(%rbx)
movb 0x1814(%rbx,%rdx), %cl
addb %cl, 0x1814(%rbx,%r14)
jmp 0x2d28
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
shrb $0x7, %cl
movb %cl, 0x1823(%rbx)
shlb 0x1814(%rbx,%r14)
addl $0x2, %eax
movw %ax, 0x1826(%rbx)
movb 0x184a(%rbx), %al
testb %al, %al
je 0x2d44
decb %al
movb %al, 0x184a(%rbx)
movb 0x184b(%rbx), %al
cmpb $0x1, %al
jne 0x2d56
decb %al
movb %al, 0x184b(%rbx)
popq %rbx
popq %r14
popq %rbp
retq
leaq 0x142a(%rip), %rdi      # 0x418c
jmp 0x2d6b
leaq 0x13e3(%rip), %rdi      # 0x414e
xorl %eax, %eax
callq 0x2070
jmp 0x2d32