// File: khedoros[P]khednes/main.cpp
// Function: run_nsf(rom&, apu&, mem&, cpu&)
int run_nsf(rom& cart, apu& apui, mem& memi, cpu& cpui) {
    bool paused=false;
    int frame = 0;
    int choice = cart.get_default_song() + 1; //get_choice(cart);
    bool success = init_nsf(cart, memi, cpui, choice);
    if(!success) {
        cout<<"Failed to init nsf."<<endl;
        return 1;
    }
    SDL_PauseAudioDevice(apui.get_id(), false);
    int pstart = SDL_GetTicks();
    while (1==1) {
exit_poll_loop:
        SDL_Event event;
        while(SDL_PollEvent(&event)){  /* Loop until there are no events left on the queue */
            switch(event.type){  /* Process the appropiate event type */
            case SDL_KEYDOWN:  /* Handle a KEYDOWN event */         
                if(event.key.keysym.scancode==SDL_SCANCODE_Q||
                  (event.key.keysym.scancode==SDL_SCANCODE_C&&(event.key.keysym.mod==KMOD_RCTRL))||
                  (event.key.keysym.scancode==SDL_SCANCODE_C&&(event.key.keysym.mod==KMOD_LCTRL))) {
                    printf("You pressed q or ctrl-c. Exiting.\n");
                    cpui.print_details("");
                    //SDL_PauseAudio(true);
                    cout<<"Calling SDL_Quit()"<<endl;
                    SDL_Quit();
                    cout<<"Called SDL_Quit()"<<endl;
                    return 0;
                }
                else if(event.key.keysym.scancode==SDL_SCANCODE_A) {
                    choice--;
                    if(choice<1) choice = cart.get_song_count();
                    SDL_PauseAudioDevice(apui.get_id(), true);
                    init_nsf(cart, memi, cpui, choice);
                    SDL_PauseAudioDevice(apui.get_id(), false);
                    goto exit_poll_loop;
                }
                else if(event.key.keysym.scancode==SDL_SCANCODE_D) {
                    choice++;
                    if(choice > (int)cart.get_song_count()) choice = 1;
                    SDL_PauseAudioDevice(apui.get_id(), true);
                    init_nsf(cart, memi, cpui, choice);
                    SDL_PauseAudioDevice(apui.get_id(), false);
                    goto exit_poll_loop;
                }
                else if(event.key.keysym.scancode==SDL_SCANCODE_R) {
                    SDL_PauseAudioDevice(apui.get_id(), true);
                    init_nsf(cart,memi,cpui, choice);
                    SDL_PauseAudioDevice(apui.get_id(), false);
                    goto exit_poll_loop;
                }
                else if(event.key.keysym.scancode==SDL_SCANCODE_P) {
                    paused=!paused;
                    if(paused) {
                        printf("Paused emulation.\n");
                        SDL_PauseAudioDevice(apui.get_id(), true);
                    }
                    else {
                        printf("Unpaused emulation.\n");
                        SDL_PauseAudioDevice(apui.get_id(), false);
                    }
                }
                else {
                    //printf("Keydown\n");
                    //printf("Time: %d\n",SDL_GetTicks());
                }
                break;
            case SDL_KEYUP: /* Handle a KEYUP event*/
                //printf("Keyup\n");
                break;
            case SDL_QUIT:
                //SDL_PauseAudio(true);
                SDL_Quit();
                cpui.print_details("");
                return 0;
                break;
            default: /* Report an unhandled event */
                //printf("I don't know what this event is! Flushing it.\n");
                SDL_FlushEvent(event.type);
                break;
            }
        }
        if(!paused) {
            //cout<<"Running the CPU"<<endl;
            int cpu_ret = 1;
            int cycle_count = 0;
            bool reset = true;
            //cout<<"Frame "<<dec<<frame<<" start"<<endl;
            while(cpu_ret && cpu_ret > 0) {
                cpu_ret=cpui.run_next_op();
                cycle_count += (3*cpu_ret);
                cpui.set_ppu_cycle(cycle_count);
                if(cycle_count >= CLK_PER_FRAME) { 
                    cycle_count -= CLK_PER_FRAME;
                    reset = false;
                    break;
                }
            }
            //cout<<"Frame "<<dec<<frame<<" end"<<endl;
            if(reset) {
                cpui.reset(cart.get_nmi_addr());
            }

            //cout<<"Ran the CPU"<<endl;
            if(cpu_ret < 0) {
                return 1;
            }
            else {
                //cout<<"Frame "<<ppui.get_frame()<<endl;

                int apu_id = apui.get_id();
                if(apu_id > 0) {
                    //cout<<"Locking audio...";
                    SDL_LockAudioDevice(apu_id);
                    //cout<<"done."<<endl;
                    apui.set_frame(frame);
                    apui.gen_audio();
                    //apui.dat.buffered += 735; Dude, do this in gen_audio, not here =/
                    //cout<<"Unlocking audio...";
                    SDL_UnlockAudioDevice(apu_id);
                    //cout<<"done."<<endl;
                }
            }
        }

        //Calculates the time that the frame should end at, and delays until that time
        //cout<<"Timing two"<<endl;
        int now = SDL_GetTicks();
        frame++;
        cpui.increment_frame();
        int delay = pstart + int(double(frame) * double(1000) / double(60)) - now;
        //cout<<"Frame: "<<frames<<" now: "<<now<<" delay: "<<delay<<endl;       
        if(delay > 0) {
            SDL_Delay(delay);
        }
    }
}

// Assembly:
subq $0x128, %rsp            # imm = 0x128
movq %rdi, 0x118(%rsp)
movq %rsi, 0x110(%rsp)
movq %rdx, 0x108(%rsp)
movq %rcx, 0x100(%rsp)
movb $0x0, 0xff(%rsp)
movl $0x0, 0xf8(%rsp)
movq 0x118(%rsp), %rdi
callq 0x10fa0
addl $0x1, %eax
movl %eax, 0xf4(%rsp)
movq 0x118(%rsp), %rdi
movq 0x108(%rsp), %rsi
movq 0x100(%rsp), %rdx
movl 0xf4(%rsp), %ecx
callq 0xe880
andb $0x1, %al
movb %al, 0xf3(%rsp)
testb $0x1, 0xf3(%rsp)
jne 0xe15a
movq 0x28e81(%rip), %rdi     # 0x36fb0
leaq 0x1d2be(%rip), %rsi     # 0x2b3f4
callq 0x43c0
movq %rax, %rdi
movq 0x28e4b(%rip), %rsi     # 0x36f90
callq 0x4400
movl $0x1, 0x124(%rsp)
jmp 0xe7a0
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
xorl %esi, %esi
callq 0x44b0
callq 0x43f0
movl %eax, 0xec(%rsp)
jmp 0xe17e
jmp 0xe180
leaq 0xb0(%rsp), %rdi
callq 0x4230
cmpl $0x0, %eax
je 0xe603
movl 0xb0(%rsp), %eax
movl %eax, 0x38(%rsp)
subl $0x100, %eax            # imm = 0x100
je 0xe547
jmp 0xe1ae
movl 0x38(%rsp), %eax
subl $0x300, %eax            # imm = 0x300
je 0xe1cf
jmp 0xe1bb
movl 0x38(%rsp), %eax
subl $0x301, %eax            # imm = 0x301
je 0xe542
jmp 0xe5f2
cmpl $0x14, 0xc0(%rsp)
je 0xe211
cmpl $0x6, 0xc0(%rsp)
jne 0xe1f2
movzwl 0xc8(%rsp), %eax
cmpl $0x80, %eax
je 0xe211
cmpl $0x6, 0xc0(%rsp)
jne 0xe323
movzwl 0xc8(%rsp), %eax
cmpl $0x40, %eax
jne 0xe323
leaq 0x1d0bd(%rip), %rdi     # 0x2b2d5
xorl %eax, %eax
callq 0x4050
movq 0x100(%rsp), %rax
movq %rax, 0x28(%rsp)
leaq 0x8f(%rsp), %rdi
movq %rdi, 0x30(%rsp)
callq 0x4690
movq 0x30(%rsp), %rdx
leaq 0x1cf23(%rip), %rsi     # 0x2b16d
leaq 0x90(%rsp), %rdi
callq 0x4500
jmp 0xe259
movq 0x28(%rsp), %rdi
leaq 0x90(%rsp), %rsi
callq 0x169e0
jmp 0xe26d
leaq 0x90(%rsp), %rdi
callq 0x4240
leaq 0x8f(%rsp), %rdi
callq 0x4410
movq 0x28d22(%rip), %rdi     # 0x36fb0
leaq 0x1d08f(%rip), %rsi     # 0x2b324
callq 0x43c0
movq %rax, %rdi
movq 0x28cec(%rip), %rsi     # 0x36f90
callq 0x4400
callq 0x40a0
movq 0x28cfb(%rip), %rdi     # 0x36fb0
leaq 0x1d07b(%rip), %rsi     # 0x2b337
callq 0x43c0
movq %rax, %rdi
movq 0x28cc5(%rip), %rsi     # 0x36f90
callq 0x4400
movl $0x0, 0x124(%rsp)
jmp 0xe7a0
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x80(%rsp)
movl %eax, 0x7c(%rsp)
jmp 0xe311
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x80(%rsp)
movl %eax, 0x7c(%rsp)
leaq 0x90(%rsp), %rdi
callq 0x4240
leaq 0x8f(%rsp), %rdi
callq 0x4410
jmp 0xe7af
cmpl $0x4, 0xc0(%rsp)
jne 0xe3b8
movl 0xf4(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0xf4(%rsp)
cmpl $0x1, 0xf4(%rsp)
jge 0xe360
movq 0x118(%rsp), %rdi
callq 0x10f60
movl %eax, 0xf4(%rsp)
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
movl $0x1, %esi
callq 0x44b0
movq 0x118(%rsp), %rdi
movq 0x108(%rsp), %rsi
movq 0x100(%rsp), %rdx
movl 0xf4(%rsp), %ecx
callq 0xe880
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
xorl %esi, %esi
callq 0x44b0
jmp 0xe17e
cmpl $0x7, 0xc0(%rsp)
jne 0xe45c
movl 0xf4(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xf4(%rsp)
movl 0xf4(%rsp), %eax
movl %eax, 0x24(%rsp)
movq 0x118(%rsp), %rdi
callq 0x10f60
movl %eax, %ecx
movl 0x24(%rsp), %eax
cmpl %ecx, %eax
jle 0xe404
movl $0x1, 0xf4(%rsp)
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
movl $0x1, %esi
callq 0x44b0
movq 0x118(%rsp), %rdi
movq 0x108(%rsp), %rsi
movq 0x100(%rsp), %rdx
movl 0xf4(%rsp), %ecx
callq 0xe880
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
xorl %esi, %esi
callq 0x44b0
jmp 0xe17e
cmpl $0x15, 0xc0(%rsp)
jne 0xe4be
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
movl $0x1, %esi
callq 0x44b0
movq 0x118(%rsp), %rdi
movq 0x108(%rsp), %rsi
movq 0x100(%rsp), %rdx
movl 0xf4(%rsp), %ecx
callq 0xe880
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
xorl %esi, %esi
callq 0x44b0
jmp 0xe17e
cmpl $0x13, 0xc0(%rsp)
jne 0xe533
movb 0xff(%rsp), %al
xorb $-0x1, %al
andb $0x1, %al
movb %al, 0xff(%rsp)
testb $0x1, 0xff(%rsp)
je 0xe50d
leaq 0x1ce5e(%rip), %rdi     # 0x2b349
movb $0x0, %al
callq 0x4050
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
movl $0x1, %esi
callq 0x44b0
jmp 0xe531
leaq 0x1ce48(%rip), %rdi     # 0x2b35c
movb $0x0, %al
callq 0x4050
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, %edi
xorl %esi, %esi
callq 0x44b0
jmp 0xe535
jmp 0xe535
jmp 0xe537
jmp 0xe539
jmp 0xe53b
jmp 0xe53d
jmp 0xe5fe
jmp 0xe5fe
callq 0x40a0
movq 0x100(%rsp), %rax
movq %rax, 0x10(%rsp)
leaq 0x57(%rsp), %rdi
movq %rdi, 0x18(%rsp)
callq 0x4690
movq 0x18(%rsp), %rdx
leaq 0x1cbf9(%rip), %rsi     # 0x2b16d
leaq 0x58(%rsp), %rdi
callq 0x4500
jmp 0xe580
movq 0x10(%rsp), %rdi
leaq 0x58(%rsp), %rsi
callq 0x169e0
jmp 0xe591
leaq 0x58(%rsp), %rdi
callq 0x4240
leaq 0x57(%rsp), %rdi
callq 0x4410
movl $0x0, 0x124(%rsp)
jmp 0xe7a0
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x80(%rsp)
movl %eax, 0x7c(%rsp)
jmp 0xe5e3
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x80(%rsp)
movl %eax, 0x7c(%rsp)
leaq 0x58(%rsp), %rdi
callq 0x4240
leaq 0x57(%rsp), %rdi
callq 0x4410
jmp 0xe7af
movl 0xb0(%rsp), %edi
callq 0x46f0
jmp 0xe180
testb $0x1, 0xff(%rsp)
jne 0xe72e
movl $0x1, 0x50(%rsp)
movl $0x0, 0x4c(%rsp)
movb $0x1, 0x4b(%rsp)
xorl %eax, %eax
cmpl $0x0, 0x50(%rsp)
movb %al, 0xf(%rsp)
je 0xe63f
cmpl $0x0, 0x50(%rsp)
setg %al
movb %al, 0xf(%rsp)
movb 0xf(%rsp), %al
testb $0x1, %al
jne 0xe649
jmp 0xe698
movq 0x100(%rsp), %rdi
callq 0x173e0
movl %eax, 0x50(%rsp)
imull $0x3, 0x50(%rsp), %eax
addl 0x4c(%rsp), %eax
movl %eax, 0x4c(%rsp)
movq 0x100(%rsp), %rdi
movl 0x4c(%rsp), %esi
callq 0x19630
cmpl $0x15cfe, 0x4c(%rsp)    # imm = 0x15CFE
jl 0xe696
movl 0x4c(%rsp), %eax
subl $0x15cfe, %eax          # imm = 0x15CFE
movl %eax, 0x4c(%rsp)
movb $0x0, 0x4b(%rsp)
jmp 0xe698
jmp 0xe626
testb $0x1, 0x4b(%rsp)
je 0xe6c3
movq 0x100(%rsp), %rax
movq %rax, (%rsp)
movq 0x118(%rsp), %rdi
callq 0x110c0
movq (%rsp), %rdi
movl %eax, %esi
callq 0x195d0
cmpl $0x0, 0x50(%rsp)
jge 0xe6da
movl $0x1, 0x124(%rsp)
jmp 0xe7a0
movq 0x110(%rsp), %rdi
callq 0x222f0
movl %eax, 0x44(%rsp)
cmpl $0x0, 0x44(%rsp)
jle 0xe72a
movl 0x44(%rsp), %edi
callq 0x43a0
movq 0x110(%rsp), %rdi
movl 0xf8(%rsp), %esi
callq 0x22300
movq 0x110(%rsp), %rdi
movl $0x2df, %esi            # imm = 0x2DF
callq 0x23650
movl 0x44(%rsp), %edi
callq 0x4650
jmp 0xe72c
jmp 0xe72e
callq 0x43f0
movl %eax, 0x40(%rsp)
movl 0xf8(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xf8(%rsp)
movq 0x100(%rsp), %rdi
callq 0x19650
movl 0xec(%rsp), %eax
cvtsi2sdl 0xf8(%rsp), %xmm0
movsd 0x1c8a3(%rip), %xmm1    # 0x2b010
mulsd %xmm1, %xmm0
movsd 0x1c88f(%rip), %xmm1    # 0x2b008
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %ecx
addl %ecx, %eax
subl 0x40(%rsp), %eax
movl %eax, 0x3c(%rsp)
cmpl $0x0, 0x3c(%rsp)
jle 0xe79b
movl 0x3c(%rsp), %edi
callq 0x42d0
jmp 0xe17c
movl 0x124(%rsp), %eax
addq $0x128, %rsp            # imm = 0x128
retq
movq 0x80(%rsp), %rdi
callq 0x4680
nopl (%rax)