struct MMC3 : Chip { bool chr_mode; bool prg_mode; uint3 bank_select; uint8 prg_bank[2]; uint8 chr_bank[6]; bool mirror; bool ram_enable; bool ram_write_protect; uint8 irq_latch; uint8 irq_counter; bool irq_enable; unsigned irq_delay; bool irq_line; uint16 chr_abus; void main() { while(true) { if(scheduler.sync == Scheduler::SynchronizeMode::All) { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } if(irq_delay) irq_delay--; cpu.set_irq_line(irq_line); tick(); } } void irq_test(unsigned addr) { if(!(chr_abus & 0x1000) && (addr & 0x1000)) { if(irq_delay == 0) { if(irq_counter == 0) { irq_counter = irq_latch; } else if(--irq_counter == 0) { if(irq_enable) irq_line = 1; } } irq_delay = 6; } chr_abus = addr; } unsigned prg_addr(unsigned addr) const { switch((addr >> 13) & 3) { case 0: if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff); return (prg_bank[0] << 13) | (addr & 0x1fff); case 1: return (prg_bank[1] << 13) | (addr & 0x1fff); case 2: if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff); return (prg_bank[0] << 13) | (addr & 0x1fff); case 3: return (0x3f << 13) | (addr & 0x1fff); } } unsigned chr_addr(unsigned addr) const { if(chr_mode == 0) { if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff); if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff); if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff); if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff); if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff); if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff); } else { if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff); if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff); if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff); if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff); if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff); if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff); } } unsigned ciram_addr(unsigned addr) const { if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff); if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff); } uint8 ram_read(unsigned addr) { if(ram_enable) return board.prgram.data[addr & 0x1fff]; return 0x00; } void ram_write(unsigned addr, uint8 data) { if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data; } void reg_write(unsigned addr, uint8 data) { switch(addr & 0xe001) { case 0x8000: chr_mode = data & 0x80; prg_mode = data & 0x40; bank_select = data & 0x07; break; case 0x8001: switch(bank_select) { case 0: chr_bank[0] = data & ~1; break; case 1: chr_bank[1] = data & ~1; break; case 2: chr_bank[2] = data; break; case 3: chr_bank[3] = data; break; case 4: chr_bank[4] = data; break; case 5: chr_bank[5] = data; break; case 6: prg_bank[0] = data & 0x3f; break; case 7: prg_bank[1] = data & 0x3f; break; } break; case 0xa000: mirror = data & 0x01; break; case 0xa001: ram_enable = data & 0x80; ram_write_protect = data & 0x40; break; case 0xc000: irq_latch = data; break; case 0xc001: irq_counter = 0; break; case 0xe000: irq_enable = false; irq_line = 0; break; case 0xe001: irq_enable = true; break; } } void power() { } void reset() { chr_mode = 0; prg_mode = 0; bank_select = 0; prg_bank[0] = 0; prg_bank[1] = 0; chr_bank[0] = 0; chr_bank[1] = 0; chr_bank[2] = 0; chr_bank[3] = 0; chr_bank[4] = 0; chr_bank[5] = 0; mirror = 0; ram_enable = 1; ram_write_protect = 0; irq_latch = 0; irq_counter = 0; irq_enable = false; irq_delay = 0; irq_line = 0; chr_abus = 0; } void serialize(serializer &s) { s.integer(chr_mode); s.integer(prg_mode); s.integer(bank_select); s.array(prg_bank); s.array(chr_bank); s.integer(mirror); s.integer(ram_enable); s.integer(ram_write_protect); s.integer(irq_latch); s.integer(irq_counter); s.integer(irq_enable); s.integer(irq_delay); s.integer(irq_line); s.integer(chr_abus); } MMC3(Board &board) : Chip(board) { } };