// File: /workspace/github_cpp_makefile_star2_O3/donfreiday[P]gb/main.cpp
// Function: main
int main(int argc, char** argv) {
  // Initialize emulator components
  g_gpu.mmu = &g_cpu.mmu;
  g_cpu.mmu.joypad = &g_joypad;
  g_gpu.reset();

  // Load ROM
#ifdef __EMSCRIPTEN__
  if (!g_cpu.mmu.load("roms/tetris.gb")) {
    printf("Invalid ROM file: roms/tetris.gb\n");
    return -1;
  }
#else
  if (argc < 2) {
    printf("Please specify a ROM file.\n");
    return -1;
  }
  if (!g_cpu.mmu.load(argv[1])) {
    printf("Invalid ROM file: %s\n", argv[1]);
    return -1;
  }
#endif

  // Setup SDL
  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    printf("SDL Error: %s\n", SDL_GetError());
    return -1;
  }

  // Setup MAX_CONTROLLERS game controllers
  // MAX_CONTROLLERS is (defined in common.hpp)
  SDL_GameController* controllers[MAX_CONTROLLERS];

  // Initialize array of controllers with nullptrs
  for (int i = 0; i < MAX_CONTROLLERS; i++) {
    controllers[i] = nullptr;
  }

  // Open game controllers
  if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) {
    printf("SDL Error initializing gamepad support: %s\n", SDL_GetError());
  } else {
    int maxJoysticks = SDL_NumJoysticks();
    int controllerID = 0;
    for (int i = 0; i < maxJoysticks && controllerID < MAX_CONTROLLERS; ++i) {
      if (SDL_IsGameController(i)) {
        controllers[i] = SDL_GameControllerOpen(i);
        controllerID++;
      }
    }
  }

  // Setup window
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
  SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
  SDL_DisplayMode current;
  SDL_GetCurrentDisplayMode(0, &current);
  g_window = SDL_CreateWindow("gb: A Gameboy Emulator", SDL_WINDOWPOS_CENTERED,
                              SDL_WINDOWPOS_CENTERED, 1024, 768,
                              SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
  SDL_GLContext glcontext = SDL_GL_CreateContext(g_window);

  // Setup ImGui binding
  ImGui_ImplSdl_Init(g_window);

  // Generate a new texture and store the handle
  glGenTextures(1, &g_lcdTexture);

  // These settings stick with the texture that's bound. Only need to set them
  // once.
  glBindTexture(GL_TEXTURE_2D, g_lcdTexture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  // allocate memory on the graphics card for the texture. It's fine if
  // texture_data doesn't have any data in it, the texture will just appear
  // black until you update it.
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, g_gpu.width, g_gpu.height, 0, GL_RGB,
               GL_UNSIGNED_BYTE, g_gpu.screenData);

  // Main loop
#ifdef __EMSCRIPTEN__
  emscripten_set_main_loop(main_loop, 0, 1);
#else
  while (!g_quit) {
    main_loop();
  }
#endif

  // Cleanup
  // When you're done using the texture, delete it. This will set texname to 0 &
  // delete all of the graphics card memory associated with the texture. If you
  // don't call this method, the texture will stay in graphics card memory until
  // you close the application.
  glDeleteTextures(1, &g_lcdTexture);

  // Close game controllers
  for (int i = 0; i < MAX_CONTROLLERS; ++i) {
    if (controllers[i]) {
      SDL_GameControllerClose(controllers[i]);
    }
  }

  ImGui_ImplSdl_Shutdown();
  SDL_GL_DeleteContext(glcontext);
  SDL_DestroyWindow(g_window);
  SDL_Quit();

  return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %rbx
subq $0x48, %rsp
movq %rsi, %rbx
movl %edi, %r14d
leaq 0x24337(%rip), %rax     # 0x5fe10
movq %rax, 0x265c0(%rip)     # 0x620a0
leaq 0x265b9(%rip), %rdi     # 0x620a0
leaq 0x373d2(%rip), %rax     # 0x72ec0
movq %rax, 0x2438b(%rip)     # 0x5fe80
callq 0x37fe0
cmpl $0x1, %r14d
jg 0x3bb11
leaq 0xe30a(%rip), %rdi      # 0x49e11
callq 0x7750
jmp 0x3bbaa
movq 0x8(%rbx), %rsi
leaq 0x242f4(%rip), %rdi     # 0x5fe10
callq 0x3787a
testb %al, %al
je 0x3bb87
movl $0x20, %edi
callq 0x78c0
testl %eax, %eax
js 0x3bb94
xorps %xmm0, %xmm0
movaps %xmm0, -0x30(%rbp)
movaps %xmm0, -0x40(%rbp)
movl $0x2000, %edi           # imm = 0x2000
callq 0x73e0
testl %eax, %eax
js 0x3bbba
callq 0x7410
testl %eax, %eax
jle 0x3bbd0
movl %eax, %r14d
xorl %ebx, %ebx
xorl %r15d, %r15d
movl %ebx, %edi
callq 0x7880
testl %eax, %eax
je 0x3bb77
movl %ebx, %edi
callq 0x7560
movq %rax, -0x40(%rbp,%rbx,8)
incl %r15d
incq %rbx
cmpq %r14, %rbx
jae 0x3bbd0
cmpl $0x4, %r15d
jl 0x3bb5d
jmp 0x3bbd0
movq 0x8(%rbx), %rsi
leaq 0xe056(%rip), %rdi      # 0x49be8
jmp 0x3bba3
callq 0x77b0
leaq 0xe05e(%rip), %rdi      # 0x49bfe
movq %rax, %rsi
xorl %eax, %eax
callq 0x7070
movl $0xffffffff, %eax       # imm = 0xFFFFFFFF
addq $0x48, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
callq 0x77b0
leaq 0xe047(%rip), %rdi      # 0x49c0d
movq %rax, %rsi
xorl %eax, %eax
callq 0x7070
movl $0x5, %edi
movl $0x1, %esi
callq 0x7790
movl $0x6, %edi
movl $0x18, %esi
callq 0x7790
movl $0x7, %edi
movl $0x8, %esi
callq 0x7790
movl $0x11, %edi
movl $0x2, %esi
callq 0x7790
movl $0x12, %edi
movl $0x2, %esi
callq 0x7790
leaq -0x58(%rbp), %rsi
xorl %edi, %edi
callq 0x75d0
leaq 0xe00c(%rip), %rdi      # 0x49c39
movl $0x2fff0000, %esi       # imm = 0x2FFF0000
movl $0x2fff0000, %edx       # imm = 0x2FFF0000
movl $0x400, %ecx            # imm = 0x400
movl $0x300, %r8d            # imm = 0x300
movl $0x22, %r9d
callq 0x7310
movq %rax, 0x37304(%rip)     # 0x72f58
movq %rax, %rdi
callq 0x7520
movq %rax, %rbx
movq 0x372f2(%rip), %rdi     # 0x72f58
callq 0x37410
leaq 0x372ee(%rip), %rsi     # 0x72f60
movl $0x1, %edi
callq 0x72c0
movl 0x372de(%rip), %esi     # 0x72f60
movl $0xde1, %edi            # imm = 0xDE1
callq 0x7890
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2801, %esi           # imm = 0x2801
movl $0x2600, %edx           # imm = 0x2600
callq 0x7580
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2800, %esi           # imm = 0x2800
movl $0x2600, %edx           # imm = 0x2600
callq 0x7580
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2802, %esi           # imm = 0x2802
movl $0x812f, %edx           # imm = 0x812F
callq 0x7580
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2803, %esi           # imm = 0x2803
movl $0x812f, %edx           # imm = 0x812F
callq 0x7580
movl 0x263d2(%rip), %ecx     # 0x620b4
movl 0x263cf(%rip), %r8d     # 0x620b8
subq $0x8, %rsp
leaq 0x263c9(%rip), %rax     # 0x620bd
movl $0xde1, %edi            # imm = 0xDE1
xorl %esi, %esi
movl $0x1907, %edx           # imm = 0x1907
xorl %r9d, %r9d
pushq %rax
pushq $0x1401                 # imm = 0x1401
pushq $0x1907                 # imm = 0x1907
callq 0x77f0
addq $0x20, %rsp
cmpb $0x0, 0x37232(%rip)     # 0x72f50
jne 0x3bd2e
callq 0x3af54
cmpb $0x1, 0x37224(%rip)     # 0x72f50
jne 0x3bd20
leaq 0x3722b(%rip), %rsi     # 0x72f60
movl $0x1, %edi
callq 0x77a0
xorl %r14d, %r14d
movq -0x40(%rbp,%r14,8), %rdi
testq %rdi, %rdi
je 0x3bd51
callq 0x7270
incq %r14
cmpq $0x4, %r14
jne 0x3bd42
callq 0x3749c
movq %rbx, %rdi
callq 0x70a0
movq 0x371ea(%rip), %rdi     # 0x72f58
callq 0x7460
callq 0x7240
xorl %eax, %eax
jmp 0x3bbaf