// File: /workspace/github_cpp_makefile_star2_O2/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 0x1956(%rip), %rdi      # 0x4020
movslq (%rdi,%rcx,4), %rcx
addq %rdi, %rcx
jmpq *%rcx
andl $0xf, %edx
cmpl $0xe, %edx
je 0x29ad
testl %edx, %edx
jne 0x29d0
pushq $0x1
popq %rax
cmpq $0x801, %rax            # imm = 0x801
je 0x291e
movb $0x0, (%rbx,%rax)
incq %rax
jmp 0x26ea
cmpl $0xa1, %edx
je 0x297e
cmpl $0x9e, %edx
jne 0x29a1
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %ecx
cmpb $0x0, 0x801(%rbx,%rcx)
jne 0x2999
jmp 0x2c9d
andl $0xfff, %esi            # imm = 0xFFF
movzbl 0x1814(%rbx), %eax
addl %esi, %eax
jmp 0x2ca0
andl $0xf, %r14d
cmpb %bpl, 0x1814(%rbx,%r14)
jne 0x2999
jmp 0x2c9d
callq 0x20a0
movl $0xff, %ecx
cltd
idivl %ecx
andb %dl, %bpl
andl $0xf, %r14d
movb %bpl, 0x1814(%rbx,%r14)
jmp 0x2921
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
shrl $0x4, %edx
cmpb 0x1814(%rbx,%rdx), %cl
jne 0x2999
jmp 0x2c9d
movzwl 0x1848(%rbx), %ecx
movw %ax, 0x1828(%rbx,%rcx,2)
incw 0x1848(%rbx)
andl $0xfff, %esi            # imm = 0xFFF
movw %si, 0x1826(%rbx)
jmp 0x2ca7
cmpl $0x7, %edx
je 0x2a95
cmpl $0xa, %edx
je 0x2bfc
cmpl $0x15, %edx
je 0x2b6f
cmpl $0x18, %edx
je 0x29dc
cmpl $0x1e, %edx
je 0x2a49
cmpl $0x29, %edx
je 0x2b53
cmpl $0x33, %edx
je 0x2aa4
cmpl $0x55, %edx
je 0x29f3
cmpl $0x65, %edx
jne 0x2cd7
andl $0xf, %r14d
leaq 0x1814(%rbx), %rax
movq %r14, %rcx
incq %rcx
movzwl 0x1824(%rbx), %edx
subq $0x1, %rcx
jb 0x2a83
movb -0x1000(%rax,%rdx), %dl
movb %dl, (%rax)
incq %rax
jmp 0x282e
andl $0xf, %r14d
cmpb %bpl, 0x1814(%rbx,%r14)
je 0x2999
jmp 0x2c9d
andl $0xf, %r14d
addb %bpl, 0x1814(%rbx,%r14)
jmp 0x2c9d
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
shrl $0x4, %edx
cmpb 0x1814(%rbx,%rdx), %cl
je 0x2999
jmp 0x2c9d
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %eax
movl %edx, %ecx
shrl $0x4, %ecx
movzbl 0x1814(%rbx,%rcx), %ecx
andl $0xf, %edx
movb $0x0, 0x1823(%rbx)
shll $0x6, %ecx
addq %rax, %rcx
leaq (%rbx,%rcx), %rax
incq %rax
xorl %esi, %esi
cmpq %rdx, %rsi
je 0x291e
movzwl 0x1824(%rbx), %ecx
addq %rsi, %rcx
movzbl 0x814(%rbx,%rcx), %edi
xorl %ecx, %ecx
cmpq $0x8, %rcx
je 0x2915
movl %edi, %r8d
shll %cl, %r8d
testb %r8b, %r8b
jns 0x2910
movb (%rax,%rcx), %r8b
cmpb $0x1, %r8b
jne 0x2908
movb $0x1, 0x1823(%rbx)
movb (%rax,%rcx), %r8b
xorb $0x1, %r8b
movb %r8b, (%rax,%rcx)
incq %rcx
jmp 0x28e2
incq %rsi
addq $0x40, %rax
jmp 0x28c9
movb $0x1, (%rbx)
addw $0x2, 0x1826(%rbx)
jmp 0x2ca7
andl $0xf, %r14d
movb %bpl, 0x1814(%rbx,%r14)
jmp 0x2c9d
andl $0xfff, %esi            # imm = 0xFFF
movw %si, 0x1824(%rbx)
jmp 0x2c9d
movl %edx, %ecx
andl $0xf, %ecx
cmpl $0xe, %ecx
ja 0x2cce
leaq 0x16fa(%rip), %rdi      # 0x4060
movslq (%rdi,%rcx,4), %rcx
addq %rdi, %rcx
jmpq *%rcx
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
jmp 0x2a9b
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %ecx
cmpb $0x0, 0x801(%rbx,%rcx)
jne 0x2c9d
addl $0x4, %eax
jmp 0x2ca0
leaq 0x17e1(%rip), %rdi      # 0x4189
jmp 0x2cde
movzwl 0x1848(%rbx), %eax
decl %eax
movw %ax, 0x1848(%rbx)
movzwl %ax, %eax
movzwl 0x1828(%rbx,%rax,2), %eax
addl $0x2, %eax
jmp 0x2ca0
leaq 0x1774(%rip), %rdi      # 0x414b
jmp 0x2cde
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
movb %cl, 0x184b(%rbx)
jmp 0x2c9d
andl $0xf, %r14d
leaq 0x1814(%rbx), %rax
movq %r14, %rcx
incq %rcx
subq $0x1, %rcx
jb 0x2a1f
movb (%rax), %dl
movzwl 0x1824(%rbx), %esi
movb %dl, -0x1000(%rax,%rsi)
incq %rax
jmp 0x2a04
leal 0x1(%r14), %eax
movd 0x1824(%rbx), %xmm0
movdqa 0x15dd(%rip), %xmm1     # 0x4010
pinsrw $0x0, %eax, %xmm1
paddw %xmm0, %xmm1
movd %xmm1, 0x1824(%rbx)
jmp 0x2ca7
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 0x2c9d
leal (%r14,%rdx), %eax
incl %eax
movw %ax, 0x1824(%rbx)
jmp 0x2921
movb 0x184a(%rbx), %cl
andl $0xf, %r14d
jmp 0x2bef
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %eax
movb $0x64, %cl
divb %cl
movzwl 0x1824(%rbx), %ecx
movb %al, 0x814(%rbx,%rcx)
movzbl 0x1814(%rbx,%r14), %eax
movb $0xa, %cl
divb %cl
movzbl %al, %eax
divb %cl
movzbl %ah, %eax
movzwl 0x1824(%rbx), %edx
movb %al, 0x815(%rbx,%rdx)
movzbl 0x1814(%rbx,%r14), %eax
divb %cl
movzbl %ah, %eax
movzwl 0x1824(%rbx), %ecx
movb %al, 0x816(%rbx,%rcx)
jmp 0x2921
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
andl $0xf, %r14d
andb %cl, 0x1814(%rbx,%r14)
jmp 0x2c9d
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 0x2c9d
andl $0xf, %r14d
movzbl 0x1814(%rbx,%r14), %ecx
leal (%rcx,%rcx,4), %ecx
movw %cx, 0x1824(%rbx)
jmp 0x2c9d
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
movb %cl, 0x184a(%rbx)
jmp 0x2c9d
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
andl $0xf, %r14d
orb %cl, 0x1814(%rbx,%r14)
jmp 0x2c9d
andl $0xf, %r14d
movb 0x1814(%rbx,%r14), %cl
andb $0x1, %cl
movb %cl, 0x1823(%rbx)
shrb 0x1814(%rbx,%r14)
jmp 0x2c9d
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 0x2c9d
andl $0xf, %r14d
xorl %ecx, %ecx
xorl %edx, %edx
cmpq $0x10, %rcx
je 0x2c26
movb 0x801(%rbx,%rcx), %sil
testb %sil, %sil
je 0x2c21
movb %sil, 0x1814(%rbx,%r14)
movb $0x1, %dl
incq %rcx
jmp 0x2c04
testb $0x1, %dl
jne 0x2c9d
jmp 0x2cc9
shrl $0x4, %edx
movb 0x1814(%rbx,%rdx), %cl
andl $0xf, %r14d
xorb %cl, 0x1814(%rbx,%r14)
jmp 0x2c9d
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 0x2c9d
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 0x2cb9
decb %al
movb %al, 0x184a(%rbx)
cmpb $0x1, 0x184b(%rbx)
jne 0x2cc9
movb $0x0, 0x184b(%rbx)
popq %rbx
popq %r14
popq %rbp
retq
leaq 0x1495(%rip), %rdi      # 0x416a
jmp 0x2cde
leaq 0x14ca(%rip), %rdi      # 0x41a8
xorl %eax, %eax
callq 0x2060
jmp 0x2ca7
nop