AxibugEmuOnline/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs

1682 lines
60 KiB
C#
Raw Normal View History

2024-07-23 18:31:59 +08:00
using System;
using System.Collections.Generic;
using System.IO;
2024-07-25 11:03:58 +08:00
using System.Text;
2024-07-23 18:31:59 +08:00
using VirtualNes.Core.Debug;
namespace VirtualNes.Core
{
public class NES
{
2024-07-29 09:25:19 +08:00
public const int FETCH_CYCLES = 8;
2024-07-23 18:31:59 +08:00
public CPU cpu;
public PPU ppu;
public APU apu;
public ROM rom;
public PAD pad;
public Mapper mapper;
2024-07-25 11:03:58 +08:00
public NesConfig nescfg;
2024-07-23 18:31:59 +08:00
2024-07-25 11:03:58 +08:00
private List<CHEATCODE> m_CheatCode = new List<CHEATCODE>();
2024-07-26 17:52:33 +08:00
private List<GENIECODE> m_GenieCode = new List<GENIECODE>();
2024-07-23 18:31:59 +08:00
private bool m_bDiskThrottle;
private int m_CommandRequest;
private int m_nSnapNo;
private bool m_bNsfPlaying;
2024-07-26 17:52:33 +08:00
private bool m_bNsfInit;
private int m_nNsfSongNo;
private int m_nNsfSongMode;
2024-07-23 18:31:59 +08:00
private bool m_bMoviePlay;
private bool m_bMovieRec;
private Stream m_fpMovie;
2024-07-30 11:57:09 +08:00
private uint m_MovieControl;
private int m_MovieStepTotal;
private int m_MovieStep;
2024-07-23 18:31:59 +08:00
private bool m_bTapePlay;
private bool m_bTapeRec;
private Stream m_fpTape;
private double m_TapeCycles;
private byte m_TapeIn;
private byte m_TapeOut;
2024-07-25 11:03:58 +08:00
// 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
2024-07-23 18:31:59 +08:00
private bool m_bBarcode2;
2024-07-25 11:03:58 +08:00
private int m_Barcode2seq;
private int m_Barcode2ptr;
private int m_Barcode2cnt;
private byte m_Barcode2bit;
private byte[] m_Barcode2data = new byte[32];
2024-07-23 18:31:59 +08:00
private int m_TurboFileBank;
private int SAVERAM_SIZE;
private int nIRQtype;
private bool bFrameIRQ;
private bool bVideoMode;
2024-07-25 11:03:58 +08:00
private int NES_scanline;
private EnumRenderMethod RenderMethod;
private bool bZapper;
2024-07-30 11:57:09 +08:00
private int ZapperX;
private int ZapperY;
2024-07-25 11:03:58 +08:00
private long base_cycles;
private long emul_cycles;
2024-07-23 18:31:59 +08:00
2024-07-30 18:53:36 +08:00
// For VS-Unisystem
byte m_VSDipValue;
VSDIPSWITCH[] m_VSDipTable;
2024-07-30 11:57:09 +08:00
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,
};
2024-07-23 18:31:59 +08:00
public NES(string fname)
{
Debuger.Log("VirtuaNES - CSharpCore\n");
m_bDiskThrottle = false;
m_CommandRequest = 0;
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;
2024-07-26 17:52:33 +08:00
nescfg = NesConfig.NESCONFIG_NTSC;
2024-07-23 18:31:59 +08:00
CheatInitial();
try
{
Debuger.Log("Allocating CPU...");
cpu = new CPU(this);
Debuger.Log("Allocating PPU...");
ppu = new PPU(this);
var screenBuffer = new uint[PPU.SCREEN_WIDTH * PPU.SCREEN_HEIGHT];
2024-07-30 11:57:09 +08:00
var colormode = new byte[PPU.SCREEN_HEIGHT];
ppu.SetScreenPtr(screenBuffer, colormode);
2024-07-23 18:31:59 +08:00
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);
2024-07-30 11:57:09 +08:00
mapper = Mapper.CreateMapper(this, rom.GetMapperNo());
Debuger.Log("OK");
Debuger.Log($"{rom.GetRomName()}");
Debuger.Log($"Mapper : #{rom.GetMapperNo():D3}");
2024-08-05 17:46:33 +08:00
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");
2024-07-30 11:57:09 +08:00
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();
2024-07-30 18:53:36 +08:00
{
// 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);
2024-07-23 18:31:59 +08:00
}
catch (Exception ex)
{
Debuger.LogError(ex.ToString());
2024-07-30 11:57:09 +08:00
throw ex;
}
}
2024-08-05 11:52:43 +08:00
internal int GetIrqType()
2024-07-30 18:53:36 +08:00
{
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}");
}
}
2024-07-30 11:57:09 +08:00
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;
2024-07-23 18:31:59 +08:00
}
}
2024-07-25 11:03:58 +08:00
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;
}
2024-07-23 18:31:59 +08:00
public void CheatInitial()
{
m_CheatCode.Clear();
}
2024-07-31 17:40:32 +08:00
private int FrameCount = 0;
2024-07-25 11:03:58 +08:00
public void EmulateFrame(bool bDraw)
{
2024-07-31 17:40:32 +08:00
FrameCount++;
2024-07-25 11:03:58 +08:00
int scanline = 0;
if (rom.IsNSF())
{
EmulateNSF();
return;
}
2024-07-23 18:31:59 +08:00
2024-07-25 11:03:58 +08:00
CheatCodeProcess();
2024-07-23 18:31:59 +08:00
2024-07-25 11:03:58 +08:00
NES_scanline = scanline;
2024-07-31 17:40:32 +08:00
bool NotTile = RenderMethod != EnumRenderMethod.TILE_RENDER;
2024-07-23 18:31:59 +08:00
2024-07-25 11:03:58 +08:00
if (RenderMethod != EnumRenderMethod.TILE_RENDER)
{
bZapper = false;
while (true)
{
ppu.SetRenderScanline(scanline);
if (scanline == 0)
{
if (RenderMethod < EnumRenderMethod.POST_RENDER)
{
EmulationCPU(nescfg.ScanlineCycles);
2024-07-29 09:25:19 +08:00
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);
2024-07-25 11:03:58 +08:00
}
}
2024-07-29 09:25:19 +08:00
else if (scanline < 240)
{
2024-07-30 11:57:09 +08:00
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();
2024-07-29 09:25:19 +08:00
2024-07-30 11:57:09 +08:00
// 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;
}
2024-07-29 09:25:19 +08:00
}
2024-07-30 11:57:09 +08:00
if (pad.IsZapperMode())
{
if (scanline == ZapperY)
bZapper = true;
else
bZapper = false;
}
scanline++;
NES_scanline = scanline;
2024-07-25 11:03:58 +08:00
}
}
2024-07-30 11:57:09 +08:00
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;
}
}
if (bDraw)
{
DrawPad();
}
}
private void DrawPad()
{
if (m_bMoviePlay)
{
int offset_h = 12;
int offset_v = Supporter.Config.graphics.bAllLine ? (240 - 18) : (240 - 22);
if (Supporter.Config.movie.bPadDisplay)
{
uint dwData = pad.GetSyncData();
for (int i = 0; i < 4; i++)
{
byte Data = (byte)(dwData >> (i * 8));
if ((m_MovieControl & (1 << i)) != 0)
{
DrawBitmap(offset_h, offset_v, m_PadImg);
// KEY
if ((Data & (1 << 4)) != 0) DrawBitmap(offset_h + 3, offset_v + 1, m_KeyImg0); // U
if ((Data & (1 << 5)) != 0) DrawBitmap(offset_h + 3, offset_v + 5, m_KeyImg0); // D
if ((Data & (1 << 6)) != 0) DrawBitmap(offset_h + 1, offset_v + 3, m_KeyImg0); // L
if ((Data & (1 << 7)) != 0) DrawBitmap(offset_h + 5, offset_v + 3, m_KeyImg0); // R
// START,SELECT
if ((Data & (1 << 2)) != 0) DrawBitmap(offset_h + 9, offset_v + 5, m_KeyImg1); // SELECT
if ((Data & (1 << 3)) != 0) DrawBitmap(offset_h + 13, offset_v + 5, m_KeyImg1); // START
// A,B
if ((Data & (1 << 0)) != 0) DrawBitmap(offset_h + 23, offset_v + 3, m_KeyImg2); // A
if ((Data & (1 << 1)) != 0) DrawBitmap(offset_h + 18, offset_v + 3, m_KeyImg2); // B
offset_h += 30;
}
}
}
if (Supporter.Config.movie.bTimeDisplay)
{
// Time display
int t = m_MovieStep;
int h = t / 216000;
t -= h * 216000;
int m = t / 3600;
t -= m * 3600;
int s = t / 60;
t -= s * 60;
string szTemp = $"{h:00}:{m:00}:{s:00}.{t * 100 / 60:00}";
DrawString(256 - 80 + 0, offset_v - 1, szTemp, 0x1F);
DrawString(256 - 80 + 0, offset_v + 1, szTemp, 0x1F);
DrawString(256 - 80 - 1, offset_v + 0, szTemp, 0x1F);
DrawString(256 - 80 + 1, offset_v + 0, szTemp, 0x1F);
DrawString(256 - 80, offset_v, szTemp, 0x30);
}
}
}
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 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);
}
}
private void DrawBitmap(int x, int y, byte[] bitMap)
{
int i, j;
int h, v;
var Scn = ppu.GetScreenPtr();
int pScn = 8 + (256 + 16) * y + x;
int pPtr;
int lpBitmap = 0;
h = bitMap[lpBitmap++];
v = bitMap[lpBitmap++];
for (j = 0; j < v; j++)
{
pPtr = pScn;
for (i = 0; i < h; i++)
{
if (bitMap[lpBitmap] != 0xFF)
{
Scn[pPtr] = bitMap[lpBitmap];
}
lpBitmap++;
pPtr++;
}
pScn += 256 + 16;
}
2024-07-25 11:03:58 +08:00
}
2024-07-23 18:31:59 +08:00
2024-07-31 17:40:32 +08:00
int CPU_CALL_COUNT = 0;
2024-07-25 11:03:58 +08:00
internal void EmulationCPU(int basecycles)
{
int cycles;
base_cycles += basecycles;
cycles = (int)((base_cycles / 12) - emul_cycles);
if (cycles > 0)
{
2024-07-31 17:40:32 +08:00
var cycleAdd = cpu.EXEC(cycles);
emul_cycles += cycleAdd;
2024-07-25 11:03:58 +08:00
}
2024-07-31 17:40:32 +08:00
CPU_CALL_COUNT++;
2024-07-25 11:03:58 +08:00
}
internal void Reset()
{
SaveSRAM();
SaveDISK();
SaveTurboFile();
2024-07-26 17:52:33 +08:00
// RAM Clear
2024-07-30 11:57:09 +08:00
MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
2024-07-26 17:52:33 +08:00
if (rom.GetPROM_CRC() == 0x29401686)
2024-07-30 11:57:09 +08:00
{ // Minna no Taabou no Nakayoshi Dai Sakusen(J)
MemoryUtility.memset(MMU.RAM, 0xFF, MMU.RAM.Length);
2024-07-26 17:52:33 +08:00
}
// RAM set
if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20)
{
2024-07-30 11:57:09 +08:00
MemoryUtility.memset(MMU.WRAM, 0xFF, MMU.WRAM.Length);
2024-07-26 17:52:33 +08:00
}
2024-07-30 11:57:09 +08:00
MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length);
2024-07-26 17:52:33 +08:00
2024-07-30 11:57:09 +08:00
MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length);
MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length);
2024-07-26 17:52:33 +08:00
2024-07-30 11:57:09 +08:00
MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length);
MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length);
2024-07-26 17:52:33 +08:00
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();
}
2024-07-31 17:40:32 +08:00
2024-07-23 18:31:59 +08:00
2024-07-25 11:03:58 +08:00
internal void SoftReset()
{
2024-07-26 17:52:33 +08:00
pad.Reset();
cpu.Reset();
apu.Reset();
if (rom.IsNSF())
{
mapper.Reset();
}
m_bDiskThrottle = false;
base_cycles = emul_cycles = 0;
2024-07-25 11:03:58 +08:00
}
internal void EmulateNSF()
{
2024-07-26 17:52:33 +08:00
R6502 reg = null;
ppu.Reset();
mapper.VSync();
//DEBUGOUT( "Frame\n" );
if (m_bNsfPlaying)
{
if (m_bNsfInit)
{
2024-07-30 11:57:09 +08:00
MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
2024-07-26 17:52:33 +08:00
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);
}
2024-07-25 11:03:58 +08:00
}
2024-07-23 18:31:59 +08:00
2024-07-25 11:03:58 +08:00
internal void CheatCodeProcess()
2024-07-23 18:31:59 +08:00
{
2024-07-30 11:57:09 +08:00
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;
}
2024-07-25 11:03:58 +08:00
}
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++)
{
2024-07-26 17:52:33 +08:00
if (MMU.WRAM[i] != 0x00)
2024-07-25 11:03:58 +08:00
break;
}
if (i < SAVERAM_SIZE)
{
var romName = rom.GetRomName();
Debuger.Log($"Saving SAVERAM...[{romName}]");
2024-07-26 17:52:33 +08:00
Supporter.SaveSRAMToFile(MMU.WRAM, romName);
2024-07-25 11:03:58 +08:00
}
}
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
2024-07-23 18:31:59 +08:00
{
2024-07-25 11:03:58 +08:00
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()
{
2024-07-30 11:57:09 +08:00
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");
}
2024-07-25 11:03:58 +08:00
}
internal void Clock(int cycles)
{
Tape(cycles);
Barcode(cycles);
}
private void Barcode(int cycles)
{
if (m_bBarcode)
2024-07-23 18:31:59 +08:00
{
2024-07-25 11:03:58 +08:00
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)
{
2024-07-30 11:57:09 +08:00
if (!(IsTapePlay() || IsTapeRec()))
{
return;
}
if ((m_TapeCycles -= (double)cycles) > 0)
return;
m_TapeCycles += (nescfg.CpuClock / 32000.0);
2024-08-06 17:50:51 +08:00
// m_TapeCycles += (nescfg.CpuClock / 22050.0); // <20>x<EFBFBD><78><EFBFBD><EFBFBD><EFBFBD>ă_<C483><5F><EFBFBD><EFBFBD><EFBFBD>ۂ<EFBFBD>
2024-07-30 11:57:09 +08:00
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;
2024-07-23 18:31:59 +08:00
}
2024-07-25 14:03:52 +08:00
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
2024-07-30 18:53:36 +08:00
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
2024-07-25 14:03:52 +08:00
}
2024-07-30 11:57:09 +08:00
return 0x00; // Warning<6E>\<5C>h
2024-07-25 14:03:52 +08:00
}
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;
}
2024-07-31 17:40:32 +08:00
public void SetRenderMethod(EnumRenderMethod type)
{
RenderMethod = type;
}
2024-07-25 18:34:52 +08:00
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
{
2024-07-26 17:52:33 +08:00
mapper.WriteLow(addr, data);
2024-07-25 18:34:52 +08:00
}
break;
case 0x03: // $6000-$7FFF
2024-07-26 17:52:33 +08:00
mapper.WriteLow(addr, data);
2024-07-25 18:34:52 +08:00
break;
case 0x04: // $8000-$9FFF
case 0x05: // $A000-$BFFF
case 0x06: // $C000-$DFFF
case 0x07: // $E000-$FFFF
2024-07-26 17:52:33 +08:00
mapper.Write(addr, data);
2024-07-25 18:34:52 +08:00
GenieCodeProcess();
break;
}
}
2024-07-26 17:52:33 +08:00
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
2024-07-30 18:53:36 +08:00
if (MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] == m_GenieCode[i].cmp)
2024-07-26 17:52:33 +08:00
{
2024-07-30 18:53:36 +08:00
MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = m_GenieCode[i].data;
2024-07-26 17:52:33 +08:00
}
}
else
{
// 6character codes
addr |= 0x8000;
2024-07-30 18:53:36 +08:00
MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = m_GenieCode[i].data;
2024-07-26 17:52:33 +08:00
}
}
}
2024-07-25 18:34:52 +08:00
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);
2024-07-26 17:52:33 +08:00
MMU.CPUREG[addr & 0xFF] = data;
2024-07-25 18:34:52 +08:00
m_TapeIn = data;
break;
case 0x17:
2024-07-26 17:52:33 +08:00
MMU.CPUREG[addr & 0xFF] = data;
pad.Write(addr, data);
apu.Write(addr, data);
2024-07-25 18:34:52 +08:00
break;
// VirtuaNES<45>ŗL<C597>|<7C>[<5B>g
case 0x18:
2024-07-26 17:52:33 +08:00
apu.Write(addr, data);
2024-07-25 18:34:52 +08:00
break;
default:
2024-07-26 17:52:33 +08:00
mapper.ExWrite(addr, data);
2024-07-25 18:34:52 +08:00
break;
}
}
internal bool GetVideoMode()
{
return bVideoMode;
}
internal void SetFrameIRQmode(bool bMode)
{
bFrameIRQ = bMode;
}
internal bool GetFrameIRQmode()
{
return bFrameIRQ;
}
2024-07-30 11:57:09 +08:00
internal EnumRenderMethod GetRenderMethod()
{
return RenderMethod;
}
internal void SetIrqType(IRQMETHOD nType)
{
nIRQtype = (int)nType;
}
internal int GetScanline()
{
return NES_scanline;
}
2024-08-05 11:52:43 +08:00
internal void SetSAVERAM_SIZE(int size)
{
SAVERAM_SIZE = size;
}
internal byte GetBarcodeStatus()
{
return m_BarcodeOut;
}
2024-07-30 11:57:09 +08:00
public enum IRQMETHOD
{
IRQ_HSYNC = 0, IRQ_CLOCK = 1
}
2024-07-25 18:34:52 +08:00
}
2024-07-23 18:31:59 +08:00
}