2011-03-01 07:25:14 +00:00
|
|
|
using System;
|
2013-11-04 00:36:15 +00:00
|
|
|
using BizHawk.Common;
|
2016-11-27 01:25:46 +00:00
|
|
|
using BizHawk.Common.NumberExtensions;
|
2011-03-01 07:25:14 +00:00
|
|
|
|
2013-11-14 13:15:41 +00:00
|
|
|
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
|
|
|
//AKA MMC1
|
|
|
|
//http://wiki.nesdev.com/w/index.php/SxROM
|
|
|
|
|
|
|
|
//consult nestopia as well.
|
|
|
|
//the initial conditions for MMC1 games are known to be different. this may have to do with which MMC1 rev it is.
|
|
|
|
//but how do we know which revision a game is? i don't know which revision is on which board
|
|
|
|
//check UNIF for more information.. it may specify board and MMC1 rev independently because boards may have any MMC1 rev
|
|
|
|
//in that case, we need to capture MMC1 rev in the game database (maybe add a new `chip` parameter)
|
|
|
|
|
2011-06-12 00:53:14 +00:00
|
|
|
//TODO - this could be refactored to use more recent techniques (bank regs instead of nested if/then)
|
|
|
|
|
2011-03-02 03:41:24 +00:00
|
|
|
//Final Fantasy
|
|
|
|
//Mega Man 2
|
|
|
|
//Blaster Master
|
|
|
|
//Metroid
|
|
|
|
//Kid Icarus
|
|
|
|
//Zelda
|
|
|
|
//Zelda 2
|
|
|
|
//Castlevania 2
|
2011-03-01 07:25:14 +00:00
|
|
|
|
2014-02-13 18:14:01 +00:00
|
|
|
// TODO -- different MMC1 revisions handle wram_disable differently; on some it doesn't work at all; on others,
|
|
|
|
// it works, but with different initial states possible. we only emulate the first case
|
|
|
|
|
2013-08-25 01:08:17 +00:00
|
|
|
public sealed class MMC1
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
2012-06-23 08:52:12 +00:00
|
|
|
public MMC1_SerialController scnt = new MMC1_SerialController();
|
2011-09-26 03:07:27 +00:00
|
|
|
|
2014-02-13 18:14:01 +00:00
|
|
|
public MMC1()
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
2011-09-26 03:07:27 +00:00
|
|
|
scnt.WriteRegister = SerialWriteRegister;
|
|
|
|
scnt.Reset = SerialReset;
|
2011-06-08 01:03:32 +00:00
|
|
|
|
2011-03-01 07:25:14 +00:00
|
|
|
//collect data about whether this is required here:
|
2011-03-01 07:31:59 +00:00
|
|
|
//kid icarus requires it
|
|
|
|
//zelda doesnt; nor megaman2; nor blastermaster; nor metroid
|
2011-03-01 07:25:14 +00:00
|
|
|
StandardReset();
|
2011-03-01 07:31:59 +00:00
|
|
|
//well, lets leave it.
|
2012-12-21 07:12:01 +00:00
|
|
|
|
|
|
|
SyncCHR();
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
|
2013-07-27 23:31:29 +00:00
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
chr_banks_4k.Dispose();
|
|
|
|
prg_banks_16k.Dispose();
|
|
|
|
}
|
|
|
|
|
2011-04-17 22:51:53 +00:00
|
|
|
public void SyncState(Serializer ser)
|
2011-03-01 09:32:12 +00:00
|
|
|
{
|
2011-09-26 03:07:27 +00:00
|
|
|
scnt.SyncState(ser);
|
2011-04-17 22:51:53 +00:00
|
|
|
ser.Sync("chr_mode", ref chr_mode);
|
|
|
|
ser.Sync("prg_mode", ref prg_mode);
|
|
|
|
ser.Sync("prg_slot", ref prg_slot);
|
|
|
|
ser.Sync("chr_0", ref chr_0);
|
|
|
|
ser.Sync("chr_1", ref chr_1);
|
|
|
|
ser.Sync("wram_disable", ref wram_disable);
|
|
|
|
ser.Sync("prg", ref prg);
|
|
|
|
ser.SyncEnum("mirror", ref mirror);
|
2012-12-21 07:12:01 +00:00
|
|
|
|
|
|
|
SyncCHR();
|
2013-07-27 23:31:29 +00:00
|
|
|
SyncPRG();
|
2011-03-01 09:32:12 +00:00
|
|
|
}
|
|
|
|
|
2011-03-01 07:25:14 +00:00
|
|
|
public enum Rev
|
|
|
|
{
|
|
|
|
A, B1, B2, B3
|
|
|
|
}
|
|
|
|
|
|
|
|
//register 0:
|
|
|
|
public int chr_mode;
|
|
|
|
public int prg_mode;
|
|
|
|
public int prg_slot; //complicated
|
2011-04-18 22:35:40 +00:00
|
|
|
public NES.NESBoardBase.EMirrorType mirror;
|
|
|
|
static NES.NESBoardBase.EMirrorType[] _mirrorTypes = new NES.NESBoardBase.EMirrorType[] { NES.NESBoardBase.EMirrorType.OneScreenA, NES.NESBoardBase.EMirrorType.OneScreenB, NES.NESBoardBase.EMirrorType.Vertical, NES.NESBoardBase.EMirrorType.Horizontal };
|
2011-03-01 07:25:14 +00:00
|
|
|
|
|
|
|
//register 1,2:
|
|
|
|
int chr_0, chr_1;
|
|
|
|
|
|
|
|
//register 3:
|
2016-11-27 01:25:46 +00:00
|
|
|
public bool wram_disable;
|
2011-03-01 07:25:14 +00:00
|
|
|
int prg;
|
|
|
|
|
2013-07-27 23:31:29 +00:00
|
|
|
//regenerable state
|
|
|
|
IntBuffer chr_banks_4k = new IntBuffer(2);
|
2013-07-28 21:03:28 +00:00
|
|
|
IntBuffer prg_banks_16k = new IntBuffer(2);
|
2012-12-21 07:12:01 +00:00
|
|
|
|
2011-09-26 03:07:27 +00:00
|
|
|
public class MMC1_SerialController
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
2011-09-26 03:07:27 +00:00
|
|
|
//state
|
|
|
|
int shift_count, shift_val;
|
2011-03-01 07:25:14 +00:00
|
|
|
|
2011-09-26 03:07:27 +00:00
|
|
|
public void SyncState(Serializer ser)
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
2011-09-26 03:07:27 +00:00
|
|
|
ser.Sync("shift_count", ref shift_count);
|
|
|
|
ser.Sync("shift_val", ref shift_val);
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
2011-09-26 03:07:27 +00:00
|
|
|
|
|
|
|
public Action Reset;
|
|
|
|
public Action<int, int> WriteRegister;
|
|
|
|
|
2012-06-23 08:52:12 +00:00
|
|
|
public void ResetShift()
|
|
|
|
{
|
|
|
|
shift_count = shift_val = 0;
|
|
|
|
}
|
|
|
|
|
2011-09-26 03:07:27 +00:00
|
|
|
public void Write(int addr, byte value)
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
2011-09-26 03:07:27 +00:00
|
|
|
int data = value & 1;
|
|
|
|
int reset = (value >> 7) & 1;
|
|
|
|
if (reset == 1)
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
|
|
|
shift_count = 0;
|
|
|
|
shift_val = 0;
|
2011-09-26 03:07:27 +00:00
|
|
|
if (Reset != null)
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
shift_val >>= 1;
|
|
|
|
shift_val |= (data << 4);
|
|
|
|
shift_count++;
|
|
|
|
if (shift_count == 5)
|
|
|
|
{
|
|
|
|
WriteRegister(addr >> 13, shift_val);
|
|
|
|
shift_count = 0;
|
|
|
|
shift_val = 0;
|
|
|
|
}
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-26 03:07:27 +00:00
|
|
|
void SerialReset()
|
|
|
|
{
|
|
|
|
prg_mode = 1;
|
|
|
|
prg_slot = 1;
|
|
|
|
}
|
|
|
|
|
2012-06-23 08:52:12 +00:00
|
|
|
public void StandardReset()
|
2011-09-26 03:07:27 +00:00
|
|
|
{
|
|
|
|
prg_mode = 1;
|
|
|
|
prg_slot = 1;
|
|
|
|
chr_mode = 1;
|
2012-06-23 08:52:12 +00:00
|
|
|
scnt.Reset();
|
2011-09-26 03:07:27 +00:00
|
|
|
mirror = NES.NESBoardBase.EMirrorType.Horizontal;
|
2013-07-28 21:03:28 +00:00
|
|
|
SyncCHR();
|
|
|
|
SyncPRG();
|
2011-09-26 03:07:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Write(int addr, byte value)
|
|
|
|
{
|
|
|
|
scnt.Write(addr, value);
|
2012-12-21 07:12:01 +00:00
|
|
|
SyncCHR();
|
2013-07-27 23:31:29 +00:00
|
|
|
SyncPRG();
|
2011-09-26 03:07:27 +00:00
|
|
|
}
|
|
|
|
|
2012-06-23 08:52:12 +00:00
|
|
|
//logical register writes, called from the serial controller
|
|
|
|
public void SerialWriteRegister(int addr, int value)
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
|
|
|
switch (addr)
|
|
|
|
{
|
|
|
|
case 0: //8000-9FFF
|
|
|
|
mirror = _mirrorTypes[value & 3];
|
|
|
|
prg_slot = ((value >> 2) & 1);
|
|
|
|
prg_mode = ((value >> 3) & 1);
|
|
|
|
chr_mode = ((value >> 4) & 1);
|
|
|
|
break;
|
|
|
|
case 1: //A000-BFFF
|
|
|
|
chr_0 = value & 0x1F;
|
|
|
|
break;
|
|
|
|
case 2: //C000-DFFF
|
|
|
|
chr_1 = value & 0x1F;
|
|
|
|
break;
|
|
|
|
case 3: //E000-FFFF
|
|
|
|
prg = value & 0xF;
|
2016-11-27 01:25:46 +00:00
|
|
|
wram_disable = value.Bit(4) ? true : false;
|
2011-03-01 07:25:14 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-06-08 01:03:32 +00:00
|
|
|
//board.NES.LogLine("mapping.. chr_mode={0}, chr={1},{2}", chr_mode, chr_0, chr_1);
|
2012-07-14 18:43:37 +00:00
|
|
|
//board.NES.LogLine("mapping.. prg_mode={0}, prg_slot{1}, prg={2}", prg_mode, prg_slot, prg);
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
|
2012-12-21 07:12:01 +00:00
|
|
|
void SyncCHR()
|
|
|
|
{
|
|
|
|
if (chr_mode == 0)
|
|
|
|
{
|
|
|
|
chr_banks_4k[0] = chr_0 & ~1;
|
|
|
|
chr_banks_4k[1] = (chr_0 & ~1)+1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
chr_banks_4k[0] = chr_0;
|
|
|
|
chr_banks_4k[1] = chr_1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-27 23:31:29 +00:00
|
|
|
void SyncPRG()
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
|
|
|
if (prg_mode == 0)
|
2013-07-27 23:31:29 +00:00
|
|
|
{
|
|
|
|
//switch 32kb
|
|
|
|
prg_banks_16k[0] = prg & ~1;
|
|
|
|
prg_banks_16k[1] = (prg & ~1) + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//switch 16KB at...
|
|
|
|
if (prg_slot == 0)
|
|
|
|
{
|
|
|
|
//...$C000:
|
2014-01-11 22:53:31 +00:00
|
|
|
prg_banks_16k[0] = 0x00;
|
2013-07-27 23:31:29 +00:00
|
|
|
prg_banks_16k[1] = prg;
|
|
|
|
}
|
2011-03-01 07:25:14 +00:00
|
|
|
else
|
|
|
|
{
|
2013-07-27 23:31:29 +00:00
|
|
|
//...$8000:
|
2013-07-28 21:03:28 +00:00
|
|
|
prg_banks_16k[0] = prg;
|
2013-12-03 19:01:37 +00:00
|
|
|
prg_banks_16k[1] = 0x0F;
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
2013-07-27 23:31:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int Get_PRGBank(int addr)
|
|
|
|
{
|
|
|
|
int bank_16k = addr >> 14;
|
|
|
|
bank_16k = prg_banks_16k[bank_16k];
|
|
|
|
return bank_16k;
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public int Get_CHRBank_4K(int addr)
|
|
|
|
{
|
2012-12-21 07:12:01 +00:00
|
|
|
int bank_4k = addr >> 12;
|
|
|
|
bank_4k = chr_banks_4k[bank_4k];
|
|
|
|
return bank_4k;
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-17 00:25:46 +00:00
|
|
|
[NES.INESBoardImplPriority]
|
2011-03-01 07:25:14 +00:00
|
|
|
public class SxROM : NES.NESBoardBase
|
|
|
|
{
|
2011-03-01 09:32:12 +00:00
|
|
|
//configuration
|
2011-06-12 00:53:14 +00:00
|
|
|
protected int prg_mask, chr_mask;
|
2012-05-27 07:44:29 +00:00
|
|
|
protected int vram_mask;
|
2012-11-02 19:52:02 +00:00
|
|
|
const int pputimeout = 4; // i don't know if this is right, but anything lower will not boot Bill & Ted
|
2012-10-28 17:50:48 +00:00
|
|
|
bool disablemirror = false; // mapper 171: mmc1 without mirroring control
|
2011-03-01 07:25:14 +00:00
|
|
|
|
2016-11-09 01:05:02 +00:00
|
|
|
|
|
|
|
//the VS actually does have 2 KB of nametable address space
|
|
|
|
//let's make the extra space here, instead of in the main NES to avoid confusion
|
|
|
|
byte[] CIRAM_VS = new byte[0x800];
|
|
|
|
|
2016-11-27 01:25:46 +00:00
|
|
|
//for snrom wram disable
|
|
|
|
public bool _is_snrom;
|
|
|
|
public bool chr_wram_enable = true;
|
2016-11-09 01:05:02 +00:00
|
|
|
|
2011-03-01 09:32:12 +00:00
|
|
|
//state
|
2012-06-23 08:52:12 +00:00
|
|
|
public MMC1 mmc1;
|
2012-11-02 19:52:02 +00:00
|
|
|
/// <summary>number of cycles since last WritePRG()</summary>
|
|
|
|
uint ppuclock;
|
|
|
|
|
|
|
|
public override void ClockPPU()
|
|
|
|
{
|
|
|
|
if (ppuclock < pputimeout)
|
|
|
|
ppuclock++;
|
|
|
|
}
|
2011-03-01 07:25:14 +00:00
|
|
|
|
|
|
|
public override void WritePRG(int addr, byte value)
|
|
|
|
{
|
2012-09-30 01:32:10 +00:00
|
|
|
// mmc1 ignores subsequent writes that are very close together
|
2012-11-02 19:52:02 +00:00
|
|
|
if (ppuclock >= pputimeout)
|
2012-09-30 01:32:10 +00:00
|
|
|
{
|
2012-11-02 19:52:02 +00:00
|
|
|
ppuclock = 0;
|
2012-09-30 01:32:10 +00:00
|
|
|
mmc1.Write(addr, value);
|
2012-10-28 17:50:48 +00:00
|
|
|
if (!disablemirror)
|
|
|
|
SetMirrorType(mmc1.mirror); //often redundant, but gets the job done
|
2012-09-30 01:32:10 +00:00
|
|
|
}
|
2016-11-27 01:25:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public override byte ReadWRAM(int addr)
|
|
|
|
{
|
|
|
|
if (_is_snrom)
|
|
|
|
{
|
2016-11-29 16:07:06 +00:00
|
|
|
if (!mmc1.wram_disable && chr_wram_enable)
|
2016-11-27 01:25:46 +00:00
|
|
|
return base.ReadWRAM(addr);
|
|
|
|
else
|
|
|
|
return NES.DB;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return base.ReadWRAM(addr);
|
|
|
|
}
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override byte ReadPRG(int addr)
|
|
|
|
{
|
|
|
|
int bank = mmc1.Get_PRGBank(addr) & prg_mask;
|
|
|
|
addr = (bank << 14) | (addr & 0x3FFF);
|
2011-03-07 10:41:46 +00:00
|
|
|
return ROM[addr];
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int Gen_CHR_Address(int addr)
|
|
|
|
{
|
|
|
|
int bank = mmc1.Get_CHRBank_4K(addr);
|
|
|
|
addr = ((bank & chr_mask) << 12) | (addr & 0x0FFF);
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override byte ReadPPU(int addr)
|
|
|
|
{
|
2016-11-29 16:07:06 +00:00
|
|
|
|
|
|
|
|
2011-03-01 07:25:14 +00:00
|
|
|
if (addr < 0x2000)
|
|
|
|
{
|
2016-11-29 16:07:06 +00:00
|
|
|
if (_is_snrom)
|
|
|
|
{
|
|
|
|
// WRAM enable is tied to ppu a12
|
|
|
|
if (Gen_CHR_Address(addr).Bit(16))
|
|
|
|
chr_wram_enable = false;
|
|
|
|
else
|
|
|
|
chr_wram_enable = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = Gen_CHR_Address(addr);
|
2016-11-27 01:25:46 +00:00
|
|
|
|
2011-03-08 07:25:35 +00:00
|
|
|
if (Cart.vram_size != 0)
|
2016-11-29 16:07:06 +00:00
|
|
|
return VRAM[addr & vram_mask];
|
|
|
|
else return VROM[addr];
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
2016-11-09 01:05:02 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (NES._isVS)
|
|
|
|
{
|
|
|
|
addr = addr - 0x2000;
|
|
|
|
if (addr < 0x800)
|
|
|
|
{
|
|
|
|
return NES.CIRAM[addr];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return CIRAM_VS[addr - 0x800];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return base.ReadPPU(addr);
|
2016-11-29 16:07:06 +00:00
|
|
|
|
2016-11-09 01:05:02 +00:00
|
|
|
}
|
2016-11-29 16:07:06 +00:00
|
|
|
}
|
2011-03-01 07:25:14 +00:00
|
|
|
|
|
|
|
public override void WritePPU(int addr, byte value)
|
|
|
|
{
|
2016-11-29 16:07:06 +00:00
|
|
|
|
2016-11-09 01:05:02 +00:00
|
|
|
|
|
|
|
if (NES._isVS)
|
|
|
|
{
|
|
|
|
if (addr < 0x2000)
|
|
|
|
{
|
|
|
|
if (VRAM != null)
|
|
|
|
VRAM[Gen_CHR_Address(addr) & vram_mask] = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
addr = addr - 0x2000;
|
|
|
|
if (addr < 0x800)
|
|
|
|
{
|
|
|
|
NES.CIRAM[addr] = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CIRAM_VS[addr - 0x800] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (addr < 0x2000)
|
2011-03-01 07:25:14 +00:00
|
|
|
{
|
2016-11-29 16:07:06 +00:00
|
|
|
if (_is_snrom)
|
|
|
|
{
|
|
|
|
// WRAM enable is tied to ppu a12
|
|
|
|
if (Gen_CHR_Address(addr).Bit(16))
|
|
|
|
chr_wram_enable = false;
|
|
|
|
else
|
|
|
|
chr_wram_enable = true;
|
|
|
|
}
|
2016-11-27 01:25:46 +00:00
|
|
|
|
2011-03-08 07:25:35 +00:00
|
|
|
if (Cart.vram_size != 0)
|
2016-09-21 20:30:10 +00:00
|
|
|
VRAM[Gen_CHR_Address(addr) & vram_mask] = value;
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
|
|
|
else base.WritePPU(addr, value);
|
|
|
|
}
|
|
|
|
|
2011-04-17 22:51:53 +00:00
|
|
|
public override void SyncState(Serializer ser)
|
2011-03-01 09:32:12 +00:00
|
|
|
{
|
2011-04-17 22:51:53 +00:00
|
|
|
base.SyncState(ser);
|
|
|
|
mmc1.SyncState(ser);
|
2012-11-02 19:52:02 +00:00
|
|
|
ser.Sync("ppuclock", ref ppuclock);
|
2016-11-27 01:25:46 +00:00
|
|
|
ser.Sync("chr wram enable", ref chr_wram_enable);
|
2016-11-09 01:05:02 +00:00
|
|
|
if (NES._isVS)
|
|
|
|
ser.Sync("VS_CIRAM", ref CIRAM_VS, false);
|
2011-03-01 09:32:12 +00:00
|
|
|
}
|
2011-03-21 06:03:58 +00:00
|
|
|
|
2011-03-20 02:12:10 +00:00
|
|
|
public override bool Configure(NES.EDetectionOrigin origin)
|
2011-03-07 10:41:46 +00:00
|
|
|
{
|
2011-03-08 07:25:35 +00:00
|
|
|
switch (Cart.board_type)
|
2011-03-07 10:41:46 +00:00
|
|
|
{
|
2012-06-23 08:52:12 +00:00
|
|
|
case "MAPPER116_HACKY":
|
|
|
|
break;
|
2016-11-09 01:05:02 +00:00
|
|
|
case "MAPPER001_VS":
|
|
|
|
// VS mapper MMC1
|
|
|
|
NES._isVS = true;
|
|
|
|
//update the state of the dip switches
|
|
|
|
//this is only done at power on
|
|
|
|
NES.VS_dips[0] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_1 ? 1 : 0);
|
2016-11-10 13:51:01 +00:00
|
|
|
NES.VS_dips[1] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_2 ? 1 : 0);
|
|
|
|
NES.VS_dips[2] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_3 ? 1 : 0);
|
|
|
|
NES.VS_dips[3] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_4 ? 1 : 0);
|
|
|
|
NES.VS_dips[4] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_5 ? 1 : 0);
|
|
|
|
NES.VS_dips[5] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_6 ? 1 : 0);
|
|
|
|
NES.VS_dips[6] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_7 ? 1 : 0);
|
|
|
|
NES.VS_dips[7] = (byte)(NES.SyncSettings.VSDipswitches.Dip_Switch_8 ? 1 : 0);
|
2016-11-09 01:05:02 +00:00
|
|
|
break;
|
2012-03-22 06:20:10 +00:00
|
|
|
case "MAPPER001":
|
2014-01-21 23:01:38 +00:00
|
|
|
// there's no way to define PRG oversize for mapper001 due to how the MMC1 regs work
|
|
|
|
// so 512KB must mean SUROM or SXROM. SUROM is more common, so we try that
|
|
|
|
if (Cart.prg_size > 256)
|
|
|
|
return false;
|
2012-03-22 06:20:10 +00:00
|
|
|
break;
|
2012-10-28 17:50:48 +00:00
|
|
|
case "MAPPER171": // Tui Do Woo Ma Jeung
|
|
|
|
AssertPrg(32); AssertChr(32); Cart.wram_size = 0;
|
|
|
|
disablemirror = true;
|
|
|
|
SetMirrorType(Cart.pad_h, Cart.pad_v);
|
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SAROM": //dragon warrior
|
|
|
|
AssertPrg(64); AssertChr(16, 32, 64); AssertVram(0); AssertWram(8);
|
2011-03-07 10:41:46 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SBROM": //dance aerobics
|
|
|
|
AssertPrg(64); AssertChr(16, 32, 64); AssertVram(0); AssertWram(0);
|
2011-03-07 10:41:46 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SCROM": //mechanized attack
|
|
|
|
case "NES-SC1ROM": //knight rider
|
|
|
|
AssertPrg(64); AssertChr(128); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SEROM": //lolo
|
|
|
|
case "HVC-SEROM": //dr. mario
|
2012-04-09 06:38:28 +00:00
|
|
|
AssertPrg(32); AssertChr(16,32); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SFROM": //bubble bobble
|
2014-01-16 00:22:40 +00:00
|
|
|
case "HVC-SFROM":
|
2014-01-15 17:00:02 +00:00
|
|
|
case "NES-SF1ROM":
|
2011-06-08 01:03:32 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(16, 32, 64); AssertVram(0); AssertWram(0);
|
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SGROM": //bionic commando
|
2011-06-09 19:45:07 +00:00
|
|
|
case "HVC-SGROM": //Ankoku Shinwa - Yamato Takeru Densetsu (J)
|
2011-03-08 07:25:35 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(0); AssertVram(8); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SHROM": //family feud
|
|
|
|
case "NES-SH1ROM": //airwolf
|
|
|
|
AssertPrg(32); AssertChr(128); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2015-08-16 12:07:50 +00:00
|
|
|
case "HVC-SIROM":
|
|
|
|
// NTF2 System Cart (U)
|
|
|
|
// bootod classifies Igo: Kyuu Roban Taikyoku as HVC-SIROM-02 with 16kb Chr online, but has this board name in the xml
|
|
|
|
AssertPrg(32); AssertChr(16, 64); AssertVram(0); AssertWram(8);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2014-01-15 17:00:02 +00:00
|
|
|
case "HVC-SJROM": //zombie hunter (wram is missing), artelius.
|
|
|
|
AssertPrg(128); AssertChr(32); AssertVram(0); AssertWram(0, 8);
|
2013-07-31 20:15:02 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SJROM": //air fortress
|
2011-06-08 01:03:32 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(16, 32, 64); AssertVram(0); AssertWram(8);
|
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SKROM": //zelda 2
|
2011-06-08 01:03:32 +00:00
|
|
|
case "HVC-SKROM": //ad&d dragons of flame (J)
|
2011-03-08 07:25:35 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(128); AssertVram(0); AssertWram(8);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2014-01-23 23:08:56 +00:00
|
|
|
case "HVC-SKEPROM":
|
2014-01-18 16:16:24 +00:00
|
|
|
case "NES-SKEPROM": // chip n dale 2 (proto)
|
|
|
|
AssertPrg(128, 256); AssertChr(128); AssertVram(0); AssertWram(0, 8);
|
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SLROM": //castlevania 2
|
2011-06-08 01:03:32 +00:00
|
|
|
case "KONAMI-SLROM": //bayou billy
|
|
|
|
case "HVC-SLROM": //Adventures of Lolo 2 (J)
|
2011-03-08 07:25:35 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(128); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2014-01-23 23:08:56 +00:00
|
|
|
case "HVC-SL1ROM": // untested
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SL1ROM": //hoops
|
|
|
|
AssertPrg(64, 128, 256); AssertChr(128); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SL2ROM": //blaster master
|
|
|
|
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SL3ROM": //goal!
|
|
|
|
AssertPrg(256); AssertChr(128); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SLRROM": //tecmo bowl
|
2014-01-15 18:18:49 +00:00
|
|
|
case "HVC-SLRROM":
|
2011-03-08 07:25:35 +00:00
|
|
|
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "HVC-SMROM": //Hokkaidou Rensa Satsujin: Okhotsu ni Shoyu
|
|
|
|
AssertPrg(256); AssertChr(0); AssertVram(8); AssertWram(0);
|
2011-06-08 01:03:32 +00:00
|
|
|
break;
|
2015-08-16 11:48:46 +00:00
|
|
|
case "HVC-SNROM": // Morita Kazuo no Shougi (J)
|
2016-06-27 14:41:51 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(0, 8); AssertVram(0, 8); AssertWram(8);
|
2015-08-16 11:48:46 +00:00
|
|
|
break;
|
2015-09-08 00:26:03 +00:00
|
|
|
case "HVC-SNROM-03": // Dragon Quest III
|
|
|
|
AssertPrg(128, 256); AssertChr(0); AssertVram(8); AssertWram(8);
|
|
|
|
break;
|
2011-03-08 07:25:35 +00:00
|
|
|
case "NES-SNROM": //dragon warrior 2
|
2016-11-27 01:25:46 +00:00
|
|
|
_is_snrom = true;
|
|
|
|
AssertPrg(128, 256); AssertChr(0); AssertVram(8); AssertWram(8);
|
|
|
|
break;
|
2014-01-15 23:27:02 +00:00
|
|
|
case "VIRGIN-SNROM":
|
2014-01-18 15:57:43 +00:00
|
|
|
case "NES-SNWEPROM": // final fantasy 2 (proto)
|
2011-06-08 01:03:32 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(0); AssertVram(8); AssertWram(8);
|
2011-03-07 10:41:46 +00:00
|
|
|
break;
|
2011-09-06 01:51:44 +00:00
|
|
|
case "SxROM-JUNK":
|
|
|
|
break;
|
2011-03-07 10:41:46 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-06-12 00:53:14 +00:00
|
|
|
BaseConfigure();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void BaseConfigure()
|
|
|
|
{
|
2014-02-13 18:14:01 +00:00
|
|
|
mmc1 = new MMC1();
|
2011-03-08 07:25:35 +00:00
|
|
|
prg_mask = (Cart.prg_size / 16) - 1;
|
2011-03-21 06:03:58 +00:00
|
|
|
vram_mask = (Cart.vram_size*1024) - 1;
|
2011-03-08 07:25:35 +00:00
|
|
|
chr_mask = (Cart.chr_size / 8 * 2) - 1;
|
2016-09-03 17:53:27 +00:00
|
|
|
//Chip n Dale (PC10) has a nonstandard chr size, which makes the mask nonsense
|
|
|
|
// let's put in a special case to deal with it
|
|
|
|
if (Cart.chr_size==136)
|
|
|
|
{
|
|
|
|
chr_mask = (128 / 8 * 2) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-28 17:50:48 +00:00
|
|
|
if (!disablemirror)
|
|
|
|
SetMirrorType(mmc1.mirror);
|
2012-11-02 19:52:02 +00:00
|
|
|
ppuclock = pputimeout;
|
2011-06-12 00:53:14 +00:00
|
|
|
}
|
|
|
|
|
2013-07-27 23:31:29 +00:00
|
|
|
public override void Dispose()
|
|
|
|
{
|
|
|
|
base.Dispose();
|
2013-07-28 21:03:28 +00:00
|
|
|
if(mmc1 != null) mmc1.Dispose();
|
2013-07-27 23:31:29 +00:00
|
|
|
}
|
2011-06-12 00:53:14 +00:00
|
|
|
} //class SxROM
|
|
|
|
|
|
|
|
|
|
|
|
class SoROM : SuROM
|
|
|
|
{
|
|
|
|
//this uses a CHR bit to select WRAM banks
|
|
|
|
//TODO - only the latter 8KB is supposed to be battery backed
|
|
|
|
public override bool Configure(NES.EDetectionOrigin origin)
|
|
|
|
{
|
|
|
|
switch (Cart.board_type)
|
|
|
|
{
|
|
|
|
case "NES-SOROM": //Nobunaga's Ambition
|
2014-01-15 18:18:49 +00:00
|
|
|
case "HVC-SOROM": // KHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN
|
2011-06-12 00:53:14 +00:00
|
|
|
AssertPrg(128, 256); AssertChr(0); AssertVram(8); AssertWram(16);
|
|
|
|
break;
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BaseConfigure();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Map_WRAM(int addr)
|
|
|
|
{
|
|
|
|
//$A000-BFFF: [...R ...C]
|
|
|
|
// R = PRG-RAM page select
|
|
|
|
// C = CHR reg 0
|
|
|
|
//* BUT THIS IS WRONG ??? R IS ONE BIT LOWER !!!??? ?!? *
|
|
|
|
int chr_bank = mmc1.Get_CHRBank_4K(0);
|
|
|
|
int ofs = addr & ((8 * 1024) - 1);
|
|
|
|
int wram_bank_8k = (chr_bank >> 3) & 1;
|
|
|
|
return (wram_bank_8k << 13) | ofs;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void WriteWRAM(int addr, byte value)
|
|
|
|
{
|
|
|
|
base.WriteWRAM(Map_WRAM(addr), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override byte ReadWRAM(int addr)
|
|
|
|
{
|
|
|
|
return base.ReadWRAM(Map_WRAM(addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class SXROM : SuROM
|
|
|
|
{
|
|
|
|
//SXROM's PRG behaves similar to SuROM (and so inherits from it)
|
|
|
|
//it also has some WRAM select bits like SoROM
|
|
|
|
public override bool Configure(NES.EDetectionOrigin origin)
|
|
|
|
{
|
|
|
|
switch (Cart.board_type)
|
|
|
|
{
|
|
|
|
case "HVC-SXROM": //final fantasy 1& 2
|
|
|
|
AssertPrg(128, 256, 512); AssertChr(0); AssertVram(8); AssertWram(32);
|
|
|
|
break;
|
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BaseConfigure();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Map_WRAM(int addr)
|
|
|
|
{
|
|
|
|
//$A000-BFFF: [...P RR..]
|
|
|
|
// P = PRG-ROM 256k block select (just like on SUROM)
|
|
|
|
// R = PRG-RAM page select (selects 8k @ $6000-7FFF, just like SOROM)
|
|
|
|
int chr_bank = mmc1.Get_CHRBank_4K(0);
|
|
|
|
int ofs = addr & ((8 * 1024) - 1);
|
|
|
|
int wram_bank_8k = (chr_bank >> 2) & 3;
|
|
|
|
return (wram_bank_8k << 13) | ofs;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void WriteWRAM(int addr, byte value)
|
|
|
|
{
|
|
|
|
base.WriteWRAM(Map_WRAM(addr), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override byte ReadWRAM(int addr)
|
|
|
|
{
|
|
|
|
return base.ReadWRAM(Map_WRAM(addr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-04 03:51:17 +00:00
|
|
|
|
2011-06-12 00:53:14 +00:00
|
|
|
class SuROM : SxROM
|
|
|
|
{
|
|
|
|
public override bool Configure(NES.EDetectionOrigin origin)
|
|
|
|
{
|
|
|
|
//SUROM uses CHR A16 to control the upper address line (PRG A18) of its 512KB PRG ROM.
|
|
|
|
|
|
|
|
switch (Cart.board_type)
|
|
|
|
{
|
2014-01-21 23:01:38 +00:00
|
|
|
case "MAPPER001":
|
|
|
|
// we try to heuristic match to iNES 001 roms with big PRG only
|
|
|
|
if (Cart.prg_size <= 256)
|
|
|
|
return false;
|
|
|
|
AssertPrg(512); AssertChr(0);
|
|
|
|
Cart.vram_size = 8;
|
|
|
|
Cart.wram_size = 8;
|
|
|
|
Cart.wram_battery = true; // all SUROM boards had batteries
|
2014-01-22 01:07:54 +00:00
|
|
|
Console.WriteLine("Guessing SUROM for 512KiB PRG ROM");
|
2014-01-21 23:01:38 +00:00
|
|
|
break;
|
2011-06-12 00:53:14 +00:00
|
|
|
case "NES-SUROM": //dragon warrior 4
|
|
|
|
case "HVC-SUROM":
|
|
|
|
AssertPrg(512); AssertChr(0); AssertVram(8); AssertWram(8);
|
|
|
|
break;
|
2014-01-21 23:01:38 +00:00
|
|
|
default:
|
|
|
|
return false;
|
2011-06-12 00:53:14 +00:00
|
|
|
}
|
2011-03-07 10:41:46 +00:00
|
|
|
|
2011-06-12 00:53:14 +00:00
|
|
|
BaseConfigure();
|
2011-03-07 10:41:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-06-12 00:53:14 +00:00
|
|
|
public override byte ReadPRG(int addr)
|
|
|
|
{
|
|
|
|
int bank = mmc1.Get_PRGBank(addr);
|
|
|
|
int chr_bank = mmc1.Get_CHRBank_4K(0);
|
|
|
|
int bank_bit18 = chr_bank >> 4;
|
|
|
|
bank |= (bank_bit18 << 4);
|
|
|
|
bank &= prg_mask;
|
|
|
|
addr = (bank << 14) | (addr & 0x3FFF);
|
|
|
|
return ROM[addr];
|
|
|
|
}
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|
2011-06-12 00:53:14 +00:00
|
|
|
|
2011-03-01 07:25:14 +00:00
|
|
|
}
|