struct VRC4 : Chip { VRC4(Board& board) : Chip(board) { } auto main() -> void { if(irq_enable) { if(irq_mode == 0) { irq_scalar -= 3; if(irq_scalar <= 0) { irq_scalar += 341; if(irq_counter == 0xff) { irq_counter = irq_latch; irq_line = 1; } else { irq_counter++; } } } if(irq_mode == 1) { if(irq_counter == 0xff) { irq_counter = irq_latch; irq_line = 1; } else { irq_counter++; } } } cpu.set_irq_line(irq_line); tick(); } auto prg_addr(uint addr) const -> uint { uint bank = 0, banks = board.prgrom.size / 0x2000; switch(addr & 0xe000) { case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break; case 0xa000: bank = prg_bank[1]; break; case 0xc000: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break; case 0xe000: bank = banks - 1; break; } return (bank * 0x2000) + (addr & 0x1fff); } auto chr_addr(uint addr) const -> uint { uint bank = chr_bank[addr / 0x0400]; return (bank * 0x0400) + (addr & 0x03ff); } auto ciram_addr(uint addr) const -> uint { switch(mirror) { case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first) case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second) } throw; } auto reg_write(uint addr, uint8 data) -> void { switch(addr) { case 0x8000: case 0x8001: case 0x8002: case 0x8003: prg_bank[0] = data & 0x1f; break; case 0x9000: case 0x9001: mirror = data & 0x03; break; case 0x9002: case 0x9003: prg_mode = data & 0x02; break; case 0xa000: case 0xa001: case 0xa002: case 0xa003: prg_bank[1] = data & 0x1f; break; case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break; case 0xb001: chr_bank[0] = (chr_bank[0] & 0x0f) | ((data & 0x0f) << 4); break; case 0xb002: chr_bank[1] = (chr_bank[1] & 0xf0) | ((data & 0x0f) << 0); break; case 0xb003: chr_bank[1] = (chr_bank[1] & 0x0f) | ((data & 0x0f) << 4); break; case 0xc000: chr_bank[2] = (chr_bank[2] & 0xf0) | ((data & 0x0f) << 0); break; case 0xc001: chr_bank[2] = (chr_bank[2] & 0x0f) | ((data & 0x0f) << 4); break; case 0xc002: chr_bank[3] = (chr_bank[3] & 0xf0) | ((data & 0x0f) << 0); break; case 0xc003: chr_bank[3] = (chr_bank[3] & 0x0f) | ((data & 0x0f) << 4); break; case 0xd000: chr_bank[4] = (chr_bank[4] & 0xf0) | ((data & 0x0f) << 0); break; case 0xd001: chr_bank[4] = (chr_bank[4] & 0x0f) | ((data & 0x0f) << 4); break; case 0xd002: chr_bank[5] = (chr_bank[5] & 0xf0) | ((data & 0x0f) << 0); break; case 0xd003: chr_bank[5] = (chr_bank[5] & 0x0f) | ((data & 0x0f) << 4); break; case 0xe000: chr_bank[6] = (chr_bank[6] & 0xf0) | ((data & 0x0f) << 0); break; case 0xe001: chr_bank[6] = (chr_bank[6] & 0x0f) | ((data & 0x0f) << 4); break; case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); break; case 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break; case 0xf000: irq_latch = (irq_latch & 0xf0) | ((data & 0x0f) << 0); break; case 0xf001: irq_latch = (irq_latch & 0x0f) | ((data & 0x0f) << 4); break; case 0xf002: irq_mode = data & 0x04; irq_enable = data & 0x02; irq_acknowledge = data & 0x01; if(irq_enable) { irq_counter = irq_latch; irq_scalar = 341; } irq_line = 0; break; case 0xf003: irq_enable = irq_acknowledge; irq_line = 0; break; } } auto power() -> void { } auto reset() -> void { prg_mode = 0; for(auto& n : prg_bank) n = 0; mirror = 0; for(auto& n : chr_bank) n = 0; irq_latch = 0; irq_mode = 0; irq_enable = 0; irq_acknowledge = 0; irq_counter = 0; irq_scalar = 0; irq_line = 0; } auto serialize(serializer& s) -> void { s.integer(prg_mode); for(auto& n : prg_bank) s.integer(n); s.integer(mirror); for(auto& n : chr_bank) s.integer(n); s.integer(irq_latch); s.integer(irq_mode); s.integer(irq_enable); s.integer(irq_acknowledge); s.integer(irq_counter); s.integer(irq_scalar); s.integer(irq_line); } bool prg_mode; uint5 prg_bank[2]; uint2 mirror; uint8 chr_bank[8]; uint8 irq_latch; bool irq_mode; bool irq_enable; bool irq_acknowledge; uint8 irq_counter; int irq_scalar; bool irq_line; };