mirror of
https://github.com/SourMesen/VisualNes.git
synced 2025-04-02 10:31:49 -04:00
804 lines
24 KiB
C#
804 lines
24 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using Be.Windows.Forms;
|
|
|
|
namespace GUI
|
|
{
|
|
public partial class frmMain : Form
|
|
{
|
|
private List<string> _tracedColumns = new List<string> { "cycle", "hpos", "vpos", "ab", "db", "cpu_a", "cpu_x", "cpu_y", "cpu_db", "cpu_ab", "io_rw", "io_ce" };
|
|
private Dictionary<int, string> _opName = new Dictionary<int, string>() {
|
|
{ 0x00,"BRK"},{0x01,"ORA (zp,X)"},{0x05,"ORA zp"},{0x06,"ASL zp"},{0x08,"PHP"},{0x09,"ORA #"},{0x0A,"ASL "},{0x0D,"ORA Abs"},{0x0E,"ASL Abs"},{0x10,"BPL "},{0x11,"ORA (zp),Y"},
|
|
{ 0x15,"ORA zp,X"},{0x16,"ASL zp,X"},{0x18,"CLC"},{0x19,"ORA Abs,Y"},{0x1D,"ORA Abs,X"},{0x1E,"ASL Abs,X"},{0x20,"JSR Abs"},{0x21,"AND (zp,X)"},{0x24,"BIT zp"},{0x25,"AND zp"},
|
|
{ 0x26,"ROL zp"},{0x28,"PLP"},{0x29,"AND #"},{0x2A,"ROL "},{0x2C,"BIT Abs"},{0x2D,"AND Abs"},{0x2E,"ROL Abs"},{0x30,"BMI "},{0x31,"AND (zp),Y"},{0x35,"AND zp,X"},{0x36,"ROL zp,X"},
|
|
{ 0x38,"SEC"},{0x39,"AND Abs,Y"},{0x3D,"AND Abs,X"},{0x3E,"ROL Abs,X"},{0x40,"RTI"},{0x41,"EOR (zp,X)"},{0x45,"EOR zp"},{0x46,"LSR zp"},{0x48,"PHA"},{0x49,"EOR #"},{0x4A,"LSR "},
|
|
{ 0x4C,"JMP Abs"},{0x4D,"EOR Abs"},{0x4E,"LSR Abs"},{0x50,"BVC "},{0x51,"EOR (zp),Y"},{0x55,"EOR zp,X"},{0x56,"LSR zp,X"},{0x58,"CLI"},{0x59,"EOR Abs,Y"},{0x5D,"EOR Abs,X"},
|
|
{ 0x5E,"LSR Abs,X"},{0x60,"RTS"},{0x61,"ADC (zp,X)"},{0x65,"ADC zp"},{0x66,"ROR zp"},{0x68,"PLA"},{0x69,"ADC #"},{0x6A,"ROR "},{0x6C,"JMP zp"},{0x6D,"ADC Abs"},{0x6E,"ROR Abs"},
|
|
{ 0x70,"BVS "},{0x71,"ADC (zp),Y"},{0x75,"ADC zp,X"},{0x76,"ROR zp,X"},{0x78,"SEI"},{0x79,"ADC Abs,Y"},{0x7D,"ADC Abs,X"},{0x7E,"ROR Abs,X"},{0x81,"STA (zp,X)"},{0x84,"STY zp"},
|
|
{ 0x85,"STA zp"},{0x86,"STX zp"},{0x88,"DEY"},{0x8A,"TXA"},{0x8C,"STY Abs"},{0x8D,"STA Abs"},{0x8E,"STX Abs"},{0x90,"BCC "},{0x91,"STA (zp),Y"},{0x94,"STY zp,X"},{0x95,"STA zp,X"},
|
|
{ 0x96,"STX zp,Y"},{0x98,"TYA"},{0x99,"STA Abs,Y"},{0x9A,"TXS"},{0x9D,"STA Abs,X"},{0xA0,"LDY #"},{0xA1,"LDA (zp,X)"},{0xA2,"LDX #"},{0xA4,"LDY zp"},{0xA5,"LDA zp"},{0xA6,"LDX zp"},
|
|
{ 0xA8,"TAY"},{0xA9,"LDA #"},{0xAA,"TAX"},{0xAC,"LDY Abs"},{0xAD,"LDA Abs"},{0xAE,"LDX Abs"},{0xB0,"BCS "},{0xB1,"LDA (zp),Y"},{0xB4,"LDY zp,X"},{0xB5,"LDA zp,X"},{0xB6,"LDX zp,Y"},
|
|
{ 0xB8,"CLV"},{0xB9,"LDA Abs,Y"},{0xBA,"TSX"},{0xBC,"LDY Abs,X"},{0xBD,"LDA Abs,X"},{0xBE,"LDX Abs,Y"},{0xC0,"CPY #"},{0xC1,"CMP (zp,X)"},{0xC4,"CPY zp"},{0xC5,"CMP zp"},{0xC6,"DEC zp"},
|
|
{ 0xC8,"INY"},{0xC9,"CMP #"},{0xCA,"DEX"},{0xCC,"CPY Abs"},{0xCD,"CMP Abs"},{0xCE,"DEC Abs"},{0xD0,"BNE "},{0xD1,"CMP (zp),Y"},{0xD5,"CMP zp,X"},{0xD6,"DEC zp,X"},{0xD8,"CLD"},
|
|
{ 0xD9,"CMP Abs,Y"},{0xDD,"CMP Abs,X"},{0xDE,"DEC Abs,X"},{0xE0,"CPX #"},{0xE1,"SBC (zp,X)"},{0xE4,"CPX zp"},{0xE5,"SBC zp"},{0xE6,"INC zp"},{0xE8,"INX"},{0xE9,"SBC #"},{0xEA,"NOP"},
|
|
{ 0xEC,"CPX Abs"},{0xED,"SBC Abs"},{0xEE,"INC Abs"},{0xF0,"BEQ "},{0xF1,"SBC (zp),Y"},{0xF5,"SBC zp,X"},{0xF6,"INC zp,X"},{0xF8,"SED"},{0xF9,"SBC Abs,Y"},{0xFD,"SBC Abs,X"},
|
|
{ 0xFE,"INC Abs,X"}
|
|
};
|
|
|
|
private const string _windowTitle = "Visual NES - 2A03 & 2C02 simulator";
|
|
internal static string CurrentRom = "";
|
|
private frmVideoViewer _videoViewer = null;
|
|
|
|
private bool _autoRun = false;
|
|
private object _runLock = new object();
|
|
private int _stepsToRun = 0;
|
|
private bool _needColumnUpdate = true;
|
|
private EmulationState _previousState = new EmulationState();
|
|
private Task _emulationThread = null;
|
|
private bool _stopFlag = false;
|
|
private string _dumpPath;
|
|
private bool _chrRamChanged = false;
|
|
private bool _nametableRamChanged = false;
|
|
private bool _paletteRamChanged = false;
|
|
private bool _spriteRamChanged = false;
|
|
private bool _cpuRamChanged = false;
|
|
private bool _prgRamChanged = false;
|
|
private int _refreshSpeed = 4000;
|
|
private bool _logging = false;
|
|
private byte[] _chrRamData;
|
|
private byte[] _nametableRamData;
|
|
private byte[] _paletteData;
|
|
private byte[] _spriteData;
|
|
private byte[] _cpuRamData;
|
|
private byte[] _prgRamData;
|
|
private ChipDefinitions _chipDef = new ChipDefinitions();
|
|
|
|
public frmMain()
|
|
{
|
|
InitializeComponent();
|
|
|
|
if(!DesignMode) {
|
|
ctrlChipDisplay.SetChipDefinitions(_chipDef);
|
|
_dumpPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "Dumps");
|
|
Directory.CreateDirectory(_dumpPath);
|
|
}
|
|
}
|
|
|
|
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
|
{
|
|
if(keyData == Keys.Escape) {
|
|
Stop();
|
|
return true;
|
|
}
|
|
return base.ProcessCmdKey(ref msg, keyData);
|
|
}
|
|
|
|
protected override void OnLoad(EventArgs e)
|
|
{
|
|
base.OnLoad(e);
|
|
|
|
if(!DesignMode) {
|
|
cboLogMaxLines.SelectedIndex = 0;
|
|
SetMirroring(MirroringType.Horizontal);
|
|
|
|
CoreWrapper.initEmulator();
|
|
CoreWrapper.setTrace(string.Join("|", _tracedColumns));
|
|
CoreWrapper.reset(string.Empty, false);
|
|
|
|
txtFindNode.Values = _chipDef.NodeNumberByName.Keys.ToArray();
|
|
|
|
Step(1);
|
|
|
|
StartEmulationThread();
|
|
}
|
|
}
|
|
|
|
protected override void OnClosed(EventArgs e)
|
|
{
|
|
Stop();
|
|
_stopFlag = true;
|
|
if(_emulationThread != null) {
|
|
_emulationThread.Wait();
|
|
}
|
|
CoreWrapper.release();
|
|
base.OnClosed(e);
|
|
}
|
|
|
|
private void StartEmulationThread()
|
|
{
|
|
_emulationThread = Task.Run(() => {
|
|
while(!_stopFlag) {
|
|
while(!_stopFlag && _stepsToRun > 0) {
|
|
UpdateRam();
|
|
|
|
int stepsToRun = Math.Min(_refreshSpeed, _stepsToRun);
|
|
Interlocked.Add(ref _stepsToRun, -stepsToRun);
|
|
|
|
Stopwatch sw = new Stopwatch();
|
|
lock(_runLock) {
|
|
sw.Start();
|
|
CoreWrapper.step((UInt32)stepsToRun);
|
|
CoreWrapper.getState(ref _previousState);
|
|
|
|
_chrRamData = CoreWrapper.getMemoryState(MemoryType.ChrRam);
|
|
_nametableRamData = CoreWrapper.getMemoryState(MemoryType.NametableRam);
|
|
_paletteData = CoreWrapper.getMemoryState(MemoryType.PaletteRam);
|
|
_spriteData = CoreWrapper.getMemoryState(MemoryType.SpriteRam);
|
|
_cpuRamData = CoreWrapper.getMemoryState(MemoryType.CpuRam);
|
|
_prgRamData = CoreWrapper.getMemoryState(MemoryType.PrgRam);
|
|
|
|
if(mnuShowSimulationState.Checked) {
|
|
ctrlChipDisplay.ShowState = true;
|
|
ctrlChipDisplay.RefreshState(true);
|
|
}
|
|
sw.Stop();
|
|
}
|
|
|
|
this.BeginInvoke((MethodInvoker)(() => {
|
|
double emulatedHz = Stopwatch.Frequency / (double)sw.ElapsedTicks * stepsToRun / 2;
|
|
int nesMasterClockSpeed = 21477272;
|
|
double speedPercent = (emulatedHz / nesMasterClockSpeed) * 100;
|
|
lblHz.Text = string.Format("{0:0} ({1:0.0000}% of NES speed - {2:0.0000} FPM)", emulatedHz, speedPercent, speedPercent / 100 * 60.1 * 60);
|
|
|
|
UpdateUI();
|
|
}));
|
|
}
|
|
|
|
if(_autoRun) {
|
|
Interlocked.Add(ref _stepsToRun, _refreshSpeed);
|
|
} else {
|
|
System.Threading.Thread.Sleep(50);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void UpdateRam()
|
|
{
|
|
lock(_runLock) {
|
|
if(_chrRamChanged) {
|
|
CoreWrapper.setMemoryState(MemoryType.ChrRam, ((StaticByteProvider)hexChrRam.ByteProvider).Bytes.ToArray());
|
|
}
|
|
if(_nametableRamChanged) {
|
|
CoreWrapper.setMemoryState(MemoryType.NametableRam, ((StaticByteProvider)hexNametableRam.ByteProvider).Bytes.ToArray());
|
|
}
|
|
if(_paletteRamChanged) {
|
|
CoreWrapper.setMemoryState(MemoryType.PaletteRam, ((StaticByteProvider)hexPaletteRam.ByteProvider).Bytes.ToArray());
|
|
}
|
|
if(_spriteRamChanged) {
|
|
CoreWrapper.setMemoryState(MemoryType.SpriteRam, ((StaticByteProvider)hexSpriteRam.ByteProvider).Bytes.ToArray());
|
|
}
|
|
if(_cpuRamChanged) {
|
|
CoreWrapper.setMemoryState(MemoryType.CpuRam, ((StaticByteProvider)hexCpuRam.ByteProvider).Bytes.ToArray());
|
|
}
|
|
if(_prgRamChanged) {
|
|
CoreWrapper.setMemoryState(MemoryType.PrgRam, ((StaticByteProvider)hexPrgRam.ByteProvider).Bytes.ToArray());
|
|
}
|
|
}
|
|
_chrRamChanged = false;
|
|
_nametableRamChanged = false;
|
|
_paletteRamChanged = false;
|
|
_spriteRamChanged = false;
|
|
_cpuRamChanged = false;
|
|
_prgRamChanged = false;
|
|
}
|
|
|
|
private void UpdateUI()
|
|
{
|
|
this.Text = _windowTitle;
|
|
if(CurrentRom.Length > 0) {
|
|
this.Text += " - " + Path.GetFileNameWithoutExtension(CurrentRom);
|
|
}
|
|
|
|
lblHalfCycle.Text = _previousState.halfcycle.ToString();
|
|
lblPixel.Text = _previousState.hpos.ToString();
|
|
lblScanline.Text = _previousState.vpos.ToString();
|
|
lblFrameCount.Text = (_previousState.halfcycle / (8 * 341 * 262 - 1)).ToString();
|
|
|
|
lblClk.Text = _previousState.clk.ToString();
|
|
lblPpuAb.Text = _previousState.ab.ToString("X4");
|
|
lblPpuDb.Text = _previousState.d.ToString("X2");
|
|
|
|
lblA.Text = _previousState.a.ToString("X2");
|
|
lblX.Text = _previousState.x.ToString("X2");
|
|
lblY.Text = _previousState.y.ToString("X2");
|
|
lblPS.Text = _previousState.ps.ToString("X2");
|
|
lblSP.Text = _previousState.sp.ToString("X2");
|
|
lblPC.Text = _previousState.pc.ToString("X4");
|
|
|
|
lblCpuAb.Text = _previousState.cpu_ab.ToString("X4");
|
|
lblPpuDb.Text = _previousState.cpu_db.ToString("X2");
|
|
lblOpCode.Text = _opName.ContainsKey(_previousState.opCode) ? _opName[_previousState.opCode] : "n/a";
|
|
|
|
UpdateMemoryViews();
|
|
UpdateLogList();
|
|
}
|
|
|
|
private void UpdateMemoryViews()
|
|
{
|
|
StaticByteProvider chrRamBp = new StaticByteProvider(_chrRamData);
|
|
StaticByteProvider nametableRamBp = new StaticByteProvider(_nametableRamData);
|
|
StaticByteProvider paletteBp = new StaticByteProvider(_paletteData);
|
|
StaticByteProvider spriteBp = new StaticByteProvider(_spriteData);
|
|
StaticByteProvider cpuRamBp = new StaticByteProvider(_cpuRamData);
|
|
StaticByteProvider prgRamBp = new StaticByteProvider(_prgRamData);
|
|
hexChrRam.ByteProvider = chrRamBp;
|
|
hexNametableRam.ByteProvider = nametableRamBp;
|
|
hexPaletteRam.ByteProvider = paletteBp;
|
|
hexSpriteRam.ByteProvider = spriteBp;
|
|
hexCpuRam.ByteProvider = cpuRamBp;
|
|
hexPrgRam.ByteProvider = prgRamBp;
|
|
|
|
chrRamBp.ByteChanged += (index, e) => { _chrRamChanged = true; };
|
|
nametableRamBp.ByteChanged += (index, e) => { _nametableRamChanged = true; };
|
|
spriteBp.ByteChanged += (index, e) => { _spriteRamChanged = true; };
|
|
cpuRamBp.ByteChanged += (index, e) => { _cpuRamChanged = true; };
|
|
prgRamBp.ByteChanged += (index, e) => { _prgRamChanged = true; };
|
|
paletteBp.ByteChanged += (index, e) => {
|
|
//Mirror palette writes
|
|
int i = (int)index;
|
|
if(i == 0x10 || i == 0x14 || i == 0x18 || i == 0x1C) {
|
|
paletteBp.WriteByte(i - 0x10, paletteBp.Bytes[i]);
|
|
} else if(i == 0x00 || i == 0x04 || i == 0x08 || i == 0x0C) {
|
|
paletteBp.WriteByte(i + 0x10, paletteBp.Bytes[i]);
|
|
}
|
|
_paletteRamChanged = true;
|
|
};
|
|
}
|
|
|
|
private void UpdateLogList()
|
|
{
|
|
lstLogView.BeginUpdate();
|
|
|
|
int numLines = int.Parse(cboLogMaxLines.SelectedItem.ToString());
|
|
numLines = Math.Min(numLines, 10000 / _tracedColumns.Count);
|
|
if(_needColumnUpdate || lstLogView.Items.Count != numLines) {
|
|
_needColumnUpdate = false;
|
|
lstLogView.Clear();
|
|
foreach(string column in _tracedColumns) {
|
|
lstLogView.Columns.Add(column);
|
|
}
|
|
|
|
for(int i = 0; i < numLines; i++) {
|
|
ListViewItem item = new ListViewItem("");
|
|
for(int j = 0; j < _tracedColumns.Count - 1; j++) {
|
|
item.SubItems.Add("");
|
|
}
|
|
lstLogView.Items.Add(item);
|
|
}
|
|
}
|
|
|
|
int pos = 0;
|
|
int row = 0;
|
|
bool endOfArray = false;
|
|
|
|
while(row < numLines) {
|
|
for(int j = 0; j < _tracedColumns.Count; j++) {
|
|
if(pos >= 10000) {
|
|
break;
|
|
}
|
|
if(_previousState.recentLog[pos] == -1) {
|
|
endOfArray = true;
|
|
}
|
|
string newVal = (pos >= _previousState.recentLog.Length || endOfArray) ? "" : _previousState.recentLog[pos].ToString(chkShowInHex.Checked ? "X2" : "");
|
|
if(lstLogView.Items[row].SubItems[j].Text != newVal) {
|
|
lstLogView.Items[row].SubItems[j].Text = newVal;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
row++;
|
|
}
|
|
|
|
lstLogView.EndUpdate();
|
|
}
|
|
|
|
private void Stop()
|
|
{
|
|
mnuRun.Enabled = true;
|
|
mnuPause.Enabled = false;
|
|
_autoRun = false;
|
|
}
|
|
|
|
private void Step(UInt32 step)
|
|
{
|
|
Interlocked.Exchange(ref _stepsToRun, (int)step);
|
|
}
|
|
|
|
private void Reset(bool softReset)
|
|
{
|
|
lock(_runLock) {
|
|
CoreWrapper.reset(string.Empty, softReset);
|
|
Step(1);
|
|
}
|
|
}
|
|
|
|
private void SaveFile(string filter, Action<string> callback)
|
|
{
|
|
using(SaveFileDialog sfd = new SaveFileDialog()) {
|
|
sfd.InitialDirectory = _dumpPath;
|
|
sfd.Filter = filter;
|
|
sfd.FileName = "data";
|
|
if(sfd.ShowDialog() == DialogResult.OK) {
|
|
callback(sfd.FileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadFile(string filter, Action<string> callback)
|
|
{
|
|
using(OpenFileDialog ofd = new OpenFileDialog()) {
|
|
ofd.InitialDirectory = _dumpPath;
|
|
ofd.Filter = filter;
|
|
ofd.FileName = "data";
|
|
if(ofd.ShowDialog() == DialogResult.OK) {
|
|
callback(ofd.FileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void btnStartLogging_Click(object sender, EventArgs e)
|
|
{
|
|
if(_logging) {
|
|
btnStartLogging.Text = "Start Logging";
|
|
_logging = false;
|
|
chkLogHex.Enabled = true;
|
|
chkLogCsv.Enabled = true;
|
|
lock(_runLock) {
|
|
CoreWrapper.stopLogging();
|
|
}
|
|
} else {
|
|
using(SaveFileDialog sfd = new SaveFileDialog()) {
|
|
sfd.Filter = "Log file (*.txt)|*.txt";
|
|
sfd.FileName = "tracelog.txt";
|
|
if(sfd.ShowDialog() == DialogResult.OK) {
|
|
lock(_runLock) {
|
|
if(CoreWrapper.startLogging(sfd.FileName, chkLogHex.Checked, chkLogCsv.Checked)) {
|
|
btnStartLogging.Text = "Stop Logging";
|
|
_logging = true;
|
|
chkLogHex.Enabled = false;
|
|
chkLogCsv.Enabled = false;
|
|
} else {
|
|
MessageBox.Show("Couldn't open file");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void btnSelectColumns_Click(object sender, EventArgs e)
|
|
{
|
|
using(frmSelectColumns frm = new frmSelectColumns(_chipDef, _tracedColumns)) {
|
|
if(frm.ShowDialog() == DialogResult.OK) {
|
|
lock(_runLock) {
|
|
_tracedColumns.Clear();
|
|
_tracedColumns.AddRange(frmSelectColumns.SelectedColumns);
|
|
CoreWrapper.setTrace(string.Join("|", _tracedColumns));
|
|
_needColumnUpdate = true;
|
|
Step(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void chkShowInHex_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateLogList();
|
|
}
|
|
|
|
private void btnResetPalette_Click(object sender, EventArgs e)
|
|
{
|
|
SetMemoryState(MemoryType.PaletteRam, new byte[0x20]);
|
|
}
|
|
|
|
private void btnImportPalette_Click(object sender, EventArgs e)
|
|
{
|
|
LoadFile("Palette ram (*.pr)|*.pr", (file) => {
|
|
SetMemoryState(MemoryType.PaletteRam, File.ReadAllBytes(file));
|
|
});
|
|
}
|
|
|
|
private void btnExportPalette_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFile("Palette ram (*.pr)|*.pr", (file) => {
|
|
lock(this._runLock) {
|
|
UpdateRam();
|
|
File.WriteAllBytes(file, CoreWrapper.getMemoryState(MemoryType.PaletteRam));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void btnResetSprite_Click(object sender, EventArgs e)
|
|
{
|
|
SetMemoryState(MemoryType.SpriteRam, new byte[0x120]);
|
|
}
|
|
|
|
private void btnImportSprite_Click(object sender, EventArgs e)
|
|
{
|
|
LoadFile("Sprite ram (*.sr)|*.sr", (file) => {
|
|
SetMemoryState(MemoryType.SpriteRam, File.ReadAllBytes(file));
|
|
});
|
|
}
|
|
|
|
private void btnExportSprite_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFile("Sprite ram (*.sr)|*.sr", (file) => {
|
|
lock(_runLock) {
|
|
UpdateRam();
|
|
File.WriteAllBytes(file, CoreWrapper.getMemoryState(MemoryType.SpriteRam));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void btnResetChrRam_Click(object sender, EventArgs e)
|
|
{
|
|
SetMemoryState(MemoryType.ChrRam, new byte[0x2000]);
|
|
}
|
|
|
|
private void btnImportChrRam_Click(object sender, EventArgs e)
|
|
{
|
|
LoadFile("CHR RAM (*.cr)|*.cr", (file) => {
|
|
SetMemoryState(MemoryType.ChrRam, File.ReadAllBytes(file));
|
|
});
|
|
}
|
|
|
|
private void btnExportChrRam_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFile("CHR RAM (*.cr)|*.cr", (file) => {
|
|
lock(_runLock) {
|
|
UpdateRam();
|
|
File.WriteAllBytes(file, CoreWrapper.getMemoryState(MemoryType.ChrRam));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void btnResetNametableRam_Click(object sender, EventArgs e)
|
|
{
|
|
SetMemoryState(MemoryType.NametableRam, new byte[0x1000]);
|
|
}
|
|
|
|
private void btnImportNametableRam_Click(object sender, EventArgs e)
|
|
{
|
|
LoadFile("Nametable RAM (*.nr)|*.nr", (file) => {
|
|
SetMemoryState(MemoryType.NametableRam, File.ReadAllBytes(file));
|
|
});
|
|
}
|
|
|
|
private void btnExportNametableRam_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFile("Nametable RAM (*.nr)|*.nr", (file) => {
|
|
lock(_runLock) {
|
|
UpdateRam();
|
|
File.WriteAllBytes(file, CoreWrapper.getMemoryState(MemoryType.NametableRam));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void btnResetCpuRam_Click(object sender, EventArgs e)
|
|
{
|
|
SetMemoryState(MemoryType.CpuRam, new byte[0x800]);
|
|
}
|
|
|
|
private void btnImportCpuRam_Click(object sender, EventArgs e)
|
|
{
|
|
LoadFile("CPU RAM (*.cr)|*.cr", (file) => {
|
|
SetMemoryState(MemoryType.CpuRam, File.ReadAllBytes(file));
|
|
});
|
|
}
|
|
|
|
private void btnExportCpuRam_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFile("CPU RAM (*.cr)|*.cr", (file) => {
|
|
lock(_runLock) {
|
|
UpdateRam();
|
|
File.WriteAllBytes(file, CoreWrapper.getMemoryState(MemoryType.CpuRam));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void btnResetPrgRam_Click(object sender, EventArgs e)
|
|
{
|
|
CurrentRom = "";
|
|
this.Text = _windowTitle;
|
|
SetMemoryState(MemoryType.PrgRam, new byte[0x8000]);
|
|
}
|
|
|
|
private void btnImportPrgRam_Click(object sender, EventArgs e)
|
|
{
|
|
LoadFile("PRG RAM (*.pr)|*.pr", (file) => {
|
|
SetMemoryState(MemoryType.PrgRam, File.ReadAllBytes(file));
|
|
});
|
|
}
|
|
|
|
private void btnExportPrgRam_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFile("PRG RAM (*.pr)|*.pr", (file) => {
|
|
lock(_runLock) {
|
|
UpdateRam();
|
|
File.WriteAllBytes(file, CoreWrapper.getMemoryState(MemoryType.PrgRam));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void SetMemoryState(MemoryType type, byte[] data)
|
|
{
|
|
lock(_runLock) {
|
|
UpdateRam();
|
|
CoreWrapper.setMemoryState(type, data);
|
|
_chrRamData = CoreWrapper.getMemoryState(MemoryType.ChrRam);
|
|
_paletteData = CoreWrapper.getMemoryState(MemoryType.PaletteRam);
|
|
_spriteData = CoreWrapper.getMemoryState(MemoryType.SpriteRam);
|
|
_cpuRamData = CoreWrapper.getMemoryState(MemoryType.CpuRam);
|
|
_prgRamData = CoreWrapper.getMemoryState(MemoryType.PrgRam);
|
|
}
|
|
UpdateMemoryViews();
|
|
}
|
|
|
|
private void mnuShowLayer_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
this.ctrlChipDisplay.SetVisibleLayers(
|
|
mnuShowDiffusion.Checked, mnuShowGroundedDiffusion.Checked, mnuShowPoweredDiffusion.Checked,
|
|
mnuShowPolysilicon.Checked, mnuShowMetal.Checked, mnuShowProtection.Checked
|
|
);
|
|
}
|
|
|
|
private void mnuShowSimulationState_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
ctrlChipDisplay.ShowState = mnuShowSimulationState.Checked;
|
|
}
|
|
|
|
private void mnuRefreshSlow_Click(object sender, EventArgs e)
|
|
{
|
|
mnuRefreshSlow.Checked = true;
|
|
mnuRefreshNormal.Checked = mnuRefreshFast.Checked = false;
|
|
_refreshSpeed = 8000;
|
|
}
|
|
|
|
private void mnuRefreshNormal_Click(object sender, EventArgs e)
|
|
{
|
|
mnuRefreshNormal.Checked = true;
|
|
mnuRefreshSlow.Checked = mnuRefreshFast.Checked = false;
|
|
_refreshSpeed = 4000;
|
|
}
|
|
|
|
private void mnuRefreshFast_Click(object sender, EventArgs e)
|
|
{
|
|
mnuRefreshFast.Checked = true;
|
|
mnuRefreshSlow.Checked = mnuRefreshNormal.Checked = false;
|
|
_refreshSpeed = 2000;
|
|
}
|
|
|
|
private void mnuRun_Click(object sender, EventArgs e)
|
|
{
|
|
if(_autoRun) {
|
|
Stop();
|
|
} else {
|
|
mnuRun.Enabled = false;
|
|
mnuPause.Enabled = true;
|
|
_autoRun = true;
|
|
}
|
|
}
|
|
|
|
private void mnuReset_Click(object sender, EventArgs e)
|
|
{
|
|
Reset(true);
|
|
}
|
|
|
|
private void mnuPowerCycle_Click(object sender, EventArgs e)
|
|
{
|
|
if(CurrentRom.Length > 0) {
|
|
LoadRom(CurrentRom);
|
|
} else {
|
|
Reset(false);
|
|
}
|
|
}
|
|
|
|
private void mnuStep_Click(object sender, EventArgs e)
|
|
{
|
|
Stop();
|
|
Step(1);
|
|
}
|
|
|
|
private void mnuNextPixel_Click(object sender, EventArgs e)
|
|
{
|
|
Stop();
|
|
Step(8);
|
|
}
|
|
|
|
private void mnuNextScanline_Click(object sender, EventArgs e)
|
|
{
|
|
Stop();
|
|
Step(2728);
|
|
}
|
|
|
|
private void mnuNextFrame_Click(object sender, EventArgs e)
|
|
{
|
|
Stop();
|
|
Step(714736);
|
|
}
|
|
|
|
private void mnuExit_Click(object sender, EventArgs e)
|
|
{
|
|
this.Close();
|
|
}
|
|
|
|
private void mnuLoadRom_Click(object sender, EventArgs e)
|
|
{
|
|
using(OpenFileDialog ofd = new OpenFileDialog()) {
|
|
ofd.Filter = "*.nes|*.nes";
|
|
if(ofd.ShowDialog() == DialogResult.OK) {
|
|
CurrentRom = ofd.FileName;
|
|
LoadRom(CurrentRom);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LoadRom(string filename)
|
|
{
|
|
byte[] rom = File.ReadAllBytes(filename);
|
|
|
|
if((rom[6] & 0x08) == 0x08) {
|
|
CoreWrapper.setMirroringType(MirroringType.FourScreens);
|
|
} else {
|
|
CoreWrapper.setMirroringType((rom[6] & 0x01) == 0x01 ? MirroringType.Vertical : MirroringType.Horizontal);
|
|
}
|
|
|
|
int prgRamSize = rom[4]*0x4000;
|
|
int chrRamSize = rom[5]*0x2000;
|
|
byte[] prgRam = new byte[prgRamSize];
|
|
byte[] chrRam = new byte[chrRamSize];
|
|
Array.Copy(rom, 16, prgRam, 0, prgRamSize);
|
|
Array.Copy(rom, 16 + prgRamSize, chrRam, 0, chrRamSize);
|
|
|
|
if(prgRamSize == 0x4000) {
|
|
Array.Resize(ref prgRam, 0x8000);
|
|
Array.Copy(prgRam, 0, prgRam, 0x4000, 0x4000);
|
|
}
|
|
|
|
lock(_runLock) {
|
|
Stop();
|
|
Reset(false);
|
|
CoreWrapper.setMemoryState(MemoryType.ChrRam, chrRam);
|
|
CoreWrapper.setMemoryState(MemoryType.PrgRam, prgRam);
|
|
}
|
|
}
|
|
|
|
private void mnuSaveState_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFile("Save states (*.ss)|*.ss", (file) => {
|
|
lock(_runLock) {
|
|
UpdateRam();
|
|
File.WriteAllBytes(file, CoreWrapper.getMemoryState(MemoryType.FullState));
|
|
}
|
|
});
|
|
}
|
|
|
|
private void mnuLoadState_Click(object sender, EventArgs e)
|
|
{
|
|
LoadFile("Save states (*.ss)|*.ss", (file) => {
|
|
lock(_runLock) {
|
|
CoreWrapper.setMemoryState(MemoryType.FullState, File.ReadAllBytes(file));
|
|
_chrRamChanged = false;
|
|
_paletteRamChanged = false;
|
|
_spriteRamChanged = false;
|
|
_cpuRamChanged = false;
|
|
_prgRamChanged = false;
|
|
Step(1);
|
|
}
|
|
});
|
|
}
|
|
|
|
void SetMirroring(MirroringType mirroringType)
|
|
{
|
|
mnuMirrorHorizontal.Checked = mirroringType == MirroringType.Horizontal;
|
|
mnuMirrorVertical.Checked = mirroringType == MirroringType.Vertical;
|
|
mnuMirrorScreenA.Checked = mirroringType == MirroringType.ScreenAOnly;
|
|
mnuMirrorScreenB.Checked = mirroringType == MirroringType.ScreenBOnly;
|
|
mnuMirrorFourScreens.Checked = mirroringType == MirroringType.FourScreens;
|
|
|
|
CoreWrapper.setMirroringType(mirroringType);
|
|
}
|
|
|
|
private void mnuMirrorVertical_Click(object sender, EventArgs e)
|
|
{
|
|
SetMirroring(MirroringType.Vertical);
|
|
}
|
|
|
|
private void mnuMirrorHorizontal_Click(object sender, EventArgs e)
|
|
{
|
|
SetMirroring(MirroringType.Horizontal);
|
|
}
|
|
|
|
private void mnuMirrorScreenA_Click(object sender, EventArgs e)
|
|
{
|
|
SetMirroring(MirroringType.ScreenAOnly);
|
|
}
|
|
|
|
private void mnuMirrorScreenB_Click(object sender, EventArgs e)
|
|
{
|
|
SetMirroring(MirroringType.ScreenBOnly);
|
|
}
|
|
|
|
private void mnuMirrorFourScreens_Click(object sender, EventArgs e)
|
|
{
|
|
SetMirroring(MirroringType.FourScreens);
|
|
}
|
|
|
|
private void mnuPause_Click(object sender, EventArgs e)
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
private void hexViewer_SizeChanged(object sender, EventArgs e)
|
|
{
|
|
HexBox hexBox = (HexBox)sender;
|
|
if(hexBox.Width > 870) {
|
|
hexBox.BytesPerLine = 32;
|
|
} else {
|
|
hexBox.BytesPerLine = 16;
|
|
}
|
|
}
|
|
|
|
private void mnuAbout_Click(object sender, EventArgs e)
|
|
{
|
|
using(frmAbout frm = new frmAbout()) {
|
|
frm.ShowDialog();
|
|
}
|
|
}
|
|
|
|
private void mnuVideoOutputViewer_Click(object sender, EventArgs e)
|
|
{
|
|
if(_videoViewer == null) {
|
|
_videoViewer = new frmVideoViewer();
|
|
_videoViewer.StartPosition = FormStartPosition.Manual;
|
|
_videoViewer.Top = this.Top + this.Height / 2 - _videoViewer.Height / 2;
|
|
_videoViewer.Left = this.Left + this.Width / 2 - _videoViewer.Width / 2;
|
|
_videoViewer.Show(this);
|
|
_videoViewer.FormClosed += (s, evt) => {
|
|
_videoViewer = null;
|
|
};
|
|
_videoViewer.Focus();
|
|
} else {
|
|
_videoViewer.Focus();
|
|
}
|
|
}
|
|
|
|
private void HighlightSearch()
|
|
{
|
|
List<int> nodeNumbers = new List<int>();
|
|
|
|
foreach(string nodeName in txtFindNode.SelectedValues) {
|
|
int nodeNumber;
|
|
if(_chipDef.NodeNumberByName.TryGetValue(nodeName.Trim(), out nodeNumber)) {
|
|
nodeNumbers.Add(nodeNumber);
|
|
}
|
|
}
|
|
|
|
ctrlChipDisplay.HighlightNode(nodeNumbers, true);
|
|
}
|
|
|
|
private void txtFindNode_KeyUp(object sender, KeyEventArgs e)
|
|
{
|
|
if(e.KeyCode == Keys.Enter) {
|
|
HighlightSearch();
|
|
}
|
|
}
|
|
|
|
private void btnSearch_Click(object sender, EventArgs e)
|
|
{
|
|
HighlightSearch();
|
|
}
|
|
}
|
|
}
|