239 lines
4.9 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|