forked from sin365/AxibugEmuOnline
VitrualNes.Core 核心从Unity里迁移到.NetStandard 2.0独立项目
This commit is contained in:
parent
dc9963a431
commit
135517065e
@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AxibugEmuOnline.Web", "Axib
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AxibugEmuOnline.Server", "AxibugEmuOnline.Server\AxibugEmuOnline.Server.csproj", "{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualNes.Core", "VirtualNes.Core\VirtualNes.Core.csproj", "{8A4771D6-74B9-453C-9932-6B774280E325}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -21,6 +23,10 @@ Global
|
||||
{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
655
Core/VirtualNes.Core/APU.cs
Normal file
655
Core/VirtualNes.Core/APU.cs
Normal file
@ -0,0 +1,655 @@
|
||||
|
||||
using System;
|
||||
using VirtualNes.Core.Debug;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU
|
||||
{
|
||||
public const uint QUEUE_LENGTH = 8192;
|
||||
|
||||
// Volume adjust
|
||||
// Internal sounds
|
||||
public const uint RECTANGLE_VOL = 0x0F0;
|
||||
public const uint TRIANGLE_VOL = 0x130;
|
||||
public const uint NOISE_VOL = 0x0C0;
|
||||
public const uint DPCM_VOL = 0x0F0;
|
||||
// Extra sounds
|
||||
public const uint VRC6_VOL = 0x0F0;
|
||||
public const uint VRC7_VOL = 0x130;
|
||||
public const uint FDS_VOL = 0x0F0;
|
||||
public const uint MMC5_VOL = 0x0F0;
|
||||
public const uint N106_VOL = 0x088;
|
||||
public const uint FME7_VOL = 0x130;
|
||||
|
||||
private NES nes;
|
||||
private byte exsound_select;
|
||||
private APU_INTERNAL @internal = new APU_INTERNAL();
|
||||
private APU_VRC6 vrc6 = new APU_VRC6();
|
||||
private APU_VRC7 vrc7 = new APU_VRC7();
|
||||
private APU_MMC5 mmc5 = new APU_MMC5();
|
||||
private APU_FDS fds = new APU_FDS();
|
||||
private APU_N106 n106 = new APU_N106();
|
||||
private APU_FME7 fme7 = new APU_FME7();
|
||||
private int last_data;
|
||||
private int last_diff;
|
||||
protected short[] m_SoundBuffer = new short[256];
|
||||
protected int[] lowpass_filter = new int[4];
|
||||
protected QUEUE queue = new QUEUE();
|
||||
protected QUEUE exqueue = new QUEUE();
|
||||
protected bool[] m_bMute = new bool[16];
|
||||
protected double elapsed_time;
|
||||
|
||||
public APU(NES parent)
|
||||
{
|
||||
exsound_select = 0;
|
||||
|
||||
nes = parent;
|
||||
@internal.SetParent(parent);
|
||||
|
||||
last_data = last_diff = 0;
|
||||
|
||||
Array.Clear(m_SoundBuffer, 0, m_SoundBuffer.Length);
|
||||
Array.Clear(lowpass_filter, 0, lowpass_filter.Length);
|
||||
|
||||
for (int i = 0; i < m_bMute.Length; i++)
|
||||
m_bMute[i] = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@internal.Dispose();
|
||||
vrc6.Dispose();
|
||||
vrc7.Dispose();
|
||||
mmc5.Dispose();
|
||||
fds.Dispose();
|
||||
n106.Dispose();
|
||||
fme7.Dispose();
|
||||
}
|
||||
|
||||
private int[] vol = new int[24];
|
||||
static double cutofftemp = (2.0 * 3.141592653579 * 40.0);
|
||||
static double tmp = 0.0;
|
||||
|
||||
public void Process(ISoundDataBuffer lpBuffer, uint dwSize)
|
||||
{
|
||||
int nBits = Supporter.Config.sound.nBits;
|
||||
uint dwLength = (uint)(dwSize / (nBits / 8));
|
||||
int output;
|
||||
QUEUEDATA q = new QUEUEDATA();
|
||||
uint writetime;
|
||||
|
||||
var pSoundBuf = m_SoundBuffer;
|
||||
int nCcount = 0;
|
||||
|
||||
int nFilterType = Supporter.Config.sound.nFilterType;
|
||||
|
||||
if (!Supporter.Config.sound.bEnable)
|
||||
{
|
||||
byte empty = (byte)(Supporter.Config.sound.nRate == 8 ? 128 : 0);
|
||||
for (int i = 0; i < dwSize; i++)
|
||||
lpBuffer.WriteByte(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
// Volume setup
|
||||
// 0:Master
|
||||
// 1:Rectangle 1
|
||||
// 2:Rectangle 2
|
||||
// 3:Triangle
|
||||
// 4:Noise
|
||||
// 5:DPCM
|
||||
// 6:VRC6
|
||||
// 7:VRC7
|
||||
// 8:FDS
|
||||
// 9:MMC5
|
||||
// 10:N106
|
||||
// 11:FME7
|
||||
MemoryUtility.ZEROMEMORY(vol, vol.Length);
|
||||
|
||||
var bMute = m_bMute;
|
||||
var nVolume = Supporter.Config.sound.nVolume;
|
||||
|
||||
int nMasterVolume = bMute[0] ? nVolume[0] : 0;
|
||||
|
||||
// Internal
|
||||
vol[0] = (int)(bMute[1] ? (RECTANGLE_VOL * nVolume[1] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[1] = (int)(bMute[2] ? (RECTANGLE_VOL * nVolume[2] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[2] = (int)(bMute[3] ? (TRIANGLE_VOL * nVolume[3] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[3] = (int)(bMute[4] ? (NOISE_VOL * nVolume[4] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[4] = (int)(bMute[5] ? (DPCM_VOL * nVolume[5] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// VRC6
|
||||
vol[5] = (int)(bMute[6] ? (VRC6_VOL * nVolume[6] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[6] = (int)(bMute[7] ? (VRC6_VOL * nVolume[6] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[7] = (int)(bMute[8] ? (VRC6_VOL * nVolume[6] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// VRC7
|
||||
vol[8] = (int)(bMute[6] ? (VRC7_VOL * nVolume[7] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// FDS
|
||||
vol[9] = (int)(bMute[6] ? (FDS_VOL * nVolume[8] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// MMC5
|
||||
vol[10] = (int)(bMute[6] ? (MMC5_VOL * nVolume[9] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[11] = (int)(bMute[7] ? (MMC5_VOL * nVolume[9] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[12] = (int)(bMute[8] ? (MMC5_VOL * nVolume[9] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// N106
|
||||
vol[13] = (int)(bMute[6] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[14] = (int)(bMute[7] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[15] = (int)(bMute[8] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[16] = (int)(bMute[9] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[17] = (int)(bMute[10] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[18] = (int)(bMute[11] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[19] = (int)(bMute[12] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[20] = (int)(bMute[13] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// FME7
|
||||
vol[21] = (int)(bMute[6] ? (FME7_VOL * nVolume[11] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[22] = (int)(bMute[7] ? (FME7_VOL * nVolume[11] * nMasterVolume) / (100 * 100) : 0);
|
||||
vol[23] = (int)(bMute[8] ? (FME7_VOL * nVolume[11] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// double cycle_rate = ((double)FRAME_CYCLES*60.0/12.0)/(double)Config.sound.nRate;
|
||||
double cycle_rate = (nes.nescfg.FrameCycles * 60.0 / 12.0) / Supporter.Config.sound.nRate;
|
||||
|
||||
// CPUサイクル数がループしてしまった時の対策処理
|
||||
if (elapsed_time > nes.cpu.GetTotalCycles())
|
||||
{
|
||||
QueueFlush();
|
||||
}
|
||||
|
||||
while ((dwLength--) != 0)
|
||||
{
|
||||
writetime = (uint)elapsed_time;
|
||||
|
||||
while (GetQueue((int)writetime, ref q))
|
||||
{
|
||||
WriteProcess(q.addr, q.data);
|
||||
}
|
||||
|
||||
while (GetExQueue((int)writetime, ref q))
|
||||
{
|
||||
WriteExProcess(q.addr, q.data);
|
||||
}
|
||||
|
||||
// 0-4:internal 5-7:VRC6 8:VRC7 9:FDS 10-12:MMC5 13-20:N106 21-23:FME7
|
||||
output = 0;
|
||||
output += @internal.Process(0) * vol[0];
|
||||
output += @internal.Process(1) * vol[1];
|
||||
output += @internal.Process(2) * vol[2];
|
||||
output += @internal.Process(3) * vol[3];
|
||||
output += @internal.Process(4) * vol[4];
|
||||
|
||||
if ((exsound_select & 0x01) != 0)
|
||||
{
|
||||
output += vrc6.Process(0) * vol[5];
|
||||
output += vrc6.Process(1) * vol[6];
|
||||
output += vrc6.Process(2) * vol[7];
|
||||
}
|
||||
if ((exsound_select & 0x02) != 0)
|
||||
{
|
||||
output += vrc7.Process(0) * vol[8];
|
||||
}
|
||||
if ((exsound_select & 0x04) != 0)
|
||||
{
|
||||
output += fds.Process(0) * vol[9];
|
||||
}
|
||||
if ((exsound_select & 0x08) != 0)
|
||||
{
|
||||
output += mmc5.Process(0) * vol[10];
|
||||
output += mmc5.Process(1) * vol[11];
|
||||
output += mmc5.Process(2) * vol[12];
|
||||
}
|
||||
if ((exsound_select & 0x10) != 0)
|
||||
{
|
||||
output += n106.Process(0) * vol[13];
|
||||
output += n106.Process(1) * vol[14];
|
||||
output += n106.Process(2) * vol[15];
|
||||
output += n106.Process(3) * vol[16];
|
||||
output += n106.Process(4) * vol[17];
|
||||
output += n106.Process(5) * vol[18];
|
||||
output += n106.Process(6) * vol[19];
|
||||
output += n106.Process(7) * vol[20];
|
||||
}
|
||||
if ((exsound_select & 0x20) != 0)
|
||||
{
|
||||
fme7.Process(3); // Envelope & Noise
|
||||
output += fme7.Process(0) * vol[21];
|
||||
output += fme7.Process(1) * vol[22];
|
||||
output += fme7.Process(2) * vol[23];
|
||||
}
|
||||
|
||||
output >>= 8;
|
||||
|
||||
if (nFilterType == 1)
|
||||
{
|
||||
//ローパスフィルターTYPE 1(Simple)
|
||||
output = (lowpass_filter[0] + output) / 2;
|
||||
lowpass_filter[0] = output;
|
||||
}
|
||||
else if (nFilterType == 2)
|
||||
{
|
||||
//ローパスフィルターTYPE 2(Weighted type 1)
|
||||
output = (lowpass_filter[1] + lowpass_filter[0] + output) / 3;
|
||||
lowpass_filter[1] = lowpass_filter[0];
|
||||
lowpass_filter[0] = output;
|
||||
}
|
||||
else if (nFilterType == 3)
|
||||
{
|
||||
//ローパスフィルターTYPE 3(Weighted type 2)
|
||||
output = (lowpass_filter[2] + lowpass_filter[1] + lowpass_filter[0] + output) / 4;
|
||||
lowpass_filter[2] = lowpass_filter[1];
|
||||
lowpass_filter[1] = lowpass_filter[0];
|
||||
lowpass_filter[0] = output;
|
||||
}
|
||||
else if (nFilterType == 4)
|
||||
{
|
||||
//ローパスフィルターTYPE 4(Weighted type 3)
|
||||
output = (lowpass_filter[1] + lowpass_filter[0] * 2 + output) / 4;
|
||||
lowpass_filter[1] = lowpass_filter[0];
|
||||
lowpass_filter[0] = output;
|
||||
}
|
||||
// DC成分のカット(HPF TEST)
|
||||
{
|
||||
// static double cutoff = (2.0*3.141592653579*40.0/44100.0);
|
||||
double cutoff = cutofftemp / Supporter.Config.sound.nRate;
|
||||
double @in, @out;
|
||||
|
||||
@in = output;
|
||||
@out = (@in - tmp);
|
||||
tmp = tmp + cutoff * @out;
|
||||
|
||||
output = (int)@out;
|
||||
}
|
||||
|
||||
// Limit
|
||||
if (output > 0x7FFF)
|
||||
{
|
||||
output = 0x7FFF;
|
||||
}
|
||||
else if (output < -0x8000)
|
||||
{
|
||||
output = -0x8000;
|
||||
}
|
||||
|
||||
if (nBits != 8)
|
||||
{
|
||||
byte highByte = (byte)(output >> 8); // 获取高8位
|
||||
byte lowByte = (byte)(output & 0xFF); // 获取低8位
|
||||
lpBuffer.WriteByte(highByte);
|
||||
lpBuffer.WriteByte(lowByte);
|
||||
}
|
||||
else
|
||||
{
|
||||
lpBuffer.WriteByte((byte)((output >> 8) ^ 0x80));
|
||||
}
|
||||
|
||||
if (nCcount < 0x0100)
|
||||
pSoundBuf[nCcount++] = (short)output;
|
||||
|
||||
// elapsedtime += cycle_rate;
|
||||
elapsed_time += cycle_rate;
|
||||
}
|
||||
|
||||
|
||||
if (elapsed_time > ((nes.nescfg.FrameCycles / 24) + nes.cpu.GetTotalCycles()))
|
||||
{
|
||||
elapsed_time = nes.cpu.GetTotalCycles();
|
||||
}
|
||||
if ((elapsed_time + (nes.nescfg.FrameCycles / 6)) < nes.cpu.GetTotalCycles())
|
||||
{
|
||||
elapsed_time = nes.cpu.GetTotalCycles();
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetExQueue(int writetime, ref QUEUEDATA ret)
|
||||
{
|
||||
if (exqueue.wrptr == exqueue.rdptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (exqueue.data[exqueue.rdptr].time <= writetime)
|
||||
{
|
||||
ret = exqueue.data[exqueue.rdptr];
|
||||
exqueue.rdptr++;
|
||||
exqueue.rdptr = (int)(exqueue.rdptr & (QUEUE_LENGTH - 1));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void QueueFlush()
|
||||
{
|
||||
while (queue.wrptr != queue.rdptr)
|
||||
{
|
||||
WriteProcess(queue.data[queue.rdptr].addr, queue.data[queue.rdptr].data);
|
||||
queue.rdptr++;
|
||||
queue.rdptr = (int)(queue.rdptr & (QUEUE_LENGTH - 1));
|
||||
}
|
||||
|
||||
while (exqueue.wrptr != exqueue.rdptr)
|
||||
{
|
||||
WriteExProcess(exqueue.data[exqueue.rdptr].addr, exqueue.data[exqueue.rdptr].data);
|
||||
exqueue.rdptr++;
|
||||
exqueue.rdptr = (int)(exqueue.rdptr & (QUEUE_LENGTH - 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteExProcess(ushort addr, byte data)
|
||||
{
|
||||
if ((exsound_select & 0x01) != 0)
|
||||
{
|
||||
vrc6.Write(addr, data);
|
||||
}
|
||||
if ((exsound_select & 0x02) != 0)
|
||||
{
|
||||
vrc7.Write(addr, data);
|
||||
}
|
||||
if ((exsound_select & 0x04) != 0)
|
||||
{
|
||||
fds.Write(addr, data);
|
||||
}
|
||||
if ((exsound_select & 0x08) != 0)
|
||||
{
|
||||
mmc5.Write(addr, data);
|
||||
}
|
||||
if ((exsound_select & 0x10) != 0)
|
||||
{
|
||||
if (addr == 0x0000)
|
||||
{
|
||||
byte dummy = n106.Read(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
n106.Write(addr, data);
|
||||
}
|
||||
}
|
||||
if ((exsound_select & 0x20) != 0)
|
||||
{
|
||||
fme7.Write(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteProcess(ushort addr, byte data)
|
||||
{
|
||||
// $4018はVirtuaNES固有ポート
|
||||
if (addr >= 0x4000 && addr <= 0x401F)
|
||||
{
|
||||
@internal.Write(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SyncDPCM(int cycles)
|
||||
{
|
||||
@internal.Sync(cycles);
|
||||
}
|
||||
|
||||
internal byte Read(ushort addr)
|
||||
{
|
||||
return @internal.SyncRead(addr);
|
||||
}
|
||||
|
||||
internal void Write(ushort addr, byte data)
|
||||
{
|
||||
// $4018偼VirtuaNES屌桳億乕僩
|
||||
if (addr >= 0x4000 && addr <= 0x401F)
|
||||
{
|
||||
@internal.SyncWrite(addr, data);
|
||||
SetQueue(nes.cpu.GetTotalCycles(), addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetQueue(int writetime, ushort addr, byte data)
|
||||
{
|
||||
queue.data[queue.wrptr].time = writetime;
|
||||
queue.data[queue.wrptr].addr = addr;
|
||||
queue.data[queue.wrptr].data = data;
|
||||
queue.wrptr++;
|
||||
|
||||
var newwrptr = (int)(queue.wrptr & (QUEUE_LENGTH - 1));
|
||||
queue.wrptr = newwrptr;
|
||||
|
||||
if (queue.wrptr == queue.rdptr)
|
||||
{
|
||||
Debuger.LogError("queue overflow.");
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetQueue(int writetime, ref QUEUEDATA ret)
|
||||
{
|
||||
if (queue.wrptr == queue.rdptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (queue.data[queue.rdptr].time <= writetime)
|
||||
{
|
||||
ret = queue.data[queue.rdptr];
|
||||
queue.rdptr++;
|
||||
var newrdptr = (int)(queue.rdptr & (QUEUE_LENGTH - 1));
|
||||
queue.rdptr = newrdptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SoundSetup()
|
||||
{
|
||||
float fClock = nes.nescfg.CpuClock;
|
||||
int nRate = Supporter.Config.sound.nRate;
|
||||
|
||||
@internal.Setup(fClock, nRate);
|
||||
vrc6.Setup(fClock, nRate);
|
||||
vrc7.Setup(fClock, nRate);
|
||||
mmc5.Setup(fClock, nRate);
|
||||
fds.Setup(fClock, nRate);
|
||||
n106.Setup(fClock, nRate);
|
||||
fme7.Setup(fClock, nRate);
|
||||
}
|
||||
|
||||
internal void SelectExSound(byte data)
|
||||
{
|
||||
exsound_select = data;
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
queue = new QUEUE();
|
||||
exqueue = new QUEUE();
|
||||
|
||||
elapsed_time = 0;
|
||||
|
||||
float fClock = nes.nescfg.CpuClock;
|
||||
int nRate = Supporter.Config.sound.nRate;
|
||||
|
||||
@internal.Reset(fClock, nRate);
|
||||
vrc6.Reset(fClock, nRate);
|
||||
vrc7.Reset(fClock, nRate);
|
||||
mmc5.Reset(fClock, nRate);
|
||||
fds.Reset(fClock, nRate);
|
||||
n106.Reset(fClock, nRate);
|
||||
fme7.Reset(fClock, nRate);
|
||||
|
||||
SoundSetup();
|
||||
}
|
||||
|
||||
internal void ExWrite(ushort addr, byte data)
|
||||
{
|
||||
SetExQueue(nes.cpu.GetTotalCycles(), addr, data);
|
||||
|
||||
if ((exsound_select & 0x04) != 0)
|
||||
{
|
||||
if (addr >= 0x4040 && addr < 0x4100)
|
||||
{
|
||||
fds.SyncWrite(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
if ((exsound_select & 0x08) != 0)
|
||||
{
|
||||
if (addr >= 0x5000 && addr <= 0x5015)
|
||||
{
|
||||
mmc5.SyncWrite(addr, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetExQueue(int writetime, ushort addr, byte data)
|
||||
{
|
||||
exqueue.data[exqueue.wrptr].time = writetime;
|
||||
exqueue.data[exqueue.wrptr].addr = addr;
|
||||
exqueue.data[exqueue.wrptr].data = data;
|
||||
exqueue.wrptr++;
|
||||
var temp = QUEUE_LENGTH - 1;
|
||||
exqueue.wrptr = (int)(exqueue.wrptr & temp);
|
||||
if (exqueue.wrptr == exqueue.rdptr)
|
||||
{
|
||||
Debuger.LogError("exqueue overflow.");
|
||||
}
|
||||
}
|
||||
|
||||
internal byte ExRead(ushort addr)
|
||||
{
|
||||
byte data = 0;
|
||||
|
||||
if ((exsound_select & 0x10) != 0)
|
||||
{
|
||||
if (addr == 0x4800)
|
||||
{
|
||||
SetExQueue(nes.cpu.GetTotalCycles(), 0, 0);
|
||||
}
|
||||
}
|
||||
if ((exsound_select & 0x04) != 0)
|
||||
{
|
||||
if (addr >= 0x4040 && addr < 0x4100)
|
||||
{
|
||||
data = fds.SyncRead(addr);
|
||||
}
|
||||
}
|
||||
if ((exsound_select & 0x08) != 0)
|
||||
{
|
||||
if (addr >= 0x5000 && addr <= 0x5015)
|
||||
{
|
||||
data = mmc5.SyncRead(addr);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
internal void GetFrameIRQ(ref int Cycle, ref byte Count, ref byte Type, ref byte IRQ, ref byte Occur)
|
||||
{
|
||||
@internal.GetFrameIRQ(ref Cycle, ref Count, ref Type, ref IRQ, ref Occur);
|
||||
}
|
||||
|
||||
internal void SetFrameIRQ(int Cycle, byte Count, byte Type, byte IRQ, byte Occur)
|
||||
{
|
||||
@internal.SetFrameIRQ(Cycle, Count, Type, IRQ, Occur);
|
||||
}
|
||||
|
||||
internal void SaveState(StateBuffer buffer)
|
||||
{
|
||||
// 時間軸を同期させる為Flushする
|
||||
QueueFlush();
|
||||
|
||||
@internal.SaveState(buffer);
|
||||
buffer.Position += (@internal.GetSize() + 15) & (~0x0F);
|
||||
|
||||
// VRC6
|
||||
if ((exsound_select & 0x01) != 0)
|
||||
{
|
||||
vrc6.SaveState(buffer);
|
||||
buffer.Position += (vrc6.GetSize() + 15) & (~0x0F); // Padding
|
||||
}
|
||||
// VRC7 (not support)
|
||||
if ((exsound_select & 0x02) != 0)
|
||||
{
|
||||
vrc7.SaveState(buffer);
|
||||
buffer.Position += (vrc7.GetSize() + 15) & (~0x0F); // Padding
|
||||
}
|
||||
// FDS
|
||||
if ((exsound_select & 0x04) != 0)
|
||||
{
|
||||
fds.SaveState(buffer);
|
||||
buffer.Position += (fds.GetSize() + 15) & (~0x0F); // Padding
|
||||
|
||||
}
|
||||
// MMC5
|
||||
if ((exsound_select & 0x08) != 0)
|
||||
{
|
||||
mmc5.SaveState(buffer);
|
||||
buffer.Position += (mmc5.GetSize() + 15) & (~0x0F); // Padding
|
||||
}
|
||||
// N106
|
||||
if ((exsound_select & 0x10) != 0)
|
||||
{
|
||||
n106.SaveState(buffer);
|
||||
buffer.Position += (n106.GetSize() + 15) & (~0x0F); // Padding
|
||||
}
|
||||
// FME7
|
||||
if ((exsound_select & 0x20) != 0)
|
||||
{
|
||||
fme7.SaveState(buffer);
|
||||
buffer.Position += (fme7.GetSize() + 15) & (~0x0F); // Padding
|
||||
}
|
||||
}
|
||||
|
||||
internal void LoadState(StateReader buffer)
|
||||
{
|
||||
@internal.LoadState(buffer);
|
||||
buffer.Skip((@internal.GetSize() + 15) & (~0x0F));
|
||||
|
||||
// VRC6
|
||||
if ((exsound_select & 0x01) != 0)
|
||||
{
|
||||
vrc6.LoadState(buffer);
|
||||
buffer.Skip((int)((vrc6.GetSize() + 15) & (~0x0F))); // Padding
|
||||
}
|
||||
// VRC7 (not support)
|
||||
if ((exsound_select & 0x02) != 0)
|
||||
{
|
||||
vrc7.LoadState(buffer);
|
||||
buffer.Skip((vrc7.GetSize() + 15) & (~0x0F)); // Padding
|
||||
}
|
||||
// FDS
|
||||
if ((exsound_select & 0x04) != 0)
|
||||
{
|
||||
fds.LoadState(buffer);
|
||||
buffer.Skip((fds.GetSize() + 15) & (~0x0F)); // Padding
|
||||
}
|
||||
// MMC5
|
||||
if ((exsound_select & 0x08) != 0)
|
||||
{
|
||||
mmc5.LoadState(buffer);
|
||||
buffer.Skip((mmc5.GetSize() + 15) & (~0x0F)); // Padding
|
||||
}
|
||||
// N106
|
||||
if ((exsound_select & 0x10) != 0)
|
||||
{
|
||||
n106.LoadState(buffer);
|
||||
buffer.Skip((n106.GetSize() + 15) & (~0x0F)); // Padding
|
||||
}
|
||||
// FME7
|
||||
if ((exsound_select & 0x20) != 0)
|
||||
{
|
||||
fme7.LoadState(buffer);
|
||||
buffer.Skip((fme7.GetSize() + 15) & (~0x0F)); // Padding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct QUEUEDATA
|
||||
{
|
||||
public int time;
|
||||
public ushort addr;
|
||||
public byte data;
|
||||
public byte reserved;
|
||||
}
|
||||
|
||||
public class QUEUE
|
||||
{
|
||||
public int rdptr;
|
||||
public int wrptr;
|
||||
public QUEUEDATA[] data = new QUEUEDATA[8192];
|
||||
}
|
||||
}
|
548
Core/VirtualNes.Core/ApuEX/APU_FDS.cs
Normal file
548
Core/VirtualNes.Core/ApuEX/APU_FDS.cs
Normal file
@ -0,0 +1,548 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_FDS : APU_INTERFACE
|
||||
{
|
||||
FDSSOUND fds = new FDSSOUND();
|
||||
FDSSOUND fds_sync = new FDSSOUND();
|
||||
int[] output_buf = new int[8];
|
||||
int sampling_rate;
|
||||
|
||||
public APU_FDS()
|
||||
{
|
||||
fds.ZeroMemory();
|
||||
fds_sync.ZeroMemory();
|
||||
|
||||
Array.Clear(output_buf, 0, output_buf.Length);
|
||||
|
||||
sampling_rate = 22050;
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
fds.ZeroMemory();
|
||||
fds_sync.ZeroMemory();
|
||||
|
||||
sampling_rate = 22050;
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
sampling_rate = nRate;
|
||||
}
|
||||
|
||||
int[] tbl_writesub = { 30, 20, 15, 12 };
|
||||
|
||||
private void WriteSub(ushort addr, byte data, FDSSOUND ch, double rate)
|
||||
{
|
||||
if (addr < 0x4040 || addr > 0x40BF)
|
||||
return;
|
||||
|
||||
ch.reg[addr - 0x4040] = data;
|
||||
if (addr >= 0x4040 && addr <= 0x407F)
|
||||
{
|
||||
if (ch.wave_setup != 0)
|
||||
{
|
||||
ch.main_wavetable[addr - 0x4040] = 0x20 - (data & 0x3F);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4080: // Volume Envelope
|
||||
ch.volenv_mode = (byte)(data >> 6);
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
ch.volenv_gain = (byte)(data & 0x3F);
|
||||
|
||||
// 即時反映
|
||||
if (ch.main_addr == 0)
|
||||
{
|
||||
ch.now_volume = (ch.volenv_gain < 0x21) ? ch.volenv_gain : 0x20;
|
||||
}
|
||||
}
|
||||
// エンベロープ1段階の演算
|
||||
ch.volenv_decay = (byte)(data & 0x3F);
|
||||
ch.volenv_phaseacc = (double)ch.envelope_speed * (double)(ch.volenv_decay + 1) * rate / (232.0 * 960.0);
|
||||
break;
|
||||
|
||||
case 0x4082: // Main Frequency(Low)
|
||||
ch.main_frequency = (ch.main_frequency & ~0x00FF) | data;
|
||||
break;
|
||||
case 0x4083: // Main Frequency(High)
|
||||
ch.main_enable = (byte)((~data) & (1 << 7));
|
||||
ch.envelope_enable = (byte)((~data) & (1 << 6));
|
||||
if (ch.main_enable == 0)
|
||||
{
|
||||
ch.main_addr = 0;
|
||||
ch.now_volume = (ch.volenv_gain < 0x21) ? ch.volenv_gain : 0x20;
|
||||
}
|
||||
// ch.main_frequency = (ch.main_frequency&0x00FF)|(((INT)data&0x3F)<<8);
|
||||
ch.main_frequency = (ch.main_frequency & 0x00FF) | ((data & 0x0F) << 8);
|
||||
break;
|
||||
|
||||
case 0x4084: // Sweep Envelope
|
||||
ch.swpenv_mode = (byte)(data >> 6);
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
ch.swpenv_gain = (byte)(data & 0x3F);
|
||||
}
|
||||
// エンベロープ1段階の演算
|
||||
ch.swpenv_decay = (byte)(data & 0x3F);
|
||||
ch.swpenv_phaseacc = (double)ch.envelope_speed * (double)(ch.swpenv_decay + 1) * rate / (232.0 * 960.0);
|
||||
break;
|
||||
|
||||
case 0x4085: // Sweep Bias
|
||||
if ((data & 0x40) != 0) ch.sweep_bias = (data & 0x3f) - 0x40;
|
||||
else ch.sweep_bias = data & 0x3f;
|
||||
ch.lfo_addr = 0;
|
||||
break;
|
||||
|
||||
case 0x4086: // Effector(LFO) Frequency(Low)
|
||||
ch.lfo_frequency = (ch.lfo_frequency & (~0x00FF)) | data;
|
||||
break;
|
||||
case 0x4087: // Effector(LFO) Frequency(High)
|
||||
ch.lfo_enable = (byte)((~data & 0x80));
|
||||
ch.lfo_frequency = (ch.lfo_frequency & 0x00FF) | ((data & 0x0F) << 8);
|
||||
break;
|
||||
|
||||
case 0x4088: // Effector(LFO) wavetable
|
||||
if (ch.lfo_enable == 0)
|
||||
{
|
||||
// FIFO?
|
||||
for (byte i = 0; i < 31; i++)
|
||||
{
|
||||
ch.lfo_wavetable[i * 2 + 0] = ch.lfo_wavetable[(i + 1) * 2 + 0];
|
||||
ch.lfo_wavetable[i * 2 + 1] = ch.lfo_wavetable[(i + 1) * 2 + 1];
|
||||
}
|
||||
ch.lfo_wavetable[31 * 2 + 0] = (byte)(data & 0x07);
|
||||
ch.lfo_wavetable[31 * 2 + 1] = (byte)(data & 0x07);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4089: // Sound control
|
||||
{
|
||||
ch.master_volume = tbl_writesub[data & 3];
|
||||
ch.wave_setup = (byte)(data & 0x80);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x408A: // Sound control 2
|
||||
ch.envelope_speed = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
WriteSub(addr, data, fds, sampling_rate);
|
||||
}
|
||||
|
||||
public override byte Read(ushort addr)
|
||||
{
|
||||
byte data = (byte)(addr >> 8);
|
||||
|
||||
if (addr >= 0x4040 && addr <= 0x407F)
|
||||
{
|
||||
data = (byte)(fds.main_wavetable[addr & 0x3F] | 0x40);
|
||||
}
|
||||
else
|
||||
if (addr == 0x4090)
|
||||
{
|
||||
data = (byte)((fds.volenv_gain & 0x3F) | 0x40);
|
||||
}
|
||||
else
|
||||
if (addr == 0x4092)
|
||||
{
|
||||
data = (byte)((fds.swpenv_gain & 0x3F) | 0x40);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int[] tbl_process = { 0, 1, 2, 4, 0, -4, -2, -1 };
|
||||
public override int Process(int channel)
|
||||
{
|
||||
// Envelope unit
|
||||
if (fds.envelope_enable != 0 && fds.envelope_speed != 0)
|
||||
{
|
||||
// Volume envelope
|
||||
if (fds.volenv_mode < 2)
|
||||
{
|
||||
double decay = ((double)fds.envelope_speed * (double)(fds.volenv_decay + 1) * (double)sampling_rate) / (232.0 * 960.0);
|
||||
fds.volenv_phaseacc -= 1.0;
|
||||
while (fds.volenv_phaseacc < 0.0)
|
||||
{
|
||||
fds.volenv_phaseacc += decay;
|
||||
|
||||
if (fds.volenv_mode == 0)
|
||||
{
|
||||
// 減少モード
|
||||
if (fds.volenv_gain != 0)
|
||||
fds.volenv_gain--;
|
||||
}
|
||||
else
|
||||
if (fds.volenv_mode == 1)
|
||||
{
|
||||
if (fds.volenv_gain < 0x20)
|
||||
fds.volenv_gain++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sweep envelope
|
||||
if (fds.swpenv_mode < 2)
|
||||
{
|
||||
double decay = ((double)fds.envelope_speed * (double)(fds.swpenv_decay + 1) * (double)sampling_rate) / (232.0 * 960.0);
|
||||
fds.swpenv_phaseacc -= 1.0;
|
||||
while (fds.swpenv_phaseacc < 0.0)
|
||||
{
|
||||
fds.swpenv_phaseacc += decay;
|
||||
|
||||
if (fds.swpenv_mode == 0)
|
||||
{
|
||||
// 減少モード
|
||||
if (fds.swpenv_gain != 0)
|
||||
fds.swpenv_gain--;
|
||||
}
|
||||
else
|
||||
if (fds.swpenv_mode == 1)
|
||||
{
|
||||
if (fds.swpenv_gain < 0x20)
|
||||
fds.swpenv_gain++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Effector(LFO) unit
|
||||
int sub_freq = 0;
|
||||
// if( fds.lfo_enable && fds.envelope_speed && fds.lfo_frequency ) {
|
||||
if (fds.lfo_enable != 0)
|
||||
{
|
||||
if (fds.lfo_frequency != 0)
|
||||
{
|
||||
fds.lfo_phaseacc -= (1789772.5 * (double)fds.lfo_frequency) / 65536.0;
|
||||
while (fds.lfo_phaseacc < 0.0)
|
||||
{
|
||||
fds.lfo_phaseacc += (double)sampling_rate;
|
||||
|
||||
if (fds.lfo_wavetable[fds.lfo_addr] == 4)
|
||||
fds.sweep_bias = 0;
|
||||
else
|
||||
fds.sweep_bias += tbl_process[fds.lfo_wavetable[fds.lfo_addr]];
|
||||
|
||||
fds.lfo_addr = (fds.lfo_addr + 1) & 63;
|
||||
}
|
||||
}
|
||||
|
||||
if (fds.sweep_bias > 63)
|
||||
fds.sweep_bias -= 128;
|
||||
else if (fds.sweep_bias < -64)
|
||||
fds.sweep_bias += 128;
|
||||
|
||||
int sub_multi = fds.sweep_bias * fds.swpenv_gain;
|
||||
|
||||
if ((sub_multi & 0x0F) != 0)
|
||||
{
|
||||
// 16で割り切れない場合
|
||||
sub_multi = (sub_multi / 16);
|
||||
if (fds.sweep_bias >= 0)
|
||||
sub_multi += 2; // 正の場合
|
||||
else
|
||||
sub_multi -= 1; // 負の場合
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16で割り切れる場合
|
||||
sub_multi = (sub_multi / 16);
|
||||
}
|
||||
// 193を超えると-258する(-64へラップ)
|
||||
if (sub_multi > 193)
|
||||
sub_multi -= 258;
|
||||
// -64を下回ると+256する(192へラップ)
|
||||
if (sub_multi < -64)
|
||||
sub_multi += 256;
|
||||
|
||||
sub_freq = (fds.main_frequency) * sub_multi / 64;
|
||||
}
|
||||
|
||||
// Main unit
|
||||
int output = 0;
|
||||
if (fds.main_enable != 0 && fds.main_frequency != 0 && fds.wave_setup == 0)
|
||||
{
|
||||
int freq;
|
||||
int main_addr_old = fds.main_addr;
|
||||
|
||||
freq = (int)((fds.main_frequency + sub_freq) * 1789772.5 / 65536.0);
|
||||
|
||||
fds.main_addr = (fds.main_addr + freq + 64 * sampling_rate) % (64 * sampling_rate);
|
||||
|
||||
// 1周期を超えたらボリューム更新
|
||||
if (main_addr_old > fds.main_addr)
|
||||
fds.now_volume = (fds.volenv_gain < 0x21) ? fds.volenv_gain : 0x20;
|
||||
|
||||
output = fds.main_wavetable[(fds.main_addr / sampling_rate) & 0x3f] * 8 * fds.now_volume * fds.master_volume / 30;
|
||||
|
||||
if (fds.now_volume != 0)
|
||||
fds.now_freq = freq * 4;
|
||||
else
|
||||
fds.now_freq = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fds.now_freq = 0;
|
||||
output = 0;
|
||||
}
|
||||
|
||||
// LPF
|
||||
output = (output_buf[0] * 2 + output) / 3;
|
||||
output_buf[0] = output;
|
||||
|
||||
fds.output = output;
|
||||
return fds.output;
|
||||
}
|
||||
|
||||
internal void SyncWrite(ushort addr, byte data)
|
||||
{
|
||||
WriteSub(addr, data, fds_sync, 1789772.5d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal byte SyncRead(ushort addr)
|
||||
{
|
||||
byte data = (byte)(addr >> 8);
|
||||
|
||||
if (addr >= 0x4040 && addr <= 0x407F)
|
||||
{
|
||||
data = (byte)(fds_sync.main_wavetable[addr & 0x3F] | 0x40);
|
||||
}
|
||||
else
|
||||
if (addr == 0x4090)
|
||||
{
|
||||
data = (byte)((fds_sync.volenv_gain & 0x3F) | 0x40);
|
||||
}
|
||||
else
|
||||
if (addr == 0x4092)
|
||||
{
|
||||
data = (byte)((fds_sync.swpenv_gain & 0x3F) | 0x40);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public override bool Sync(int cycles)
|
||||
{
|
||||
// Envelope unit
|
||||
if (fds_sync.envelope_enable != 0 && fds_sync.envelope_speed != 0)
|
||||
{
|
||||
// Volume envelope
|
||||
double decay;
|
||||
if (fds_sync.volenv_mode < 2)
|
||||
{
|
||||
decay = ((double)fds_sync.envelope_speed * (double)(fds_sync.volenv_decay + 1) * 1789772.5) / (232.0 * 960.0);
|
||||
fds_sync.volenv_phaseacc -= (double)cycles;
|
||||
while (fds_sync.volenv_phaseacc < 0.0)
|
||||
{
|
||||
fds_sync.volenv_phaseacc += decay;
|
||||
|
||||
if (fds_sync.volenv_mode == 0)
|
||||
{
|
||||
// 減少モード
|
||||
if (fds_sync.volenv_gain != 0)
|
||||
fds_sync.volenv_gain--;
|
||||
}
|
||||
else
|
||||
if (fds_sync.volenv_mode == 1)
|
||||
{
|
||||
// 増加モード
|
||||
if (fds_sync.volenv_gain < 0x20)
|
||||
fds_sync.volenv_gain++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sweep envelope
|
||||
if (fds_sync.swpenv_mode < 2)
|
||||
{
|
||||
decay = ((double)fds_sync.envelope_speed * (double)(fds_sync.swpenv_decay + 1) * 1789772.5) / (232.0 * 960.0);
|
||||
fds_sync.swpenv_phaseacc -= (double)cycles;
|
||||
while (fds_sync.swpenv_phaseacc < 0.0)
|
||||
{
|
||||
fds_sync.swpenv_phaseacc += decay;
|
||||
|
||||
if (fds_sync.swpenv_mode == 0)
|
||||
{
|
||||
// 減少モード
|
||||
if (fds_sync.swpenv_gain != 0)
|
||||
fds_sync.swpenv_gain--;
|
||||
}
|
||||
else
|
||||
if (fds_sync.swpenv_mode == 1)
|
||||
{
|
||||
// 増加モード
|
||||
if (fds_sync.swpenv_gain < 0x20)
|
||||
fds_sync.swpenv_gain++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetFreq(int channel)
|
||||
{
|
||||
return fds.now_freq;
|
||||
}
|
||||
|
||||
public override uint GetSize()
|
||||
{
|
||||
return fds.GetSize() + fds_sync.GetSize();
|
||||
}
|
||||
|
||||
public override void SaveState(StateBuffer buffer)
|
||||
{
|
||||
fds.SaveState(buffer);
|
||||
fds_sync.SaveState(buffer);
|
||||
}
|
||||
|
||||
private class FDSSOUND : IStateBufferObject
|
||||
{
|
||||
public byte[] reg = new byte[0x80];
|
||||
public byte volenv_mode; // Volume Envelope
|
||||
public byte volenv_gain;
|
||||
public byte volenv_decay;
|
||||
public double volenv_phaseacc;
|
||||
public byte swpenv_mode; // Sweep Envelope
|
||||
public byte swpenv_gain;
|
||||
public byte swpenv_decay;
|
||||
public double swpenv_phaseacc;
|
||||
// For envelope unit
|
||||
public byte envelope_enable; // $4083 bit6
|
||||
public byte envelope_speed; // $408A
|
||||
// For $4089
|
||||
public byte wave_setup; // bit7
|
||||
public int master_volume; // bit1-0
|
||||
// For Main unit
|
||||
public int[] main_wavetable = new int[64];
|
||||
public byte main_enable;
|
||||
public int main_frequency;
|
||||
public int main_addr;
|
||||
// For Effector(LFO) unit
|
||||
public byte[] lfo_wavetable = new byte[64];
|
||||
public byte lfo_enable; // 0:Enable 1:Wavetable setup
|
||||
public int lfo_frequency;
|
||||
public int lfo_addr;
|
||||
public double lfo_phaseacc;
|
||||
// For Sweep unit
|
||||
public int sweep_bias;
|
||||
// Misc
|
||||
public int now_volume;
|
||||
public int now_freq;
|
||||
public int output;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, reg.Length);
|
||||
volenv_mode = 0;
|
||||
volenv_gain = 0;
|
||||
volenv_decay = 0;
|
||||
volenv_phaseacc = 0.0;
|
||||
swpenv_mode = 0;
|
||||
swpenv_gain = 0;
|
||||
swpenv_decay = 0;
|
||||
swpenv_phaseacc = 0.0;
|
||||
envelope_enable = 0;
|
||||
envelope_speed = 0;
|
||||
wave_setup = 0;
|
||||
master_volume = 0;
|
||||
Array.Clear(main_wavetable, 0, main_wavetable.Length);
|
||||
main_enable = 0;
|
||||
main_frequency = 0;
|
||||
main_addr = 0;
|
||||
Array.Clear(lfo_wavetable, 0, lfo_wavetable.Length);
|
||||
lfo_enable = 0;
|
||||
lfo_frequency = 0;
|
||||
lfo_addr = 0;
|
||||
lfo_phaseacc = 0.0;
|
||||
sweep_bias = 0;
|
||||
now_volume = 0;
|
||||
now_freq = 0;
|
||||
output = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 512;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg);
|
||||
buffer.Write(volenv_mode);
|
||||
buffer.Write(volenv_gain);
|
||||
buffer.Write(volenv_decay);
|
||||
buffer.Write(volenv_phaseacc);
|
||||
buffer.Write(swpenv_mode);
|
||||
buffer.Write(swpenv_gain);
|
||||
buffer.Write(swpenv_decay);
|
||||
buffer.Write(swpenv_phaseacc);
|
||||
buffer.Write(envelope_enable);
|
||||
buffer.Write(envelope_speed);
|
||||
buffer.Write(wave_setup);
|
||||
buffer.Write(master_volume);
|
||||
buffer.Write(main_wavetable);
|
||||
buffer.Write(main_enable);
|
||||
buffer.Write(main_frequency);
|
||||
buffer.Write(main_addr);
|
||||
buffer.Write(lfo_wavetable);
|
||||
buffer.Write(lfo_enable);
|
||||
buffer.Write(lfo_frequency);
|
||||
buffer.Write(lfo_addr);
|
||||
buffer.Write(lfo_phaseacc);
|
||||
buffer.Write(sweep_bias);
|
||||
buffer.Write(now_volume);
|
||||
buffer.Write(now_freq);
|
||||
buffer.Write(output);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
reg = buffer.Read_bytes(0x80);
|
||||
volenv_mode = buffer.Read_byte();
|
||||
volenv_gain = buffer.Read_byte();
|
||||
volenv_decay = buffer.Read_byte();
|
||||
volenv_phaseacc = buffer.Read_double();
|
||||
swpenv_mode = buffer.Read_byte();
|
||||
swpenv_gain = buffer.Read_byte();
|
||||
swpenv_decay = buffer.Read_byte();
|
||||
swpenv_phaseacc = buffer.Read_double();
|
||||
envelope_enable = buffer.Read_byte();
|
||||
envelope_speed = buffer.Read_byte();
|
||||
wave_setup = buffer.Read_byte();
|
||||
master_volume = buffer.Read_int();
|
||||
main_wavetable = buffer.Read_ints(64);
|
||||
main_enable = buffer.Read_byte();
|
||||
main_frequency = buffer.Read_int();
|
||||
main_addr = buffer.Read_int();
|
||||
lfo_wavetable = buffer.Read_bytes(64);
|
||||
lfo_enable = buffer.Read_byte();
|
||||
lfo_frequency = buffer.Read_int();
|
||||
lfo_addr = buffer.Read_int();
|
||||
lfo_phaseacc = buffer.Read_double();
|
||||
sweep_bias = buffer.Read_int();
|
||||
now_volume = buffer.Read_int();
|
||||
now_freq = buffer.Read_int();
|
||||
output = buffer.Read_int();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
505
Core/VirtualNes.Core/ApuEX/APU_FME7.cs
Normal file
505
Core/VirtualNes.Core/ApuEX/APU_FME7.cs
Normal file
@ -0,0 +1,505 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_FME7 : APU_INTERFACE
|
||||
{
|
||||
// Envelope tables
|
||||
byte[] envelope_pulse0 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
||||
};
|
||||
byte[] envelope_pulse1 = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x00
|
||||
};
|
||||
byte[] envelope_pulse2 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x1F
|
||||
};
|
||||
byte[] envelope_pulse3 = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x1F
|
||||
};
|
||||
sbyte[] envstep_pulse = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0
|
||||
};
|
||||
|
||||
byte[] envelope_sawtooth0 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
||||
};
|
||||
byte[] envelope_sawtooth1 = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
|
||||
};
|
||||
sbyte[] envstep_sawtooth = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, -15
|
||||
};
|
||||
|
||||
byte[] envelope_triangle0 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
|
||||
};
|
||||
byte[] envelope_triangle1 = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
||||
};
|
||||
sbyte[] envstep_triangle = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, -31
|
||||
};
|
||||
|
||||
byte[][] envelope_table;
|
||||
sbyte[][] envstep_table;
|
||||
|
||||
ENVELOPE envelope;
|
||||
NOISE noise = new NOISE();
|
||||
CHANNEL[] op = new CHANNEL[3] { new CHANNEL(), new CHANNEL(), new CHANNEL() };
|
||||
byte address;
|
||||
int[] vol_table = new int[0x20];
|
||||
int cycle_rate;
|
||||
|
||||
float cpu_clock;
|
||||
|
||||
public APU_FME7()
|
||||
{
|
||||
envelope_table = new byte[16][]
|
||||
{
|
||||
envelope_pulse0, envelope_pulse0, envelope_pulse0, envelope_pulse0,
|
||||
envelope_pulse1, envelope_pulse1, envelope_pulse1, envelope_pulse1,
|
||||
envelope_sawtooth0, envelope_pulse0, envelope_triangle0, envelope_pulse2,
|
||||
envelope_sawtooth1, envelope_pulse3, envelope_triangle1, envelope_pulse1
|
||||
};
|
||||
|
||||
envstep_table = new sbyte[16][]
|
||||
{
|
||||
envstep_pulse, envstep_pulse, envstep_pulse, envstep_pulse,
|
||||
envstep_pulse, envstep_pulse, envstep_pulse, envstep_pulse,
|
||||
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse,
|
||||
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse
|
||||
};
|
||||
envelope = new ENVELOPE(envelope_table, envstep_table);
|
||||
|
||||
Reset(APU_CLOCK, 22050);
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
int i;
|
||||
|
||||
envelope.ZeroMemory();
|
||||
noise.ZeroMemory();
|
||||
|
||||
foreach (var item in op)
|
||||
{
|
||||
item.ZeroMemory();
|
||||
}
|
||||
|
||||
envelope.envtbl_index = 0;
|
||||
envelope.envstep_index = 0;
|
||||
|
||||
noise.noiserange = 1;
|
||||
noise.noiseout = 0xFF;
|
||||
|
||||
address = 0;
|
||||
|
||||
// Volume to voltage
|
||||
double @out = 0x1FFF;
|
||||
for (i = 31; i > 1; i--)
|
||||
{
|
||||
vol_table[i] = (int)(@out + 0.5);
|
||||
@out /= 1.188502227; /* = 10 ^ (1.5/20) = 1.5dB */
|
||||
}
|
||||
vol_table[1] = 0;
|
||||
vol_table[0] = 0;
|
||||
|
||||
Setup(fClock, nRate);
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
cpu_clock = fClock;
|
||||
cycle_rate = (int)((fClock / 16.0f) * (1 << 16) / nRate);
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0xC000)
|
||||
{
|
||||
address = data;
|
||||
}
|
||||
else if (addr == 0xE000)
|
||||
{
|
||||
byte chaddr = address;
|
||||
switch (chaddr)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
{
|
||||
CHANNEL ch = op[chaddr >> 1];
|
||||
ch.reg[chaddr & 0x01] = data;
|
||||
ch.freq = INT2FIX(((ch.reg[1] & 0x0F) << 8) + ch.reg[0] + 1);
|
||||
}
|
||||
break;
|
||||
case 0x06:
|
||||
noise.freq = INT2FIX((data & 0x1F) + 1);
|
||||
break;
|
||||
case 0x07:
|
||||
{
|
||||
for (byte i = 0; i < 3; i++)
|
||||
{
|
||||
op[i].enable = (byte)(data & (1 << i));
|
||||
op[i].noise_on = (byte)(data & (8 << i));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
{
|
||||
CHANNEL ch = op[chaddr & 3];
|
||||
ch.reg[2] = data;
|
||||
ch.env_on = (byte)(data & 0x10);
|
||||
ch.volume = (byte)((data & 0x0F) * 2);
|
||||
}
|
||||
break;
|
||||
case 0x0B:
|
||||
case 0x0C:
|
||||
envelope.reg[chaddr - 0x0B] = data;
|
||||
envelope.freq = INT2FIX(((envelope.reg[1] & 0x0F) << 8) + envelope.reg[0] + 1);
|
||||
break;
|
||||
case 0x0D:
|
||||
envelope.envtbl_index = (byte)(data & 0x0F);
|
||||
envelope.envstep_index = (byte)(data & 0x0F);
|
||||
envelope.envadr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
if (channel < 3)
|
||||
{
|
||||
return ChannelRender(op[channel]);
|
||||
}
|
||||
else if (channel == 3)
|
||||
{
|
||||
// 必ずch3を1回呼んでからch0-2を呼ぶ事
|
||||
EnvelopeRender();
|
||||
NoiseRender();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int GetFreq(int channel)
|
||||
{
|
||||
if (channel < 3)
|
||||
{
|
||||
CHANNEL ch = op[channel];
|
||||
|
||||
if (ch.enable != 0 || ch.freq == 0)
|
||||
return 0;
|
||||
if (ch.env_on != 0)
|
||||
{
|
||||
if (envelope.volume == 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ch.volume == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 16.0f));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EnvelopeRender()
|
||||
{
|
||||
if (envelope.freq == 0)
|
||||
return;
|
||||
envelope.phaseacc -= cycle_rate;
|
||||
if (envelope.phaseacc >= 0)
|
||||
return;
|
||||
while (envelope.phaseacc < 0)
|
||||
{
|
||||
envelope.phaseacc += envelope.freq;
|
||||
envelope.envadr += envelope.envstep[envelope.envadr];
|
||||
}
|
||||
envelope.volume = envelope.envtbl[envelope.envadr];
|
||||
}
|
||||
void NoiseRender()
|
||||
{
|
||||
if (noise.freq == 0)
|
||||
return;
|
||||
noise.phaseacc -= cycle_rate;
|
||||
if (noise.phaseacc >= 0)
|
||||
return;
|
||||
while (noise.phaseacc < 0)
|
||||
{
|
||||
noise.phaseacc += noise.freq;
|
||||
if (((noise.noiserange + 1) & 0x02) != 0)
|
||||
noise.noiseout = (byte)(~noise.noiseout);
|
||||
if ((noise.noiserange & 0x01) != 0)
|
||||
noise.noiserange ^= 0x28000;
|
||||
noise.noiserange >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
int ChannelRender(CHANNEL ch)
|
||||
{
|
||||
int output, volume;
|
||||
|
||||
if (ch.enable != 0)
|
||||
return 0;
|
||||
if (ch.freq == 0)
|
||||
return 0;
|
||||
|
||||
ch.phaseacc -= cycle_rate;
|
||||
while (ch.phaseacc < 0)
|
||||
{
|
||||
ch.phaseacc += ch.freq;
|
||||
ch.adder++;
|
||||
}
|
||||
|
||||
output = volume = 0;
|
||||
volume = ch.env_on != 0 ? vol_table[envelope.volume] : vol_table[ch.volume + 1];
|
||||
|
||||
if ((ch.adder & 0x01) != 0)
|
||||
{
|
||||
output += volume;
|
||||
}
|
||||
else
|
||||
{
|
||||
output -= volume;
|
||||
}
|
||||
if (ch.noise_on == 0)
|
||||
{
|
||||
if (noise.noiseout != 0)
|
||||
output += volume;
|
||||
else
|
||||
output -= volume;
|
||||
}
|
||||
|
||||
ch.output_vol = output;
|
||||
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
public override uint GetSize()
|
||||
{
|
||||
return (uint)(1 + envelope.GetSize() + noise.GetSize() + op[0].GetSize() * op.Length);
|
||||
}
|
||||
|
||||
public override void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(address);
|
||||
|
||||
envelope.SaveState(buffer);
|
||||
noise.SaveState(buffer);
|
||||
foreach (var oneOp in op)
|
||||
oneOp.SaveState(buffer);
|
||||
}
|
||||
|
||||
public class ENVELOPE : IStateBufferObject
|
||||
{
|
||||
public byte[] reg = new byte[3];
|
||||
public byte volume;
|
||||
|
||||
public int freq;
|
||||
public int phaseacc;
|
||||
public int envadr;
|
||||
|
||||
public byte envtbl_index;
|
||||
public byte envstep_index;
|
||||
|
||||
byte[][] ref_envtbl;
|
||||
sbyte[][] ref_envstep;
|
||||
public byte[] envtbl => ref_envtbl[envtbl_index];
|
||||
public sbyte[] envstep => ref_envstep[envstep_index];
|
||||
public ENVELOPE(byte[][] envtbl, sbyte[][] envstep)
|
||||
{
|
||||
ref_envtbl = envtbl;
|
||||
ref_envstep = envstep;
|
||||
}
|
||||
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, 3);
|
||||
volume = 0;
|
||||
freq = 0;
|
||||
phaseacc = 0;
|
||||
envadr = 0;
|
||||
envtbl_index = 0;
|
||||
envstep_index = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 18;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg);
|
||||
buffer.Write(volume);
|
||||
buffer.Write(freq);
|
||||
buffer.Write(phaseacc);
|
||||
buffer.Write(envadr);
|
||||
buffer.Write(envtbl_index);
|
||||
buffer.Write(envstep_index);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
reg = buffer.Read_bytes(3);
|
||||
volume = buffer.Read_byte();
|
||||
freq = buffer.Read_int();
|
||||
phaseacc = buffer.Read_int();
|
||||
envadr = buffer.Read_int();
|
||||
envtbl_index = buffer.Read_byte();
|
||||
envstep_index = buffer.Read_byte();
|
||||
}
|
||||
}
|
||||
|
||||
public class NOISE : IStateBufferObject
|
||||
{
|
||||
public int freq;
|
||||
public int phaseacc;
|
||||
public int noiserange;
|
||||
public byte noiseout;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
freq = 0; phaseacc = 0; noiserange = 0; noiseout = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(freq);
|
||||
buffer.Write(phaseacc);
|
||||
buffer.Write(noiserange);
|
||||
buffer.Write(noiseout);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
freq = buffer.Read_int();
|
||||
phaseacc = buffer.Read_int();
|
||||
noiserange = buffer.Read_int();
|
||||
noiseout = buffer.Read_byte();
|
||||
}
|
||||
}
|
||||
|
||||
public class CHANNEL : IStateBufferObject
|
||||
{
|
||||
public byte[] reg = new byte[3];
|
||||
public byte enable;
|
||||
public byte env_on;
|
||||
public byte noise_on;
|
||||
public byte adder;
|
||||
public byte volume;
|
||||
|
||||
public int freq;
|
||||
public int phaseacc;
|
||||
|
||||
public int output_vol;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, reg.Length);
|
||||
enable = 0;
|
||||
env_on = 0;
|
||||
noise_on = 0;
|
||||
adder = 0;
|
||||
volume = 0; freq = 0;
|
||||
phaseacc = 0;
|
||||
output_vol = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg);
|
||||
buffer.Write(enable);
|
||||
buffer.Write(env_on);
|
||||
buffer.Write(noise_on);
|
||||
buffer.Write(adder);
|
||||
buffer.Write(volume);
|
||||
buffer.Write(freq);
|
||||
buffer.Write(phaseacc);
|
||||
buffer.Write(output_vol);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
reg = buffer.Read_bytes(3);
|
||||
enable = buffer.Read_byte();
|
||||
env_on = buffer.Read_byte();
|
||||
noise_on = buffer.Read_byte();
|
||||
adder = buffer.Read_byte();
|
||||
volume = buffer.Read_byte();
|
||||
freq = buffer.Read_int();
|
||||
phaseacc = buffer.Read_int();
|
||||
output_vol = buffer.Read_int();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
Core/VirtualNes.Core/ApuEX/APU_INTERFACE.cs
Normal file
41
Core/VirtualNes.Core/ApuEX/APU_INTERFACE.cs
Normal file
@ -0,0 +1,41 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public abstract class APU_INTERFACE : IStateBufferObject
|
||||
{
|
||||
public const float APU_CLOCK = 1789772.5f;
|
||||
|
||||
public virtual void Dispose() { }
|
||||
|
||||
public abstract void Reset(float fClock, int nRate);
|
||||
public abstract void Setup(float fClock, int nRate);
|
||||
public abstract void Write(ushort addr, byte data);
|
||||
public abstract int Process(int channel);
|
||||
public virtual byte Read(ushort addr)
|
||||
{
|
||||
return (byte)(addr >> 8);
|
||||
}
|
||||
public virtual void WriteSync(ushort addr, byte data) { }
|
||||
public virtual byte ReadSync(ushort addr) { return 0; }
|
||||
public virtual void VSync() { }
|
||||
public virtual bool Sync(int cycles) { return false; }
|
||||
public virtual int GetFreq(int channel) { return 0; }
|
||||
public virtual void SaveState(StateBuffer buffer) { }
|
||||
public virtual void LoadState(StateReader buffer) { }
|
||||
|
||||
public static int INT2FIX(int x)
|
||||
{
|
||||
return x << 16;
|
||||
}
|
||||
|
||||
public static int FIX2INT(int x)
|
||||
{
|
||||
return x >> 16;
|
||||
}
|
||||
|
||||
|
||||
public virtual uint GetSize()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
1702
Core/VirtualNes.Core/ApuEX/APU_INTERNAL.cs
Normal file
1702
Core/VirtualNes.Core/ApuEX/APU_INTERNAL.cs
Normal file
File diff suppressed because it is too large
Load Diff
499
Core/VirtualNes.Core/ApuEX/APU_MMC5.cs
Normal file
499
Core/VirtualNes.Core/ApuEX/APU_MMC5.cs
Normal file
@ -0,0 +1,499 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_MMC5 : APU_INTERFACE
|
||||
{
|
||||
public const int RECTANGLE_VOL_SHIFT = 8;
|
||||
public const int DAOUT_VOL_SHIFT = 6;
|
||||
|
||||
SYNCRECTANGLE sch0 = new SYNCRECTANGLE();
|
||||
SYNCRECTANGLE sch1 = new SYNCRECTANGLE();
|
||||
RECTANGLE ch0 = new RECTANGLE();
|
||||
RECTANGLE ch1 = new RECTANGLE();
|
||||
|
||||
byte reg5010;
|
||||
byte reg5011;
|
||||
byte reg5015;
|
||||
byte sync_reg5015;
|
||||
int FrameCycle;
|
||||
float cpu_clock;
|
||||
int cycle_rate;
|
||||
|
||||
// Tables
|
||||
static int[] vbl_length = new int[32];
|
||||
static int[] duty_lut = new int[4];
|
||||
|
||||
static int[] decay_lut = new int[16];
|
||||
static int[] vbl_lut = new int[32];
|
||||
|
||||
public APU_MMC5()
|
||||
{
|
||||
// 仮設定
|
||||
Reset(APU_INTERFACE.APU_CLOCK, 22050);
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
sch0.ZeroMemory();
|
||||
sch1.ZeroMemory();
|
||||
|
||||
reg5010 = reg5011 = reg5015 = 0;
|
||||
|
||||
sync_reg5015 = 0;
|
||||
FrameCycle = 0;
|
||||
|
||||
Setup(fClock, nRate);
|
||||
|
||||
for (ushort addr = 0x5000; addr <= 0x5015; addr++)
|
||||
{
|
||||
Write(addr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
cpu_clock = fClock;
|
||||
cycle_rate = (int)(fClock * 65536.0f / nRate);
|
||||
|
||||
// Create Tables
|
||||
int i;
|
||||
int samples = (int)(nRate / 60.0f);
|
||||
for (i = 0; i < 16; i++)
|
||||
decay_lut[i] = (i + 1) * samples * 5;
|
||||
for (i = 0; i < 32; i++)
|
||||
vbl_lut[i] = vbl_length[i] * samples * 5;
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
// MMC5 CH0 rectangle
|
||||
case 0x5000:
|
||||
ch0.reg[0] = data;
|
||||
ch0.volume = (byte)(data & 0x0F);
|
||||
ch0.holdnote = (byte)(data & 0x20);
|
||||
ch0.fixed_envelope = (byte)(data & 0x10);
|
||||
ch0.env_decay = decay_lut[data & 0x0F];
|
||||
ch0.duty_flip = duty_lut[data >> 6];
|
||||
break;
|
||||
case 0x5001:
|
||||
ch0.reg[1] = data;
|
||||
break;
|
||||
case 0x5002:
|
||||
ch0.reg[2] = data;
|
||||
ch0.freq = INT2FIX(((ch0.reg[3] & 0x07) << 8) + data + 1);
|
||||
break;
|
||||
case 0x5003:
|
||||
ch0.reg[3] = data;
|
||||
ch0.vbl_length = vbl_lut[data >> 3];
|
||||
ch0.env_vol = 0;
|
||||
ch0.freq = INT2FIX(((data & 0x07) << 8) + ch0.reg[2] + 1);
|
||||
if ((reg5015 & 0x01) != 0)
|
||||
ch0.enable = 0xFF;
|
||||
break;
|
||||
// MMC5 CH1 rectangle
|
||||
case 0x5004:
|
||||
ch1.reg[0] = data;
|
||||
ch1.volume = (byte)(data & 0x0F);
|
||||
ch1.holdnote = (byte)(data & 0x20);
|
||||
ch1.fixed_envelope = (byte)(data & 0x10);
|
||||
ch1.env_decay = decay_lut[data & 0x0F];
|
||||
ch1.duty_flip = duty_lut[data >> 6];
|
||||
break;
|
||||
case 0x5005:
|
||||
ch1.reg[1] = data;
|
||||
break;
|
||||
case 0x5006:
|
||||
ch1.reg[2] = data;
|
||||
ch1.freq = INT2FIX(((ch1.reg[3] & 0x07) << 8) + data + 1);
|
||||
break;
|
||||
case 0x5007:
|
||||
ch1.reg[3] = data;
|
||||
ch1.vbl_length = vbl_lut[data >> 3];
|
||||
ch1.env_vol = 0;
|
||||
ch1.freq = INT2FIX(((data & 0x07) << 8) + ch1.reg[2] + 1);
|
||||
if ((reg5015 & 0x02) != 0)
|
||||
ch1.enable = 0xFF;
|
||||
break;
|
||||
case 0x5010:
|
||||
reg5010 = data;
|
||||
break;
|
||||
case 0x5011:
|
||||
reg5011 = data;
|
||||
break;
|
||||
case 0x5012:
|
||||
case 0x5013:
|
||||
case 0x5014:
|
||||
break;
|
||||
case 0x5015:
|
||||
reg5015 = data;
|
||||
if ((reg5015 & 0x01) != 0)
|
||||
{
|
||||
ch0.enable = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch0.enable = 0;
|
||||
ch0.vbl_length = 0;
|
||||
}
|
||||
if ((reg5015 & 0x02) != 0)
|
||||
{
|
||||
ch1.enable = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch1.enable = 0;
|
||||
ch1.vbl_length = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal void SyncWrite(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
// MMC5 CH0 rectangle
|
||||
case 0x5000:
|
||||
sch0.reg[0] = data;
|
||||
sch0.holdnote = (byte)(data & 0x20);
|
||||
break;
|
||||
case 0x5001:
|
||||
case 0x5002:
|
||||
sch0.reg[addr & 3] = data;
|
||||
break;
|
||||
case 0x5003:
|
||||
sch0.reg[3] = data;
|
||||
sch0.vbl_length = vbl_length[data >> 3];
|
||||
if ((sync_reg5015 & 0x01) != 0)
|
||||
sch0.enable = 0xFF;
|
||||
break;
|
||||
// MMC5 CH1 rectangle
|
||||
case 0x5004:
|
||||
sch1.reg[0] = data;
|
||||
sch1.holdnote = (byte)(data & 0x20);
|
||||
break;
|
||||
case 0x5005:
|
||||
case 0x5006:
|
||||
sch1.reg[addr & 3] = data;
|
||||
break;
|
||||
case 0x5007:
|
||||
sch1.reg[3] = data;
|
||||
sch1.vbl_length = vbl_length[data >> 3];
|
||||
if ((sync_reg5015 & 0x02) != 0)
|
||||
sch1.enable = 0xFF;
|
||||
break;
|
||||
case 0x5010:
|
||||
case 0x5011:
|
||||
case 0x5012:
|
||||
case 0x5013:
|
||||
case 0x5014:
|
||||
break;
|
||||
case 0x5015:
|
||||
sync_reg5015 = data;
|
||||
if ((sync_reg5015 & 0x01) != 0)
|
||||
{
|
||||
sch0.enable = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
sch0.enable = 0;
|
||||
sch0.vbl_length = 0;
|
||||
}
|
||||
if ((sync_reg5015 & 0x02) != 0)
|
||||
{
|
||||
sch1.enable = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
sch1.enable = 0;
|
||||
sch1.vbl_length = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte SyncRead(ushort addr)
|
||||
{
|
||||
byte data = 0;
|
||||
|
||||
if (addr == 0x5015)
|
||||
{
|
||||
if ((sch0.enable != 0) && sch0.vbl_length > 0) data |= (1 << 0);
|
||||
if ((sch1.enable != 0) && sch1.vbl_length > 0) data |= (1 << 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public override bool Sync(int cycles)
|
||||
{
|
||||
FrameCycle += cycles;
|
||||
if (FrameCycle >= 7457 * 5 / 2)
|
||||
{
|
||||
FrameCycle -= 7457 * 5 / 2;
|
||||
|
||||
if (sch0.enable != 0 && sch0.holdnote == 0)
|
||||
{
|
||||
if ((sch0.vbl_length) != 0)
|
||||
{
|
||||
sch0.vbl_length--;
|
||||
}
|
||||
}
|
||||
if (sch1.enable != 0 && sch1.holdnote == 0)
|
||||
{
|
||||
if ((sch1.vbl_length) != 0)
|
||||
{
|
||||
sch1.vbl_length--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
switch (channel)
|
||||
{
|
||||
case 0:
|
||||
return RectangleRender(ch0);
|
||||
case 1:
|
||||
return RectangleRender(ch1);
|
||||
case 2:
|
||||
return reg5011 << DAOUT_VOL_SHIFT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int GetFreq(int channel)
|
||||
{
|
||||
if (channel == 0 || channel == 1)
|
||||
{
|
||||
RECTANGLE ch = null;
|
||||
if (channel == 0) ch = ch0;
|
||||
else ch = ch1;
|
||||
|
||||
if (ch.enable == 0 || ch.vbl_length <= 0)
|
||||
return 0;
|
||||
if (ch.freq < INT2FIX(8))
|
||||
return 0;
|
||||
if (ch.fixed_envelope != 0)
|
||||
{
|
||||
if (ch.volume == 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((0x0F - ch.env_vol) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 16.0f));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int RectangleRender(RECTANGLE ch)
|
||||
{
|
||||
if (ch.enable == 0 || ch.vbl_length <= 0)
|
||||
return 0;
|
||||
|
||||
// vbl length counter
|
||||
if (ch.holdnote == 0)
|
||||
ch.vbl_length -= 5;
|
||||
|
||||
// envelope unit
|
||||
ch.env_phase -= 5 * 4;
|
||||
while (ch.env_phase < 0)
|
||||
{
|
||||
ch.env_phase += ch.env_decay;
|
||||
if ((ch.holdnote) != 0)
|
||||
ch.env_vol = (byte)((ch.env_vol + 1) & 0x0F);
|
||||
else if (ch.env_vol < 0x0F)
|
||||
ch.env_vol++;
|
||||
}
|
||||
|
||||
if (ch.freq < INT2FIX(8))
|
||||
return 0;
|
||||
|
||||
int volume;
|
||||
if ((ch.fixed_envelope) != 0)
|
||||
volume = ch.volume;
|
||||
else
|
||||
volume = (0x0F - ch.env_vol);
|
||||
|
||||
int output = volume << RECTANGLE_VOL_SHIFT;
|
||||
|
||||
ch.phaseacc -= cycle_rate;
|
||||
if (ch.phaseacc >= 0)
|
||||
{
|
||||
if (ch.adder < ch.duty_flip)
|
||||
ch.output_vol = output;
|
||||
else
|
||||
ch.output_vol = -output;
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
if (ch.freq > cycle_rate)
|
||||
{
|
||||
ch.phaseacc += ch.freq;
|
||||
ch.adder = (ch.adder + 1) & 0x0F;
|
||||
if (ch.adder < ch.duty_flip)
|
||||
ch.output_vol = output;
|
||||
else
|
||||
ch.output_vol = -output;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 加重平均
|
||||
int num_times, total;
|
||||
num_times = total = 0;
|
||||
while (ch.phaseacc < 0)
|
||||
{
|
||||
ch.phaseacc += ch.freq;
|
||||
ch.adder = (ch.adder + 1) & 0x0F;
|
||||
if (ch.adder < ch.duty_flip)
|
||||
total += output;
|
||||
else
|
||||
total -= output;
|
||||
num_times++;
|
||||
}
|
||||
ch.output_vol = total / num_times;
|
||||
}
|
||||
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
public override uint GetSize()
|
||||
{
|
||||
//3*sizeof(BYTE) + sizeof(ch0) + sizeof(ch1) + sizeof(sch0) + sizeof(sch1); 源代码似乎少了sync_reg5015的大小
|
||||
return 3 + ch0.GetSize() + ch1.GetSize() + 1 + sch0.GetSize() + sch1.GetSize();
|
||||
}
|
||||
|
||||
public override void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg5010);
|
||||
buffer.Write(reg5011);
|
||||
buffer.Write(reg5015);
|
||||
|
||||
ch0.SaveState(buffer);
|
||||
ch1.SaveState(buffer);
|
||||
|
||||
buffer.Write(sync_reg5015);
|
||||
sch0.SaveState(buffer);
|
||||
sch1.SaveState(buffer);
|
||||
}
|
||||
|
||||
public class SYNCRECTANGLE : IStateBufferObject
|
||||
{
|
||||
// For sync
|
||||
public byte[] reg = new byte[4];
|
||||
public byte enable;
|
||||
public byte holdnote;
|
||||
public byte[] dummy = new byte[2];
|
||||
public int vbl_length;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, reg.Length);
|
||||
enable = 0;
|
||||
holdnote = 0;
|
||||
Array.Clear(dummy, 0, dummy.Length);
|
||||
vbl_length = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg);
|
||||
buffer.Write(enable);
|
||||
buffer.Write(holdnote);
|
||||
buffer.Write(dummy);
|
||||
buffer.Write(vbl_length);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
reg = buffer.Read_bytes(4);
|
||||
enable = buffer.Read_byte();
|
||||
holdnote = buffer.Read_byte();
|
||||
dummy = buffer.Read_bytes(2);
|
||||
vbl_length = buffer.Read_int();
|
||||
}
|
||||
}
|
||||
|
||||
public class RECTANGLE : IStateBufferObject
|
||||
{
|
||||
public byte[] reg = new byte[4];
|
||||
public byte enable;
|
||||
|
||||
public int vbl_length;
|
||||
|
||||
public int phaseacc;
|
||||
public int freq;
|
||||
|
||||
public int output_vol;
|
||||
public byte fixed_envelope;
|
||||
public byte holdnote;
|
||||
public byte volume;
|
||||
|
||||
public byte env_vol;
|
||||
public int env_phase;
|
||||
public int env_decay;
|
||||
|
||||
public int adder;
|
||||
public int duty_flip;
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 45;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg);
|
||||
buffer.Write(enable);
|
||||
buffer.Write(vbl_length);
|
||||
buffer.Write(phaseacc);
|
||||
buffer.Write(freq);
|
||||
buffer.Write(output_vol);
|
||||
buffer.Write(fixed_envelope);
|
||||
buffer.Write(holdnote);
|
||||
buffer.Write(volume);
|
||||
buffer.Write(env_vol);
|
||||
buffer.Write(env_phase);
|
||||
buffer.Write(env_decay);
|
||||
buffer.Write(adder);
|
||||
buffer.Write(duty_flip);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
reg = buffer.Read_bytes(4);
|
||||
enable = buffer.Read_byte();
|
||||
vbl_length = buffer.Read_int();
|
||||
phaseacc = buffer.Read_int();
|
||||
freq = buffer.Read_int();
|
||||
output_vol = buffer.Read_int();
|
||||
fixed_envelope = buffer.Read_byte();
|
||||
holdnote = buffer.Read_byte();
|
||||
volume = buffer.Read_byte();
|
||||
env_vol = buffer.Read_byte();
|
||||
env_phase = buffer.Read_int();
|
||||
env_decay = buffer.Read_int();
|
||||
adder = buffer.Read_int();
|
||||
duty_flip = buffer.Read_int();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
256
Core/VirtualNes.Core/ApuEX/APU_N106.cs
Normal file
256
Core/VirtualNes.Core/ApuEX/APU_N106.cs
Normal file
@ -0,0 +1,256 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_N106 : APU_INTERFACE
|
||||
{
|
||||
CHANNEL[] op = new CHANNEL[8];
|
||||
|
||||
const int CHANNEL_VOL_SHIFT = 6;
|
||||
float cpu_clock;
|
||||
uint cycle_rate;
|
||||
|
||||
byte addrinc;
|
||||
byte address;
|
||||
byte channel_use;
|
||||
|
||||
byte[] tone = new byte[0x100];
|
||||
|
||||
public APU_N106()
|
||||
{
|
||||
// 仮設定
|
||||
cpu_clock = APU_CLOCK;
|
||||
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * 22050.0f));
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
op[i].ZeroMemory();
|
||||
op[i].tonelen = 0x10 << 18;
|
||||
}
|
||||
|
||||
address = 0;
|
||||
addrinc = 1;
|
||||
channel_use = 8;
|
||||
|
||||
Setup(fClock, nRate);
|
||||
|
||||
// TONEの初期化はしない...
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
cpu_clock = fClock;
|
||||
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * nRate));
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0x4800)
|
||||
{
|
||||
// tone[address*2+0] = (INT)(data&0x0F);
|
||||
// tone[address*2+1] = (INT)(data >>4);
|
||||
tone[address * 2 + 0] = (byte)(data & 0x0F);
|
||||
tone[address * 2 + 1] = (byte)(data >> 4);
|
||||
|
||||
if (address >= 0x40)
|
||||
{
|
||||
int no = (address - 0x40) >> 3;
|
||||
uint tonelen = 0;
|
||||
ref CHANNEL ch = ref op[no];
|
||||
|
||||
switch (address & 7)
|
||||
{
|
||||
case 0x00:
|
||||
ch.freq = (uint)((ch.freq & ~0x000000FF) | data);
|
||||
break;
|
||||
case 0x02:
|
||||
ch.freq = (uint)((ch.freq & ~0x0000FF00) | ((uint)data << 8));
|
||||
break;
|
||||
case 0x04:
|
||||
ch.freq = (uint)((ch.freq & ~0x00030000) | (((uint)data & 0x03) << 16));
|
||||
tonelen = (uint)((0x20 - (data & 0x1c)) << 18);
|
||||
ch.databuf = (byte)((data & 0x1c) >> 2);
|
||||
if (ch.tonelen != tonelen)
|
||||
{
|
||||
ch.tonelen = tonelen;
|
||||
ch.phase = 0;
|
||||
}
|
||||
break;
|
||||
case 0x06:
|
||||
ch.toneadr = data;
|
||||
break;
|
||||
case 0x07:
|
||||
ch.vol = (byte)(data & 0x0f);
|
||||
ch.volupdate = 0xFF;
|
||||
if (no == 7)
|
||||
channel_use = (byte)(((data >> 4) & 0x07) + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addrinc != 0)
|
||||
{
|
||||
address = (byte)((address + 1) & 0x7f);
|
||||
}
|
||||
}
|
||||
else if (addr == 0xF800)
|
||||
{
|
||||
address = (byte)(data & 0x7F);
|
||||
addrinc = (byte)(data & 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
public override byte Read(ushort addr)
|
||||
{
|
||||
// $4800 dummy read!!
|
||||
if (addr == 0x0000)
|
||||
{
|
||||
if (addrinc != 0)
|
||||
{
|
||||
address = (byte)((address + 1) & 0x7F);
|
||||
}
|
||||
}
|
||||
|
||||
return (byte)(addr >> 8);
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
if (channel >= (8 - channel_use) && channel < 8)
|
||||
{
|
||||
return ChannelRender(ref op[channel]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int GetFreq(int channel)
|
||||
{
|
||||
if (channel < 8)
|
||||
{
|
||||
channel &= 7;
|
||||
if (channel < (8 - channel_use))
|
||||
return 0;
|
||||
|
||||
ref CHANNEL ch = ref op[channel & 0x07];
|
||||
if (ch.freq == 0 || ch.vol == 0)
|
||||
return 0;
|
||||
int temp = channel_use * (8 - ch.databuf) * 4 * 45;
|
||||
if (temp == 0)
|
||||
return 0;
|
||||
return (int)(256.0 * cpu_clock * 12.0 * ch.freq / ((double)0x40000 * temp));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int ChannelRender(ref CHANNEL ch)
|
||||
{
|
||||
uint phasespd = (uint)(channel_use << 20);
|
||||
|
||||
ch.phaseacc -= (int)cycle_rate;
|
||||
if (ch.phaseacc >= 0)
|
||||
{
|
||||
if (ch.volupdate != 0)
|
||||
{
|
||||
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
|
||||
ch.volupdate = 0;
|
||||
}
|
||||
return ch.output;
|
||||
}
|
||||
|
||||
while (ch.phaseacc < 0)
|
||||
{
|
||||
ch.phaseacc += (int)phasespd;
|
||||
ch.phase += ch.freq;
|
||||
}
|
||||
while (ch.tonelen != 0 && (ch.phase >= ch.tonelen))
|
||||
{
|
||||
ch.phase -= ch.tonelen;
|
||||
}
|
||||
|
||||
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
|
||||
|
||||
return ch.output;
|
||||
}
|
||||
|
||||
public override uint GetSize()
|
||||
{
|
||||
return (uint)(3 * sizeof(byte) + 8 * op[0].GetSize() + tone.Length);
|
||||
}
|
||||
|
||||
public override void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(addrinc);
|
||||
buffer.Write(address);
|
||||
buffer.Write(channel_use);
|
||||
|
||||
foreach (var oneOp in op)
|
||||
oneOp.SaveState(buffer);
|
||||
|
||||
buffer.Write(tone);
|
||||
}
|
||||
|
||||
public struct CHANNEL : IStateBufferObject
|
||||
{
|
||||
public int phaseacc;
|
||||
|
||||
public uint freq;
|
||||
public uint phase;
|
||||
public uint tonelen;
|
||||
|
||||
public int output;
|
||||
|
||||
public byte toneadr;
|
||||
public byte volupdate;
|
||||
|
||||
public byte vol;
|
||||
public byte databuf;
|
||||
|
||||
internal void ZeroMemory()
|
||||
{
|
||||
phaseacc = 0;
|
||||
freq = 0;
|
||||
phase = 0;
|
||||
tonelen = 0;
|
||||
output = 0;
|
||||
toneadr = 0;
|
||||
volupdate = 0;
|
||||
vol = 0;
|
||||
databuf = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 4 * 5 + 4;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(phaseacc);
|
||||
buffer.Write(freq);
|
||||
buffer.Write(phase);
|
||||
buffer.Write(tonelen);
|
||||
buffer.Write(output);
|
||||
buffer.Write(toneadr);
|
||||
buffer.Write(volupdate);
|
||||
buffer.Write(vol);
|
||||
buffer.Write(databuf);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
phaseacc = buffer.Read_int();
|
||||
freq = buffer.Read_uint();
|
||||
phase = buffer.Read_uint();
|
||||
tonelen = buffer.Read_uint();
|
||||
output = buffer.Read_int();
|
||||
toneadr = buffer.Read_byte();
|
||||
volupdate = buffer.Read_byte();
|
||||
vol = buffer.Read_byte();
|
||||
databuf = buffer.Read_byte();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
383
Core/VirtualNes.Core/ApuEX/APU_VRC6.cs
Normal file
383
Core/VirtualNes.Core/ApuEX/APU_VRC6.cs
Normal file
@ -0,0 +1,383 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_VRC6 : APU_INTERFACE
|
||||
{
|
||||
public const int RECTANGLE_VOL_SHIFT = 8;
|
||||
public const int SAWTOOTH_VOL_SHIFT = 6;
|
||||
|
||||
private RECTANGLE ch0 = new RECTANGLE();
|
||||
private RECTANGLE ch1 = new RECTANGLE();
|
||||
private SAWTOOTH ch2 = new SAWTOOTH();
|
||||
|
||||
private int cycle_rate;
|
||||
private float cpu_clock;
|
||||
|
||||
public APU_VRC6()
|
||||
{
|
||||
Reset(APU_CLOCK, 22050);
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
ch0.ZeroMemory();
|
||||
ch1.ZeroMemory();
|
||||
ch2.ZeroMemory();
|
||||
|
||||
Setup(fClock, nRate);
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
cpu_clock = fClock;
|
||||
cycle_rate = (int)(fClock * 65536.0f / nRate);
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
// VRC6 CH0 rectangle
|
||||
case 0x9000:
|
||||
ch0.reg[0] = data;
|
||||
ch0.gate = (byte)(data & 0x80);
|
||||
ch0.volume = (byte)(data & 0x0F);
|
||||
ch0.duty_pos = (byte)((data >> 4) & 0x07);
|
||||
break;
|
||||
case 0x9001:
|
||||
ch0.reg[1] = data;
|
||||
ch0.freq = INT2FIX((((ch0.reg[2] & 0x0F) << 8) | data) + 1);
|
||||
break;
|
||||
case 0x9002:
|
||||
ch0.reg[2] = data;
|
||||
ch0.enable = (byte)(data & 0x80);
|
||||
ch0.freq = INT2FIX((((data & 0x0F) << 8) | ch0.reg[1]) + 1);
|
||||
break;
|
||||
// VRC6 CH1 rectangle
|
||||
case 0xA000:
|
||||
ch1.reg[0] = data;
|
||||
ch1.gate = (byte)(data & 0x80);
|
||||
ch1.volume = (byte)(data & 0x0F);
|
||||
ch1.duty_pos = (byte)((data >> 4) & 0x07);
|
||||
break;
|
||||
case 0xA001:
|
||||
ch1.reg[1] = data;
|
||||
ch1.freq = INT2FIX((((ch1.reg[2] & 0x0F) << 8) | data) + 1);
|
||||
break;
|
||||
case 0xA002:
|
||||
ch1.reg[2] = data;
|
||||
ch1.enable = (byte)(data & 0x80);
|
||||
ch1.freq = INT2FIX((((data & 0x0F) << 8) | ch1.reg[1]) + 1);
|
||||
break;
|
||||
// VRC6 CH2 sawtooth
|
||||
case 0xB000:
|
||||
ch2.reg[1] = data;
|
||||
ch2.phaseaccum = (byte)(data & 0x3F);
|
||||
break;
|
||||
case 0xB001:
|
||||
ch2.reg[1] = data;
|
||||
ch2.freq = INT2FIX((((ch2.reg[2] & 0x0F) << 8) | data) + 1);
|
||||
break;
|
||||
case 0xB002:
|
||||
ch2.reg[2] = data;
|
||||
ch2.enable = (byte)(data & 0x80);
|
||||
ch2.freq = INT2FIX((((data & 0x0F) << 8) | ch2.reg[1]) + 1);
|
||||
// ch2.adder = 0; // 僋儕傾偡傞偲僲僀僘偺尨場偵側傞
|
||||
// ch2.accum = 0; // 僋儕傾偡傞偲僲僀僘偺尨場偵側傞
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
switch (channel)
|
||||
{
|
||||
case 0:
|
||||
return RectangleRender(ch0);
|
||||
case 1:
|
||||
return RectangleRender(ch1);
|
||||
case 2:
|
||||
return SawtoothRender(ch2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int GetFreq(int channel)
|
||||
{
|
||||
if (channel == 0 || channel == 1)
|
||||
{
|
||||
RECTANGLE ch = null;
|
||||
if (channel == 0) ch = ch0;
|
||||
else ch = ch1;
|
||||
if (ch.enable == 0 || ch.gate != 0 || ch.volume == 0)
|
||||
return 0;
|
||||
if (ch.freq < INT2FIX(8))
|
||||
return 0;
|
||||
return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 16.0f));
|
||||
}
|
||||
if (channel == 2)
|
||||
{
|
||||
SAWTOOTH ch = ch2;
|
||||
if (ch.enable == 0 || ch.phaseaccum == 0)
|
||||
return 0;
|
||||
if (ch.freq < INT2FIX(8))
|
||||
return 0;
|
||||
return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 14.0f));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int RectangleRender(RECTANGLE ch)
|
||||
{
|
||||
// Enable?
|
||||
if (ch.enable == 0)
|
||||
{
|
||||
ch.output_vol = 0;
|
||||
ch.adder = 0;
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
// Digitized output
|
||||
if (ch.gate != 0)
|
||||
{
|
||||
ch.output_vol = ch.volume << RECTANGLE_VOL_SHIFT;
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
// 堦掕埲忋偺廃攇悢偼張棟偟側偄(柍懯)
|
||||
if (ch.freq < INT2FIX(8))
|
||||
{
|
||||
ch.output_vol = 0;
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
ch.phaseacc -= cycle_rate;
|
||||
if (ch.phaseacc >= 0)
|
||||
return ch.output_vol;
|
||||
|
||||
int output = ch.volume << RECTANGLE_VOL_SHIFT;
|
||||
|
||||
if (ch.freq > cycle_rate)
|
||||
{
|
||||
// add 1 step
|
||||
ch.phaseacc += ch.freq;
|
||||
ch.adder = (byte)((ch.adder + 1) & 0x0F);
|
||||
if (ch.adder <= ch.duty_pos)
|
||||
ch.output_vol = output;
|
||||
else
|
||||
ch.output_vol = -output;
|
||||
}
|
||||
else
|
||||
{
|
||||
// average calculate
|
||||
int num_times, total;
|
||||
num_times = total = 0;
|
||||
while (ch.phaseacc < 0)
|
||||
{
|
||||
ch.phaseacc += ch.freq;
|
||||
ch.adder = (byte)((ch.adder + 1) & 0x0F);
|
||||
if (ch.adder <= ch.duty_pos)
|
||||
total += output;
|
||||
else
|
||||
total += -output;
|
||||
num_times++;
|
||||
}
|
||||
ch.output_vol = total / num_times;
|
||||
}
|
||||
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
private int SawtoothRender(SAWTOOTH ch)
|
||||
{
|
||||
// Digitized output
|
||||
if (ch.enable == 0)
|
||||
{
|
||||
ch.output_vol = 0;
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
// 堦掕埲忋偺廃攇悢偼張棟偟側偄(柍懯)
|
||||
if (ch.freq < INT2FIX(9))
|
||||
{
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
ch.phaseacc -= cycle_rate / 2;
|
||||
if (ch.phaseacc >= 0)
|
||||
return ch.output_vol;
|
||||
|
||||
if (ch.freq > cycle_rate / 2)
|
||||
{
|
||||
// add 1 step
|
||||
ch.phaseacc += ch.freq;
|
||||
if (++ch.adder >= 7)
|
||||
{
|
||||
ch.adder = 0;
|
||||
ch.accum = 0;
|
||||
}
|
||||
ch.accum += ch.phaseaccum;
|
||||
ch.output_vol = ch.accum << SAWTOOTH_VOL_SHIFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// average calculate
|
||||
int num_times, total;
|
||||
num_times = total = 0;
|
||||
while (ch.phaseacc < 0)
|
||||
{
|
||||
ch.phaseacc += ch.freq;
|
||||
if (++ch.adder >= 7)
|
||||
{
|
||||
ch.adder = 0;
|
||||
ch.accum = 0;
|
||||
}
|
||||
ch.accum += ch.phaseaccum;
|
||||
total += ch.accum << SAWTOOTH_VOL_SHIFT;
|
||||
num_times++;
|
||||
}
|
||||
ch.output_vol = (total / num_times);
|
||||
}
|
||||
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
public override uint GetSize()
|
||||
{
|
||||
return ch0.GetSize() + ch1.GetSize() + ch2.GetSize();
|
||||
}
|
||||
public override void SaveState(StateBuffer p)
|
||||
{
|
||||
ch0.SaveState(p);
|
||||
ch1.SaveState(p);
|
||||
ch2.SaveState(p);
|
||||
}
|
||||
|
||||
public class RECTANGLE : IStateBufferObject
|
||||
{
|
||||
public byte[] reg = new byte[3];
|
||||
|
||||
public byte enable;
|
||||
public byte gate;
|
||||
public byte volume;
|
||||
|
||||
public int phaseacc;
|
||||
public int freq;
|
||||
public int output_vol;
|
||||
|
||||
public byte adder;
|
||||
public byte duty_pos;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, reg.Length);
|
||||
enable = 0;
|
||||
gate = 0;
|
||||
volume = 0;
|
||||
|
||||
phaseacc = 0;
|
||||
freq = 0;
|
||||
output_vol = 0;
|
||||
|
||||
adder = 0;
|
||||
duty_pos = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg);
|
||||
buffer.Write(enable);
|
||||
buffer.Write(gate);
|
||||
buffer.Write(volume);
|
||||
buffer.Write(phaseacc);
|
||||
buffer.Write(freq);
|
||||
buffer.Write(output_vol);
|
||||
buffer.Write(adder);
|
||||
buffer.Write(duty_pos);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
reg = buffer.Read_bytes(3);
|
||||
enable = buffer.Read_byte();
|
||||
gate = buffer.Read_byte();
|
||||
volume = buffer.Read_byte();
|
||||
phaseacc = buffer.Read_int();
|
||||
freq = buffer.Read_int();
|
||||
output_vol = buffer.Read_int();
|
||||
adder = buffer.Read_byte();
|
||||
duty_pos = buffer.Read_byte();
|
||||
}
|
||||
}
|
||||
|
||||
public class SAWTOOTH : IStateBufferObject
|
||||
{
|
||||
public byte[] reg = new byte[3];
|
||||
|
||||
public byte enable;
|
||||
public byte volume;
|
||||
|
||||
public int phaseacc;
|
||||
public int freq;
|
||||
public int output_vol;
|
||||
|
||||
public byte adder;
|
||||
public byte accum;
|
||||
public byte phaseaccum;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, reg.Length);
|
||||
enable = 0;
|
||||
volume = 0;
|
||||
|
||||
phaseacc = 0;
|
||||
freq = 0;
|
||||
output_vol = 0;
|
||||
|
||||
adder = 0;
|
||||
accum = 0;
|
||||
phaseaccum = 0;
|
||||
}
|
||||
|
||||
public uint GetSize()
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
public void SaveState(StateBuffer buffer)
|
||||
{
|
||||
buffer.Write(reg);
|
||||
buffer.Write(enable);
|
||||
buffer.Write(volume);
|
||||
buffer.Write(phaseacc);
|
||||
buffer.Write(freq);
|
||||
buffer.Write(output_vol);
|
||||
buffer.Write(adder);
|
||||
buffer.Write(accum);
|
||||
buffer.Write(phaseaccum);
|
||||
}
|
||||
|
||||
public void LoadState(StateReader buffer)
|
||||
{
|
||||
reg = buffer.Read_bytes(3);
|
||||
enable = buffer.Read_byte();
|
||||
volume = buffer.Read_byte();
|
||||
phaseacc = buffer.Read_int();
|
||||
freq = buffer.Read_int();
|
||||
output_vol = buffer.Read_int();
|
||||
adder = buffer.Read_byte();
|
||||
accum = buffer.Read_byte();
|
||||
phaseaccum = buffer.Read_byte();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
106
Core/VirtualNes.Core/ApuEX/APU_VRC7.cs
Normal file
106
Core/VirtualNes.Core/ApuEX/APU_VRC7.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using VirtualNes.Core.Emu2413;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_VRC7 : APU_INTERFACE
|
||||
{
|
||||
OPLL VRC7_OPLL;
|
||||
byte address;
|
||||
|
||||
public APU_VRC7()
|
||||
{
|
||||
Emu2413API.OPLL_init(3579545, 22050); // 仮のサンプリングレート
|
||||
VRC7_OPLL = Emu2413API.OPLL_new();
|
||||
|
||||
if (VRC7_OPLL != null)
|
||||
{
|
||||
Emu2413API.OPLL_reset(VRC7_OPLL);
|
||||
Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE);
|
||||
VRC7_OPLL.masterVolume = 128;
|
||||
}
|
||||
|
||||
// 仮設定
|
||||
Reset(APU_CLOCK, 22050);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (VRC7_OPLL != null)
|
||||
{
|
||||
Emu2413API.OPLL_delete(VRC7_OPLL);
|
||||
VRC7_OPLL = null;
|
||||
// OPLL_close(); // 無くても良い(中身無し)
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
if (VRC7_OPLL != null)
|
||||
{
|
||||
Emu2413API.OPLL_reset(VRC7_OPLL);
|
||||
Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE);
|
||||
VRC7_OPLL.masterVolume = 128;
|
||||
}
|
||||
|
||||
address = 0;
|
||||
|
||||
Setup(fClock, nRate);
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
Emu2413API.OPLL_setClock((UInt32)(fClock * 2.0f), (UInt32)nRate);
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (VRC7_OPLL != null)
|
||||
{
|
||||
if (addr == 0x9010)
|
||||
{
|
||||
address = data;
|
||||
}
|
||||
else if (addr == 0x9030)
|
||||
{
|
||||
Emu2413API.OPLL_writeReg(VRC7_OPLL, address, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
if (VRC7_OPLL != null)
|
||||
return Emu2413API.OPLL_calc(VRC7_OPLL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float[] blkmul = { 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f };
|
||||
public override int GetFreq(int channel)
|
||||
{
|
||||
if (VRC7_OPLL != null && channel < 8)
|
||||
{
|
||||
int fno = ((VRC7_OPLL.reg[0x20 + channel] & 0x01) << 8) + VRC7_OPLL.reg[0x10 + channel];
|
||||
int blk = (VRC7_OPLL.reg[0x20 + channel] >> 1) & 0x07;
|
||||
|
||||
if ((VRC7_OPLL.reg[0x20 + channel] & 0x10) != 0)
|
||||
{
|
||||
return (int)((256.0d * fno * blkmul[blk]) / ((1 << 18) / (3579545.0 / 72.0)));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override uint GetSize()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override void SaveState(StateBuffer buffer)
|
||||
{
|
||||
//not impl
|
||||
}
|
||||
}
|
||||
}
|
2084
Core/VirtualNes.Core/CPU.cs
Normal file
2084
Core/VirtualNes.Core/CPU.cs
Normal file
File diff suppressed because it is too large
Load Diff
36
Core/VirtualNes.Core/Cheat.cs
Normal file
36
Core/VirtualNes.Core/Cheat.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CHEATCODE
|
||||
{
|
||||
// 埲壓偺俀偮偼OR儅僗僋
|
||||
public const int CHEAT_ENABLE = 1 << 0;
|
||||
public const int CHEAT_KEYDISABLE = 1 << 1;
|
||||
|
||||
// 彂偒崬傒庬椶
|
||||
public const int CHEAT_TYPE_ALWAYS = 0; // 忢偵彂偒崬傒
|
||||
public const int CHEAT_TYPE_ONCE = 1; // 侾夞偩偗彂偒崬傒
|
||||
public const int CHEAT_TYPE_GREATER = 2; // 僨乕僞傛傝戝偒偄帪
|
||||
public const int CHEAT_TYPE_LESS = 3; // 僨乕僞傛傝彫偝偄帪
|
||||
|
||||
// 僨乕僞挿
|
||||
public const int CHEAT_LENGTH_1BYTE = 0;
|
||||
public const int CHEAT_LENGTH_2BYTE = 1;
|
||||
public const int CHEAT_LENGTH_3BYTE = 2;
|
||||
public const int CHEAT_LENGTH_4BYTE = 3;
|
||||
|
||||
public byte enable;
|
||||
public byte type;
|
||||
public byte length;
|
||||
public ushort address;
|
||||
public uint data;
|
||||
|
||||
public string comment;
|
||||
}
|
||||
|
||||
class GENIECODE
|
||||
{
|
||||
public ushort address;
|
||||
public byte data;
|
||||
public byte cmp;
|
||||
};
|
||||
}
|
77
Core/VirtualNes.Core/CoreLibs/ByteArrayRef.cs
Normal file
77
Core/VirtualNes.Core/CoreLibs/ByteArrayRef.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class ArrayRef<T>
|
||||
{
|
||||
public T[] RawArray => m_rawArray;
|
||||
private T[] m_rawArray;
|
||||
private int m_offset;
|
||||
private int m_length;
|
||||
|
||||
public int Offset
|
||||
{
|
||||
get => m_offset;
|
||||
set
|
||||
{
|
||||
var gap = value - m_offset;
|
||||
m_length -= gap;
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayRef() { }
|
||||
public ArrayRef(T[] array, int offset, int length)
|
||||
{
|
||||
SetArray(array, offset, length);
|
||||
}
|
||||
|
||||
public ArrayRef(T[] array) : this(array, 0, array.Length) { }
|
||||
public ArrayRef(T[] array, int offset) : this(array, offset, array.Length - offset) { }
|
||||
|
||||
public void SetArray(T[] array, int offset, int length)
|
||||
{
|
||||
m_rawArray = array;
|
||||
m_offset = offset;
|
||||
m_length = length;
|
||||
}
|
||||
|
||||
public void SetArray(T[] array, int offset)
|
||||
{
|
||||
m_rawArray = array;
|
||||
m_offset = offset;
|
||||
m_length = array.Length - offset;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_rawArray[m_offset + index];
|
||||
}
|
||||
set
|
||||
{
|
||||
m_rawArray[(m_offset + index)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteTo(T[] source, int start, int length)
|
||||
{
|
||||
Array.Copy(source, 0, m_rawArray, Offset + start, length);
|
||||
}
|
||||
|
||||
public Span<T> Span(int start, int length)
|
||||
{
|
||||
return new Span<T>(m_rawArray, start + Offset, length);
|
||||
}
|
||||
|
||||
public static implicit operator ArrayRef<T>(T[] array)
|
||||
{
|
||||
return new ArrayRef<T>(array);
|
||||
}
|
||||
|
||||
public static implicit operator Span<T>(ArrayRef<T> array)
|
||||
{
|
||||
return new Span<T>(array.m_rawArray, array.Offset, array.m_length);
|
||||
}
|
||||
}
|
||||
}
|
87
Core/VirtualNes.Core/CoreLibs/CRC.cs
Normal file
87
Core/VirtualNes.Core/CoreLibs/CRC.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public static class CRC
|
||||
{
|
||||
const int CHAR_BIT = 8;
|
||||
const uint CRCPOLY1 = 0x04C11DB7U;
|
||||
const uint CRCPOLY2 = 0xEDB88320U;
|
||||
|
||||
static bool m_Init;
|
||||
static bool m_InitRev;
|
||||
static uint[] m_CrcTable = new uint[byte.MaxValue + 1];
|
||||
static uint[] m_CrcTableRev = new uint[byte.MaxValue + 1];
|
||||
|
||||
public static ulong Crc(int size, Span<byte> c)
|
||||
{
|
||||
if (!m_Init)
|
||||
{
|
||||
MakeTable();
|
||||
m_Init = true;
|
||||
}
|
||||
|
||||
ulong r = 0xFFFFFFFFUL;
|
||||
int step = 0;
|
||||
while (--size >= 0)
|
||||
{
|
||||
r = (r << CHAR_BIT) ^ m_CrcTable[(byte)(r >> (32 - CHAR_BIT)) ^ c[step]];
|
||||
step++;
|
||||
}
|
||||
return ~r & 0xFFFFFFFFUL;
|
||||
}
|
||||
public static uint CrcRev(int size, Span<byte> c)
|
||||
{
|
||||
if (!m_InitRev)
|
||||
{
|
||||
MakeTableRev();
|
||||
m_InitRev = true;
|
||||
}
|
||||
|
||||
uint r = 0xFFFFFFFFU;
|
||||
int step = 0;
|
||||
while (--size >= 0)
|
||||
{
|
||||
r = (r >> CHAR_BIT) ^ m_CrcTableRev[(byte)r ^ c[step]];
|
||||
step++;
|
||||
}
|
||||
return r ^ 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
static void MakeTable()
|
||||
{
|
||||
int i, j;
|
||||
uint r;
|
||||
|
||||
for (i = 0; i <= byte.MaxValue; i++)
|
||||
{
|
||||
r = (uint)i << (32 - CHAR_BIT);
|
||||
for (j = 0; j < CHAR_BIT; j++)
|
||||
{
|
||||
if ((r & 0x80000000UL) > 0) r = (r << 1) ^ CRCPOLY1;
|
||||
else r <<= 1;
|
||||
}
|
||||
m_CrcTable[i] = r & 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
}
|
||||
static void MakeTableRev()
|
||||
{
|
||||
int i, j;
|
||||
uint r;
|
||||
|
||||
for (i = 0; i <= byte.MaxValue; i++)
|
||||
{
|
||||
r = (uint)i;
|
||||
for (j = 0; j < CHAR_BIT; j++)
|
||||
{
|
||||
if ((r & 1) > 0) r = (r >> 1) ^ CRCPOLY2;
|
||||
else r >>= 1;
|
||||
}
|
||||
m_CrcTableRev[i] = r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
1543
Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413.cs
Normal file
1543
Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413.cs
Normal file
File diff suppressed because it is too large
Load Diff
208
Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413_Class.cs
Normal file
208
Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413_Class.cs
Normal file
@ -0,0 +1,208 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core.Emu2413
|
||||
{
|
||||
public static class Const
|
||||
{
|
||||
internal static sbyte[][] Create_Default_Inst()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
sbyte[][] res = new sbyte[Emu2413API.OPLL_TONE_NUM][]
|
||||
{
|
||||
new sbyte[]
|
||||
{
|
||||
(sbyte)0x00,(sbyte) 0x00, (sbyte)0x00, (sbyte)0x00,(sbyte) 0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x61,(sbyte) 0x61, (sbyte)0x1e, (sbyte)0x17,(sbyte) 0xf0, (sbyte)0x7f, (sbyte)0x07, (sbyte)0x17, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x13,(sbyte) 0x41, (sbyte)0x0f, (sbyte)0x0d,(sbyte) 0xce, (sbyte)0xd2, (sbyte)0x43, (sbyte)0x13, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x03,(sbyte) 0x01, (sbyte)0x99, (sbyte)0x04,(sbyte) 0xff, (sbyte)0xc3, (sbyte)0x03, (sbyte)0x73, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x21,(sbyte) 0x61, (sbyte)0x1b, (sbyte)0x07,(sbyte) 0xaf, (sbyte)0x63, (sbyte)0x40, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x22,(sbyte) 0x21, (sbyte)0x1e, (sbyte)0x06,(sbyte) 0xf0, (sbyte)0x76, (sbyte)0x08, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x31,(sbyte) 0x22, (sbyte)0x16, (sbyte)0x05,(sbyte) 0x90, (sbyte)0x71, (sbyte)0x00, (sbyte)0x18, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x21,(sbyte) 0x61, (sbyte)0x1d, (sbyte)0x07,(sbyte) 0x82, (sbyte)0x81, (sbyte)0x10, (sbyte)0x17, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x23,(sbyte) 0x21, (sbyte)0x2d, (sbyte)0x16,(sbyte) 0xc0, (sbyte)0x70, (sbyte)0x07, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x61,(sbyte) 0x21, (sbyte)0x1b, (sbyte)0x06,(sbyte) 0x64, (sbyte)0x65, (sbyte)0x18, (sbyte)0x18, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x61,(sbyte) 0x61, (sbyte)0x0c, (sbyte)0x18,(sbyte) 0x85, (sbyte)0xa0, (sbyte)0x79, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x23,(sbyte) 0x21, (sbyte)0x87, (sbyte)0x11,(sbyte) 0xf0, (sbyte)0xa4, (sbyte)0x00, (sbyte)0xf7, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x97,(sbyte) 0xe1, (sbyte)0x28, (sbyte)0x07,(sbyte) 0xff, (sbyte)0xf3, (sbyte)0x02, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x61,(sbyte) 0x10, (sbyte)0x0c, (sbyte)0x05,(sbyte) 0xf2, (sbyte)0xc4, (sbyte)0x40, (sbyte)0xc8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x01,(sbyte) 0x01, (sbyte)0x56, (sbyte)0x03,(sbyte) 0xb4, (sbyte)0xb2, (sbyte)0x23, (sbyte)0x58, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x61,(sbyte) 0x41, (sbyte)0x89, (sbyte)0x03,(sbyte) 0xf1, (sbyte)0xf4, (sbyte)0xf0, (sbyte)0x13, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x04,(sbyte) 0x21, (sbyte)0x28, (sbyte)0x00,(sbyte) 0xdf, (sbyte)0xf8, (sbyte)0xff, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x23,(sbyte) 0x22, (sbyte)0x00, (sbyte)0x00,(sbyte) 0xd8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x25,(sbyte) 0x18, (sbyte)0x00, (sbyte)0x00,(sbyte) 0xf8, (sbyte)0xda, (sbyte)0xf8, (sbyte)0x55, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
},
|
||||
new sbyte[]
|
||||
{
|
||||
(sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x33, (sbyte)0x01, (sbyte)0x09, (sbyte)0x0e, (sbyte)0x94, (sbyte)0x90, (sbyte)0x40, (sbyte)0x01, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x13, (sbyte)0x41, (sbyte)0x0f, (sbyte)0x0d, (sbyte)0xce, (sbyte)0xd3, (sbyte)0x43, (sbyte)0x13, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x01, (sbyte)0x12, (sbyte)0x1b, (sbyte)0x06, (sbyte)0xff, (sbyte)0xd2, (sbyte)0x00, (sbyte)0x32, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x61, (sbyte)0x61, (sbyte)0x1b, (sbyte)0x07, (sbyte)0xaf, (sbyte)0x63, (sbyte)0x20, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x22, (sbyte)0x21, (sbyte)0x1e, (sbyte)0x06, (sbyte)0xf0, (sbyte)0x76, (sbyte)0x08, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x66, (sbyte)0x21, (sbyte)0x15, (sbyte)0x00, (sbyte)0x93, (sbyte)0x94, (sbyte)0x20, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x21, (sbyte)0x61, (sbyte)0x1c, (sbyte)0x07, (sbyte)0x82, (sbyte)0x81, (sbyte)0x10, (sbyte)0x17, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x23, (sbyte)0x21, (sbyte)0x20, (sbyte)0x1f, (sbyte)0xc0, (sbyte)0x71, (sbyte)0x07, (sbyte)0x47, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x25, (sbyte)0x31, (sbyte)0x26, (sbyte)0x05, (sbyte)0x64, (sbyte)0x41, (sbyte)0x18, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x17, (sbyte)0x21, (sbyte)0x28, (sbyte)0x07, (sbyte)0xff, (sbyte)0x83, (sbyte)0x02, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x97, (sbyte)0x81, (sbyte)0x25, (sbyte)0x07, (sbyte)0xcf, (sbyte)0xc8, (sbyte)0x02, (sbyte)0x14, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x21, (sbyte)0x21, (sbyte)0x54, (sbyte)0x0f, (sbyte)0x80, (sbyte)0x7f, (sbyte)0x07, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x01, (sbyte)0x01, (sbyte)0x56, (sbyte)0x03, (sbyte)0xd3, (sbyte)0xb2, (sbyte)0x43, (sbyte)0x58, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x31, (sbyte)0x21, (sbyte)0x0c, (sbyte)0x03, (sbyte)0x82, (sbyte)0xc0, (sbyte)0x40, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x21, (sbyte)0x01, (sbyte)0x0c, (sbyte)0x03, (sbyte)0xd4, (sbyte)0xd3, (sbyte)0x40, (sbyte)0x84, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x04, (sbyte)0x21, (sbyte)0x28, (sbyte)0x00, (sbyte)0xdf, (sbyte)0xf8, (sbyte)0xff, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x23, (sbyte)0x22, (sbyte)0x00, (sbyte)0x00, (sbyte)0xa8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
(sbyte)0x25, (sbyte)0x18, (sbyte)0x00, (sbyte)0x00, (sbyte)0xf8, (sbyte)0xa9, (sbyte)0xf8, (sbyte)0x55, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00,
|
||||
}
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
internal static OPLL_PATCH[][] Create_Default_Patch()
|
||||
{
|
||||
OPLL_PATCH[][] res = new OPLL_PATCH[Emu2413API.OPLL_TONE_NUM][]
|
||||
{
|
||||
new OPLL_PATCH[(16 + 3) * 2],
|
||||
new OPLL_PATCH[(16 + 3) * 2],
|
||||
};
|
||||
|
||||
for (int x = 0; x < Emu2413API.OPLL_TONE_NUM; x++)
|
||||
for (int y = 0; y < (16 + 3) * 2; y++)
|
||||
res[x][y] = new OPLL_PATCH();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
internal static uint[,,,] Create_tllTable()
|
||||
{
|
||||
var res = new uint[16, 8, 1 << Emu2413API.TL_BITS, 4];
|
||||
return res;
|
||||
}
|
||||
|
||||
internal static Int32[,,] Create_rksTable()
|
||||
{
|
||||
return new int[2, 8, 2];
|
||||
}
|
||||
|
||||
internal static UInt32[,,] Create_dphaseTable()
|
||||
{
|
||||
return new uint[512, 8, 16];
|
||||
}
|
||||
}
|
||||
public class OPLL_PATCH
|
||||
{
|
||||
public uint TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WF;
|
||||
|
||||
public void Copy(OPLL_PATCH other)
|
||||
{
|
||||
TL = other.TL;
|
||||
FB = other.FB;
|
||||
EG = other.EG;
|
||||
ML = other.ML;
|
||||
AR = other.AR;
|
||||
DR = other.DR;
|
||||
SL = other.SL;
|
||||
RR = other.RR;
|
||||
KR = other.KR;
|
||||
KL = other.KL;
|
||||
AM = other.AM;
|
||||
PM = other.PM;
|
||||
WF = other.WF;
|
||||
}
|
||||
}
|
||||
|
||||
public class OPLL_SLOT
|
||||
{
|
||||
public OPLL_PATCH patch;
|
||||
|
||||
public int type; /* 0 : modulator 1 : carrier */
|
||||
|
||||
/* OUTPUT */
|
||||
public Int32 feedback;
|
||||
public Int32[] output = new Int32[5]; /* Output value of slot */
|
||||
|
||||
/* for Phase Generator (PG) */
|
||||
public UInt32[] sintbl; /* Wavetable */
|
||||
public UInt32 phase; /* Phase */
|
||||
public UInt32 dphase; /* Phase increment amount */
|
||||
public UInt32 pgout; /* output */
|
||||
|
||||
/* for Envelope Generator (EG) */
|
||||
public int fnum; /* F-Number */
|
||||
public int block; /* Block */
|
||||
public int volume; /* Current volume */
|
||||
public int sustine; /* Sustine 1 = ON, 0 = OFF */
|
||||
public UInt32 tll; /* Total Level + Key scale level*/
|
||||
public UInt32 rks; /* Key scale offset (Rks) */
|
||||
public int eg_mode; /* Current state */
|
||||
public UInt32 eg_phase; /* Phase */
|
||||
public UInt32 eg_dphase; /* Phase increment amount */
|
||||
public UInt32 egout; /* output */
|
||||
|
||||
|
||||
/* refer to opll-> */
|
||||
public int plfo_pm => m_host.lfo_pm;
|
||||
public int plfo_am => m_host.lfo_am;
|
||||
|
||||
private OPLL m_host;
|
||||
public void SetHost(OPLL host)
|
||||
{
|
||||
m_host = host;
|
||||
}
|
||||
}
|
||||
|
||||
public class OPLL_CH
|
||||
{
|
||||
public int patch_number;
|
||||
public int key_status;
|
||||
public OPLL_SLOT mod;
|
||||
public OPLL_SLOT car;
|
||||
}
|
||||
|
||||
public class OPLL
|
||||
{
|
||||
public UInt32 adr;
|
||||
public Int32[] output = new Int32[2];
|
||||
|
||||
/* Register */
|
||||
public byte[] reg = new byte[0x40];
|
||||
public int[] slot_on_flag = new int[18];
|
||||
|
||||
/* Rythm Mode : 0 = OFF, 1 = ON */
|
||||
public int rythm_mode;
|
||||
|
||||
/* Pitch Modulator */
|
||||
public UInt32 pm_phase;
|
||||
public Int32 lfo_pm;
|
||||
|
||||
/* Amp Modulator */
|
||||
public Int32 am_phase;
|
||||
public Int32 lfo_am;
|
||||
|
||||
/* Noise Generator */
|
||||
public UInt32 noise_seed;
|
||||
public UInt32 whitenoise;
|
||||
public UInt32 noiseA;
|
||||
public UInt32 noiseB;
|
||||
public UInt32 noiseA_phase;
|
||||
public UInt32 noiseB_phase;
|
||||
public UInt32 noiseA_idx;
|
||||
public UInt32 noiseB_idx;
|
||||
public UInt32 noiseA_dphase;
|
||||
public UInt32 noiseB_dphase;
|
||||
|
||||
/* Channel & Slot */
|
||||
public OPLL_CH[] ch = new OPLL_CH[9];
|
||||
public OPLL_SLOT[] slot = new OPLL_SLOT[18];
|
||||
|
||||
/* Voice Data */
|
||||
public OPLL_PATCH[] patch = new OPLL_PATCH[19 * 2];
|
||||
public int[] patch_update = new int[2]; /* flag for check patch update */
|
||||
|
||||
public UInt32 mask;
|
||||
|
||||
public int masterVolume; /* 0min -- 64 -- 127 max (Liner) */
|
||||
}
|
||||
}
|
12
Core/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs
Normal file
12
Core/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
// 昤夋曽幃
|
||||
public enum EnumRenderMethod
|
||||
{
|
||||
POST_ALL_RENDER = 0, // 僗僉儍儞儔僀儞暘偺柦椷幚峴屻丆儗儞僟儕儞僌
|
||||
PRE_ALL_RENDER = 1, // 儗儞僟儕儞僌偺幚峴屻丆僗僉儍儞儔僀儞暘偺柦椷幚峴
|
||||
POST_RENDER = 2, // 昞帵婜娫暘偺柦椷幚峴屻丆儗儞僟儕儞僌
|
||||
PRE_RENDER = 3, // 儗儞僟儕儞僌幚峴屻丆昞帵婜娫暘偺柦椷幚峴
|
||||
TILE_RENDER = 4 // 僞僀儖儀乕僗儗儞僟儕儞僌
|
||||
}
|
||||
}
|
7
Core/VirtualNes.Core/CoreLibs/ISoundDataBuffer.cs
Normal file
7
Core/VirtualNes.Core/CoreLibs/ISoundDataBuffer.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public interface ISoundDataBuffer
|
||||
{
|
||||
void WriteByte(byte value);
|
||||
}
|
||||
}
|
77
Core/VirtualNes.Core/CoreLibs/MemoryUtility.cs
Normal file
77
Core/VirtualNes.Core/CoreLibs/MemoryUtility.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public static class MemoryUtility
|
||||
{
|
||||
public static void ZEROMEMORY(byte[] array, int length)
|
||||
{
|
||||
Array.Clear(array, 0, array.Length);
|
||||
}
|
||||
public static void ZEROMEMORY(int[] array, int length)
|
||||
{
|
||||
Array.Clear(array, 0, array.Length);
|
||||
}
|
||||
|
||||
public static void memset(byte[] array, byte value, int length)
|
||||
{
|
||||
memset(array, 0, value, length);
|
||||
}
|
||||
|
||||
public unsafe static void memset(byte[] array, int offset, byte value, int length)
|
||||
{
|
||||
fixed (byte* ptr = array)
|
||||
{
|
||||
var offsetptr = ptr + offset;
|
||||
|
||||
Unsafe.InitBlockUnaligned(offsetptr, value, (uint)length);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe static void memset(uint[] array, int offset, byte value, int length)
|
||||
{
|
||||
fixed (uint* ptr = array)
|
||||
{
|
||||
var offsetptr = ptr + offset;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
offsetptr[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe static void memset(byte* ptr, int offset, byte value, int length)
|
||||
{
|
||||
var offsetptr = ptr + offset;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
offsetptr[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe static void memset(byte* ptr, byte value, int length)
|
||||
{
|
||||
memset(ptr, 0, value, length);
|
||||
}
|
||||
|
||||
public unsafe static void memset(uint* ptr, int offset, uint value, int length)
|
||||
{
|
||||
var offsetptr = ptr + offset;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
offsetptr[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe static void memset(uint* ptr, uint value, int length)
|
||||
{
|
||||
memset(ptr, 0, value, length);
|
||||
}
|
||||
|
||||
public static void memcpy(Array dst, Array src, int length)
|
||||
{
|
||||
Array.Copy(src, dst, length);
|
||||
}
|
||||
}
|
||||
}
|
51
Core/VirtualNes.Core/CoreLibs/NesConfig.cs
Normal file
51
Core/VirtualNes.Core/CoreLibs/NesConfig.cs
Normal file
@ -0,0 +1,51 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class NesConfig
|
||||
{
|
||||
public float BaseClock; // NTSC:21477270.0 PAL:21281364.0
|
||||
public float CpuClock; // NTSC: 1789772.5 PAL: 1773447.0
|
||||
|
||||
public int TotalScanlines; // NTSC: 262 PAL: 312
|
||||
|
||||
public int ScanlineCycles; // NTSC:1364 PAL:1362
|
||||
|
||||
public int HDrawCycles; // NTSC:1024 PAL:1024
|
||||
public int HBlankCycles; // NTSC: 340 PAL: 338
|
||||
public int ScanlineEndCycles; // NTSC: 4 PAL: 2
|
||||
|
||||
public int FrameCycles; // NTSC:29829.52 PAL:35468.94
|
||||
public int FrameIrqCycles; // NTSC:29829.52 PAL:35468.94
|
||||
|
||||
public int FrameRate; // NTSC:60(59.94) PAL:50
|
||||
public float FramePeriod; // NTSC:16.683 PAL:20.0
|
||||
|
||||
public static NesConfig NESCONFIG_NTSC = new NesConfig
|
||||
{
|
||||
BaseClock = 21477270.0f,
|
||||
CpuClock = 1789772.5f,
|
||||
TotalScanlines = 262,
|
||||
ScanlineCycles = 1364,
|
||||
HDrawCycles = 1024,
|
||||
HBlankCycles = 340,
|
||||
ScanlineEndCycles = 4,
|
||||
FrameCycles = 1364 * 262,
|
||||
FrameIrqCycles = 29830,
|
||||
FrameRate = 60,
|
||||
FramePeriod = 1000.0f / 60.0f
|
||||
};
|
||||
public static NesConfig NESCONFIG_PAL = new NesConfig
|
||||
{
|
||||
BaseClock = 26601714.0f,
|
||||
CpuClock = 1662607.125f,
|
||||
TotalScanlines = 312,
|
||||
ScanlineCycles = 1278,
|
||||
HDrawCycles = 960,
|
||||
HBlankCycles = 318,
|
||||
ScanlineEndCycles = 2,
|
||||
FrameCycles = 1278 * 312,
|
||||
FrameIrqCycles = 33252,
|
||||
FrameRate = 50,
|
||||
FramePeriod = 1000.0f / 50.0f
|
||||
};
|
||||
}
|
||||
}
|
140
Core/VirtualNes.Core/CoreLibs/ROMClasses.cs
Normal file
140
Core/VirtualNes.Core/CoreLibs/ROMClasses.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public enum EnumRomControlByte1 : byte
|
||||
{
|
||||
ROM_VMIRROR = 0x01,
|
||||
ROM_SAVERAM = 0x02,
|
||||
ROM_TRAINER = 0x04,
|
||||
ROM_4SCREEN = 0x08,
|
||||
}
|
||||
|
||||
public enum EnumRomControlByte2 : byte
|
||||
{
|
||||
ROM_VSUNISYSTEM = 0x01
|
||||
}
|
||||
|
||||
public enum EnumRomType
|
||||
{
|
||||
InValid,
|
||||
NES,
|
||||
/// <summary> Nintendo Disk System </summary>
|
||||
FDS,
|
||||
NSF
|
||||
}
|
||||
|
||||
public class NSFHEADER
|
||||
{
|
||||
byte[] ID;
|
||||
byte Version;
|
||||
byte TotalSong;
|
||||
byte StartSong;
|
||||
ushort LoadAddress;
|
||||
ushort InitAddress;
|
||||
ushort PlayAddress;
|
||||
byte[] SongName;
|
||||
byte[] ArtistName;
|
||||
byte[] CopyrightName;
|
||||
ushort SpeedNTSC;
|
||||
byte[] BankSwitch;
|
||||
ushort SpeedPAL;
|
||||
byte NTSC_PALbits;
|
||||
public byte ExtraChipSelect;
|
||||
byte[] Expansion; // must be 0
|
||||
|
||||
|
||||
public static int SizeOf()
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
|
||||
public static NSFHEADER GetDefault()
|
||||
{
|
||||
var res = new NSFHEADER();
|
||||
res.ID = new byte[5];
|
||||
res.SongName = new byte[32];
|
||||
res.ArtistName = new byte[32];
|
||||
res.CopyrightName = new byte[32];
|
||||
res.BankSwitch = new byte[8];
|
||||
res.Expansion = new byte[4];
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public class NESHEADER
|
||||
{
|
||||
public byte[] ID;
|
||||
public byte PRG_PAGE_SIZE;
|
||||
public byte CHR_PAGE_SIZE;
|
||||
public byte control1;
|
||||
public byte control2;
|
||||
public byte[] reserved;
|
||||
|
||||
public bool CheckValid()
|
||||
{
|
||||
return GetRomType() != EnumRomType.InValid;
|
||||
}
|
||||
|
||||
public static int SizeOf()
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
public EnumRomType GetRomType()
|
||||
{
|
||||
if (ID[0] == 'N' && ID[1] == 'E' && ID[2] == 'S' && ID[3] == 0x1A)
|
||||
return EnumRomType.NES;
|
||||
if (ID[0] == 'F' && ID[1] == 'D' && ID[2] == 'S' && ID[3] == 0x1A)
|
||||
return EnumRomType.FDS;
|
||||
if (ID[0] == 'N' && ID[1] == 'E' && ID[2] == 'S' && ID[3] == 'M')
|
||||
return EnumRomType.NSF;
|
||||
|
||||
return EnumRomType.InValid;
|
||||
}
|
||||
|
||||
public static NESHEADER GetDefault()
|
||||
{
|
||||
var res = new NESHEADER();
|
||||
res.ID = new byte[4];
|
||||
res.reserved = new byte[8];
|
||||
return res;
|
||||
}
|
||||
|
||||
public static NESHEADER Read(Span<byte> data)
|
||||
{
|
||||
var res = new NESHEADER();
|
||||
res.ID = data.Slice(0, 4).ToArray();
|
||||
res.PRG_PAGE_SIZE = data[4];
|
||||
res.CHR_PAGE_SIZE = data[5];
|
||||
res.control1 = data[6];
|
||||
res.control2 = data[7];
|
||||
res.reserved = data.Slice(8, 8).ToArray();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public byte[] DataToBytes()
|
||||
{
|
||||
byte[] res = new byte[16];
|
||||
res[0] = ID[0];
|
||||
res[1] = ID[1];
|
||||
res[2] = ID[2];
|
||||
res[3] = ID[3];
|
||||
res[4] = PRG_PAGE_SIZE;
|
||||
res[5] = CHR_PAGE_SIZE;
|
||||
res[6] = control1;
|
||||
res[7] = control2;
|
||||
res[8] = reserved[0];
|
||||
res[9] = reserved[1];
|
||||
res[10] = reserved[2];
|
||||
res[11] = reserved[3];
|
||||
res[12] = reserved[4];
|
||||
res[13] = reserved[5];
|
||||
res[14] = reserved[6];
|
||||
res[15] = reserved[7];
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
420
Core/VirtualNes.Core/CoreLibs/RomPatch.cs
Normal file
420
Core/VirtualNes.Core/CoreLibs/RomPatch.cs
Normal file
@ -0,0 +1,420 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public static class RomPatch
|
||||
{
|
||||
public static void DoPatch(ref uint crc, ref byte[] lpPRG, ref byte[] lpCHR, ref int mapper, ref NESHEADER header)
|
||||
{
|
||||
// Mapper 000
|
||||
if (crc == 0x57970078)
|
||||
{ // F-1 Race(J)
|
||||
lpPRG[0x078C] = 0x6C;
|
||||
lpPRG[0x3FE1] = 0xFF;
|
||||
lpPRG[0x3FE6] = 0x00;
|
||||
}
|
||||
if (crc == 0xaf2bbcbc // Mach Rider(JU)
|
||||
|| crc == 0x3acd4bf1 // Mach Rider(Alt)(JU) 無理矢理パッチ(^^;
|
||||
|| crc == 0x8bbe9bec)
|
||||
{
|
||||
lpPRG[0x090D] = 0x6E;
|
||||
lpPRG[0x7FDF] = 0xFF;
|
||||
lpPRG[0x7FE4] = 0x00;
|
||||
|
||||
header.control1 = (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
|
||||
if (crc == 0xe16bb5fe)
|
||||
{ // Zippy Race(J)
|
||||
header.control1 &= 0xf6;
|
||||
}
|
||||
if (crc == 0x85534474)
|
||||
{ // Lode Runner(J)
|
||||
lpPRG[0x29E9] = 0xEA; // セーブメニューを出すパッチ
|
||||
lpPRG[0x29EA] = 0xEA;
|
||||
lpPRG[0x29F8] = 0xEA;
|
||||
lpPRG[0x29F9] = 0xEA;
|
||||
}
|
||||
|
||||
// Mapper 001
|
||||
if (crc == 0x7831b2ff // America Daitouryou Senkyo(J)
|
||||
|| crc == 0x190a3e11 // Be-Bop-Highschool - Koukousei Gokuraku Densetsu(J)
|
||||
|| crc == 0x52449508 // Home Run Nighter - Pennant League!!(J)
|
||||
|| crc == 0x0973f714 // Jangou(J)
|
||||
|| crc == 0x7172f3d4 // Kabushiki Doujou(J)
|
||||
|| crc == 0xa5781280 // Kujaku Ou 2(J)
|
||||
|| crc == 0x8ce9c87b // Money Game, The(J)
|
||||
|| crc == 0xec47296d // Morita Kazuo no Shougi(J)
|
||||
|| crc == 0xcee5857b // Ninjara Hoi!(J)
|
||||
|| crc == 0xe63d9193 // Tanigawa Kouji no Shougi Shinan 3(J)
|
||||
|| crc == 0xd54f5da9 // Tsuppari Wars(J)
|
||||
|| crc == 0x1e0c7ea3)
|
||||
{ // AD&D Dragons of Flame(J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
if (crc == 0x1995ac4e)
|
||||
{ // Ferrari Grand Prix Challenge(J) 無理矢理パッチ(^^;
|
||||
lpPRG[0x1F7AD] = 0xFF;
|
||||
lpPRG[0x1F7BC] = 0x00;
|
||||
}
|
||||
|
||||
if (crc == 0x20d22251)
|
||||
{ // Top rider(J) 無理矢理パッチ(^^;
|
||||
lpPRG[0x1F17E] = 0xEA;
|
||||
lpPRG[0x1F17F] = 0xEA;
|
||||
}
|
||||
|
||||
if (crc == 0x11469ce3)
|
||||
{ // Viva! Las Vegas(J) 無理矢理パッチ(^^;
|
||||
lpCHR[0x0000] = 0x01;
|
||||
}
|
||||
|
||||
if (crc == 0x3fccdc7b)
|
||||
{ // Baseball Star - Mezase Sankanou!!(J) 無理矢理パッチ(^^;
|
||||
lpPRG[0x0F666] = 0x9D;
|
||||
}
|
||||
|
||||
if (crc == 0xdb564628)
|
||||
{ // Mario Open Golf(J)
|
||||
lpPRG[0x30195] = 0xC0;
|
||||
}
|
||||
|
||||
// Mapper 002
|
||||
if (crc == 0x63af202f)
|
||||
{ // JJ - Tobidase Daisakusen Part 2(J)
|
||||
header.control1 &= 0xf6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
|
||||
if (crc == 0x99a62e47)
|
||||
{ // Black Bass 2, The(J)
|
||||
header.control1 &= 0xf6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
|
||||
if (crc == 0x0eaa7515 // Rod Land(J)
|
||||
|| crc == 0x22ab9694)
|
||||
{ // Rod Land(E)
|
||||
header.control1 &= 0xf6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
|
||||
if (crc == 0x2061772a)
|
||||
{ // Tantei Jinguji Taburou Tokino Sugiyukumamani (J)
|
||||
header.control1 &= 0xf6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
|
||||
// Mapper 003
|
||||
if (crc == 0x29401686)
|
||||
{ // Minna no Taabou no Nakayoshi Dai Sakusen(J)
|
||||
// lpPRG[0x2B3E] = 0x60;
|
||||
}
|
||||
if (crc == 0x932a077a)
|
||||
{ // TwinBee(J)
|
||||
mapper = 87;
|
||||
}
|
||||
if (crc == 0x8218c637)
|
||||
{ // Space Hunter(J)
|
||||
// header.control1 &= 0xf6;
|
||||
// header.control1 |= ROM_4SCREEN;
|
||||
header.control1 = (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
if (crc == 0x2bb6a0f8 // Sherlock Holmes - Hakushaku Reijou Yuukai Jiken(J)
|
||||
|| crc == 0x28c11d24 // Sukeban Deka 3(J)
|
||||
|| crc == 0x02863604)
|
||||
{ // Sukeban Deka 3(J)(Alt)
|
||||
header.control1 &= 0xf6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
|
||||
// Mapper 004
|
||||
if (crc == 0x58581770)
|
||||
{ // Rasaaru Ishii no Childs Quest(J)
|
||||
header.control1 &= 0xf6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
if (crc == 0xf3feb3ab // Kunio Kun no Jidaigeki Dayo Zenin Shuugou! (J)
|
||||
|| crc == 0xa524ae9b // Otaku no Seiza - An Adventure in the Otaku Galaxy (J)
|
||||
|| crc == 0x46dc6e57 // SD Gundam - Gachapon Senshi 2 - Capsule Senki (J)
|
||||
|| crc == 0x66b2dec7 // SD Gundam - Gachapon Senshi 3 - Eiyuu Senki (J)
|
||||
|| crc == 0x92b07fd9 // SD Gundam - Gachapon Senshi 4 - New Type Story (J)
|
||||
|| crc == 0x8ee6463a // SD Gundam - Gachapon Senshi 5 - Battle of Universal Century (J)
|
||||
|| crc == 0xaf754426 // Ultraman Club 3 (J)
|
||||
|| crc == 0xfe4e5b11 // Ushio to Tora - Shinen no Daiyou (J)
|
||||
|| crc == 0x57c12c17)
|
||||
{ // Yamamura Misa Suspense - Kyouto Zaiteku Satsujin Jiken (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
if (crc == 0x42e03e4a)
|
||||
{ // RPG Jinsei Game (J)
|
||||
mapper = 118;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
if (crc == 0xfd0299c3)
|
||||
{ // METAL MAX(J)
|
||||
lpPRG[0x3D522] = 0xA9;
|
||||
lpPRG[0x3D523] = 0x19;
|
||||
}
|
||||
if (crc == 0x1d2e5018 // Rockman 3(J)
|
||||
|| crc == 0x6b999aaf)
|
||||
{ // Mega Man 3(U)
|
||||
// lpPRG[0x3C179] = 0xBA;//
|
||||
// lpPRG[0x3C9CC] = 0x9E;
|
||||
}
|
||||
|
||||
// Mapper 005
|
||||
if (crc == 0xe91548d8)
|
||||
{ // Shin 4 Nin Uchi Mahjong - Yakuman Tengoku (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
if (crc == 0x255b129c)
|
||||
{ // Gun Sight (J) / Gun Sight (J)[a1]
|
||||
lpPRG[0x02D0B] = 0x01;
|
||||
lpPRG[0x0BEC0] = 0x01;
|
||||
}
|
||||
|
||||
|
||||
// Mapper 010
|
||||
if (crc == 0xc9cce8f2)
|
||||
{ // Famicom Wars (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 016
|
||||
if (crc == 0x983d8175 // Datach - Battle Rush - Build Up Robot Tournament (J)
|
||||
|| crc == 0x894efdbc // Datach - Crayon Shin Chan - Ora to Poi Poi (J)
|
||||
|| crc == 0x19e81461 // Datach - Dragon Ball Z - Gekitou Tenkaichi Budou Kai (J)
|
||||
|| crc == 0xbe06853f // Datach - J League Super Top Players (J)
|
||||
|| crc == 0x0be0a328 // Datach - SD Gundam - Gundam Wars (J)
|
||||
|| crc == 0x5b457641 // Datach - Ultraman Club - Supokon Fight! (J)
|
||||
|| crc == 0xf51a7f46 // Datach - Yuu Yuu Hakusho - Bakutou Ankoku Bujutsu Kai (J)
|
||||
|| crc == 0x31cd9903 // Dragon Ball Z - Kyoushuu! Saiya Jin (J)
|
||||
|| crc == 0xe49fc53e // Dragon Ball Z 2 - Gekishin Freeza!! (J)
|
||||
|| crc == 0x09499f4d // Dragon Ball Z 3 - Ressen Jinzou Ningen (J)
|
||||
|| crc == 0x2e991109 // Dragon Ball Z Gaiden - Saiya Jin Zetsumetsu Keikaku (J)
|
||||
|| crc == 0x170250de)
|
||||
{ // Rokudenashi Blues(J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 019
|
||||
if (crc == 0x3296ff7a // Battle Fleet (J)
|
||||
|| crc == 0x429fd177 // Famista '90 (J)
|
||||
|| crc == 0xdd454208 // Hydlide 3 - Yami Kara no Houmonsha (J)
|
||||
|| crc == 0xb1b9e187 // Kaijuu Monogatari (J)
|
||||
|| crc == 0xaf15338f)
|
||||
{ // Mindseeker (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 026
|
||||
if (crc == 0x836cc1ab)
|
||||
{ // Mouryou Senki Madara (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 033
|
||||
if (crc == 0x547e6cc1)
|
||||
{ // Flintstones - The Rescue of Dino & Hoppy, The(J)
|
||||
mapper = 48;
|
||||
}
|
||||
|
||||
// Mapper 065
|
||||
if (crc == 0xfd3fc292)
|
||||
{ // Ai Sensei no Oshiete - Watashi no Hoshi (J)
|
||||
mapper = 32;
|
||||
}
|
||||
|
||||
// Mapper 068
|
||||
if (crc == 0xfde79681)
|
||||
{ // Maharaja (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 069
|
||||
if (crc == 0xfeac6916 // Honoo no Toukyuuji - Dodge Danpei 2(J)
|
||||
|| crc == 0x67898319)
|
||||
{ // Barcode World(J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 080
|
||||
if (crc == 0x95aaed34 // Mirai Shinwa Jarvas (J)
|
||||
|| crc == 0x17627d4b)
|
||||
{ // Taito Grand Prix - Eikou heno License (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 082
|
||||
if (crc == 0x4819a595)
|
||||
{ // Kyuukyoku Harikiri Stadium - Heisei Gannen Ban (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 086
|
||||
if (crc == 0xe63f7d0b)
|
||||
{ // Urusei Yatsura - Lum no Wedding Bell(J)
|
||||
mapper = 101;
|
||||
}
|
||||
|
||||
// Mapper 118
|
||||
if (crc == 0x3b0fb600)
|
||||
{ // Ys 3 - Wonderers From Ys (J)
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM;
|
||||
}
|
||||
|
||||
// Mapper 180
|
||||
if (crc == 0xc68363f6)
|
||||
{ // Crazy Climber(J)
|
||||
header.control1 &= 0xf6;
|
||||
}
|
||||
|
||||
// VS-Unisystem
|
||||
if (crc == 0x70901b25)
|
||||
{ // VS Slalom
|
||||
mapper = 99;
|
||||
}
|
||||
|
||||
if (crc == 0xd5d7eac4)
|
||||
{ // VS Dr. Mario
|
||||
mapper = 1;
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (crc == 0xffbef374 // VS Castlevania
|
||||
|| crc == 0x8c0c2df5)
|
||||
{ // VS Top Gun
|
||||
mapper = 2;
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (crc == 0xeb2dba63 // VS TKO Boxing
|
||||
|| crc == 0x98cfe016 // VS TKO Boxing (Alt)
|
||||
|| crc == 0x9818f656)
|
||||
{ // VS TKO Boxing (f1)
|
||||
mapper = 4;
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (crc == 0x135adf7c)
|
||||
{ // VS Atari RBI Baseball
|
||||
mapper = 4;
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (crc == 0xf9d3b0a3 // VS Super Xevious
|
||||
|| crc == 0x9924980a // VS Super Xevious (b1)
|
||||
|| crc == 0x66bb838f)
|
||||
{ // VS Super Xevious (b2)
|
||||
mapper = 4;
|
||||
header.control1 &= 0xF6;
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (crc == 0x17ae56be)
|
||||
{ // VS Freedom Force
|
||||
mapper = 4;
|
||||
header.control1 &= 0xF6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN;
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (crc == 0xe2c0a2be)
|
||||
{ // VS Platoon
|
||||
mapper = 68;
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (crc == 0xcbe85490 // VS Excitebike
|
||||
|| crc == 0x29155e0c // VS Excitebike (Alt)
|
||||
|| crc == 0xff5135a3)
|
||||
{ // VS Hogan's Alley
|
||||
header.control1 &= 0xF6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN;
|
||||
}
|
||||
|
||||
if (crc == 0x0b65a917)
|
||||
{ // VS Mach Rider(Endurance Course)
|
||||
lpPRG[0x7FDF] = 0xFF;
|
||||
lpPRG[0x7FE4] = 0x00;
|
||||
}
|
||||
|
||||
if (crc == 0x8a6a9848 // VS Mach Rider(Endurance Course)(Alt)
|
||||
|| crc == 0xae8063ef)
|
||||
{ // VS Mach Rider(Japan, Fighting Course)
|
||||
lpPRG[0x7FDD] = 0xFF;
|
||||
lpPRG[0x7FE2] = 0x00;
|
||||
}
|
||||
|
||||
if (crc == 0x16d3f469)
|
||||
{ // VS Ninja Jajamaru Kun (J)
|
||||
header.control1 &= 0xf6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR;
|
||||
}
|
||||
|
||||
if (crc == 0xc99ec059)
|
||||
{ // VS Raid on Bungeling Bay(J)
|
||||
mapper = 99;
|
||||
header.control1 &= 0xF6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN;
|
||||
}
|
||||
if (crc == 0xca85e56d)
|
||||
{ // VS Mighty Bomb Jack(J)
|
||||
mapper = 99;
|
||||
header.control1 &= 0xF6;
|
||||
header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN;
|
||||
}
|
||||
|
||||
|
||||
if (crc == 0xeb2dba63 // VS TKO Boxing
|
||||
|| crc == 0x9818f656 // VS TKO Boxing
|
||||
|| crc == 0xed588f00 // VS Duck Hunt
|
||||
|| crc == 0x8c0c2df5 // VS Top Gun
|
||||
|| crc == 0x16d3f469 // VS Ninja Jajamaru Kun
|
||||
|| crc == 0x8850924b // VS Tetris
|
||||
|| crc == 0xcf36261e // VS Sky Kid
|
||||
|| crc == 0xe1aa8214 // VS Star Luster
|
||||
|| crc == 0xec461db9 // VS Pinball
|
||||
|| crc == 0xe528f651 // VS Pinball (alt)
|
||||
|| crc == 0x17ae56be // VS Freedom Force
|
||||
|| crc == 0xe2c0a2be // VS Platoon
|
||||
|| crc == 0xff5135a3 // VS Hogan's Alley
|
||||
|| crc == 0x70901b25 // VS Slalom
|
||||
|| crc == 0x0b65a917 // VS Mach Rider(Endurance Course)
|
||||
|| crc == 0x8a6a9848 // VS Mach Rider(Endurance Course)(Alt)
|
||||
|| crc == 0xae8063ef // VS Mach Rider(Japan, Fighting Course)
|
||||
|| crc == 0xcc2c4b5d // VS Golf
|
||||
|| crc == 0xa93a5aee // VS Stroke and Match Golf
|
||||
|| crc == 0x86167220 // VS Lady Golf
|
||||
|| crc == 0xffbef374 // VS Castlevania
|
||||
|| crc == 0x135adf7c // VS Atari RBI Baseball
|
||||
|| crc == 0xd5d7eac4 // VS Dr. Mario
|
||||
|| crc == 0x46914e3e // VS Soccer
|
||||
|| crc == 0x70433f2c // VS Battle City
|
||||
|| crc == 0x8d15a6e6 // VS bad .nes
|
||||
|| crc == 0x1e438d52 // VS Goonies
|
||||
|| crc == 0xcbe85490 // VS Excitebike
|
||||
|| crc == 0x29155e0c // VS Excitebike (alt)
|
||||
|| crc == 0x07138c06 // VS Clu Clu Land
|
||||
|| crc == 0x43a357ef // VS Ice Climber
|
||||
|| crc == 0x737dd1bf // VS Super Mario Bros
|
||||
|| crc == 0x4bf3972d // VS Super Mario Bros
|
||||
|| crc == 0x8b60cc58 // VS Super Mario Bros
|
||||
|| crc == 0x8192c804 // VS Super Mario Bros
|
||||
|| crc == 0xd99a2087 // VS Gradius
|
||||
|| crc == 0xf9d3b0a3 // VS Super Xevious
|
||||
|| crc == 0x9924980a // VS Super Xevious
|
||||
|| crc == 0x66bb838f // VS Super Xevious
|
||||
|| crc == 0xc99ec059 // VS Raid on Bungeling Bay(J)
|
||||
|| crc == 0xca85e56d)
|
||||
{ // VS Mighty Bomb Jack(J)
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
|
||||
if (mapper == 99 || mapper == 151)
|
||||
{
|
||||
header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
Core/VirtualNes.Core/CoreLibs/State.cs
Normal file
54
Core/VirtualNes.Core/CoreLibs/State.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class DISKFILEHDR
|
||||
{
|
||||
public byte[] ID = new byte[12]; // "VirtuaNES DI"
|
||||
public ushort BlockVersion; // 0x0200:0.30 0x0210:0.31
|
||||
public ushort Reserved;
|
||||
public ulong ProgID; // 僾儘僌儔儉ID
|
||||
public ushort MakerID; // 儊乕僇乕ID
|
||||
public ushort DiskNo; // 僨傿僗僋悢
|
||||
public ulong DifferentSize; // 憡堘悢
|
||||
|
||||
|
||||
public byte[] ToBytes()
|
||||
{
|
||||
byte[] res = new byte[36];
|
||||
Array.Copy(ID, res, ID.Length);
|
||||
var temp = BitConverter.GetBytes(BlockVersion);
|
||||
res[12] = temp[0];
|
||||
res[13] = temp[1];
|
||||
temp = BitConverter.GetBytes(Reserved);
|
||||
res[14] = temp[0];
|
||||
res[15] = temp[1];
|
||||
temp = BitConverter.GetBytes(ProgID);
|
||||
res[16] = temp[0];
|
||||
res[17] = temp[1];
|
||||
res[18] = temp[2];
|
||||
res[19] = temp[3];
|
||||
res[20] = temp[4];
|
||||
res[21] = temp[5];
|
||||
res[22] = temp[6];
|
||||
res[23] = temp[7];
|
||||
temp = BitConverter.GetBytes(MakerID);
|
||||
res[24] = temp[0];
|
||||
res[25] = temp[1];
|
||||
temp = BitConverter.GetBytes(DiskNo);
|
||||
res[26] = temp[0];
|
||||
res[27] = temp[1];
|
||||
temp = BitConverter.GetBytes(ProgID);
|
||||
res[28] = temp[0];
|
||||
res[29] = temp[1];
|
||||
res[30] = temp[2];
|
||||
res[31] = temp[3];
|
||||
res[32] = temp[4];
|
||||
res[33] = temp[5];
|
||||
res[34] = temp[6];
|
||||
res[35] = temp[7];
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
26
Core/VirtualNes.Core/Debuger.cs
Normal file
26
Core/VirtualNes.Core/Debuger.cs
Normal file
@ -0,0 +1,26 @@
|
||||
namespace VirtualNes.Core.Debug
|
||||
{
|
||||
public static class Debuger
|
||||
{
|
||||
private static IDebugerImpl s_debuger;
|
||||
public static void Setup(IDebugerImpl debuger)
|
||||
{
|
||||
s_debuger = debuger;
|
||||
}
|
||||
public static void Log(string message)
|
||||
{
|
||||
s_debuger.Log(message);
|
||||
}
|
||||
|
||||
public static void LogError(string message)
|
||||
{
|
||||
s_debuger.LogError(message);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDebugerImpl
|
||||
{
|
||||
void Log(string message);
|
||||
void LogError(string message);
|
||||
}
|
||||
}
|
260
Core/VirtualNes.Core/MMU.cs
Normal file
260
Core/VirtualNes.Core/MMU.cs
Normal file
@ -0,0 +1,260 @@
|
||||
using VirtualNes.Core;
|
||||
|
||||
namespace VirtualNes
|
||||
{
|
||||
public static class MMU
|
||||
{
|
||||
// CPU 儊儌儕僶儞僋
|
||||
public static ArrayRef<byte>[] CPU_MEM_BANK = new ArrayRef<byte>[8]; // 8K扨埵
|
||||
public static byte[] CPU_MEM_TYPE = new byte[8];
|
||||
public static int[] CPU_MEM_PAGE = new int[8]; // 僗僥乕僩僙乕僽梡
|
||||
// PPU 儊儌儕僶儞僋
|
||||
public static ArrayRef<byte>[] PPU_MEM_BANK = new ArrayRef<byte>[12]; // 1K扨埵
|
||||
public static byte[] PPU_MEM_TYPE = new byte[12];
|
||||
public static int[] PPU_MEM_PAGE = new int[12]; // 僗僥乕僩僙乕僽梡
|
||||
public static byte[] CRAM_USED = new byte[16]; // 僗僥乕僩僙乕僽梡
|
||||
|
||||
// NES儊儌儕
|
||||
public static byte[] RAM = new byte[8 * 1024]; // NES撪憻RAM
|
||||
public static byte[] WRAM = new byte[128 * 1024]; // 儚乕僋/僶僢僋傾僢僾RAM
|
||||
public static byte[] DRAM = new byte[40 * 1024]; // 僨傿僗僋僔僗僥儉RAM
|
||||
public static byte[] XRAM = new byte[8 * 1024]; // 僟儈乕僶儞僋
|
||||
public static byte[] ERAM = new byte[32 * 1024]; // 奼挘婡婍梡RAM
|
||||
|
||||
public static byte[] CRAM = new byte[32 * 1024]; // 僉儍儔僋僞僷僞乕儞RAM
|
||||
public static byte[] VRAM = new byte[4 * 1024]; // 僱乕儉僥乕僽儖/傾僩儕價儏乕僩RAM
|
||||
|
||||
public static byte[] SPRAM = new byte[0x100]; // 僗僾儔僀僩RAM
|
||||
public static byte[] BGPAL = new byte[0x10]; // BG僷儗僢僩
|
||||
public static byte[] SPPAL = new byte[0x10]; // SP僷儗僢僩
|
||||
// 儗僕僗僞
|
||||
public static byte[] CPUREG = new byte[0x18]; // Nes $4000-$4017
|
||||
public static byte[] PPUREG = new byte[0x04]; // Nes $2000-$2003
|
||||
|
||||
// PPU撪晹儗僕僗僞
|
||||
public static byte PPU56Toggle; // $2005-$2006 Toggle
|
||||
public static byte PPU7_Temp; // $2007 read buffer
|
||||
public static ushort loopy_t; // same as $2005/$2006
|
||||
public static ushort loopy_v; // same as $2005/$2006
|
||||
public static ushort loopy_x; // tile x offset
|
||||
|
||||
// ROM僨乕僞億僀儞僞
|
||||
public static byte[] PROM; // PROM ptr
|
||||
public static byte[] VROM; // VROM ptr
|
||||
|
||||
// For dis...
|
||||
public static byte PROM_ACCESS;
|
||||
|
||||
// ROM 僶儞僋僒僀僘
|
||||
public static int PROM_8K_SIZE, PROM_16K_SIZE, PROM_32K_SIZE;
|
||||
public static int VROM_1K_SIZE, VROM_2K_SIZE, VROM_4K_SIZE, VROM_8K_SIZE;
|
||||
|
||||
// 儊儌儕僞僀僾
|
||||
// For PROM (CPU)
|
||||
public const byte BANKTYPE_ROM = 0x00;
|
||||
public const byte BANKTYPE_RAM = 0xFF;
|
||||
public const byte BANKTYPE_DRAM = 0x01;
|
||||
public const byte BANKTYPE_MAPPER = 0x80;
|
||||
// For VROM/VRAM=/CRAM (PPU)
|
||||
public const byte BANKTYPE_VROM = 0x00;
|
||||
public const byte BANKTYPE_CRAM = 0x01;
|
||||
public const byte BANKTYPE_VRAM = 0x80;
|
||||
|
||||
// =ミラータイプ;
|
||||
public const byte VRAM_HMIRROR = 0x00; // Horizontal
|
||||
public const byte VRAM_VMIRROR = 0x01; // Virtical
|
||||
public const byte VRAM_MIRROR4 = 0x02; // All screen
|
||||
public const byte VRAM_MIRROR4L = 0x03; // PA10 L固定 $2000-$23FFのミラー
|
||||
public const byte VRAM_MIRROR4H = 0x04; // PA10 H固定 $2400-$27FFのミラー
|
||||
|
||||
// Frame-IRQ儗僕僗僞($4017)
|
||||
public static int FrameIRQ;
|
||||
|
||||
internal static void SetPROM_Bank(byte page, byte[] ptr, byte type)
|
||||
{
|
||||
CPU_MEM_BANK[page] = new ArrayRef<byte>(ptr, 0, ptr.Length);
|
||||
CPU_MEM_TYPE[page] = type;
|
||||
CPU_MEM_PAGE[page] = 0;
|
||||
}
|
||||
|
||||
internal static void SetPROM_Bank(byte page, ArrayRef<byte> ptr, byte type)
|
||||
{
|
||||
CPU_MEM_BANK[page] = ptr;
|
||||
CPU_MEM_TYPE[page] = type;
|
||||
CPU_MEM_PAGE[page] = 0;
|
||||
}
|
||||
|
||||
internal static void SetPROM_4K_Bank(ushort addr, int bank)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
|
||||
bank %= (PROM_8K_SIZE * 2);
|
||||
|
||||
//TODO
|
||||
//memcpy(&CPU_MEM_BANK[addr >> 13][addr & 0x1FFF], PROM + 0x1000 * bank, 0x1000);
|
||||
//// memcpy( &CPU_MEM_BANK[addr>>13][addr&0x1FFF], YSRAM+0x1000*bank, 0x1000);
|
||||
CPU_MEM_TYPE[addr >> 13] = BANKTYPE_ROM;
|
||||
CPU_MEM_PAGE[addr >> 13] = 0;
|
||||
}
|
||||
|
||||
|
||||
internal static void SetPROM_8K_Bank(byte page, int bank)
|
||||
{
|
||||
bank %= PROM_8K_SIZE;
|
||||
CPU_MEM_BANK[page] = new ArrayRef<byte>(MMU.PROM, 0x2000 * bank, MMU.PROM.Length - 0x2000 * bank);
|
||||
CPU_MEM_TYPE[page] = BANKTYPE_ROM;
|
||||
CPU_MEM_PAGE[page] = bank;
|
||||
}
|
||||
|
||||
internal static void SetPROM_16K_Bank(byte page, int bank)
|
||||
{
|
||||
SetPROM_8K_Bank((byte)(page + 0), bank * 2 + 0);
|
||||
SetPROM_8K_Bank((byte)(page + 1), bank * 2 + 1);
|
||||
}
|
||||
|
||||
internal static void SetPROM_32K_Bank(int bank)
|
||||
{
|
||||
SetPROM_8K_Bank(4, bank * 4 + 0);
|
||||
SetPROM_8K_Bank(5, bank * 4 + 1);
|
||||
SetPROM_8K_Bank(6, bank * 4 + 2);
|
||||
SetPROM_8K_Bank(7, bank * 4 + 3);
|
||||
}
|
||||
|
||||
internal static void SetPROM_32K_Bank(int bank0, int bank1, int bank2, int bank3)
|
||||
{
|
||||
SetPROM_8K_Bank(4, bank0);
|
||||
SetPROM_8K_Bank(5, bank1);
|
||||
SetPROM_8K_Bank(6, bank2);
|
||||
SetPROM_8K_Bank(7, bank3);
|
||||
}
|
||||
|
||||
// PPU VROM bank
|
||||
internal static void SetVROM_Bank(byte page, ArrayRef<byte> ptr, byte type)
|
||||
{
|
||||
PPU_MEM_BANK[page] = ptr;
|
||||
PPU_MEM_TYPE[page] = type;
|
||||
PPU_MEM_PAGE[page] = 0;
|
||||
}
|
||||
|
||||
internal static void SetVROM_1K_Bank(byte page, int bank)
|
||||
{
|
||||
bank %= VROM_1K_SIZE;
|
||||
PPU_MEM_BANK[page] = new ArrayRef<byte>(VROM, 0x0400 * bank, VROM.Length - (0x0400 * bank));
|
||||
PPU_MEM_TYPE[page] = BANKTYPE_VROM;
|
||||
PPU_MEM_PAGE[page] = bank;
|
||||
}
|
||||
|
||||
internal static void SetVROM_2K_Bank(byte page, int bank)
|
||||
{
|
||||
SetVROM_1K_Bank((byte)(page + 0), bank * 2 + 0);
|
||||
SetVROM_1K_Bank((byte)(page + 1), bank * 2 + 1);
|
||||
}
|
||||
|
||||
internal static void SetVROM_4K_Bank(byte page, int bank)
|
||||
{
|
||||
SetVROM_1K_Bank((byte)(page + 0), bank * 4 + 0);
|
||||
SetVROM_1K_Bank((byte)(page + 1), bank * 4 + 1);
|
||||
SetVROM_1K_Bank((byte)(page + 2), bank * 4 + 2);
|
||||
SetVROM_1K_Bank((byte)(page + 3), bank * 4 + 3);
|
||||
}
|
||||
|
||||
internal static void SetVROM_8K_Bank(int bank)
|
||||
{
|
||||
for (byte i = 0; i < 8; i++)
|
||||
{
|
||||
SetVROM_1K_Bank(i, bank * 8 + i);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetVROM_8K_Bank(int bank0, int bank1, int bank2, int bank3,
|
||||
int bank4, int bank5, int bank6, int bank7)
|
||||
{
|
||||
SetVROM_1K_Bank(0, bank0);
|
||||
SetVROM_1K_Bank(1, bank1);
|
||||
SetVROM_1K_Bank(2, bank2);
|
||||
SetVROM_1K_Bank(3, bank3);
|
||||
SetVROM_1K_Bank(4, bank4);
|
||||
SetVROM_1K_Bank(5, bank5);
|
||||
SetVROM_1K_Bank(6, bank6);
|
||||
SetVROM_1K_Bank(7, bank7);
|
||||
}
|
||||
|
||||
internal static void SetCRAM_1K_Bank(byte page, int bank)
|
||||
{
|
||||
bank &= 0x1F;
|
||||
PPU_MEM_BANK[page] = new ArrayRef<byte>(MMU.CRAM, 0x0400 * bank, MMU.CRAM.Length - 0x0400 * bank);
|
||||
PPU_MEM_TYPE[page] = BANKTYPE_CRAM;
|
||||
PPU_MEM_PAGE[page] = bank;
|
||||
|
||||
CRAM_USED[bank >> 2] = 0xFF; // CRAM巊梡僼儔僌
|
||||
}
|
||||
|
||||
internal static void SetCRAM_2K_Bank(byte page, int bank)
|
||||
{
|
||||
SetCRAM_1K_Bank((byte)(page + 0), bank * 2 + 0);
|
||||
SetCRAM_1K_Bank((byte)(page + 1), bank * 2 + 1);
|
||||
}
|
||||
|
||||
internal static void SetCRAM_4K_Bank(byte page, int bank)
|
||||
{
|
||||
SetCRAM_1K_Bank((byte)(page + 0), bank * 4 + 0);
|
||||
SetCRAM_1K_Bank((byte)(page + 1), bank * 4 + 1);
|
||||
SetCRAM_1K_Bank((byte)(page + 2), bank * 4 + 2);
|
||||
SetCRAM_1K_Bank((byte)(page + 3), bank * 4 + 3);
|
||||
}
|
||||
|
||||
internal static void SetCRAM_8K_Bank(int bank)
|
||||
{
|
||||
for (byte i = 0; i < 8; i++)
|
||||
{
|
||||
SetCRAM_1K_Bank(i, bank * 8 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetVRAM_1K_Bank(byte page, int bank)
|
||||
{
|
||||
bank &= 3;
|
||||
PPU_MEM_BANK[page] = new ArrayRef<byte>(VRAM, 0x0400 * bank, VRAM.Length - 0x0400 * bank);
|
||||
PPU_MEM_TYPE[page] = BANKTYPE_VRAM;
|
||||
PPU_MEM_PAGE[page] = bank;
|
||||
}
|
||||
|
||||
internal static void SetVRAM_Bank(int bank0, int bank1, int bank2, int bank3)
|
||||
{
|
||||
SetVRAM_1K_Bank(8, bank0);
|
||||
SetVRAM_1K_Bank(9, bank1);
|
||||
SetVRAM_1K_Bank(10, bank2);
|
||||
SetVRAM_1K_Bank(11, bank3);
|
||||
}
|
||||
|
||||
internal static void SetVRAM_Mirror(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VRAM_HMIRROR:
|
||||
SetVRAM_Bank(0, 0, 1, 1);
|
||||
break;
|
||||
case VRAM_VMIRROR:
|
||||
SetVRAM_Bank(0, 1, 0, 1);
|
||||
break;
|
||||
case VRAM_MIRROR4L:
|
||||
SetVRAM_Bank(0, 0, 0, 0);
|
||||
break;
|
||||
case VRAM_MIRROR4H:
|
||||
SetVRAM_Bank(1, 1, 1, 1);
|
||||
break;
|
||||
case VRAM_MIRROR4:
|
||||
SetVRAM_Bank(0, 1, 2, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetVRAM_Mirror(int bank0, int bank1, int bank2, int bank3)
|
||||
{
|
||||
SetVRAM_1K_Bank(8, bank0);
|
||||
SetVRAM_1K_Bank(9, bank1);
|
||||
SetVRAM_1K_Bank(10, bank2);
|
||||
SetVRAM_1K_Bank(11, bank3);
|
||||
}
|
||||
}
|
||||
}
|
457
Core/VirtualNes.Core/Mapper/EEPROM.cs
Normal file
457
Core/VirtualNes.Core/Mapper/EEPROM.cs
Normal file
@ -0,0 +1,457 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
|
||||
|
||||
public class X24C01
|
||||
{
|
||||
public const int X24C01_IDLE = 0; // Idle
|
||||
public const int X24C01_ADDRESS = 1; // Address set
|
||||
public const int X24C01_READ = 2; // Read
|
||||
public const int X24C01_WRITE = 3; // Write
|
||||
public const int X24C01_ACK = 4; // Acknowledge
|
||||
public const int X24C01_ACK_WAIT = 5; // Acknowledge wait
|
||||
|
||||
int now_state, next_state;
|
||||
int bitcnt;
|
||||
byte addr, data;
|
||||
byte sda;
|
||||
byte scl_old, sda_old;
|
||||
|
||||
ArrayRef<byte> pEEPDATA;
|
||||
|
||||
public X24C01()
|
||||
{
|
||||
now_state = X24C01_IDLE;
|
||||
next_state = X24C01_IDLE;
|
||||
addr = 0;
|
||||
data = 0;
|
||||
sda = 0xFF;
|
||||
scl_old = 0;
|
||||
sda_old = 0;
|
||||
|
||||
pEEPDATA = null;
|
||||
}
|
||||
|
||||
public void Reset(ArrayRef<byte> ptr)
|
||||
{
|
||||
now_state = X24C01_IDLE;
|
||||
next_state = X24C01_IDLE;
|
||||
addr = 0;
|
||||
data = 0;
|
||||
sda = 0xFF;
|
||||
scl_old = 0;
|
||||
sda_old = 0;
|
||||
|
||||
pEEPDATA = ptr;
|
||||
}
|
||||
|
||||
public void Write(byte scl_in, byte sda_in)
|
||||
{
|
||||
// Clock line
|
||||
byte scl_rise = (byte)(~scl_old & scl_in);
|
||||
byte scl_fall = (byte)(scl_old & ~scl_in);
|
||||
// Data line
|
||||
byte sda_rise = (byte)(~sda_old & sda_in);
|
||||
byte sda_fall = (byte)(sda_old & ~sda_in);
|
||||
|
||||
byte scl_old_temp = scl_old;
|
||||
byte sda_old_temp = sda_old;
|
||||
|
||||
scl_old = scl_in;
|
||||
sda_old = sda_in;
|
||||
|
||||
// Start condition?
|
||||
if (scl_old_temp != 0 && sda_fall != 0)
|
||||
{
|
||||
now_state = X24C01_ADDRESS;
|
||||
bitcnt = 0;
|
||||
addr = 0;
|
||||
sda = 0xFF;
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop condition?
|
||||
if (scl_old_temp != 0 && sda_rise != 0)
|
||||
{
|
||||
now_state = X24C01_IDLE;
|
||||
sda = 0xFF;
|
||||
return;
|
||||
}
|
||||
|
||||
// SCL ____---- RISE
|
||||
if (scl_rise != 0)
|
||||
{
|
||||
switch (now_state)
|
||||
{
|
||||
case X24C01_ADDRESS:
|
||||
if (bitcnt < 7)
|
||||
{
|
||||
// 本来はMSB->LSB
|
||||
addr = (byte)(addr & (~(1 << bitcnt)));
|
||||
addr = (byte)(addr | ((sda_in != 0 ? 1 : 0) << bitcnt));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sda_in != 0)
|
||||
{
|
||||
next_state = X24C01_READ;
|
||||
data = pEEPDATA[addr & 0x7F];
|
||||
}
|
||||
else
|
||||
{
|
||||
next_state = X24C01_WRITE;
|
||||
}
|
||||
}
|
||||
bitcnt++;
|
||||
break;
|
||||
case X24C01_ACK:
|
||||
sda = 0; // ACK
|
||||
break;
|
||||
case X24C01_READ:
|
||||
if (bitcnt < 8)
|
||||
{
|
||||
// 本来はMSB->LSB
|
||||
sda = (byte)((data & (1 << bitcnt)) != 0 ? 1 : 0);
|
||||
}
|
||||
bitcnt++;
|
||||
break;
|
||||
case X24C01_WRITE:
|
||||
if (bitcnt < 8)
|
||||
{
|
||||
// 本来はMSB->LSB
|
||||
data = (byte)(data & (~(1 << bitcnt)));
|
||||
data = (byte)(data | ((sda_in != 0 ? 1 : 0) << bitcnt));
|
||||
}
|
||||
bitcnt++;
|
||||
break;
|
||||
|
||||
case X24C01_ACK_WAIT:
|
||||
if (sda_in == 0)
|
||||
{
|
||||
next_state = X24C01_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SCL ----____ FALL
|
||||
if (scl_fall != 0)
|
||||
{
|
||||
switch (now_state)
|
||||
{
|
||||
case X24C01_ADDRESS:
|
||||
if (bitcnt >= 8)
|
||||
{
|
||||
now_state = X24C01_ACK;
|
||||
sda = 0xFF;
|
||||
}
|
||||
break;
|
||||
case X24C01_ACK:
|
||||
now_state = next_state;
|
||||
bitcnt = 0;
|
||||
sda = 0xFF;
|
||||
break;
|
||||
case X24C01_READ:
|
||||
if (bitcnt >= 8)
|
||||
{
|
||||
now_state = X24C01_ACK_WAIT;
|
||||
addr = (byte)((addr + 1) & 0x7F);
|
||||
}
|
||||
break;
|
||||
case X24C01_WRITE:
|
||||
if (bitcnt >= 8)
|
||||
{
|
||||
now_state = X24C01_ACK;
|
||||
next_state = X24C01_IDLE;
|
||||
pEEPDATA[addr & 0x7F] = data;
|
||||
addr = (byte)((addr + 1) & 0x7F);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte Read()
|
||||
{
|
||||
return sda;
|
||||
}
|
||||
|
||||
public unsafe void Load(byte* p)
|
||||
{
|
||||
//now_state = *((INT*)&p[0]);
|
||||
now_state = *(int*)p;
|
||||
//next_state = *((INT*)&p[4]);
|
||||
next_state = *(int*)p[4];
|
||||
//bitcnt = *((INT*)&p[8]);
|
||||
bitcnt = *(int*)p[8];
|
||||
addr = p[12];
|
||||
data = p[13];
|
||||
sda = p[14];
|
||||
scl_old = p[15];
|
||||
sda_old = p[16];
|
||||
}
|
||||
|
||||
public unsafe void Save(byte* p)
|
||||
{
|
||||
*((int*)&p[0]) = now_state;
|
||||
*((int*)&p[4]) = next_state;
|
||||
*((int*)&p[8]) = bitcnt;
|
||||
p[12] = addr;
|
||||
p[13] = data;
|
||||
p[14] = sda;
|
||||
p[15] = scl_old;
|
||||
p[16] = sda_old;
|
||||
}
|
||||
}
|
||||
|
||||
public class X24C02
|
||||
{
|
||||
public const int X24C02_IDLE = 0; // Idle
|
||||
public const int X24C02_DEVADDR = 1; // Device address set
|
||||
public const int X24C02_ADDRESS = 2; // Address set
|
||||
public const int X24C02_READ = 3; // Read
|
||||
public const int X24C02_WRITE = 4; // Write
|
||||
public const int X24C02_ACK = 5; // Acknowledge
|
||||
public const int X24C02_NAK = 6; // Not Acknowledge
|
||||
public const int X24C02_ACK_WAIT = 7; // Acknowledge wait
|
||||
|
||||
int now_state, next_state;
|
||||
int bitcnt;
|
||||
byte addr, data, rw;
|
||||
byte sda;
|
||||
byte scl_old, sda_old;
|
||||
|
||||
ArrayRef<byte> pEEPDATA;
|
||||
|
||||
public X24C02()
|
||||
{
|
||||
now_state = X24C02_IDLE;
|
||||
next_state = X24C02_IDLE;
|
||||
addr = 0;
|
||||
data = 0;
|
||||
rw = 0;
|
||||
sda = 0xFF;
|
||||
scl_old = 0;
|
||||
sda_old = 0;
|
||||
|
||||
pEEPDATA = null;
|
||||
}
|
||||
|
||||
public void Reset(ArrayRef<byte> ptr)
|
||||
{
|
||||
now_state = X24C02_IDLE;
|
||||
next_state = X24C02_IDLE;
|
||||
addr = 0;
|
||||
data = 0;
|
||||
rw = 0;
|
||||
sda = 0xFF;
|
||||
scl_old = 0;
|
||||
sda_old = 0;
|
||||
|
||||
pEEPDATA = ptr;
|
||||
}
|
||||
|
||||
public void Write(byte scl_in, byte sda_in)
|
||||
{
|
||||
// Clock line
|
||||
byte scl_rise = (byte)(~scl_old & scl_in);
|
||||
byte scl_fall = (byte)(scl_old & ~scl_in);
|
||||
// Data line
|
||||
byte sda_rise = (byte)(~sda_old & sda_in);
|
||||
byte sda_fall = (byte)(sda_old & ~sda_in);
|
||||
|
||||
byte scl_old_temp = scl_old;
|
||||
byte sda_old_temp = sda_old;
|
||||
|
||||
scl_old = scl_in;
|
||||
sda_old = sda_in;
|
||||
|
||||
// Start condition?
|
||||
if (scl_old_temp != 0 && sda_fall != 0)
|
||||
{
|
||||
now_state = X24C02_DEVADDR;
|
||||
bitcnt = 0;
|
||||
sda = 0xFF;
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop condition?
|
||||
if (scl_old_temp != 0 && sda_rise != 0)
|
||||
{
|
||||
now_state = X24C02_IDLE;
|
||||
sda = 0xFF;
|
||||
return;
|
||||
}
|
||||
|
||||
// SCL ____---- RISE
|
||||
if (scl_rise != 0)
|
||||
{
|
||||
switch (now_state)
|
||||
{
|
||||
case X24C02_DEVADDR:
|
||||
if (bitcnt < 8)
|
||||
{
|
||||
data = (byte)(data & (~(1 << (7 - bitcnt))));
|
||||
data = (byte)(data | ((sda_in != 0 ? 1 : 0) << (7 - bitcnt)));
|
||||
}
|
||||
bitcnt++;
|
||||
break;
|
||||
case X24C02_ADDRESS:
|
||||
if (bitcnt < 8)
|
||||
{
|
||||
addr = (byte)(addr & (~(1 << (7 - bitcnt))));
|
||||
addr = (byte)(addr | ((sda_in != 0 ? 1 : 0) << (7 - bitcnt)));
|
||||
}
|
||||
bitcnt++;
|
||||
break;
|
||||
case X24C02_READ:
|
||||
if (bitcnt < 8)
|
||||
{
|
||||
sda = (byte)((data & (1 << (7 - bitcnt))) != 0 ? 1 : 0);
|
||||
}
|
||||
bitcnt++;
|
||||
break;
|
||||
case X24C02_WRITE:
|
||||
if (bitcnt < 8)
|
||||
{
|
||||
data = (byte)(data & (~(1 << (7 - bitcnt))));
|
||||
data = (byte)(data | ((sda_in != 0 ? 1 : 0) << (7 - bitcnt)));
|
||||
}
|
||||
bitcnt++;
|
||||
break;
|
||||
case X24C02_NAK:
|
||||
sda = 0xFF; // NAK
|
||||
break;
|
||||
case X24C02_ACK:
|
||||
sda = 0; // ACK
|
||||
break;
|
||||
case X24C02_ACK_WAIT:
|
||||
if (sda_in == 0)
|
||||
{
|
||||
next_state = X24C02_READ;
|
||||
data = pEEPDATA[addr];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SCL ----____ FALL
|
||||
if (scl_fall != 0)
|
||||
{
|
||||
switch (now_state)
|
||||
{
|
||||
case X24C02_DEVADDR:
|
||||
if (bitcnt >= 8)
|
||||
{
|
||||
if ((data & 0xA0) == 0xA0)
|
||||
{
|
||||
now_state = X24C02_ACK;
|
||||
rw = (byte)(data & 0x01);
|
||||
sda = 0xFF;
|
||||
if (rw != 0)
|
||||
{
|
||||
// Now address read
|
||||
next_state = X24C02_READ;
|
||||
data = pEEPDATA[addr];
|
||||
}
|
||||
else
|
||||
{
|
||||
next_state = X24C02_ADDRESS;
|
||||
}
|
||||
bitcnt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
now_state = X24C02_NAK;
|
||||
next_state = X24C02_IDLE;
|
||||
sda = 0xFF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case X24C02_ADDRESS:
|
||||
if (bitcnt >= 8)
|
||||
{
|
||||
now_state = X24C02_ACK;
|
||||
sda = 0xFF;
|
||||
if (rw != 0)
|
||||
{
|
||||
// Readでは絶対来ないが念の為
|
||||
next_state = X24C02_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// to Data Write
|
||||
next_state = X24C02_WRITE;
|
||||
}
|
||||
bitcnt = 0;
|
||||
}
|
||||
break;
|
||||
case X24C02_READ:
|
||||
if (bitcnt >= 8)
|
||||
{
|
||||
now_state = X24C02_ACK_WAIT;
|
||||
addr = (byte)((addr + 1) & 0xFF);
|
||||
}
|
||||
break;
|
||||
case X24C02_WRITE:
|
||||
if (bitcnt >= 8)
|
||||
{
|
||||
pEEPDATA[addr] = data;
|
||||
now_state = X24C02_ACK;
|
||||
next_state = X24C02_WRITE;
|
||||
addr = (byte)((addr + 1) & 0xFF);
|
||||
bitcnt = 0;
|
||||
}
|
||||
break;
|
||||
case X24C02_NAK:
|
||||
now_state = X24C02_IDLE;
|
||||
bitcnt = 0;
|
||||
sda = 0xFF;
|
||||
break;
|
||||
case X24C02_ACK:
|
||||
now_state = next_state;
|
||||
bitcnt = 0;
|
||||
sda = 0xFF;
|
||||
break;
|
||||
case X24C02_ACK_WAIT:
|
||||
now_state = next_state;
|
||||
bitcnt = 0;
|
||||
sda = 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte Read()
|
||||
{
|
||||
return sda;
|
||||
}
|
||||
|
||||
public unsafe void Load(byte* p)
|
||||
{
|
||||
//now_state = *((INT*)&p[0]);
|
||||
next_state = *((int*)&p[4]);
|
||||
//bitcnt = *((INT*)&p[8]);
|
||||
bitcnt = *((int*)&p[8]);
|
||||
addr = p[12];
|
||||
data = p[13];
|
||||
rw = p[14];
|
||||
sda = p[15];
|
||||
scl_old = p[16];
|
||||
sda_old = p[17];
|
||||
}
|
||||
|
||||
public unsafe void Save(byte* p)
|
||||
{
|
||||
*((int*)&p[0]) = now_state;
|
||||
*((int*)&p[4]) = next_state;
|
||||
*((int*)&p[8]) = bitcnt;
|
||||
p[12] = addr;
|
||||
p[13] = data;
|
||||
p[14] = rw;
|
||||
p[15] = sda;
|
||||
p[16] = scl_old;
|
||||
p[17] = sda_old;
|
||||
}
|
||||
}
|
||||
}
|
247
Core/VirtualNes.Core/Mapper/Mapper.cs
Normal file
247
Core/VirtualNes.Core/Mapper/Mapper.cs
Normal file
@ -0,0 +1,247 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public abstract class Mapper
|
||||
{
|
||||
protected NES nes;
|
||||
|
||||
public Mapper(NES parent)
|
||||
{
|
||||
nes = parent;
|
||||
}
|
||||
|
||||
public virtual void Dispose() { }
|
||||
|
||||
public abstract void Reset();
|
||||
|
||||
// $8000-$FFFF Memory write
|
||||
public virtual void Write(ushort addr, byte data) { }
|
||||
// $8000-$FFFF Memory read(Dummy)
|
||||
public virtual void Read(ushort addr, byte data) { }
|
||||
|
||||
// $4100-$7FFF Lower Memory read/write
|
||||
public virtual byte ReadLow(ushort addr)
|
||||
{
|
||||
// $6000-$7FFF WRAM
|
||||
if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||
{
|
||||
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
||||
}
|
||||
|
||||
return (byte)(addr >> 8);
|
||||
}
|
||||
public virtual void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||
{
|
||||
MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data;
|
||||
}
|
||||
}
|
||||
|
||||
// $4018-$40FF Extention register read/write
|
||||
public virtual byte ExRead(ushort addr) { return 0x00; }
|
||||
public virtual void ExWrite(ushort addr, byte data) { }
|
||||
|
||||
public virtual byte ExCmdRead(EXCMDRD cmd) { return 0x00; }
|
||||
public virtual void ExCmdWrite(EXCMDWR cmd, byte data) { }
|
||||
|
||||
// H sync/V sync/Clock sync
|
||||
public virtual void HSync(int scanline) { }
|
||||
public virtual void VSync() { }
|
||||
public virtual void Clock(int cycles) { }
|
||||
// PPU address bus latch
|
||||
public virtual void PPU_Latch(ushort addr) { }
|
||||
// PPU Character latch
|
||||
public virtual void PPU_ChrLatch(ushort addr) { }
|
||||
// PPU Extension character/palette
|
||||
public virtual void PPU_ExtLatchX(int x) { }
|
||||
public virtual void PPU_ExtLatch(ushort addr, ref byte chr_l, ref byte chr_h, ref byte attr) { }
|
||||
// For State save
|
||||
public virtual bool IsStateSave() { return false; }
|
||||
public virtual void SaveState(byte[] p) { }
|
||||
public virtual void LoadState(byte[] p) { }
|
||||
|
||||
// Extension commands
|
||||
// For ExCmdRead command
|
||||
public enum EXCMDRD
|
||||
{
|
||||
EXCMDRD_NONE = 0,
|
||||
EXCMDRD_DISKACCESS,
|
||||
}
|
||||
// For ExCmdWrite command
|
||||
public enum EXCMDWR
|
||||
{
|
||||
EXCMDWR_NONE = 0,
|
||||
EXCMDWR_DISKINSERT,
|
||||
EXCMDWR_DISKEJECT,
|
||||
}
|
||||
|
||||
public static Mapper CreateMapper(NES parent, int no)
|
||||
{
|
||||
//todo : 实现加载mapper
|
||||
switch (no)
|
||||
{
|
||||
case 0: return new Mapper000(parent);
|
||||
case 1: return new Mapper001(parent);
|
||||
case 2: return new Mapper002(parent);
|
||||
case 3: return new Mapper003(parent);
|
||||
case 4: return new Mapper004(parent);
|
||||
case 5: return new Mapper005(parent);
|
||||
case 6: return new Mapper006(parent);
|
||||
case 7: return new Mapper007(parent);
|
||||
case 8: return new Mapper008(parent);
|
||||
case 9: return new Mapper009(parent);
|
||||
case 10: return new Mapper010(parent);
|
||||
case 11: return new Mapper011(parent);
|
||||
case 12: return new Mapper012(parent);
|
||||
case 13: return new Mapper013(parent);
|
||||
case 15: return new Mapper015(parent);
|
||||
case 16: return new Mapper016(parent);
|
||||
case 17: return new Mapper017(parent);
|
||||
case 18: return new Mapper018(parent);
|
||||
case 19: return new Mapper019(parent);
|
||||
case 21: return new Mapper021(parent);
|
||||
case 22: return new Mapper022(parent);
|
||||
case 23: return new Mapper023(parent);
|
||||
case 24: return new Mapper024(parent);
|
||||
case 25: return new Mapper025(parent);
|
||||
case 26: return new Mapper026(parent);
|
||||
case 27: return new Mapper027(parent);
|
||||
case 32: return new Mapper032(parent);
|
||||
case 33: return new Mapper033(parent);
|
||||
case 34: return new Mapper034(parent);
|
||||
case 35: return new Mapper035(parent);
|
||||
case 40: return new Mapper040(parent);
|
||||
case 41: return new Mapper041(parent);
|
||||
case 42: return new Mapper042(parent);
|
||||
case 43: return new Mapper043(parent);
|
||||
case 44: return new Mapper044(parent);
|
||||
case 45: return new Mapper045(parent);
|
||||
case 46: return new Mapper046(parent);
|
||||
case 47: return new Mapper047(parent);
|
||||
case 48: return new Mapper048(parent);
|
||||
case 50: return new Mapper050(parent);
|
||||
case 51: return new Mapper051(parent);
|
||||
case 57: return new Mapper057(parent);
|
||||
case 58: return new Mapper058(parent);
|
||||
case 60: return new Mapper060(parent);
|
||||
case 61: return new Mapper061(parent);
|
||||
case 62: return new Mapper062(parent);
|
||||
case 64: return new Mapper064(parent);
|
||||
case 65: return new Mapper065(parent);
|
||||
case 66: return new Mapper066(parent);
|
||||
case 67: return new Mapper067(parent);
|
||||
case 68: return new Mapper068(parent);
|
||||
case 69: return new Mapper069(parent);
|
||||
case 70: return new Mapper070(parent);
|
||||
case 71: return new Mapper071(parent);
|
||||
case 72: return new Mapper072(parent);
|
||||
case 73: return new Mapper073(parent);
|
||||
case 74: return new Mapper074(parent);
|
||||
case 75: return new Mapper075(parent);
|
||||
case 76: return new Mapper076(parent);
|
||||
case 77: return new Mapper077(parent);
|
||||
case 78: return new Mapper078(parent);
|
||||
case 79: return new Mapper079(parent);
|
||||
case 80: return new Mapper080(parent);
|
||||
case 82: return new Mapper082(parent);
|
||||
case 83: return new Mapper083(parent);
|
||||
case 85: return new Mapper085(parent);
|
||||
case 86: return new Mapper086(parent);
|
||||
case 87: return new Mapper087(parent);
|
||||
case 88: return new Mapper088(parent);
|
||||
case 89: return new Mapper089(parent);
|
||||
case 90: return new Mapper090(parent);
|
||||
case 91: return new Mapper091(parent);
|
||||
case 92: return new Mapper092(parent);
|
||||
case 93: return new Mapper093(parent);
|
||||
case 94: return new Mapper094(parent);
|
||||
case 95: return new Mapper095(parent);
|
||||
case 96: return new Mapper096(parent);
|
||||
case 97: return new Mapper097(parent);
|
||||
case 99: return new Mapper099(parent);
|
||||
case 100: return new Mapper100(parent);
|
||||
case 101: return new Mapper101(parent);
|
||||
case 105: return new Mapper105(parent);
|
||||
case 108: return new Mapper108(parent);
|
||||
case 109: return new Mapper109(parent);
|
||||
case 110: return new Mapper110(parent);
|
||||
case 111: return new Mapper111(parent);
|
||||
case 112: return new Mapper112(parent);
|
||||
case 113: return new Mapper113(parent);
|
||||
case 114: return new Mapper114(parent);
|
||||
case 115: return new Mapper115(parent);
|
||||
case 116: return new Mapper116(parent);
|
||||
case 117: return new Mapper117(parent);
|
||||
case 118: return new Mapper118(parent);
|
||||
case 119: return new Mapper119(parent);
|
||||
case 122: return new Mapper122(parent);
|
||||
case 133: return new Mapper133(parent);
|
||||
case 134: return new Mapper134(parent);
|
||||
case 135: return new Mapper135(parent);
|
||||
case 140: return new Mapper140(parent);
|
||||
case 142: return new Mapper142(parent);
|
||||
case 151: return new Mapper151(parent);
|
||||
case 160: return new Mapper160(parent);
|
||||
case 162: return new Mapper162(parent);
|
||||
case 163: return new Mapper163(parent);
|
||||
case 164: return new Mapper164(parent);
|
||||
case 165: return new Mapper165(parent);
|
||||
case 167: return new Mapper167(parent);
|
||||
case 175: return new Mapper175(parent);
|
||||
case 176: return new Mapper176(parent);
|
||||
case 178: return new Mapper178(parent);
|
||||
case 180: return new Mapper180(parent);
|
||||
case 181: return new Mapper181(parent);
|
||||
case 182: return new Mapper182(parent);
|
||||
case 183: return new Mapper183(parent);
|
||||
case 185: return new Mapper185(parent);
|
||||
case 187: return new Mapper187(parent);
|
||||
case 188: return new Mapper188(parent);
|
||||
case 189: return new Mapper189(parent);
|
||||
case 190: return new Mapper190(parent);
|
||||
case 191: return new Mapper191(parent);
|
||||
case 192: return new Mapper192(parent);
|
||||
case 193: return new Mapper193(parent);
|
||||
case 194: return new Mapper194(parent);
|
||||
case 195: return new Mapper195(parent);
|
||||
case 198: return new Mapper198(parent);
|
||||
case 199: return new Mapper199(parent);
|
||||
case 200: return new Mapper200(parent);
|
||||
case 201: return new Mapper201(parent);
|
||||
case 202: return new Mapper202(parent);
|
||||
case 216: return new Mapper216(parent);
|
||||
case 222: return new Mapper222(parent);
|
||||
case 225: return new Mapper225(parent);
|
||||
case 226: return new Mapper226(parent);
|
||||
case 227: return new Mapper227(parent);
|
||||
case 228: return new Mapper228(parent);
|
||||
case 229: return new Mapper229(parent);
|
||||
case 230: return new Mapper230(parent);
|
||||
case 231: return new Mapper231(parent);
|
||||
case 232: return new Mapper232(parent);
|
||||
case 233: return new Mapper233(parent);
|
||||
case 234: return new Mapper234(parent);
|
||||
case 235: return new Mapper235(parent);
|
||||
case 236: return new Mapper236(parent);
|
||||
case 240: return new Mapper240(parent);
|
||||
case 241: return new Mapper241(parent);
|
||||
case 242: return new Mapper242(parent);
|
||||
case 243: return new Mapper243(parent);
|
||||
case 244: return new Mapper244(parent);
|
||||
case 245: return new Mapper245(parent);
|
||||
case 246: return new Mapper246(parent);
|
||||
case 248: return new Mapper248(parent);
|
||||
case 249: return new Mapper249(parent);
|
||||
case 251: return new Mapper251(parent);
|
||||
case 252: return new Mapper252(parent);
|
||||
case 254: return new Mapper254(parent);
|
||||
case 255: return new Mapper255(parent);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"Mapper#{no:000} is not Impl");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
Core/VirtualNes.Core/Mapper/Mapper000.cs
Normal file
44
Core/VirtualNes.Core/Mapper/Mapper000.cs
Normal file
@ -0,0 +1,44 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper000 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper000 : Mapper
|
||||
{
|
||||
|
||||
public Mapper000(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
switch (PROM_16K_SIZE)
|
||||
{
|
||||
default:
|
||||
case 1: // 16K only
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
SetPROM_16K_Bank(6, 0);
|
||||
break;
|
||||
case 2: // 32K
|
||||
SetPROM_32K_Bank(0);
|
||||
break;
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0x4e7db5af)
|
||||
{ // Circus Charlie(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
if (crc == 0x57970078)
|
||||
{ // F-1 Race(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
if (crc == 0xaf2bbcbc // Mach Rider(JU)
|
||||
|| crc == 0x3acd4bf1)
|
||||
{ // Mach Rider(Alt)(JU)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
409
Core/VirtualNes.Core/Mapper/Mapper001.cs
Normal file
409
Core/VirtualNes.Core/Mapper/Mapper001.cs
Normal file
@ -0,0 +1,409 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper001 Nintendo MMC1 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper001 : Mapper
|
||||
{
|
||||
ushort last_addr;
|
||||
|
||||
BYTE patch;
|
||||
BYTE wram_patch;
|
||||
BYTE wram_bank;
|
||||
BYTE wram_count;
|
||||
|
||||
BYTE[] reg = new byte[4];
|
||||
BYTE shift, regbuf;
|
||||
|
||||
public Mapper001(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
reg[0] = 0x0C; // D3=1,D2=1
|
||||
reg[1] = reg[2] = reg[3] = 0;
|
||||
shift = regbuf = 0;
|
||||
|
||||
patch = 0;
|
||||
wram_patch = 0;
|
||||
|
||||
if (PROM_16K_SIZE < 32)
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For 512K/1M byte Cartridge
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
SetPROM_16K_Bank(6, 16 - 1);
|
||||
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
// SetVROM_8K_Bank( 0 );
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xb8e16bd0)
|
||||
{ // Snow Bros.(J)
|
||||
patch = 2;
|
||||
}
|
||||
// if( crc == 0x9b565541 ) { // Triathron, The(J)
|
||||
// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
if (crc == 0xc96c6f04)
|
||||
{ // Venus Senki(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
// if( crc == 0x5e3f7004 ) { // Softball Tengoku(J)
|
||||
// }
|
||||
|
||||
if (crc == 0x4d2edf70)
|
||||
{ // Night Rider(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xcd2a73f0)
|
||||
{ // Pirates!(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
patch = 2;
|
||||
}
|
||||
|
||||
// if( crc == 0x09efe54b ) { // Majaventure - Mahjong Senki(J)
|
||||
// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
|
||||
if (crc == 0x11469ce3)
|
||||
{ // Viva! Las Vegas(J)
|
||||
}
|
||||
if (crc == 0xd878ebf5)
|
||||
{ // Ninja Ryukenden(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
|
||||
// if( crc == 0x7bd7b849 ) { // Nekketsu Koukou - Dodgeball Bu(J)
|
||||
// }
|
||||
|
||||
if (crc == 0x466efdc2)
|
||||
{ // Final Fantasy(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtMonoMode(true);
|
||||
}
|
||||
if (crc == 0xc9556b36)
|
||||
{ // Final Fantasy I&II(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtMonoMode(true);
|
||||
nes.SetSAVERAM_SIZE(16 * 1024);
|
||||
wram_patch = 2;
|
||||
}
|
||||
|
||||
if (crc == 0x717e1169)
|
||||
{ // Cosmic Wars(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xC05D2034)
|
||||
{ // Snake's Revenge(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xb8747abf // Best Play - Pro Yakyuu Special(J)
|
||||
|| crc == 0x29449ba9 // Nobunaga no Yabou - Zenkoku Ban(J)
|
||||
|| crc == 0x2b11e0b0 // Nobunaga no Yabou - Zenkoku Ban(J)(alt)
|
||||
|| crc == 0x4642dda6 // Nobunaga's Ambition(U)
|
||||
|| crc == 0xfb69743a // Aoki Ookami to Shiroki Mejika - Genghis Khan(J)
|
||||
|| crc == 0x2225c20f // Genghis Khan(U)
|
||||
|| crc == 0xabbf7217 // Sangokushi(J)
|
||||
)
|
||||
{
|
||||
|
||||
nes.SetSAVERAM_SIZE(16 * 1024);
|
||||
wram_patch = 1;
|
||||
wram_bank = 0;
|
||||
wram_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper001::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
// DEBUGOUT( "MMC1 %04X=%02X\n", addr&0xFFFF,data&0xFF );
|
||||
|
||||
if (wram_patch == 1 && addr == 0xBFFF)
|
||||
{
|
||||
wram_count++;
|
||||
wram_bank += (byte)(data & 0x01);
|
||||
if (wram_count == 5)
|
||||
{
|
||||
if (wram_bank != 0)
|
||||
{
|
||||
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x2000), BANKTYPE_RAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x0000), BANKTYPE_RAM);
|
||||
}
|
||||
wram_bank = wram_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (patch != 1)
|
||||
{
|
||||
if ((addr & 0x6000) != (last_addr & 0x6000))
|
||||
{
|
||||
shift = regbuf = 0;
|
||||
}
|
||||
last_addr = addr;
|
||||
}
|
||||
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
shift = regbuf = 0;
|
||||
// reg[0] = 0x0C; // D3=1,D2=1
|
||||
reg[0] |= 0x0C; // D3=1,D2=1 残りはリセットされない
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data & 0x01) != 0) regbuf |= (byte)(1 << shift);
|
||||
if (++shift < 5)
|
||||
return;
|
||||
addr = (ushort)((addr & 0x7FFF) >> 13);
|
||||
reg[addr] = regbuf;
|
||||
|
||||
// DEBUGOUT( "MMC1 %d=%02X\n", addr&0xFFFF,regbuf&0xFF );
|
||||
|
||||
regbuf = 0;
|
||||
shift = 0;
|
||||
|
||||
if (patch != 1)
|
||||
{
|
||||
// For Normal Cartridge
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
if ((reg[0] & 0x02) != 0)
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// Register #1
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// Register #2
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!((reg[0] & 0x08) != 0))
|
||||
{
|
||||
// PRG 32K bank ($8000-$FFFF)
|
||||
SetPROM_32K_Bank(reg[3] >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x04) != 0)
|
||||
{
|
||||
// PRG 16K bank ($8000-$BFFF)
|
||||
SetPROM_16K_Bank(4, reg[3]);
|
||||
SetPROM_16K_Bank(6, PROM_16K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PRG 16K bank ($C000-$FFFF)
|
||||
SetPROM_16K_Bank(6, reg[3]);
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For 512K/1M byte Cartridge
|
||||
INT PROM_BASE = 0;
|
||||
if (PROM_16K_SIZE >= 32)
|
||||
{
|
||||
PROM_BASE = reg[1] & 0x10;
|
||||
}
|
||||
|
||||
// For FinalFantasy I&II
|
||||
if (wram_patch == 2)
|
||||
{
|
||||
if (((reg[1] & 0x18) == 0))
|
||||
{
|
||||
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x0000), BANKTYPE_RAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x2000), BANKTYPE_RAM);
|
||||
}
|
||||
}
|
||||
|
||||
// Register #0
|
||||
if (addr == 0)
|
||||
{
|
||||
if ((reg[0] & 0x02) != 0)
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
// Register #1
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
}
|
||||
// Register #2
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
// Register #3
|
||||
if (((reg[0] & 0x08) == 0))
|
||||
{
|
||||
// PRG 32K bank ($8000-$FFFF)
|
||||
SetPROM_32K_Bank((reg[3] & (0xF + PROM_BASE)) >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x04) != 0)
|
||||
{
|
||||
// PRG 16K bank ($8000-$BFFF)
|
||||
SetPROM_16K_Bank(4, PROM_BASE + (reg[3] & 0x0F));
|
||||
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(6, PROM_BASE + 16 - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PRG 16K bank ($C000-$FFFF)
|
||||
SetPROM_16K_Bank(6, PROM_BASE + (reg[3] & 0x0F));
|
||||
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(4, PROM_BASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper001::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = reg[3];
|
||||
p[4] = shift;
|
||||
p[5] = regbuf;
|
||||
|
||||
p[6] = wram_bank;
|
||||
p[7] = wram_count;
|
||||
}
|
||||
|
||||
//void Mapper001::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
reg[3] = p[3];
|
||||
shift = p[4];
|
||||
regbuf = p[5];
|
||||
|
||||
wram_bank = p[6];
|
||||
wram_count = p[7];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
70
Core/VirtualNes.Core/Mapper/Mapper002.cs
Normal file
70
Core/VirtualNes.Core/Mapper/Mapper002.cs
Normal file
@ -0,0 +1,70 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper002 UNROM //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper002 : Mapper
|
||||
{
|
||||
|
||||
BYTE patch;
|
||||
public Mapper002(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
patch = 0;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
// if( crc == 0x322c9b09 ) { // Metal Gear (Alt)(J)
|
||||
//// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
// if( crc == 0xe7a3867b ) { // Dragon Quest 2(Alt)(J)
|
||||
// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
//// if( crc == 0x9622fbd9 ) { // Ballblazer(J)
|
||||
//// patch = 0;
|
||||
//// }
|
||||
if (crc == 0x8c3d54e8 // Ikari(J)
|
||||
|| crc == 0x655efeed // Ikari Warriors(U)
|
||||
|| crc == 0x538218b2)
|
||||
{ // Ikari Warriors(E)
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
if (crc == 0xb20c1030)
|
||||
{ // Shanghai(J)(original)
|
||||
patch = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//void Mapper002::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (!nes.rom.IsSAVERAM())
|
||||
{
|
||||
if (addr >= 0x5000 && patch == 1)
|
||||
SetPROM_16K_Bank(4, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.WriteLow(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper002::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (patch != 2)
|
||||
SetPROM_16K_Bank(4, data);
|
||||
else
|
||||
SetPROM_16K_Bank(4, data >> 4);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
63
Core/VirtualNes.Core/Mapper/Mapper003.cs
Normal file
63
Core/VirtualNes.Core/Mapper/Mapper003.cs
Normal file
@ -0,0 +1,63 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper003 CNROM //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper003 : Mapper
|
||||
{
|
||||
|
||||
public Mapper003(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
switch (PROM_16K_SIZE)
|
||||
{
|
||||
case 1: // 16K only
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
SetPROM_16K_Bank(6, 0);
|
||||
break;
|
||||
case 2: // 32K
|
||||
SetPROM_32K_Bank(0);
|
||||
break;
|
||||
}
|
||||
// nes.SetRenderMethod( NES::TILE_RENDER );
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x2b72fe7e)
|
||||
{ // Ganso Saiyuuki - Super Monkey Dai Bouken(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtNameTableMode(true);
|
||||
}
|
||||
|
||||
// if( crc == 0xE44D95B5 ) { // ひみつw
|
||||
// }
|
||||
}
|
||||
|
||||
#if FALSE//0
|
||||
void Mapper003::WriteLow( WORD addr, BYTE data )
|
||||
{
|
||||
if( patch ) {
|
||||
Mapper::WriteLow( addr, data );
|
||||
} else {
|
||||
if( nes.rom.IsSAVERAM() ) {
|
||||
Mapper::WriteLow( addr, data );
|
||||
} else {
|
||||
if( addr >= 0x4800 ) {
|
||||
SetVROM_8K_Bank( data & 0x03 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//void Mapper003::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetVROM_8K_Bank(data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
624
Core/VirtualNes.Core/Mapper/Mapper004.cs
Normal file
624
Core/VirtualNes.Core/Mapper/Mapper004.cs
Normal file
@ -0,0 +1,624 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper004 : Mapper
|
||||
{
|
||||
private const int MMC3_IRQ_KLAX = 1;
|
||||
private const int MMC3_IRQ_SHOUGIMEIKAN = 2;
|
||||
private const int MMC3_IRQ_DAI2JISUPER = 3;
|
||||
private const int MMC3_IRQ_DBZ2 = 4;
|
||||
private const int MMC3_IRQ_ROCKMAN3 = 5;
|
||||
|
||||
protected byte[] reg = new byte[8];
|
||||
protected byte prg0, prg1;
|
||||
protected byte chr01, chr23, chr4, chr5, chr6, chr7;
|
||||
protected byte we_sram;
|
||||
|
||||
protected byte irq_type;
|
||||
protected byte irq_enable;
|
||||
protected byte irq_counter;
|
||||
protected byte irq_latch;
|
||||
protected byte irq_request;
|
||||
protected byte irq_preset;
|
||||
protected byte irq_preset_vbl;
|
||||
|
||||
protected byte vs_patch;
|
||||
protected byte vs_index;
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private byte[] VS_TKO_Security = new byte[32]
|
||||
{
|
||||
0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f,
|
||||
0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14,
|
||||
0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90,
|
||||
0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00
|
||||
};
|
||||
|
||||
public Mapper004(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0x00;
|
||||
}
|
||||
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
SetBank_CPU();
|
||||
|
||||
chr01 = 0;
|
||||
chr23 = 2;
|
||||
chr4 = 4;
|
||||
chr5 = 5;
|
||||
chr6 = 6;
|
||||
chr7 = 7;
|
||||
SetBank_PPU();
|
||||
|
||||
we_sram = 0; // Disable
|
||||
irq_enable = 0; // Disable
|
||||
irq_counter = 0;
|
||||
irq_latch = 0xFF;
|
||||
irq_request = 0;
|
||||
irq_preset = 0;
|
||||
irq_preset_vbl = 0;
|
||||
|
||||
// IRQ僞僀僾愝掕
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK);
|
||||
irq_type = 0;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x5c707ac4)
|
||||
{ // Mother(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xcb106f49)
|
||||
{ // F-1 Sensation(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x1170392a)
|
||||
{ // Karakuri Kengou Den - Musashi Road - Karakuri Nin Hashiru!(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0x14a01c70)
|
||||
{ // Gun-Dec(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xeffeea40)
|
||||
{ // For Klax(J)
|
||||
irq_type = MMC3_IRQ_KLAX;
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xc17ae2dc)
|
||||
{ // God Slayer - Haruka Tenkuu no Sonata(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x126ea4a0)
|
||||
{ // Summer Carnival '92 - Recca(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0x1f2f4861)
|
||||
{ // J League Fighting Soccer - The King of Ace Strikers(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0x5a6860f1)
|
||||
{ // Shougi Meikan '92(J)
|
||||
irq_type = MMC3_IRQ_SHOUGIMEIKAN;
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0xae280e20)
|
||||
{ // Shougi Meikan '93(J)
|
||||
irq_type = MMC3_IRQ_SHOUGIMEIKAN;
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0xe19a2473)
|
||||
{ // Sugoro Quest - Dice no Senshi Tachi(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x702d9b33)
|
||||
{ // Star Wars - The Empire Strikes Back(Victor)(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0xa9a0d729)
|
||||
{ // Dai Kaijuu - Deburas(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xc5fea9f2)
|
||||
{ // Dai 2 Ji - Super Robot Taisen(J)
|
||||
irq_type = MMC3_IRQ_DAI2JISUPER;
|
||||
}
|
||||
if (crc == 0xd852c2f7)
|
||||
{ // Time Zone(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xecfd3c69)
|
||||
{ // Taito Chase H.Q.(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x7a748058)
|
||||
{ // Tom & Jerry (and Tuffy)(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0xaafe699c)
|
||||
{ // Ninja Ryukenden 3 - Yomi no Hakobune(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x6cc62c06)
|
||||
{ // Hoshi no Kirby - Yume no Izumi no Monogatari(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x877dba77)
|
||||
{ // My Life My Love - Boku no Yume - Watashi no Negai(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0x6f96ed15)
|
||||
{ // Max Warrior - Wakusei Kaigenrei(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0x8685f366)
|
||||
{ // Matendouji(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x8635fed1)
|
||||
{ // Mickey Mouse 3 - Yume Fuusen(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x26ff3ea2)
|
||||
{ // Yume Penguin Monogatari(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0x7671bc51)
|
||||
{ // Red Ariimaa 2(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0xade11141)
|
||||
{ // Wanpaku Kokkun no Gourmet World(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0x7c7ab58e)
|
||||
{ // Walkuere no Bouken - Toki no Kagi Densetsu(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
if (crc == 0x26ff3ea2)
|
||||
{ // Yume Penguin Monogatari(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x126ea4a0)
|
||||
{ // Summer Carnival '92 Recca(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0x1d2e5018 // Rockman 3(J)
|
||||
|| crc == 0x6b999aaf)
|
||||
{ // Megaman 3(U)
|
||||
irq_type = MMC3_IRQ_ROCKMAN3;
|
||||
}
|
||||
|
||||
if (crc == 0xd88d48d7)
|
||||
{ // Kick Master(U)
|
||||
irq_type = MMC3_IRQ_ROCKMAN3;
|
||||
}
|
||||
|
||||
if (crc == 0xA67EA466)
|
||||
{ // Alien 3(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xe763891b)
|
||||
{ // DBZ2
|
||||
irq_type = MMC3_IRQ_DBZ2;
|
||||
}
|
||||
|
||||
// VS-Unisystem
|
||||
vs_patch = 0;
|
||||
vs_index = 0;
|
||||
|
||||
if (crc == 0xeb2dba63 // VS TKO Boxing
|
||||
|| crc == 0x98cfe016)
|
||||
{ // VS TKO Boxing (Alt)
|
||||
vs_patch = 1;
|
||||
}
|
||||
if (crc == 0x135adf7c)
|
||||
{ // VS Atari RBI Baseball
|
||||
vs_patch = 2;
|
||||
}
|
||||
if (crc == 0xf9d3b0a3 // VS Super Xevious
|
||||
|| crc == 0x9924980a // VS Super Xevious (b1)
|
||||
|| crc == 0x66bb838f)
|
||||
{ // VS Super Xevious (b2)
|
||||
vs_patch = 3;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBank_PPU()
|
||||
{
|
||||
if (MMU.VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
MMU.SetVROM_8K_Bank(chr4, chr5, chr6, chr7,
|
||||
chr01, chr01 + 1, chr23, chr23 + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MMU.SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1,
|
||||
chr4, chr5, chr6, chr7);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
MMU.SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(0, chr4 & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(1, chr5 & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(2, chr6 & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(3, chr7 & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
MMU.SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(4, chr4 & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(5, chr5 & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(6, chr6 & 0x07);
|
||||
MMU.SetCRAM_1K_Bank(7, chr7 & 0x07);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
if (vs_patch == 0)
|
||||
{
|
||||
if (addr >= 0x5000 && addr <= 0x5FFF)
|
||||
{
|
||||
return MMU.XRAM[addr - 0x4000];
|
||||
}
|
||||
}
|
||||
else if (vs_patch == 1)
|
||||
{
|
||||
// VS TKO Boxing Security
|
||||
if (addr == 0x5E00)
|
||||
{
|
||||
vs_index = 0;
|
||||
return 0x00;
|
||||
}
|
||||
else if (addr == 0x5E01)
|
||||
{
|
||||
return VS_TKO_Security[(vs_index++) & 0x1F];
|
||||
}
|
||||
}
|
||||
else if (vs_patch == 2)
|
||||
{
|
||||
// VS Atari RBI Baseball Security
|
||||
if (addr == 0x5E00)
|
||||
{
|
||||
vs_index = 0;
|
||||
return 0x00;
|
||||
}
|
||||
else if (addr == 0x5E01)
|
||||
{
|
||||
if (vs_index++ == 9)
|
||||
return 0x6F;
|
||||
else
|
||||
return 0xB4;
|
||||
}
|
||||
}
|
||||
else if (vs_patch == 3)
|
||||
{
|
||||
// VS Super Xevious
|
||||
switch (addr)
|
||||
{
|
||||
case 0x54FF:
|
||||
return 0x05;
|
||||
case 0x5678:
|
||||
if (vs_index != 0)
|
||||
return 0x00;
|
||||
else
|
||||
return 0x01;
|
||||
case 0x578f:
|
||||
if (vs_index != 0)
|
||||
return 0xD1;
|
||||
else
|
||||
return 0x89;
|
||||
case 0x5567:
|
||||
if (vs_index != 0)
|
||||
{
|
||||
vs_index = 0;
|
||||
return 0x3E;
|
||||
}
|
||||
else
|
||||
{
|
||||
vs_index = 1;
|
||||
return 0x37;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return base.ReadLow(addr);
|
||||
}
|
||||
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr >= 0x5000 && addr <= 0x5FFF)
|
||||
{
|
||||
MMU.XRAM[addr - 0x4000] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.WriteLow(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = data;
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[1] = data;
|
||||
|
||||
switch (reg[0] & 0x07)
|
||||
{
|
||||
case 0x00:
|
||||
chr01 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x01:
|
||||
chr23 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x02:
|
||||
chr4 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x03:
|
||||
chr5 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x04:
|
||||
chr6 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x05:
|
||||
chr7 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x06:
|
||||
prg0 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
if (!nes.rom.Is4SCREEN())
|
||||
{
|
||||
if ((data & 0x01) != 0) MMU.SetVRAM_Mirror(MMU.VRAM_HMIRROR);
|
||||
else MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
case 0xA001:
|
||||
reg[3] = data;
|
||||
//DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() );
|
||||
break;
|
||||
case 0xC000:
|
||||
//DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() );
|
||||
reg[4] = data;
|
||||
if (irq_type == MMC3_IRQ_KLAX || irq_type == MMC3_IRQ_ROCKMAN3)
|
||||
{
|
||||
irq_counter = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_latch = data;
|
||||
}
|
||||
if (irq_type == MMC3_IRQ_DBZ2)
|
||||
{
|
||||
irq_latch = 0x07;
|
||||
}
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[5] = data;
|
||||
if (irq_type == MMC3_IRQ_KLAX || irq_type == MMC3_IRQ_ROCKMAN3)
|
||||
{
|
||||
irq_latch = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((nes.GetScanline() < 240) || (irq_type == MMC3_IRQ_SHOUGIMEIKAN))
|
||||
{
|
||||
irq_counter |= 0x80;
|
||||
irq_preset = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter |= 0x80;
|
||||
irq_preset_vbl = 0xFF;
|
||||
irq_preset = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[6] = data;
|
||||
irq_enable = 0;
|
||||
irq_request = 0;
|
||||
|
||||
nes.cpu.ClrIRQ(CPU.IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[7] = data;
|
||||
irq_enable = 1;
|
||||
irq_request = 0;
|
||||
|
||||
// nes->cpu->ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_type == MMC3_IRQ_KLAX)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_request = 0xFF;
|
||||
}
|
||||
if (irq_counter > 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (irq_request != 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(CPU.IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
else if (irq_type == MMC3_IRQ_ROCKMAN3)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if ((--irq_counter) == 0)
|
||||
{
|
||||
irq_request = 0xFF;
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (irq_request != 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(CPU.IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_preset_vbl != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_preset_vbl = 0;
|
||||
}
|
||||
if (irq_preset != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_preset = 0;
|
||||
if (irq_type == MMC3_IRQ_DAI2JISUPER && scanline == 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
}
|
||||
else if (irq_counter > 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
irq_request = 0xFF;
|
||||
|
||||
nes.cpu.SetIRQ(CPU.IRQ_MAPPER);
|
||||
}
|
||||
irq_preset = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBank_CPU()
|
||||
{
|
||||
if ((reg[0] & 0x40) != 0)
|
||||
{
|
||||
MMU.SetPROM_32K_Bank(MMU.PROM_8K_SIZE - 2, prg1, prg0, MMU.PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MMU.SetPROM_32K_Bank(prg0, prg1, MMU.PROM_8K_SIZE - 2, MMU.PROM_8K_SIZE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[8] = prg0;
|
||||
p[9] = prg1;
|
||||
p[10] = chr01;
|
||||
p[11] = chr23;
|
||||
p[12] = chr4;
|
||||
p[13] = chr5;
|
||||
p[14] = chr6;
|
||||
p[15] = chr7;
|
||||
p[16] = irq_enable;
|
||||
p[17] = (byte)irq_counter;
|
||||
p[18] = irq_latch;
|
||||
p[19] = irq_request;
|
||||
p[20] = irq_preset;
|
||||
p[21] = irq_preset_vbl;
|
||||
}
|
||||
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
prg0 = p[8];
|
||||
prg1 = p[9];
|
||||
chr01 = p[10];
|
||||
chr23 = p[11];
|
||||
chr4 = p[12];
|
||||
chr5 = p[13];
|
||||
chr6 = p[14];
|
||||
chr7 = p[15];
|
||||
irq_enable = p[16];
|
||||
irq_counter = (Byte)p[17];
|
||||
irq_latch = p[18];
|
||||
irq_request = p[19];
|
||||
irq_preset = p[20];
|
||||
irq_preset_vbl = p[21];
|
||||
}
|
||||
}
|
||||
}
|
845
Core/VirtualNes.Core/Mapper/Mapper005.cs
Normal file
845
Core/VirtualNes.Core/Mapper/Mapper005.cs
Normal file
@ -0,0 +1,845 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper005 Nintendo MMC5 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.Core.PPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper005 : Mapper
|
||||
{
|
||||
public const int MMC5_IRQ_METAL = 1 << 0;
|
||||
|
||||
BYTE sram_size;
|
||||
|
||||
BYTE prg_size; // $5100
|
||||
BYTE chr_size; // $5101
|
||||
BYTE sram_we_a, sram_we_b; // $5102-$5103
|
||||
BYTE graphic_mode; // $5104
|
||||
BYTE nametable_mode; // $5105
|
||||
BYTE[] nametable_type = new byte[4]; // $5105 use
|
||||
|
||||
BYTE sram_page; // $5113
|
||||
|
||||
BYTE fill_chr, fill_pal; // $5106-$5107
|
||||
BYTE split_control; // $5200
|
||||
BYTE split_scroll; // $5201
|
||||
BYTE split_page; // $5202
|
||||
|
||||
BYTE split_x;
|
||||
ushort split_addr;
|
||||
ushort split_yofs;
|
||||
|
||||
BYTE chr_type;
|
||||
BYTE chr_mode; // $5120-$512B use
|
||||
//BYTE chr_page[2][8];
|
||||
BYTE[,] chr_page = new byte[2, 8]; // $5120-$512B
|
||||
// BGパターン用バンク
|
||||
ArrayRef<byte>[] BG_MEM_BANK = new ArrayRef<byte>[8]{
|
||||
new ArrayRef<byte>(),
|
||||
new ArrayRef<byte>(),
|
||||
new ArrayRef<byte>(),
|
||||
new ArrayRef<byte>(),
|
||||
new ArrayRef<byte>(),
|
||||
new ArrayRef<byte>(),
|
||||
new ArrayRef<byte>(),
|
||||
new ArrayRef<byte>(),
|
||||
};
|
||||
BYTE[] BG_MEM_PAGE = new byte[8];
|
||||
|
||||
BYTE irq_status; // $5204(R)
|
||||
BYTE irq_enable; // $5204(W)
|
||||
BYTE irq_line; // $5203
|
||||
BYTE irq_scanline;
|
||||
BYTE irq_clear; // HSyncで使用
|
||||
BYTE irq_type;
|
||||
|
||||
BYTE mult_a, mult_b; // $5205-$5206
|
||||
public Mapper005(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
byte i;
|
||||
|
||||
prg_size = 3;
|
||||
chr_size = 3;
|
||||
|
||||
sram_we_a = 0x00;
|
||||
sram_we_b = 0x00;
|
||||
|
||||
graphic_mode = 0;
|
||||
nametable_mode = 0;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
nametable_type[i] = 0;
|
||||
}
|
||||
|
||||
fill_chr = fill_pal = 0;
|
||||
split_control = split_scroll = split_page = 0;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_status = 0;
|
||||
irq_scanline = 0;
|
||||
irq_line = 0;
|
||||
irq_clear = 0;
|
||||
|
||||
irq_type = 0;
|
||||
|
||||
mult_a = mult_b = 0;
|
||||
|
||||
chr_type = 0;
|
||||
chr_mode = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
chr_page[0, i] = i;
|
||||
chr_page[1, i] = (byte)(4 + (i & 0x03));
|
||||
}
|
||||
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
BG_MEM_BANK[i].SetArray(VROM, 0x0400 * i);
|
||||
BG_MEM_PAGE[i] = i;
|
||||
}
|
||||
|
||||
// SRAM設定
|
||||
SetBank_SRAM(3, 0);
|
||||
|
||||
sram_size = 0;
|
||||
nes.SetSAVERAM_SIZE(16 * 1024);
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x2b548d75 // Bandit Kings of Ancient China(U)
|
||||
|| crc == 0xf4cd4998 // Dai Koukai Jidai(J)
|
||||
|| crc == 0x8fa95456 // Ishin no Arashi(J)
|
||||
|| crc == 0x98c8e090 // Nobunaga no Yabou - Sengoku Gunyuu Den(J)
|
||||
|| crc == 0x8e9a5e2f // L'Empereur(Alt)(U)
|
||||
|| crc == 0x57e3218b // L'Empereur(U)
|
||||
|| crc == 0x2f50bd38 // L'Empereur(J)
|
||||
|| crc == 0xb56958d1 // Nobunaga's Ambition 2(U)
|
||||
|| crc == 0xe6c28c5f // Suikoden - Tenmei no Chikai(J)
|
||||
|| crc == 0xcd35e2e9)
|
||||
{ // Uncharted Waters(U)
|
||||
sram_size = 1;
|
||||
nes.SetSAVERAM_SIZE(32 * 1024);
|
||||
}
|
||||
else
|
||||
if (crc == 0xf4120e58 // Aoki Ookami to Shiroki Mejika - Genchou Hishi(J)
|
||||
|| crc == 0x286613d8 // Nobunaga no Yabou - Bushou Fuuun Roku(J)
|
||||
|| crc == 0x11eaad26 // Romance of the Three Kingdoms 2(U)
|
||||
|| crc == 0x95ba5733)
|
||||
{ // Sangokushi 2(J)
|
||||
sram_size = 2;
|
||||
nes.SetSAVERAM_SIZE(64 * 1024);
|
||||
}
|
||||
|
||||
if (crc == 0x95ca9ec7)
|
||||
{ // Castlevania 3 - Dracula's Curse(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xcd9acf43)
|
||||
{ // Metal Slader Glory(J)
|
||||
irq_type = MMC5_IRQ_METAL;
|
||||
}
|
||||
|
||||
if (crc == 0xe91548d8)
|
||||
{ // Shin 4 Nin Uchi Mahjong - Yakuman Tengoku(J)
|
||||
chr_type = 1;
|
||||
}
|
||||
|
||||
nes.ppu.SetExtLatchMode(true);
|
||||
nes.apu.SelectExSound(8);
|
||||
}
|
||||
|
||||
//BYTE Mapper005::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
BYTE data = (BYTE)(addr >> 8);
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x5015:
|
||||
data = nes.apu.ExRead(addr);
|
||||
break;
|
||||
|
||||
case 0x5204:
|
||||
data = irq_status;
|
||||
// irq_status = 0;
|
||||
irq_status = (byte)(irq_status & ~0x80);
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x5205:
|
||||
data = (byte)(mult_a * mult_b);
|
||||
break;
|
||||
case 0x5206:
|
||||
data = (BYTE)((mult_a * mult_b) >> 8);
|
||||
break;
|
||||
}
|
||||
|
||||
if (addr >= 0x5C00 && addr <= 0x5FFF)
|
||||
{
|
||||
if (graphic_mode >= 2)
|
||||
{ // ExRAM mode
|
||||
data = VRAM[0x0800 + (addr & 0x3FF)];
|
||||
}
|
||||
}
|
||||
else if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||
{
|
||||
data = base.ReadLow(addr);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//void Mapper005::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
byte i;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x5100:
|
||||
prg_size = (byte)(data & 0x03);
|
||||
break;
|
||||
case 0x5101:
|
||||
chr_size = (byte)(data & 0x03);
|
||||
break;
|
||||
|
||||
case 0x5102:
|
||||
sram_we_a = (byte)(data & 0x03);
|
||||
break;
|
||||
case 0x5103:
|
||||
sram_we_b = (byte)(data & 0x03);
|
||||
break;
|
||||
|
||||
case 0x5104:
|
||||
graphic_mode = (byte)(data & 0x03);
|
||||
break;
|
||||
case 0x5105:
|
||||
nametable_mode = data;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
nametable_type[i] = (byte)(data & 0x03);
|
||||
SetVRAM_1K_Bank((byte)(8 + i), nametable_type[i]);
|
||||
data >>= 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x5106:
|
||||
fill_chr = data;
|
||||
break;
|
||||
case 0x5107:
|
||||
fill_pal = (byte)(data & 0x03);
|
||||
break;
|
||||
|
||||
case 0x5113:
|
||||
SetBank_SRAM(3, (byte)(data & 0x07));
|
||||
break;
|
||||
|
||||
case 0x5114:
|
||||
case 0x5115:
|
||||
case 0x5116:
|
||||
case 0x5117:
|
||||
SetBank_CPU(addr, data);
|
||||
break;
|
||||
|
||||
case 0x5120:
|
||||
case 0x5121:
|
||||
case 0x5122:
|
||||
case 0x5123:
|
||||
case 0x5124:
|
||||
case 0x5125:
|
||||
case 0x5126:
|
||||
case 0x5127:
|
||||
chr_mode = 0;
|
||||
chr_page[0, addr & 0x07] = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
|
||||
case 0x5128:
|
||||
case 0x5129:
|
||||
case 0x512A:
|
||||
case 0x512B:
|
||||
chr_mode = 1;
|
||||
chr_page[1, (addr & 0x03) + 0] = data;
|
||||
chr_page[1, (addr & 0x03) + 4] = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
|
||||
case 0x5200:
|
||||
split_control = data;
|
||||
break;
|
||||
case 0x5201:
|
||||
split_scroll = data;
|
||||
break;
|
||||
case 0x5202:
|
||||
split_page = (byte)(data & 0x3F);
|
||||
break;
|
||||
|
||||
case 0x5203:
|
||||
irq_line = data;
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x5204:
|
||||
irq_enable = data;
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0x5205:
|
||||
mult_a = data;
|
||||
break;
|
||||
case 0x5206:
|
||||
mult_b = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (addr >= 0x5000 && addr <= 0x5015)
|
||||
{
|
||||
nes.apu.ExWrite(addr, data);
|
||||
}
|
||||
else if (addr >= 0x5C00 && addr <= 0x5FFF)
|
||||
{
|
||||
if (graphic_mode == 2)
|
||||
{ // ExRAM
|
||||
VRAM[0x0800 + (addr & 0x3FF)] = data;
|
||||
}
|
||||
else if (graphic_mode != 3)
|
||||
{ // Split,ExGraphic
|
||||
if ((irq_status & 0x40) != 0)
|
||||
{
|
||||
VRAM[0x0800 + (addr & 0x3FF)] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
VRAM[0x0800 + (addr & 0x3FF)] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||
{
|
||||
if ((sram_we_a == 0x02) && (sram_we_b == 0x01))
|
||||
{
|
||||
if (CPU_MEM_TYPE[3] == BANKTYPE_RAM)
|
||||
{
|
||||
CPU_MEM_BANK[3][addr & 0x1FFF] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper005::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (sram_we_a == 0x02 && sram_we_b == 0x01)
|
||||
{
|
||||
if (addr >= 0x8000 && addr < 0xE000)
|
||||
{
|
||||
if (CPU_MEM_TYPE[addr >> 13] == BANKTYPE_RAM)
|
||||
{
|
||||
CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_CPU(uint addr, BYTE data)
|
||||
{
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
// PROM Bank
|
||||
switch (addr & 7)
|
||||
{
|
||||
case 4:
|
||||
if (prg_size == 3)
|
||||
{
|
||||
SetPROM_8K_Bank(4, data & 0x7F);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (prg_size == 1 || prg_size == 2)
|
||||
{
|
||||
SetPROM_16K_Bank(4, (data & 0x7F) >> 1);
|
||||
}
|
||||
else if (prg_size == 3)
|
||||
{
|
||||
SetPROM_8K_Bank(5, (data & 0x7F));
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (prg_size == 2 || prg_size == 3)
|
||||
{
|
||||
SetPROM_8K_Bank(6, (data & 0x7F));
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (prg_size == 0)
|
||||
{
|
||||
SetPROM_32K_Bank((data & 0x7F) >> 2);
|
||||
}
|
||||
else if (prg_size == 1)
|
||||
{
|
||||
SetPROM_16K_Bank(6, (data & 0x7F) >> 1);
|
||||
}
|
||||
else if (prg_size == 2 || prg_size == 3)
|
||||
{
|
||||
SetPROM_8K_Bank(7, (data & 0x7F));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// WRAM Bank
|
||||
switch (addr & 7)
|
||||
{
|
||||
case 4:
|
||||
if (prg_size == 3)
|
||||
{
|
||||
SetBank_SRAM(4, (byte)(data & 0x07));
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (prg_size == 1 || prg_size == 2)
|
||||
{
|
||||
SetBank_SRAM(4, (byte)((data & 0x06) + 0));
|
||||
SetBank_SRAM(5, (byte)((data & 0x06) + 1));
|
||||
}
|
||||
else if (prg_size == 3)
|
||||
{
|
||||
SetBank_SRAM(5, (byte)(data & 0x07));
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (prg_size == 2 || prg_size == 3)
|
||||
{
|
||||
SetBank_SRAM(6, (byte)(data & 0x07));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayRef<byte> _prom_bank = new ArrayRef<byte>();
|
||||
void SetBank_SRAM(BYTE page, BYTE data)
|
||||
{
|
||||
if (sram_size == 0) data = (byte)((data > 3) ? 8 : 0);
|
||||
if (sram_size == 1) data = (byte)((data > 3) ? 1 : 0);
|
||||
if (sram_size == 2) data = (byte)((data > 3) ? 8 : data);
|
||||
if (sram_size == 3) data = (byte)((data > 3) ? 4 : data);
|
||||
|
||||
if (data != 8)
|
||||
{
|
||||
int offset = 0x2000 * data;
|
||||
_prom_bank.SetArray(WRAM, offset, WRAM.Length - offset);
|
||||
SetPROM_Bank(page, _prom_bank, BANKTYPE_RAM);
|
||||
CPU_MEM_PAGE[page] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPU_MEM_TYPE[page] = BANKTYPE_ROM;
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
INT i;
|
||||
|
||||
if (chr_mode == 0)
|
||||
{
|
||||
// PPU SP Bank
|
||||
switch (chr_size)
|
||||
{
|
||||
case 0:
|
||||
SetVROM_8K_Bank(chr_page[0, 7]);
|
||||
break;
|
||||
case 1:
|
||||
SetVROM_4K_Bank(0, chr_page[0, 3]);
|
||||
SetVROM_4K_Bank(4, chr_page[0, 7]);
|
||||
break;
|
||||
case 2:
|
||||
SetVROM_2K_Bank(0, chr_page[0, 1]);
|
||||
SetVROM_2K_Bank(2, chr_page[0, 3]);
|
||||
SetVROM_2K_Bank(4, chr_page[0, 5]);
|
||||
SetVROM_2K_Bank(6, chr_page[0, 7]);
|
||||
break;
|
||||
case 3:
|
||||
SetVROM_8K_Bank(chr_page[0, 0],
|
||||
chr_page[0, 1],
|
||||
chr_page[0, 2],
|
||||
chr_page[0, 3],
|
||||
chr_page[0, 4],
|
||||
chr_page[0, 5],
|
||||
chr_page[0, 6],
|
||||
chr_page[0, 7]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (chr_mode == 1)
|
||||
{
|
||||
// PPU BG Bank
|
||||
switch (chr_size)
|
||||
{
|
||||
case 0:
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
BG_MEM_BANK[i].SetArray(VROM, 0x2000 * (chr_page[1, 7] % VROM_8K_SIZE) + 0x0400 * i);
|
||||
BG_MEM_PAGE[i] = (byte)((chr_page[1, 7] % VROM_8K_SIZE) * 8 + i);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
BG_MEM_BANK[i + 0].SetArray(VROM, 0x1000 * (chr_page[1, 3] % VROM_4K_SIZE) + 0x0400 * i);
|
||||
BG_MEM_BANK[i + 4].SetArray(VROM, 0x1000 * (chr_page[1, 7] % VROM_4K_SIZE) + 0x0400 * i);
|
||||
BG_MEM_PAGE[i + 0] = (byte)((chr_page[1, 3] % VROM_4K_SIZE) * 4 + i);
|
||||
BG_MEM_PAGE[i + 4] = (byte)((chr_page[1, 7] % VROM_4K_SIZE) * 4 + i);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
BG_MEM_BANK[i + 0].SetArray(VROM, 0x0800 * (chr_page[1, 1] % VROM_2K_SIZE) + 0x0400 * i);
|
||||
BG_MEM_BANK[i + 2].SetArray(VROM, 0x0800 * (chr_page[1, 3] % VROM_2K_SIZE) + 0x0400 * i);
|
||||
BG_MEM_BANK[i + 4].SetArray(VROM, 0x0800 * (chr_page[1, 5] % VROM_2K_SIZE) + 0x0400 * i);
|
||||
BG_MEM_BANK[i + 6].SetArray(VROM, 0x0800 * (chr_page[1, 7] % VROM_2K_SIZE) + 0x0400 * i);
|
||||
BG_MEM_PAGE[i + 0] = (byte)((chr_page[1, 1] % VROM_2K_SIZE) * 2 + i);
|
||||
BG_MEM_PAGE[i + 2] = (byte)((chr_page[1, 3] % VROM_2K_SIZE) * 2 + i);
|
||||
BG_MEM_PAGE[i + 4] = (byte)((chr_page[1, 5] % VROM_2K_SIZE) * 2 + i);
|
||||
BG_MEM_PAGE[i + 6] = (byte)((chr_page[1, 7] % VROM_2K_SIZE) * 2 + i);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
BG_MEM_BANK[i].SetArray(VROM, 0x0400 * (chr_page[1, i] % VROM_1K_SIZE));
|
||||
BG_MEM_PAGE[i] = (byte)((chr_page[1, i] % VROM_1K_SIZE) + i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((irq_type & MMC5_IRQ_METAL) != 0)
|
||||
{
|
||||
if (irq_scanline == irq_line)
|
||||
{
|
||||
irq_status |= 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
// if( nes.ppu.IsDispON() && scanline < 239 ) {
|
||||
if (nes.ppu.IsDispON() && scanline < 240)
|
||||
{
|
||||
irq_scanline++;
|
||||
irq_status |= 0x40;
|
||||
irq_clear = 0;
|
||||
}
|
||||
else if ((irq_type & MMC5_IRQ_METAL) != 0)
|
||||
{
|
||||
irq_scanline = 0;
|
||||
irq_status = (byte)(irq_status & ~0x80);
|
||||
irq_status = (byte)(irq_status & ~0x40);
|
||||
}
|
||||
|
||||
if ((irq_type & MMC5_IRQ_METAL) == 0)
|
||||
{
|
||||
if (irq_scanline == irq_line)
|
||||
{
|
||||
irq_status |= 0x80;
|
||||
}
|
||||
|
||||
if (++irq_clear > 2)
|
||||
{
|
||||
irq_scanline = 0;
|
||||
irq_status = (byte)(irq_status & ~0x80);
|
||||
irq_status = (byte)(irq_status & ~0x40);
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
|
||||
if ((irq_enable & 0x80) != 0 && (irq_status & 0x80) != 0 && (irq_status & 0x40) != 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
/// nes.cpu.IRQ_NotPending();
|
||||
}
|
||||
|
||||
// For Split mode!
|
||||
if (scanline == 0)
|
||||
{
|
||||
split_yofs = (ushort)(split_scroll & 0x07);
|
||||
split_addr = (ushort)(((split_scroll & 0xF8) << 2));
|
||||
}
|
||||
else if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (split_yofs == 7)
|
||||
{
|
||||
split_yofs = 0;
|
||||
if ((split_addr & 0x03E0) == 0x03A0)
|
||||
{
|
||||
split_addr &= 0x001F;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((split_addr & 0x03E0) == 0x03E0)
|
||||
{
|
||||
split_addr &= 0x001F;
|
||||
}
|
||||
else
|
||||
{
|
||||
split_addr += 0x0020;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
split_yofs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper005::PPU_ExtLatchX(INT x)
|
||||
public override void PPU_ExtLatchX(int x)
|
||||
{
|
||||
split_x = (byte)x;
|
||||
}
|
||||
|
||||
//void Mapper005::PPU_ExtLatch(WORD addr, BYTE& chr_l, BYTE& chr_h, BYTE& attr )
|
||||
public override void PPU_ExtLatch(ushort addr, ref byte chr_l, ref byte chr_h, ref byte attr)
|
||||
{
|
||||
ushort ntbladr, attradr, tileadr, tileofs;
|
||||
ushort tile_yofs;
|
||||
uint tilebank;
|
||||
bool bSplit;
|
||||
|
||||
tile_yofs = nes.ppu.GetTILEY();
|
||||
|
||||
bSplit = false;
|
||||
if ((split_control & 0x80) != 0)
|
||||
{
|
||||
if ((split_control & 0x40) == 0)
|
||||
{
|
||||
// Left side
|
||||
if ((split_control & 0x1F) > split_x)
|
||||
{
|
||||
bSplit = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Right side
|
||||
if ((split_control & 0x1F) <= split_x)
|
||||
{
|
||||
bSplit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bSplit)
|
||||
{
|
||||
if (nametable_type[(addr & 0x0C00) >> 10] == 3)
|
||||
{
|
||||
// Fill mode
|
||||
if (graphic_mode == 1)
|
||||
{
|
||||
// ExGraphic mode
|
||||
ntbladr = (ushort)(0x2000 + (addr & 0x0FFF));
|
||||
// Get Nametable
|
||||
tileadr = (ushort)(fill_chr * 0x10 + tile_yofs);
|
||||
// Get TileBank
|
||||
tilebank = (uint)(0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE));
|
||||
// Attribute
|
||||
attr = (byte)((fill_pal << 2) & 0x0C);
|
||||
// Get Pattern
|
||||
chr_l = VROM[tilebank + tileadr];
|
||||
chr_h = VROM[tilebank + tileadr + 8];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal
|
||||
tileofs = (ushort)((PPUREG[0] & PPU_BGTBL_BIT) != 0 ? 0x1000 : 0x0000);
|
||||
tileadr = (ushort)(tileofs + fill_chr * 0x10 + tile_yofs);
|
||||
attr = (byte)((fill_pal << 2) & 0x0C);
|
||||
// Get Pattern
|
||||
if (chr_type != 0)
|
||||
{
|
||||
chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
|
||||
chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
|
||||
}
|
||||
else
|
||||
{
|
||||
chr_l = BG_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
|
||||
chr_h = BG_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (graphic_mode == 1)
|
||||
{
|
||||
// ExGraphic mode
|
||||
ntbladr = (ushort)(0x2000 + (addr & 0x0FFF));
|
||||
// Get Nametable
|
||||
tileadr = (ushort)(PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs);
|
||||
// Get TileBank
|
||||
tilebank = (uint)(0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE));
|
||||
// Get Attribute
|
||||
attr = (byte)((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0xC0) >> 4);
|
||||
// Get Pattern
|
||||
chr_l = VROM[tilebank + tileadr];
|
||||
chr_h = VROM[tilebank + tileadr + 8];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal or ExVRAM
|
||||
tileofs = (ushort)((PPUREG[0] & PPU_BGTBL_BIT) != 0 ? 0x1000 : 0x0000);
|
||||
ntbladr = (ushort)(0x2000 + (addr & 0x0FFF));
|
||||
attradr = (ushort)(0x23C0 + (addr & 0x0C00) + ((addr & 0x0380) >> 4) + ((addr & 0x001C) >> 2));
|
||||
// Get Nametable
|
||||
tileadr = (ushort)(tileofs + PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs);
|
||||
// Get Attribute
|
||||
attr = PPU_MEM_BANK[attradr >> 10][attradr & 0x03FF];
|
||||
if ((ntbladr & 0x0002) != 0) attr >>= 2;
|
||||
if ((ntbladr & 0x0040) != 0) attr >>= 4;
|
||||
attr = (byte)((attr & 0x03) << 2);
|
||||
// Get Pattern
|
||||
if (chr_type != 0)
|
||||
{
|
||||
chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
|
||||
chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
|
||||
}
|
||||
else
|
||||
{
|
||||
chr_l = BG_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
|
||||
chr_h = BG_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ntbladr = (ushort)(((split_addr & 0x03E0) | (split_x & 0x1F)) & 0x03FF);
|
||||
// Get Split TileBank
|
||||
tilebank = (uint)(0x1000 * (split_page % VROM_4K_SIZE));
|
||||
tileadr = (ushort)(VRAM[0x0800 + ntbladr] * 0x10 + split_yofs);
|
||||
// Get Attribute
|
||||
attradr = (ushort)(0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2));
|
||||
attr = VRAM[0x0800 + attradr];
|
||||
if ((ntbladr & 0x0002) != 0) attr >>= 2;
|
||||
if ((ntbladr & 0x0040) != 0) attr >>= 4;
|
||||
attr = (byte)((attr & 0x03) << 2);
|
||||
// Get Pattern
|
||||
chr_l = VROM[tilebank + tileadr];
|
||||
chr_h = VROM[tilebank + tileadr + 8];
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper005::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = prg_size;
|
||||
p[1] = chr_size;
|
||||
p[2] = sram_we_a;
|
||||
p[3] = sram_we_b;
|
||||
p[4] = graphic_mode;
|
||||
p[5] = nametable_mode;
|
||||
p[6] = nametable_type[0];
|
||||
p[7] = nametable_type[1];
|
||||
p[8] = nametable_type[2];
|
||||
p[9] = nametable_type[3];
|
||||
p[10] = sram_page;
|
||||
p[11] = fill_chr;
|
||||
p[12] = fill_pal;
|
||||
p[13] = split_control;
|
||||
p[14] = split_scroll;
|
||||
p[15] = split_page;
|
||||
p[16] = chr_mode;
|
||||
p[17] = irq_status;
|
||||
p[18] = irq_enable;
|
||||
p[19] = irq_line;
|
||||
p[20] = irq_scanline;
|
||||
p[21] = irq_clear;
|
||||
p[22] = mult_a;
|
||||
p[23] = mult_b;
|
||||
|
||||
INT i, j;
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
p[24 + j * 8 + i] = chr_page[j, i];
|
||||
}
|
||||
}
|
||||
// for( i = 0; i < 8; i++ ) {
|
||||
// p[40+i] = BG_MEM_PAGE[i];
|
||||
// }
|
||||
}
|
||||
|
||||
//void Mapper005::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
prg_size = p[0];
|
||||
chr_size = p[1];
|
||||
sram_we_a = p[2];
|
||||
sram_we_b = p[3];
|
||||
graphic_mode = p[4];
|
||||
nametable_mode = p[5];
|
||||
nametable_type[0] = p[6];
|
||||
nametable_type[1] = p[7];
|
||||
nametable_type[2] = p[8];
|
||||
nametable_type[3] = p[9];
|
||||
sram_page = p[10];
|
||||
fill_chr = p[11];
|
||||
fill_pal = p[12];
|
||||
split_control = p[13];
|
||||
split_scroll = p[14];
|
||||
split_page = p[15];
|
||||
chr_mode = p[16];
|
||||
irq_status = p[17];
|
||||
irq_enable = p[18];
|
||||
irq_line = p[19];
|
||||
irq_scanline = p[20];
|
||||
irq_clear = p[21];
|
||||
mult_a = p[22];
|
||||
mult_b = p[23];
|
||||
|
||||
INT i, j;
|
||||
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
chr_page[j, i] = p[24 + j * 8 + i];
|
||||
}
|
||||
}
|
||||
// // BGバンクの再設定処理
|
||||
// for( i = 0; i < 8; i++ ) {
|
||||
// BG_MEM_PAGE[i] = p[40+i]%VROM_1K_SIZE;
|
||||
// }
|
||||
// for( i = 0; i < 8; i++ ) {
|
||||
// BG_MEM_BANK[i] = VROM+0x0400*BG_MEM_PAGE[i];
|
||||
// }
|
||||
|
||||
SetBank_PPU();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
115
Core/VirtualNes.Core/Mapper/Mapper006.cs
Normal file
115
Core/VirtualNes.Core/Mapper/Mapper006.cs
Normal file
@ -0,0 +1,115 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper006 FFE F4xxx //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper006 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
public Mapper006(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 14, 15);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_8K_Bank(0);
|
||||
}
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
}
|
||||
|
||||
//void Mapper006::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x42FE:
|
||||
if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
case 0x42FF:
|
||||
if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
|
||||
case 0x4501:
|
||||
irq_enable = 0;
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x4502:
|
||||
irq_counter = (irq_counter & 0xFF00) | data;
|
||||
break;
|
||||
case 0x4503:
|
||||
irq_counter = (irq_counter & 0x00FF) | ((INT)data << 8);
|
||||
irq_enable = 0xFF;
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
default:
|
||||
base.WriteLow(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper006::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_16K_Bank(4, (data & 0x3C) >> 2);
|
||||
SetCRAM_8K_Bank(data & 0x03);
|
||||
}
|
||||
|
||||
//void Mapper006::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
irq_counter += 133;
|
||||
if (irq_counter >= 0xFFFF)
|
||||
{
|
||||
// nes.cpu.IRQ();
|
||||
irq_counter = 0;
|
||||
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper006::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
//*(INT*)&p[1] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 1);
|
||||
}
|
||||
|
||||
//void Mapper006::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
//irq_counter = *(INT*)&p[1];
|
||||
irq_counter = BitConverter.ToInt32(p, 1);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
55
Core/VirtualNes.Core/Mapper/Mapper007.cs
Normal file
55
Core/VirtualNes.Core/Mapper/Mapper007.cs
Normal file
@ -0,0 +1,55 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper007 AOROM/AMROM //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper007 : Mapper
|
||||
{
|
||||
|
||||
BYTE patch;
|
||||
public Mapper007(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
SetPROM_32K_Bank(0);
|
||||
SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0x3c9fe649)
|
||||
{ // WWF Wrestlemania Challenge(U)
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
patch = 1;
|
||||
}
|
||||
if (crc == 0x09874777)
|
||||
{ // Marble Madness(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0x279710DC // Battletoads (U)
|
||||
|| crc == 0xCEB65B06)
|
||||
{ // Battletoads Double Dragon (U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
//::memset(WRAM, 0, sizeof(WRAM));
|
||||
MemoryUtility.ZEROMEMORY(WRAM, WRAM.Length);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper007::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_32K_Bank(data & 0x07);
|
||||
|
||||
if (patch != 0)
|
||||
{
|
||||
if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
29
Core/VirtualNes.Core/Mapper/Mapper008.cs
Normal file
29
Core/VirtualNes.Core/Mapper/Mapper008.cs
Normal file
@ -0,0 +1,29 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper008 FFE F3xxx //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper008 : Mapper
|
||||
{
|
||||
|
||||
public Mapper008(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 2, 3);
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
//void Mapper008::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_16K_Bank(4, (data & 0xF8) >> 3);
|
||||
SetVROM_8K_Bank(data & 0x07);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
127
Core/VirtualNes.Core/Mapper/Mapper009.cs
Normal file
127
Core/VirtualNes.Core/Mapper/Mapper009.cs
Normal file
@ -0,0 +1,127 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper009 Nintendo MMC2 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper009 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[4];
|
||||
BYTE latch_a, latch_b;
|
||||
|
||||
public Mapper009(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
reg[0] = 0; reg[1] = 4;
|
||||
reg[2] = 0; reg[3] = 0;
|
||||
|
||||
latch_a = 0xFE;
|
||||
latch_b = 0xFE;
|
||||
SetVROM_4K_Bank(0, 4);
|
||||
SetVROM_4K_Bank(4, 0);
|
||||
|
||||
nes.ppu.SetChrLatchMode(true);
|
||||
}
|
||||
|
||||
//void Mapper009::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0xB000:
|
||||
reg[0] = data;
|
||||
if (latch_a == 0xFD)
|
||||
{
|
||||
SetVROM_4K_Bank(0, reg[0]);
|
||||
}
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[1] = data;
|
||||
if (latch_a == 0xFE)
|
||||
{
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
break;
|
||||
case 0xD000:
|
||||
reg[2] = data;
|
||||
if (latch_b == 0xFD)
|
||||
{
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[3] = data;
|
||||
if (latch_b == 0xFE)
|
||||
{
|
||||
SetVROM_4K_Bank(4, reg[3]);
|
||||
}
|
||||
break;
|
||||
case 0xF000:
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper009::PPU_ChrLatch(WORD addr)
|
||||
public override void PPU_ChrLatch(ushort addr)
|
||||
{
|
||||
if ((addr & 0x1FF0) == 0x0FD0 && latch_a != 0xFD)
|
||||
{
|
||||
latch_a = 0xFD;
|
||||
SetVROM_4K_Bank(0, reg[0]);
|
||||
}
|
||||
else if ((addr & 0x1FF0) == 0x0FE0 && latch_a != 0xFE)
|
||||
{
|
||||
latch_a = 0xFE;
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
else if ((addr & 0x1FF0) == 0x1FD0 && latch_b != 0xFD)
|
||||
{
|
||||
latch_b = 0xFD;
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else if ((addr & 0x1FF0) == 0x1FE0 && latch_b != 0xFE)
|
||||
{
|
||||
latch_b = 0xFE;
|
||||
SetVROM_4K_Bank(4, reg[3]);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper009::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = reg[3];
|
||||
p[4] = latch_a;
|
||||
p[5] = latch_b;
|
||||
}
|
||||
|
||||
//void Mapper009::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
reg[3] = p[3];
|
||||
latch_a = p[4];
|
||||
latch_b = p[5];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
127
Core/VirtualNes.Core/Mapper/Mapper010.cs
Normal file
127
Core/VirtualNes.Core/Mapper/Mapper010.cs
Normal file
@ -0,0 +1,127 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper010 Nintendo MMC4 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper010 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[4];
|
||||
BYTE latch_a, latch_b;
|
||||
|
||||
public Mapper010(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
reg[0] = 0; reg[1] = 4;
|
||||
reg[2] = 0; reg[3] = 0;
|
||||
|
||||
latch_a = 0xFE;
|
||||
latch_b = 0xFE;
|
||||
SetVROM_4K_Bank(0, 4);
|
||||
SetVROM_4K_Bank(4, 0);
|
||||
|
||||
nes.ppu.SetChrLatchMode(true);
|
||||
}
|
||||
|
||||
//void Mapper010::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0xA000:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
case 0xB000:
|
||||
reg[0] = data;
|
||||
if (latch_a == 0xFD)
|
||||
{
|
||||
SetVROM_4K_Bank(0, reg[0]);
|
||||
}
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[1] = data;
|
||||
if (latch_a == 0xFE)
|
||||
{
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
break;
|
||||
case 0xD000:
|
||||
reg[2] = data;
|
||||
if (latch_b == 0xFD)
|
||||
{
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[3] = data;
|
||||
if (latch_b == 0xFE)
|
||||
{
|
||||
SetVROM_4K_Bank(4, reg[3]);
|
||||
}
|
||||
break;
|
||||
case 0xF000:
|
||||
if ((data & 0x01) != 0)
|
||||
SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper010::PPU_ChrLatch(WORD addr)
|
||||
public override void PPU_ChrLatch(ushort addr)
|
||||
{
|
||||
if ((addr & 0x1FF0) == 0x0FD0 && latch_a != 0xFD)
|
||||
{
|
||||
latch_a = 0xFD;
|
||||
SetVROM_4K_Bank(0, reg[0]);
|
||||
}
|
||||
else if ((addr & 0x1FF0) == 0x0FE0 && latch_a != 0xFE)
|
||||
{
|
||||
latch_a = 0xFE;
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
else if ((addr & 0x1FF0) == 0x1FD0 && latch_b != 0xFD)
|
||||
{
|
||||
latch_b = 0xFD;
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else if ((addr & 0x1FF0) == 0x1FE0 && latch_b != 0xFE)
|
||||
{
|
||||
latch_b = 0xFE;
|
||||
SetVROM_4K_Bank(4, reg[3]);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper010::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = reg[3];
|
||||
p[4] = latch_a;
|
||||
p[5] = latch_b;
|
||||
}
|
||||
|
||||
//void Mapper010::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
reg[3] = p[3];
|
||||
latch_a = p[4];
|
||||
latch_b = p[5];
|
||||
}
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
39
Core/VirtualNes.Core/Mapper/Mapper011.cs
Normal file
39
Core/VirtualNes.Core/Mapper/Mapper011.cs
Normal file
@ -0,0 +1,39 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper011 Color Dreams //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper011 : Mapper
|
||||
{
|
||||
|
||||
public Mapper011(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
// SetVROM_8K_Bank( 1 );
|
||||
}
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
|
||||
//void Mapper011::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT("WR A:%04X D:%02X\n", addr, data);
|
||||
SetPROM_32K_Bank(data);
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(data >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
336
Core/VirtualNes.Core/Mapper/Mapper012.cs
Normal file
336
Core/VirtualNes.Core/Mapper/Mapper012.cs
Normal file
@ -0,0 +1,336 @@
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper012 : Mapper
|
||||
{
|
||||
uint vb0, vb1;
|
||||
BYTE[] reg = new byte[8];
|
||||
BYTE prg0, prg1;
|
||||
BYTE chr01, chr23, chr4, chr5, chr6, chr7;
|
||||
BYTE we_sram;
|
||||
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
BYTE irq_request;
|
||||
BYTE irq_preset;
|
||||
BYTE irq_preset_vbl;
|
||||
|
||||
public Mapper012(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0x00;
|
||||
}
|
||||
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
SetBank_CPU();
|
||||
|
||||
vb0 = 0;
|
||||
vb1 = 0;
|
||||
chr01 = 0;
|
||||
chr23 = 2;
|
||||
chr4 = 4;
|
||||
chr5 = 5;
|
||||
chr6 = 6;
|
||||
chr7 = 7;
|
||||
SetBank_PPU();
|
||||
|
||||
we_sram = 0; // Disable
|
||||
irq_enable = 0; // Disable
|
||||
irq_counter = 0;
|
||||
irq_latch = 0xFF;
|
||||
irq_request = 0;
|
||||
irq_preset = 0;
|
||||
irq_preset_vbl = 0;
|
||||
}
|
||||
|
||||
//void Mapper012::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr > 0x4100 && addr < 0x6000)
|
||||
{
|
||||
vb0 = (byte)((data & 0x01) << 8);
|
||||
vb1 = (byte)((data & 0x10) << 4);
|
||||
SetBank_PPU();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.WriteLow(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
//BYTE Mapper012::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
//void Mapper012::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() );
|
||||
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = data;
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[1] = data;
|
||||
|
||||
switch (reg[0] & 0x07)
|
||||
{
|
||||
case 0x00:
|
||||
chr01 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x01:
|
||||
chr23 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x02:
|
||||
chr4 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x03:
|
||||
chr5 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x04:
|
||||
chr6 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x05:
|
||||
chr7 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x06:
|
||||
prg0 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
if (!nes.rom.Is4SCREEN())
|
||||
{
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
case 0xA001:
|
||||
reg[3] = data;
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[4] = data;
|
||||
irq_latch = data;
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[5] = data;
|
||||
if (nes.GetScanline() < 240)
|
||||
{
|
||||
irq_counter |= 0x80;
|
||||
irq_preset = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter |= 0x80;
|
||||
irq_preset_vbl = 0xFF;
|
||||
irq_preset = 0;
|
||||
}
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[6] = data;
|
||||
irq_enable = 0;
|
||||
irq_request = 0;
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[7] = data;
|
||||
irq_enable = 1;
|
||||
irq_request = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper012::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_preset_vbl != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_preset_vbl = 0;
|
||||
}
|
||||
if (irq_preset != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_preset = 0;
|
||||
}
|
||||
else if (irq_counter > 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
// Some game set irq_latch to zero to disable irq. So check it here.
|
||||
if (irq_enable != 0 && irq_latch != 0)
|
||||
{
|
||||
irq_request = 0xFF;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
irq_preset = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_CPU()
|
||||
{
|
||||
if ((reg[0] & 0x40) != 0)
|
||||
{
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(
|
||||
(int)(vb0 + chr4),
|
||||
(int)(vb0 + chr5),
|
||||
(int)(vb0 + chr6),
|
||||
(int)(vb0 + chr7),
|
||||
(int)(vb1 + chr01),
|
||||
(int)(vb1 + chr01 + 1),
|
||||
(int)(vb1 + chr23),
|
||||
(int)(vb1 + chr23 + 1)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_8K_Bank(
|
||||
(int)(vb0 + chr01),
|
||||
(int)(vb0 + chr01 + 1),
|
||||
(int)(vb0 + chr23),
|
||||
(int)(vb0 + chr23 + 1),
|
||||
(int)(vb1 + chr4),
|
||||
(int)(vb1 + chr5),
|
||||
(int)(vb1 + chr6),
|
||||
(int)(vb1 + chr7))
|
||||
;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(0, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(1, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(2, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(3, chr7 & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(4, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(5, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(6, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(7, chr7 & 0x07);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper012::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[8] = prg0;
|
||||
p[9] = prg1;
|
||||
p[10] = chr01;
|
||||
p[11] = chr23;
|
||||
p[12] = chr4;
|
||||
p[13] = chr5;
|
||||
p[14] = chr6;
|
||||
p[15] = chr7;
|
||||
p[16] = irq_enable;
|
||||
p[17] = (BYTE)irq_counter;
|
||||
p[18] = irq_latch;
|
||||
p[19] = irq_request;
|
||||
p[20] = irq_preset;
|
||||
p[21] = irq_preset_vbl;
|
||||
//*((DWORD*)&p[22]) = vb0;
|
||||
BitConverter.GetBytes(vb0).CopyTo(p, 22);
|
||||
//*((DWORD*)&p[26]) = vb1;
|
||||
BitConverter.GetBytes(vb1).CopyTo(p, 26);
|
||||
}
|
||||
|
||||
//void Mapper012::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
prg0 = p[8];
|
||||
prg1 = p[9];
|
||||
chr01 = p[10];
|
||||
chr23 = p[11];
|
||||
chr4 = p[12];
|
||||
chr5 = p[13];
|
||||
chr6 = p[14];
|
||||
chr7 = p[15];
|
||||
irq_enable = p[16];
|
||||
irq_counter = (byte)p[17];
|
||||
irq_latch = p[18];
|
||||
irq_request = p[19];
|
||||
irq_preset = p[20];
|
||||
irq_preset_vbl = p[21];
|
||||
//vb0 = *((DWORD*)&p[22]);
|
||||
vb0 = BitConverter.ToUInt32(p, 22);
|
||||
//vb1 = *((DWORD*)&p[26]);
|
||||
vb1 = BitConverter.ToUInt32(p, 26);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
30
Core/VirtualNes.Core/Mapper/Mapper013.cs
Normal file
30
Core/VirtualNes.Core/Mapper/Mapper013.cs
Normal file
@ -0,0 +1,30 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper013 CPROM //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper013 : Mapper
|
||||
{
|
||||
|
||||
public Mapper013(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 2, 3);
|
||||
SetCRAM_4K_Bank(0, 0);
|
||||
SetCRAM_4K_Bank(4, 0);
|
||||
}
|
||||
|
||||
//void Mapper013::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_32K_Bank((data & 0x30) >> 4);
|
||||
SetCRAM_4K_Bank(4, data & 0x03);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
90
Core/VirtualNes.Core/Mapper/Mapper015.cs
Normal file
90
Core/VirtualNes.Core/Mapper/Mapper015.cs
Normal file
@ -0,0 +1,90 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper015 100-in-1 chip //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper015 : Mapper
|
||||
{
|
||||
|
||||
public Mapper015(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 2, 3);
|
||||
}
|
||||
|
||||
//void Mapper015::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 1);
|
||||
SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 0);
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 3);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 0);
|
||||
SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 1);
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 2);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 3);
|
||||
}
|
||||
if ((data & 0x40) != 0)
|
||||
SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
case 0x8001:
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 1);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 0);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 1);
|
||||
}
|
||||
break;
|
||||
case 0x8002:
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 1);
|
||||
SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 1);
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 1);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 0);
|
||||
SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 0);
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 0);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 0);
|
||||
}
|
||||
break;
|
||||
case 0x8003:
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 1);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 0);
|
||||
SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 1);
|
||||
}
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
438
Core/VirtualNes.Core/Mapper/Mapper016.cs
Normal file
438
Core/VirtualNes.Core/Mapper/Mapper016.cs
Normal file
@ -0,0 +1,438 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper016 Bandai Standard //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper016 : Mapper
|
||||
{
|
||||
BYTE patch; // For Famicom Jump 2
|
||||
BYTE eeprom_type; // EEPROM type
|
||||
|
||||
BYTE[] reg = new byte[3];
|
||||
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
INT irq_latch;
|
||||
BYTE irq_type;
|
||||
|
||||
X24C01 x24c01;
|
||||
X24C02 x24c02;
|
||||
public Mapper016(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
reg[0] = reg[1] = reg[2] = 0;
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
|
||||
irq_type = 0;
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK);
|
||||
|
||||
eeprom_type = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x3f15d20d // Famicom Jump 2(J)
|
||||
|| crc == 0xf76aa523)
|
||||
{ // Famicom Jump 2(J)(alt)
|
||||
patch = 1;
|
||||
eeprom_type = 0xFF;
|
||||
|
||||
WRAM[0x0BBC] = 0xFF; // SRAM対策
|
||||
}
|
||||
|
||||
if (crc == 0x1d6f27f7)
|
||||
{ // Dragon Ball Z 2(Korean Hack)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
eeprom_type = 1;
|
||||
}
|
||||
if (crc == 0x6f7247c8)
|
||||
{ // Dragon Ball Z 3(Korean Hack)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK);
|
||||
eeprom_type = 1;
|
||||
}
|
||||
|
||||
if (crc == 0x7fb799fd)
|
||||
{ // Dragon Ball 2 - Dai Maou Fukkatsu(J)
|
||||
}
|
||||
if (crc == 0x6c6c2feb // Dragon Ball 3 - Gokuu Den(J)
|
||||
|| crc == 0x8edeb257)
|
||||
{ // Dragon Ball 3 - Gokuu Den(J)(Alt)
|
||||
}
|
||||
if (crc == 0x31cd9903)
|
||||
{ // Dragon Ball Z - Kyoushuu! Saiya Jin(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
}
|
||||
if (crc == 0xe49fc53e // Dragon Ball Z 2 - Gekishin Freeza!!(J)
|
||||
|| crc == 0x1582fee0)
|
||||
{ // Dragon Ball Z 2 - Gekishin Freeza!!(J) [alt]
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
eeprom_type = 1;
|
||||
}
|
||||
if (crc == 0x09499f4d)
|
||||
{ // Dragon Ball Z 3 - Ressen Jinzou Ningen(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
eeprom_type = 1;
|
||||
}
|
||||
if (crc == 0x2e991109)
|
||||
{ // Dragon Ball Z Gaiden - Saiya Jin Zetsumetsu Keikaku (J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
eeprom_type = 1;
|
||||
}
|
||||
if (crc == 0x146fb9c3)
|
||||
{ // SD Gundam Gaiden - Knight Gundam Monogatari(J)
|
||||
}
|
||||
|
||||
if (crc == 0x73ac76db // SD Gundam Gaiden - Knight Gundam Monogatari 2 - Hikari no Kishi(J)
|
||||
|| crc == 0x81a15eb8)
|
||||
{ // SD Gundam Gaiden - Knight Gundam Monogatari 3 - Densetsu no Kishi Dan(J)
|
||||
eeprom_type = 1;
|
||||
}
|
||||
if (crc == 0x170250de)
|
||||
{ // Rokudenashi Blues(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
eeprom_type = 1;
|
||||
}
|
||||
|
||||
// DATACH系
|
||||
if (crc == 0x0be0a328 // Datach - SD Gundam - Gundam Wars(J)
|
||||
|| crc == 0x19e81461 // Datach - Dragon Ball Z - Gekitou Tenkaichi Budou Kai(J)
|
||||
|| crc == 0x5b457641 // Datach - Ultraman Club - Supokon Fight!(J)
|
||||
|| crc == 0x894efdbc // Datach - Crayon Shin Chan - Ora to Poi Poi(J)
|
||||
|| crc == 0x983d8175 // Datach - Battle Rush - Build Up Robot Tournament(J)
|
||||
|| crc == 0xbe06853f)
|
||||
{ // Datach - J League Super Top Players(J)
|
||||
eeprom_type = 2;
|
||||
}
|
||||
if (crc == 0xf51a7f46)
|
||||
{ // Datach - Yuu Yuu Hakusho - Bakutou Ankoku Bujutsu Kai(J)
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC);
|
||||
eeprom_type = 2;
|
||||
}
|
||||
|
||||
if (eeprom_type == 0)
|
||||
{
|
||||
nes.SetSAVERAM_SIZE(128);
|
||||
x24c01.Reset(WRAM);
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 1)
|
||||
{
|
||||
nes.SetSAVERAM_SIZE(256);
|
||||
x24c02.Reset(WRAM);
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 2)
|
||||
{
|
||||
nes.SetSAVERAM_SIZE(384);
|
||||
x24c02.Reset(WRAM);
|
||||
x24c01.Reset(new ArrayRef<byte>(WRAM, 256));
|
||||
}
|
||||
}
|
||||
|
||||
//BYTE Mapper016::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
if (patch != 0)
|
||||
{
|
||||
return base.ReadLow(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((addr & 0x00FF) == 0x0000)
|
||||
{
|
||||
BYTE ret = 0;
|
||||
if (eeprom_type == 0)
|
||||
{
|
||||
ret = x24c01.Read();
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 1)
|
||||
{
|
||||
ret = x24c02.Read();
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 2)
|
||||
{
|
||||
ret = (byte)(x24c02.Read() & x24c01.Read());
|
||||
}
|
||||
return (byte)((ret != 0 ? 0x10 : 0) | (nes.GetBarcodeStatus()));
|
||||
}
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
//void Mapper016::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (patch == 0)
|
||||
{
|
||||
WriteSubA(addr, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.WriteLow(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (patch == 0)
|
||||
{
|
||||
WriteSubA(addr, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteSubB(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
static BYTE eeprom_addinc;
|
||||
|
||||
// Normal mapper #16
|
||||
void WriteSubA(ushort addr, BYTE data)
|
||||
{
|
||||
switch (addr & 0x000F)
|
||||
{
|
||||
case 0x0000:
|
||||
case 0x0001:
|
||||
case 0x0002:
|
||||
case 0x0003:
|
||||
case 0x0004:
|
||||
case 0x0005:
|
||||
case 0x0006:
|
||||
case 0x0007:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank((byte)(addr & 0x0007), data);
|
||||
}
|
||||
if (eeprom_type == 2)
|
||||
{
|
||||
reg[0] = data;
|
||||
x24c01.Write((byte)((data & 0x08) != 0 ? 0xFF : 0), (byte)((reg[1] & 0x40) != 0 ? 0xFF : 0));
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0008:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0x0009:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0x000A:
|
||||
irq_enable = (byte)(data & 0x01);
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x000B:
|
||||
irq_latch = (irq_latch & 0xFF00) | data;
|
||||
irq_counter = (irq_counter & 0xFF00) | data;
|
||||
break;
|
||||
case 0x000C:
|
||||
irq_latch = (data << 8) | (irq_latch & 0x00FF);
|
||||
irq_counter = (data << 8) | (irq_counter & 0x00FF);
|
||||
break;
|
||||
|
||||
case 0x000D:
|
||||
// EEPTYPE0(DragonBallZ)
|
||||
if (eeprom_type == 0)
|
||||
{
|
||||
x24c01.Write((byte)((data & 0x20) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0));
|
||||
}
|
||||
// EEPTYPE1(DragonBallZ2,Z3,Z Gaiden)
|
||||
if (eeprom_type == 1)
|
||||
{
|
||||
x24c02.Write((byte)((data & 0x20) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0));
|
||||
}
|
||||
// EEPTYPE2(DATACH)
|
||||
if (eeprom_type == 2)
|
||||
{
|
||||
reg[1] = data;
|
||||
x24c02.Write((byte)((data & 0x20) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0));
|
||||
x24c01.Write((byte)((reg[0] & 0x08) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Famicom Jump 2
|
||||
void WriteSubB(ushort addr, BYTE data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
case 0x8001:
|
||||
case 0x8002:
|
||||
case 0x8003:
|
||||
reg[0] = (byte)(data & 0x01);
|
||||
SetPROM_8K_Bank(4, reg[0] * 0x20 + reg[2] * 2 + 0);
|
||||
SetPROM_8K_Bank(5, reg[0] * 0x20 + reg[2] * 2 + 1);
|
||||
break;
|
||||
case 0x8004:
|
||||
case 0x8005:
|
||||
case 0x8006:
|
||||
case 0x8007:
|
||||
reg[1] = (byte)(data & 0x01);
|
||||
SetPROM_8K_Bank(6, reg[1] * 0x20 + 0x1E);
|
||||
SetPROM_8K_Bank(7, reg[1] * 0x20 + 0x1F);
|
||||
break;
|
||||
case 0x8008:
|
||||
reg[2] = data;
|
||||
SetPROM_8K_Bank(4, reg[0] * 0x20 + reg[2] * 2 + 0);
|
||||
SetPROM_8K_Bank(5, reg[0] * 0x20 + reg[2] * 2 + 1);
|
||||
SetPROM_8K_Bank(6, reg[1] * 0x20 + 0x1E);
|
||||
SetPROM_8K_Bank(7, reg[1] * 0x20 + 0x1F);
|
||||
break;
|
||||
|
||||
case 0x8009:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0x800A:
|
||||
irq_enable = (byte)(data & 0x01);
|
||||
irq_counter = irq_latch;
|
||||
|
||||
// if( !irq_enable ) {
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
// }
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x800B:
|
||||
irq_latch = (irq_latch & 0xFF00) | data;
|
||||
break;
|
||||
case 0x800C:
|
||||
irq_latch = (data << 8) | (irq_latch & 0x00FF);
|
||||
break;
|
||||
|
||||
case 0x800D:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_HSYNC))
|
||||
{
|
||||
if (irq_counter <= 113)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
// nes.cpu.IRQ();
|
||||
//// nes.cpu.IRQ_NotPending();
|
||||
// irq_enable = 0;
|
||||
// irq_counter = 0;
|
||||
irq_counter &= 0xFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter -= 113;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_CLOCK))
|
||||
{
|
||||
if ((irq_counter -= cycles) <= 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
// nes.cpu.IRQ();
|
||||
//// nes.cpu.IRQ_NotPending();
|
||||
// irq_enable = 0;
|
||||
// irq_counter = 0;
|
||||
irq_counter &= 0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe override void SaveState(byte[] buffer)
|
||||
{
|
||||
fixed (byte* p = buffer)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = irq_enable;
|
||||
*(INT*)&p[4] = irq_counter;
|
||||
*(INT*)&p[8] = irq_latch;
|
||||
|
||||
if (eeprom_type == 0)
|
||||
{
|
||||
x24c01.Save(&p[16]);
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 1)
|
||||
{
|
||||
x24c02.Save(&p[16]);
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 2)
|
||||
{
|
||||
x24c02.Save(&p[16]);
|
||||
x24c01.Save(&p[48]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe override void LoadState(byte[] buffer)
|
||||
{
|
||||
fixed (byte* p = buffer)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
irq_enable = p[3];
|
||||
irq_counter = *(INT*)&p[4];
|
||||
irq_latch = *(INT*)&p[8];
|
||||
if (eeprom_type == 0)
|
||||
{
|
||||
x24c01.Load(&p[16]);
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 1)
|
||||
{
|
||||
//x24c02.Load(&p[16]);
|
||||
x24c02.Load(p + 16);
|
||||
}
|
||||
else
|
||||
if (eeprom_type == 2)
|
||||
{
|
||||
//x24c02.Load(&p[16]);
|
||||
//x24c01.Load(&p[48]);
|
||||
x24c02.Load(p + 16);
|
||||
x24c01.Load(p + 48);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
132
Core/VirtualNes.Core/Mapper/Mapper017.cs
Normal file
132
Core/VirtualNes.Core/Mapper/Mapper017.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper017 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
INT irq_latch;
|
||||
public Mapper017(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
}
|
||||
|
||||
//void Mapper017::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x42FE:
|
||||
if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
case 0x42FF:
|
||||
if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
|
||||
case 0x4501:
|
||||
irq_enable = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x4502:
|
||||
irq_latch = (irq_latch & 0xFF00) | data;
|
||||
break;
|
||||
case 0x4503:
|
||||
irq_latch = (irq_latch & 0x00FF) | ((INT)data << 8);
|
||||
irq_counter = irq_latch;
|
||||
irq_enable = 0xFF;
|
||||
break;
|
||||
|
||||
case 0x4504:
|
||||
case 0x4505:
|
||||
case 0x4506:
|
||||
case 0x4507:
|
||||
SetPROM_8K_Bank((byte)(addr & 0x07), data);
|
||||
break;
|
||||
|
||||
case 0x4510:
|
||||
case 0x4511:
|
||||
case 0x4512:
|
||||
case 0x4513:
|
||||
case 0x4514:
|
||||
case 0x4515:
|
||||
case 0x4516:
|
||||
case 0x4517:
|
||||
SetVROM_1K_Bank((byte)(addr & 0x07), data);
|
||||
break;
|
||||
|
||||
default:
|
||||
base.WriteLow(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper017::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (irq_counter >= 0xFFFF - 113)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
// nes.cpu.IRQ();
|
||||
// irq_counter = 0;
|
||||
// irq_enable = 0;
|
||||
irq_counter &= 0xFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter += 113;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper017::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
//*(INT*)&p[1] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 1);
|
||||
//*(INT*)&p[5] = irq_latch;
|
||||
BitConverter.GetBytes(irq_latch).CopyTo(p, 5);
|
||||
}
|
||||
|
||||
//void Mapper017::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
//irq_counter = *(INT*)&p[1];
|
||||
irq_counter = BitConverter.ToInt32(p, 1);
|
||||
//irq_latch = *(INT*)&p[5];
|
||||
irq_latch = BitConverter.ToInt32(p, 5);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
283
Core/VirtualNes.Core/Mapper/Mapper018.cs
Normal file
283
Core/VirtualNes.Core/Mapper/Mapper018.cs
Normal file
@ -0,0 +1,283 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper018 Jaleco SS8806 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper018 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[11];
|
||||
|
||||
BYTE irq_enable;
|
||||
BYTE irq_mode;
|
||||
INT irq_latch;
|
||||
INT irq_counter;
|
||||
public Mapper018(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (INT i = 0; i < 11; i++)
|
||||
{
|
||||
reg[i] = 0;
|
||||
}
|
||||
reg[2] = (byte)(PROM_8K_SIZE - 2);
|
||||
reg[3] = (byte)(PROM_8K_SIZE - 1);
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
irq_enable = 0;
|
||||
irq_mode = 0;
|
||||
irq_counter = 0xFFFF;
|
||||
irq_latch = 0xFFFF;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xefb1df9e)
|
||||
{ // The Lord of King(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
if (crc == 0x3746f951)
|
||||
{ // Pizza Pop!(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
|
||||
// nes.SetRenderMethod( NES::PRE_ALL_RENDER );
|
||||
// nes.SetRenderMethod( NES::POST_ALL_RENDER );
|
||||
}
|
||||
|
||||
//void Mapper018::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F));
|
||||
SetPROM_8K_Bank(4, reg[0]);
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetPROM_8K_Bank(4, reg[0]);
|
||||
break;
|
||||
case 0x8002:
|
||||
reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F));
|
||||
SetPROM_8K_Bank(5, reg[1]);
|
||||
break;
|
||||
case 0x8003:
|
||||
reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetPROM_8K_Bank(5, reg[1]);
|
||||
break;
|
||||
case 0x9000:
|
||||
reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F));
|
||||
SetPROM_8K_Bank(6, reg[2]);
|
||||
break;
|
||||
case 0x9001:
|
||||
reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetPROM_8K_Bank(6, reg[2]);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(0, reg[3]);
|
||||
break;
|
||||
case 0xA001:
|
||||
reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(0, reg[3]);
|
||||
break;
|
||||
case 0xA002:
|
||||
reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(1, reg[4]);
|
||||
break;
|
||||
case 0xA003:
|
||||
reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(1, reg[4]);
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(2, reg[5]);
|
||||
break;
|
||||
case 0xB001:
|
||||
reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(2, reg[5]);
|
||||
break;
|
||||
case 0xB002:
|
||||
reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(3, reg[6]);
|
||||
break;
|
||||
case 0xB003:
|
||||
reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(3, reg[6]);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(4, reg[7]);
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(4, reg[7]);
|
||||
break;
|
||||
case 0xC002:
|
||||
reg[8] = (byte)((reg[8] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(5, reg[8]);
|
||||
break;
|
||||
case 0xC003:
|
||||
reg[8] = (byte)((reg[8] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(5, reg[8]);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
reg[9] = (byte)((reg[9] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(6, reg[9]);
|
||||
break;
|
||||
case 0xD001:
|
||||
reg[9] = (byte)((reg[9] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(6, reg[9]);
|
||||
break;
|
||||
case 0xD002:
|
||||
reg[10] = (byte)((reg[10] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(7, reg[10]);
|
||||
break;
|
||||
case 0xD003:
|
||||
reg[10] = (byte)((reg[10] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(7, reg[10]);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
irq_latch = (irq_latch & 0xFFF0) | (data & 0x0F);
|
||||
break;
|
||||
case 0xE001:
|
||||
irq_latch = (irq_latch & 0xFF0F) | ((data & 0x0F) << 4);
|
||||
break;
|
||||
case 0xE002:
|
||||
irq_latch = (irq_latch & 0xF0FF) | ((data & 0x0F) << 8);
|
||||
break;
|
||||
case 0xE003:
|
||||
irq_latch = (irq_latch & 0x0FFF) | ((data & 0x0F) << 12);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
// if( data & 0x01 ) {
|
||||
irq_counter = irq_latch;
|
||||
// } else {
|
||||
// irq_counter = 0;
|
||||
// }
|
||||
break;
|
||||
case 0xF001:
|
||||
irq_mode = (byte)((data >> 1) & 0x07);
|
||||
irq_enable = ((byte)(data & 0x01));
|
||||
// if( !irq_enable ) {
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
// }
|
||||
break;
|
||||
|
||||
case 0xF002:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper018::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
bool bIRQ = false;
|
||||
INT irq_counter_old = irq_counter;
|
||||
|
||||
if (irq_enable != 0 && irq_counter != 0)
|
||||
{
|
||||
irq_counter -= cycles;
|
||||
|
||||
switch (irq_mode)
|
||||
{
|
||||
case 0:
|
||||
if (irq_counter <= 0)
|
||||
{
|
||||
bIRQ = true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if ((irq_counter & 0xF000) != (irq_counter_old & 0xF000))
|
||||
{
|
||||
bIRQ = true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if ((irq_counter & 0xFF00) != (irq_counter_old & 0xFF00))
|
||||
{
|
||||
bIRQ = true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
if ((irq_counter & 0xFFF0) != (irq_counter_old & 0xFFF0))
|
||||
{
|
||||
bIRQ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (bIRQ)
|
||||
{
|
||||
//// irq_enable = 0;
|
||||
// irq_counter = irq_latch;
|
||||
irq_counter = 0;
|
||||
irq_enable = 0;
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper018::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 11; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[11] = irq_enable;
|
||||
p[12] = irq_mode;
|
||||
//*(INT*)&p[13] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 13);
|
||||
//*(INT*)&p[17] = irq_latch;
|
||||
BitConverter.GetBytes(irq_latch).CopyTo(p, 17);
|
||||
}
|
||||
|
||||
//void Mapper018::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 11; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
irq_enable = p[11];
|
||||
irq_mode = p[12];
|
||||
//irq_counter = *(INT*)&p[13];
|
||||
irq_counter = BitConverter.ToInt32(p, 13);
|
||||
//irq_latch = *(INT*)&p[17];
|
||||
irq_latch = BitConverter.ToInt32(p, 17);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
421
Core/VirtualNes.Core/Mapper/Mapper019.cs
Normal file
421
Core/VirtualNes.Core/Mapper/Mapper019.cs
Normal file
@ -0,0 +1,421 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper019 Namcot 106 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper019 : Mapper
|
||||
{
|
||||
BYTE patch;
|
||||
BYTE exsound_enable;
|
||||
|
||||
BYTE[] reg = new byte[3];
|
||||
BYTE[] exram = new byte[128];
|
||||
|
||||
BYTE irq_enable;
|
||||
ushort irq_counter;
|
||||
public Mapper019(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
reg[0] = reg[1] = reg[2] = 0;
|
||||
|
||||
MemoryUtility.ZEROMEMORY(exram, exram.Length);
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE >= 8)
|
||||
{
|
||||
SetVROM_8K_Bank(VROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
exsound_enable = 0xFF;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xb62a7b71)
|
||||
{ // Family Circuit '91(J)
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
if (crc == 0x02738c68)
|
||||
{ // Wagan Land 2(J)
|
||||
patch = 3;
|
||||
}
|
||||
if (crc == 0x14942c06)
|
||||
{ // Wagan Land 3(J)
|
||||
patch = 2;
|
||||
}
|
||||
|
||||
if (crc == 0x968dcf09)
|
||||
{ // Final Lap(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
if (crc == 0x3deac303)
|
||||
{ // Rolling Thunder(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xb1b9e187)
|
||||
{ // For Kaijuu Monogatari(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0x6901346e)
|
||||
{ // For Sangokushi 2 - Haou no Tairiku(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
|
||||
// if( crc == 0xdd454208 ) { // Hydlide 3(J)
|
||||
// nes.SetRenderMethod( NES::PRE_ALL_RENDER );
|
||||
// }
|
||||
|
||||
if (crc == 0xaf15338f // For Mindseeker(J)
|
||||
|| crc == 0xb1b9e187 // For Kaijuu Monogatari(J)
|
||||
|| crc == 0x96533999 // Dokuganryuu Masamune(J)
|
||||
// || crc == 0x2b825ce1 // Namco Classic(J)
|
||||
// || crc == 0x9a2b0641 // Namco Classic 2(J)
|
||||
|| crc == 0x3296ff7a // Battle Fleet(J)
|
||||
|| crc == 0xdd454208)
|
||||
{ // Hydlide 3(J)
|
||||
exsound_enable = 0;
|
||||
}
|
||||
|
||||
if (crc == 0x429fd177)
|
||||
{ // Famista '90(J)
|
||||
exsound_enable = 0;
|
||||
}
|
||||
|
||||
if (exsound_enable != 0)
|
||||
{
|
||||
nes.apu.SelectExSound(0x10);
|
||||
}
|
||||
}
|
||||
|
||||
//BYTE Mapper019::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
BYTE data = 0;
|
||||
|
||||
switch (addr & 0xF800)
|
||||
{
|
||||
case 0x4800:
|
||||
if (addr == 0x4800)
|
||||
{
|
||||
if (exsound_enable != 0)
|
||||
{
|
||||
nes.apu.ExRead(addr);
|
||||
data = exram[reg[2] & 0x7F];
|
||||
}
|
||||
else
|
||||
{
|
||||
data = WRAM[reg[2] & 0x7F];
|
||||
}
|
||||
if ((reg[2] & 0x80) != 0)
|
||||
reg[2] = (byte)((reg[2] + 1) | 0x80);
|
||||
return data;
|
||||
}
|
||||
break;
|
||||
case 0x5000:
|
||||
return (byte)((BYTE)irq_counter & 0x00FF);
|
||||
case 0x5800:
|
||||
return (BYTE)((irq_counter >> 8) & 0x7F);
|
||||
case 0x6000:
|
||||
case 0x6800:
|
||||
case 0x7000:
|
||||
case 0x7800:
|
||||
return base.ReadLow(addr);
|
||||
}
|
||||
|
||||
return (BYTE)(addr >> 8);
|
||||
}
|
||||
|
||||
//void Mapper019::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF800)
|
||||
{
|
||||
case 0x4800:
|
||||
if (addr == 0x4800)
|
||||
{
|
||||
if (exsound_enable != 0)
|
||||
{
|
||||
nes.apu.ExWrite(addr, data);
|
||||
exram[reg[2] & 0x7F] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
WRAM[reg[2] & 0x7F] = data;
|
||||
}
|
||||
if ((reg[2] & 0x80) != 0)
|
||||
reg[2] = (byte)((reg[2] + 1) | 0x80);
|
||||
}
|
||||
break;
|
||||
case 0x5000:
|
||||
irq_counter = (byte)((irq_counter & 0xFF00) | (ushort)data);
|
||||
// if( irq_enable ) {
|
||||
// irq_counter++;
|
||||
// }
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x5800:
|
||||
irq_counter = (byte)((irq_counter & 0x00FF) | ((ushort)(data & 0x7F) << 8));
|
||||
irq_enable = (byte)(data & 0x80);
|
||||
// if( irq_enable ) {
|
||||
// irq_counter++;
|
||||
// }
|
||||
// if( !irq_enable ) {
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
// }
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x6000:
|
||||
case 0x6800:
|
||||
case 0x7000:
|
||||
case 0x7800:
|
||||
base.WriteLow(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper019::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//if( addr >= 0xC000 ) {
|
||||
//DEBUGOUT( "W %04X %02X L:%3d\n", addr, data, nes.GetScanline() );
|
||||
//}
|
||||
switch (addr & 0xF800)
|
||||
{
|
||||
case 0x8000:
|
||||
if ((data < 0xE0) || (reg[0] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(0, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(0, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0x8800:
|
||||
if ((data < 0xE0) || (reg[0] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(1, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(1, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0x9000:
|
||||
if ((data < 0xE0) || (reg[0] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(2, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(2, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0x9800:
|
||||
if ((data < 0xE0) || (reg[0] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(3, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(3, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
if ((data < 0xE0) || (reg[1] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(4, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(4, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0xA800:
|
||||
if ((data < 0xE0) || (reg[1] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(5, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(5, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0xB000:
|
||||
if ((data < 0xE0) || (reg[1] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(6, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0xB800:
|
||||
if ((data < 0xE0) || (reg[1] != 0))
|
||||
{
|
||||
SetVROM_1K_Bank(7, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(7, data & 0x1F);
|
||||
}
|
||||
break;
|
||||
case 0xC000:
|
||||
if (patch == 0)
|
||||
{
|
||||
if (data <= 0xDF)
|
||||
{
|
||||
SetVROM_1K_Bank(8, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVRAM_1K_Bank(8, data & 0x01);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xC800:
|
||||
if (patch == 0)
|
||||
{
|
||||
if (data <= 0xDF)
|
||||
{
|
||||
SetVROM_1K_Bank(9, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVRAM_1K_Bank(9, data & 0x01);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xD000:
|
||||
if (patch == 0)
|
||||
{
|
||||
if (data <= 0xDF)
|
||||
{
|
||||
SetVROM_1K_Bank(10, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVRAM_1K_Bank(10, data & 0x01);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xD800:
|
||||
if (patch == 0)
|
||||
{
|
||||
if (data <= 0xDF)
|
||||
{
|
||||
SetVROM_1K_Bank(11, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVRAM_1K_Bank(11, data & 0x01);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xE000:
|
||||
SetPROM_8K_Bank(4, data & 0x3F);
|
||||
if (patch == 2)
|
||||
{
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
if (patch == 3)
|
||||
{
|
||||
if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
case 0xE800:
|
||||
reg[0] = (byte)(data & 0x40);
|
||||
reg[1] = (byte)(data & 0x80);
|
||||
SetPROM_8K_Bank(5, data & 0x3F);
|
||||
break;
|
||||
case 0xF000:
|
||||
SetPROM_8K_Bank(6, data & 0x3F);
|
||||
break;
|
||||
case 0xF800:
|
||||
if (addr == 0xF800)
|
||||
{
|
||||
if (exsound_enable != 0)
|
||||
{
|
||||
nes.apu.ExWrite(addr, data);
|
||||
}
|
||||
reg[2] = data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper019::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
irq_counter = (ushort)(irq_counter + cycles);
|
||||
if (irq_counter >= 0x7FFF)
|
||||
{
|
||||
// irq_counter = 0x7FFF;
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
|
||||
irq_enable = 0;
|
||||
// irq_counter &= 0x7FFF;
|
||||
irq_counter = 0x7FFF;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper019::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = irq_enable;
|
||||
//*(WORD*)&p[4] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 4);
|
||||
|
||||
//::memcpy(&p[8], exram, sizeof(exram));
|
||||
Array.Copy(exram, p, exram.Length);
|
||||
|
||||
}
|
||||
|
||||
//void Mapper019::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
irq_enable = p[3];
|
||||
//irq_counter = *(WORD*)&p[4];
|
||||
irq_counter = BitConverter.ToUInt16(p, 4);
|
||||
//::memcpy(exram, &p[8], sizeof(exram));
|
||||
Array.Copy(p, exram, exram.Length);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
258
Core/VirtualNes.Core/Mapper/Mapper021.cs
Normal file
258
Core/VirtualNes.Core/Mapper/Mapper021.cs
Normal file
@ -0,0 +1,258 @@
|
||||
//////////////////
|
||||
// Mapper021 Konami VRC4 (Address mask $F006 or $F0C0) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper021 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[9];
|
||||
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
INT irq_clock;
|
||||
public Mapper021(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (byte i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = i;
|
||||
}
|
||||
reg[8] = 0;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
//void Mapper021::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF0CF)
|
||||
{
|
||||
case 0x8000:
|
||||
if ((reg[8] & 0x02) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0x9002:
|
||||
case 0x9080:
|
||||
reg[8] = data;
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
case 0xB002:
|
||||
case 0xB040:
|
||||
reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
|
||||
case 0xB001:
|
||||
case 0xB004:
|
||||
case 0xB080:
|
||||
reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
case 0xB003:
|
||||
case 0xB006:
|
||||
case 0xB0C0:
|
||||
reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
case 0xC002:
|
||||
case 0xC040:
|
||||
reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
|
||||
case 0xC001:
|
||||
case 0xC004:
|
||||
case 0xC080:
|
||||
reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
case 0xC003:
|
||||
case 0xC006:
|
||||
case 0xC0C0:
|
||||
reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
case 0xD002:
|
||||
case 0xD040:
|
||||
reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
|
||||
case 0xD001:
|
||||
case 0xD004:
|
||||
case 0xD080:
|
||||
reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
case 0xD003:
|
||||
case 0xD006:
|
||||
case 0xD0C0:
|
||||
reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
case 0xE002:
|
||||
case 0xE040:
|
||||
reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
case 0xE004:
|
||||
case 0xE080:
|
||||
reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
case 0xE003:
|
||||
case 0xE006:
|
||||
case 0xE0C0:
|
||||
reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F));
|
||||
break;
|
||||
case 0xF002:
|
||||
case 0xF040:
|
||||
irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4));
|
||||
break;
|
||||
|
||||
case 0xF003:
|
||||
case 0xF0C0:
|
||||
case 0xF006:
|
||||
irq_enable = (byte)((irq_enable & 0x01) * 3);
|
||||
irq_clock = 0;
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF004:
|
||||
case 0xF080:
|
||||
irq_enable = (byte)(data & 0x03);
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
}
|
||||
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
// case 0xF006:
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper021::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
if ((irq_clock -= cycles) < 0)
|
||||
{
|
||||
irq_clock += 0x72;
|
||||
if (irq_counter == 0xFF)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
// irq_enable = (irq_enable & 0x01) * 3;
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper021::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 9; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[9] = irq_enable;
|
||||
p[10] = irq_counter;
|
||||
p[11] = irq_latch;
|
||||
//*(INT*)&p[12] = irq_clock;
|
||||
BitConverter.GetBytes(irq_clock).CopyTo(p, 12);
|
||||
}
|
||||
|
||||
//void Mapper021::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 9; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
irq_enable = p[9];
|
||||
irq_counter = p[10];
|
||||
irq_latch = p[11];
|
||||
//irq_clock = *(INT*)&p[12];
|
||||
irq_clock = BitConverter.ToInt32(p, 12);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
78
Core/VirtualNes.Core/Mapper/Mapper022.cs
Normal file
78
Core/VirtualNes.Core/Mapper/Mapper022.cs
Normal file
@ -0,0 +1,78 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper022 Konami VRC2 type A //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper022 : Mapper
|
||||
{
|
||||
public Mapper022(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
//void Mapper022::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
SetVROM_1K_Bank(0, data >> 1);
|
||||
break;
|
||||
|
||||
case 0xB001:
|
||||
SetVROM_1K_Bank(1, data >> 1);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
SetVROM_1K_Bank(2, data >> 1);
|
||||
break;
|
||||
|
||||
case 0xC001:
|
||||
SetVROM_1K_Bank(3, data >> 1);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
SetVROM_1K_Bank(4, data >> 1);
|
||||
break;
|
||||
|
||||
case 0xD001:
|
||||
SetVROM_1K_Bank(5, data >> 1);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
SetVROM_1K_Bank(6, data >> 1);
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
SetVROM_1K_Bank(7, data >> 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
274
Core/VirtualNes.Core/Mapper/Mapper023.cs
Normal file
274
Core/VirtualNes.Core/Mapper/Mapper023.cs
Normal file
@ -0,0 +1,274 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper023 Konami VRC2 type B //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper023 : Mapper
|
||||
{
|
||||
|
||||
ushort addrmask;
|
||||
|
||||
BYTE[] reg = new byte[9];
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
INT irq_clock;
|
||||
public Mapper023(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
addrmask = 0xFFFF;
|
||||
|
||||
for (byte i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = i;
|
||||
}
|
||||
reg[8] = 0;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
reg[9] = 1;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
|
||||
// nes.SetRenderMethod( NES::POST_RENDER );
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x93794634 // Akumajou Special Boku Dracula Kun(J)
|
||||
|| crc == 0xc7829dae // Akumajou Special Boku Dracula Kun(T-Eng)
|
||||
|| crc == 0xf82dc02f)
|
||||
{ // Akumajou Special Boku Dracula Kun(T-Eng v1.02)
|
||||
addrmask = 0xF00C;
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
if (crc == 0xdd53c4ae)
|
||||
{ // Tiny Toon Adventures(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper023::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() );
|
||||
switch (addr & addrmask)
|
||||
{
|
||||
case 0x8000:
|
||||
case 0x8004:
|
||||
case 0x8008:
|
||||
case 0x800C:
|
||||
if (reg[8] != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
if (data != 0xFF)
|
||||
{
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9008:
|
||||
reg[8] = (byte)(data & 0x02);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
case 0xA004:
|
||||
case 0xA008:
|
||||
case 0xA00C:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
case 0xB001:
|
||||
case 0xB004:
|
||||
reg[0] = ((byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4)));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
|
||||
case 0xB002:
|
||||
case 0xB008:
|
||||
reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
|
||||
case 0xB003:
|
||||
case 0xB00C:
|
||||
reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
|
||||
case 0xC001:
|
||||
case 0xC004:
|
||||
reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
|
||||
case 0xC002:
|
||||
case 0xC008:
|
||||
reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
|
||||
case 0xC003:
|
||||
case 0xC00C:
|
||||
reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
|
||||
case 0xD001:
|
||||
case 0xD004:
|
||||
reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
|
||||
case 0xD002:
|
||||
case 0xD008:
|
||||
reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
|
||||
case 0xD003:
|
||||
case 0xD00C:
|
||||
reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
case 0xE004:
|
||||
reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
|
||||
case 0xE002:
|
||||
case 0xE008:
|
||||
reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
|
||||
case 0xE003:
|
||||
case 0xE00C:
|
||||
reg[7] = ((byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4)));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F));
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xF004:
|
||||
irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4));
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF008:
|
||||
irq_enable = (byte)(data & 0x03);
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF00C:
|
||||
irq_enable = (byte)((irq_enable & 0x01) * 3);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper023::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
irq_clock += cycles * 3;
|
||||
while (irq_clock >= 341)
|
||||
{
|
||||
irq_clock -= 341;
|
||||
irq_counter++;
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper023::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 9; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[9] = irq_enable;
|
||||
p[10] = irq_counter;
|
||||
p[11] = irq_latch;
|
||||
//*(INT*)&p[12] = irq_clock;
|
||||
BitConverter.GetBytes(irq_clock).CopyTo(p, 12);
|
||||
}
|
||||
|
||||
//void Mapper023::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 9; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
irq_enable = p[9];
|
||||
irq_counter = p[10];
|
||||
irq_latch = p[11];
|
||||
//irq_clock = *(INT*)&p[12];
|
||||
irq_clock = BitConverter.ToInt32(p, 12);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
177
Core/VirtualNes.Core/Mapper/Mapper024.cs
Normal file
177
Core/VirtualNes.Core/Mapper/Mapper024.cs
Normal file
@ -0,0 +1,177 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper024 Konami VRC6 (Normal) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper024 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
INT irq_clock;
|
||||
public Mapper024(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
// nes.SetRenderMethod( NES::PRE_RENDER );
|
||||
|
||||
nes.apu.SelectExSound(1);
|
||||
}
|
||||
|
||||
//void Mapper024::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF003)
|
||||
{
|
||||
case 0x8000:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
case 0x9001:
|
||||
case 0x9002:
|
||||
case 0xA000:
|
||||
case 0xA001:
|
||||
case 0xA002:
|
||||
case 0xB000:
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
nes.apu.ExWrite(addr, data);
|
||||
break;
|
||||
|
||||
case 0xB003:
|
||||
data = (byte)(data & 0x0C);
|
||||
if (data == 0x00) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 0x04) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 0x08) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else if (data == 0x0C) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
SetVROM_1K_Bank(0, data);
|
||||
break;
|
||||
|
||||
case 0xD001:
|
||||
SetVROM_1K_Bank(1, data);
|
||||
break;
|
||||
|
||||
case 0xD002:
|
||||
SetVROM_1K_Bank(2, data);
|
||||
break;
|
||||
|
||||
case 0xD003:
|
||||
SetVROM_1K_Bank(3, data);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
SetVROM_1K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
SetVROM_1K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0xE002:
|
||||
SetVROM_1K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0xE003:
|
||||
SetVROM_1K_Bank(7, data);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
case 0xF001:
|
||||
irq_enable = (byte)(data & 0x03);
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
}
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xF002:
|
||||
irq_enable = (byte)((irq_enable & 0x01) * 3);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper024::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
if ((irq_clock += cycles) >= 0x72)
|
||||
{
|
||||
irq_clock -= 0x72;
|
||||
if (irq_counter == 0xFF)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper024::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
p[1] = irq_counter;
|
||||
p[2] = irq_latch;
|
||||
//*(INT*)&p[3] = irq_clock;
|
||||
BitConverter.GetBytes(irq_clock).CopyTo(p, 3);
|
||||
}
|
||||
|
||||
//void Mapper024::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
irq_counter = p[1];
|
||||
irq_latch = p[2];
|
||||
//irq_clock = *(INT*)&p[3];
|
||||
irq_clock = BitConverter.ToInt32(p, 3);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
281
Core/VirtualNes.Core/Mapper/Mapper025.cs
Normal file
281
Core/VirtualNes.Core/Mapper/Mapper025.cs
Normal file
@ -0,0 +1,281 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper025 Konami VRC4 (Normal) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper025 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[11];
|
||||
BYTE irq_enable;
|
||||
BYTE irq_latch;
|
||||
BYTE irq_occur;
|
||||
BYTE irq_counter;
|
||||
INT irq_clock;
|
||||
public Mapper025(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (INT i = 0; i < 11; i++)
|
||||
{
|
||||
reg[i] = 0;
|
||||
}
|
||||
reg[9] = (byte)(PROM_8K_SIZE - 2);
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_occur = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0xc71d4ce7)
|
||||
{ // Gradius II(J)
|
||||
// nes.SetRenderMethod( NES::POST_RENDER );
|
||||
}
|
||||
if (crc == 0xa2e68da8)
|
||||
{ // For Racer Mini Yonku - Japan Cup(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xea74c587)
|
||||
{ // For Teenage Mutant Ninja Turtles(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x5f82cb7d)
|
||||
{ // For Teenage Mutant Ninja Turtles 2(J)
|
||||
}
|
||||
if (crc == 0x0bbd85ff)
|
||||
{ // For Bio Miracle Bokutte Upa(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper025::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//if( addr >= 0xF000 )
|
||||
//DEBUGOUT( "M25 WR $%04X=$%02X L=%3d\n", addr, data, nes.GetScanline() );
|
||||
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0x8000:
|
||||
if ((reg[10] & 0x02) != 0)
|
||||
{
|
||||
reg[9] = data;
|
||||
SetPROM_8K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
reg[8] = data;
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (addr & 0xF00F)
|
||||
{
|
||||
case 0x9000:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0x9001:
|
||||
case 0x9004:
|
||||
if ((reg[10] & 0x02) != (data & 0x02))
|
||||
{
|
||||
BYTE swap = reg[8];
|
||||
reg[8] = reg[9];
|
||||
reg[9] = swap;
|
||||
|
||||
SetPROM_8K_Bank(4, reg[8]);
|
||||
SetPROM_8K_Bank(6, reg[9]);
|
||||
}
|
||||
reg[10] = data;
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
case 0xB002:
|
||||
case 0xB008:
|
||||
reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
|
||||
case 0xB001:
|
||||
case 0xB004:
|
||||
reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
case 0xB003:
|
||||
case 0xB00C:
|
||||
reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
case 0xC002:
|
||||
case 0xC008:
|
||||
reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
|
||||
case 0xC001:
|
||||
case 0xC004:
|
||||
reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
case 0xC003:
|
||||
case 0xC00C:
|
||||
reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
case 0xD002:
|
||||
case 0xD008:
|
||||
reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
|
||||
case 0xD001:
|
||||
case 0xD004:
|
||||
reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
case 0xD003:
|
||||
case 0xD00C:
|
||||
reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
case 0xE002:
|
||||
case 0xE008:
|
||||
reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
case 0xE004:
|
||||
reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
case 0xE003:
|
||||
case 0xE00C:
|
||||
reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F));
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF002:
|
||||
case 0xF008:
|
||||
irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4));
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF001:
|
||||
case 0xF004:
|
||||
irq_enable = (byte)(data & 0x03);
|
||||
// irq_counter = 0x100 - irq_latch;
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF003:
|
||||
case 0xF00C:
|
||||
irq_enable = (byte)((irq_enable & 0x01) * 3);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper025::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
irq_clock += cycles * 3;
|
||||
while (irq_clock >= 341)
|
||||
{
|
||||
irq_clock -= 341;
|
||||
irq_counter++;
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper025::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 11; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[11] = irq_enable;
|
||||
p[12] = irq_occur;
|
||||
p[13] = irq_latch;
|
||||
p[14] = irq_counter;
|
||||
//*((INT*)&p[15]) = irq_clock;
|
||||
BitConverter.GetBytes(irq_clock).CopyTo(p, 15);
|
||||
}
|
||||
|
||||
//void Mapper025::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 11; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
irq_enable = p[11];
|
||||
irq_occur = p[12];
|
||||
irq_latch = p[13];
|
||||
irq_counter = p[14];
|
||||
//irq_clock = *((INT*)&p[15]);
|
||||
irq_clock = BitConverter.ToInt32(p, 15);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
188
Core/VirtualNes.Core/Mapper/Mapper026.cs
Normal file
188
Core/VirtualNes.Core/Mapper/Mapper026.cs
Normal file
@ -0,0 +1,188 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper026 Konami VRC6 (PA0,PA1 reverse) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper026 : Mapper
|
||||
{
|
||||
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
INT irq_clock;
|
||||
public Mapper026(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0x30e64d03)
|
||||
{ // Esper Dream 2 - Aratanaru Tatakai(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
if (crc == 0x836cc1ab)
|
||||
{ // Mouryou Senki Madara(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
nes.apu.SelectExSound(1);
|
||||
}
|
||||
|
||||
//void Mapper026::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF003)
|
||||
{
|
||||
case 0x8000:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
case 0x9001:
|
||||
case 0x9002:
|
||||
case 0x9003:
|
||||
case 0xA000:
|
||||
case 0xA001:
|
||||
case 0xA002:
|
||||
case 0xA003:
|
||||
case 0xB000:
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
addr = (ushort)((addr & 0xfffc) | ((addr & 1) << 1) | ((addr & 2) >> 1));
|
||||
nes.apu.ExWrite(addr, data);
|
||||
break;
|
||||
|
||||
case 0xB003:
|
||||
data = (byte)(data & 0x7F);
|
||||
if (data == 0x08 || data == 0x2C) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else if (data == 0x20) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 0x24) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 0x28) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
SetVROM_1K_Bank(0, data);
|
||||
break;
|
||||
|
||||
case 0xD001:
|
||||
SetVROM_1K_Bank(2, data);
|
||||
break;
|
||||
|
||||
case 0xD002:
|
||||
SetVROM_1K_Bank(1, data);
|
||||
break;
|
||||
|
||||
case 0xD003:
|
||||
SetVROM_1K_Bank(3, data);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
SetVROM_1K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
SetVROM_1K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0xE002:
|
||||
SetVROM_1K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0xE003:
|
||||
SetVROM_1K_Bank(7, data);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
case 0xF001:
|
||||
irq_enable = (byte)((irq_enable & 0x01) * 3);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xF002:
|
||||
irq_enable = (byte)(data & 0x03);
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
}
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper026::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
if ((irq_clock += cycles) >= 0x72)
|
||||
{
|
||||
irq_clock -= 0x72;
|
||||
if (irq_counter >= 0xFF)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
//// nes.cpu.IRQ();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper026::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
p[1] = irq_counter;
|
||||
p[2] = irq_latch;
|
||||
//*(INT*)&p[3] = irq_clock;
|
||||
BitConverter.GetBytes(irq_clock).CopyTo(p, 3);
|
||||
}
|
||||
|
||||
//void Mapper026::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
irq_counter = p[1];
|
||||
irq_latch = p[2];
|
||||
//irq_clock = *(INT*)&p[3];
|
||||
irq_clock = BitConverter.ToInt32(p, 3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
231
Core/VirtualNes.Core/Mapper/Mapper027.cs
Normal file
231
Core/VirtualNes.Core/Mapper/Mapper027.cs
Normal file
@ -0,0 +1,231 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper027 Konami VRC4 (World Hero) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper027 : Mapper
|
||||
{
|
||||
ushort[] reg = new ushort[9];
|
||||
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
INT irq_clock;
|
||||
public Mapper027(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = (byte)i;
|
||||
}
|
||||
reg[8] = 0;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0x47DCBCC4)
|
||||
{ // Gradius II(sample)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
if (crc == 0x468F21FC)
|
||||
{ // Racer Mini 4 ku(sample)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper027::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF0CF)
|
||||
{
|
||||
case 0x8000:
|
||||
if ((reg[8] & 0x02) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0x9002:
|
||||
case 0x9080:
|
||||
reg[8] = data;
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
reg[0] = (ushort)((reg[0] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
case 0xB001:
|
||||
reg[0] = (ushort)((reg[0] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
|
||||
case 0xB002:
|
||||
reg[1] = (ushort)((reg[1] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
case 0xB003:
|
||||
reg[1] = (ushort)((reg[1] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
reg[2] = (ushort)((reg[2] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[2] = (ushort)((reg[2] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
|
||||
case 0xC002:
|
||||
reg[3] = (ushort)((reg[3] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
case 0xC003:
|
||||
reg[3] = (ushort)((reg[3] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
reg[4] = (ushort)((reg[4] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
case 0xD001:
|
||||
reg[4] = (ushort)((reg[4] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
|
||||
case 0xD002:
|
||||
reg[5] = (ushort)((reg[5] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
case 0xD003:
|
||||
reg[5] = (ushort)((reg[5] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
reg[6] = (ushort)((reg[6] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[6] = (ushort)((reg[6] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
|
||||
case 0xE002:
|
||||
reg[7] = (ushort)((reg[7] & 0xFF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
case 0xE003:
|
||||
reg[7] = (ushort)((reg[7] & 0x0F) | (data << 4));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F));
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xF001:
|
||||
irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4));
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF003:
|
||||
irq_enable = (byte)((irq_enable & 0x01) * 3);
|
||||
irq_clock = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF002:
|
||||
irq_enable = (byte)(data & 0x03);
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
}
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper027::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
if (irq_counter == 0xFF)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper027::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 9; i++)
|
||||
{
|
||||
p[i] = (byte)reg[i];
|
||||
}
|
||||
p[9] = irq_enable;
|
||||
p[10] = irq_counter;
|
||||
p[11] = irq_latch;
|
||||
//*(INT*)&p[12] = irq_clock;
|
||||
BitConverter.GetBytes(irq_clock).CopyTo(p, 12);
|
||||
}
|
||||
|
||||
//void Mapper027::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 9; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
irq_enable = p[9];
|
||||
irq_counter = p[10];
|
||||
irq_latch = p[11];
|
||||
//irq_clock = *(INT*)&p[12];
|
||||
irq_clock = BitConverter.ToInt32(p, 12);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
121
Core/VirtualNes.Core/Mapper/Mapper032.cs
Normal file
121
Core/VirtualNes.Core/Mapper/Mapper032.cs
Normal file
@ -0,0 +1,121 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper032 Irem G101 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper032 : Mapper
|
||||
{
|
||||
BYTE patch;
|
||||
|
||||
BYTE reg;
|
||||
public Mapper032(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
reg = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
// For Major League(J)
|
||||
if (crc == 0xc0fed437)
|
||||
{
|
||||
patch = 1;
|
||||
}
|
||||
// For Ai Sensei no Oshiete - Watashi no Hoshi(J)
|
||||
if (crc == 0xfd3fc292)
|
||||
{
|
||||
SetPROM_32K_Bank(30, 31, 30, 31);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper032::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0x8000:
|
||||
if ((reg & 0x02) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
reg = data;
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (addr & 0xF007)
|
||||
{
|
||||
case 0xB000:
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
SetVROM_1K_Bank((byte)(addr & 0x0007), data);
|
||||
break;
|
||||
case 0xB006:
|
||||
SetVROM_1K_Bank(6, data);
|
||||
|
||||
if (patch != 0 && ((data & 0x40) != 0))
|
||||
{
|
||||
SetVRAM_Mirror(0, 0, 0, 1);
|
||||
}
|
||||
break;
|
||||
case 0xB007:
|
||||
SetVROM_1K_Bank(7, data);
|
||||
|
||||
if (patch != 0 && ((data & 0x40) != 0))
|
||||
{
|
||||
SetVRAM_Mirror(0, 0, 0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper032::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg;
|
||||
}
|
||||
|
||||
//void Mapper032::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg = p[0];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
260
Core/VirtualNes.Core/Mapper/Mapper033.cs
Normal file
260
Core/VirtualNes.Core/Mapper/Mapper033.cs
Normal file
@ -0,0 +1,260 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper033 Taito TC0190 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper033 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[7];
|
||||
|
||||
BYTE patch;
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
public Mapper033(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
|
||||
reg[0] = 0;
|
||||
reg[1] = 2;
|
||||
reg[2] = 4;
|
||||
reg[3] = 5;
|
||||
reg[4] = 6;
|
||||
reg[5] = 7;
|
||||
reg[6] = 1;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetBank();
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
// Check For Old #33 games.... (CRC code by NesToy)
|
||||
if (crc == 0x5e9bc161 // Akira(J)
|
||||
|| crc == 0xecdbafa4 // Bakushou!! Jinsei Gekijou(J)
|
||||
|| crc == 0x59cd0c31 // Don Doko Don(J)
|
||||
|| crc == 0x837c1342 // Golf Ko Open(J)
|
||||
|| crc == 0x42d893e4 // Operation Wolf(J)
|
||||
|| crc == 0x1388aeb9 // Operation Wolf(U)
|
||||
|| crc == 0x07ee6d8f // Power Blazer(J)
|
||||
|| crc == 0x5193fb54 // Takeshi no Sengoku Fuuunji(J)
|
||||
|| crc == 0xa71c3452)
|
||||
{ // Insector X(J)
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_RENDER);
|
||||
|
||||
if (crc == 0x202df297)
|
||||
{ // Captain Saver(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0x63bb86b5)
|
||||
{ // The Jetsons(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper033::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
// LOG( "Mapper033 addr=%04X data=%02X", addr&0xFFFF, data&0xFF );
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
if (patch != 0)
|
||||
{
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
SetPROM_8K_Bank(4, data & 0x1F);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
case 0x8001:
|
||||
if (patch != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(5, data & 0x1F);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(5, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8002:
|
||||
reg[0] = data;
|
||||
SetBank();
|
||||
break;
|
||||
case 0x8003:
|
||||
reg[1] = data;
|
||||
SetBank();
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
SetBank();
|
||||
break;
|
||||
case 0xA001:
|
||||
reg[3] = data;
|
||||
SetBank();
|
||||
break;
|
||||
case 0xA002:
|
||||
reg[4] = data;
|
||||
SetBank();
|
||||
break;
|
||||
case 0xA003:
|
||||
reg[5] = data;
|
||||
SetBank();
|
||||
break;
|
||||
|
||||
#if FLASE//0
|
||||
case 0xC003:
|
||||
case 0xE003:
|
||||
reg[6] = data;
|
||||
SetBank();
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
irq_counter = data;
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
|
||||
case 0xC001:
|
||||
case 0xC002:
|
||||
case 0xE001:
|
||||
case 0xE002:
|
||||
irq_enable = data;
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
#else
|
||||
case 0xC000:
|
||||
irq_latch = data;
|
||||
irq_counter = irq_latch;
|
||||
break;
|
||||
case 0xC001:
|
||||
irq_counter = irq_latch;
|
||||
break;
|
||||
case 0xC002:
|
||||
irq_enable = 1;
|
||||
break;
|
||||
case 0xC003:
|
||||
irq_enable = 0;
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
case 0xE002:
|
||||
case 0xE003:
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 0xE000:
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper033::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
#if FALSE//0
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
if( scanline >= 0 && scanline <= 239 ) {
|
||||
if( nes.ppu.IsDispON() ) {
|
||||
if( irq_enable ) {
|
||||
if( irq_counter == 0xFF ) {
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
// nes.cpu.SetIRQ( IRQ_MAPPER );
|
||||
nes.cpu.SetIRQ( IRQ_TRIGGER );
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (scanline >= 0 && scanline <= 239 && nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (++irq_counter == 0)
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
nes.cpu.SetIRQ(IRQ_TRIGGER);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetBank()
|
||||
{
|
||||
SetVROM_2K_Bank(0, reg[0]);
|
||||
SetVROM_2K_Bank(2, reg[1]);
|
||||
|
||||
// if( reg[6] & 0x01 ) {
|
||||
SetVROM_1K_Bank(4, reg[2]);
|
||||
SetVROM_1K_Bank(5, reg[3]);
|
||||
SetVROM_1K_Bank(6, reg[4]);
|
||||
SetVROM_1K_Bank(7, reg[5]);
|
||||
// } else {
|
||||
// SetVROM_2K_Bank( 4, reg[0] );
|
||||
// SetVROM_2K_Bank( 6, reg[1] );
|
||||
// }
|
||||
}
|
||||
|
||||
//void Mapper033::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 7; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
|
||||
p[7] = irq_enable;
|
||||
p[8] = irq_counter;
|
||||
p[9] = irq_latch;
|
||||
}
|
||||
|
||||
//void Mapper033::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 7; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
|
||||
irq_enable = p[7];
|
||||
irq_counter = p[8];
|
||||
irq_latch = p[9];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
51
Core/VirtualNes.Core/Mapper/Mapper034.cs
Normal file
51
Core/VirtualNes.Core/Mapper/Mapper034.cs
Normal file
@ -0,0 +1,51 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper034 Nina-1 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper034 : Mapper
|
||||
{
|
||||
public Mapper034(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper034::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x7FFD:
|
||||
SetPROM_32K_Bank(data);
|
||||
break;
|
||||
case 0x7FFE:
|
||||
SetVROM_4K_Bank(0, data);
|
||||
break;
|
||||
case 0x7FFF:
|
||||
SetVROM_4K_Bank(4, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper034::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_32K_Bank(data);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
123
Core/VirtualNes.Core/Mapper/Mapper035.cs
Normal file
123
Core/VirtualNes.Core/Mapper/Mapper035.cs
Normal file
@ -0,0 +1,123 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper035 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper035 : Mapper
|
||||
{
|
||||
|
||||
BYTE[] reg = new byte[8];
|
||||
BYTE[] chr = new byte[8];
|
||||
ushort IRQCount, IRQa;
|
||||
public Mapper035(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
reg[i] = chr[i] = 0;
|
||||
|
||||
IRQCount = IRQa = 0;
|
||||
|
||||
//SetPROM_32K_Bank( 0, 1, PROM_8K_SIZE-2, PROM_8K_SIZE-1 );
|
||||
|
||||
Sync();
|
||||
//setprg8r(0x10,0x6000,0);
|
||||
SetPROM_8K_Bank(7, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
void Sync()
|
||||
{
|
||||
int i;
|
||||
SetPROM_8K_Bank(4, reg[0]);
|
||||
SetPROM_8K_Bank(5, reg[1]);
|
||||
SetPROM_8K_Bank(6, reg[2]);
|
||||
for (i = 0; i < 8; i++)
|
||||
SetVROM_1K_Bank((byte)i, chr[i]);
|
||||
SetVRAM_Mirror(reg[3] ^ 1);
|
||||
}
|
||||
|
||||
//void Mapper035::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||
{
|
||||
XRAM[addr - 0x6000] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.WriteLow(addr, data);
|
||||
}
|
||||
}
|
||||
//BYTE Mapper035::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||
{
|
||||
return XRAM[addr - 0x6000];
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.ReadLow(addr);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper035::Write(WORD A, BYTE V)
|
||||
public override void Write(ushort A, byte V)
|
||||
{
|
||||
switch (A)
|
||||
{
|
||||
case 0x8000: reg[0] = V; break;
|
||||
case 0x8001: reg[1] = V; break;
|
||||
case 0x8002: reg[2] = V; break;
|
||||
case 0x9000: chr[0] = V; break;
|
||||
case 0x9001: chr[1] = V; break;
|
||||
case 0x9002: chr[2] = V; break;
|
||||
case 0x9003: chr[3] = V; break;
|
||||
case 0x9004: chr[4] = V; break;
|
||||
case 0x9005: chr[5] = V; break;
|
||||
case 0x9006: chr[6] = V; break;
|
||||
case 0x9007: chr[7] = V; break;
|
||||
case 0xC002:
|
||||
IRQa = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER); break;
|
||||
case 0xC005: IRQCount = V; break;
|
||||
case 0xC003: IRQa = 1; break;
|
||||
case 0xD001: reg[3] = V; break;
|
||||
}
|
||||
Sync();
|
||||
}
|
||||
|
||||
//void Mapper035::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (IRQa != 0)
|
||||
{
|
||||
IRQCount--;
|
||||
if (IRQCount == 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
IRQa = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
93
Core/VirtualNes.Core/Mapper/Mapper040.cs
Normal file
93
Core/VirtualNes.Core/Mapper/Mapper040.cs
Normal file
@ -0,0 +1,93 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper040 SMB2J //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper040 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
INT irq_line;
|
||||
public Mapper040(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_line = 0;
|
||||
|
||||
SetPROM_8K_Bank(3, 6);
|
||||
SetPROM_32K_Bank(4, 5, 0, 7);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper040::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xE000)
|
||||
{
|
||||
case 0x8000:
|
||||
irq_enable = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xA000:
|
||||
irq_enable = 0xFF;
|
||||
irq_line = 37;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xC000:
|
||||
break;
|
||||
case 0xE000:
|
||||
SetPROM_8K_Bank(6, data & 0x07);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper040::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (--irq_line <= 0)
|
||||
{
|
||||
// nes.cpu.IRQ();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper040::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
//*(INT*)&p[1] = irq_line;
|
||||
BitConverter.GetBytes(irq_line).CopyTo(p, 1);
|
||||
}
|
||||
|
||||
//void Mapper040::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
//irq_line = *(INT*)&p[1];
|
||||
irq_line = BitConverter.ToInt32(p, 1);
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
76
Core/VirtualNes.Core/Mapper/Mapper041.cs
Normal file
76
Core/VirtualNes.Core/Mapper/Mapper041.cs
Normal file
@ -0,0 +1,76 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper041 Caltron 6-in-1 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper041 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[2];
|
||||
public Mapper041(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
reg[0] = reg[1] = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, 2, 3);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper041::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr >= 0x6000 && addr < 0x6800)
|
||||
{
|
||||
SetPROM_32K_Bank(addr & 0x07);
|
||||
reg[0] = (byte)(addr & 0x04);
|
||||
reg[1] &= 0x03;
|
||||
reg[1] |= (byte)((addr >> 1) & 0x0C);
|
||||
SetVROM_8K_Bank(reg[1]);
|
||||
if ((addr & 0x20) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper041::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (reg[0] != 0)
|
||||
{
|
||||
reg[1] &= 0x0C;
|
||||
reg[1] |= (byte)(addr & 0x03);
|
||||
SetVROM_8K_Bank(reg[1]);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper041::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
}
|
||||
|
||||
//void Mapper041::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
102
Core/VirtualNes.Core/Mapper/Mapper042.cs
Normal file
102
Core/VirtualNes.Core/Mapper/Mapper042.cs
Normal file
@ -0,0 +1,102 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper042 Mario Baby //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper042 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
public Mapper042(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
|
||||
SetPROM_8K_Bank(3, 0);
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 4, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper042::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xE003)
|
||||
{
|
||||
case 0xE000:
|
||||
SetPROM_8K_Bank(3, data & 0x0F);
|
||||
break;
|
||||
|
||||
case 0xE001:
|
||||
if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
|
||||
case 0xE002:
|
||||
if ((data & 0x02) != 0)
|
||||
{
|
||||
irq_enable = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
}
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper042::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (irq_counter < 215)
|
||||
{
|
||||
irq_counter++;
|
||||
}
|
||||
if (irq_counter == 215)
|
||||
{
|
||||
irq_enable = 0;
|
||||
// nes.cpu.IRQ();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper042::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
p[1] = irq_counter;
|
||||
}
|
||||
|
||||
//void Mapper042::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
irq_counter = p[1];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
141
Core/VirtualNes.Core/Mapper/Mapper043.cs
Normal file
141
Core/VirtualNes.Core/Mapper/Mapper043.cs
Normal file
@ -0,0 +1,141 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper043 SMB2J //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper043 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
public Mapper043(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
irq_enable = 0xFF;
|
||||
irq_counter = 0;
|
||||
|
||||
SetPROM_8K_Bank(3, 2);
|
||||
SetPROM_32K_Bank(1, 0, 4, 9);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//BYTE Mapper043::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
if (0x5000 <= addr && addr < 0x6000)
|
||||
{
|
||||
byte[] pPtr = nes.rom.GetPROM();
|
||||
return pPtr[0x2000 * 8 + 0x1000 + (addr - 0x5000)];
|
||||
}
|
||||
return (BYTE)(addr >> 8);
|
||||
}
|
||||
|
||||
//void Mapper043::ExWrite(WORD addr, BYTE data)
|
||||
public override void ExWrite(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0xF0FF) == 0x4022)
|
||||
{
|
||||
switch (data & 0x07)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
SetPROM_8K_Bank(6, 4);
|
||||
break;
|
||||
case 0x01:
|
||||
SetPROM_8K_Bank(6, 3);
|
||||
break;
|
||||
case 0x05:
|
||||
SetPROM_8K_Bank(6, 7);
|
||||
break;
|
||||
case 0x06:
|
||||
SetPROM_8K_Bank(6, 5);
|
||||
break;
|
||||
case 0x07:
|
||||
SetPROM_8K_Bank(6, 6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper043::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0xF0FF) == 0x4022)
|
||||
{
|
||||
ExWrite(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper043::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0x8122)
|
||||
{
|
||||
if ((data & 0x03) != 0)
|
||||
{
|
||||
irq_enable = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter = 0;
|
||||
irq_enable = 0;
|
||||
}
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper043::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
irq_counter += 341;
|
||||
if (irq_counter >= 12288)
|
||||
{
|
||||
irq_counter = 0;
|
||||
// nes.cpu.IRQ();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper043::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
//*(INT*)&p[1] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 1);
|
||||
}
|
||||
|
||||
//void Mapper043::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
//irq_counter = *(INT*)&p[1];
|
||||
irq_counter = BitConverter.ToInt32(p, 1);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
289
Core/VirtualNes.Core/Mapper/Mapper044.cs
Normal file
289
Core/VirtualNes.Core/Mapper/Mapper044.cs
Normal file
@ -0,0 +1,289 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper044 Super HiK 7-in-1 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper044 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[8];
|
||||
BYTE patch;
|
||||
BYTE bank;
|
||||
BYTE prg0, prg1;
|
||||
BYTE chr01, chr23, chr4, chr5, chr6, chr7;
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
public Mapper044(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
if (nes.rom.GetPROM_CRC() == 0x7eef434c)
|
||||
{
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0;
|
||||
}
|
||||
|
||||
bank = 0;
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
|
||||
// set VROM banks
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
chr01 = 0;
|
||||
chr23 = 2;
|
||||
chr4 = 4;
|
||||
chr5 = 5;
|
||||
chr6 = 6;
|
||||
chr7 = 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
chr01 = chr23 = chr4 = chr5 = chr6 = chr7 = 0;
|
||||
}
|
||||
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
}
|
||||
|
||||
//void Mapper044::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0x6000)
|
||||
{
|
||||
if (patch != 0)
|
||||
{
|
||||
bank = (byte)((data & 0x06) >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bank = (byte)((data & 0x01) << 1);
|
||||
}
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper044::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = data;
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[1] = data;
|
||||
switch (reg[0] & 0x07)
|
||||
{
|
||||
case 0x00:
|
||||
chr01 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x01:
|
||||
chr23 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x02:
|
||||
chr4 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x03:
|
||||
chr5 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x04:
|
||||
chr6 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x05:
|
||||
chr7 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x06:
|
||||
prg0 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
if (!nes.rom.Is4SCREEN())
|
||||
{
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
case 0xA001:
|
||||
reg[3] = data;
|
||||
bank = (byte)(data & 0x07);
|
||||
if (bank == 7)
|
||||
{
|
||||
bank = 6;
|
||||
}
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[4] = data;
|
||||
irq_counter = data;
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[5] = data;
|
||||
irq_latch = data;
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[6] = data;
|
||||
irq_enable = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[7] = data;
|
||||
irq_enable = 1;
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper044::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if ((--irq_counter) == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
// nes.cpu.IRQ();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_CPU()
|
||||
{
|
||||
if ((reg[0] & 0x40) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(4, ((bank == 6) ? 0x1e : 0x0e) | (bank << 4));
|
||||
SetPROM_8K_Bank(5, ((bank == 6) ? 0x1f & prg1 : 0x0f & prg1) | (bank << 4));
|
||||
SetPROM_8K_Bank(6, ((bank == 6) ? 0x1f & prg0 : 0x0f & prg0) | (bank << 4));
|
||||
SetPROM_8K_Bank(7, ((bank == 6) ? 0x1f : 0x0f) | (bank << 4));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, ((bank == 6) ? 0x1f & prg0 : 0x0f & prg0) | (bank << 4));
|
||||
SetPROM_8K_Bank(5, ((bank == 6) ? 0x1f & prg1 : 0x0f & prg1) | (bank << 4));
|
||||
SetPROM_8K_Bank(6, ((bank == 6) ? 0x1e : 0x0e) | (bank << 4));
|
||||
SetPROM_8K_Bank(7, ((bank == 6) ? 0x1f : 0x0f) | (bank << 4));
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(0, ((bank == 6) ? 0xff & chr4 : 0x7f & chr4) | (bank << 7));
|
||||
SetVROM_1K_Bank(1, ((bank == 6) ? 0xff & chr5 : 0x7f & chr5) | (bank << 7));
|
||||
SetVROM_1K_Bank(2, ((bank == 6) ? 0xff & chr6 : 0x7f & chr6) | (bank << 7));
|
||||
SetVROM_1K_Bank(3, ((bank == 6) ? 0xff & chr7 : 0x7f & chr7) | (bank << 7));
|
||||
SetVROM_1K_Bank(4, ((bank == 6) ? 0xff & chr01 : 0x7f & chr01) | (bank << 7));
|
||||
SetVROM_1K_Bank(5, ((bank == 6) ? 0xff & (chr01 + 1) : 0x7f & (chr01 + 1)) | (bank << 7));
|
||||
SetVROM_1K_Bank(6, ((bank == 6) ? 0xff & chr23 : 0x7f & chr23) | (bank << 7));
|
||||
SetVROM_1K_Bank(7, ((bank == 6) ? 0xff & (chr23 + 1) : 0x7f & (chr23 + 1)) | (bank << 7));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(0, ((bank == 6) ? 0xff & chr01 : 0x7f & chr01) | (bank << 7));
|
||||
SetVROM_1K_Bank(1, ((bank == 6) ? 0xff & (chr01 + 1) : 0x7f & (chr01 + 1)) | (bank << 7));
|
||||
SetVROM_1K_Bank(2, ((bank == 6) ? 0xff & chr23 : 0x7f & chr23) | (bank << 7));
|
||||
SetVROM_1K_Bank(3, ((bank == 6) ? 0xff & (chr23 + 1) : 0x7f & (chr23 + 1)) | (bank << 7));
|
||||
SetVROM_1K_Bank(4, ((bank == 6) ? 0xff & chr4 : 0x7f & chr4) | (bank << 7));
|
||||
SetVROM_1K_Bank(5, ((bank == 6) ? 0xff & chr5 : 0x7f & chr5) | (bank << 7));
|
||||
SetVROM_1K_Bank(6, ((bank == 6) ? 0xff & chr6 : 0x7f & chr6) | (bank << 7));
|
||||
SetVROM_1K_Bank(7, ((bank == 6) ? 0xff & chr7 : 0x7f & chr7) | (bank << 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper044::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[8] = prg0;
|
||||
p[9] = prg1;
|
||||
p[10] = chr01;
|
||||
p[11] = chr23;
|
||||
p[12] = chr4;
|
||||
p[13] = chr5;
|
||||
p[14] = chr6;
|
||||
p[15] = chr7;
|
||||
p[16] = irq_enable;
|
||||
p[17] = irq_counter;
|
||||
p[18] = irq_latch;
|
||||
p[19] = bank;
|
||||
}
|
||||
|
||||
//void Mapper044::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
prg0 = p[8];
|
||||
prg1 = p[9];
|
||||
chr01 = p[10];
|
||||
chr23 = p[11];
|
||||
chr4 = p[12];
|
||||
chr5 = p[13];
|
||||
chr6 = p[14];
|
||||
chr7 = p[15];
|
||||
irq_enable = p[16];
|
||||
irq_counter = p[17];
|
||||
irq_latch = p[18];
|
||||
bank = p[19];
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
390
Core/VirtualNes.Core/Mapper/Mapper045.cs
Normal file
390
Core/VirtualNes.Core/Mapper/Mapper045.cs
Normal file
@ -0,0 +1,390 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper045 1000000-in-1 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper045 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[8];
|
||||
BYTE patch;
|
||||
BYTE prg0, prg1, prg2, prg3;
|
||||
BYTE chr0, chr1, chr2, chr3, chr4, chr5, chr6, chr7;
|
||||
BYTE[] p = new byte[4];
|
||||
INT[] c = new int[8];
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
BYTE irq_latched;
|
||||
BYTE irq_reset;
|
||||
public Mapper045(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0;
|
||||
}
|
||||
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
prg2 = (byte)(PROM_8K_SIZE - 2);
|
||||
prg3 = (byte)(PROM_8K_SIZE - 1);
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0x58bcacf6 // Kunio 8-in-1 (Pirate Cart)
|
||||
|| crc == 0x9103cfd6 // HIK 7-in-1 (Pirate Cart)
|
||||
|| crc == 0xc082e6d3)
|
||||
{ // Super 8-in-1 (Pirate Cart)
|
||||
patch = 1;
|
||||
prg2 = 62;
|
||||
prg3 = 63;
|
||||
}
|
||||
if (crc == 0xe0dd259d)
|
||||
{ // Super 3-in-1 (Pirate Cart)
|
||||
patch = 2;
|
||||
}
|
||||
SetPROM_32K_Bank(prg0, prg1, prg2, prg3);
|
||||
p[0] = prg0;
|
||||
p[1] = prg1;
|
||||
p[2] = prg2;
|
||||
p[3] = prg3;
|
||||
|
||||
SetVROM_8K_Bank(0);
|
||||
|
||||
// chr0 = c[0] = 0;
|
||||
// chr1 = c[1] = 0
|
||||
// chr2 = c[2] = 0;
|
||||
// chr3 = c[3] = 0;
|
||||
// chr4 = c[4] = 0;
|
||||
// chr5 = c[5] = 0;
|
||||
// chr6 = c[6] = 0;
|
||||
// chr7 = c[7] = 0;
|
||||
|
||||
c[0] = chr0 = 0;
|
||||
c[1] = chr1 = 1;
|
||||
c[2] = chr2 = 2;
|
||||
c[3] = chr3 = 3;
|
||||
c[4] = chr4 = 4;
|
||||
c[5] = chr5 = 5;
|
||||
c[6] = chr6 = 6;
|
||||
c[7] = chr7 = 7;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_latched = 0;
|
||||
irq_reset = 0;
|
||||
}
|
||||
|
||||
//void Mapper045::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
// if( addr == 0x6000 ) {
|
||||
// if( addr == 0x6000 && !(reg[3]&0x40) ) {
|
||||
if ((reg[3] & 0x40) == 0)
|
||||
{
|
||||
reg[reg[5]] = data;
|
||||
reg[5] = (byte)((reg[5] + 1) & 0x03);
|
||||
|
||||
SetBank_CPU_4(prg0);
|
||||
SetBank_CPU_5(prg1);
|
||||
SetBank_CPU_6(prg2);
|
||||
SetBank_CPU_7(prg3);
|
||||
SetBank_PPU();
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper045::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
if ((data & 0x40) != (reg[6] & 0x40))
|
||||
{
|
||||
BYTE swp;
|
||||
swp = prg0; prg0 = prg2; prg2 = swp;
|
||||
swp = p[0]; p[0] = p[2]; p[2] = swp;
|
||||
SetBank_CPU_4(p[0]);
|
||||
SetBank_CPU_5(p[1]);
|
||||
}
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((data & 0x80) != (reg[6] & 0x80))
|
||||
{
|
||||
INT swp;
|
||||
swp = chr4; chr4 = chr0; chr0 = (byte)swp;
|
||||
swp = chr5; chr5 = chr1; chr1 = (byte)swp;
|
||||
swp = chr6; chr6 = chr2; chr2 = (byte)swp;
|
||||
swp = chr7; chr7 = chr3; chr3 = (byte)swp;
|
||||
swp = c[4]; c[4] = c[0]; c[0] = swp;
|
||||
swp = c[5]; c[5] = c[1]; c[1] = swp;
|
||||
swp = c[6]; c[6] = c[2]; c[2] = swp;
|
||||
swp = c[7]; c[7] = c[3]; c[3] = swp;
|
||||
SetVROM_8K_Bank(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
|
||||
}
|
||||
}
|
||||
reg[6] = data;
|
||||
break;
|
||||
case 0x8001:
|
||||
switch (reg[6] & 0x07)
|
||||
{
|
||||
case 0x00:
|
||||
chr0 = (byte)((data & 0xFE) + 0);
|
||||
chr1 = (byte)((data & 0xFE) + 1);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x01:
|
||||
chr2 = (byte)((data & 0xFE) + 0);
|
||||
chr3 = (byte)((data & 0xFE) + 1);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x02:
|
||||
chr4 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x03:
|
||||
chr5 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x04:
|
||||
chr6 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x05:
|
||||
chr7 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x06:
|
||||
if ((reg[6] & 0x40) != 0)
|
||||
{
|
||||
prg2 = (byte)(data & 0x3F);
|
||||
SetBank_CPU_6(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
prg0 = (byte)(data & 0x3F);
|
||||
SetBank_CPU_4(data);
|
||||
}
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = (byte)(data & 0x3F);
|
||||
SetBank_CPU_5(data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
case 0xC000:
|
||||
if (patch == 2)
|
||||
{
|
||||
if (data == 0x29 || data == 0x70)
|
||||
data = 0x07;
|
||||
}
|
||||
irq_latch = data;
|
||||
irq_latched = 1;
|
||||
if (irq_reset != 0)
|
||||
{
|
||||
irq_counter = data;
|
||||
irq_latched = 0;
|
||||
}
|
||||
// irq_counter = data;
|
||||
break;
|
||||
case 0xC001:
|
||||
// irq_latch = data;
|
||||
irq_counter = irq_latch;
|
||||
break;
|
||||
case 0xE000:
|
||||
irq_enable = 0;
|
||||
irq_reset = 1;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
irq_enable = 1;
|
||||
if (irq_latched != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper045::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
irq_reset = 0;
|
||||
if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_counter != 0)
|
||||
{
|
||||
irq_counter--;
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_CPU_4(INT data)
|
||||
{
|
||||
data &= (reg[3] & 0x3F) ^ 0xFF;
|
||||
data &= 0x3F;
|
||||
data |= reg[1];
|
||||
SetPROM_8K_Bank(4, data);
|
||||
p[0] = (byte)data;
|
||||
}
|
||||
|
||||
void SetBank_CPU_5(INT data)
|
||||
{
|
||||
data &= (reg[3] & 0x3F) ^ 0xFF;
|
||||
data &= 0x3F;
|
||||
data |= reg[1];
|
||||
SetPROM_8K_Bank(5, data);
|
||||
p[1] = (byte)data;
|
||||
}
|
||||
|
||||
void SetBank_CPU_6(INT data)
|
||||
{
|
||||
data &= (reg[3] & 0x3F) ^ 0xFF;
|
||||
data &= 0x3F;
|
||||
data |= reg[1];
|
||||
SetPROM_8K_Bank(6, data);
|
||||
p[2] = (byte)data;
|
||||
}
|
||||
|
||||
void SetBank_CPU_7(INT data)
|
||||
{
|
||||
data &= (reg[3] & 0x3F) ^ 0xFF;
|
||||
data &= 0x3F;
|
||||
data |= reg[1];
|
||||
SetPROM_8K_Bank(7, data);
|
||||
p[3] = (byte)data;
|
||||
}
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
BYTE[] table = new byte[] {
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF
|
||||
};
|
||||
|
||||
c[0] = chr0;
|
||||
c[1] = chr1;
|
||||
c[2] = chr2;
|
||||
c[3] = chr3;
|
||||
c[4] = chr4;
|
||||
c[5] = chr5;
|
||||
c[6] = chr6;
|
||||
c[7] = chr7;
|
||||
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
c[i] &= table[reg[2] & 0x0F];
|
||||
c[i] |= reg[0] & ((patch != 1) ? 0xFF : 0xC0);
|
||||
c[i] += (reg[2] & ((patch != 1) ? 0x10 : 0x30)) << 4;
|
||||
}
|
||||
|
||||
if ((reg[6] & 0x80) != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(c[4], c[5], c[6], c[7], c[0], c[1], c[2], c[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_8K_Bank(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper045::SaveState(LPBYTE ps)
|
||||
public override void SaveState(byte[] ps)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
ps[i] = reg[i];
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
ps[i + 8] = p[i];
|
||||
}
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
//*(INT*)&ps[i * 4 + 64] = c[i];
|
||||
BitConverter.GetBytes(c[i]).CopyTo(ps, i * 4 + 64);
|
||||
}
|
||||
ps[20] = prg0;
|
||||
ps[21] = prg1;
|
||||
ps[22] = prg2;
|
||||
ps[23] = prg3;
|
||||
ps[24] = chr0;
|
||||
ps[25] = chr1;
|
||||
ps[26] = chr2;
|
||||
ps[27] = chr3;
|
||||
ps[28] = chr4;
|
||||
ps[29] = chr5;
|
||||
ps[30] = chr6;
|
||||
ps[31] = chr7;
|
||||
ps[32] = irq_enable;
|
||||
ps[33] = irq_counter;
|
||||
ps[34] = irq_latch;
|
||||
}
|
||||
|
||||
//void Mapper045::LoadState(LPBYTE ps)
|
||||
public override void LoadState(byte[] ps)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = ps[i];
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
p[i] = ps[i + 8];
|
||||
}
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
//c[i] = *(INT*)&ps[i * 4 + 64];
|
||||
c[i] = BitConverter.ToInt32(ps, i * 4 + 64);
|
||||
}
|
||||
prg0 = ps[20];
|
||||
prg1 = ps[21];
|
||||
prg2 = ps[22];
|
||||
prg3 = ps[23];
|
||||
chr0 = ps[24];
|
||||
chr1 = ps[25];
|
||||
chr2 = ps[26];
|
||||
chr3 = ps[27];
|
||||
chr4 = ps[28];
|
||||
chr5 = ps[29];
|
||||
chr6 = ps[30];
|
||||
chr7 = ps[31];
|
||||
irq_enable = ps[32];
|
||||
irq_counter = ps[33];
|
||||
irq_latch = ps[34];
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
85
Core/VirtualNes.Core/Mapper/Mapper046.cs
Normal file
85
Core/VirtualNes.Core/Mapper/Mapper046.cs
Normal file
@ -0,0 +1,85 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper046 Rumble Station //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper046 : Mapper
|
||||
{
|
||||
int[] reg = new int[4];
|
||||
public Mapper046(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
reg[0] = 0;
|
||||
reg[1] = 0;
|
||||
reg[2] = 0;
|
||||
reg[3] = 0;
|
||||
|
||||
SetBank();
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
|
||||
//void Mapper046::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
reg[0] = data & 0x0F;
|
||||
reg[1] = (data & 0xF0) >> 4;
|
||||
SetBank();
|
||||
}
|
||||
|
||||
//void Mapper046::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
reg[2] = data & 0x01;
|
||||
reg[3] = (data & 0x70) >> 4;
|
||||
SetBank();
|
||||
}
|
||||
|
||||
void SetBank()
|
||||
{
|
||||
SetPROM_8K_Bank(4, reg[0] * 8 + reg[2] * 4 + 0);
|
||||
SetPROM_8K_Bank(5, reg[0] * 8 + reg[2] * 4 + 1);
|
||||
SetPROM_8K_Bank(6, reg[0] * 8 + reg[2] * 4 + 2);
|
||||
SetPROM_8K_Bank(7, reg[0] * 8 + reg[2] * 4 + 3);
|
||||
|
||||
SetVROM_1K_Bank(0, reg[1] * 64 + reg[3] * 8 + 0);
|
||||
SetVROM_1K_Bank(1, reg[1] * 64 + reg[3] * 8 + 1);
|
||||
SetVROM_1K_Bank(2, reg[1] * 64 + reg[3] * 8 + 2);
|
||||
SetVROM_1K_Bank(3, reg[1] * 64 + reg[3] * 8 + 3);
|
||||
SetVROM_1K_Bank(4, reg[1] * 64 + reg[3] * 8 + 4);
|
||||
SetVROM_1K_Bank(5, reg[1] * 64 + reg[3] * 8 + 5);
|
||||
SetVROM_1K_Bank(6, reg[1] * 64 + reg[3] * 8 + 6);
|
||||
SetVROM_1K_Bank(7, reg[1] * 64 + reg[3] * 8 + 7);
|
||||
}
|
||||
|
||||
//void Mapper046::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = (byte)reg[0];
|
||||
p[1] = (byte)reg[1];
|
||||
p[2] = (byte)reg[2];
|
||||
p[3] = (byte)reg[3];
|
||||
}
|
||||
|
||||
//void Mapper046::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
reg[3] = p[3];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
274
Core/VirtualNes.Core/Mapper/Mapper047.cs
Normal file
274
Core/VirtualNes.Core/Mapper/Mapper047.cs
Normal file
@ -0,0 +1,274 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper047 NES-QJ //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper047 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[8];
|
||||
BYTE patch;
|
||||
BYTE bank;
|
||||
BYTE prg0, prg1;
|
||||
BYTE chr01, chr23, chr4, chr5, chr6, chr7;
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
public Mapper047(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
if (nes.rom.GetPROM_CRC() == 0x7eef434c)
|
||||
{
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0;
|
||||
}
|
||||
|
||||
bank = 0;
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
|
||||
// set VROM banks
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
chr01 = 0;
|
||||
chr23 = 2;
|
||||
chr4 = 4;
|
||||
chr5 = 5;
|
||||
chr6 = 6;
|
||||
chr7 = 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
chr01 = chr23 = chr4 = chr5 = chr6 = chr7 = 0;
|
||||
}
|
||||
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
}
|
||||
|
||||
//void Mapper047::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0x6000)
|
||||
{
|
||||
if (patch != 0)
|
||||
{
|
||||
bank = (byte)((data & 0x06) >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bank = (byte)((data & 0x01) << 1);
|
||||
}
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper047::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = data;
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[1] = data;
|
||||
switch (reg[0] & 0x07)
|
||||
{
|
||||
case 0x00:
|
||||
chr01 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x01:
|
||||
chr23 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x02:
|
||||
chr4 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x03:
|
||||
chr5 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x04:
|
||||
chr6 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x05:
|
||||
chr7 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x06:
|
||||
prg0 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
case 0xA001:
|
||||
reg[3] = data;
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[4] = data;
|
||||
irq_counter = data;
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[5] = data;
|
||||
irq_latch = data;
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[6] = data;
|
||||
irq_enable = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[7] = data;
|
||||
irq_enable = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper047::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if ((--irq_counter) == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
// nes.cpu.IRQ();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_CPU()
|
||||
{
|
||||
if ((reg[0] & 0x40) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(4, bank * 8 + ((patch != 0 && bank != 2) ? 6 : 14));
|
||||
SetPROM_8K_Bank(5, bank * 8 + prg1);
|
||||
SetPROM_8K_Bank(6, bank * 8 + prg0);
|
||||
SetPROM_8K_Bank(7, bank * 8 + ((patch != 0 && bank != 2) ? 7 : 15));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, bank * 8 + prg0);
|
||||
SetPROM_8K_Bank(5, bank * 8 + prg1);
|
||||
SetPROM_8K_Bank(6, bank * 8 + ((patch != 0 && bank != 2) ? 6 : 14));
|
||||
SetPROM_8K_Bank(7, bank * 8 + ((patch != 0 && bank != 2) ? 7 : 15));
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(0, (bank & 0x02) * 64 + chr4);
|
||||
SetVROM_1K_Bank(1, (bank & 0x02) * 64 + chr5);
|
||||
SetVROM_1K_Bank(2, (bank & 0x02) * 64 + chr6);
|
||||
SetVROM_1K_Bank(3, (bank & 0x02) * 64 + chr7);
|
||||
SetVROM_1K_Bank(4, (bank & 0x02) * 64 + chr01 + 0);
|
||||
SetVROM_1K_Bank(5, (bank & 0x02) * 64 + chr01 + 1);
|
||||
SetVROM_1K_Bank(6, (bank & 0x02) * 64 + chr23 + 0);
|
||||
SetVROM_1K_Bank(7, (bank & 0x02) * 64 + chr23 + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(0, (bank & 0x02) * 64 + chr01 + 0);
|
||||
SetVROM_1K_Bank(1, (bank & 0x02) * 64 + chr01 + 1);
|
||||
SetVROM_1K_Bank(2, (bank & 0x02) * 64 + chr23 + 0);
|
||||
SetVROM_1K_Bank(3, (bank & 0x02) * 64 + chr23 + 1);
|
||||
SetVROM_1K_Bank(4, (bank & 0x02) * 64 + chr4);
|
||||
SetVROM_1K_Bank(5, (bank & 0x02) * 64 + chr5);
|
||||
SetVROM_1K_Bank(6, (bank & 0x02) * 64 + chr6);
|
||||
SetVROM_1K_Bank(7, (bank & 0x02) * 64 + chr7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper047::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[8] = prg0;
|
||||
p[9] = prg1;
|
||||
p[10] = chr01;
|
||||
p[11] = chr23;
|
||||
p[12] = chr4;
|
||||
p[13] = chr5;
|
||||
p[14] = chr6;
|
||||
p[15] = chr7;
|
||||
p[16] = irq_enable;
|
||||
p[17] = irq_counter;
|
||||
p[18] = irq_latch;
|
||||
p[19] = bank;
|
||||
}
|
||||
|
||||
//void Mapper047::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
prg0 = p[8];
|
||||
prg1 = p[9];
|
||||
chr01 = p[10];
|
||||
chr23 = p[11];
|
||||
chr4 = p[12];
|
||||
chr5 = p[13];
|
||||
chr6 = p[14];
|
||||
chr7 = p[15];
|
||||
irq_enable = p[16];
|
||||
irq_counter = p[17];
|
||||
irq_latch = p[18];
|
||||
bank = p[19];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
141
Core/VirtualNes.Core/Mapper/Mapper048.cs
Normal file
141
Core/VirtualNes.Core/Mapper/Mapper048.cs
Normal file
@ -0,0 +1,141 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper048 Taito TC190V //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper048 : Mapper
|
||||
{
|
||||
BYTE reg;
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
public Mapper048(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
reg = 0;
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
// if( crc == 0x547e6cc1 ) { // Flintstones - The Rescue of Dino & Hoppy(J)
|
||||
// nes.SetRenderMethod( NES::POST_RENDER );
|
||||
// }
|
||||
}
|
||||
|
||||
//void Mapper048::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
if (reg == 0)
|
||||
{
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x8001:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0x8002:
|
||||
SetVROM_2K_Bank(0, data);
|
||||
break;
|
||||
case 0x8003:
|
||||
SetVROM_2K_Bank(2, data);
|
||||
break;
|
||||
case 0xA000:
|
||||
SetVROM_1K_Bank(4, data);
|
||||
break;
|
||||
case 0xA001:
|
||||
SetVROM_1K_Bank(5, data);
|
||||
break;
|
||||
case 0xA002:
|
||||
SetVROM_1K_Bank(6, data);
|
||||
break;
|
||||
case 0xA003:
|
||||
SetVROM_1K_Bank(7, data);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
irq_counter = data;
|
||||
irq_enable = 0;
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
|
||||
case 0xC001:
|
||||
irq_counter = data;
|
||||
irq_enable = 1;
|
||||
// irq_enable = data & 0x01;
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
|
||||
case 0xC002:
|
||||
break;
|
||||
case 0xC003:
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
reg = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper048::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (irq_counter == 0xFF)
|
||||
{
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
// nes.cpu.SetIRQ( IRQ_MAPPER );
|
||||
nes.cpu.SetIRQ(IRQ_TRIGGER2);
|
||||
}
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper048::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg;
|
||||
p[1] = irq_enable;
|
||||
p[2] = irq_counter;
|
||||
}
|
||||
|
||||
//void Mapper048::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg = p[0];
|
||||
irq_enable = p[1];
|
||||
irq_counter = p[2];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
98
Core/VirtualNes.Core/Mapper/Mapper050.cs
Normal file
98
Core/VirtualNes.Core/Mapper/Mapper050.cs
Normal file
@ -0,0 +1,98 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper050 SMB2J //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper050 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
public Mapper050(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
irq_enable = 0;
|
||||
SetPROM_8K_Bank(3, 15);
|
||||
SetPROM_8K_Bank(4, 8);
|
||||
SetPROM_8K_Bank(5, 9);
|
||||
SetPROM_8K_Bank(6, 0);
|
||||
SetPROM_8K_Bank(7, 11);
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper050::ExWrite(WORD addr, BYTE data)
|
||||
public override void ExWrite(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0xE060) == 0x4020)
|
||||
{
|
||||
if ((addr & 0x0100) != 0)
|
||||
{
|
||||
irq_enable = (byte)(data & 0x01);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(6, (data & 0x08) | ((data & 0x01) << 2) | ((data & 0x06) >> 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper050::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0xE060) == 0x4020)
|
||||
{
|
||||
if ((addr & 0x0100) != 0)
|
||||
{
|
||||
irq_enable = (byte)(data & 0x01);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(6, (data & 0x08) | ((data & 0x01) << 2) | ((data & 0x06) >> 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper050::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (scanline == 21)
|
||||
{
|
||||
// nes.cpu.IRQ();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper050::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
}
|
||||
|
||||
//void Mapper050::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
105
Core/VirtualNes.Core/Mapper/Mapper051.cs
Normal file
105
Core/VirtualNes.Core/Mapper/Mapper051.cs
Normal file
@ -0,0 +1,105 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper051 11-in-1 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper051 : Mapper
|
||||
{
|
||||
int mode, bank;
|
||||
public Mapper051(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
bank = 0;
|
||||
mode = 1;
|
||||
|
||||
SetBank_CPU();
|
||||
SetCRAM_8K_Bank(0);
|
||||
}
|
||||
|
||||
//void Mapper051::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr >= 0x6000)
|
||||
{
|
||||
mode = ((data & 0x10) >> 3) | ((data & 0x02) >> 1);
|
||||
SetBank_CPU();
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper051::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
bank = (data & 0x0f) << 2;
|
||||
if (0xC000 <= addr && addr <= 0xDFFF)
|
||||
{
|
||||
mode = (mode & 0x01) | ((data & 0x10) >> 3);
|
||||
}
|
||||
SetBank_CPU();
|
||||
}
|
||||
|
||||
void SetBank_CPU()
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
SetPROM_8K_Bank(3, (bank | 0x2c | 3));
|
||||
SetPROM_8K_Bank(4, (bank | 0x00 | 0));
|
||||
SetPROM_8K_Bank(5, (bank | 0x00 | 1));
|
||||
SetPROM_8K_Bank(6, (bank | 0x0c | 2));
|
||||
SetPROM_8K_Bank(7, (bank | 0x0c | 3));
|
||||
break;
|
||||
case 1:
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
SetPROM_8K_Bank(3, (bank | 0x20 | 3));
|
||||
SetPROM_8K_Bank(4, (bank | 0x00 | 0));
|
||||
SetPROM_8K_Bank(5, (bank | 0x00 | 1));
|
||||
SetPROM_8K_Bank(6, (bank | 0x00 | 2));
|
||||
SetPROM_8K_Bank(7, (bank | 0x00 | 3));
|
||||
break;
|
||||
case 2:
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
SetPROM_8K_Bank(3, (bank | 0x2e | 3));
|
||||
SetPROM_8K_Bank(4, (bank | 0x02 | 0));
|
||||
SetPROM_8K_Bank(5, (bank | 0x02 | 1));
|
||||
SetPROM_8K_Bank(6, (bank | 0x0e | 2));
|
||||
SetPROM_8K_Bank(7, (bank | 0x0e | 3));
|
||||
break;
|
||||
case 3:
|
||||
SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
SetPROM_8K_Bank(3, (bank | 0x20 | 3));
|
||||
SetPROM_8K_Bank(4, (bank | 0x00 | 0));
|
||||
SetPROM_8K_Bank(5, (bank | 0x00 | 1));
|
||||
SetPROM_8K_Bank(6, (bank | 0x00 | 2));
|
||||
SetPROM_8K_Bank(7, (bank | 0x00 | 3));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper051::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = (byte)mode;
|
||||
p[1] = (byte)bank;
|
||||
}
|
||||
|
||||
//void Mapper051::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
mode = p[0];
|
||||
bank = p[1];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
198
Core/VirtualNes.Core/Mapper/Mapper052.cs
Normal file
198
Core/VirtualNes.Core/Mapper/Mapper052.cs
Normal file
@ -0,0 +1,198 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper052 Konami VRC2 type B //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using VirtualNes.Core.Debug;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper052 : Mapper
|
||||
{
|
||||
|
||||
BYTE[] reg = new byte[9];
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
INT irq_clock;
|
||||
public Mapper052(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
for (byte i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = i;
|
||||
}
|
||||
reg[8] = 0;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
// nes->SetRenderMethod( NES::POST_RENDER );
|
||||
// nes->SetRenderMethod( NES::POST_ALL_RENDER );
|
||||
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (addr >= 0xf000) Debuger.Log($"MPRWR A={addr & 0xFFFF} D={data & 0xFF} L={nes.GetScanline()} CYC={nes.cpu.GetTotalCycles()}\n");
|
||||
if (addr >= 0xf000) Debuger.Log($"MPRWR A={addr & 0xFFFF} RAM={RAM[0x1c0] & 0xFF} L={nes.GetScanline()} CYC={nes.cpu.GetTotalCycles()}\n");
|
||||
switch (addr & 0xFFFF)
|
||||
{
|
||||
case 0x8000:
|
||||
if (reg[8] != 0) SetPROM_8K_Bank(6, data);
|
||||
else SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x9002:
|
||||
reg[8] = (byte)(data & 0x02);
|
||||
break;
|
||||
|
||||
case 0x9004:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
case 0xB001:
|
||||
reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(0, reg[0]);
|
||||
break;
|
||||
case 0xB002:
|
||||
reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
case 0xB003:
|
||||
reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(1, reg[1]);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(2, reg[2]);
|
||||
break;
|
||||
case 0xC002:
|
||||
reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
case 0xC003:
|
||||
reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(3, reg[3]);
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
case 0xD001:
|
||||
reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(4, reg[4]);
|
||||
break;
|
||||
case 0xD002:
|
||||
reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
case 0xD003:
|
||||
reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(5, reg[5]);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(6, reg[6]);
|
||||
break;
|
||||
case 0xE002:
|
||||
reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
case 0xE003:
|
||||
reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4));
|
||||
SetVROM_1K_Bank(7, reg[7]);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
break;
|
||||
case 0xF004:
|
||||
case 0xFF04:
|
||||
RAM[0x7f8] = 1;
|
||||
break;
|
||||
case 0xF008:
|
||||
case 0xFF08:
|
||||
irq_enable = 1;
|
||||
// irq_latch = ((RAM[0x7f8]*2)+0x11)^0xFF; //Akumajou Special - Boku Dracula-kun
|
||||
irq_latch = (byte)(((RAM[0x1c0] * 2) + 0x11) ^ 0xFF); //Teenage Mutant Ninja Turtles
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
nes.cpu.ClrIRQ(CPU.IRQ_MAPPER);
|
||||
break;
|
||||
case 0xF00C:
|
||||
irq_enable = 0;
|
||||
nes.cpu.ClrIRQ(CPU.IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if (irq_enable != null)
|
||||
{
|
||||
irq_clock += cycles * 3;
|
||||
while (irq_clock >= 341)
|
||||
{
|
||||
irq_clock -= 341;
|
||||
irq_counter++;
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.SetIRQ(CPU.IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
84
Core/VirtualNes.Core/Mapper/Mapper057.cs
Normal file
84
Core/VirtualNes.Core/Mapper/Mapper057.cs
Normal file
@ -0,0 +1,84 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper057 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper057 : Mapper
|
||||
{
|
||||
|
||||
BYTE reg;
|
||||
public Mapper057(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 0, 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
reg = 0;
|
||||
}
|
||||
|
||||
//void Mapper057::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
case 0x8001:
|
||||
case 0x8002:
|
||||
case 0x8003:
|
||||
if ((data & 0x40) != 0)
|
||||
{
|
||||
SetVROM_8K_Bank((data & 0x03) + ((reg & 0x10) >> 1) + (reg & 0x07));
|
||||
}
|
||||
break;
|
||||
case 0x8800:
|
||||
reg = data;
|
||||
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(4, ((data & 0x40) >> 6) * 4 + 8 + 0);
|
||||
SetPROM_8K_Bank(5, ((data & 0x40) >> 6) * 4 + 8 + 1);
|
||||
SetPROM_8K_Bank(6, ((data & 0x40) >> 6) * 4 + 8 + 2);
|
||||
SetPROM_8K_Bank(7, ((data & 0x40) >> 6) * 4 + 8 + 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, ((data & 0x60) >> 5) * 2 + 0);
|
||||
SetPROM_8K_Bank(5, ((data & 0x60) >> 5) * 2 + 1);
|
||||
SetPROM_8K_Bank(6, ((data & 0x60) >> 5) * 2 + 0);
|
||||
SetPROM_8K_Bank(7, ((data & 0x60) >> 5) * 2 + 1);
|
||||
}
|
||||
|
||||
SetVROM_8K_Bank((data & 0x07) + ((data & 0x10) >> 1));
|
||||
|
||||
if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper057::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg;
|
||||
}
|
||||
|
||||
//void Mapper057::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg = p[0];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
49
Core/VirtualNes.Core/Mapper/Mapper058.cs
Normal file
49
Core/VirtualNes.Core/Mapper/Mapper058.cs
Normal file
@ -0,0 +1,49 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper058 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper058 : Mapper
|
||||
{
|
||||
public Mapper058(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 0, 1);
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper058::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0x40) != 0)
|
||||
{
|
||||
SetPROM_16K_Bank(4, addr & 0x07);
|
||||
SetPROM_16K_Bank(6, addr & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_32K_Bank((addr & 0x06) >> 1);
|
||||
}
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank((addr & 0x38) >> 3);
|
||||
}
|
||||
|
||||
if ((data & 0x02) != 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
63
Core/VirtualNes.Core/Mapper/Mapper060.cs
Normal file
63
Core/VirtualNes.Core/Mapper/Mapper060.cs
Normal file
@ -0,0 +1,63 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper060 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper060 : Mapper
|
||||
{
|
||||
BYTE patch;
|
||||
BYTE game_sel;
|
||||
public Mapper060(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0xf9c484a0)
|
||||
{ // Reset Based 4-in-1(Unl)
|
||||
SetPROM_16K_Bank(4, game_sel);
|
||||
SetPROM_16K_Bank(6, game_sel);
|
||||
SetVROM_8K_Bank(game_sel);
|
||||
game_sel++;
|
||||
game_sel &= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
patch = 1;
|
||||
SetPROM_32K_Bank(0);
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper060::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if (patch != 0)
|
||||
{
|
||||
if ((addr & 0x80) != 0)
|
||||
{
|
||||
SetPROM_16K_Bank(4, (addr & 0x70) >> 4);
|
||||
SetPROM_16K_Bank(6, (addr & 0x70) >> 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_32K_Bank((addr & 0x70) >> 5);
|
||||
}
|
||||
|
||||
SetVROM_8K_Bank(addr & 0x07);
|
||||
|
||||
if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
43
Core/VirtualNes.Core/Mapper/Mapper061.cs
Normal file
43
Core/VirtualNes.Core/Mapper/Mapper061.cs
Normal file
@ -0,0 +1,43 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper061 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper061 : Mapper
|
||||
{
|
||||
public Mapper061(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
//void Mapper061::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0x30)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x30:
|
||||
SetPROM_32K_Bank(addr & 0x0F);
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x20:
|
||||
SetPROM_16K_Bank(4, ((addr & 0x0F) << 1) | ((addr & 0x20) >> 4));
|
||||
SetPROM_16K_Bank(6, ((addr & 0x0F) << 1) | ((addr & 0x20) >> 4));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((addr & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
49
Core/VirtualNes.Core/Mapper/Mapper062.cs
Normal file
49
Core/VirtualNes.Core/Mapper/Mapper062.cs
Normal file
@ -0,0 +1,49 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper062 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper062 : Mapper
|
||||
{
|
||||
public Mapper062(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0);
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
//void Mapper062::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xFF00)
|
||||
{
|
||||
case 0x8100:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
SetPROM_8K_Bank(5, data + 1);
|
||||
break;
|
||||
case 0x8500:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x8700:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
SetVROM_1K_Bank(0, data + 0);
|
||||
SetVROM_1K_Bank(1, data + 1);
|
||||
SetVROM_1K_Bank(2, data + 2);
|
||||
SetVROM_1K_Bank(3, data + 3);
|
||||
SetVROM_1K_Bank(4, data + 4);
|
||||
SetVROM_1K_Bank(5, data + 5);
|
||||
SetVROM_1K_Bank(6, data + 6);
|
||||
SetVROM_1K_Bank(7, data + 7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
287
Core/VirtualNes.Core/Mapper/Mapper064.cs
Normal file
287
Core/VirtualNes.Core/Mapper/Mapper064.cs
Normal file
@ -0,0 +1,287 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper064 Tengen Rambo-1 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper064 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[3];
|
||||
BYTE irq_enable;
|
||||
BYTE irq_mode;
|
||||
INT irq_counter;
|
||||
INT irq_counter2;
|
||||
BYTE irq_latch;
|
||||
BYTE irq_reset;
|
||||
public Mapper064(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
reg[0] = reg[1] = reg[2] = 0;
|
||||
|
||||
irq_enable = 0;
|
||||
irq_mode = 0;
|
||||
irq_counter = 0;
|
||||
irq_counter2 = 0;
|
||||
irq_latch = 0;
|
||||
irq_reset = 0;
|
||||
}
|
||||
|
||||
//void Mapper064::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "$%04X:$%02X\n", addr, data );
|
||||
switch (addr & 0xF003)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = (byte)(data & 0x0F);
|
||||
reg[1] = (byte)(data & 0x40);
|
||||
reg[2] = (byte)(data & 0x80);
|
||||
break;
|
||||
|
||||
case 0x8001:
|
||||
switch (reg[0])
|
||||
{
|
||||
case 0x00:
|
||||
if (reg[2] != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(4, data + 0);
|
||||
SetVROM_1K_Bank(5, data + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(0, data + 0);
|
||||
SetVROM_1K_Bank(1, data + 1);
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
if (reg[2] != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(6, data + 0);
|
||||
SetVROM_1K_Bank(7, data + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(2, data + 0);
|
||||
SetVROM_1K_Bank(3, data + 1);
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
if (reg[2] != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(0, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
if (reg[2] != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(1, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(5, data);
|
||||
}
|
||||
break;
|
||||
case 0x04:
|
||||
if (reg[2] != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(2, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(6, data);
|
||||
}
|
||||
break;
|
||||
case 0x05:
|
||||
if (reg[2] != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(3, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(7, data);
|
||||
}
|
||||
break;
|
||||
case 0x06:
|
||||
if (reg[1] != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(5, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
case 0x07:
|
||||
if (reg[1] != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(5, data);
|
||||
}
|
||||
break;
|
||||
case 0x08:
|
||||
SetVROM_1K_Bank(1, data);
|
||||
break;
|
||||
case 0x09:
|
||||
SetVROM_1K_Bank(3, data);
|
||||
break;
|
||||
case 0x0F:
|
||||
if (reg[1] != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(4, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_8K_Bank(6, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
irq_latch = data;
|
||||
if (irq_reset != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
break;
|
||||
case 0xC001:
|
||||
irq_reset = 0xFF;
|
||||
irq_counter = irq_latch;
|
||||
irq_mode = (byte)(data & 0x01);
|
||||
break;
|
||||
case 0xE000:
|
||||
irq_enable = 0;
|
||||
if (irq_reset != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
irq_enable = 0xFF;
|
||||
if (irq_reset != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper064::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if (irq_mode == 0)
|
||||
return;
|
||||
|
||||
irq_counter2 += cycles;
|
||||
while (irq_counter2 >= 4)
|
||||
{
|
||||
irq_counter2 -= 4;
|
||||
if (irq_counter >= 0)
|
||||
{
|
||||
irq_counter--;
|
||||
if (irq_counter < 0)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper064::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_mode != 0)
|
||||
return;
|
||||
|
||||
irq_reset = 0;
|
||||
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_counter >= 0)
|
||||
{
|
||||
irq_counter--;
|
||||
if (irq_counter < 0)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
irq_reset = 1;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper064::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = irq_enable;
|
||||
p[4] = irq_mode;
|
||||
p[5] = irq_latch;
|
||||
p[6] = irq_reset;
|
||||
//*((INT*)&p[8]) = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 8);
|
||||
//* ((INT*)&p[12]) = irq_counter2;
|
||||
BitConverter.GetBytes(irq_counter2).CopyTo(p, 12);
|
||||
}
|
||||
|
||||
//void Mapper064::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
irq_enable = p[3];
|
||||
irq_mode = p[4];
|
||||
irq_latch = p[5];
|
||||
irq_reset = p[6];
|
||||
//irq_counter = *((INT*)&p[8]);
|
||||
irq_counter = BitConverter.ToInt32(p, 8);
|
||||
//irq_counter2 = *((INT*)&p[12]);
|
||||
irq_counter2 = BitConverter.ToInt32(p, 12);
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
193
Core/VirtualNes.Core/Mapper/Mapper065.cs
Normal file
193
Core/VirtualNes.Core/Mapper/Mapper065.cs
Normal file
@ -0,0 +1,193 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper065 Irem H3001 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper065 : Mapper
|
||||
{
|
||||
BYTE patch;
|
||||
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
INT irq_latch;
|
||||
public Mapper065(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
// Kaiketsu Yanchamaru 3(J)
|
||||
if (nes.rom.GetPROM_CRC() == 0xe30b7f64)
|
||||
{
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
}
|
||||
|
||||
//void Mapper065::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
if (patch == 0)
|
||||
{
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9001:
|
||||
if (patch != 0)
|
||||
{
|
||||
if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9003:
|
||||
if (patch == 0)
|
||||
{
|
||||
irq_enable = (byte)(data & 0x8);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
}
|
||||
break;
|
||||
case 0x9004:
|
||||
if (patch == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
break;
|
||||
case 0x9005:
|
||||
if (patch != 0)
|
||||
{
|
||||
irq_counter = (BYTE)(data << 1);
|
||||
irq_enable = data;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_latch = (irq_latch & 0x00FF) | ((INT)data << 8);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9006:
|
||||
if (patch != 0)
|
||||
{
|
||||
irq_enable = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_latch = (irq_latch & 0xFF00) | data;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
SetVROM_1K_Bank((byte)(addr & 0x0007), data);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
case 0xC000:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper065::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (patch != 0)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper065::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if (patch == 0)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (irq_counter <= 0)
|
||||
{
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter -= cycles;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper065::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
//*(INT*)&p[1] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 1);
|
||||
//* (INT*)&p[5] = irq_latch;
|
||||
BitConverter.GetBytes(irq_latch).CopyTo(p, 5);
|
||||
}
|
||||
|
||||
//void Mapper065::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
//irq_counter = *(INT*)&p[1];
|
||||
irq_counter = BitConverter.ToInt32(p, 1);
|
||||
//irq_latch = *(INT*)&p[5];
|
||||
irq_latch = BitConverter.ToInt32(p, 5);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
44
Core/VirtualNes.Core/Mapper/Mapper066.cs
Normal file
44
Core/VirtualNes.Core/Mapper/Mapper066.cs
Normal file
@ -0,0 +1,44 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper066 Bandai 74161 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper066 : Mapper
|
||||
{
|
||||
public Mapper066(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
|
||||
// if( nes->rom->GetPROM_CRC() == 0xe30552db ) { // Paris-Dakar Rally Special
|
||||
// nes->SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
}
|
||||
|
||||
//void Mapper066::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr >= 0x6000)
|
||||
{
|
||||
SetPROM_32K_Bank((data & 0xF0) >> 4);
|
||||
SetVROM_8K_Bank(data & 0x0F);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper066::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_32K_Bank((data & 0xF0) >> 4);
|
||||
SetVROM_8K_Bank(data & 0x0F);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
142
Core/VirtualNes.Core/Mapper/Mapper067.cs
Normal file
142
Core/VirtualNes.Core/Mapper/Mapper067.cs
Normal file
@ -0,0 +1,142 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper067 SunSoft Mapper 3 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper067 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
BYTE irq_occur;
|
||||
BYTE irq_toggle;
|
||||
INT irq_counter;
|
||||
public Mapper067(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_toggle = 0;
|
||||
irq_counter = 0;
|
||||
irq_occur = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
SetVROM_4K_Bank(0, 0);
|
||||
SetVROM_4K_Bank(4, VROM_4K_SIZE - 1);
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x7f2a04bf)
|
||||
{ // For Fantasy Zone 2(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper067::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF800)
|
||||
{
|
||||
case 0x8800:
|
||||
SetVROM_2K_Bank(0, data);
|
||||
break;
|
||||
case 0x9800:
|
||||
SetVROM_2K_Bank(2, data);
|
||||
break;
|
||||
case 0xA800:
|
||||
SetVROM_2K_Bank(4, data);
|
||||
break;
|
||||
case 0xB800:
|
||||
SetVROM_2K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0xC800:
|
||||
if (irq_toggle == 0)
|
||||
{
|
||||
irq_counter = (irq_counter & 0x00FF) | ((INT)data << 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter = (irq_counter & 0xFF00) | ((INT)data & 0xFF);
|
||||
}
|
||||
irq_toggle ^= 1;
|
||||
irq_occur = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xD800:
|
||||
irq_enable = (byte)(data & 0x10);
|
||||
irq_toggle = 0;
|
||||
irq_occur = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xE800:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0xF800:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper067::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if ((irq_counter -= cycles) <= 0)
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_occur = 0xFF;
|
||||
irq_counter = 0xFFFF;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
|
||||
// if( irq_occur ) {
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
// }
|
||||
}
|
||||
|
||||
//void Mapper067::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
p[1] = irq_occur;
|
||||
p[2] = irq_toggle;
|
||||
//*((INT*)&p[3]) = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 3);
|
||||
}
|
||||
|
||||
//void Mapper067::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
irq_occur = p[1];
|
||||
irq_toggle = p[2];
|
||||
//irq_counter = *((INT*)&p[3]);
|
||||
irq_counter = BitConverter.ToInt32(p, 3);
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
162
Core/VirtualNes.Core/Mapper/Mapper068.cs
Normal file
162
Core/VirtualNes.Core/Mapper/Mapper068.cs
Normal file
@ -0,0 +1,162 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper068 SunSoft Mapper 4 (After Burner II) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper068 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[4];
|
||||
BYTE coin;
|
||||
public Mapper068(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
reg[0] = reg[1] = reg[2] = reg[3] = 0;
|
||||
coin = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
|
||||
#if FALSE //0
|
||||
BYTE Mapper068::ExRead( WORD addr )
|
||||
{
|
||||
if( addr == 0x4020 ) {
|
||||
DEBUGOUT( "RD $4020:%02X\n", coin );
|
||||
return coin;
|
||||
}
|
||||
|
||||
return addr>>8;
|
||||
}
|
||||
|
||||
void Mapper068::ExWrite( WORD addr, BYTE data )
|
||||
{
|
||||
if( addr == 0x4020 ) {
|
||||
DEBUGOUT( "WR $4020:%02X\n", data );
|
||||
coin = data;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//void Mapper068::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0x8000:
|
||||
SetVROM_2K_Bank(0, data);
|
||||
break;
|
||||
case 0x9000:
|
||||
SetVROM_2K_Bank(2, data);
|
||||
break;
|
||||
case 0xA000:
|
||||
SetVROM_2K_Bank(4, data);
|
||||
break;
|
||||
case 0xB000:
|
||||
SetVROM_2K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
reg[2] = data;
|
||||
SetBank();
|
||||
break;
|
||||
case 0xD000:
|
||||
reg[3] = data;
|
||||
SetBank();
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[0] = (byte)((data & 0x10) >> 4);
|
||||
reg[1] = (byte)(data & 0x03);
|
||||
SetBank();
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank()
|
||||
{
|
||||
if (reg[0] != 0)
|
||||
{
|
||||
switch (reg[1])
|
||||
{
|
||||
case 0:
|
||||
SetVROM_1K_Bank(8, (INT)reg[2] + 0x80);
|
||||
SetVROM_1K_Bank(9, (INT)reg[3] + 0x80);
|
||||
SetVROM_1K_Bank(10, (INT)reg[2] + 0x80);
|
||||
SetVROM_1K_Bank(11, (INT)reg[3] + 0x80);
|
||||
break;
|
||||
case 1:
|
||||
SetVROM_1K_Bank(8, (INT)reg[2] + 0x80);
|
||||
SetVROM_1K_Bank(9, (INT)reg[2] + 0x80);
|
||||
SetVROM_1K_Bank(10, (INT)reg[3] + 0x80);
|
||||
SetVROM_1K_Bank(11, (INT)reg[3] + 0x80);
|
||||
break;
|
||||
case 2:
|
||||
SetVROM_1K_Bank(8, (INT)reg[2] + 0x80);
|
||||
SetVROM_1K_Bank(9, (INT)reg[2] + 0x80);
|
||||
SetVROM_1K_Bank(10, (INT)reg[2] + 0x80);
|
||||
SetVROM_1K_Bank(11, (INT)reg[2] + 0x80);
|
||||
break;
|
||||
case 3:
|
||||
SetVROM_1K_Bank(8, (INT)reg[3] + 0x80);
|
||||
SetVROM_1K_Bank(9, (INT)reg[3] + 0x80);
|
||||
SetVROM_1K_Bank(10, (INT)reg[3] + 0x80);
|
||||
SetVROM_1K_Bank(11, (INT)reg[3] + 0x80);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (reg[1])
|
||||
{
|
||||
case 0:
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
break;
|
||||
case 1:
|
||||
SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
break;
|
||||
case 2:
|
||||
SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
case 3:
|
||||
SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper068::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = reg[3];
|
||||
}
|
||||
|
||||
//void Mapper068::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
reg[3] = p[3];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
179
Core/VirtualNes.Core/Mapper/Mapper069.cs
Normal file
179
Core/VirtualNes.Core/Mapper/Mapper069.cs
Normal file
@ -0,0 +1,179 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper069 SunSoft FME-7 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper069 : Mapper
|
||||
{
|
||||
BYTE patch;
|
||||
|
||||
BYTE reg;
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
public Mapper069(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
reg = 0;
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
nes.apu.SelectExSound(32);
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK);
|
||||
patch = 0;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xfeac6916)
|
||||
{ // Honoo no Toukyuuji - Dodge Danpei 2(J)
|
||||
// nes.SetIrqType( NES::IRQ_HSYNC );
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xad28aef6)
|
||||
{ // Dynamite Batman(J) / Dynamite Batman - Return of the Joker(U)
|
||||
patch = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper069::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xE000)
|
||||
{
|
||||
case 0x8000:
|
||||
reg = data;
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
switch (reg & 0x0F)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
SetVROM_1K_Bank((byte)(reg & 0x07), data);
|
||||
break;
|
||||
case 0x08:
|
||||
if (patch == 0 && (data & 0x40) == 0)
|
||||
{
|
||||
SetPROM_8K_Bank(3, data);
|
||||
}
|
||||
break;
|
||||
case 0x09:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x0A:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
case 0x0B:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0x0C:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0x0D:
|
||||
irq_enable = data;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0x0E:
|
||||
irq_counter = (irq_counter & 0xFF00) | data;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0x0F:
|
||||
irq_counter = (irq_counter & 0x00FF) | (data << 8);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
case 0xE000:
|
||||
nes.apu.ExWrite(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper069::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
//if (irq_enable && (nes.GetIrqType() == NES::IRQ_CLOCK))
|
||||
if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_HSYNC))
|
||||
{
|
||||
irq_counter -= cycles;
|
||||
if (irq_counter <= 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
irq_enable = 0;
|
||||
irq_counter = 0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper069::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_HSYNC))
|
||||
{
|
||||
irq_counter -= 114;
|
||||
if (irq_counter <= 0)
|
||||
{
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
irq_enable = 0;
|
||||
irq_counter = 0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper069::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg;
|
||||
p[1] = irq_enable;
|
||||
//*(INT*)&p[2] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 2);
|
||||
}
|
||||
|
||||
//void Mapper069::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg = p[0];
|
||||
irq_enable = p[1];
|
||||
//irq_counter = *(INT*)&p[2];
|
||||
irq_counter = BitConverter.ToInt32(p, 2);
|
||||
}
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
61
Core/VirtualNes.Core/Mapper/Mapper070.cs
Normal file
61
Core/VirtualNes.Core/Mapper/Mapper070.cs
Normal file
@ -0,0 +1,61 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper070 Bandai 74161 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper070 : Mapper
|
||||
{
|
||||
BYTE patch;
|
||||
public Mapper070(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
patch = 0;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xa59ca2ef)
|
||||
{ // Kamen Rider Club(J)
|
||||
patch = 1;
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
if (crc == 0x10bb8f9a)
|
||||
{ // Family Trainer - Manhattan Police(J)
|
||||
patch = 1;
|
||||
}
|
||||
if (crc == 0x0cd00488)
|
||||
{ // Space Shadow(J)
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
//void Mapper070::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_16K_Bank(4, (data & 0x70) >> 4);
|
||||
SetVROM_8K_Bank(data & 0x0F);
|
||||
|
||||
if (patch != 0)
|
||||
{
|
||||
if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
50
Core/VirtualNes.Core/Mapper/Mapper071.cs
Normal file
50
Core/VirtualNes.Core/Mapper/Mapper071.cs
Normal file
@ -0,0 +1,50 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper071 Camerica //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper071 : Mapper
|
||||
{
|
||||
public Mapper071(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
//void Mapper071::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0xE000) == 0x6000)
|
||||
{
|
||||
SetPROM_16K_Bank(4, data);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper071::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0x9000:
|
||||
if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
case 0xD000:
|
||||
case 0xE000:
|
||||
case 0xF000:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
55
Core/VirtualNes.Core/Mapper/Mapper072.cs
Normal file
55
Core/VirtualNes.Core/Mapper/Mapper072.cs
Normal file
@ -0,0 +1,55 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper072 Jaleco/Type1 lower bank switch //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using VirtualNes.Core.Debug;
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper072 : Mapper
|
||||
{
|
||||
public Mapper072(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper072::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetPROM_16K_Bank(4, data & 0x0F);
|
||||
}
|
||||
else if ((data & 0x40) != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(data & 0x0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addr >= 0xC100 && addr <= 0xC11F && data == 0x20)
|
||||
{
|
||||
Debuger.Log($"SOUND CODE:{addr & 0x1F:X2}");
|
||||
|
||||
// OSDにするべきか…
|
||||
if (Supporter.Config.sound.bExtraSoundEnable)
|
||||
{
|
||||
//TODO : 似乎VirtuaNES有直接播放某个音频文件的功能
|
||||
//DirectSound.EsfAllStop();
|
||||
//DirectSound.EsfPlay(ESF_MOETENNIS_00 + (addr & 0x1F));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
96
Core/VirtualNes.Core/Mapper/Mapper073.cs
Normal file
96
Core/VirtualNes.Core/Mapper/Mapper073.cs
Normal file
@ -0,0 +1,96 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper073 Konami VRC3 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper073 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
public Mapper073(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
//void Mapper073::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0xF000:
|
||||
SetPROM_16K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0x8000:
|
||||
irq_counter = (irq_counter & 0xFFF0) | (data & 0x0F);
|
||||
break;
|
||||
case 0x9000:
|
||||
irq_counter = (irq_counter & 0xFF0F) | ((data & 0x0F) << 4);
|
||||
break;
|
||||
case 0xA000:
|
||||
irq_counter = (irq_counter & 0xF0FF) | ((data & 0x0F) << 8);
|
||||
break;
|
||||
case 0xB000:
|
||||
irq_counter = (irq_counter & 0x0FFF) | ((data & 0x0F) << 12);
|
||||
break;
|
||||
case 0xC000:
|
||||
irq_enable = (byte)(data & 0x02);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xD000:
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper073::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if ((irq_counter += cycles) >= 0xFFFF)
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter &= 0xFFFF;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper073::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
//*(INT*)&p[1] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 1);
|
||||
}
|
||||
|
||||
//void Mapper073::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
//irq_counter = *(INT*)&p[1];
|
||||
irq_counter = BitConverter.ToInt32(p, 1);
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
311
Core/VirtualNes.Core/Mapper/Mapper074.cs
Normal file
311
Core/VirtualNes.Core/Mapper/Mapper074.cs
Normal file
@ -0,0 +1,311 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper074 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper074 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[8];
|
||||
BYTE prg0, prg1;
|
||||
BYTE chr01, chr23, chr4, chr5, chr6, chr7;
|
||||
BYTE we_sram;
|
||||
|
||||
BYTE irq_type;
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
BYTE irq_request;
|
||||
|
||||
BYTE patch;
|
||||
public Mapper074(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0x00;
|
||||
}
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
SetBank_CPU();
|
||||
|
||||
chr01 = 0;
|
||||
chr23 = 2;
|
||||
chr4 = 4;
|
||||
chr5 = 5;
|
||||
chr6 = 6;
|
||||
chr7 = 7;
|
||||
SetBank_PPU();
|
||||
|
||||
we_sram = 0; // Disable
|
||||
irq_enable = 0; // Disable
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_request = 0;
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
patch = 0;
|
||||
if (crc == 0x37ae04a8)
|
||||
{
|
||||
patch = 1;
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper074::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() );
|
||||
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = data;
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[1] = data;
|
||||
|
||||
switch (reg[0] & 0x07)
|
||||
{
|
||||
case 0x00:
|
||||
chr01 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x01:
|
||||
chr23 = (byte)(data & 0xFE);
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x02:
|
||||
chr4 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x03:
|
||||
chr5 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x04:
|
||||
chr6 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x05:
|
||||
chr7 = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
case 0x06:
|
||||
prg0 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
if (!nes.rom.Is4SCREEN())
|
||||
{
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
case 0xA001:
|
||||
reg[3] = data;
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[4] = data;
|
||||
irq_counter = data;
|
||||
irq_request = 0;
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[5] = data;
|
||||
irq_latch = data;
|
||||
irq_request = 0;
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[6] = data;
|
||||
irq_enable = 0;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[7] = data;
|
||||
irq_enable = 1;
|
||||
irq_request = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//void Mapper074::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0 && irq_request == 0)
|
||||
{
|
||||
if (scanline == 0)
|
||||
{
|
||||
if (irq_counter != 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
}
|
||||
if ((irq_counter--) == 0)
|
||||
{
|
||||
irq_request = 0xFF;
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_CPU()
|
||||
{
|
||||
if ((reg[0] & 0x40) != 0)
|
||||
{
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
// SetVROM_8K_Bank( chr4, chr5, chr6, chr7,
|
||||
// chr01, chr01+1, chr23, chr23+1 );
|
||||
SetBank_PPUSUB(4, chr01 + 0);
|
||||
SetBank_PPUSUB(5, chr01 + 1);
|
||||
SetBank_PPUSUB(6, chr23 + 0);
|
||||
SetBank_PPUSUB(7, chr23 + 1);
|
||||
SetBank_PPUSUB(0, chr4);
|
||||
SetBank_PPUSUB(1, chr5);
|
||||
SetBank_PPUSUB(2, chr6);
|
||||
SetBank_PPUSUB(3, chr7);
|
||||
}
|
||||
else
|
||||
{
|
||||
// SetVROM_8K_Bank( chr01, chr01+1, chr23, chr23+1,
|
||||
// chr4, chr5, chr6, chr7 );
|
||||
SetBank_PPUSUB(0, chr01 + 0);
|
||||
SetBank_PPUSUB(1, chr01 + 1);
|
||||
SetBank_PPUSUB(2, chr23 + 0);
|
||||
SetBank_PPUSUB(3, chr23 + 1);
|
||||
SetBank_PPUSUB(4, chr4);
|
||||
SetBank_PPUSUB(5, chr5);
|
||||
SetBank_PPUSUB(6, chr6);
|
||||
SetBank_PPUSUB(7, chr7);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(0, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(1, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(2, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(3, chr7 & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(4, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(5, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(6, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(7, chr7 & 0x07);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_PPUSUB(int bank, int page)
|
||||
{
|
||||
if (patch == 0 && (page == 8 || page == 9))
|
||||
{
|
||||
SetCRAM_1K_Bank((byte)bank, page & 7);
|
||||
}
|
||||
else if (patch == 1 && page >= 128)
|
||||
{
|
||||
SetCRAM_1K_Bank((byte)bank, page & 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank((byte)bank, page);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper074::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
p[i] = reg[i];
|
||||
}
|
||||
p[8] = prg0;
|
||||
p[9] = prg1;
|
||||
p[10] = chr01;
|
||||
p[11] = chr23;
|
||||
p[12] = chr4;
|
||||
p[13] = chr5;
|
||||
p[14] = chr6;
|
||||
p[15] = chr7;
|
||||
p[16] = irq_enable;
|
||||
p[17] = irq_counter;
|
||||
p[18] = irq_latch;
|
||||
p[19] = irq_request;
|
||||
}
|
||||
|
||||
//void Mapper074::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = p[i];
|
||||
}
|
||||
prg0 = p[8];
|
||||
prg1 = p[9];
|
||||
chr01 = p[10];
|
||||
chr23 = p[11];
|
||||
chr4 = p[12];
|
||||
chr5 = p[13];
|
||||
chr6 = p[14];
|
||||
chr7 = p[15];
|
||||
irq_enable = p[16];
|
||||
irq_counter = p[17];
|
||||
irq_latch = p[18];
|
||||
irq_request = p[19];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
87
Core/VirtualNes.Core/Mapper/Mapper075.cs
Normal file
87
Core/VirtualNes.Core/Mapper/Mapper075.cs
Normal file
@ -0,0 +1,87 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper075 Konami VRC1/Jaleco D65005 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper075 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[2];
|
||||
public Mapper075(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
reg[0] = 0;
|
||||
reg[1] = 1;
|
||||
}
|
||||
|
||||
//void Mapper075::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0x8000:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
|
||||
reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x02) << 3));
|
||||
reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x04) << 2));
|
||||
SetVROM_4K_Bank(0, reg[0]);
|
||||
SetVROM_4K_Bank(4, reg[1]);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
case 0xC000:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
reg[0] = (byte)((reg[0] & 0x10) | (data & 0x0F));
|
||||
SetVROM_4K_Bank(0, reg[0]);
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
reg[1] = (byte)((reg[1] & 0x10) | (data & 0x0F));
|
||||
SetVROM_4K_Bank(4, reg[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper075::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
}
|
||||
|
||||
//void Mapper075::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
79
Core/VirtualNes.Core/Mapper/Mapper076.cs
Normal file
79
Core/VirtualNes.Core/Mapper/Mapper076.cs
Normal file
@ -0,0 +1,79 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper076 Namcot 109 (女神転生) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper076 : Mapper
|
||||
{
|
||||
BYTE reg;
|
||||
public Mapper076(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE >= 8)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper076::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
reg = data;
|
||||
break;
|
||||
case 0x8001:
|
||||
switch (reg & 0x07)
|
||||
{
|
||||
case 2:
|
||||
SetVROM_2K_Bank(0, data);
|
||||
break;
|
||||
case 3:
|
||||
SetVROM_2K_Bank(2, data);
|
||||
break;
|
||||
case 4:
|
||||
SetVROM_2K_Bank(4, data);
|
||||
break;
|
||||
case 5:
|
||||
SetVROM_2K_Bank(6, data);
|
||||
break;
|
||||
case 6:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 7:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper076::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg;
|
||||
}
|
||||
|
||||
//void Mapper076::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg = p[0];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
35
Core/VirtualNes.Core/Mapper/Mapper077.cs
Normal file
35
Core/VirtualNes.Core/Mapper/Mapper077.cs
Normal file
@ -0,0 +1,35 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper077 Irem Early Mapper #0 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper077 : Mapper
|
||||
{
|
||||
public Mapper077(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0);
|
||||
|
||||
SetVROM_2K_Bank(0, 0);
|
||||
SetCRAM_2K_Bank(2, 1);
|
||||
SetCRAM_2K_Bank(4, 2);
|
||||
SetCRAM_2K_Bank(6, 3);
|
||||
}
|
||||
|
||||
//void Mapper077::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetPROM_32K_Bank(data & 0x07);
|
||||
|
||||
SetVROM_2K_Bank(0, (data & 0xF0) >> 4);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
41
Core/VirtualNes.Core/Mapper/Mapper078.cs
Normal file
41
Core/VirtualNes.Core/Mapper/Mapper078.cs
Normal file
@ -0,0 +1,41 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper078 Jaleco(Cosmo Carrier) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper078 : Mapper
|
||||
{
|
||||
public Mapper078(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper078::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "MAP78 WR $%04X=$%02X L=%d\n", addr, data, nes->GetScanline() );
|
||||
SetPROM_16K_Bank(4, data & 0x0F);
|
||||
SetVROM_8K_Bank((data & 0xF0) >> 4);
|
||||
|
||||
if ((addr & 0xFE00) != 0xFE00)
|
||||
{
|
||||
if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
37
Core/VirtualNes.Core/Mapper/Mapper079.cs
Normal file
37
Core/VirtualNes.Core/Mapper/Mapper079.cs
Normal file
@ -0,0 +1,37 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper079 Nina-3 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper079 : Mapper
|
||||
{
|
||||
public Mapper079(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper079::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0x0100) != 0)
|
||||
{
|
||||
SetPROM_32K_Bank((data >> 3) & 0x01);
|
||||
SetVROM_8K_Bank(data & 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
102
Core/VirtualNes.Core/Mapper/Mapper080.cs
Normal file
102
Core/VirtualNes.Core/Mapper/Mapper080.cs
Normal file
@ -0,0 +1,102 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper080 Taito X1-005 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper080 : Mapper
|
||||
{
|
||||
public Mapper080(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper080::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x7EF0:
|
||||
SetVROM_2K_Bank(0, (data >> 1) & 0x3F);
|
||||
if (PROM_8K_SIZE == 32)
|
||||
{
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetVRAM_1K_Bank(8, 1);
|
||||
SetVRAM_1K_Bank(9, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVRAM_1K_Bank(8, 0);
|
||||
SetVRAM_1K_Bank(9, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7EF1:
|
||||
SetVROM_2K_Bank(2, (data >> 1) & 0x3F);
|
||||
if (PROM_8K_SIZE == 32)
|
||||
{
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
SetVRAM_1K_Bank(10, 1);
|
||||
SetVRAM_1K_Bank(11, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVRAM_1K_Bank(10, 0);
|
||||
SetVRAM_1K_Bank(11, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7EF2:
|
||||
SetVROM_1K_Bank(4, data);
|
||||
break;
|
||||
case 0x7EF3:
|
||||
SetVROM_1K_Bank(5, data);
|
||||
break;
|
||||
case 0x7EF4:
|
||||
SetVROM_1K_Bank(6, data);
|
||||
break;
|
||||
case 0x7EF5:
|
||||
SetVROM_1K_Bank(7, data);
|
||||
break;
|
||||
|
||||
case 0x7EF6:
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
break;
|
||||
|
||||
case 0x7EFA:
|
||||
case 0x7EFB:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x7EFC:
|
||||
case 0x7EFD:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
case 0x7EFE:
|
||||
case 0x7EFF:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
default:
|
||||
base.WriteLow(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
116
Core/VirtualNes.Core/Mapper/Mapper082.cs
Normal file
116
Core/VirtualNes.Core/Mapper/Mapper082.cs
Normal file
@ -0,0 +1,116 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper082 Taito C075 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper082 : Mapper
|
||||
{
|
||||
BYTE reg;
|
||||
|
||||
public Mapper082(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
reg = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
|
||||
//void Mapper082::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x7EF0:
|
||||
if (reg != 0)
|
||||
{
|
||||
SetVROM_2K_Bank(4, data >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_2K_Bank(0, data >> 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7EF1:
|
||||
if (reg != 0)
|
||||
{
|
||||
SetVROM_2K_Bank(6, data >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_2K_Bank(2, data >> 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7EF2:
|
||||
if (reg != 0) SetVROM_1K_Bank(0, data);
|
||||
else SetVROM_1K_Bank(4, data);
|
||||
break;
|
||||
case 0x7EF3:
|
||||
if (reg != 0) SetVROM_1K_Bank(1, data);
|
||||
else SetVROM_1K_Bank(5, data);
|
||||
break;
|
||||
case 0x7EF4:
|
||||
if (reg != 0) SetVROM_1K_Bank(2, data);
|
||||
else SetVROM_1K_Bank(6, data);
|
||||
break;
|
||||
case 0x7EF5:
|
||||
if (reg != 0) SetVROM_1K_Bank(3, data);
|
||||
else SetVROM_1K_Bank(7, data);
|
||||
break;
|
||||
|
||||
case 0x7EF6:
|
||||
reg = (byte)(data & 0x02);
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
break;
|
||||
|
||||
case 0x7EFA:
|
||||
SetPROM_8K_Bank(4, data >> 2);
|
||||
break;
|
||||
case 0x7EFB:
|
||||
SetPROM_8K_Bank(5, data >> 2);
|
||||
break;
|
||||
case 0x7EFC:
|
||||
SetPROM_8K_Bank(6, data >> 2);
|
||||
break;
|
||||
default:
|
||||
base.WriteLow(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper082::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg;
|
||||
}
|
||||
|
||||
//void Mapper082::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg = p[0];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
247
Core/VirtualNes.Core/Mapper/Mapper083.cs
Normal file
247
Core/VirtualNes.Core/Mapper/Mapper083.cs
Normal file
@ -0,0 +1,247 @@
|
||||
/////////////////////////////
|
||||
// Mapper083 Cony //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper083 : Mapper
|
||||
{
|
||||
BYTE[] reg = new byte[3];
|
||||
INT chr_bank;
|
||||
BYTE irq_enable;
|
||||
INT irq_counter;
|
||||
|
||||
BYTE patch;
|
||||
public Mapper083(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
for (INT i = 0; i < 3; i++)
|
||||
{
|
||||
reg[i] = 0x00;
|
||||
}
|
||||
|
||||
if (PROM_8K_SIZE >= 32)
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 30, 31);
|
||||
reg[1] = 0x30;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
chr_bank = 0;
|
||||
|
||||
irq_enable = 0; // Disable
|
||||
irq_counter = 0;
|
||||
|
||||
patch = 0;
|
||||
if (nes.rom.GetPROM_CRC() == 0x1461D1F8)
|
||||
{
|
||||
patch = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//BYTE Mapper083::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
if ((addr & 0x5100) == 0x5100)
|
||||
{
|
||||
return reg[2];
|
||||
}
|
||||
else if (addr >= 0x6000)
|
||||
{
|
||||
return base.ReadLow(addr);
|
||||
}
|
||||
return (BYTE)(addr >> 8);
|
||||
}
|
||||
|
||||
//void Mapper083::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() );
|
||||
switch (addr)
|
||||
{
|
||||
case 0x5101:
|
||||
case 0x5102:
|
||||
case 0x5103:
|
||||
reg[2] = data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (addr >= 0x6000)
|
||||
{
|
||||
base.WriteLow(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper083::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() );
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
case 0xB000:
|
||||
case 0xB0FF:
|
||||
case 0xB1FF:
|
||||
reg[0] = data;
|
||||
chr_bank = (data & 0x30) << 4;
|
||||
SetPROM_16K_Bank(4, data);
|
||||
SetPROM_16K_Bank(6, (data & 0x30) | 0x0F);
|
||||
break;
|
||||
|
||||
case 0x8100:
|
||||
reg[1] = (byte)(data & 0x80);
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0x8200:
|
||||
irq_counter = (irq_counter & 0xFF00) | (INT)data;
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
case 0x8201:
|
||||
irq_counter = (irq_counter & 0x00FF) | ((INT)data << 8);
|
||||
irq_enable = reg[1];
|
||||
// nes.cpu.ClrIRQ( IRQ_MAPPER );
|
||||
break;
|
||||
|
||||
case 0x8300:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x8301:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
case 0x8302:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0x8310:
|
||||
if (patch != 0)
|
||||
{
|
||||
SetVROM_2K_Bank(0, chr_bank | data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(0, chr_bank | data);
|
||||
}
|
||||
break;
|
||||
case 0x8311:
|
||||
if (patch != 0)
|
||||
{
|
||||
SetVROM_2K_Bank(2, chr_bank | data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(1, chr_bank | data);
|
||||
}
|
||||
break;
|
||||
case 0x8312:
|
||||
SetVROM_1K_Bank(2, chr_bank | data);
|
||||
break;
|
||||
case 0x8313:
|
||||
SetVROM_1K_Bank(3, chr_bank | data);
|
||||
break;
|
||||
case 0x8314:
|
||||
SetVROM_1K_Bank(4, chr_bank | data);
|
||||
break;
|
||||
case 0x8315:
|
||||
SetVROM_1K_Bank(5, chr_bank | data);
|
||||
break;
|
||||
case 0x8316:
|
||||
if (patch != 0)
|
||||
{
|
||||
SetVROM_2K_Bank(4, chr_bank | data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(6, chr_bank | data);
|
||||
}
|
||||
break;
|
||||
case 0x8317:
|
||||
if (patch != 0)
|
||||
{
|
||||
SetVROM_2K_Bank(6, chr_bank | data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_1K_Bank(7, chr_bank | data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8318:
|
||||
SetPROM_16K_Bank(4, (reg[0] & 0x30) | data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper083::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
if (irq_counter <= 113)
|
||||
{
|
||||
// nes.cpu.IRQ();
|
||||
irq_enable = 0;
|
||||
// nes.cpu.SetIRQ( IRQ_MAPPER );
|
||||
nes.cpu.SetIRQ(IRQ_TRIGGER);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_counter -= 113;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper083::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
//*(INT*)&p[3] = chr_bank;
|
||||
BitConverter.GetBytes(chr_bank).CopyTo(p, 3);
|
||||
p[7] = irq_enable;
|
||||
//*(INT*)&p[8] = irq_counter;
|
||||
BitConverter.GetBytes(irq_counter).CopyTo(p, 8);
|
||||
}
|
||||
|
||||
//void Mapper083::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
//chr_bank = *(INT*)&p[3];
|
||||
chr_bank = BitConverter.ToInt32(p, 3);
|
||||
irq_enable = p[7];
|
||||
//irq_counter = *(INT*)&p[8];
|
||||
irq_counter = BitConverter.ToInt32(p, 8);
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
240
Core/VirtualNes.Core/Mapper/Mapper085.cs
Normal file
240
Core/VirtualNes.Core/Mapper/Mapper085.cs
Normal file
@ -0,0 +1,240 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper085 Konami VRC7 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using System;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper085 : Mapper
|
||||
{
|
||||
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
INT irq_clock;
|
||||
public Mapper085(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_clock = 0;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_8K_Bank(0);
|
||||
}
|
||||
|
||||
#if FALSE//0
|
||||
// DWORD crc = nes.rom.GetPROM_CRC();
|
||||
// if( crc == 0x1aa0479c ) { // For Tiny Toon Adventures 2 - Montana Land he Youkoso(J)
|
||||
// nes.SetRenderMethod( NES::PRE_RENDER );
|
||||
// }
|
||||
// if( crc == 0x33ce3ff0 ) { // For Lagrange Point(J)
|
||||
// nes.SetRenderMethod( NES::TILE_RENDER );
|
||||
// }
|
||||
#endif
|
||||
nes.apu.SelectExSound(2);
|
||||
}
|
||||
|
||||
//void Mapper085::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF038)
|
||||
{
|
||||
case 0x8000:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x8008:
|
||||
case 0x8010:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
case 0x9000:
|
||||
SetPROM_8K_Bank(6, data);
|
||||
break;
|
||||
|
||||
case 0x9010:
|
||||
case 0x9030:
|
||||
nes.apu.ExWrite(addr, data);
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(0, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(0, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xA008:
|
||||
case 0xA010:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(1, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(1, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(2, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(2, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xB008:
|
||||
case 0xB010:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(3, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(3, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(4, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(4, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xC008:
|
||||
case 0xC010:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(5, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(5, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(6, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(6, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xD008:
|
||||
case 0xD010:
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(7, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(7, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
data &= 0x03;
|
||||
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
break;
|
||||
|
||||
case 0xE008:
|
||||
case 0xE010:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xF000:
|
||||
irq_enable = (byte)(data & 0x03);
|
||||
irq_counter = irq_latch;
|
||||
irq_clock = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
|
||||
case 0xF008:
|
||||
case 0xF010:
|
||||
irq_enable = (byte)((irq_enable & 0x01) * 3);
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper085::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
if ((irq_enable & 0x02) != 0)
|
||||
{
|
||||
irq_clock += cycles * 4;
|
||||
while (irq_clock >= 455)
|
||||
{
|
||||
irq_clock -= 455;
|
||||
irq_counter++;
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper085::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
p[1] = irq_counter;
|
||||
p[2] = irq_latch;
|
||||
//*((INT*)&p[4]) = irq_clock;
|
||||
BitConverter.GetBytes(irq_clock).CopyTo(p, 4);
|
||||
}
|
||||
|
||||
//void Mapper085::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
irq_counter = p[1];
|
||||
irq_latch = p[2];
|
||||
//irq_clock = *((INT*)&p[4]);
|
||||
irq_clock = BitConverter.ToInt32(p, 4);
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
69
Core/VirtualNes.Core/Mapper/Mapper086.cs
Normal file
69
Core/VirtualNes.Core/Mapper/Mapper086.cs
Normal file
@ -0,0 +1,69 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper086 Jaleco Early Mapper #2 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper086 : Mapper
|
||||
{
|
||||
BYTE reg, cnt;
|
||||
public Mapper086(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 2, 3);
|
||||
SetVROM_8K_Bank(0);
|
||||
reg = 0xFF;
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
//void Mapper086::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0x6000)
|
||||
{
|
||||
SetPROM_32K_Bank((data & 0x30) >> 4);
|
||||
|
||||
SetVROM_8K_Bank((data & 0x03) | ((data & 0x40) >> 4));
|
||||
}
|
||||
if (addr == 0x7000)
|
||||
{
|
||||
if ((reg & 0x10) == 0 && ((data & 0x10) != 0) && cnt == 0)
|
||||
{
|
||||
//DEBUGOUT( "WR:$%02X\n", data );
|
||||
if ((data & 0x0F) == 0 // Strike
|
||||
|| (data & 0x0F) == 5)
|
||||
{ // Foul
|
||||
cnt = 60; // 次の発声を1秒程禁止する
|
||||
}
|
||||
|
||||
// OSDにするべきか…
|
||||
if (Supporter.Config.sound.bExtraSoundEnable)
|
||||
{
|
||||
//TODO : 似乎VirtuaNES有直接播放某个音频文件的功能
|
||||
//DirectSound.EsfAllStop();
|
||||
//DirectSound.EsfPlay(ESF_MOEPRO_STRIKE + (data & 0x0F));
|
||||
}
|
||||
}
|
||||
reg = data;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper086::VSync()
|
||||
public override void VSync()
|
||||
{
|
||||
if (cnt != 0)
|
||||
{
|
||||
cnt--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
32
Core/VirtualNes.Core/Mapper/Mapper087.cs
Normal file
32
Core/VirtualNes.Core/Mapper/Mapper087.cs
Normal file
@ -0,0 +1,32 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper087 Konami 74161/32 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper087 : Mapper
|
||||
{
|
||||
public Mapper087(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, 2, 3);
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
//void Mapper087::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0x6000)
|
||||
{
|
||||
SetVROM_8K_Bank((data & 0x02) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
104
Core/VirtualNes.Core/Mapper/Mapper088.cs
Normal file
104
Core/VirtualNes.Core/Mapper/Mapper088.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper088 : Mapper
|
||||
{
|
||||
BYTE reg;
|
||||
BYTE patch;
|
||||
public Mapper088(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE >= 8)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
patch = 0;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
if (crc == 0xc1b6b2a6)
|
||||
{ // Devil Man(J)
|
||||
patch = 1;
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
if (crc == 0xd9803a35)
|
||||
{ // Quinty(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_RENDER);
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper088::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x8000:
|
||||
reg = data;
|
||||
if (patch != 0)
|
||||
{
|
||||
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
break;
|
||||
case 0x8001:
|
||||
switch (reg & 0x07)
|
||||
{
|
||||
case 0:
|
||||
SetVROM_2K_Bank(0, data >> 1);
|
||||
break;
|
||||
case 1:
|
||||
SetVROM_2K_Bank(2, data >> 1);
|
||||
break;
|
||||
case 2:
|
||||
SetVROM_1K_Bank(4, data + 0x40);
|
||||
break;
|
||||
case 3:
|
||||
SetVROM_1K_Bank(5, data + 0x40);
|
||||
break;
|
||||
case 4:
|
||||
SetVROM_1K_Bank(6, data + 0x40);
|
||||
break;
|
||||
case 5:
|
||||
SetVROM_1K_Bank(7, data + 0x40);
|
||||
break;
|
||||
case 6:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 7:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xC000:
|
||||
if (data != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper088::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg;
|
||||
}
|
||||
|
||||
//void Mapper088::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg = p[0];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
37
Core/VirtualNes.Core/Mapper/Mapper089.cs
Normal file
37
Core/VirtualNes.Core/Mapper/Mapper089.cs
Normal file
@ -0,0 +1,37 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper089 SunSoft (水戸黄門) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.MMU;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper089 : Mapper
|
||||
{
|
||||
public Mapper089(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
//void Mapper089::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
if ((addr & 0xFF00) == 0xC000)
|
||||
{
|
||||
SetPROM_16K_Bank(4, (data & 0x70) >> 4);
|
||||
|
||||
SetVROM_8K_Bank(((data & 0x80) >> 4) | (data & 0x07));
|
||||
|
||||
if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
481
Core/VirtualNes.Core/Mapper/Mapper090.cs
Normal file
481
Core/VirtualNes.Core/Mapper/Mapper090.cs
Normal file
@ -0,0 +1,481 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper090 PC-JY-?? //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using VirtualNes.Core.Debug;
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
using INT = System.Int32;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper090 : Mapper
|
||||
{
|
||||
BYTE patch;
|
||||
|
||||
BYTE[] prg_reg = new byte[4];
|
||||
BYTE[] nth_reg = new byte[4];
|
||||
BYTE[] ntl_reg = new byte[4];
|
||||
BYTE[] chh_reg = new byte[8];
|
||||
BYTE[] chl_reg = new byte[8];
|
||||
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
BYTE irq_latch;
|
||||
BYTE irq_occur;
|
||||
BYTE irq_preset;
|
||||
BYTE irq_offset;
|
||||
|
||||
BYTE prg_6000, prg_E000;
|
||||
BYTE prg_size, chr_size;
|
||||
BYTE mir_mode, mir_type;
|
||||
|
||||
BYTE key_val;
|
||||
BYTE mul_val1, mul_val2;
|
||||
BYTE sw_val;
|
||||
public Mapper090(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 4, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
SetVROM_8K_Bank(0);
|
||||
patch = 0;
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x2a268152)
|
||||
{
|
||||
patch = 1;
|
||||
}
|
||||
if (crc == 0x2224b882)
|
||||
{
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
|
||||
irq_enable = 0; // Disable
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_occur = 0;
|
||||
irq_preset = 0;
|
||||
irq_offset = 0;
|
||||
|
||||
prg_6000 = 0;
|
||||
prg_E000 = 0;
|
||||
prg_size = 0;
|
||||
chr_size = 0;
|
||||
mir_mode = 0;
|
||||
mir_type = 0;
|
||||
|
||||
key_val = 0;
|
||||
mul_val1 = mul_val2 = 0;
|
||||
|
||||
for (byte i = 0; i < 4; i++)
|
||||
{
|
||||
prg_reg[i] = (byte)(PROM_8K_SIZE - 4 + i);
|
||||
ntl_reg[i] = 0;
|
||||
nth_reg[i] = 0;
|
||||
chl_reg[i] = i;
|
||||
chh_reg[i] = 0;
|
||||
chl_reg[i + 4] = (byte)(i + 4);
|
||||
chh_reg[i + 4] = 0;
|
||||
}
|
||||
|
||||
if (sw_val != 0)
|
||||
sw_val = 0x00;
|
||||
else
|
||||
sw_val = 0xFF;
|
||||
|
||||
// nes.SetRenderMethod( NES::PRE_ALL_RENDER );
|
||||
}
|
||||
|
||||
//BYTE Mapper090::ReadLow(WORD addr)
|
||||
public override byte ReadLow(ushort addr)
|
||||
{
|
||||
Debuger.Log($"RD:{addr:X4}");
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x5000:
|
||||
return (byte)((sw_val != 0) ? 0x00 : 0xFF);
|
||||
case 0x5800:
|
||||
return (BYTE)(mul_val1 * mul_val2);
|
||||
case 0x5801:
|
||||
return (BYTE)((mul_val1 * mul_val2) >> 8);
|
||||
case 0x5803:
|
||||
return key_val;
|
||||
}
|
||||
|
||||
if (addr >= 0x6000)
|
||||
{
|
||||
return base.ReadLow(addr);
|
||||
}
|
||||
|
||||
// return sw_val?0x00:0xFF;
|
||||
return (BYTE)(addr >> 8);
|
||||
}
|
||||
|
||||
//void Mapper090::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
Debuger.Log($"WR:{addr:X4} {data:X2}");
|
||||
|
||||
if (addr == 0x5800)
|
||||
{
|
||||
mul_val1 = data;
|
||||
}
|
||||
else
|
||||
if (addr == 0x5801)
|
||||
{
|
||||
mul_val2 = data;
|
||||
}
|
||||
else
|
||||
if (addr == 0x5803)
|
||||
{
|
||||
key_val = data;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper090::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF007)
|
||||
{
|
||||
case 0x8000:
|
||||
case 0x8001:
|
||||
case 0x8002:
|
||||
case 0x8003:
|
||||
prg_reg[addr & 3] = data;
|
||||
SetBank_CPU();
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
case 0x9001:
|
||||
case 0x9002:
|
||||
case 0x9003:
|
||||
case 0x9004:
|
||||
case 0x9005:
|
||||
case 0x9006:
|
||||
case 0x9007:
|
||||
chl_reg[addr & 7] = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
case 0xA001:
|
||||
case 0xA002:
|
||||
case 0xA003:
|
||||
case 0xA004:
|
||||
case 0xA005:
|
||||
case 0xA006:
|
||||
case 0xA007:
|
||||
chh_reg[addr & 7] = data;
|
||||
SetBank_PPU();
|
||||
break;
|
||||
|
||||
case 0xB000:
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
ntl_reg[addr & 3] = data;
|
||||
SetBank_VRAM();
|
||||
break;
|
||||
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
nth_reg[addr & 3] = data;
|
||||
SetBank_VRAM();
|
||||
break;
|
||||
|
||||
case 0xC002:
|
||||
irq_enable = 0;
|
||||
irq_occur = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xC003:
|
||||
irq_enable = 0xFF;
|
||||
irq_preset = 0xFF;
|
||||
break;
|
||||
case 0xC004:
|
||||
break;
|
||||
case 0xC005:
|
||||
if ((irq_offset & 0x80) != 0)
|
||||
{
|
||||
irq_latch = (byte)(data ^ (irq_offset | 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_latch = (byte)(data | (irq_offset & 0x27));
|
||||
}
|
||||
irq_preset = 0xFF;
|
||||
break;
|
||||
case 0xC006:
|
||||
if (patch != 0)
|
||||
{
|
||||
irq_offset = data;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xD000:
|
||||
prg_6000 = (byte)(data & 0x80);
|
||||
prg_E000 = (byte)(data & 0x04);
|
||||
prg_size = (byte)(data & 0x03);
|
||||
chr_size = (byte)((data & 0x18) >> 3);
|
||||
mir_mode = (byte)(data & 0x20);
|
||||
SetBank_CPU();
|
||||
SetBank_PPU();
|
||||
SetBank_VRAM();
|
||||
break;
|
||||
|
||||
case 0xD001:
|
||||
mir_type = (byte)(data & 0x03);
|
||||
SetBank_VRAM();
|
||||
break;
|
||||
|
||||
case 0xD003:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper090::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_preset != 0)
|
||||
{
|
||||
irq_counter = irq_latch;
|
||||
irq_preset = 0;
|
||||
}
|
||||
if (irq_counter != 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
if (irq_counter == 0)
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
// irq_occur = 0xFF;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper090::Clock(INT cycles)
|
||||
public override void Clock(int cycles)
|
||||
{
|
||||
// if( irq_occur ) {
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
// }
|
||||
}
|
||||
|
||||
void SetBank_CPU()
|
||||
{
|
||||
if (prg_size == 0)
|
||||
{
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 4, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
if (prg_size == 1)
|
||||
{
|
||||
SetPROM_32K_Bank(prg_reg[1] * 2, prg_reg[1] * 2 + 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
if (prg_size == 2)
|
||||
{
|
||||
if (prg_E000 != 0)
|
||||
{
|
||||
SetPROM_32K_Bank(prg_reg[0], prg_reg[1], prg_reg[2], prg_reg[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prg_6000 != 0)
|
||||
{
|
||||
SetPROM_8K_Bank(3, prg_reg[3]);
|
||||
}
|
||||
SetPROM_32K_Bank(prg_reg[0], prg_reg[1], prg_reg[2], PROM_8K_SIZE - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_32K_Bank(prg_reg[3], prg_reg[2], prg_reg[1], prg_reg[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
INT[] bank = new int[8];
|
||||
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
bank[i] = ((INT)chh_reg[i] << 8) | ((INT)chl_reg[i]);
|
||||
}
|
||||
|
||||
if (chr_size == 0)
|
||||
{
|
||||
SetVROM_8K_Bank(bank[0]);
|
||||
}
|
||||
else
|
||||
if (chr_size == 1)
|
||||
{
|
||||
SetVROM_4K_Bank(0, bank[0]);
|
||||
SetVROM_4K_Bank(4, bank[4]);
|
||||
}
|
||||
else
|
||||
if (chr_size == 2)
|
||||
{
|
||||
SetVROM_2K_Bank(0, bank[0]);
|
||||
SetVROM_2K_Bank(2, bank[2]);
|
||||
SetVROM_2K_Bank(4, bank[4]);
|
||||
SetVROM_2K_Bank(6, bank[6]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_8K_Bank(bank[0], bank[1], bank[2], bank[3], bank[4], bank[5], bank[6], bank[7]);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBank_VRAM()
|
||||
{
|
||||
INT[] bank = new int[4];
|
||||
|
||||
for (INT i = 0; i < 4; i++)
|
||||
{
|
||||
bank[i] = ((INT)nth_reg[i] << 8) | ((INT)ntl_reg[i]);
|
||||
}
|
||||
|
||||
if (patch == 0 && mir_mode != 0)
|
||||
{
|
||||
for (INT i = 0; i < 4; i++)
|
||||
{
|
||||
if (nth_reg[i] == 0 && (ntl_reg[i] == (BYTE)i))
|
||||
{
|
||||
mir_mode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mir_mode != 0)
|
||||
{
|
||||
SetVROM_1K_Bank(8, bank[0]);
|
||||
SetVROM_1K_Bank(9, bank[1]);
|
||||
SetVROM_1K_Bank(10, bank[2]);
|
||||
SetVROM_1K_Bank(11, bank[3]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mir_type == 0)
|
||||
{
|
||||
SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
if (mir_type == 1)
|
||||
{
|
||||
SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper090::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
INT i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
p[i] = prg_reg[i];
|
||||
}
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
p[i + 4] = chh_reg[i];
|
||||
}
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
p[i + 12] = chl_reg[i];
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
p[i + 20] = nth_reg[i];
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
p[i + 24] = ntl_reg[i];
|
||||
}
|
||||
p[28] = irq_enable;
|
||||
p[29] = irq_counter;
|
||||
p[30] = irq_latch;
|
||||
p[31] = prg_6000;
|
||||
p[32] = prg_E000;
|
||||
p[33] = prg_size;
|
||||
p[34] = chr_size;
|
||||
p[35] = mir_mode;
|
||||
p[36] = mir_type;
|
||||
p[37] = mul_val1;
|
||||
p[38] = mul_val2;
|
||||
p[39] = key_val;
|
||||
p[40] = irq_occur;
|
||||
p[41] = irq_preset;
|
||||
p[42] = irq_offset;
|
||||
}
|
||||
|
||||
//void Mapper090::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
INT i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
prg_reg[i] = p[i];
|
||||
}
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
chh_reg[i] = p[i + 4];
|
||||
}
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
chl_reg[i] = p[i + 12];
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
nth_reg[i] = p[i + 20];
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
ntl_reg[i] = p[i + 24];
|
||||
}
|
||||
irq_enable = p[28];
|
||||
irq_counter = p[29];
|
||||
irq_latch = p[30];
|
||||
prg_6000 = p[31];
|
||||
prg_E000 = p[32];
|
||||
prg_size = p[33];
|
||||
chr_size = p[34];
|
||||
mir_mode = p[35];
|
||||
mir_type = p[36];
|
||||
mul_val1 = p[37];
|
||||
mul_val2 = p[38];
|
||||
key_val = p[39];
|
||||
irq_occur = p[40];
|
||||
irq_preset = p[41];
|
||||
irq_offset = p[42];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
102
Core/VirtualNes.Core/Mapper/Mapper091.cs
Normal file
102
Core/VirtualNes.Core/Mapper/Mapper091.cs
Normal file
@ -0,0 +1,102 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Mapper091 PC-HK-SF3 //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
using static VirtualNes.Core.CPU;
|
||||
using static VirtualNes.MMU;
|
||||
using BYTE = System.Byte;
|
||||
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper091 : Mapper
|
||||
{
|
||||
BYTE irq_enable;
|
||||
BYTE irq_counter;
|
||||
public Mapper091(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetPROM_32K_Bank(PROM_8K_SIZE - 2, PROM_8K_SIZE - 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
|
||||
//void Mapper091::WriteLow(WORD addr, BYTE data)
|
||||
public override void WriteLow(ushort addr, byte data)
|
||||
{
|
||||
//DEBUGOUT( "$%04X:$%02X(%3d) L=%3d\n", addr, data, data, nes.GetScanline() );
|
||||
switch (addr & 0xF003)
|
||||
{
|
||||
case 0x6000:
|
||||
case 0x6001:
|
||||
case 0x6002:
|
||||
case 0x6003:
|
||||
SetVROM_2K_Bank((byte)((addr & 0x03) * 2), data);
|
||||
break;
|
||||
|
||||
case 0x7000:
|
||||
SetPROM_8K_Bank(4, data);
|
||||
break;
|
||||
case 0x7001:
|
||||
SetPROM_8K_Bank(5, data);
|
||||
break;
|
||||
|
||||
case 0x7002:
|
||||
irq_enable = 0;
|
||||
irq_counter = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0x7003:
|
||||
irq_enable = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper091::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline < 240) && nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0)
|
||||
{
|
||||
irq_counter++;
|
||||
}
|
||||
if (irq_counter >= 8)
|
||||
{
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper091::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = irq_enable;
|
||||
p[1] = irq_counter;
|
||||
}
|
||||
|
||||
//void Mapper091::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
irq_enable = p[0];
|
||||
irq_counter = p[1];
|
||||
}
|
||||
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user