// File: /workspace/github_cpp_makefile_star2_O2/khedoros[P]khednes/ppu3.cpp
// Function: ppu::reg_write(unsigned int, unsigned char, unsigned int)
void ppu::reg_write(const unsigned int addr,const unsigned char val, const unsigned int cycle) {
    //cout<<"!! "<<phases[phase(cycle)]<<" Write address: 0x"<<hex<<addr<<" Value: 0x"<<int(val)<<dec<<" Cycle: "<<cycle<<endl;
    ppu_phase curphase = phase(cycle);
    if(curphase == RENDER) ++s.writes_during_render;
    switch(addr) {
        case 0x2000:
        {
            int bg_table = control0.bg_table;
            int spr_table = control0.sprite_table;
            control0.reg = val;
            vram_ptr_reset.fields.page_y = (control0.base_addr & 2)/2;
            vram_ptr_reset.fields.page_x = (control0.base_addr & 1);

            int xscroll = (control0.base_addr%2) * 32 + vram_ptr_reset.fields.tile_x;
            int yscroll = (control0.base_addr/2) * 30 + vram_ptr_reset.fields.tile_y;
            if(curphase != RENDER && curphase != POST_RENDER) { //keeps track of where the screen will start
                set_scroll(xscroll*8+fine_x, yscroll*8+vram_ptr_reset.fields.fine_y);
                cart.ppu_change(cycle, BG_CHANGE_ADDR, control0.bg_table);
                cart.ppu_change(cycle, SPR_CHANGE_ADDR, control0.sprite_table);
                //if(control0.bg_table != bg_table) cout<<"Changed the bg table (to "<<control0.bg_table<<"), cycle: "<<cycle<<endl;
                //if(control0.sprite_table != spr_table) cout<<"Changed the sprite table (to "<<control0.sprite_table<<"), cycle: "<<cycle<<endl;
            } else { //keeps track of when (during rendering) the reset shifts, but only worry about the X reset
                set_scroll(xscroll*8+fine_x, yscroll*8+vram_ptr_reset.fields.fine_y, cycle%CLK_PER_LINE, cycle/CLK_PER_LINE);
                if(control0.bg_table != bg_table) {
                    //cout<<"Changed the bg table mid-frame! (to "<<control0.bg_table<<"), cycle: "<<cycle<<endl;
                    cart.ppu_change(cycle, BG_CHANGE_ADDR, 1 - control0.bg_table);
                }
                if(control0.sprite_table != spr_table) {
                    //cout<<"Changed the sprite table mid-frame! (to "<<control0.sprite_table<<"), cycle: "<<cycle<<endl;
                    cart.ppu_change(cycle, SPR_CHANGE_ADDR, 1 - control0.sprite_table);
                }
            }
        }
            break;
        case 0x2001:
            /*
            cout<<"Wrote "<<hex<<val<<dec<<" to 2001 (Color disabled: "<<control1.bits.color_disabled
                <<" show left bg: "<<control1.bits.show_left_bg<<" show left sprites: "<<control1.bits.show_left_sprites
                <<" show bg: "<<control1.bits.show_bg<<" show sprites: "<<control1.bits.show_sprites<<endl;
            */
            control1.reg = val;
            break;
        case 0x2003:
            spraddr = val;
            break;
        case 0x2004:
            if(spraddr/4 == 0) recalc_s0 = true;
            recalc_overflow = true;
            sprites[spraddr/4].val[spraddr%4] = val;
            spraddr++;
            break;
        case 0x2005:
            if(!scroll_latch) {
                vram_ptr_reset.fields.tile_x = (val & 0xf8)>>(3) ;
                fine_x = (val & 0x7);
                //cout<<"Set X reset to ("<<(control0.base_addr&1)*256+vram_ptr_reset.fields.tile_x*8+fine_x<<", "
                //    <<(control0.base_addr&2)*120+vram_ptr_reset.fields.tile_y*8+vram_ptr_reset.fields.fine_y<<") at cycle "
                //    <<cycle<<" ("<<cycle/CLK_PER_LINE<<", "<<cycle%CLK_PER_LINE<<") ("<<phases[curphase]<<")"<<endl;
            }
            else {
                vram_ptr_reset.fields.tile_y = (val & 0xf8)>>(3);
                vram_ptr_reset.fields.fine_y = (val & 0x7);
                //cout<<"Set Y reset to ("<<(control0.base_addr&1)*256+vram_ptr_reset.fields.tile_x*8+fine_x<<", "
                //    <<(control0.base_addr&2)*120+vram_ptr_reset.fields.tile_y*8+vram_ptr_reset.fields.fine_y<<") at cycle "
                //    <<cycle<<" ("<<cycle/CLK_PER_LINE<<", "<<cycle%CLK_PER_LINE<<") ("<<phases[curphase]<<")"<<endl;
            }
            if(curphase != RENDER) { //keeps track of where the screen will start
                int xscroll = (control0.base_addr%2) * 32 + vram_ptr_reset.fields.tile_x;
                int yscroll = (control0.base_addr/2) * 30 + vram_ptr_reset.fields.tile_y;
                set_scroll(xscroll*8+fine_x, yscroll*8+vram_ptr_reset.fields.fine_y);
            }
            else if(curphase == RENDER) { //keeps track of when (during rendering) the reset shifts, but only worry about the X reset
                int xscroll = (control0.base_addr%2) * 32 + vram_ptr_reset.fields.tile_x;
                int yscroll = (control0.base_addr/2) * 30 + vram_ptr_reset.fields.tile_y;
                set_scroll(xscroll*8+fine_x, yscroll*8+vram_ptr_reset.fields.fine_y, cycle%CLK_PER_LINE, cycle/CLK_PER_LINE);
            }
            scroll_latch = !scroll_latch;
            break;
        case 0x2006:
            if(!scroll_latch) {
                vram_ptr_reset.bytes.high = val;
                vram_ptr_reset.bytes.pad = 0;
            }
            else {
                vram_ptr_reset.bytes.low = val;
                vram_ptr = vram_ptr_reset;
            }

            if(curphase != RENDER) { //keep track of where the screen starts
                int xscroll = (control0.base_addr%2) * 32 + vram_ptr_reset.fields.tile_x;
                int yscroll = (control0.base_addr/2) * 30 + vram_ptr_reset.fields.tile_y;
                //cout<<"2006: set scroll to ("<<yscroll<<", "<<xscroll<<")"<<endl;
                set_scroll(xscroll*8+fine_x, yscroll*8+vram_ptr_reset.fields.fine_y);
            }
            else if(curphase == RENDER) {
                int xscroll = (control0.base_addr%2) * 32 + vram_ptr_reset.fields.tile_x;
                int yscroll = (control0.base_addr/2) * 30 + vram_ptr_reset.fields.tile_y;
                set_scroll(xscroll*8+fine_x, yscroll*8+vram_ptr_reset.fields.fine_y, cycle%CLK_PER_LINE, cycle/CLK_PER_LINE);
            }
            scroll_latch = !scroll_latch;
            break;
        case 0x2007:
            //cout<<"0x2007 (0x"<<hex<<vram_ptr.pointer<<dec<<"): "<<hex<<int(val)<<dec<<"(";
            if(vram_ptr.pointer < 0x2000) {
                //cout<<"Writing to Pattern Table)"<<endl;
                bool wrote = cart.put_cbyte(val, vram_ptr.pointer);
                if(wrote) {
                    int table = (vram_ptr.pointer < 0x1000)?0:1;
                    int tile = (vram_ptr.pointer & 0xfff) / 16;
                    for(int palnum = 0; palnum < 8; ++palnum) {
                        unget_Surface(table, tile, palnum);
                    }
                }
            }
            else {
                unsigned int ptr = vram_ptr.pointer;
                if(ptr >= 0x3000 && ptr < 0x3F00) ptr -= 0x1000;
                if(ptr < 0x23C0) {
                    //cout<<"Writing to Name Table 0)"<<endl;
                    nt[0][ptr - 0x2000] = val;
                }
                else if(ptr < 0x2400) {
                    //cout<<"Writing to Attribute Table 0)"<<endl;
                    at[0][ptr - 0x23C0] = val;
                }
                else if(ptr < 0x27C0) {
                    //cout<<"Writing to Name Table 1)"<<endl;
                    nt[1][ptr - 0x2400] = val;
                }
                else if(ptr < 0x2800) {
                    //cout<<"Writing to Attribute Table 1)"<<endl;
                    at[1][ptr - 0x27C0] = val;
                }
                else if(ptr < 0x2BC0) {
                    //cout<<"Writing to Name Table 2)"<<endl;
                    nt[2][ptr - 0x2800] = val;
                }
                else if(ptr < 0x2C00) {
                    //cout<<"Writing to Attribute Table 2)"<<endl;
                    at[2][ptr - 0x2BC0] = val;
                }
                else if(ptr < 0x2FC0) {
                    //cout<<"Writing to Name Table 3)"<<endl;
                    nt[3][ptr - 0x2C00] = val;
                }
                else if(ptr < 0x3000) {
                    //cout<<"Writing to Attribute Table 3)"<<endl;
                    at[3][ptr - 0x2FC0] = val;
                }
                else {
                    ptr &= 0x1f;
                    if(ptr < 0x10) {
                        //cout<<"Writing to Background Palette)"<<endl;
                    }
                    else {
                        //cout<<"Writing to Sprite Palette)"<<endl;
                    }
                    int oldval = *(pal[ptr]);
                    if(val != oldval) {
                        *(pal[ptr]) = (val&0x3f);
                        //cout<<hex<<"Palette value "<<ptr<<" changed, clearing palette "<<(ptr>>(2))<<" tiles"<<endl;
                        for(int table = 0; table < 2; ++table) {
                            for(int tile = 0; tile < 256; ++tile) {
                                unget_Surface(table, tile, ptr>>(2));
                            }
                        }
                    }
                }
            }
            vram_ptr.pointer++;
            if(control0.v_inc_down) vram_ptr.pointer +=31;
            break;
        default:
            cout<<"Writing to register 0x"<<hex<<addr<<" isn't handled."<<dec<<endl;
        break;
    }
    ++s.writes;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
movl %ecx, %ebp
movl %edx, %r14d
movl %esi, %r15d
movq %rdi, %rbx
movl %ecx, %esi
callq 0xce48
cmpl $0x3, %eax
jne 0xc91e
incl 0x1c4(%rbx)
leal -0x2000(%r15), %ecx
cmpl $0x7, %ecx
ja 0xca04
leaq 0x6b9b(%rip), %rdx      # 0x134d0
movslq (%rdx,%rcx,4), %rcx
addq %rdx, %rcx
jmpq *%rcx
movb 0x21850(%rbx), %r15b
movb %r14b, 0x21850(%rbx)
movl %r14d, %edx
shrb %dl
movzwl 0x6d8(%rbx), %ecx
andb $0x1, %dl
movzbl %dl, %edx
shll $0xb, %edx
movl %ecx, %esi
andl $0xf3ff, %esi           # imm = 0xF3FF
orl %edx, %esi
movl %r14d, %edx
andb $0x1, %dl
movzbl %dl, %edx
shll $0xa, %edx
orl %esi, %edx
movw %dx, 0x6d8(%rbx)
movl %r14d, %edx
andb $0x3, %dl
movzbl %dl, %esi
movl %esi, %edi
shll $0x5, %edi
andl $0x20, %edi
movl %ecx, %edx
andl $0x1f, %edx
orl %edi, %edx
addl $-0x5, %eax
cmpl $-0x3, %eax
ja 0xcba1
shrl %esi
imull $0x1e, %esi, %eax
movl %ecx, %esi
shrl $0x5, %esi
andl $0x1f, %esi
addl %eax, %esi
movzbl 0x6e0(%rbx), %eax
andl $0x7, %eax
leal (%rax,%rdx,8), %eax
shrl $0xc, %ecx
andl $0x7, %ecx
leal (%rcx,%rsi,8), %ecx
movl %eax, 0x1e8(%rbx)
movl %ecx, 0x1ec(%rbx)
movq (%rbx), %rdi
shrb $0x4, %r14b
andb $0x1, %r14b
movzbl %r14b, %ecx
movl %ebp, %esi
movl $0x20004, %edx          # imm = 0x20004
callq 0x8f48
movq (%rbx), %rdi
movl 0x21850(%rbx), %ecx
shrl $0x3, %ecx
andl $0x1, %ecx
jmp 0xcc1a
movq 0xe5a5(%rip), %rdi      # 0x1afb0
leaq 0x6ba4(%rip), %rsi      # 0x135b6
callq 0x4390
movq (%rax), %rcx
movq -0x18(%rcx), %rcx
movl 0x18(%rax,%rcx), %edx
andl $-0x4b, %edx
orl $0x8, %edx
movl %edx, 0x18(%rax,%rcx)
movl %r15d, %esi
movq %rax, %rdi
callq 0x4270
leaq 0x6b8f(%rip), %rsi      # 0x135cd
movq %rax, %rdi
callq 0x4390
movq (%rax), %rcx
movq -0x18(%rcx), %rcx
movl 0x18(%rax,%rcx), %edx
andl $-0x4b, %edx
orl $0x2, %edx
movl %edx, 0x18(%rax,%rcx)
movq %rax, %rdi
callq 0x41c0
jmp 0xccfe
movb 0x6e2(%rbx), %cl
testb %cl, %cl
je 0xcb81
movb %r14b, 0x6d8(%rbx)
movl 0x6d8(%rbx), %edx
movl %edx, 0x6dc(%rbx)
jmp 0xcc84
movb %r14b, 0x21854(%rbx)
jmp 0xccfe
movzwl 0x6dc(%rbx), %edx
andl $0x3fff, %edx           # imm = 0x3FFF
cmpw $0x1fff, %dx            # imm = 0x1FFF
ja 0xcc2b
movq (%rbx), %rdi
movzbl %r14b, %esi
callq 0x8f32
testb %al, %al
je 0xcdbc
movzwl 0x6dc(%rbx), %eax
xorl %ebp, %ebp
testw $0x3000, %ax            # imm = 0x3000
setne %bpl
shrl $0x4, %eax
movzbl %al, %r14d
xorl %r15d, %r15d
cmpl $0x8, %r15d
je 0xcdbc
movq %rbx, %rdi
movl %ebp, %esi
movl %r14d, %edx
movl %r15d, %ecx
callq 0xc722
incl %r15d
jmp 0xcae1
movb %r14b, 0x308(%rbx)
jmp 0xccfe
movzbl 0x308(%rbx), %eax
cmpl $0x3, %eax
ja 0xcb1f
movb $0x1, 0x6d5(%rbx)
movb $0x1, 0x6d4(%rbx)
movl %eax, %ecx
andl $-0x4, %ecx
addq %rbx, %rcx
movl %eax, %edx
andl $0x3, %edx
movb %r14b, 0x208(%rdx,%rcx)
incb %al
movb %al, 0x308(%rbx)
jmp 0xccfe
movb 0x6e2(%rbx), %cl
movl %r14d, %edx
shrb $0x3, %dl
movzbl %dl, %edx
movzwl 0x6d8(%rbx), %esi
andb $0x7, %r14b
testb %cl, %cl
je 0xcc60
movzwl %dx, %edx
shll $0x5, %edx
andl $0xffff8c1f, %esi       # imm = 0xFFFF8C1F
orl %edx, %esi
movzbl %r14b, %edx
shll $0xc, %edx
jmp 0xcb93
movzbl 0x6d8(%rbx), %esi
andb $0x3f, %r14b
movzbl %r14b, %edx
shll $0x8, %edx
orl %esi, %edx
movw %dx, 0x6d8(%rbx)
jmp 0xcc84
movl %r15d, %r14d
shrb $0x3, %r14b
andb $0x1, %r14b
movzbl 0x6e0(%rbx), %eax
andl $0x7, %eax
leal (%rax,%rdx,8), %esi
movl $0x155, %ecx            # imm = 0x155
movl %ebp, %eax
xorl %edx, %edx
divl %ecx
movq %rbx, %rdi
movl %eax, %r8d
callq 0xd226
movl 0x21850(%rbx), %eax
movl %eax, %ecx
shrl $0x4, %ecx
andl $0x1, %ecx
movzbl %r15b, %edx
btl $0x4, %edx
movl %ecx, %edx
sbbb $0x0, %dl
je 0xcc04
movq (%rbx), %rdi
xorl $0x1, %ecx
movl %ebp, %esi
movl $0x20004, %edx          # imm = 0x20004
callq 0x8f48
movb 0x21850(%rbx), %al
shrb $0x3, %al
andb $0x1, %al
cmpb %r14b, %al
je 0xccfe
movq (%rbx), %rdi
xorb $0x1, %al
movzbl %al, %ecx
movl %ebp, %esi
movl $0x20003, %edx          # imm = 0x20003
callq 0x8f48
jmp 0xccfe
movzwl %dx, %eax
addl $0xffffd000, %eax       # imm = 0xFFFFD000
leal -0x1000(%rdx), %r15d
cmpw $0xf00, %ax             # imm = 0xF00
cmovael %edx, %r15d
cmpl $0x23c0, %r15d          # imm = 0x23C0
jae 0xcd0f
movq $-0x2000, %rax          # imm = 0xE000
movl $0x16f0, %ecx           # imm = 0x16F0
jmp 0xcdb1
andl $-0x20, %esi
movzwl %dx, %edx
orl %esi, %edx
movw %dx, 0x6d8(%rbx)
movb 0x6e0(%rbx), %sil
andb $-0x8, %sil
orb %r14b, %sil
movb %sil, 0x6e0(%rbx)
movzbl 0x21850(%rbx), %edi
movl %edi, %esi
shll $0x5, %esi
andl $0x20, %esi
movl %edx, %r8d
andl $0x1f, %r8d
orl %esi, %r8d
movzbl 0x6e0(%rbx), %esi
andl $0x7, %esi
leal (%rsi,%r8,8), %esi
cmpl $0x3, %eax
jne 0xccce
movl $0x155, %ecx            # imm = 0x155
movl %ebp, %eax
xorl %edx, %edx
divl %ecx
movq %rbx, %rdi
movl %eax, %r8d
callq 0xd226
movb 0x6e2(%rbx), %cl
jmp 0xccf5
andl $0x3, %edi
shrl %edi
imull $0x1e, %edi, %eax
movl %edx, %edi
shrl $0x5, %edi
andl $0x1f, %edi
addl %eax, %edi
shrl $0xc, %edx
andl $0x7, %edx
leal (%rdx,%rdi,8), %eax
movl %esi, 0x1e8(%rbx)
movl %eax, 0x1ec(%rbx)
xorb $0x1, %cl
movb %cl, 0x6e2(%rbx)
incl 0x1cc(%rbx)
addq $0x8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
cmpl $0x2400, %r15d          # imm = 0x2400
jae 0xcd29
movq $-0x23c0, %rax          # imm = 0xDC40
movl $0x1710, %ecx           # imm = 0x1710
jmp 0xcdb1
cmpl $0x27c0, %r15d          # imm = 0x27C0
jae 0xcd40
movq $-0x2400, %rax          # imm = 0xDC00
movl $0x16f8, %ecx           # imm = 0x16F8
jmp 0xcdb1
cmpl $0x2800, %r15d          # imm = 0x2800
jae 0xcd57
movq $-0x27c0, %rax          # imm = 0xD840
movl $0x1718, %ecx           # imm = 0x1718
jmp 0xcdb1
cmpl $0x2bc0, %r15d          # imm = 0x2BC0
jae 0xcd6e
movq $-0x2800, %rax          # imm = 0xD800
movl $0x1700, %ecx           # imm = 0x1700
jmp 0xcdb1
cmpl $0x2c00, %r15d          # imm = 0x2C00
jae 0xcd85
movq $-0x2bc0, %rax          # imm = 0xD440
movl $0x1720, %ecx           # imm = 0x1720
jmp 0xcdb1
cmpl $0x2fc0, %r15d          # imm = 0x2FC0
jae 0xcd9c
movq $-0x2c00, %rax          # imm = 0xD400
movl $0x1708, %ecx           # imm = 0x1708
jmp 0xcdb1
cmpl $0x3000, %r15d          # imm = 0x3000
jae 0xce00
movq $-0x2fc0, %rax          # imm = 0xD040
movl $0x1728, %ecx           # imm = 0x1728
movl %r15d, %edx
addq (%rbx,%rcx), %rdx
movb %r14b, (%rax,%rdx)
movzwl 0x6dc(%rbx), %eax
leal 0x1(%rax), %edx
andl $0x3fff, %edx           # imm = 0x3FFF
movl %eax, %ecx
andl $0xc000, %ecx           # imm = 0xC000
orl %ecx, %edx
movw %dx, 0x6dc(%rbx)
testb $0x4, 0x21850(%rbx)
je 0xccfe
addl $0x20, %eax
andl $0x3fff, %eax           # imm = 0x3FFF
orl %eax, %ecx
movw %cx, 0x6dc(%rbx)
jmp 0xccfe
andl $0x1f, %r15d
movq 0x1750(%rbx,%r15,8), %rax
cmpb %r14b, (%rax)
je 0xcdbc
andb $0x3f, %r14b
movb %r14b, (%rax)
shrl $0x2, %r15d
xorl %ebp, %ebp
cmpl $0x2, %ebp
je 0xcdbc
xorl %r14d, %r14d
cmpl $0x100, %r14d           # imm = 0x100
je 0xce44
movq %rbx, %rdi
movl %ebp, %esi
movl %r14d, %edx
movl %r15d, %ecx
callq 0xc722
incl %r14d
jmp 0xce26
incl %ebp
jmp 0xce1e