// File: khedoros[P]khednes/apu.cpp
// Function: apu::gen_audio(unsigned short)
void apu::gen_audio(uint16_t to_gen) { //To be run once per frame, to generate audio
    //cout<<"Gen_audio"<<endl;
    write_pos = !write_pos;
    //assert(to_gen == SAMPLES_PER_FRAME);
    //std::cout<<"apu::gen_audio("<<to_gen<<")"<<std::endl;
    uint16_t reg_audio_sample = SAMPLES_PER_FRAME + 20;
    int write_cycle = 0;
    int write_frame = 0;
    if(writes.size() > 0) { //If there are reg writes left on the queue, find the frame where the next one should apply
        //cout<<writes.size()<<" reg writes in queue"<<endl;
        write_frame = writes.front().first.first;
        //frame = write_frame;
        write_cycle = writes.front().first.second; //Write cycle is in terms of PPU cycle
        reg_audio_sample = uint16_t(double(write_cycle) * double(SAMPLES_PER_FRAME) / double(CLK_PER_FRAME)); //Convert it to which sample the change applies to
        //if(writes.size() > 50) {
            //cout<<"writes.size() = "<<writes.size()<<" next reg at sample: "<<reg_audio_sample<<endl;
        //}
    }

    for(int i = 0; i < SAMPLES_PER_FRAME; ++i) {
        //cout<<"Sample "<<i<<endl;
        //clock the 60Hz, 120Hz, and 240Hz things
        if(i == SAMPLES_PER_FRAME / 4) {
            clock_quarter();
        }
        else if(i == SAMPLES_PER_FRAME / 2) {
            clock_quarter();
            clock_half();
        }
        else if(i == (SAMPLES_PER_FRAME * 3) / 4) {
            clock_quarter();
        }
        else if(i == (SAMPLES_PER_FRAME - 1)) {
            clock_quarter();
            clock_half();
        }

        //Clock the things that need updated *every* cycle (but we've got to emulate that, since this loop only covers every sample)
        clock_every();

        while(writes.size() > 0 && ((write_frame == frame && reg_audio_sample <= i) || write_frame < frame)) { //Apply Register writes that go here
            //cout<<"Woot, applying a reg write with "<<writes.size()<<"writes remaining"<<endl;
            reg_dequeue();
            if(writes.size() > 0) {
                write_frame = writes.front().first.first;
                write_cycle = writes.front().first.second;
                reg_audio_sample = uint16_t(double(write_cycle) * double(SAMPLES_PER_FRAME) / double(CLK_PER_FRAME));
            }
            else {
                reg_audio_sample = SAMPLES_PER_FRAME + 20; //i.e. don't trigger a dequeue again
            }
        }
        char accum = 0;
        for(int voice = 0; voice < 4; ++voice) {
            //cout<<"Doing voice "<<voice<<endl;
            //Get sample value for each voice, put into appropriate buffer location
            if(enabled[voice] && length[voice] != 0 && (voice != TRI || !enable_linear_cntr || linear_cntr != 0)) {
                //cout<<"getting sample =)"<<endl;
                accum += get_sample(voice);
            }
            //else
            //    cout<<"Not getting sample for voice "<<voice<<endl;
        }
        //if(int(accum) + pcm_data > 255) cout<<"Too big: accum="<<int(accum)<<", pcm="<<pcm_data<<endl;
        //if(pcm_data != 0) cout<<"pcm="<<pcm_data<<endl;
        //cout<<"first: "<<dat.first<<"loc: "<<dat.first*SAMPLES_PER_FRAME<<endl;
        dat.buffer[i+(buffer_write_pos * SAMPLES_PER_FRAME)] = accum + dmc_pcm_data/4;

    }
    //cout<<hex<<val.first<<": 0x"<<val.second.first<<" val: 0x"<<val.second.second<<endl;
    //frame++;
    //if(write_pos) write_pos = 0;
    //else write_pos = 1;
    //cout<<"End of gen_audio"<<endl;
    dat.buffered+=SAMPLES_PER_FRAME;
    buffer_write_pos++;
    buffer_write_pos %= obtained.samples;
    //cout<<"Generate 735"<<endl;
    //cout<<"Write: Frame: "<<frame<<" buffer index: "<<buffer_write_pos<<" samples: "<<buffer_write_pos*SAMPLES_PER_FRAME<<" -> "<<(buffer_write_pos+1)*SAMPLES_PER_FRAME<<endl;
}

// Assembly:
subq $0x48, %rsp
movw %si, %ax
movq %rdi, 0x40(%rsp)
movw %ax, 0x3e(%rsp)
movq 0x40(%rsp), %rdi
movq %rdi, 0x20(%rsp)
movb (%rdi), %al
xorb $-0x1, %al
andb $0x1, %al
movb %al, (%rdi)
movw $0x2f3, 0x3c(%rsp)      # imm = 0x2F3
movl $0x0, 0x38(%rsp)
movl $0x0, 0x34(%rsp)
addq $0x50, %rdi
callq 0x24930
cmpq $0x0, %rax
jbe 0x236ea
movq 0x20(%rsp), %rdi
addq $0x50, %rdi
movq %rdi, 0x18(%rsp)
callq 0x24960
movq 0x18(%rsp), %rdi
movl (%rax), %eax
movl %eax, 0x34(%rsp)
callq 0x24960
movl 0x4(%rax), %eax
movl %eax, 0x38(%rsp)
cvtsi2sdl 0x38(%rsp), %xmm0
movsd 0x9677(%rip), %xmm1     # 0x2cd48
mulsd %xmm1, %xmm0
movsd 0x9673(%rip), %xmm1     # 0x2cd50
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
movw %ax, 0x3c(%rsp)
movl $0x0, 0x30(%rsp)
cmpl $0x2df, 0x30(%rsp)      # imm = 0x2DF
jge 0x23934
cmpl $0xb7, 0x30(%rsp)
jne 0x23716
movq 0x20(%rsp), %rdi
callq 0x23000
jmp 0x23770
cmpl $0x16f, 0x30(%rsp)      # imm = 0x16F
jne 0x23736
movq 0x20(%rsp), %rdi
callq 0x23000
movq 0x20(%rsp), %rdi
callq 0x231a0
jmp 0x2376e
cmpl $0x227, 0x30(%rsp)      # imm = 0x227
jne 0x2374c
movq 0x20(%rsp), %rdi
callq 0x23000
jmp 0x2376c
cmpl $0x2de, 0x30(%rsp)      # imm = 0x2DE
jne 0x2376a
movq 0x20(%rsp), %rdi
callq 0x23000
movq 0x20(%rsp), %rdi
callq 0x231a0
jmp 0x2376c
jmp 0x2376e
jmp 0x23770
movq 0x20(%rsp), %rdi
callq 0x233c0
movq 0x20(%rsp), %rdi
addq $0x50, %rdi
callq 0x24930
movq %rax, %rcx
xorl %eax, %eax
cmpq $0x0, %rcx
movb %al, 0x17(%rsp)
jbe 0x237d7
movq 0x20(%rsp), %rcx
movl 0x34(%rsp), %eax
cmpl 0x244(%rcx), %eax
jne 0x237b9
movzwl 0x3c(%rsp), %ecx
movb $0x1, %al
cmpl 0x30(%rsp), %ecx
movb %al, 0x16(%rsp)
jle 0x237cf
movq 0x20(%rsp), %rcx
movl 0x34(%rsp), %eax
cmpl 0x244(%rcx), %eax
setl %al
movb %al, 0x16(%rsp)
movb 0x16(%rsp), %al
movb %al, 0x17(%rsp)
movb 0x17(%rsp), %al
testb $0x1, %al
jne 0x237e1
jmp 0x2385e
movq 0x20(%rsp), %rdi
callq 0x23970
movq 0x20(%rsp), %rdi
addq $0x50, %rdi
callq 0x24930
cmpq $0x0, %rax
jbe 0x23852
movq 0x20(%rsp), %rdi
addq $0x50, %rdi
movq %rdi, 0x8(%rsp)
callq 0x24960
movq 0x8(%rsp), %rdi
movl (%rax), %eax
movl %eax, 0x34(%rsp)
callq 0x24960
movl 0x4(%rax), %eax
movl %eax, 0x38(%rsp)
cvtsi2sdl 0x38(%rsp), %xmm0
movsd 0x9511(%rip), %xmm1     # 0x2cd48
mulsd %xmm1, %xmm0
movsd 0x950d(%rip), %xmm1     # 0x2cd50
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
movw %ax, 0x3c(%rsp)
jmp 0x23859
movw $0x2f3, 0x3c(%rsp)      # imm = 0x2F3
jmp 0x2377a
movb $0x0, 0x2f(%rsp)
movl $0x0, 0x28(%rsp)
cmpl $0x4, 0x28(%rsp)
jge 0x238e7
movq 0x20(%rsp), %rax
movslq 0x28(%rsp), %rcx
testb $0x1, 0xb4(%rax,%rcx)
je 0x238d8
movq 0x20(%rsp), %rax
movslq 0x28(%rsp), %rcx
cmpl $0x0, 0x174(%rax,%rcx,4)
je 0x238d8
cmpl $0x2, 0x28(%rsp)
jne 0x238bd
movq 0x20(%rsp), %rax
testb $0x1, 0x11c(%rax)
je 0x238bd
movq 0x20(%rsp), %rax
cmpl $0x0, 0x118(%rax)
je 0x238d8
movq 0x20(%rsp), %rdi
movl 0x28(%rsp), %esi
callq 0x24120
movl %eax, %ecx
movsbl 0x2f(%rsp), %eax
addl %ecx, %eax
movb %al, 0x2f(%rsp)
jmp 0x238da
movl 0x28(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x28(%rsp)
jmp 0x2386b
movq 0x20(%rsp), %rsi
movsbl 0x2f(%rsp), %eax
movl %eax, 0x4(%rsp)
movl 0x220(%rsi), %eax
movl $0x4, %ecx
cltd
idivl %ecx
movl %eax, %ecx
movl 0x4(%rsp), %eax
addl %ecx, %eax
movb %al, %dl
movq 0x18(%rsi), %rax
movslq 0x30(%rsp), %rcx
imulq $0x2df, 0x8(%rsi), %rsi # imm = 0x2DF
addq %rsi, %rcx
movb %dl, (%rax,%rcx)
movl 0x30(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x30(%rsp)
jmp 0x236f2
movq 0x20(%rsp), %rax
movl 0x2c(%rax), %ecx
addl $0x2df, %ecx            # imm = 0x2DF
movl %ecx, 0x2c(%rax)
movq 0x8(%rax), %rcx
addq $0x1, %rcx
movq %rcx, 0x8(%rax)
movzwl 0x98(%rax), %ecx
movq 0x8(%rax), %rax
xorl %edx, %edx
divq %rcx
movq 0x20(%rsp), %rax
movq %rdx, 0x8(%rax)
addq $0x48, %rsp
retq
nop