// File: khedoros[P]khednes/apu.cpp
// Function: apu::clock_every()
void apu::clock_every() {
    if(!enabled[DMC]||!dmc_length) {
        //cout<<"returning"<<endl;
        return;
    }

    //CLK_PER_FRAME is the *pixel clock* which runs 3x faster than the CPU clock, which is where the *3 comes from.
    const int clocks_per_sample = int(double(CLK_PER_FRAME) / double(SAMPLES_PER_FRAME * 3));
    int accum = 0;       //Generating either 1 or 2 samples, so this is where I'm collecting them
    int samples = 0;     //Number of samples generated, so that I can divide accum by the right number

    //"clock" represents a CPU clock. The goal is to generate samples faster than incrementing/decrementing the PCM value by 1 at a time
    for(int clock = 0; clock < clocks_per_sample;) {
        samples++;
        int sign = ((dmc_cur_byte & (1<<(dmc_bit))) > 0)?2:-2;

        //Either run up to the wavelength count, or the number of CPU cycles we need to simulate, whichever is less
        //If we still need to simulate more cycles, that'll happen in the next loop iteration.
        int increment = (dmc_freq_cnt > (clocks_per_sample - clock)) ? (clocks_per_sample - clock) : dmc_freq_cnt;
        dmc_pcm_data += (increment*sign);

        //The PCM data doesn't wrap like an X86 register, it "saturates" to stay in the range [0,127]
        if(dmc_pcm_data < 0) dmc_pcm_data = 0;
        else if(dmc_pcm_data > 127) dmc_pcm_data = 127;

        accum +=dmc_pcm_data;

        dmc_freq_cnt-=increment;
        clock += increment;

        //Clock the DMC registers if we're at the end of the wavelength counter
        if(!dmc_freq_cnt) {
            dmc_freq_cnt = dmc_freq;
            dmc_bit++;
            dmc_bit%=8;
            if(!dmc_bit) {
                //TODO: Fix so that it ends when the length runs out, but also wraps around to 0x8000 if it doesn't
                dmc_addr++;
                if(dmc_addr==dmc_addr_reset+dmc_length && !dmc_loop) {
                    dmc_addr = dmc_addr_reset;
                    enabled[DMC] = false;
                    return;
                }
                else if (dmc_addr==dmc_addr_reset+dmc_length) {
                    dmc_addr = dmc_addr_reset;
                    dmc_bit = 0;
                    dmc_freq_cnt = dmc_freq;
                }
                //Grab the next byte both if we're not done going through samples and if we're done, but looping.
                dmc_cur_byte = memi->read(dmc_addr);
            }
        }
    }
    dmc_pcm_data = accum/samples;
    //cout<<"Final data: "<<dmc_pcm_data<<endl;
}

// Assembly:
subq $0x38, %rsp
movq %rdi, 0x30(%rsp)
movq 0x30(%rsp), %rax
movq %rax, 0x10(%rsp)
testb $0x1, 0xb8(%rax)
je 0x233ea
movq 0x10(%rsp), %rax
cmpl $0x0, 0x238(%rax)
jne 0x233ef
jmp 0x2363c
movl $0x28, 0x2c(%rsp)
movl $0x0, 0x28(%rsp)
movl $0x0, 0x24(%rsp)
movl $0x0, 0x20(%rsp)
cmpl $0x28, 0x20(%rsp)
jge 0x23626
movq 0x10(%rsp), %rax
movl 0x24(%rsp), %ecx
addl $0x1, %ecx
movl %ecx, 0x24(%rsp)
movl 0x240(%rax), %esi
movl 0x23c(%rax), %ecx
movl $0x1, %edx
shll %cl, %edx
movl %edx, %ecx
andl %ecx, %esi
movl $0xfffffffe, %ecx       # imm = 0xFFFFFFFE
movl $0x2, %edx
cmpl $0x0, %esi
cmovgl %edx, %ecx
movl %ecx, 0x1c(%rsp)
movl 0x22c(%rax), %eax
movl $0x28, %ecx
subl 0x20(%rsp), %ecx
cmpl %ecx, %eax
jle 0x23477
movl $0x28, %eax
subl 0x20(%rsp), %eax
movl %eax, 0xc(%rsp)
jmp 0x23486
movq 0x10(%rsp), %rax
movl 0x22c(%rax), %eax
movl %eax, 0xc(%rsp)
movq 0x10(%rsp), %rax
movl 0xc(%rsp), %ecx
movl %ecx, 0x18(%rsp)
movl 0x18(%rsp), %ecx
imull 0x1c(%rsp), %ecx
addl 0x220(%rax), %ecx
movl %ecx, 0x220(%rax)
cmpl $0x0, 0x220(%rax)
jge 0x234c2
movq 0x10(%rsp), %rax
movl $0x0, 0x220(%rax)
jmp 0x234e1
movq 0x10(%rsp), %rax
cmpl $0x7f, 0x220(%rax)
jle 0x234df
movq 0x10(%rsp), %rax
movl $0x7f, 0x220(%rax)
jmp 0x234e1
movq 0x10(%rsp), %rax
movl 0x220(%rax), %ecx
addl 0x28(%rsp), %ecx
movl %ecx, 0x28(%rsp)
movl 0x18(%rsp), %edx
movl 0x22c(%rax), %ecx
subl %edx, %ecx
movl %ecx, 0x22c(%rax)
movl 0x18(%rsp), %ecx
addl 0x20(%rsp), %ecx
movl %ecx, 0x20(%rsp)
cmpl $0x0, 0x22c(%rax)
jne 0x23621
movq 0x10(%rsp), %rax
movl 0x230(%rax), %ecx
movl %ecx, 0x22c(%rax)
movl 0x23c(%rax), %ecx
addl $0x1, %ecx
movl %ecx, 0x23c(%rax)
movl 0x23c(%rax), %eax
movl $0x8, %ecx
cltd
idivl %ecx
movq 0x10(%rsp), %rax
movl %edx, 0x23c(%rax)
cmpl $0x0, 0x23c(%rax)
jne 0x2361f
movq 0x10(%rsp), %rdx
movl 0x224(%rdx), %eax
addl $0x1, %eax
movl %eax, 0x224(%rdx)
movl 0x224(%rdx), %eax
movl 0x228(%rdx), %ecx
addl 0x238(%rdx), %ecx
cmpl %ecx, %eax
jne 0x235ba
movq 0x10(%rsp), %rax
testb $0x1, 0x235(%rax)
jne 0x235ba
movq 0x10(%rsp), %rax
movl 0x228(%rax), %ecx
movl %ecx, 0x224(%rax)
movb $0x0, 0xb8(%rax)
jmp 0x2363c
movq 0x10(%rsp), %rdx
movl 0x224(%rdx), %eax
movl 0x228(%rdx), %ecx
addl 0x238(%rdx), %ecx
cmpl %ecx, %eax
jne 0x235fc
movq 0x10(%rsp), %rax
movl 0x228(%rax), %ecx
movl %ecx, 0x224(%rax)
movl $0x0, 0x23c(%rax)
movl 0x230(%rax), %ecx
movl %ecx, 0x22c(%rax)
jmp 0x235fe
movq 0x10(%rsp), %rax
movq 0x30(%rax), %rdi
movl 0x224(%rax), %esi
callq 0x14460
movl %eax, %ecx
movq 0x10(%rsp), %rax
movl %ecx, 0x240(%rax)
jmp 0x23621
jmp 0x2340f
movl 0x28(%rsp), %eax
cltd
idivl 0x24(%rsp)
movl %eax, %ecx
movq 0x10(%rsp), %rax
movl %ecx, 0x220(%rax)
addq $0x38, %rsp
retq
nopw %cs:(%rax,%rax)