AxibugEmuOnline/Core/VirtualNes.Core/NES.cs

2011 lines
69 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using VirtualNes.Core.Debug;
namespace VirtualNes.Core
{
public class NES
{
public const int FETCH_CYCLES = 8;
public CPU cpu;
public PPU ppu;
public APU apu;
public ROM rom;
public PAD pad;
public Mapper mapper;
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_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;
private uint m_MovieControl;
private int m_MovieStepTotal;
private int m_MovieStep;
private bool m_bTapePlay;
private bool m_bTapeRec;
private Stream m_fpTape;
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 int ZapperX;
private int ZapperY;
private long base_cycles;
private long emul_cycles;
// For VS-Unisystem
byte m_VSDipValue;
VSDIPSWITCH[] m_VSDipTable;
private byte[] m_PadImg = new byte[226]
{
28, 8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F,
0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
private byte[] m_KeyImg0 = new byte[6]
{
2, 2,
0x2A, 0x2A,
0x2A, 0x2A,
};
private byte[] m_KeyImg1 = new byte[8]
{
3, 3,
0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A,
};
private byte[] m_KeyImg2 = new byte[18]
{
4, 4,
0xFF, 0x2A, 0x2A, 0xFF,
0x2A, 0x2A, 0x2A, 0x2A,
0x2A, 0x2A, 0x2A, 0x2A,
0xFF, 0x2A, 0x2A, 0xFF,
};
private byte[] Font6x8 = new byte[768]
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,
0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00,
0x20,0x78,0xA0,0x70,0x28,0xF0,0x20,0x00,0x48,0xB0,0x50,0x20,0x50,0x68,0x90,0x00,
0x40,0xA0,0xA8,0x68,0x90,0x90,0x68,0x00,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00,
0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x20,0x40,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x08,0x10,0x10,0x20,0x40,0x40,0x80,0x00,
0x70,0x88,0x98,0xA8,0xC8,0x88,0x70,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0xF8,0x00,
0x70,0x88,0x08,0x30,0x40,0x80,0xF8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,
0x30,0x50,0x90,0x90,0xF8,0x10,0x10,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0xF0,0x00,
0x70,0x88,0x80,0xF0,0x88,0x88,0x70,0x00,0xF8,0x08,0x10,0x10,0x20,0x20,0x20,0x00,
0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x78,0x08,0x88,0x70,0x00,
0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x40,0x00,
0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00,
0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,
0x30,0x48,0x88,0x98,0xA8,0xA8,0x78,0x00,0x20,0x50,0x50,0x88,0xF8,0x88,0x88,0x00,
0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,
0xF0,0x88,0x88,0x88,0x88,0x88,0xF0,0x00,0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00,
0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0xB8,0x88,0x88,0x70,0x00,
0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,
0x38,0x08,0x08,0x08,0x08,0x88,0x70,0x00,0x88,0x88,0x90,0xE0,0x90,0x88,0x88,0x00,
0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x88,0xD8,0xA8,0xA8,0xA8,0xA8,0xA8,0x00,
0x88,0xC8,0xA8,0xA8,0xA8,0x98,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00,
0xF0,0x88,0x88,0xF0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,
0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
0x88,0x88,0x88,0x50,0x50,0x50,0x20,0x00,0x88,0xA8,0xA8,0xA8,0xA8,0xD8,0x88,0x00,
0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,0x88,0x88,0x70,0x20,0x20,0x20,0x00,
0xF8,0x08,0x10,0x20,0x40,0x80,0xF8,0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00,
0x88,0x50,0xF8,0x20,0xF8,0x20,0x20,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00,
0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00,
0x80,0xC0,0xE0,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0xF8,0x00,
0x80,0x80,0x80,0xF0,0x88,0x88,0xF0,0x00,0x00,0x00,0x78,0x80,0x80,0x80,0x78,0x00,
0x08,0x08,0x08,0x78,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xF8,0x80,0x78,0x00,
0x18,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0xF0,0x00,
0x80,0x80,0x80,0xF0,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00,
0x20,0x00,0x20,0x20,0x20,0x20,0xC0,0x00,0x80,0x80,0x88,0x90,0xE0,0x90,0x88,0x00,
0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00,
0x00,0x00,0xF0,0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,
0x00,0x00,0xF0,0x88,0xF0,0x80,0x80,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0x08,0x00,
0x00,0x00,0xB8,0xC0,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xF0,0x00,
0x20,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x70,0x00,
0x00,0x00,0x88,0x88,0x50,0x50,0x20,0x00,0x00,0x00,0x88,0xA8,0xA8,0xD8,0x88,0x00,
0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x78,0x08,0xF0,0x00,
0x00,0x00,0xF8,0x08,0x70,0x80,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
public NES(string fname)
{
Debuger.Log("VirtuaNES - CSharpCore\n");
m_bDiskThrottle = false;
m_nSnapNo = 0;
m_bNsfPlaying = false;
m_bMoviePlay = m_bMovieRec = false;
m_fpMovie = null;
m_bTapePlay = m_bTapeRec = false;
m_fpTape = null;
m_TapeCycles = 0d;
m_TapeIn = m_TapeOut = 0;
m_bBarcode2 = false;
m_TurboFileBank = 0;
cpu = null;
ppu = null;
apu = null;
rom = null;
pad = null;
mapper = null;
SAVERAM_SIZE = 8 * 1024;
nIRQtype = 0;
bFrameIRQ = true;
bVideoMode = false;
nescfg = NesConfig.NESCONFIG_NTSC;
CheatInitial();
try
{
Debuger.Log("Allocating CPU...");
cpu = new CPU(this);
Debuger.Log("Allocating PPU...");
ppu = new PPU(this);
ppu.InitBuffer();
Debuger.Log("Allocating APU...");
apu = new APU(this);
Debuger.Log("Allocating PAD...");
pad = new PAD(this);
Debuger.Log("Loading ROM Image...");
rom = new ROM(fname);
mapper = Mapper.CreateMapper(this, rom.GetMapperNo());
Debuger.Log("OK");
Debuger.Log($"{rom.GetRomName()}");
Debuger.Log($"Mapper : #{rom.GetMapperNo():D3}");
Debuger.Log($"PROM-CRC : #{rom.GetPROM_CRC():X2}");
Debuger.Log($"VROM-CRC : #{rom.GetVROM_CRC():X2}");
Debuger.Log($"PRG SIZE : {16 * rom.GetPROM_SIZE():0000}K");
Debuger.Log($"CHR SIZE : {8 * rom.GetVROM_SIZE():0000}K");
Debuger.Log($"V MIRROR :{rom.IsVMIRROR()}");
Debuger.Log($"4 SCREEN :{rom.Is4SCREEN()}");
Debuger.Log($"SAVE RAM :{rom.IsSAVERAM()}");
Debuger.Log($"TRAINER :{rom.IsTRAINER()}");
Debuger.Log($"VS-Unisystem :{rom.IsVSUNISYSTEM()}");
NesSub_MemoryInitial();
LoadSRAM();
LoadDISK();
{
// Pad<61><64><EFBFBD><EFBFBD>ڤ<EFBFBD><DAA4>ȳ<EFBFBD><C8B3>ڻ<EFBFBD><DABB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߥ󥰤<DFA5><F3A5B0A4>W<EFBFBD><57><EFBFBD>ΤǤ<CEA4><C7A4><EFBFBD><EFBFBD><EFBFBD>
uint crc = rom.GetPROM_CRC();
if (
crc == 0xe792de94 // Best Play - Pro Yakyuu (New) (J)
|| crc == 0xf79d684a // Best Play - Pro Yakyuu (Old) (J)
|| crc == 0xc2ef3422 // Best Play - Pro Yakyuu 2 (J)
|| crc == 0x974e8840 // Best Play - Pro Yakyuu '90 (J)
|| crc == 0xb8747abf // Best Play - Pro Yakyuu Special (J)
|| crc == 0x9fa1c11f // Castle Excellent (J)
|| crc == 0x0b0d4d1b // Derby Stallion - Zenkoku Ban (J)
|| crc == 0x728c3d98 // Downtown - Nekketsu Monogatari (J)
|| crc == 0xd68a6f33 // Dungeon Kid (J)
|| crc == 0x3a51eb04 // Fleet Commander (J)
|| crc == 0x7c46998b // Haja no Fuuin (J)
|| crc == 0x7e5d2f1a // Itadaki Street - Watashi no Mise ni Yottette (J)
|| crc == 0xcee5857b // Ninjara Hoi! (J)
|| crc == 0x50ec5e8b // Wizardry - Legacy of Llylgamyn (J)
|| crc == 0x343e9146 // Wizardry - Proving Grounds of the Mad Overlord (J)
|| crc == 0x33d07e45)
{ // Wizardry - The Knight of Diamonds (J)
pad.SetExController(EXCONTROLLER.EXCONTROLLER_TURBOFILE);
}
}
LoadTurboFile();
// VS-Unisystem<65>Υǥե<C7A5><D5A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F>
if (rom.IsVSUNISYSTEM())
{
uint crc = rom.GetPROM_CRC();
m_VSDipValue = 0;
m_VSDipTable = VsUnisystem.vsdip_default;
}
Reset();
// <20><><EFBFBD>`<60><><EFBFBD><EFBFBD><EFBFBD>ФΥǥե<C7A5><D5A5><EFBFBD><EFBFBD>ȥ<EFBFBD><C8A5>ץ<EFBFBD><D7A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F>(<28>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>r<EFBFBD><72>ʹ<EFBFBD><CAB9><EFBFBD><EFBFBD>)
GameOption.defRenderMethod = (int)GetRenderMethod();
GameOption.defIRQtype = GetIrqType();
GameOption.defFrameIRQ = GetFrameIRQmode();
GameOption.defVideoMode = GetVideoMode();
// <20>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>`<60>ɤ<EFBFBD><C9A4><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD>ȥ꤬<C8A5>o<EFBFBD><6F><EFBFBD><EFBFBD><EFBFBD>Хǥե<C7A5><D5A5><EFBFBD><EFBFBD>Ȥ<EFBFBD><C8A4><EFBFBD><EFBFBD><EFBFBD>)
if (rom.GetMapperNo() != 20)
{
GameOption.Load(rom.GetPROM_CRC());
}
else
{
GameOption.Load(rom.GetGameID(), rom.GetMakerID());
}
SetRenderMethod((EnumRenderMethod)GameOption.nRenderMethod);
SetIrqType((IRQMETHOD)GameOption.nIRQtype);
SetFrameIRQmode(GameOption.bFrameIRQ);
SetVideoMode(GameOption.bVideoMode);
}
catch (Exception ex)
{
Debuger.LogError(ex.ToString());
throw ex;
}
}
internal int GetIrqType()
{
return nIRQtype;
}
private void LoadTurboFile()
{
MemoryUtility.ZEROMEMORY(MMU.ERAM, MMU.ERAM.Length);
if (pad.GetExController() != (int)EXCONTROLLER.EXCONTROLLER_TURBOFILE)
return;
var fp = Supporter.OpenFile(Supporter.Config.path.szSavePath, "TurboFile.vtf");
try
{
if (fp == null)
{
// xxx <20>ե<EFBFBD><D5A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD>ޤ<EFBFBD><DEA4><EFBFBD>
throw new Exception($"Can Not Open File [TurboFile.vtf]");
}
long size = fp.Length;
// <20>ե<EFBFBD><D5A5><EFBFBD><EFBFBD><EFBFBD><EBA5B5><EFBFBD><EFBFBD>ȡ<EFBFBD><C8A1>
if (size > 32 * 1024)
{
size = 32 * 1024;
}
fp.Read(MMU.ERAM, 0, MMU.ERAM.Length);
fp.Close();
}
catch (Exception ex)
{
fp?.Close();
Debuger.LogError($"Loading TurboFile Error.\n{ex}");
}
}
private void LoadDISK()
{
//todo : <20>ŵ<EFBFBD><C5B5><EFBFBD><EFBFBD><EFBFBD>ȡ֧<C8A1><D6A7>
}
private void LoadSRAM()
{
if (rom.IsNSF())
return;
MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length);
if (!rom.IsSAVERAM())
return;
var saveFileDir = Supporter.Config.path.szSavePath;
var saveFileName = $"{rom.GetRomName()}.sav";
var fp = Supporter.OpenFile(saveFileDir, saveFileName);
try
{
if (fp == null)
{
throw new Exception("not find ram file to read");
}
Debuger.Log("Loading SAVERAM...");
int size = (int)fp.Length;
if (size <= 128 * 1024)
{
fp.Read(MMU.WRAM, 0, size);
}
Debuger.Log("OK.");
fp.Close();
}
catch (Exception ex)
{
fp?.Close();
fp = null;
}
}
private void NesSub_MemoryInitial()
{
int i;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>N<EFBFBD><4E><EFBFBD>A
MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.DRAM, MMU.DRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.ERAM, MMU.ERAM.Length);
MemoryUtility.ZEROMEMORY(MMU.XRAM, MMU.XRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length);
MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length);
MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length);
MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length);
MMU.FrameIRQ = 0xC0;
MMU.PROM = MMU.VROM = null;
// 0 <20><><EFBFBD>Z<EFBFBD>h<EFBFBD>~<7E>΍<EFBFBD>
MMU.PROM_8K_SIZE = MMU.PROM_16K_SIZE = MMU.PROM_32K_SIZE = 1;
MMU.VROM_1K_SIZE = MMU.VROM_2K_SIZE = MMU.VROM_4K_SIZE = MMU.VROM_8K_SIZE = 1;
// <20>f<EFBFBD>t<EFBFBD>H<EFBFBD><48><EFBFBD>g<EFBFBD>o<EFBFBD><6F><EFBFBD>N<EFBFBD>ݒ<EFBFBD>
for (i = 0; i < 8; i++)
{
MMU.CPU_MEM_BANK[i] = null;
MMU.CPU_MEM_TYPE[i] = MMU.BANKTYPE_ROM;
MMU.CPU_MEM_PAGE[i] = 0;
}
// <20><><EFBFBD><EFBFBD>RAM/WRAM
MMU.SetPROM_Bank(0, MMU.RAM, MMU.BANKTYPE_RAM);
MMU.SetPROM_Bank(3, MMU.WRAM, MMU.BANKTYPE_RAM);
// <20>_<EFBFBD>~<7E>[
MMU.SetPROM_Bank(1, MMU.XRAM, MMU.BANKTYPE_ROM);
MMU.SetPROM_Bank(2, MMU.XRAM, MMU.BANKTYPE_ROM);
for (i = 0; i < 8; i++)
{
MMU.CRAM_USED[i] = 0;
}
}
public void CheatInitial()
{
m_CheatCode.Clear();
}
public uint FrameCount { get; private set; }
public void EmulateFrame(bool bDraw)
{
int scanline = 0;
if (rom.IsNSF())
{
EmulateNSF();
return;
}
CheatCodeProcess();
NES_scanline = scanline;
bool NotTile = RenderMethod != EnumRenderMethod.TILE_RENDER;
if (RenderMethod != EnumRenderMethod.TILE_RENDER)
{
bZapper = false;
while (true)
{
ppu.SetRenderScanline(scanline);
if (scanline == 0)
{
if (RenderMethod < EnumRenderMethod.POST_RENDER)
{
EmulationCPU(nescfg.ScanlineCycles);
ppu.FrameStart();
ppu.ScanlineNext();
mapper.HSync(scanline);
ppu.ScanlineStart();
}
else
{
EmulationCPU(nescfg.HDrawCycles);
ppu.FrameStart();
ppu.ScanlineNext();
mapper.HSync(scanline);
EmulationCPU(FETCH_CYCLES * 32);
ppu.ScanlineStart();
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
}
}
else if (scanline < 240)
{
if (RenderMethod < EnumRenderMethod.POST_RENDER)
{
if (RenderMethod == EnumRenderMethod.POST_ALL_RENDER)
EmulationCPU(nescfg.ScanlineCycles);
if (bDraw)
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
}
else
{
if (pad.IsZapperMode() && scanline == ZapperY)
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
}
else
{
if (!ppu.IsSprite0(scanline))
{
ppu.DummyScanline(scanline);
}
else
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
}
}
}
ppu.ScanlineNext(); // <20><><EFBFBD><EFBFBD><EFBFBD>̈ʒu<CA92>Ń<EFBFBD><C583>X<EFBFBD>^<5E>[<5B>n<EFBFBD>͉<EFBFBD><CD89>ʂ<EFBFBD><CA82>
if (RenderMethod == EnumRenderMethod.PRE_ALL_RENDER)
EmulationCPU(nescfg.ScanlineCycles);
mapper.HSync(scanline);
ppu.ScanlineStart();
}
else
{
if (RenderMethod == EnumRenderMethod.POST_RENDER)
EmulationCPU(nescfg.HDrawCycles);
if (bDraw)
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
}
else
{
if (pad.IsZapperMode() && scanline == ZapperY)
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
}
else
{
if (!ppu.IsSprite0(scanline))
{
ppu.DummyScanline(scanline);
}
else
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
}
}
}
if (RenderMethod == EnumRenderMethod.PRE_RENDER)
EmulationCPU(nescfg.HDrawCycles);
ppu.ScanlineNext();
mapper.HSync(scanline);
EmulationCPU(FETCH_CYCLES * 32);
ppu.ScanlineStart();
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
}
}
else if (scanline == 240)
{
mapper.VSync();
if (RenderMethod < EnumRenderMethod.POST_RENDER)
{
EmulationCPU(nescfg.ScanlineCycles);
mapper.HSync(scanline);
}
else
{
EmulationCPU(nescfg.HDrawCycles);
mapper.HSync(scanline);
EmulationCPU(nescfg.HBlankCycles);
}
}
else if (scanline <= nescfg.TotalScanlines - 1)
{
pad.VSync();
// VBLANK<4E><4B><EFBFBD><EFBFBD>
if (scanline == nescfg.TotalScanlines - 1)
{
ppu.VBlankEnd();
}
if (RenderMethod < EnumRenderMethod.POST_RENDER)
{
if (scanline == 241)
{
ppu.VBlankStart();
if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0)
{
cpu.NMI();
}
}
EmulationCPU(nescfg.ScanlineCycles);
mapper.HSync(scanline);
}
else
{
if (scanline == 241)
{
ppu.VBlankStart();
if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0)
{
cpu.NMI();
}
}
EmulationCPU(nescfg.HDrawCycles);
mapper.HSync(scanline);
EmulationCPU(nescfg.HBlankCycles);
}
if (scanline == nescfg.TotalScanlines - 1)
{
break;
}
}
if (pad.IsZapperMode())
{
if (scanline == ZapperY)
bZapper = true;
else
bZapper = false;
}
scanline++;
NES_scanline = scanline;
}
}
else
{
bZapper = false;
while (true)
{
ppu.SetRenderScanline(scanline);
if (scanline == 0)
{
// <20>_<EFBFBD>~<7E>[<5B>X<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD><43>
// H-Draw (4fetches*32)
EmulationCPU(FETCH_CYCLES * 128);
ppu.FrameStart();
ppu.ScanlineNext();
EmulationCPU(FETCH_CYCLES * 10);
mapper.HSync(scanline);
EmulationCPU(FETCH_CYCLES * 22);
ppu.ScanlineStart();
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
}
else if (scanline < 240)
{
// <20>X<EFBFBD>N<EFBFBD><4E><EFBFBD>[<5B><><EFBFBD>`<60><>(Scanline 1<>`239)
if (bDraw)
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
ppu.ScanlineNext();
EmulationCPU(FETCH_CYCLES * 10);
mapper.HSync(scanline);
EmulationCPU(FETCH_CYCLES * 22);
ppu.ScanlineStart();
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
}
else
{
if (pad.IsZapperMode() && scanline == ZapperY)
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
ppu.ScanlineNext();
EmulationCPU(FETCH_CYCLES * 10);
mapper.HSync(scanline);
EmulationCPU(FETCH_CYCLES * 22);
ppu.ScanlineStart();
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
}
else
{
if (!ppu.IsSprite0(scanline))
{
// H-Draw (4fetches*32)
EmulationCPU(FETCH_CYCLES * 128);
ppu.DummyScanline(scanline);
ppu.ScanlineNext();
EmulationCPU(FETCH_CYCLES * 10);
mapper.HSync(scanline);
EmulationCPU(FETCH_CYCLES * 22);
ppu.ScanlineStart();
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
}
else
{
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
ppu.ScanlineNext();
EmulationCPU(FETCH_CYCLES * 10);
mapper.HSync(scanline);
EmulationCPU(FETCH_CYCLES * 22);
ppu.ScanlineStart();
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
}
}
}
}
else if (scanline == 240)
{
// <20>_<EFBFBD>~<7E>[<5B>X<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD><43> (Scanline 240)
mapper.VSync();
EmulationCPU(nescfg.HDrawCycles);
// H-Sync
mapper.HSync(scanline);
EmulationCPU(nescfg.HBlankCycles);
}
else if (scanline <= nescfg.TotalScanlines - 1)
{
pad.VSync();
// VBLANK<4E><4B><EFBFBD><EFBFBD>
if (scanline == nescfg.TotalScanlines - 1)
{
ppu.VBlankEnd();
}
if (scanline == 241)
{
ppu.VBlankStart();
if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0)
{
cpu.NMI();
}
}
EmulationCPU(nescfg.HDrawCycles);
// H-Sync
mapper.HSync(scanline);
EmulationCPU(nescfg.HBlankCycles);
if (scanline == nescfg.TotalScanlines - 1)
{
break;
}
}
if (pad.IsZapperMode())
{
if (scanline == ZapperY)
bZapper = true;
else
bZapper = false;
}
scanline++;
NES_scanline = scanline;
}
}
FrameCount++;
}
internal void DrawString(int x, int y, string str, byte col)
{
foreach (var @char in str)
{
DrawFont(x, y, (byte)@char, col);
x += 6;
}
}
internal unsafe void DrawFont(int x, int y, byte chr, byte col)
{
int i;
int pFnt;
int pPtr;
var Scn = ppu.GetScreenPtr();
int pScn = 8;
if (chr < 0x20 || chr > 0x7F)
return;
chr -= 0x20;
pFnt = chr * 8;
pPtr = pScn + (256 + 16) * y + x;
for (i = 0; i < 8; i++)
{
if ((Font6x8[pFnt + i] & 0x80) != 0) Scn[pPtr + 0] = col;
if ((Font6x8[pFnt + i] & 0x40) != 0) Scn[pPtr + 1] = col;
if ((Font6x8[pFnt + i] & 0x20) != 0) Scn[pPtr + 2] = col;
if ((Font6x8[pFnt + i] & 0x10) != 0) Scn[pPtr + 3] = col;
if ((Font6x8[pFnt + i] & 0x08) != 0) Scn[pPtr + 4] = col;
if ((Font6x8[pFnt + i] & 0x04) != 0) Scn[pPtr + 5] = col;
pPtr += (256 + 16);
}
}
int CPU_CALL_COUNT = 0;
internal void EmulationCPU(int basecycles)
{
int cycles;
base_cycles += basecycles;
cycles = (int)((base_cycles / 12) - emul_cycles);
if (cycles > 0)
{
var cycleAdd = cpu.EXEC(cycles);
emul_cycles += cycleAdd;
}
CPU_CALL_COUNT++;
}
public void Reset()
{
SaveSRAM();
SaveDISK();
SaveTurboFile();
// RAM Clear
MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
if (rom.GetPROM_CRC() == 0x29401686)
{ // Minna no Taabou no Nakayoshi Dai Sakusen(J)
MemoryUtility.memset(MMU.RAM, 0xFF, MMU.RAM.Length);
}
// RAM set
if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20)
{
MemoryUtility.memset(MMU.WRAM, 0xFF, MMU.WRAM.Length);
}
MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length);
MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length);
MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length);
MemoryUtility.ZEROMEMORY(MMU.PPUREG, 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();
// <20>f<EFBFBD>t<EFBFBD>H<EFBFBD><48><EFBFBD>g<EFBFBD>o<EFBFBD><6F><EFBFBD>N
if (MMU.VROM_8K_SIZE != 0)
{
MMU.SetVROM_8K_Bank(0);
}
else
{
MMU.SetCRAM_8K_Bank(0);
}
// <20>~<7E><><EFBFBD>[
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 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, 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;
// <20><><EFBFBD>S<EFBFBD>΍<EFBFBD><CE8D><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˂Ă<CB82><C482><EFBFBD><EFBFBD>ă<EFBFBD><C483>[<5B>v<EFBFBD><76>(1<>b<EFBFBD><62>)
for (int i = 0; i < nescfg.TotalScanlines * 60; i++)
{
EmulationCPU(nescfg.ScanlineCycles);
cpu.GetContext(ref reg);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[<5B>v<EFBFBD>ɓ<EFBFBD><C993><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƃ<EFBFBD><C682>m<EFBFBD>F<EFBFBD><46><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E794B2><EFBFBD><EFBFBD>
if (reg.PC == 0x4700)
{
break;
}
}
m_bNsfInit = false;
}
cpu.GetContext(ref reg);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[<5B>v<EFBFBD>ɓ<EFBFBD><C993><EFBFBD><EFBFBD>Ă<EFBFBD><C482><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Đݒ肷<DD92><E882B7>
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; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[<5B>v
reg.S = 0xFF;
EmulationCPU(nescfg.ScanlineCycles * nescfg.TotalScanlines);
}
}
internal void CheatCodeProcess()
{
foreach (var it in m_CheatCode)
{
if ((it.enable & CHEATCODE.CHEAT_ENABLE) == 0)
continue;
switch (it.type)
{
case CHEATCODE.CHEAT_TYPE_ALWAYS:
CheatWrite(it.length, it.address, it.data);
break;
case CHEATCODE.CHEAT_TYPE_ONCE:
CheatWrite(it.length, it.address, it.data);
it.enable = 0;
break;
case CHEATCODE.CHEAT_TYPE_GREATER:
if (CheatRead(it.length, it.address) > it.data)
{
CheatWrite(it.length, it.address, it.data);
}
break;
case CHEATCODE.CHEAT_TYPE_LESS:
if (CheatRead(it.length, it.address) < it.data)
{
CheatWrite(it.length, it.address, it.data);
}
break;
}
}
}
private uint CheatRead(byte length, ushort addr)
{
uint data = 0;
for (int i = 0; i <= length; i++)
{
data |= (uint)(Read((ushort)(addr + i)) * (1 << (i * 8)));
}
return data;
}
private void CheatWrite(int length, ushort addr, uint data)
{
for (int i = 0; i <= length; i++)
{
Write((ushort)(addr + i), (byte)(data & 0xFF));
data >>= 8;
}
}
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()
{
int i;
if (pad.GetExController() != (int)EXCONTROLLER.EXCONTROLLER_TURBOFILE)
return;
for (i = 0; i < MMU.ERAM.Length; i++)
{
if (MMU.ERAM[i] != 0x00)
break;
}
if (i < MMU.ERAM.Length)
{
Debuger.Log("Saving TURBOFILE...");
Supporter.SaveFile(MMU.ERAM, Supporter.Config.path.szSavePath, "TurboFile.vtf");
}
}
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;
// <20><><EFBFBD>~<7E>H
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)
{
if (!(IsTapePlay() || IsTapeRec()))
{
return;
}
if ((m_TapeCycles -= cycles) > 0)
return;
m_TapeCycles += (nescfg.CpuClock / 32000.0);
// m_TapeCycles += (nescfg.CpuClock / 22050.0); // <20>x<EFBFBD><78><EFBFBD><EFBFBD><EFBFBD>ă_<C483><5F><EFBFBD><EFBFBD><EFBFBD>ۂ<EFBFBD>
if (m_bTapePlay)
{
int data = m_fpTape.ReadByte();
if (data != -1) //EOF
{
if ((data & 0xFF) >= 0x8C)
{
m_TapeOut = 0x02;
}
else
if ((data & 0xFF) <= 0x74)
{
m_TapeOut = 0x00;
}
}
else
{
TapeStop();
}
}
if (m_bTapeRec)
{
m_fpTape.WriteByte((m_TapeIn & 7) == 7 ? (byte)0x90 : (byte)0x70);
}
}
private void TapeStop()
{
if (!m_bBarcode)
{
cpu.SetClockProcess(false);
}
m_bTapePlay = m_bTapeRec = false;
m_fpTape?.Dispose();
m_fpTape = null;
}
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][addr & 0x1FFF];
}
return 0x00; // Warning<6E>\<5C>h
}
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;
}
public void SetRenderMethod(EnumRenderMethod type)
{
RenderMethod = type;
}
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][addr & 0x1FFF] == m_GenieCode[i].cmp)
{
MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = m_GenieCode[i].data;
}
}
else
{
// 6character codes
addr |= 0x8000;
MMU.CPU_MEM_BANK[addr >> 13][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<45>ŗL<C597>|<7C>[<5B>g
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;
}
internal EnumRenderMethod GetRenderMethod()
{
return RenderMethod;
}
internal void SetIrqType(IRQMETHOD nType)
{
nIRQtype = (int)nType;
}
internal int GetScanline()
{
return NES_scanline;
}
internal void SetSAVERAM_SIZE(int size)
{
SAVERAM_SIZE = size;
}
internal byte GetBarcodeStatus()
{
return m_BarcodeOut;
}
public State GetState()
{
State state = new State();
//HEADER
{
state.HEADER.ID = "VirtuaNES ST";
state.HEADER.BlockVersion = 0x0200;
if (rom.GetMapperNo() != 20)
state.HEADER.Ext0 = rom.GetPROM_CRC();
else
{
state.HEADER.Ext0 = rom.GetGameID();
state.HEADER.Ext1 = (ushort)rom.GetMakerID();
state.HEADER.Ext2 = (ushort)rom.GetDiskNo();
}
}
//REGISTER STATE
{
state.regBLOCK.ID = "REG DATA";
state.regBLOCK.BlockVersion = 0x0210;
state.regBLOCK.BlockSize = state.reg.GetSize();
R6502 R = null;
cpu.GetContext(ref R);
state.reg.cpureg.PC = R.PC;
state.reg.cpureg.A = R.A;
state.reg.cpureg.X = R.X;
state.reg.cpureg.Y = R.Y;
state.reg.cpureg.S = R.S;
state.reg.cpureg.P = R.P;
state.reg.cpureg.I = R.INT_pending;
int cycles = 0;
apu.GetFrameIRQ(ref cycles,
ref state.reg.cpureg.FrameIRQ_count,
ref state.reg.cpureg.FrameIRQ_type,
ref state.reg.cpureg.FrameIRQ,
ref state.reg.cpureg.FrameIRQ_occur);
state.reg.cpureg.FrameIRQ_cycles = cycles; // <20><><EFBFBD>դ<EFBFBD>INT<4E>ʞ飨<CA9E><E9A3A8>
state.reg.cpureg.DMA_cycles = cpu.GetDmaCycles();
state.reg.cpureg.emul_cycles = emul_cycles;
state.reg.cpureg.base_cycles = base_cycles;
// SAVE PPU STATE
state.reg.ppureg.reg0 = MMU.PPUREG[0];
state.reg.ppureg.reg1 = MMU.PPUREG[1];
state.reg.ppureg.reg2 = MMU.PPUREG[2];
state.reg.ppureg.reg3 = MMU.PPUREG[3];
state.reg.ppureg.reg7 = MMU.PPU7_Temp;
state.reg.ppureg.loopy_t = MMU.loopy_t;
state.reg.ppureg.loopy_v = MMU.loopy_v;
state.reg.ppureg.loopy_x = MMU.loopy_x;
state.reg.ppureg.toggle56 = MMU.PPU56Toggle;
}
//RAM STATE
{
state.ram = RAMSTAT.GetDefault();
uint size = 0;
// SAVE RAM STATE
MemoryUtility.memcpy(state.ram.RAM, MMU.RAM, state.ram.RAM.Length);
MemoryUtility.memcpy(state.ram.BGPAL, MMU.BGPAL, state.ram.BGPAL.Length);
MemoryUtility.memcpy(state.ram.SPPAL, MMU.SPPAL, state.ram.SPPAL.Length);
MemoryUtility.memcpy(state.ram.SPRAM, MMU.SPRAM, state.ram.SPRAM.Length);
// S-RAM STATE(ʹ<><CAB9>ʹ<CEB4>ä<EFBFBD><C3A4>v<EFBFBD><76><EFBFBD><EFBFBD><E9A4BA><EFBFBD>ڤ<EFBFBD><DAA4><EFBFBD><EFBFBD>Х<EFBFBD><D0A5>`<60>֤<EFBFBD><D6A4><EFBFBD>)
if (rom.IsSAVERAM())
{
size = (uint)SAVERAM_SIZE;
}
// Create Header
state.ramBLOCK.ID = "RAM DATA";
state.ramBLOCK.BlockVersion = 0x0100;
state.ramBLOCK.BlockSize = size + state.ram.GetSize();
if (rom.IsSAVERAM())
{
state.WRAM = new byte[SAVERAM_SIZE];
Array.Copy(MMU.WRAM, state.WRAM, SAVERAM_SIZE);
}
}
//BANK STATE
{
state.mmu = MMUSTAT.GetDefault();
uint size = 0;
// SAVE CPU MEMORY BANK DATA
// BANK0,1,2<>ϥХ󥯥<D0A5><F3A5AFA5>`<60>֤<EFBFBD><D6A4>v<EFBFBD>S<EFBFBD>ʤ<EFBFBD>
// VirtuaNES0.30<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20>Х󥯣<D0A5><F3A5AFA3><EFBFBD>SRAMʹ<4D>ä<EFBFBD><C3A4>v<EFBFBD><76><EFBFBD><EFBFBD><E9A4BA><EFBFBD>`<60><>
for (int i = 3; i < 8; i++)
{
state.mmu.CPU_MEM_TYPE[i] = MMU.CPU_MEM_TYPE[i];
state.mmu.CPU_MEM_PAGE[i] = (ushort)MMU.CPU_MEM_PAGE[i];
if (MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_RAM
|| MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_DRAM)
{
size += 8 * 1024; // 8K BANK
}
}
// SAVE VRAM MEMORY DATA
for (int i = 0; i < 12; i++)
{
state.mmu.PPU_MEM_TYPE[i] = MMU.PPU_MEM_TYPE[i];
state.mmu.PPU_MEM_PAGE[i] = (ushort)MMU.PPU_MEM_PAGE[i];
}
size += 4 * 1024; // 1K BANK x 4 (VRAM)
for (int i = 0; i < 8; i++)
{
state.mmu.CRAM_USED[i] = MMU.CRAM_USED[i];
if (MMU.CRAM_USED[i] != 0)
{
size += 4 * 1024; // 4K BANK
}
}
// Create Header
state.mmuBLOCK.ID = "MMU DATA";
state.mmuBLOCK.BlockVersion = 0x0200;
state.mmuBLOCK.BlockSize = size + state.mmu.GetSize();
state.CPU_MEM_BANK = new List<byte>();
// WRITE CPU RAM MEMORY BANK
for (int i = 3; i < 8; i++)
{
if (state.mmu.CPU_MEM_TYPE[i] != MMU.BANKTYPE_ROM)
{
state.CPU_MEM_BANK.AddRange(MMU.CPU_MEM_BANK[i].Span(0, 8 * 1024).ToArray());
}
}
// WRITE VRAM MEMORY(<28><><EFBFBD><EFBFBD>4K<34>֤<EFBFBD><D6A4>٤ƕ<D9A4><C695><EFBFBD><EFBFBD>z<EFBFBD><7A>)
state.VRAM = new byte[4 * 1024];
Array.Copy(MMU.VRAM, state.VRAM, state.VRAM.Length);
state.CRAM = new List<byte>();
// WRITE CRAM MEMORY
for (int i = 0; i < 8; i++)
{
if (MMU.CRAM_USED[i] != 0)
{
var bytes = new byte[4 * 1024];
Array.Copy(MMU.CRAM, 0x1000 * i, bytes, 0, bytes.Length);
state.CRAM.AddRange(bytes);
}
}
}
// MMC STATE
{
state.mmc = MMCSTAT.GetDefault();
// Create Header
state.mmcBLOCK.ID = "MMC DATA";
state.mmcBLOCK.BlockVersion = 0x0100;
state.mmcBLOCK.BlockSize = state.mmc.GetSize();
if (mapper.IsStateSave())
{
mapper.SaveState(state.mmc.mmcdata);
}
}
//CONTROLLER STATE
{
// Create Header
state.ctrBLOCK.ID = "CTR DATA";
state.ctrBLOCK.BlockVersion = 0x0100;
state.ctrBLOCK.BlockSize = state.ctr.GetSize();
state.ctr.pad1bit = 0;
state.ctr.pad2bit = 0;
state.ctr.pad3bit = 0;
state.ctr.pad4bit = 0;
state.ctr.strobe = 0;
}
//SND STATE
{
state.snd = SNDSTAT.GetDefault();
// Create Header
state.sndBLOCK.ID = "SND DATA";
state.sndBLOCK.BlockVersion = 0x0100;
state.sndBLOCK.BlockSize = state.snd.GetSize();
StateBuffer buffer = new StateBuffer();
apu.SaveState(buffer);
Array.Copy(buffer.Data.ToArray(), state.snd.snddata, buffer.Data.Count);
}
// DISKIMAGE STATE
if (rom.GetMapperNo() == 20)
{
var lpDisk = rom.GetPROM();
var lpWrite = rom.GetDISK();
int DiskSize = 16 + 65500 * rom.GetDiskNo();
// <20><><EFBFBD>`<60><><EFBFBD>򥫥<EFBFBD><F2A5ABA5><EFBFBD><EFBFBD><EFBFBD>
for (int i = 16; i < DiskSize; i++)
{
if (lpWrite[i] != 0)
state.dsk.DifferentSize++;
}
state.dskBLOCK.ID = "DISKDATA";
state.dskBLOCK.BlockVersion = 0x0210;
state.dskBLOCK.BlockSize = 0;
state.dskdata = new List<uint>();
for (int i = 16; i < DiskSize; i++)
{
if (lpWrite[i] != 0)
{
uint data = (uint)(i & 0x00FFFFFF);
data |= ((uint)lpDisk[i] & 0xFF) << 24;
state.dskdata.Add(data);
}
}
}
// EXCTR STATE
if (pad.GetExController() != 0)
{
state.exctrBLOCK.ID = "EXCTRDAT";
state.exctrBLOCK.BlockVersion = 0x0100;
state.exctrBLOCK.BlockSize = state.exctr.GetSize();
// Some excontrollers will default 0
state.exctr.data = pad.GetSyncExData();
}
return state;
}
public void LoadState(State state)
{
FrameCount = 0;
//HEADER
{
state.HEADER.ID = "VirtuaNES ST";
state.HEADER.BlockVersion = 0x0200;
if (rom.GetMapperNo() != 20)
rom.SetPROM_CRC(state.HEADER.Ext0);
else
{
rom.SetGameID(state.HEADER.Ext0);
rom.SetMakerID(state.HEADER.Ext1);
rom.SetDiskNo(state.HEADER.Ext2);
}
}
//REGISTER STATE
{
R6502 R = new R6502();
R.PC = state.reg.cpureg.PC;
R.A = state.reg.cpureg.A;
R.X = state.reg.cpureg.X;
R.Y = state.reg.cpureg.Y;
R.S = state.reg.cpureg.S;
R.P = state.reg.cpureg.P;
R.INT_pending = state.reg.cpureg.I;
cpu.SetContext(R);
apu.SetFrameIRQ(
state.reg.cpureg.FrameIRQ_cycles,
state.reg.cpureg.FrameIRQ_count,
state.reg.cpureg.FrameIRQ_type,
state.reg.cpureg.FrameIRQ,
state.reg.cpureg.FrameIRQ_occur
);
cpu.SetDmaCycles(state.reg.cpureg.DMA_cycles);
emul_cycles = state.reg.cpureg.emul_cycles;
base_cycles = state.reg.cpureg.base_cycles;
// LOAD PPU STATE
MMU.PPUREG[0] = state.reg.ppureg.reg0;
MMU.PPUREG[1] = state.reg.ppureg.reg1;
MMU.PPUREG[2] = state.reg.ppureg.reg2;
MMU.PPUREG[3] = state.reg.ppureg.reg3;
MMU.PPU7_Temp = state.reg.ppureg.reg7;
MMU.loopy_t = state.reg.ppureg.loopy_t;
MMU.loopy_v = state.reg.ppureg.loopy_v;
MMU.loopy_x = state.reg.ppureg.loopy_x;
MMU.PPU56Toggle = state.reg.ppureg.toggle56;
}
//RAM STATE
{
// SAVE RAM STATE
MemoryUtility.memcpy(MMU.RAM, state.ram.RAM, state.ram.RAM.Length);
MemoryUtility.memcpy(MMU.BGPAL, state.ram.BGPAL, state.ram.BGPAL.Length);
MemoryUtility.memcpy(MMU.SPPAL, state.ram.SPPAL, state.ram.SPPAL.Length);
MemoryUtility.memcpy(MMU.SPRAM, state.ram.SPRAM, state.ram.SPRAM.Length);
if (rom.IsSAVERAM())
{
Array.Copy(state.WRAM, MMU.WRAM, SAVERAM_SIZE);
}
}
//BANK STATE
{
// SAVE CPU MEMORY BANK DATA
// BANK0,1,2<>ϥХ󥯥<D0A5><F3A5AFA5>`<60>֤<EFBFBD><D6A4>v<EFBFBD>S<EFBFBD>ʤ<EFBFBD>
// VirtuaNES0.30<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20>Х󥯣<D0A5><F3A5AFA3><EFBFBD>SRAMʹ<4D>ä<EFBFBD><C3A4>v<EFBFBD><76><EFBFBD><EFBFBD><E9A4BA><EFBFBD>`<60><>
for (byte i = 3; i < 8; i++)
{
MMU.CPU_MEM_TYPE[i] = state.mmu.CPU_MEM_TYPE[i];
MMU.CPU_MEM_PAGE[i] = state.mmu.CPU_MEM_PAGE[i];
if (MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_ROM)
MMU.SetPROM_8K_Bank(i, MMU.CPU_MEM_PAGE[i]);
else
{
MMU.CPU_MEM_BANK[i].SetArray(state.CPU_MEM_BANK.ToArray(), 0);
}
}
// VRAM
MemoryUtility.memcpy(MMU.VRAM, state.VRAM, 4 * 1024);
// CRAM
for (int i = 0; i < 8; i++)
{
MMU.CRAM_USED[i] = state.mmu.CRAM_USED[i];
}
// SAVE VRAM MEMORY DATA
for (byte i = 0; i < 12; i++)
{
if (state.mmu.PPU_MEM_TYPE[i] == MMU.BANKTYPE_VROM)
{
MMU.SetVROM_1K_Bank(i, state.mmu.PPU_MEM_PAGE[i]);
}
else if (state.mmu.PPU_MEM_TYPE[i] == MMU.BANKTYPE_CRAM)
{
MMU.SetCRAM_1K_Bank(i, state.mmu.PPU_MEM_PAGE[i]);
}
else if (state.mmu.PPU_MEM_TYPE[i] == MMU.BANKTYPE_VRAM)
{
MMU.SetVRAM_1K_Bank(i, state.mmu.PPU_MEM_PAGE[i]);
}
else
{
throw new Exception("Unknown bank types.");
}
}
// WRITE CPU RAM MEMORY BANK
int stateStep = 0;
var stateCPU_MEM_BANK = state.CPU_MEM_BANK.ToArray();
for (int i = 3; i < 8; i++)
{
if (state.mmu.CPU_MEM_TYPE[i] != MMU.BANKTYPE_ROM)
{
var sourceData = new Span<byte>(stateCPU_MEM_BANK, stateStep * 8 * 1024, 8 * 1024);
MMU.CPU_MEM_BANK[i].WriteTo(sourceData.ToArray(), 0, 8 * 1024);
stateStep++;
}
}
Array.Copy(state.VRAM, MMU.VRAM, state.VRAM.Length);
stateStep = 0;
var stateCRAM = state.CRAM.ToArray();
// LOAD CRAM MEMORY
for (int i = 0; i < 8; i++)
{
if (MMU.CRAM_USED[i] != 0)
{
var sourceData = stateCRAM.AsSpan(stateStep * 4 * 1024, 4 * 1024).ToArray();
Array.Copy(sourceData, 0, MMU.CRAM, 0x1000 * i, 4 * 1024);
}
}
}
// MMC STATE
{
mapper.LoadState(state.mmc.mmcdata);
}
//CONTROLLER STATE
{
pad.pad1bit = state.ctr.pad1bit;
pad.pad2bit = state.ctr.pad2bit;
pad.pad3bit = state.ctr.pad3bit;
pad.pad4bit = state.ctr.pad4bit;
pad.SetStrobe(state.ctr.strobe == 0 ? false : true);
}
//SND STATE
{
var buffer = new StateReader(state.snd.snddata);
apu.LoadState(buffer);
}
// DISKIMAGE STATE
if (rom.GetMapperNo() == 20)
{
var lpDisk = rom.GetPROM();
var lpWrite = rom.GetDISK();
int DiskSize = 16 + 65500 * rom.GetDiskNo();
Array.Clear(lpWrite, 0, DiskSize);
for (int i = 0; i < state.dsk.DifferentSize; i++)
{
var pos = state.dskdata[i];
byte data = (byte)(pos >> 24);
pos &= 0x00FFFFFF;
if (pos >= 16 && pos < DiskSize)
{
lpDisk[pos] = data;
lpWrite[pos] = 0xFF;
}
}
}
// EXCTR STATE
if (pad.GetExController() != 0)
{
pad.SetSyncExData(state.exctr.data);
}
}
internal void SetZapperPos(int x, int y)
{
ZapperX = x; ZapperY = y;
}
public enum IRQMETHOD
{
IRQ_HSYNC = 0, IRQ_CLOCK = 1
}
}
}