Merge pull request 'dev_4VirtualNes' (#18) from Alienjack/AxibugEmuOnline:dev_4VirtualNes into dev_4VirtualNes
Reviewed-on: #18
This commit is contained in:
commit
69aa14ca67
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using UnityEngine;
|
||||
using VirtualNes.Core;
|
||||
|
||||
@ -45,5 +46,23 @@ namespace AxibugEmuOnline.Client
|
||||
{
|
||||
return File.Open($"{Application.streamingAssetsPath}/Disksys.rom", FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
public void SaveSRAMToFile(byte[] sramContent, string romName)
|
||||
{
|
||||
string sramDirectoryPath = $"{Application.persistentDataPath}/sav";
|
||||
Directory.CreateDirectory(sramDirectoryPath);
|
||||
romName = Path.GetFileNameWithoutExtension(romName);
|
||||
File.WriteAllBytes($"{sramDirectoryPath}/{romName}.sav", sramContent);
|
||||
}
|
||||
|
||||
public void SaveDISKToFile(byte[] diskFileContent, string romName)
|
||||
{
|
||||
string diskFileDirectoryPath = $"{Application.persistentDataPath}/dsv";
|
||||
Directory.CreateDirectory(diskFileDirectoryPath);
|
||||
romName = Path.GetFileNameWithoutExtension(romName);
|
||||
File.WriteAllBytes($"{diskFileDirectoryPath}/{romName}.dsv", diskFileContent);
|
||||
}
|
||||
|
||||
public EmulatorConfig Config { get; private set; } = new EmulatorConfig();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class NesEmulator : MonoBehaviour
|
||||
{
|
||||
private NES m_nesIns;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
StartGame("Kirby.nes");
|
||||
@ -13,9 +15,23 @@ namespace AxibugEmuOnline.Client
|
||||
|
||||
public void StartGame(string romName)
|
||||
{
|
||||
StopGame();
|
||||
|
||||
Supporter.Setup(new CoreSupporter());
|
||||
Debuger.Setup(new CoreDebuger());
|
||||
NES nes = new NES(romName);
|
||||
m_nesIns = new NES(romName);
|
||||
m_nesIns.Command(NESCOMMAND.NESCMD_HWRESET);
|
||||
}
|
||||
|
||||
public void StopGame()
|
||||
{
|
||||
m_nesIns?.Dispose();
|
||||
m_nesIns = null;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
m_nesIns?.EmulateFrame(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,32 @@
|
||||
using Codice.CM.Common;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System;
|
||||
using VirtualNes.Core.Debug;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU
|
||||
{
|
||||
public const uint QUEUE_LENGTH = 8192;
|
||||
|
||||
private NES nes;
|
||||
private byte exsound_select;
|
||||
private APU_INTERNAL @internal;
|
||||
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;
|
||||
protected QUEUE exqueue;
|
||||
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)
|
||||
{
|
||||
@internal = new APU_INTERNAL();
|
||||
|
||||
exsound_select = 0;
|
||||
|
||||
nes = parent;
|
||||
@ -30,12 +36,142 @@ namespace VirtualNes.Core
|
||||
|
||||
Array.Clear(m_SoundBuffer, 0, m_SoundBuffer.Length);
|
||||
Array.Clear(lowpass_filter, 0, lowpass_filter.Length);
|
||||
queue = QUEUE.GetDefault();
|
||||
exqueue = QUEUE.GetDefault();
|
||||
|
||||
for (int i = 0; i < m_bMute.Length; i++)
|
||||
m_bMute[i] = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct QUEUEDATA
|
||||
@ -46,19 +182,10 @@ namespace VirtualNes.Core
|
||||
public byte reserved;
|
||||
}
|
||||
|
||||
public struct QUEUE
|
||||
public class QUEUE
|
||||
{
|
||||
public int rdptr;
|
||||
public int wrptr;
|
||||
QUEUEDATA[] data;
|
||||
|
||||
public static QUEUE GetDefault()
|
||||
{
|
||||
var res = new QUEUE();
|
||||
res.rdptr = 0;
|
||||
res.wrptr = 0;
|
||||
res.data = new QUEUEDATA[8192];
|
||||
return res;
|
||||
}
|
||||
public QUEUEDATA[] data = new QUEUEDATA[8192];
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_FDS : APU_INTERFACE
|
||||
{
|
||||
private FDSSOUND fds = new FDSSOUND();
|
||||
private FDSSOUND fds_sync = new FDSSOUND();
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
internal void SyncWrite(ushort addr, byte data)
|
||||
{
|
||||
WriteSub(addr, data, fds_sync, 1789772.5d);
|
||||
}
|
||||
|
||||
private void WriteSub(ushort addr, byte data, FDSSOUND ch, double rate)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class FDSSOUND
|
||||
{
|
||||
//todo : 实现
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e16912525198924a860e53ab4ef0c81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_FME7 : APU_INTERFACE
|
||||
{
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03e0258857a7134438a497aec27ea607
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public abstract class APU_INTERFACE
|
||||
{
|
||||
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);
|
||||
@ -24,5 +22,15 @@ namespace VirtualNes.Core
|
||||
public virtual int GetStateSize() { return 0; }
|
||||
public virtual void SaveState(byte[] p) { }
|
||||
public virtual void LoadState(byte[] p) { }
|
||||
|
||||
public static int INT2FIX(int x)
|
||||
{
|
||||
return x << 16;
|
||||
}
|
||||
|
||||
public static int FIX2INT(int x)
|
||||
{
|
||||
return x >> 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,129 @@ namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_INTERNAL : APU_INTERFACE
|
||||
{
|
||||
private static int[] vbl_length = new int[32]
|
||||
{
|
||||
5, 127, 10, 1, 19, 2, 40, 3,
|
||||
80, 4, 30, 5, 7, 6, 13, 7,
|
||||
6, 8, 12, 9, 24, 10, 48, 11,
|
||||
96, 12, 36, 13, 8, 14, 16, 15,
|
||||
};
|
||||
|
||||
private static int[] dpcm_cycles_pal = new int[16]
|
||||
{
|
||||
397, 353, 315, 297, 265, 235, 209, 198,
|
||||
176, 148, 131, 118, 98, 78, 66, 50,
|
||||
};
|
||||
|
||||
private static int[] dpcm_cycles = new int[16]
|
||||
{
|
||||
428, 380, 340, 320, 286, 254, 226, 214,
|
||||
190, 160, 142, 128, 106, 85, 72, 54,
|
||||
};
|
||||
|
||||
private NES nes;
|
||||
// Frame Counter
|
||||
private int FrameCycle;
|
||||
private int FrameCount;
|
||||
private int FrameType;
|
||||
private byte FrameIRQ;
|
||||
private byte FrameIRQoccur;
|
||||
|
||||
// Channels
|
||||
private RECTANGLE ch0 = new RECTANGLE();
|
||||
private RECTANGLE ch1 = new RECTANGLE();
|
||||
private TRIANGLE ch2 = new TRIANGLE();
|
||||
private NOISE ch3 = new NOISE();
|
||||
private DPCM ch4 = new DPCM();
|
||||
|
||||
// $4015 Reg
|
||||
private byte reg4015, sync_reg4015;
|
||||
|
||||
public void SetParent(NES parent)
|
||||
{
|
||||
nes = parent;
|
||||
}
|
||||
|
||||
public override bool Sync(int cycles)
|
||||
{
|
||||
FrameCycle -= cycles * 2;
|
||||
if (FrameCycle <= 0)
|
||||
{
|
||||
FrameCycle += 14915;
|
||||
|
||||
UpdateFrame();
|
||||
}
|
||||
|
||||
var result = FrameIRQoccur | (SyncUpdateDPCM(cycles) ? 1 : 0);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
private bool SyncUpdateDPCM(int cycles)
|
||||
{
|
||||
bool bIRQ = false;
|
||||
|
||||
if (ch4.sync_enable != 0)
|
||||
{
|
||||
ch4.sync_cycles -= cycles;
|
||||
while (ch4.sync_cycles < 0)
|
||||
{
|
||||
ch4.sync_cycles += ch4.sync_cache_cycles;
|
||||
if (ch4.sync_dmalength != 0)
|
||||
{
|
||||
// if( !(--ch4.sync_dmalength) ) {
|
||||
if (--ch4.sync_dmalength < 2)
|
||||
{
|
||||
if (ch4.sync_looping != 0)
|
||||
{
|
||||
ch4.sync_dmalength = ch4.sync_cache_dmalength;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch4.sync_dmalength = 0;
|
||||
|
||||
if (ch4.sync_irq_gen != 0)
|
||||
{
|
||||
ch4.sync_irq_enable = 0xFF;
|
||||
nes.cpu.SetIRQ(CPU.IRQ_DPCM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch4.sync_irq_enable != 0)
|
||||
{
|
||||
bIRQ = true;
|
||||
}
|
||||
|
||||
return bIRQ;
|
||||
}
|
||||
|
||||
private void UpdateFrame()
|
||||
{
|
||||
if (FrameCount == 0)
|
||||
{
|
||||
if ((FrameIRQ & 0xC0) == 0 && nes.GetFrameIRQmode())
|
||||
{
|
||||
FrameIRQoccur = 0xFF;
|
||||
nes.cpu.SetIRQ(CPU.IRQ_FRAMEIRQ);
|
||||
}
|
||||
}
|
||||
|
||||
if (FrameCount == 3)
|
||||
{
|
||||
if ((FrameIRQ & 0x80) != 0)
|
||||
{
|
||||
FrameCycle += 14915;
|
||||
}
|
||||
}
|
||||
|
||||
// Counters Update
|
||||
nes.Write(0x4018, (byte)FrameCount);
|
||||
|
||||
FrameCount = (FrameCount + 1) & 3;
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
@ -28,5 +144,299 @@ namespace VirtualNes.Core
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
internal byte SyncRead(ushort addr)
|
||||
{
|
||||
byte data = (byte)(addr >> 8);
|
||||
|
||||
if (addr == 0x4015)
|
||||
{
|
||||
data = 0;
|
||||
if ((ch0.sync_enable != 0) && ch0.sync_len_count > 0) data |= (1 << 0);
|
||||
if ((ch1.sync_enable != 0) && ch1.sync_len_count > 0) data |= (1 << 1);
|
||||
if ((ch2.sync_enable != 0) && ch2.sync_len_count > 0) data |= (1 << 2);
|
||||
if ((ch3.sync_enable != 0) && ch3.sync_len_count > 0) data |= (1 << 3);
|
||||
if ((ch4.sync_enable != 0) && (ch4.sync_dmalength != 0)) data |= (1 << 4);
|
||||
if (FrameIRQoccur != 0) data |= (1 << 6);
|
||||
if (ch4.sync_irq_enable != 0) data |= (1 << 7);
|
||||
FrameIRQoccur = 0;
|
||||
|
||||
nes.cpu.ClrIRQ(CPU.IRQ_FRAMEIRQ);
|
||||
}
|
||||
if (addr == 0x4017)
|
||||
{
|
||||
if (FrameIRQoccur != 0)
|
||||
{
|
||||
data = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
data |= (1 << 6);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
internal void SyncWrite(ushort addr, byte data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
// CH0,1 rectangle
|
||||
case 0x4000:
|
||||
case 0x4001:
|
||||
case 0x4002:
|
||||
case 0x4003:
|
||||
case 0x4004:
|
||||
case 0x4005:
|
||||
case 0x4006:
|
||||
case 0x4007:
|
||||
SyncWriteRectangle((addr < 0x4004) ? 0 : 1, addr, data);
|
||||
break;
|
||||
// CH2 triangle
|
||||
case 0x4008:
|
||||
case 0x4009:
|
||||
case 0x400A:
|
||||
case 0x400B:
|
||||
SyncWriteTriangle(addr, data);
|
||||
break;
|
||||
// CH3 noise
|
||||
case 0x400C:
|
||||
case 0x400D:
|
||||
case 0x400E:
|
||||
case 0x400F:
|
||||
SyncWriteNoise(addr, data);
|
||||
break;
|
||||
// CH4 DPCM
|
||||
case 0x4010:
|
||||
case 0x4011:
|
||||
case 0x4012:
|
||||
case 0x4013:
|
||||
SyncWriteDPCM(addr, data);
|
||||
break;
|
||||
|
||||
case 0x4015:
|
||||
sync_reg4015 = data;
|
||||
|
||||
if ((data & (1 << 0)) == 0)
|
||||
{
|
||||
ch0.sync_enable = 0;
|
||||
ch0.sync_len_count = 0;
|
||||
}
|
||||
if ((data & (1 << 1)) == 0)
|
||||
{
|
||||
ch1.sync_enable = 0;
|
||||
ch1.sync_len_count = 0;
|
||||
}
|
||||
if ((data & (1 << 2)) == 0)
|
||||
{
|
||||
ch2.sync_enable = 0;
|
||||
ch2.sync_len_count = 0;
|
||||
ch2.sync_lin_count = 0;
|
||||
ch2.sync_counter_start = 0;
|
||||
}
|
||||
if ((data & (1 << 3)) == 0)
|
||||
{
|
||||
ch3.sync_enable = 0;
|
||||
ch3.sync_len_count = 0;
|
||||
}
|
||||
if ((data & (1 << 4)) == 0)
|
||||
{
|
||||
ch4.sync_enable = 0;
|
||||
ch4.sync_dmalength = 0;
|
||||
ch4.sync_irq_enable = 0;
|
||||
|
||||
nes.cpu.ClrIRQ(CPU.IRQ_DPCM);
|
||||
}
|
||||
else
|
||||
{
|
||||
ch4.sync_enable = 0xFF;
|
||||
if (ch4.sync_dmalength == 0)
|
||||
{
|
||||
// ch4.sync_cycles = ch4.sync_cache_cycles;
|
||||
ch4.sync_dmalength = ch4.sync_cache_dmalength;
|
||||
ch4.sync_cycles = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4017:
|
||||
SyncWrite4017(data);
|
||||
break;
|
||||
|
||||
// VirtuaNESŒÅ—Lƒ|<7C>[ƒg
|
||||
case 0x4018:
|
||||
SyncUpdateRectangle(ch0, data);
|
||||
SyncUpdateRectangle(ch1, data);
|
||||
SyncUpdateTriangle(data);
|
||||
SyncUpdateNoise(data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncUpdateNoise(int type)
|
||||
{
|
||||
if (ch3.sync_enable == 0 || ch3.sync_len_count <= 0)
|
||||
return;
|
||||
|
||||
// Update Length
|
||||
if (ch3.sync_len_count != 0 && ch3.sync_holdnote == 0)
|
||||
{
|
||||
if ((type & 1) == 0 && ch3.sync_len_count != 0)
|
||||
{
|
||||
ch3.sync_len_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncUpdateTriangle(int type)
|
||||
{
|
||||
if (ch2.sync_enable == 0)
|
||||
return;
|
||||
|
||||
if ((type & 1) == 0 && ch2.sync_holdnote == 0)
|
||||
{
|
||||
if (ch2.sync_len_count != 0)
|
||||
{
|
||||
ch2.sync_len_count--;
|
||||
}
|
||||
}
|
||||
|
||||
// Update Length/Linear
|
||||
if (ch2.sync_counter_start != 0)
|
||||
{
|
||||
ch2.sync_lin_count = ch2.sync_reg[0] & 0x7F;
|
||||
}
|
||||
else if (ch2.sync_lin_count != 0)
|
||||
{
|
||||
ch2.sync_lin_count--;
|
||||
}
|
||||
if (ch2.sync_holdnote == 0 && ch2.sync_lin_count != 0)
|
||||
{
|
||||
ch2.sync_counter_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncUpdateRectangle(RECTANGLE ch, int type)
|
||||
{
|
||||
if (ch.sync_enable == 0 || ch.sync_len_count <= 0)
|
||||
return;
|
||||
|
||||
// Update Length
|
||||
if (ch.sync_len_count != 0 && ch.sync_holdnote == 0)
|
||||
{
|
||||
if ((type & 1) == 0 && ch.sync_len_count != 0)
|
||||
{
|
||||
ch.sync_len_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncWrite4017(byte data)
|
||||
{
|
||||
FrameCycle = 0;
|
||||
FrameIRQ = data;
|
||||
FrameIRQoccur = 0;
|
||||
|
||||
nes.cpu.ClrIRQ(CPU.IRQ_FRAMEIRQ);
|
||||
|
||||
FrameType = (data & 0x80) != 0 ? 1 : 0;
|
||||
FrameCount = 0;
|
||||
if ((data & 0x80) > 0)
|
||||
{
|
||||
UpdateFrame();
|
||||
}
|
||||
FrameCount = 1;
|
||||
FrameCycle = 14915;
|
||||
}
|
||||
|
||||
private void SyncWriteDPCM(ushort addr, byte data)
|
||||
{
|
||||
ch4.reg[addr & 3] = data;
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
ch4.sync_cache_cycles = nes.GetVideoMode() ? dpcm_cycles_pal[data & 0x0F] * 8 : dpcm_cycles[data & 0x0F] * 8;
|
||||
ch4.sync_looping = (byte)(data & 0x40);
|
||||
ch4.sync_irq_gen = (byte)(data & 0x80);
|
||||
if (ch4.sync_irq_gen == 0)
|
||||
{
|
||||
ch4.sync_irq_enable = 0;
|
||||
nes.cpu.ClrIRQ(CPU.IRQ_DPCM);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
case 3:
|
||||
ch4.sync_cache_dmalength = (data << 4) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncWriteNoise(ushort addr, byte data)
|
||||
{
|
||||
ch3.sync_reg[addr & 3] = data;
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
ch3.sync_holdnote = (byte)(data & 0x20);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
case 3: // Master
|
||||
ch3.sync_len_count = vbl_length[data >> 3] * 2;
|
||||
if ((sync_reg4015 & (1 << 3)) != 0)
|
||||
ch3.sync_enable = 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncWriteTriangle(ushort addr, byte data)
|
||||
{
|
||||
ch2.sync_reg[addr & 3] = data;
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
ch2.sync_holdnote = (byte)(data & 0x80);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
case 3: // Master
|
||||
ch2.sync_len_count = vbl_length[ch2.sync_reg[3] >> 3] * 2;
|
||||
ch2.sync_counter_start = 0x80;
|
||||
|
||||
if ((sync_reg4015 & (1 << 2)) != 0)
|
||||
ch2.sync_enable = 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncWriteRectangle(int no, ushort addr, byte data)
|
||||
{
|
||||
RECTANGLE ch = (no == 0) ? ch0 : ch1;
|
||||
|
||||
ch.sync_reg[addr & 3] = data;
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
ch.sync_holdnote = (byte)(data & 0x20);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
break;
|
||||
case 3: // Master
|
||||
ch.sync_len_count = vbl_length[data >> 3] * 2;
|
||||
if ((sync_reg4015 & (1 << no)) != 0)
|
||||
ch.sync_enable = 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_MMC5 : APU_INTERFACE
|
||||
{
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
internal void SyncWrite(ushort addr, byte data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15b983a12234c3c47baefb9fa2751351
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,25 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_N106 : APU_INTERFACE
|
||||
{
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a47ed257e942d4478215338d8fe4c35
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
310
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs
Normal file
310
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs
Normal file
@ -0,0 +1,310 @@
|
||||
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;
|
||||
}
|
||||
|
||||
private class RECTANGLE
|
||||
{
|
||||
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 = default;
|
||||
gate = default;
|
||||
volume = default;
|
||||
|
||||
phaseacc = default;
|
||||
freq = default;
|
||||
output_vol = default;
|
||||
|
||||
adder = default;
|
||||
duty_pos = default;
|
||||
}
|
||||
}
|
||||
|
||||
private class SAWTOOTH
|
||||
{
|
||||
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 = default;
|
||||
volume = default;
|
||||
|
||||
phaseacc = default;
|
||||
freq = default;
|
||||
output_vol = default;
|
||||
|
||||
adder = default;
|
||||
accum = default;
|
||||
phaseaccum = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 190f3271accd30f4eb5b13590417d265
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,25 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_VRC7 : APU_INTERFACE
|
||||
{
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 180a87918f9d49e4fad978014f1d594f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
28
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs
Normal file
28
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs
Normal file
@ -0,0 +1,28 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class DPCM
|
||||
{
|
||||
public byte[] reg = new byte[4];
|
||||
public byte enable;
|
||||
public byte looping;
|
||||
public byte cur_byte;
|
||||
public byte dpcm_value;
|
||||
|
||||
public int freq;
|
||||
public int phaseacc;
|
||||
public int output;
|
||||
|
||||
ushort address, cache_addr;
|
||||
public int dmalength, cache_dmalength;
|
||||
public int dpcm_output_real, dpcm_output_fake, dpcm_output_old, dpcm_output_offset;
|
||||
|
||||
// For sync
|
||||
public byte[] sync_reg = new byte[4];
|
||||
public byte sync_enable;
|
||||
public byte sync_looping;
|
||||
public byte sync_irq_gen;
|
||||
public byte sync_irq_enable;
|
||||
public int sync_cycles, sync_cache_cycles;
|
||||
public int sync_dmalength, sync_cache_dmalength;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6289a516ac91b541b2b1807bb07e2b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
36
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs
Normal file
36
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class NOISE
|
||||
{
|
||||
public byte[] reg = new byte[4]; // register
|
||||
|
||||
public byte enable; // enable
|
||||
public byte holdnote; // holdnote
|
||||
public byte volume; // volume
|
||||
public byte xor_tap;
|
||||
public int shift_reg;
|
||||
|
||||
// For Render
|
||||
public int phaseacc;
|
||||
public int freq;
|
||||
public int len_count;
|
||||
|
||||
public int nowvolume;
|
||||
public int output;
|
||||
|
||||
// For Envelope
|
||||
public byte env_fixed;
|
||||
public byte env_decay;
|
||||
public byte env_count;
|
||||
public byte dummy0;
|
||||
public int env_vol;
|
||||
|
||||
// For sync;
|
||||
public byte[] sync_reg = new byte[4];
|
||||
public byte sync_output_enable;
|
||||
public byte sync_enable;
|
||||
public byte sync_holdnote;
|
||||
public byte dummy1;
|
||||
public int sync_len_count;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8680ce7dbdceb504dbda3b98dbdb1297
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,85 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class RECTANGLE
|
||||
{
|
||||
public byte[] reg = new byte[4]; // register
|
||||
|
||||
public byte enable; // enable
|
||||
public byte holdnote; // holdnote
|
||||
public byte volume; // volume
|
||||
public byte complement;
|
||||
|
||||
// For Render
|
||||
public int phaseacc;
|
||||
public int freq;
|
||||
public int freqlimit;
|
||||
public int adder;
|
||||
public int duty;
|
||||
public int len_count;
|
||||
|
||||
public int nowvolume;
|
||||
|
||||
// For Envelope
|
||||
public byte env_fixed;
|
||||
public byte env_decay;
|
||||
public byte env_count;
|
||||
public byte dummy0;
|
||||
public int env_vol;
|
||||
|
||||
// For Sweep
|
||||
public byte swp_on;
|
||||
public byte swp_inc;
|
||||
public byte swp_shift;
|
||||
public byte swp_decay;
|
||||
public byte swp_count;
|
||||
public byte[] dummy1 = new byte[3];
|
||||
|
||||
// For sync;
|
||||
public byte[] sync_reg = new byte[4];
|
||||
public byte sync_output_enable;
|
||||
public byte sync_enable;
|
||||
public byte sync_holdnote;
|
||||
public byte dummy2;
|
||||
public int sync_len_count;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, reg.Length);
|
||||
enable = 0;
|
||||
holdnote = 0;
|
||||
volume = 0;
|
||||
complement = 0;
|
||||
|
||||
phaseacc = 0;
|
||||
freq = 0;
|
||||
freqlimit = 0;
|
||||
adder = 0;
|
||||
duty = 0;
|
||||
len_count = 0;
|
||||
|
||||
nowvolume = 0;
|
||||
|
||||
env_fixed = 0;
|
||||
env_decay = 0;
|
||||
env_count = 0;
|
||||
dummy0 = 0;
|
||||
env_vol = 0;
|
||||
|
||||
swp_on = 0;
|
||||
swp_inc = 0;
|
||||
swp_shift = 0;
|
||||
swp_decay = 0;
|
||||
swp_count = 0;
|
||||
Array.Clear(dummy1, 0, dummy1.Length);
|
||||
|
||||
Array.Clear(sync_reg, 0, sync_reg.Length);
|
||||
sync_output_enable = 0;
|
||||
sync_enable = 0;
|
||||
sync_holdnote = 0;
|
||||
dummy2 = 0;
|
||||
sync_len_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e50831f6c445fe489d7e1737269296e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,29 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class TRIANGLE
|
||||
{
|
||||
public byte[] reg = new byte[4];
|
||||
|
||||
public byte enable;
|
||||
public byte holdnote;
|
||||
public byte counter_start;
|
||||
public byte dummy0;
|
||||
|
||||
public int phaseacc;
|
||||
public int freq;
|
||||
public int len_count;
|
||||
public int lin_count;
|
||||
public int adder;
|
||||
|
||||
public int nowvolume;
|
||||
|
||||
// For sync;
|
||||
public byte[] sync_reg = new byte[4];
|
||||
public byte sync_enable;
|
||||
public byte sync_holdnote;
|
||||
public byte sync_counter_start;
|
||||
// public byte dummy1;
|
||||
public int sync_len_count;
|
||||
public int sync_lin_count;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ed2788da33fe474facc1d7ce1b34d03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
20
AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs
Normal file
20
AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CHEATCODE
|
||||
{
|
||||
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;
|
||||
};
|
||||
}
|
@ -1,22 +1,17 @@
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting.Antlr3.Runtime.Tree;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public static class CRC
|
||||
{
|
||||
const int CHAR_BIT = 8;
|
||||
const ulong CRCPOLY1 = 0x04C11DB7UL;
|
||||
const ulong CRCPOLY2 = 0xEDB88320UL;
|
||||
const uint CRCPOLY1 = 0x04C11DB7U;
|
||||
const uint CRCPOLY2 = 0xEDB88320U;
|
||||
|
||||
static bool m_Init;
|
||||
static bool m_InitRev;
|
||||
static ulong[] m_CrcTable = new ulong[byte.MaxValue + 1];
|
||||
static ulong[] m_CrcTableRev = new ulong[byte.MaxValue + 1];
|
||||
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)
|
||||
{
|
||||
@ -35,7 +30,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
return ~r & 0xFFFFFFFFUL;
|
||||
}
|
||||
public static ulong CrcRev(int size, Span<byte> c)
|
||||
public static uint CrcRev(int size, Span<byte> c)
|
||||
{
|
||||
if (!m_InitRev)
|
||||
{
|
||||
@ -43,41 +38,41 @@ namespace VirtualNes.Core
|
||||
m_InitRev = true;
|
||||
}
|
||||
|
||||
ulong r = 0xFFFFFFFFUL;
|
||||
uint r = 0xFFFFFFFFU;
|
||||
int step = 0;
|
||||
while (--size >= 0)
|
||||
{
|
||||
r = (r >> CHAR_BIT) ^ m_CrcTableRev[(byte)r ^ c[step]];
|
||||
step++;
|
||||
}
|
||||
return r ^ 0xFFFFFFFFUL;
|
||||
return r ^ 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
static void MakeTable()
|
||||
{
|
||||
int i, j;
|
||||
ulong r;
|
||||
uint r;
|
||||
|
||||
for (i = 0; i <= byte.MaxValue; i++)
|
||||
{
|
||||
r = (ulong)i << (32 - CHAR_BIT);
|
||||
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 & 0xFFFFFFFFUL;
|
||||
m_CrcTable[i] = r & 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
}
|
||||
static void MakeTableRev()
|
||||
{
|
||||
int i, j;
|
||||
ulong r;
|
||||
uint r;
|
||||
|
||||
for (i = 0; i <= byte.MaxValue; i++)
|
||||
{
|
||||
r = (ulong)i;
|
||||
r = (uint)i;
|
||||
for (j = 0; j < CHAR_BIT; j++)
|
||||
{
|
||||
if ((r & 1) > 0) r = (r >> 1) ^ CRCPOLY2;
|
||||
|
@ -0,0 +1,145 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class OPLL_PATCH
|
||||
{
|
||||
public uint TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, 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 UInt32 plfo_pm;
|
||||
public UInt32 plfo_am;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static class Emu2413API
|
||||
{
|
||||
public static void OPLL_init(UInt32 c, UInt32 r)
|
||||
{
|
||||
makePmTable();
|
||||
makeAmTable();
|
||||
makeDB2LinTable();
|
||||
makeAdjustTable();
|
||||
makeTllTable();
|
||||
makeRksTable();
|
||||
makeSinTable();
|
||||
makeDefaultPatch();
|
||||
OPLL_setClock(c, r);
|
||||
}
|
||||
|
||||
private static void OPLL_setClock(uint c, uint r)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makeDefaultPatch()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makeSinTable()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makeRksTable()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makeTllTable()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makeAdjustTable()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makeDB2LinTable()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makeAmTable()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void makePmTable()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5ecddbb6b69204478d799a484d8c47c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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 // 僞僀儖儀乕僗儗儞僟儕儞僌
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c379fb6535bd23449474dee5018652c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,17 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public static class MemoryUtility
|
||||
{
|
||||
public static void ZEROMEMORY(byte[] array, uint length)
|
||||
{
|
||||
memset(array, 0, length);
|
||||
}
|
||||
|
||||
public static void memset(byte[] array, byte value, uint length)
|
||||
{
|
||||
Unsafe.InitBlock(ref array[0], value, length);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8586eb710dc81124593eb5adfa08d73b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public enum NESCOMMAND
|
||||
{
|
||||
NESCMD_NONE = 0,
|
||||
NESCMD_HWRESET,
|
||||
NESCMD_SWRESET,
|
||||
NESCMD_EXCONTROLLER, // Commandparam
|
||||
NESCMD_DISK_THROTTLE_ON,
|
||||
NESCMD_DISK_THROTTLE_OFF,
|
||||
NESCMD_DISK_EJECT,
|
||||
NESCMD_DISK_0A,
|
||||
NESCMD_DISK_0B,
|
||||
NESCMD_DISK_1A,
|
||||
NESCMD_DISK_1B,
|
||||
NESCMD_DISK_2A,
|
||||
NESCMD_DISK_2B,
|
||||
NESCMD_DISK_3A,
|
||||
NESCMD_DISK_3B,
|
||||
|
||||
NESCMD_SOUND_MUTE, // CommandParam
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7e8126382c9728429056ba33afc85eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4865f8871b37b0041b77060cf3c62664
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
@ -27,7 +24,7 @@ namespace VirtualNes.Core
|
||||
NSF
|
||||
}
|
||||
|
||||
public struct NSFHEADER
|
||||
public class NSFHEADER
|
||||
{
|
||||
byte[] ID;
|
||||
byte Version;
|
||||
@ -43,7 +40,7 @@ namespace VirtualNes.Core
|
||||
byte[] BankSwitch;
|
||||
ushort SpeedPAL;
|
||||
byte NTSC_PALbits;
|
||||
byte ExtraChipSelect;
|
||||
public byte ExtraChipSelect;
|
||||
byte[] Expansion; // must be 0
|
||||
|
||||
|
||||
@ -65,7 +62,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
}
|
||||
|
||||
public struct NESHEADER
|
||||
public class NESHEADER
|
||||
{
|
||||
public byte[] ID;
|
||||
public byte PRG_PAGE_SIZE;
|
||||
|
@ -1,13 +1,8 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public static class RomPatch
|
||||
{
|
||||
public static void DoPatch(ref ulong crc, ref byte[] lpPRG, ref byte[] lpCHR, ref int mapper, ref NESHEADER header)
|
||||
public static void DoPatch(ref uint crc, ref byte[] lpPRG, ref byte[] lpCHR, ref int mapper, ref NESHEADER header)
|
||||
{
|
||||
// Mapper 000
|
||||
if (crc == 0x57970078)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 447095b8c8ae4c74885562c127998e9e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
236
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs
Normal file
236
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs
Normal file
@ -0,0 +1,236 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes
|
||||
{
|
||||
public static class MMU
|
||||
{
|
||||
// CPU 儊儌儕僶儞僋
|
||||
public static Memory<byte>[] CPU_MEM_BANK = new Memory<byte>[8]; // 8K扨埵
|
||||
public static byte[] CPU_MEM_TYPE = new byte[8];
|
||||
public static int[] CPU_MEM_PAGE = new int[8]; // 僗僥乕僩僙乕僽梡
|
||||
// PPU 儊儌儕僶儞僋
|
||||
public static Memory<byte>[] PPU_MEM_BANK = new Memory<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偺儈儔乕
|
||||
|
||||
internal static void SetPROM_Bank(byte page, Memory<byte> ptr, byte type)
|
||||
{
|
||||
CPU_MEM_BANK[page] = ptr;
|
||||
CPU_MEM_TYPE[page] = type;
|
||||
CPU_MEM_PAGE[page] = 0;
|
||||
}
|
||||
|
||||
internal static void SetPROM_8K_Bank(byte page, int bank)
|
||||
{
|
||||
bank %= PROM_8K_SIZE;
|
||||
CPU_MEM_BANK[page] = new Memory<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, Memory<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 Memory<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 Memory<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 Memory<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);
|
||||
}
|
||||
}
|
||||
}
|
11
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta
Normal file
11
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 863989820a4fb1d49a7c0c883c5a7078
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,12 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper
|
||||
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].Span[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].Span[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)
|
||||
{
|
||||
switch (no)
|
||||
{
|
||||
default:
|
||||
throw new NotImplementedException($"Mapper#{no} is not Impl");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using VirtualNes.Core.Debug;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
@ -13,14 +14,17 @@ namespace VirtualNes.Core
|
||||
public ROM rom;
|
||||
public PAD pad;
|
||||
public Mapper mapper;
|
||||
public NesConfig NesCfg;
|
||||
|
||||
protected List<CHEATCODE> m_CheatCode = new List<CHEATCODE>();
|
||||
public NesConfig nescfg;
|
||||
|
||||
private List<CHEATCODE> m_CheatCode = new List<CHEATCODE>();
|
||||
private List<GENIECODE> m_GenieCode = new List<GENIECODE>();
|
||||
private bool m_bDiskThrottle;
|
||||
private int m_CommandRequest;
|
||||
private int m_nSnapNo;
|
||||
private bool m_bNsfPlaying;
|
||||
private bool m_bNsfInit;
|
||||
private int m_nNsfSongNo;
|
||||
private int m_nNsfSongMode;
|
||||
private bool m_bMoviePlay;
|
||||
private bool m_bMovieRec;
|
||||
private Stream m_fpMovie;
|
||||
@ -30,12 +34,32 @@ namespace VirtualNes.Core
|
||||
private double m_TapeCycles;
|
||||
private byte m_TapeIn;
|
||||
private byte m_TapeOut;
|
||||
|
||||
// For Barcode
|
||||
private bool m_bBarcode;
|
||||
private byte m_BarcodeOut;
|
||||
private byte m_BarcodePtr;
|
||||
private int m_BarcodeCycles;
|
||||
private byte[] m_BarcodeData = new byte[256];
|
||||
|
||||
// For Barcode
|
||||
private bool m_bBarcode2;
|
||||
private int m_Barcode2seq;
|
||||
private int m_Barcode2ptr;
|
||||
private int m_Barcode2cnt;
|
||||
private byte m_Barcode2bit;
|
||||
private byte[] m_Barcode2data = new byte[32];
|
||||
|
||||
private int m_TurboFileBank;
|
||||
private int SAVERAM_SIZE;
|
||||
private int nIRQtype;
|
||||
private bool bFrameIRQ;
|
||||
private bool bVideoMode;
|
||||
private int NES_scanline;
|
||||
private EnumRenderMethod RenderMethod;
|
||||
private bool bZapper;
|
||||
private long base_cycles;
|
||||
private long emul_cycles;
|
||||
|
||||
public NES(string fname)
|
||||
{
|
||||
@ -75,7 +99,7 @@ namespace VirtualNes.Core
|
||||
|
||||
bVideoMode = false;
|
||||
|
||||
NesCfg = NesConfig.GetNTSC();
|
||||
nescfg = NesConfig.NESCONFIG_NTSC;
|
||||
|
||||
CheatInitial();
|
||||
|
||||
@ -102,65 +126,684 @@ namespace VirtualNes.Core
|
||||
}
|
||||
}
|
||||
|
||||
public void Command(NESCOMMAND cmd)
|
||||
{
|
||||
CommandParam(cmd, 0);
|
||||
}
|
||||
|
||||
public bool CommandParam(NESCOMMAND cmd, int param)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case NESCOMMAND.NESCMD_NONE: break;
|
||||
case NESCOMMAND.NESCMD_HWRESET:
|
||||
Reset();
|
||||
m_CommandRequest = (int)cmd;
|
||||
break;
|
||||
case NESCOMMAND.NESCMD_SWRESET:
|
||||
SoftReset();
|
||||
m_CommandRequest = (int)cmd;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"{cmd} not impl right now");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CheatInitial()
|
||||
{
|
||||
m_CheatCode.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public struct 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 ScanlingCycles; // 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 GetNTSC()
|
||||
public void EmulateFrame(bool bDraw)
|
||||
{
|
||||
return new NesConfig
|
||||
int scanline = 0;
|
||||
if (rom.IsNSF())
|
||||
{
|
||||
BaseClock = 21477270.0f,
|
||||
CpuClock = 1789772.5f,
|
||||
TotalScanLines = 262,
|
||||
ScanlingCycles = 1364,
|
||||
HDrawCycles = 1024,
|
||||
HBlankCycles = 340,
|
||||
ScanlineEndCycles = 4,
|
||||
FrameCycles = 1364 * 262,
|
||||
FrameIrqCycles = 29830,
|
||||
FrameRate = 60,
|
||||
FramePeriod = 1000.0f / 60.0f
|
||||
};
|
||||
EmulateNSF();
|
||||
return;
|
||||
}
|
||||
|
||||
CheatCodeProcess();
|
||||
|
||||
NES_scanline = scanline;
|
||||
|
||||
if (RenderMethod != EnumRenderMethod.TILE_RENDER)
|
||||
{
|
||||
bZapper = false;
|
||||
while (true)
|
||||
{
|
||||
ppu.SetRenderScanline(scanline);
|
||||
|
||||
if (scanline == 0)
|
||||
{
|
||||
if (RenderMethod < EnumRenderMethod.POST_RENDER)
|
||||
{
|
||||
EmulationCPU(nescfg.ScanlineCycles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static NesConfig GetPAL()
|
||||
internal void EmulationCPU(int basecycles)
|
||||
{
|
||||
return new NesConfig
|
||||
int cycles;
|
||||
|
||||
base_cycles += basecycles;
|
||||
cycles = (int)((base_cycles / 12) - emul_cycles);
|
||||
|
||||
if (cycles > 0)
|
||||
{
|
||||
BaseClock = 26601714.0f,
|
||||
CpuClock = 1662607.125f,
|
||||
TotalScanLines = 312,
|
||||
ScanlingCycles = 1278,
|
||||
HDrawCycles = 960,
|
||||
HBlankCycles = 318,
|
||||
ScanlineEndCycles = 2,
|
||||
FrameCycles = 1278 * 312,
|
||||
FrameIrqCycles = 33252,
|
||||
FrameRate = 50,
|
||||
FramePeriod = 1000.0f / 50.0f
|
||||
};
|
||||
emul_cycles += cpu.EXEC(cycles);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
SaveSRAM();
|
||||
SaveDISK();
|
||||
SaveTurboFile();
|
||||
|
||||
// RAM Clear
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length);
|
||||
if (rom.GetPROM_CRC() == 0x29401686)
|
||||
{ // Minna no Taabou no Nakayoshi Dai Sakusen(J)
|
||||
MemoryUtility.memset(MMU.RAM, 0xFF, (uint)MMU.RAM.Length);
|
||||
}
|
||||
|
||||
// RAM set
|
||||
if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20)
|
||||
{
|
||||
MemoryUtility.memset(MMU.WRAM, 0xFF, (uint)MMU.WRAM.Length);
|
||||
}
|
||||
|
||||
MemoryUtility.ZEROMEMORY(MMU.CRAM, (uint)MMU.CRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.VRAM, (uint)MMU.VRAM.Length);
|
||||
|
||||
MemoryUtility.ZEROMEMORY(MMU.SPRAM, (uint)MMU.SPRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.BGPAL, (uint)MMU.BGPAL.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.SPPAL, (uint)MMU.SPPAL.Length);
|
||||
|
||||
MemoryUtility.ZEROMEMORY(MMU.CPUREG, (uint)MMU.CPUREG.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.PPUREG, (uint)MMU.PPUREG.Length);
|
||||
|
||||
m_bDiskThrottle = false;
|
||||
|
||||
SetRenderMethod(EnumRenderMethod.PRE_RENDER);
|
||||
|
||||
if (rom.IsPAL())
|
||||
{
|
||||
SetVideoMode(true);
|
||||
}
|
||||
|
||||
MMU.PROM = rom.GetPROM();
|
||||
MMU.VROM = rom.GetVROM();
|
||||
|
||||
MMU.PROM_8K_SIZE = rom.GetPROM_SIZE() * 2;
|
||||
MMU.PROM_16K_SIZE = rom.GetPROM_SIZE();
|
||||
MMU.PROM_32K_SIZE = rom.GetPROM_SIZE() / 2;
|
||||
|
||||
MMU.VROM_1K_SIZE = rom.GetVROM_SIZE() * 8;
|
||||
MMU.VROM_2K_SIZE = rom.GetVROM_SIZE() * 4;
|
||||
MMU.VROM_4K_SIZE = rom.GetVROM_SIZE() * 2;
|
||||
MMU.VROM_8K_SIZE = rom.GetVROM_SIZE();
|
||||
|
||||
// デフォルトバンク
|
||||
if (MMU.VROM_8K_SIZE != 0)
|
||||
{
|
||||
MMU.SetVROM_8K_Bank(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
MMU.SetCRAM_8K_Bank(0);
|
||||
}
|
||||
|
||||
// ミラー
|
||||
if (rom.Is4SCREEN())
|
||||
{
|
||||
MMU.SetVRAM_Mirror(MMU.VRAM_MIRROR4);
|
||||
}
|
||||
else if (rom.IsVMIRROR())
|
||||
{
|
||||
MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
MMU.SetVRAM_Mirror(MMU.VRAM_HMIRROR);
|
||||
}
|
||||
|
||||
apu.SelectExSound(0);
|
||||
|
||||
ppu.Reset();
|
||||
mapper.Reset();
|
||||
|
||||
// Trainer
|
||||
if (rom.IsTRAINER())
|
||||
{
|
||||
Array.Copy(rom.GetTRAINER(), 0, MMU.WRAM, 0x1000, 512);
|
||||
}
|
||||
|
||||
pad.Reset();
|
||||
cpu.Reset();
|
||||
apu.Reset();
|
||||
|
||||
if (rom.IsNSF())
|
||||
{
|
||||
mapper.Reset();
|
||||
}
|
||||
|
||||
base_cycles = emul_cycles = 0;
|
||||
}
|
||||
|
||||
internal void SetVideoMode(bool bMode)
|
||||
{
|
||||
bVideoMode = bMode;
|
||||
if (!bVideoMode)
|
||||
{
|
||||
nescfg = NesConfig.NESCONFIG_NTSC;
|
||||
}
|
||||
else
|
||||
{
|
||||
nescfg = NesConfig.NESCONFIG_PAL;
|
||||
}
|
||||
apu.SoundSetup();
|
||||
}
|
||||
|
||||
public void SetRenderMethod(EnumRenderMethod type)
|
||||
{
|
||||
RenderMethod = type;
|
||||
}
|
||||
|
||||
internal void SoftReset()
|
||||
{
|
||||
pad.Reset();
|
||||
cpu.Reset();
|
||||
apu.Reset();
|
||||
|
||||
if (rom.IsNSF())
|
||||
{
|
||||
mapper.Reset();
|
||||
}
|
||||
|
||||
m_bDiskThrottle = false;
|
||||
|
||||
base_cycles = emul_cycles = 0;
|
||||
}
|
||||
|
||||
internal void EmulateNSF()
|
||||
{
|
||||
R6502 reg = null;
|
||||
|
||||
ppu.Reset();
|
||||
mapper.VSync();
|
||||
|
||||
//DEBUGOUT( "Frame\n" );
|
||||
|
||||
if (m_bNsfPlaying)
|
||||
{
|
||||
if (m_bNsfInit)
|
||||
{
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length);
|
||||
if ((rom.GetNsfHeader().ExtraChipSelect & 0x04) == 0)
|
||||
{
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, 0x2000);
|
||||
}
|
||||
|
||||
apu.Reset();
|
||||
apu.Write(0x4015, 0x0F);
|
||||
apu.Write(0x4017, 0xC0);
|
||||
apu.ExWrite(0x4080, 0x80); // FDS Volume 0
|
||||
apu.ExWrite(0x408A, 0xE8); // FDS Envelope Speed
|
||||
|
||||
cpu.GetContext(ref reg);
|
||||
reg.PC = 0x4710; // Init Address
|
||||
reg.A = (byte)m_nNsfSongNo;
|
||||
reg.X = (byte)m_nNsfSongMode;
|
||||
reg.Y = 0;
|
||||
reg.S = 0xFF;
|
||||
reg.P = CPU.Z_FLAG | CPU.R_FLAG | CPU.I_FLAG;
|
||||
|
||||
// 安全対策を兼ねてあえてループに(1秒分)
|
||||
for (int i = 0; i < nescfg.TotalScanlines * 60; i++)
|
||||
{
|
||||
EmulationCPU(nescfg.ScanlineCycles);
|
||||
cpu.GetContext(ref reg);
|
||||
|
||||
// 無限ループに入ったことを確認したら抜ける
|
||||
if (reg.PC == 0x4700)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_bNsfInit = false;
|
||||
}
|
||||
|
||||
cpu.GetContext(ref reg);
|
||||
// 無限ループに入っていたら再設定する
|
||||
if (reg.PC == 0x4700)
|
||||
{
|
||||
reg.PC = 0x4720; // Play Address
|
||||
reg.A = 0;
|
||||
reg.S = 0xFF;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nescfg.TotalScanlines; i++)
|
||||
{
|
||||
EmulationCPU(nescfg.ScanlineCycles);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cpu.GetContext(ref reg);
|
||||
reg.PC = 0x4700; // 無限ループ
|
||||
reg.S = 0xFF;
|
||||
|
||||
EmulationCPU(nescfg.ScanlineCycles * nescfg.TotalScanlines);
|
||||
}
|
||||
}
|
||||
|
||||
internal void CheatCodeProcess()
|
||||
{
|
||||
//todo : ハオマヨラ<EFBE96>ラツ<EFBE97>
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
cpu?.Dispose();
|
||||
ppu?.Dispose();
|
||||
apu?.Dispose();
|
||||
pad?.Dispose();
|
||||
rom?.Dispose();
|
||||
}
|
||||
|
||||
private void SaveSRAM()
|
||||
{
|
||||
int i;
|
||||
if (rom.IsNSF()) return;
|
||||
if (rom.IsSAVERAM()) return;
|
||||
|
||||
for (i = 0; i < SAVERAM_SIZE; i++)
|
||||
{
|
||||
if (MMU.WRAM[i] != 0x00)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < SAVERAM_SIZE)
|
||||
{
|
||||
var romName = rom.GetRomName();
|
||||
|
||||
Debuger.Log($"Saving SAVERAM...[{romName}]");
|
||||
|
||||
Supporter.SaveSRAMToFile(MMU.WRAM, romName);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveDISK()
|
||||
{
|
||||
if (rom.GetMapperNo() != 20)
|
||||
return;
|
||||
|
||||
int i = 0;
|
||||
Stream fp = null;
|
||||
DISKFILEHDR ifh;
|
||||
byte[] lpDisk = rom.GetPROM();
|
||||
byte[] lpWrite = rom.GetDISK();
|
||||
long DiskSize = 16 + 65500 * rom.GetDiskNo();
|
||||
ulong data;
|
||||
|
||||
try
|
||||
{
|
||||
ifh = new DISKFILEHDR();
|
||||
ifh.ID = ASCIIEncoding.ASCII.GetBytes("VirtuaNES DI");
|
||||
ifh.BlockVersion = 0x0210;
|
||||
ifh.ProgID = rom.GetGameID();
|
||||
ifh.MakerID = (ushort)rom.GetMakerID();
|
||||
ifh.DiskNo = (ushort)rom.GetDiskNo();
|
||||
|
||||
for (i = 16; i < DiskSize; i++)
|
||||
{
|
||||
if (lpWrite[i] > 0)
|
||||
ifh.DifferentSize++;
|
||||
}
|
||||
|
||||
if (ifh.DifferentSize == 0)
|
||||
return;
|
||||
|
||||
List<byte> contents = new List<byte>();
|
||||
contents.AddRange(ifh.ToBytes());
|
||||
|
||||
for (i = 16; i < DiskSize; i++)
|
||||
{
|
||||
if (lpWrite[i] > 0)
|
||||
{
|
||||
data = (ulong)(i & 0x00FFFFFF);
|
||||
data |= ((ulong)lpDisk[i] & 0xFF) << 24;
|
||||
contents.AddRange(BitConverter.GetBytes(data));
|
||||
}
|
||||
}
|
||||
|
||||
Supporter.SaveDISKToFile(contents.ToArray(), rom.GetRomName());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debuger.LogError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveTurboFile()
|
||||
{
|
||||
//todo : ハオマヨ
|
||||
}
|
||||
|
||||
internal void Clock(int cycles)
|
||||
{
|
||||
Tape(cycles);
|
||||
Barcode(cycles);
|
||||
}
|
||||
|
||||
private void Barcode(int cycles)
|
||||
{
|
||||
if (m_bBarcode)
|
||||
{
|
||||
m_BarcodeCycles += cycles;
|
||||
if (m_BarcodeCycles > 1000)
|
||||
{
|
||||
m_BarcodeCycles = 0;
|
||||
// 停止?
|
||||
if (m_BarcodeData[m_BarcodePtr] != 0xFF)
|
||||
{
|
||||
m_BarcodeOut = m_BarcodeData[m_BarcodePtr++];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bBarcode = false;
|
||||
m_BarcodeOut = 0;
|
||||
Debuger.Log("Barcode data trasnfer complete!!");
|
||||
|
||||
if (!(IsTapePlay() || IsTapeRec()))
|
||||
{
|
||||
cpu.SetClockProcess(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTapeRec()
|
||||
{
|
||||
return m_bTapeRec;
|
||||
}
|
||||
|
||||
public bool IsTapePlay()
|
||||
{
|
||||
return m_bTapePlay;
|
||||
}
|
||||
|
||||
internal void Tape(int cycles)
|
||||
{
|
||||
//todo : ハオマヨTape (トソイ簗ヌシヌツシヘ貍メイルラ<EFBE99>ルPlay,モナマネシカコワオヘ)
|
||||
}
|
||||
|
||||
internal byte Read(ushort addr)
|
||||
{
|
||||
switch (addr >> 13)
|
||||
{
|
||||
case 0x00: // $0000-$1FFF
|
||||
return MMU.RAM[addr & 0x07FF];
|
||||
case 0x01: // $2000-$3FFF
|
||||
return ppu.Read((ushort)(addr & 0xE007));
|
||||
case 0x02: // $4000-$5FFF
|
||||
if (addr < 0x4100)
|
||||
{
|
||||
return ReadReg(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return mapper.ReadLow(addr);
|
||||
}
|
||||
case 0x03: // $6000-$7FFF
|
||||
return mapper.ReadLow(addr);
|
||||
case 0x04: // $8000-$9FFF
|
||||
case 0x05: // $A000-$BFFF
|
||||
case 0x06: // $C000-$DFFF
|
||||
case 0x07: // $E000-$FFFF
|
||||
return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF];
|
||||
}
|
||||
|
||||
return 0x00; // Warning予防
|
||||
}
|
||||
|
||||
private byte ReadReg(ushort addr)
|
||||
{
|
||||
switch (addr & 0xFF)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B:
|
||||
case 0x0C:
|
||||
case 0x0D:
|
||||
case 0x0E:
|
||||
case 0x0F:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
return apu.Read(addr);
|
||||
case 0x15:
|
||||
return apu.Read(addr);
|
||||
case 0x14:
|
||||
return (byte)(addr & 0xFF);
|
||||
case 0x16:
|
||||
if (rom.IsVSUNISYSTEM())
|
||||
{
|
||||
return pad.Read(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (byte)(pad.Read(addr) | 0x40 | m_TapeOut);
|
||||
}
|
||||
case 0x17:
|
||||
if (rom.IsVSUNISYSTEM())
|
||||
{
|
||||
return pad.Read(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (byte)(pad.Read(addr) | apu.Read(addr));
|
||||
}
|
||||
default:
|
||||
return mapper.ExRead(addr);
|
||||
}
|
||||
}
|
||||
|
||||
internal byte Barcode2()
|
||||
{
|
||||
byte ret = 0x00;
|
||||
|
||||
if (!m_bBarcode2 || m_Barcode2seq < 0)
|
||||
return ret;
|
||||
|
||||
switch (m_Barcode2seq)
|
||||
{
|
||||
case 0:
|
||||
m_Barcode2seq++;
|
||||
m_Barcode2ptr = 0;
|
||||
ret = 0x04; // d3
|
||||
break;
|
||||
|
||||
case 1:
|
||||
m_Barcode2seq++;
|
||||
m_Barcode2bit = m_Barcode2data[m_Barcode2ptr];
|
||||
m_Barcode2cnt = 0;
|
||||
ret = 0x04; // d3
|
||||
break;
|
||||
|
||||
case 2:
|
||||
ret = (byte)((m_Barcode2bit & 0x01) != 0 ? 0x00 : 0x04); // Bit rev.
|
||||
m_Barcode2bit >>= 1;
|
||||
if (++m_Barcode2cnt > 7)
|
||||
{
|
||||
m_Barcode2seq++;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (++m_Barcode2ptr > 19)
|
||||
{
|
||||
m_bBarcode2 = false;
|
||||
m_Barcode2seq = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Barcode2seq = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr >> 13)
|
||||
{
|
||||
case 0x00: // $0000-$1FFF
|
||||
MMU.RAM[addr & 0x07FF] = data;
|
||||
break;
|
||||
case 0x01: // $2000-$3FFF
|
||||
if (!rom.IsNSF())
|
||||
{
|
||||
ppu.Write((ushort)(addr & 0xE007), data);
|
||||
}
|
||||
break;
|
||||
case 0x02: // $4000-$5FFF
|
||||
if (addr < 0x4100)
|
||||
{
|
||||
WriteReg(addr, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapper.WriteLow(addr, data);
|
||||
}
|
||||
break;
|
||||
case 0x03: // $6000-$7FFF
|
||||
mapper.WriteLow(addr, data);
|
||||
break;
|
||||
case 0x04: // $8000-$9FFF
|
||||
case 0x05: // $A000-$BFFF
|
||||
case 0x06: // $C000-$DFFF
|
||||
case 0x07: // $E000-$FFFF
|
||||
mapper.Write(addr, data);
|
||||
|
||||
GenieCodeProcess();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void GenieCodeProcess()
|
||||
{
|
||||
ushort addr;
|
||||
|
||||
for (int i = 0; i < m_GenieCode.Count; i++)
|
||||
{
|
||||
addr = m_GenieCode[i].address;
|
||||
if ((addr & 0x8000) != 0)
|
||||
{
|
||||
// 8character codes
|
||||
if (MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF] == m_GenieCode[i].cmp)
|
||||
{
|
||||
MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF] = m_GenieCode[i].data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 6character codes
|
||||
addr |= 0x8000;
|
||||
MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF] = m_GenieCode[i].data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteReg(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xFF)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B:
|
||||
case 0x0C:
|
||||
case 0x0D:
|
||||
case 0x0E:
|
||||
case 0x0F:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
case 0x15:
|
||||
apu.Write(addr, data);
|
||||
MMU.CPUREG[addr & 0xFF] = data;
|
||||
break;
|
||||
case 0x14:
|
||||
ppu.DMA(data);
|
||||
cpu.DMA(514); // DMA Pending cycle
|
||||
MMU.CPUREG[addr & 0xFF] = data;
|
||||
break;
|
||||
case 0x16:
|
||||
mapper.ExWrite(addr, data); // For VS-Unisystem
|
||||
pad.Write(addr, data);
|
||||
MMU.CPUREG[addr & 0xFF] = data;
|
||||
m_TapeIn = data;
|
||||
break;
|
||||
case 0x17:
|
||||
MMU.CPUREG[addr & 0xFF] = data;
|
||||
pad.Write(addr, data);
|
||||
apu.Write(addr, data);
|
||||
break;
|
||||
// VirtuaNES固有ポート
|
||||
case 0x18:
|
||||
apu.Write(addr, data);
|
||||
break;
|
||||
default:
|
||||
mapper.ExWrite(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool GetVideoMode()
|
||||
{
|
||||
return bVideoMode;
|
||||
}
|
||||
|
||||
internal void SetFrameIRQmode(bool bMode)
|
||||
{
|
||||
bFrameIRQ = bMode;
|
||||
}
|
||||
|
||||
internal bool GetFrameIRQmode()
|
||||
{
|
||||
return bFrameIRQ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,29 @@
|
||||
namespace VirtualNes.Core
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class PAD
|
||||
{
|
||||
protected NES nes;
|
||||
protected int excontroller_select;
|
||||
protected EXPAD expad;
|
||||
protected bool bStrobe;
|
||||
protected bool bSwapButton;
|
||||
protected bool bSwapPlayer;
|
||||
protected bool bZapperMode;
|
||||
protected VSType nVSSwapType;
|
||||
protected byte[] padbit = new byte[4];
|
||||
protected byte micbit;
|
||||
protected byte[] padbitsync = new byte[4];
|
||||
protected byte micbitsync;
|
||||
private NES nes;
|
||||
private int excontroller_select;
|
||||
private EXPAD expad;
|
||||
private bool bStrobe;
|
||||
private bool bSwapButton;
|
||||
private bool bSwapPlayer;
|
||||
private bool bZapperMode;
|
||||
private VSType nVSSwapType;
|
||||
private byte[] padbit = new byte[4];
|
||||
private byte micbit;
|
||||
private byte[] padbitsync = new byte[4];
|
||||
private byte micbitsync;
|
||||
private bool bBarcodeWorld;
|
||||
private int[][] padcnt = new int[4][]
|
||||
{
|
||||
new int[2],new int[2],new int[2],new int[2],
|
||||
};
|
||||
|
||||
public uint pad1bit, pad2bit, pad3bit, pad4bit;
|
||||
|
||||
public PAD(NES parent)
|
||||
{
|
||||
@ -32,6 +42,368 @@
|
||||
padbitsync[0] = padbitsync[1] = padbitsync[2] = padbitsync[3] = 0;
|
||||
micbitsync = 0;
|
||||
}
|
||||
|
||||
internal byte Read(ushort addr)
|
||||
{
|
||||
byte data = 0x00;
|
||||
|
||||
if (addr == 0x4016)
|
||||
{
|
||||
data = (byte)(pad1bit & 1);
|
||||
pad1bit >>= 1;
|
||||
data |= (byte)(((pad3bit & 1)) << 1);
|
||||
pad3bit >>= 1;
|
||||
// Mic
|
||||
if (!nes.rom.IsVSUNISYSTEM())
|
||||
{
|
||||
data |= micbitsync;
|
||||
}
|
||||
if (expad != null)
|
||||
{
|
||||
data |= expad.Read4016();
|
||||
}
|
||||
}
|
||||
if (addr == 0x4017)
|
||||
{
|
||||
data = (byte)(pad2bit & 1);
|
||||
pad2bit >>= 1;
|
||||
data |= (byte)((pad4bit & 1) << 1);
|
||||
pad4bit >>= 1;
|
||||
|
||||
if (expad != null)
|
||||
{
|
||||
data |= expad.Read4017();
|
||||
}
|
||||
|
||||
if (bBarcodeWorld)
|
||||
{
|
||||
data |= nes.Barcode2();
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
public void Dispose() { }
|
||||
|
||||
internal void Write(ushort addr, byte data)
|
||||
{
|
||||
if (addr == 0x4016)
|
||||
{
|
||||
if ((data & 0x01) != 0)
|
||||
{
|
||||
bStrobe = true;
|
||||
}
|
||||
else if (bStrobe)
|
||||
{
|
||||
bStrobe = false;
|
||||
|
||||
Strobe();
|
||||
if (expad != null)
|
||||
{
|
||||
expad.Strobe();
|
||||
}
|
||||
}
|
||||
|
||||
if (expad != null)
|
||||
{
|
||||
expad.Write4016(data);
|
||||
}
|
||||
}
|
||||
if (addr == 0x4017)
|
||||
{
|
||||
if (expad != null)
|
||||
{
|
||||
expad.Write4017(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Strobe()
|
||||
{
|
||||
// For VS-Unisystem
|
||||
if (nes.rom.IsVSUNISYSTEM())
|
||||
{
|
||||
uint pad1 = (uint)(padbitsync[0] & 0xF3);
|
||||
uint pad2 = (uint)(padbitsync[1] & 0xF3);
|
||||
uint st1 = (uint)(padbitsync[0] & 0x08) >> 3;
|
||||
uint st2 = (uint)(padbitsync[1] & 0x08) >> 3;
|
||||
|
||||
switch (nVSSwapType)
|
||||
{
|
||||
case VSType.VS_TYPE0:
|
||||
pad1bit = pad1 | (st1 << 2);
|
||||
pad2bit = pad2 | (st2 << 2);
|
||||
break;
|
||||
case VSType.VS_TYPE1:
|
||||
pad1bit = pad2 | (st1 << 2);
|
||||
pad2bit = pad1 | (st2 << 2);
|
||||
break;
|
||||
case VSType.VS_TYPE2:
|
||||
pad1bit = pad1 | (st1 << 2) | (st2 << 3);
|
||||
pad2bit = pad2;
|
||||
break;
|
||||
case VSType.VS_TYPE3:
|
||||
pad1bit = pad2 | (st1 << 2) | (st2 << 3);
|
||||
pad2bit = pad1;
|
||||
break;
|
||||
case VSType.VS_TYPE4:
|
||||
pad1bit = pad1 | (st1 << 2) | 0x08; // 0x08=Start Protect
|
||||
pad2bit = pad2 | (st2 << 2) | 0x08; // 0x08=Start Protect
|
||||
break;
|
||||
case VSType.VS_TYPE5:
|
||||
pad1bit = pad2 | (st1 << 2) | 0x08; // 0x08=Start Protect
|
||||
pad2bit = pad1 | (st2 << 2) | 0x08; // 0x08=Start Protect
|
||||
break;
|
||||
case VSType.VS_TYPE6:
|
||||
pad1bit = pad1 | (st1 << 2) | (((uint)padbitsync[0] & 0x04) << 1);
|
||||
pad2bit = pad2 | (st2 << 2) | (((uint)padbitsync[1] & 0x04) << 1);
|
||||
break;
|
||||
case VSType.VS_TYPEZ:
|
||||
pad1bit = 0;
|
||||
pad2bit = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Coin 2偲旐傞堊偵徚偡
|
||||
micbit = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Supporter.Config.emulator.bFourPlayer)
|
||||
{
|
||||
// NES type
|
||||
pad1bit = padbitsync[0] | ((uint)padbitsync[2] << 8) | 0x00080000;
|
||||
pad2bit = padbitsync[1] | ((uint)padbitsync[3] << 8) | 0x00040000;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Famicom type
|
||||
pad1bit = padbitsync[0];
|
||||
pad2bit = padbitsync[1];
|
||||
}
|
||||
}
|
||||
pad3bit = padbitsync[2];
|
||||
pad4bit = padbitsync[3];
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
pad1bit = pad2bit = 0;
|
||||
bStrobe = false;
|
||||
|
||||
bBarcodeWorld = false;
|
||||
|
||||
for (int x = 0; x < 4; x++)
|
||||
{
|
||||
for (int y = 0; y < 2; y++)
|
||||
{
|
||||
padcnt[x][y] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Select Extension Devices
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xfbfc6a6c // Adventures of Bayou Billy, The(E)
|
||||
|| crc == 0xcb275051 // Adventures of Bayou Billy, The(U)
|
||||
|| crc == 0xfb69c131 // Baby Boomer(Unl)(U)
|
||||
|| crc == 0xf2641ad0 // Barker Bill's Trick Shooting(U)
|
||||
|| crc == 0xbc1dce96 // Chiller (Unl)(U)
|
||||
|| crc == 0x90ca616d // Duck Hunt(JUE)
|
||||
|| crc == 0x59e3343f // Freedom Force(U)
|
||||
|| crc == 0x242a270c // Gotcha!(U)
|
||||
|| crc == 0x7b5bd2de // Gumshoe(UE)
|
||||
|| crc == 0x255b129c // Gun Sight(J)
|
||||
|| crc == 0x8963ae6e // Hogan's Alley(JU)
|
||||
|| crc == 0x51d2112f // Laser Invasion(U)
|
||||
|| crc == 0x0a866c94 // Lone Ranger, The(U)
|
||||
// || crc == 0xe4c04eea // Mad City(J)
|
||||
|| crc == 0x9eef47aa // Mechanized Attack(U)
|
||||
|| crc == 0xc2db7551 // Shooting Range(U)
|
||||
|| crc == 0x163e86c0 // To The Earth(U)
|
||||
|| crc == 0x42d893e4 // Operation Wolf(J)
|
||||
|| crc == 0x1388aeb9 // Operation Wolf(U)
|
||||
|| crc == 0x0d3cf705 // Wild Gunman(J)
|
||||
|| crc == 0x389960db)
|
||||
{ // Wild Gunman(JUE)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_ZAPPER);
|
||||
}
|
||||
if (crc == 0x35893b67 // Arkanoid(J)
|
||||
|| crc == 0x6267fbd1)
|
||||
{ // Arkanoid 2(J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_PADDLE);
|
||||
}
|
||||
if (crc == 0xff6621ce // Hyper Olympic(J)
|
||||
|| crc == 0xdb9418e8 // Hyper Olympic(Tonosama Ban)(J)
|
||||
|| crc == 0xac98cd70)
|
||||
{ // Hyper Sports(J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_HYPERSHOT);
|
||||
}
|
||||
if (crc == 0xf9def527 // Family BASIC(Ver2.0)
|
||||
|| crc == 0xde34526e // Family BASIC(Ver2.1a)
|
||||
|| crc == 0xf050b611 // Family BASIC(Ver3)
|
||||
|| crc == 0x3aaeed3f // Family BASIC(Ver3)(Alt)
|
||||
|| crc == 0x868FCD89 // Family BASIC(Ver1.0)
|
||||
|| crc == 0x2D6B7E5A // PLAYBOX BASIC(J) (Prototype_v0.0)
|
||||
|| crc == 0xDA03D908)
|
||||
{ // PLAYBOX BASIC (J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_KEYBOARD);
|
||||
}
|
||||
if (crc == 0x589b6b0d // Supor Computer V3.0
|
||||
|| crc == 0x8b265862 // Supor English
|
||||
|| crc == 0x41401c6d // Supor Computer V4.0
|
||||
|| crc == 0x82F1Fb96 // Supor Computer(Russia) V1.0
|
||||
|| crc == 0xd5d6eac4)
|
||||
{ // EDU(C) Computer
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_SUPOR_KEYBOARD);
|
||||
nes.SetVideoMode(true);
|
||||
}
|
||||
if (crc == 0xc68363f6 // Crazy Climber(J)
|
||||
|| crc == 0x2989ead6 // Smash TV(U) [!]
|
||||
|| crc == 0x0b8f8128)
|
||||
{ // Smash TV(E) [!]
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER);
|
||||
}
|
||||
if (crc == 0x20d22251)
|
||||
{ // Top rider(J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_TOPRIDER);
|
||||
}
|
||||
if (crc == 0x0cd00488)
|
||||
{ // Space Shadow(J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN);
|
||||
}
|
||||
|
||||
if (crc == 0x8c8fa83b // Family Trainer - Athletic World (J)
|
||||
|| crc == 0x7e704a14 // Family Trainer - Jogging Race (J)
|
||||
|| crc == 0x2330a5d3)
|
||||
{ // Family Trainer - Rairai Kyonshiizu (J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A);
|
||||
}
|
||||
if (crc == 0xf8da2506 // Family Trainer - Aerobics Studio (J)
|
||||
|| crc == 0xca26a0f1 // Family Trainer - Dai Undoukai (J)
|
||||
|| crc == 0x28068b8c // Family Trainer - Fuuun Takeshi Jou 2 (J)
|
||||
|| crc == 0x10bb8f9a // Family Trainer - Manhattan Police (J)
|
||||
|| crc == 0xad3df455 // Family Trainer - Meiro Dai Sakusen (J)
|
||||
|| crc == 0x8a5b72c0 // Family Trainer - Running Stadium (J)
|
||||
|| crc == 0x59794f2d)
|
||||
{ // Family Trainer - Totsugeki Fuuun Takeshi Jou (J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B);
|
||||
}
|
||||
if (crc == 0x9fae4d46 // Ide Yousuke Meijin no Jissen Mahjong (J)
|
||||
|| crc == 0x7b44fb2a)
|
||||
{ // Ide Yousuke Meijin no Jissen Mahjong 2 (J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_MAHJANG);
|
||||
}
|
||||
if (crc == 0x786148b6)
|
||||
{ // Exciting Boxing (J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING);
|
||||
}
|
||||
if (crc == 0xc3c0811d // Oeka Kids - Anpanman no Hiragana Daisuki (J)
|
||||
|| crc == 0x9d048ea4)
|
||||
{ // Oeka Kids - Anpanman to Oekaki Shiyou!! (J)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET);
|
||||
}
|
||||
|
||||
if (crc == 0x67898319)
|
||||
{ // Barcode World (J)
|
||||
bBarcodeWorld = true;
|
||||
}
|
||||
|
||||
// VS-Unisystem
|
||||
if (nes.rom.IsVSUNISYSTEM())
|
||||
{
|
||||
if (crc == 0xff5135a3 // VS Hogan's Alley
|
||||
|| crc == 0xed588f00 // VS Duck Hunt
|
||||
|| crc == 0x17ae56be)
|
||||
{ // VS Freedom Force
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_VSZAPPER);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_VSUNISYSTEM);
|
||||
}
|
||||
}
|
||||
|
||||
if (crc == 0x21b099f3)
|
||||
{ // Gyromite (JUE)
|
||||
SetExController(EXCONTROLLER.EXCONTROLLER_GYROMITE);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetExController(EXCONTROLLER type)
|
||||
{
|
||||
excontroller_select = (int)type;
|
||||
|
||||
expad?.Dispose();
|
||||
expad = null;
|
||||
|
||||
bZapperMode = false;
|
||||
|
||||
// ExPad Instance create
|
||||
switch (type)
|
||||
{
|
||||
case EXCONTROLLER.EXCONTROLLER_ZAPPER:
|
||||
expad = new EXPAD_Zapper(nes);
|
||||
bZapperMode = true;
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_PADDLE:
|
||||
expad = new EXPAD_Paddle(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_HYPERSHOT:
|
||||
expad = new EXPAD_HyperShot(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_KEYBOARD:
|
||||
expad = new EXPAD_Keyboard(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_SUPOR_KEYBOARD:
|
||||
expad = new EXPAD_Supor_Keyboard(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER:
|
||||
expad = new EXPAD_CrazyClimber(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_TOPRIDER:
|
||||
expad = new EXPAD_Toprider(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN:
|
||||
expad = new EXPAD_SpaceShadowGun(nes);
|
||||
bZapperMode = true;
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A:
|
||||
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B:
|
||||
expad = new EXPAD_FamlyTrainer(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING:
|
||||
expad = new EXPAD_ExcitingBoxing(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_MAHJANG:
|
||||
expad = new EXPAD_Mahjang(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET:
|
||||
expad = new EXPAD_OekakidsTablet(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_TURBOFILE:
|
||||
expad = new EXPAD_TurboFile(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_VSUNISYSTEM:
|
||||
expad = new EXPAD_VSUnisystem(nes);
|
||||
break;
|
||||
case EXCONTROLLER.EXCONTROLLER_VSZAPPER:
|
||||
expad = new EXPAD_VSZapper(nes);
|
||||
bZapperMode = true;
|
||||
break;
|
||||
|
||||
case EXCONTROLLER.EXCONTROLLER_GYROMITE:
|
||||
expad = new EXPAD_Gyromite(nes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (expad != null)
|
||||
{
|
||||
expad.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum VSType
|
||||
@ -45,4 +417,31 @@
|
||||
VS_TYPE6, // SELECT1P=START1P/SELECT2P=START2P 1P/2P Reverse (For Golf)
|
||||
VS_TYPEZ, // ZAPPER
|
||||
}
|
||||
|
||||
public enum EXCONTROLLER
|
||||
{
|
||||
EXCONTROLLER_NONE = 0,
|
||||
EXCONTROLLER_PADDLE,
|
||||
EXCONTROLLER_HYPERSHOT,
|
||||
EXCONTROLLER_ZAPPER,
|
||||
EXCONTROLLER_KEYBOARD,
|
||||
EXCONTROLLER_CRAZYCLIMBER,
|
||||
EXCONTROLLER_TOPRIDER,
|
||||
EXCONTROLLER_SPACESHADOWGUN,
|
||||
|
||||
EXCONTROLLER_FAMILYTRAINER_A,
|
||||
EXCONTROLLER_FAMILYTRAINER_B,
|
||||
EXCONTROLLER_EXCITINGBOXING,
|
||||
EXCONTROLLER_MAHJANG,
|
||||
EXCONTROLLER_OEKAKIDS_TABLET,
|
||||
EXCONTROLLER_TURBOFILE,
|
||||
|
||||
EXCONTROLLER_VSUNISYSTEM,
|
||||
EXCONTROLLER_VSZAPPER,
|
||||
|
||||
EXCONTROLLER_GYROMITE,
|
||||
EXCONTROLLER_STACKUP,
|
||||
|
||||
EXCONTROLLER_SUPOR_KEYBOARD,
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,124 @@
|
||||
namespace VirtualNes.Core
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class PPU
|
||||
{
|
||||
private NES m_nes;
|
||||
private static byte[][] CreateCOLORMAP()
|
||||
{
|
||||
byte[][] res = new byte[5][];
|
||||
res[0] = new byte[64]
|
||||
{ 0x35, 0xFF, 0x16, 0x22, 0x1C, 0xFF, 0xFF, 0x15,
|
||||
0xFF, 0x00, 0x27, 0x05, 0x04, 0x27, 0x08, 0x30,
|
||||
0x21, 0xFF, 0xFF, 0x29, 0x3C, 0xFF, 0x36, 0x12,
|
||||
0xFF, 0x2B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01,
|
||||
0xFF, 0x31, 0xFF, 0x2A, 0x2C, 0x0C, 0xFF, 0xFF,
|
||||
0xFF, 0x07, 0x34, 0x06, 0x13, 0xFF, 0x26, 0x0F,
|
||||
0xFF, 0x19, 0x10, 0x0A, 0xFF, 0xFF, 0xFF, 0x17,
|
||||
0xFF, 0x11, 0x09, 0xFF, 0xFF, 0x25, 0x18, 0xFF
|
||||
};
|
||||
res[1] = new byte[64]
|
||||
{ 0xFF, 0x27, 0x18, 0xFF, 0x3A, 0x25, 0xFF, 0x31,
|
||||
0x16, 0x13, 0x38, 0x34, 0x20, 0x23, 0x31, 0x1A,
|
||||
0xFF, 0x21, 0x06, 0xFF, 0x1B, 0x29, 0xFF, 0x22,
|
||||
0xFF, 0x24, 0xFF, 0xFF, 0xFF, 0x08, 0xFF, 0x03,
|
||||
0xFF, 0x36, 0x26, 0x33, 0x11, 0xFF, 0x10, 0x02,
|
||||
0x14, 0xFF, 0x00, 0x09, 0x12, 0x0F, 0xFF, 0x30,
|
||||
0xFF, 0xFF, 0x2A, 0x17, 0x0C, 0x01, 0x15, 0x19,
|
||||
0xFF, 0x2C, 0x07, 0x37, 0xFF, 0x05, 0xFF, 0xFF
|
||||
};
|
||||
res[2] = new byte[64]
|
||||
{ 0xFF, 0xFF, 0xFF, 0x10, 0x1A, 0x30, 0x31, 0x09,
|
||||
0x01, 0x0F, 0x36, 0x08, 0x15, 0xFF, 0xFF, 0xF0,
|
||||
0x22, 0x1C, 0xFF, 0x12, 0x19, 0x18, 0x17, 0xFF,
|
||||
0x00, 0xFF, 0xFF, 0x02, 0x16, 0x06, 0xFF, 0x35,
|
||||
0x23, 0xFF, 0x8B, 0xF7, 0xFF, 0x27, 0x26, 0x20,
|
||||
0x29, 0xFF, 0x21, 0x24, 0x11, 0xFF, 0xEF, 0xFF,
|
||||
0x2C, 0xFF, 0xFF, 0xFF, 0x07, 0xF9, 0x28, 0xFF,
|
||||
0x0A, 0xFF, 0x32, 0x37, 0x13, 0xFF, 0xFF, 0x0C
|
||||
};
|
||||
res[3] = new byte[64]
|
||||
{ 0x18, 0xFF, 0x1C, 0x89, 0x0F, 0xFF, 0x01, 0x17, // 00-07
|
||||
0x10, 0x0F, 0x2A, 0xFF, 0x36, 0x37, 0x1A, 0xFF, // 08-0F
|
||||
0x25, 0xFF, 0x12, 0xFF, 0x0F, 0xFF, 0xFF, 0x26, // 10-17
|
||||
0xFF, 0xFF, 0x22, 0xFF, 0xFF, 0x0F, 0x3A, 0x21, // 18-1F
|
||||
0x05, 0x0A, 0x07, 0xC2, 0x13, 0xFF, 0x00, 0x15, // 20-27
|
||||
0x0C, 0xFF, 0x11, 0xFF, 0xFF, 0x38, 0xFF, 0xFF, // 28-2F
|
||||
0xFF, 0xFF, 0x08, 0x16, 0xFF, 0xFF, 0x30, 0x3C, // 30-37
|
||||
0x0F, 0x27, 0xFF, 0x60, 0x29, 0xFF, 0x30, 0x09 // 38-3F
|
||||
};
|
||||
res[4] = new byte[64]
|
||||
{
|
||||
// Super Xevious/Gradius
|
||||
0x35, 0xFF, 0x16, 0x22, 0x1C, 0x09, 0xFF, 0x15, // 00-07
|
||||
0x20, 0x00, 0x27, 0x05, 0x04, 0x28, 0x08, 0x30, // 08-0F
|
||||
0x21, 0xFF, 0xFF, 0x29, 0x3C, 0xFF, 0x36, 0x12, // 10-17
|
||||
0xFF, 0x2B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, // 18-1F
|
||||
0xFF, 0x31, 0xFF, 0x2A, 0x2C, 0x0C, 0x1B, 0xFF, // 20-27
|
||||
0xFF, 0x07, 0x34, 0x06, 0xFF, 0x25, 0x26, 0x0F, // 28-2F
|
||||
0xFF, 0x19, 0x10, 0x0A, 0xFF, 0xFF, 0xFF, 0x17, // 30-37
|
||||
0xFF, 0x11, 0x1A, 0xFF, 0x38, 0xFF, 0x18, 0x3A, // 38-3F
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
private static byte[][] VSColorMap = CreateCOLORMAP();
|
||||
|
||||
// PPU Control Register #1 PPU #0
|
||||
public const byte PPU_VBLANK_BIT = 0x80;
|
||||
public const byte PPU_SPHIT_BIT = 0x40; // 堘偆丠
|
||||
public const byte PPU_SP16_BIT = 0x20;
|
||||
public const byte PPU_BGTBL_BIT = 0x10;
|
||||
public const byte PPU_SPTBL_BIT = 0x08;
|
||||
public const byte PPU_INC32_BIT = 0x04;
|
||||
public const byte PPU_NAMETBL_BIT = 0x03;
|
||||
|
||||
// PPU Control Register #2 PPU #1
|
||||
public const byte PPU_BGCOLOR_BIT = 0xE0;
|
||||
public const byte PPU_SPDISP_BIT = 0x10;
|
||||
public const byte PPU_BGDISP_BIT = 0x08;
|
||||
public const byte PPU_SPCLIP_BIT = 0x04;
|
||||
public const byte PPU_BGCLIP_BIT = 0x02;
|
||||
public const byte PPU_COLORMODE_BIT = 0x01;
|
||||
|
||||
// PPU Status Register PPU #2
|
||||
public const byte PPU_VBLANK_FLAG = 0x80;
|
||||
public const byte PPU_SPHIT_FLAG = 0x40;
|
||||
public const byte PPU_SPMAX_FLAG = 0x20;
|
||||
public const byte PPU_WENABLE_FLAG = 0x10;
|
||||
|
||||
// SPRITE Attribute
|
||||
public const byte SP_VMIRROR_BIT = 0x80;
|
||||
public const byte SP_HMIRROR_BIT = 0x40;
|
||||
public const byte SP_PRIORITY_BIT = 0x20;
|
||||
public const byte SP_COLOR_BIT = 0x03;
|
||||
|
||||
private NES nes;
|
||||
|
||||
private bool bExtLatch; // For MMC5
|
||||
private bool bChrLatch; // For MMC2/MMC4
|
||||
private bool bExtNameTable; // For Super Monkey no Dai Bouken
|
||||
private bool bExtMono; // For Final Fantasy
|
||||
|
||||
private ushort loopy_y;
|
||||
private ushort loopy_shift;
|
||||
|
||||
private byte[] lpScreen;
|
||||
/// <summary> 作为lpScreen数组的索引 </summary>
|
||||
private int lpScanline;
|
||||
private int ScanlineNo;
|
||||
private byte[] lpColormode;
|
||||
|
||||
private bool bVSMode;
|
||||
private int nVSColorMap;
|
||||
private byte VSSecurityData;
|
||||
private byte[] Bit2Rev = new byte[256];
|
||||
|
||||
|
||||
public PPU(NES nes)
|
||||
{
|
||||
m_nes = nes;
|
||||
this.nes = nes;
|
||||
lpScreen = null;
|
||||
lpColormode = null;
|
||||
|
||||
@ -31,5 +138,235 @@
|
||||
Bit2Rev[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
internal byte Read(ushort addr)
|
||||
{
|
||||
byte data = 0x00;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
// Write only Register
|
||||
case 0x2000: // PPU Control Register #1(W)
|
||||
case 0x2001: // PPU Control Register #2(W)
|
||||
case 0x2003: // SPR-RAM Address Register(W)
|
||||
case 0x2005: // PPU Scroll Register(W2)
|
||||
case 0x2006: // VRAM Address Register(W2)
|
||||
data = MMU.PPU7_Temp; // 懡暘
|
||||
break;
|
||||
// Read/Write Register
|
||||
case 0x2002: // PPU Status Register(R)
|
||||
//DEBUGOUT( "2002 RD L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() );
|
||||
data = (byte)(MMU.PPUREG[2] | VSSecurityData);
|
||||
MMU.PPU56Toggle = 0;
|
||||
byte temp = unchecked((byte)~PPU_VBLANK_FLAG);
|
||||
MMU.PPUREG[2] &= temp;
|
||||
break;
|
||||
case 0x2004: // SPR_RAM I/O Register(RW)
|
||||
data = MMU.SPRAM[MMU.PPUREG[3]++];
|
||||
break;
|
||||
case 0x2007: // VRAM I/O Register(RW)
|
||||
addr = (ushort)(MMU.loopy_v & 0x3FFF);
|
||||
data = MMU.PPU7_Temp;
|
||||
if ((MMU.PPUREG[0] & PPU_INC32_BIT) != 0) MMU.loopy_v += 32;
|
||||
else MMU.loopy_v++;
|
||||
if (addr >= 0x3000)
|
||||
{
|
||||
if (addr >= 0x3F00)
|
||||
{
|
||||
// data &= 0x3F;
|
||||
if ((addr & 0x0010) == 0)
|
||||
{
|
||||
return MMU.BGPAL[addr & 0x000F];
|
||||
}
|
||||
else
|
||||
{
|
||||
return MMU.SPPAL[addr & 0x000F];
|
||||
}
|
||||
}
|
||||
addr &= 0xEFFF;
|
||||
}
|
||||
MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10].Span[addr & 0x03FF];
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
internal void SetRenderScanline(int scanline)
|
||||
{
|
||||
ScanlineNo = scanline;
|
||||
if (scanline < 240)
|
||||
{
|
||||
lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Write(ushort addr, byte data)
|
||||
{
|
||||
if (bVSMode && VSSecurityData != 0)
|
||||
{
|
||||
if (addr == 0x2000)
|
||||
{
|
||||
addr = 0x2001;
|
||||
}
|
||||
else if (addr == 0x2001)
|
||||
{
|
||||
addr = 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
// Read only Register
|
||||
case 0x2002: // PPU Status register(R)
|
||||
break;
|
||||
// Write Register
|
||||
case 0x2000: // PPU Control Register #1(W)
|
||||
// NameTable select
|
||||
// t:0000110000000000=d:00000011
|
||||
MMU.loopy_t = (ushort)((MMU.loopy_t & 0xF3FF) | ((data & 0x03) << 10));
|
||||
|
||||
if ((data & 0x80) != 0 && (MMU.PPUREG[0] & 0x80) == 0 && (MMU.PPUREG[2] & 0x80) != 0)
|
||||
{
|
||||
nes.cpu.NMI(); // hmm...
|
||||
}
|
||||
|
||||
MMU.PPUREG[0] = data;
|
||||
break;
|
||||
case 0x2001: // PPU Control Register #2(W)
|
||||
MMU.PPUREG[1] = data;
|
||||
break;
|
||||
case 0x2003: // SPR-RAM Address Register(W)
|
||||
MMU.PPUREG[3] = data;
|
||||
break;
|
||||
case 0x2004: // SPR_RAM I/O Register(RW)
|
||||
MMU.SPRAM[MMU.PPUREG[3]++] = data;
|
||||
break;
|
||||
|
||||
case 0x2005: // PPU Scroll Register(W2)
|
||||
//DEBUGOUT( "SCR WRT L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() );
|
||||
if (MMU.PPU56Toggle == 0)
|
||||
{
|
||||
// First write
|
||||
// tile X t:0000000000011111=d:11111000
|
||||
MMU.loopy_t = (ushort)((MMU.loopy_t & 0xFFE0) | ((data) >> 3));
|
||||
// scroll offset X x=d:00000111
|
||||
MMU.loopy_x = (ushort)(data & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Second write
|
||||
// tile Y t:0000001111100000=d:11111000
|
||||
MMU.loopy_t = (ushort)((MMU.loopy_t & 0xFC1F) | (((data) & 0xF8) << 2));
|
||||
// scroll offset Y t:0111000000000000=d:00000111
|
||||
MMU.loopy_t = (ushort)((MMU.loopy_t & 0x8FFF) | (((data) & 0x07) << 12));
|
||||
}
|
||||
MMU.PPU56Toggle = (byte)(MMU.PPU56Toggle == 0 ? 1 : 0);
|
||||
break;
|
||||
case 0x2006: // VRAM Address Register(W2)
|
||||
if (MMU.PPU56Toggle == 0)
|
||||
{
|
||||
// First write
|
||||
// t:0011111100000000=d:00111111
|
||||
// t:1100000000000000=0
|
||||
MMU.loopy_t = (ushort)((MMU.loopy_t & 0x00FF) | (((data) & 0x3F) << 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Second write
|
||||
// t:0000000011111111=d:11111111
|
||||
MMU.loopy_t = (ushort)((MMU.loopy_t & 0xFF00) | data);
|
||||
// v=t
|
||||
MMU.loopy_v = MMU.loopy_t;
|
||||
nes.mapper.PPU_Latch(MMU.loopy_v);
|
||||
}
|
||||
MMU.PPU56Toggle = (byte)(MMU.PPU56Toggle == 0 ? 1 : 0);
|
||||
break;
|
||||
case 0x2007: // VRAM I/O Register(RW)
|
||||
ushort vaddr = (ushort)(MMU.loopy_v & 0x3FFF);
|
||||
if ((MMU.PPUREG[0] & PPU_INC32_BIT) != 0) MMU.loopy_v += 32;
|
||||
else MMU.loopy_v++;
|
||||
|
||||
if (vaddr >= 0x3000)
|
||||
{
|
||||
if (vaddr >= 0x3F00)
|
||||
{
|
||||
data &= 0x3F;
|
||||
if (bVSMode && nVSColorMap != -1)
|
||||
{
|
||||
byte temp = VSColorMap[nVSColorMap][data];
|
||||
if (temp != 0xFF)
|
||||
{
|
||||
data = (byte)(temp & 0x3F);
|
||||
}
|
||||
}
|
||||
|
||||
if ((vaddr & 0x000F) == 0)
|
||||
{
|
||||
MMU.BGPAL[0] = MMU.SPPAL[0] = data;
|
||||
}
|
||||
else if ((vaddr & 0x0010) == 0)
|
||||
{
|
||||
MMU.BGPAL[vaddr & 0x000F] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
MMU.SPPAL[vaddr & 0x000F] = data;
|
||||
}
|
||||
MMU.BGPAL[0x04] = MMU.BGPAL[0x08] = MMU.BGPAL[0x0C] = MMU.BGPAL[0x00];
|
||||
MMU.SPPAL[0x00] = MMU.SPPAL[0x04] = MMU.SPPAL[0x08] = MMU.SPPAL[0x0C] = MMU.BGPAL[0x00];
|
||||
return;
|
||||
}
|
||||
vaddr &= 0xEFFF;
|
||||
}
|
||||
if (MMU.PPU_MEM_TYPE[vaddr >> 10] != MMU.BANKTYPE_VROM)
|
||||
{
|
||||
MMU.PPU_MEM_BANK[vaddr >> 10].Span[vaddr & 0x03FF] = data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal void DMA(byte data)
|
||||
{
|
||||
ushort addr = (ushort)(data << 8);
|
||||
|
||||
for (ushort i = 0; i < 256; i++)
|
||||
{
|
||||
MMU.SPRAM[i] = nes.Read((ushort)(addr + i));
|
||||
}
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
bExtLatch = false;
|
||||
bChrLatch = false;
|
||||
bExtNameTable = false;
|
||||
bExtMono = false;
|
||||
|
||||
MMU.PPUREG[0] = MMU.PPUREG[1] = 0;
|
||||
|
||||
MMU.PPU56Toggle = 0;
|
||||
|
||||
MMU.PPU7_Temp = 0xFF; // VS Excitebike偱偍偐偟偔側傞($2006傪撉傒偵峴偔僶僌偑偁傞)
|
||||
// PPU7_Temp = 0;
|
||||
|
||||
MMU.loopy_v = MMU.loopy_t = 0;
|
||||
MMU.loopy_x = loopy_y = 0;
|
||||
loopy_shift = 0;
|
||||
|
||||
if (lpScreen != null)
|
||||
MemoryUtility.memset(lpScreen, 0x3F, (int)(Screen.SCREEN_WIDTH) * (int)(Screen.SCREEN_HEIGHT));
|
||||
if (lpColormode != null)
|
||||
MemoryUtility.memset(lpColormode, 0, (int)(Screen.SCREEN_HEIGHT));
|
||||
}
|
||||
|
||||
private enum Screen
|
||||
{
|
||||
SCREEN_WIDTH = 256 + 16,
|
||||
SCREEN_HEIGHT = 240
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,24 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class EXPAD
|
||||
{
|
||||
protected NES nes;
|
||||
|
||||
public EXPAD(NES parent)
|
||||
{
|
||||
nes = parent;
|
||||
}
|
||||
|
||||
public virtual void Dispose() { }
|
||||
|
||||
public virtual void Reset() { }
|
||||
public virtual void Strobe() { }
|
||||
public virtual byte Read4016() { return 0x00; }
|
||||
public virtual byte Read4017() { return 0x00; }
|
||||
public virtual void Write4016(byte data) { }
|
||||
public virtual void Write4017(byte data) { }
|
||||
public virtual void Sync() { }
|
||||
public virtual void SetSyncData(int type, int data) { }
|
||||
public virtual int GetSyncData(int type) { return 0x00; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_CrazyClimber : EXPAD
|
||||
{
|
||||
public EXPAD_CrazyClimber(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d7ac655210edb74ea198ad8802cb545
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_ExcitingBoxing : EXPAD
|
||||
{
|
||||
public EXPAD_ExcitingBoxing(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ac6f1c51a9c1b64e970bc5fe5e00b9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_FamlyTrainer : EXPAD
|
||||
{
|
||||
public EXPAD_FamlyTrainer(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a2cfbbbe2b90bd4ba754bc9a8f014af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_Gyromite : EXPAD
|
||||
{
|
||||
public EXPAD_Gyromite(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c49edbe29e34f0245b621a7920d4981e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_HyperShot : EXPAD
|
||||
{
|
||||
public EXPAD_HyperShot(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac43bf6e282394c46b8ea717595d8664
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_Keyboard : EXPAD
|
||||
{
|
||||
public EXPAD_Keyboard(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f56b9a9d01afad4b967b5d606f7ef11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_Mahjang : EXPAD
|
||||
{
|
||||
public EXPAD_Mahjang(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 146c88b5d0fab5b43ba7e570f0ff116b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_OekakidsTablet : EXPAD
|
||||
{
|
||||
public EXPAD_OekakidsTablet(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47337ac29c001d047a911c028e305b43
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class EXPAD_Paddle : EXPAD
|
||||
{
|
||||
public EXPAD_Paddle(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e48f82e9b202e64fa7ebe3816804ae9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_SpaceShadowGun : EXPAD
|
||||
{
|
||||
public EXPAD_SpaceShadowGun(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01ac1bc69454b414cb5a9db1d12f1aac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_Supor_Keyboard : EXPAD
|
||||
{
|
||||
public EXPAD_Supor_Keyboard(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5adc370589ec4bf42b7ea0c20acb2a48
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_Toprider : EXPAD
|
||||
{
|
||||
public EXPAD_Toprider(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0b1053511d6f224a840b0e9df4f9889
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_TurboFile : EXPAD
|
||||
{
|
||||
public EXPAD_TurboFile(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b6b41e81a80079489f60eaa5be220cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_VSUnisystem : EXPAD
|
||||
{
|
||||
public EXPAD_VSUnisystem(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d343a07cca05ea642be6db80607ea23a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
internal class EXPAD_VSZapper : EXPAD
|
||||
{
|
||||
public EXPAD_VSZapper(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6803ff5b7be6edc49ba2b71256aa16a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 光枪
|
||||
/// </summary>
|
||||
public class EXPAD_Zapper : EXPAD
|
||||
{
|
||||
public EXPAD_Zapper(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c378fd8c53bb8084f979b05d2405bb9c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,7 +1,5 @@
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using VirtualNes.Core.Debug;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
@ -21,13 +19,13 @@ namespace VirtualNes.Core
|
||||
protected byte[] lpTrainer;
|
||||
protected byte[] lpDiskBios;
|
||||
protected byte[] lpDisk;
|
||||
protected ulong crc;
|
||||
protected ulong crcall;
|
||||
protected ulong crcvrom;
|
||||
protected uint crc;
|
||||
protected uint crcall;
|
||||
protected uint crcvrom;
|
||||
protected int mapper;
|
||||
protected int diskno;
|
||||
protected ulong fdsmakerID;
|
||||
protected ulong fdsgameID;
|
||||
protected uint fdsmakerID;
|
||||
protected uint fdsgameID;
|
||||
|
||||
public ROM(string fname)
|
||||
{
|
||||
@ -255,7 +253,7 @@ namespace VirtualNes.Core
|
||||
crc = crcall = crcvrom = 0;
|
||||
|
||||
fdsmakerID = lpPRG[0x1F];
|
||||
fdsgameID = (ulong)((lpPRG[0x20] << 24) | (lpPRG[0x21] << 16) | (lpPRG[0x22] << 8) | (lpPRG[0x23] << 0));
|
||||
fdsgameID = (uint)((lpPRG[0x20] << 24) | (lpPRG[0x21] << 16) | (lpPRG[0x22] << 8) | (lpPRG[0x23] << 0));
|
||||
}
|
||||
}
|
||||
else //NSF
|
||||
@ -282,11 +280,34 @@ namespace VirtualNes.Core
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lpPRG = null;
|
||||
lpCHR = null;
|
||||
lpTrainer = null;
|
||||
lpDiskBios = null;
|
||||
lpDisk = null;
|
||||
}
|
||||
|
||||
public bool IsTRAINER()
|
||||
{
|
||||
return (header.control1 & (byte)EnumRomControlByte1.ROM_TRAINER) > 0;
|
||||
}
|
||||
|
||||
public bool IsNSF()
|
||||
{
|
||||
return bNSF;
|
||||
}
|
||||
public bool IsPAL()
|
||||
{
|
||||
return bPAL;
|
||||
}
|
||||
|
||||
public bool IsSAVERAM()
|
||||
{
|
||||
return (header.control1 & (byte)EnumRomControlByte1.ROM_SAVERAM) > 0;
|
||||
}
|
||||
|
||||
protected void FileNameCheck(string fname)
|
||||
{
|
||||
if (fname.Contains("(E)"))
|
||||
@ -295,6 +316,86 @@ namespace VirtualNes.Core
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetRomName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
internal int GetMapperNo()
|
||||
{
|
||||
return mapper;
|
||||
}
|
||||
|
||||
internal byte[] GetPROM()
|
||||
{
|
||||
return lpPRG;
|
||||
}
|
||||
|
||||
internal byte[] GetVROM()
|
||||
{
|
||||
return lpCHR;
|
||||
}
|
||||
|
||||
internal byte[] GetDISK()
|
||||
{
|
||||
return lpDisk;
|
||||
}
|
||||
|
||||
internal int GetDiskNo()
|
||||
{
|
||||
return diskno;
|
||||
}
|
||||
|
||||
internal ulong GetGameID()
|
||||
{
|
||||
return fdsgameID;
|
||||
}
|
||||
|
||||
internal ulong GetMakerID()
|
||||
{
|
||||
return fdsmakerID;
|
||||
}
|
||||
|
||||
internal bool IsVSUNISYSTEM()
|
||||
{
|
||||
return (header.control2 & (byte)EnumRomControlByte2.ROM_VSUNISYSTEM) != 0;
|
||||
}
|
||||
|
||||
internal uint GetPROM_CRC()
|
||||
{
|
||||
return crc;
|
||||
}
|
||||
|
||||
internal byte GetPROM_SIZE()
|
||||
{
|
||||
return header.PRG_PAGE_SIZE;
|
||||
}
|
||||
|
||||
internal byte GetVROM_SIZE()
|
||||
{
|
||||
return header.CHR_PAGE_SIZE;
|
||||
}
|
||||
|
||||
internal bool Is4SCREEN()
|
||||
{
|
||||
return (header.control1 & (byte)EnumRomControlByte1.ROM_4SCREEN) != 0;
|
||||
}
|
||||
|
||||
internal bool IsVMIRROR()
|
||||
{
|
||||
return (header.control1 & (byte)EnumRomControlByte1.ROM_VMIRROR) != 0;
|
||||
}
|
||||
|
||||
internal byte[] GetTRAINER()
|
||||
{
|
||||
return lpTrainer;
|
||||
}
|
||||
|
||||
internal NSFHEADER GetNsfHeader()
|
||||
{
|
||||
return nsfheader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd0714f580724604da063207fe69e274
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CfgController
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3328948cdc73baa4fb90c995f39f454f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,18 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CfgEmulator
|
||||
{
|
||||
public bool bIllegalOp { get; set; } = false;
|
||||
public bool bAutoFrameSkip { get; set; } = true;
|
||||
public bool bThrottle { get; set; } = true;
|
||||
public int nThrottleFPS { get; set; } = 120;
|
||||
public bool bBackground { get; set; } = false;
|
||||
public int nPriority { get; set; } = 3;
|
||||
public bool bFourPlayer { get; set; } = true;
|
||||
public bool bCrcCheck { get; set; } = true;
|
||||
public bool bDiskThrottle { get; set; } = true;
|
||||
public bool bLoadFullscreen { get; set; } = false;
|
||||
public bool bPNGsnapshot { get; set; } = false;
|
||||
public bool bAutoIPS { get; set; } = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aae682cf38878ae40a449ab1a13fc7fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CfgExtraSound
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c707e4c8c38fa2e40a884047d0582b7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,6 +1,7 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CHEATCODE
|
||||
public class CfgGeneral
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 797171ffbac5b8748923ba914141781d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CfgGraphics
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5d72263cee06c74e94d67af00befbdf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CfgLanguage
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c23af0b4dd8a4e04a89d1a380c54688f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CfgLauncher
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a033c816849b204c8cda167cd1fddb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class CfgMovie
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdbdc9850f494ad44a16ec1ec82157cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user