using System; using System.IO; using System.Diagnostics; namespace BizHawk.Emulation.Consoles.Nintendo { //Mapper 069 is FME7 //or, Sunsoft-5, which is FME7 with additional sound hardware class Sunsoft_5 : Sunsoft_FME7 { public override bool Configure(NES.EDetectionOrigin origin) { //configure switch (Cart.board_type) { case "SUNSOFT-5B": //Gimmick! (J) AssertPrg(256); AssertChr(128); AssertWram(0); AssertVram(0); AssertBattery(false); break; default: return false; } BaseConfigure(); return true; } public override void WritePRG(int addr, byte value) { //TODO - sound base.WritePRG(addr, value); } } class Sunsoft_FME7 : NES.NESBoardBase { //configuration int prg_bank_mask_8k, chr_bank_mask_1k, wram_bank_mask_8k; //state int addr_reg; ByteBuffer regs = new ByteBuffer(12); ByteBuffer prg_banks_8k = new ByteBuffer(4); int wram_bank; bool wram_ram_selected, wram_ram_enabled; ushort irq_counter; bool irq_countdown, irq_enabled, irq_asserted; int clock_counter; public override void SyncState(Serializer ser) { base.SyncState(ser); ser.Sync("addr_reg", ref addr_reg); ser.Sync("regs", ref regs); ser.Sync("prg_banks_8k", ref prg_banks_8k); ser.Sync("wram_bank", ref wram_bank); ser.Sync("wram_ram_selected", ref wram_ram_selected); ser.Sync("wram_ram_enabled", ref wram_ram_enabled); ser.Sync("irq_counter", ref irq_counter); ser.Sync("irq_countdown", ref irq_countdown); ser.Sync("irq_enabled", ref irq_enabled); ser.Sync("irq_asserted", ref irq_asserted); ser.Sync("clock_counter", ref clock_counter); SyncIrq(); } public override void Dispose() { base.Dispose(); regs.Dispose(); prg_banks_8k.Dispose(); } public override bool Configure(NES.EDetectionOrigin origin) { //configure switch (Cart.board_type) { case "MAPPER069": break; case "SUNSOFT-5A": //Batman (J) AssertPrg(128); AssertChr(128); AssertWram(0); AssertVram(0); AssertBattery(false); break; case "SUNSOFT-FME-7": //Barcode World (J) AssertPrg(128,256); AssertChr(128,256); AssertWram(0,8); AssertVram(0); break; case "NES-BTR": //Batman - Return of the Joker (U) AssertPrg(128); AssertChr(256); AssertWram(8); AssertVram(0); AssertBattery(false); break; default: return false; } BaseConfigure(); return true; } protected void BaseConfigure() { prg_bank_mask_8k = (Cart.prg_size / 8) - 1; wram_bank_mask_8k = (Cart.wram_size / 8) - 1; chr_bank_mask_1k = Cart.chr_size - 1; prg_banks_8k[3] = 0xFF; SetMirrorType(EMirrorType.Vertical); } void SyncPRG() { wram_ram_enabled = (regs[8] & 0x80) != 0; wram_ram_selected = (regs[8]&0x40)!=0; wram_bank = (byte)(regs[8] & 0x7F); for(int i=0;i<3;i++) { prg_banks_8k[i] = regs[8 + i + 1]; } } public override void WritePRG(int addr, byte value) { addr &= 0xE000; switch (addr) { case 0x0000: //$8000: [.... AAAA] Address for use with $A000 addr_reg = value & 0xF; break; case 0x2000: //$A000: [DDDD DDDD] Data port switch(addr_reg) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: regs[addr_reg] = value; //NES.LogLine("cr set to {0},{1},{2},{3},{4},{5},{6},{7}", regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]); break; case 8: case 9: case 0xA: case 0xB: regs[addr_reg] = value; //NES.LogLine("pr/wr set to {0},{1},{2},{3},~0xFF~", regs[8], regs[9], regs[10], regs[11]); SyncPRG(); break; case 0xC: switch (value & 3) { case 0: SetMirrorType(EMirrorType.Vertical); break; case 1: SetMirrorType(EMirrorType.Horizontal); break; case 2: SetMirrorType(EMirrorType.OneScreenA); break; case 3: SetMirrorType(EMirrorType.OneScreenB); break; } break; case 0xD: irq_countdown = value.Bit(7); irq_enabled = value.Bit(0); //if (value != 0) NES.LogLine("irq set to {0},{1} with value {2:x2}", irq_countdown, irq_enabled, value); if (!irq_enabled) irq_asserted = false; SyncIrq(); break; case 0xE: irq_counter &= 0xFF00; irq_counter |= value; //NES.LogLine("irq_counter set to {0:x4}", irq_counter); break; case 0xF: irq_counter &= 0x00FF; irq_counter |= (ushort)(value << 8); //NES.LogLine("irq_counter set to {0:x4}", irq_counter); break; } break; } } void SyncIrq() { IRQSignal = irq_asserted; } void ClockCPU() { if (!irq_countdown) return; irq_counter--; if (irq_counter == 0xFFFF) { irq_asserted = true; SyncIrq(); } } public override void ClockPPU() { clock_counter++; if (clock_counter == 3) { ClockCPU(); clock_counter = 0; } } public override byte ReadPRG(int addr) { int bank_8k = addr >> 13; int ofs = addr & ((1<<13)-1); bank_8k = prg_banks_8k[bank_8k]; bank_8k &= prg_bank_mask_8k; addr = (bank_8k << 13) | ofs; return ROM[addr]; } int CalcWRAMAddress(int addr, int bank_mask_8k) { int ofs = addr & ((1 << 13) - 1); int bank_8k = wram_bank; bank_8k &= bank_mask_8k; addr = (bank_8k << 13) | ofs; return addr; } int CalcPPUAddress(int addr) { int bank_1k = addr >> 10; int ofs = addr & ((1 << 10) - 1); bank_1k = regs[bank_1k]; bank_1k &= chr_bank_mask_1k; return (bank_1k<<10) | ofs; } public override byte ReadPPU(int addr) { if (addr < 0x2000) return VROM[CalcPPUAddress(addr)]; else return base.ReadPPU(addr); } public override void WritePPU(int addr, byte value) { if (addr < 0x2000) { } else base.WritePPU(addr, value); } public override byte ReadWRAM(int addr) { if (!wram_ram_selected) { addr = CalcWRAMAddress(addr, prg_bank_mask_8k); return ROM[addr]; } else if (!wram_ram_enabled) return 0xFF; //empty bus else { addr = CalcWRAMAddress(addr, wram_bank_mask_8k); return WRAM[addr]; } } public override void WriteWRAM(int addr, byte value) { if (!wram_ram_selected) return; else if (!wram_ram_enabled) return; //empty bus else { addr = CalcWRAMAddress(addr, wram_bank_mask_8k); WRAM[addr] = value; } } } }