// File: RegulusImpact[P]sadboy/src/main.cpp
// Function: main
int main() {
    /* BLARGGS CPU INSTRUCTION TESTS */
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/01-special.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/02-interrupts.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/03-op sp,hl.gb"); // -- Passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/04-op r,imm.gb"); // -- Passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/05-op rp.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/06-ld r,r.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/07-jr,jp,call,ret,rst.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/08-misc instrs.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/09-op r,r.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/10-bit ops.gb"); // -- passed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/cpu_instrs/individual/11-op a,(hl).gb"); // -- passed

    /* BLARGGS TIMING TESTS */
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/interrupt_time/interrupt_time.gb"); // -- failed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/instr_timing/instr_timing.gb"); // -- failed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/mem_timing/mem_timing.gb"); // -- failed
    // Cartridge* cart = new Cartridge("submodules/gb-test-roms/mem_timing/individual/01-read_timing.gb");

    // The holy grail of tests :D
    // Cartridge* cart = new Cartridge("/home/regulus/github/sadboy/test/div_write.gb"); // -- failed
    // Cartridge* cart = new Cartridge("/home/regulus/github/sadboy/test/ie_push.gb"); // -- failed
    // Cartridge* cart = new Cartridge("/home/regulus/github/sadboy/test/rapid_toggle.gb"); // -- failed
    Cartridge* cart = new Cartridge("/home/regulus/github/java-gb/src/main/resources/tetris.gb"); // -- failed
    if (!cart->IsLoaded()) {
        std::cout << "Cartridge is not loaded." << std::endl;
        exit(1);
    }

    MMU* mmu = new MMU(cart);
    CPU* cpu = new CPU(mmu);
    InterruptService* is = new InterruptService(cpu, mmu);
    TimerService* ts = new TimerService(cpu, mmu, is);
    iGPU* gpu = new XGPU(mmu, 3);

    uint32_t counter = 0;
    uint32_t breakpointExtension = 0;
    int qq;

    while (true) {
        bool dispatcher = is->CheckInterrupts();

                ts->Increment();
        if (dispatcher) {
            is->Dispatch();
        } else {
            cpu->Read(); // fetch, decode, execute
        }
                ts->Increment();

        gpu->Step(cpu->GetCycles());

        if (0 < breakpointExtension && 50 > breakpointExtension) {
            std::cout << "Breakpoint" << std::endl;
            printf("\tAF=%.4X", cpu->Get(FULL_REGISTERS::AF));
            printf("\tBC=%.4X", cpu->Get(FULL_REGISTERS::BC));
            printf("\tDE=%.4X", cpu->Get(FULL_REGISTERS::DE));
            printf("\tHL=%.4X", cpu->Get(FULL_REGISTERS::HL));
            printf("\tSP=%.4X", cpu->stackPointer           );
            printf("\tPC=%.4X", cpu->programCounter         );
            std::cin >> qq;
            breakpointExtension--;
        }

        if (0x0100 == cpu->programCounter && mmu->readBios) {
            cpu->CheckRegisters();
            mmu->CheckMemory();
            mmu->readBios = false;
        }

        counter++;
    }

    return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xc0, %rsp
movl $0x0, -0x4(%rbp)
movl $0x10, %edi
callq 0x5490
movq %rax, -0x90(%rbp)
movb $0x1, -0x45(%rbp)
leaq -0x31(%rbp), %rdi
movq %rdi, -0x88(%rbp)
callq 0x5790
movq -0x88(%rbp), %rdx
leaq 0xb598(%rip), %rsi      # 0x38dcd
leaq -0x30(%rbp), %rdi
callq 0x5650
jmp 0x2d840
movq -0x90(%rbp), %rdi
leaq -0x30(%rbp), %rsi
callq 0x33db0
jmp 0x2d852
movb $0x0, -0x45(%rbp)
leaq -0x30(%rbp), %rdi
callq 0x52c0
leaq -0x31(%rbp), %rdi
callq 0x54f0
movq -0x90(%rbp), %rax
movq %rax, -0x10(%rbp)
movq -0x10(%rbp), %rdi
callq 0x2dc60
testb $0x1, %al
jne 0x2d8f6
movq 0x1e739(%rip), %rdi     # 0x4bfc0
leaq 0xb579(%rip), %rsi      # 0x38e07
callq 0x5460
movq %rax, %rdi
movq 0x1e70b(%rip), %rsi     # 0x4bfa8
callq 0x54d0
movl $0x1, %edi
callq 0x5590
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x40(%rbp)
movl %eax, -0x44(%rbp)
jmp 0x2d8cf
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x40(%rbp)
movl %eax, -0x44(%rbp)
leaq -0x30(%rbp), %rdi
callq 0x52c0
leaq -0x31(%rbp), %rdi
callq 0x54f0
testb $0x1, -0x45(%rbp)
jne 0x2d8e0
jmp 0x2d8f1
movq -0x90(%rbp), %rdi
movl $0x10, %esi
callq 0x54a0
jmp 0x2dc4a
movl $0x143b0, %edi          # imm = 0x143B0
callq 0x5490
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0x98(%rbp)
movq -0x10(%rbp), %rsi
callq 0x32ef0
jmp 0x2d918
movq -0x98(%rbp), %rax
movq %rax, -0x50(%rbp)
movl $0x20, %edi
callq 0x5490
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0xa0(%rbp)
movq -0x50(%rbp), %rsi
callq 0x2dda0
jmp 0x2d945
movq -0xa0(%rbp), %rax
movq %rax, -0x58(%rbp)
movl $0x18, %edi
callq 0x5490
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0xa8(%rbp)
movq -0x58(%rbp), %rsi
movq -0x50(%rbp), %rdx
callq 0x33f50
jmp 0x2d976
movq -0xa8(%rbp), %rax
movq %rax, -0x60(%rbp)
movl $0x30, %edi
callq 0x5490
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0xb0(%rbp)
movq -0x58(%rbp), %rsi
movq -0x50(%rbp), %rdx
movq -0x60(%rbp), %rcx
callq 0x34540
jmp 0x2d9ab
movq -0xb0(%rbp), %rax
movq %rax, -0x68(%rbp)
movl $0x5aa0, %edi           # imm = 0x5AA0
callq 0x5490
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0xb8(%rbp)
movq -0x50(%rbp), %rsi
movl $0x3, %edx
callq 0x34f00
jmp 0x2d9dd
movq -0xb8(%rbp), %rax
movq %rax, -0x70(%rbp)
movl $0x0, -0x74(%rbp)
movl $0x0, -0x78(%rbp)
movq -0x60(%rbp), %rdi
callq 0x340c0
andb $0x1, %al
movb %al, -0x7d(%rbp)
movq -0x68(%rbp), %rdi
callq 0x34640
testb $0x1, -0x7d(%rbp)
je 0x2dacf
movq -0x60(%rbp), %rdi
callq 0x341e0
jmp 0x2dad8
movq -0x98(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x40(%rbp)
movl %eax, -0x44(%rbp)
movl $0x143b0, %esi          # imm = 0x143B0
callq 0x54a0
jmp 0x2dc4a
movq -0xa0(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x40(%rbp)
movl %eax, -0x44(%rbp)
movl $0x20, %esi
callq 0x54a0
jmp 0x2dc4a
movq -0xa8(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x40(%rbp)
movl %eax, -0x44(%rbp)
movl $0x18, %esi
callq 0x54a0
jmp 0x2dc4a
movq -0xb0(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x40(%rbp)
movl %eax, -0x44(%rbp)
movl $0x30, %esi
callq 0x54a0
jmp 0x2dc4a
movq -0xb8(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x40(%rbp)
movl %eax, -0x44(%rbp)
movl $0x5aa0, %esi           # imm = 0x5AA0
callq 0x54a0
jmp 0x2dc4a
movq -0x58(%rbp), %rdi
callq 0x2e660
movq -0x68(%rbp), %rdi
callq 0x34640
movq -0x70(%rbp), %rax
movq %rax, -0xc0(%rbp)
movq -0x58(%rbp), %rdi
callq 0x2e230
movq -0xc0(%rbp), %rdi
movzbl %al, %esi
movq (%rdi), %rax
callq *0x90(%rax)
xorl %eax, %eax
cmpl -0x78(%rbp), %eax
jae 0x2dc01
movl $0x32, %eax
cmpl -0x78(%rbp), %eax
jbe 0x2dc01
movq 0x1e498(%rip), %rdi     # 0x4bfc0
leaq 0xb2f1(%rip), %rsi      # 0x38e20
callq 0x5460
movq %rax, %rdi
movq 0x1e46a(%rip), %rsi     # 0x4bfa8
callq 0x54d0
movq -0x58(%rbp), %rdi
movl $0x3, %esi
callq 0x2de60
movzwl %ax, %esi
leaq 0xb2d0(%rip), %rdi      # 0x38e2b
movb $0x0, %al
callq 0x5070
movq -0x58(%rbp), %rdi
xorl %esi, %esi
callq 0x2de60
movzwl %ax, %esi
leaq 0xb2bd(%rip), %rdi      # 0x38e34
movb $0x0, %al
callq 0x5070
movq -0x58(%rbp), %rdi
movl $0x1, %esi
callq 0x2de60
movzwl %ax, %esi
leaq 0xb2a7(%rip), %rdi      # 0x38e3d
movb $0x0, %al
callq 0x5070
movq -0x58(%rbp), %rdi
movl $0x2, %esi
callq 0x2de60
movzwl %ax, %esi
leaq 0xb291(%rip), %rdi      # 0x38e46
movb $0x0, %al
callq 0x5070
movq -0x58(%rbp), %rax
movzwl 0x1a(%rax), %esi
leaq 0xb284(%rip), %rdi      # 0x38e4f
movb $0x0, %al
callq 0x5070
movq -0x58(%rbp), %rax
movzwl 0x18(%rax), %esi
leaq 0xb277(%rip), %rdi      # 0x38e58
movb $0x0, %al
callq 0x5070
movq 0x1e3e9(%rip), %rdi     # 0x4bfd8
leaq -0x7c(%rbp), %rsi
callq 0x52b0
movl -0x78(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0x78(%rbp)
movq -0x58(%rbp), %rax
movzwl 0x18(%rax), %ecx
movl $0x100, %eax            # imm = 0x100
cmpl %ecx, %eax
jne 0x2dc3c
movq -0x50(%rbp), %rax
testb $0x1, 0xe398(%rax)
je 0x2dc3c
movq -0x58(%rbp), %rdi
callq 0x32b90
movq -0x50(%rbp), %rdi
callq 0x33990
movq -0x50(%rbp), %rax
movb $0x0, 0xe398(%rax)
movl -0x74(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x74(%rbp)
jmp 0x2d9f6
movq -0x40(%rbp), %rdi
callq 0x5780
nopw %cs:(%rax,%rax)
nopl (%rax)