BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/Mapper116.cs

239 lines
4.9 KiB
C#

//TODO - emulation of mirroring is all bolloxed.
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Nintendo.NES
{
public sealed class Mapper116 : NES.NESBoardBase
{
[NES.INESBoardImplCancel]
class MMC3_CustomBoard : MMC3Board_Base
{
public override void WritePRG(int addr, byte value)
{
base.WritePRG(addr, value);
SetMirrorType(mmc3.MirrorType); //often redundant, but gets the job done
}
public override bool Configure(NES.EDetectionOrigin origin)
{
BaseSetup();
return true;
}
public MMC3_CustomBoard(Mapper116 master)
{
this.master = master;
}
public override void SyncIRQ(bool flag)
{
master.SyncIRQ(flag);
}
Mapper116 master;
}
//configuration
//state
int mode;
SxROM mmc1;
MMC3_CustomBoard mmc3;
VRC2_4 vrc2;
public override void SyncState(Serializer ser)
{
ser.Sync("mod", ref mode);
ser.BeginSection("M116MMC1");
mmc1.SyncState(ser);
ser.EndSection();
ser.BeginSection("M116MMC3");
mmc3.SyncState(ser);
ser.EndSection();
ser.BeginSection("M116VRC2");
vrc2.SyncState(ser);
ser.EndSection();
base.SyncState(ser);
SyncIRQ(mmc3.mmc3.irq_pending);
}
public override bool Configure(NES.EDetectionOrigin origin)
{
string oldBoardType = Cart.board_type;
//configure
switch (Cart.board_type)
{
case "MAPPER116":
break;
default:
return false;
}
SetMirrorType(Cart.pad_h, Cart.pad_v);
Cart.board_type = "MAPPER116_HACKY";
vrc2 = new VRC2_4();
vrc2.Create(NES);
vrc2.Configure(origin);
//not exactly the same as fceu-mm. is it important?
for(int i=0;i<16;i++)
vrc2.chr_bank_reg_1k[i] = 0x0F;
vrc2.SyncCHR();
mmc3 = new MMC3_CustomBoard(this);
mmc3.Create(NES);
mmc3.Configure(origin);
//is this important? not sure.
mmc3.mmc3.regs[0] = 0;
mmc3.mmc3.regs[1] = 2;
mmc3.mmc3.regs[2] = 3;
mmc3.mmc3.regs[3] = 4;
mmc3.mmc3.regs[4] = 5;
mmc3.mmc3.regs[5] = 7;
mmc3.mmc3.Sync();
mmc1 = new SxROM();
mmc1.Create(NES);
mmc1.Configure(origin);
mmc1_reset();
Cart.board_type = oldBoardType;
mode = 0;
Sync();
return true;
}
public override void PostConfigure()
{
SyncRoms();
base.PostConfigure();
}
public override void SyncIRQ(bool flag)
{
if(mode == 1)
base.SyncIRQ(flag);
}
void SyncRoms()
{
foreach (var board in new NES.NESBoardBase[] { mmc3, vrc2, mmc1 })
{
board.ROM = ROM;
board.VROM = VROM;
board.VRAM = VRAM;
board.WRAM = WRAM;
}
}
void Sync()
{
}
void mmc1_reset()
{
mmc1.mmc1.StandardReset();
}
void WriteModeControl(int addr, byte value)
{
if ((addr & 0x4100) != 0x4100) return;
mode = value & 3;
bool chr_base = value.Bit(2);
vrc2.extra_vrom = mmc3.extra_vrom = chr_base ? 256 * 1024 : 0;
//fceu-mm has special "hacky hacky" logic here to initialize mmc1 a special way sometimes. read about it on the nesdevwiki. not sure how important it is
if ((addr & 1) == 1)
{
mmc1_reset();
}
Sync();
if(mode == 1) SyncIRQ(mmc3.mmc3.irq_pending);
Console.Write("MODE: {0} ",mode);
if (mode == 0) Console.WriteLine("(vrc2)");
if (mode == 1) Console.WriteLine("(mmc3)");
if (mode == 2) Console.WriteLine("(mmc1)");
if (mode == 3) Console.WriteLine("(mmc1)");
}
public override void WriteEXP(int addr, byte value)
{
WriteModeControl(addr + 0x4000, value);
}
public override void WritePPU(int addr, byte value)
{
switch (mode)
{
case 0: vrc2.WritePPU(addr, value); break;
case 1: mmc3.WritePPU(addr, value); break;
case 2:
case 3: mmc1.WritePPU(addr, value); break;
}
}
public override byte ReadPPU(int addr)
{
switch (mode)
{
case 0: return vrc2.ReadPPU(addr);
case 1: return mmc3.ReadPPU(addr);
case 2:
case 3: return mmc1.ReadPPU(addr);
}
return 0;
}
public override void WritePRG(int addr, byte value)
{
Console.WriteLine("{0:X4} = {1:X2}", addr+0x8000, value);
switch (mode)
{
case 0:
addr += 0x8000;
if((addr & 0xF000) < 0xB000) addr &= 0xF000; //Garou Densetsu Special depends on this
addr -= 0x8000;
vrc2.WritePRG(addr, value);
break;
case 1: mmc3.WritePRG(addr, value); break;
case 2:
case 3: mmc1.WritePRG(addr, value); break;
}
}
public override byte ReadPRG(int addr)
{
switch (mode)
{
case 0: return vrc2.ReadPRG(addr);
case 1: return mmc3.ReadPRG(addr);
case 2:
case 3: return mmc1.ReadPRG(addr);
}
return 0;
}
public override void AddressPPU(int addr)
{
mmc3.AddressPPU(addr);
}
public override void ClockPPU()
{
mmc3.ClockPPU();
}
}
}