diff --git a/6502EmulatorFrontend/6502EmulatorFrontend.psess b/6502EmulatorFrontend/6502EmulatorFrontend.psess
new file mode 100644
index 0000000..b1b1a3c
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend.psess
@@ -0,0 +1,80 @@
+
+
+
+ 6502EmulatorFrontend.sln
+ Sampling
+ None
+ true
+ true
+ Timestamp
+ Cycles
+ 10000000
+ 10
+ 10
+
+ false
+
+
+
+ false
+ 500
+
+ \Memory\Pages/sec
+ \PhysicalDisk(_Total)\Avg. Disk Queue Length
+ \Processor(_Total)\% Processor Time
+
+
+
+ true
+ false
+ false
+
+ false
+
+
+ false
+
+
+
+ 6502EmulatorFrontend\obj\Release\6502EmulatorFrontend.exe
+ 01/01/0001 00:00:00
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ Executable
+ 6502EmulatorFrontend\bin\Release\6502EmulatorFrontend.exe
+ 6502EmulatorFrontend\bin\Release\
+
+
+ IIS
+ InternetExplorer
+ true
+ false
+
+ false
+
+
+ false
+
+ {C198497A-3D3E-4F01-A72B-642DAB6B11D6}|6502EmulatorFrontend\6502EmulatorFrontend.csproj
+ 6502EmulatorFrontend\6502EmulatorFrontend.csproj
+ 6502EmulatorFrontend
+
+
+
+
+ 6502EmulatorFrontend151126.vsp
+
+
+
+
+ :PB:{C198497A-3D3E-4F01-A72B-642DAB6B11D6}|6502EmulatorFrontend\6502EmulatorFrontend.csproj
+
+
+
\ No newline at end of file
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend.sln b/6502EmulatorFrontend/6502EmulatorFrontend.sln
new file mode 100644
index 0000000..1e8e8fb
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend.sln
@@ -0,0 +1,36 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.24627.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "6502EmulatorFrontend", "6502EmulatorFrontend\6502EmulatorFrontend.csproj", "{C198497A-3D3E-4F01-A72B-642DAB6B11D6}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6804C707-42F4-43D0-9C94-526B5A7DB885}"
+EndProject
+Global
+ GlobalSection(Performance) = preSolution
+ HasPerformanceSessions = true
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C198497A-3D3E-4F01-A72B-642DAB6B11D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C198497A-3D3E-4F01-A72B-642DAB6B11D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C198497A-3D3E-4F01-A72B-642DAB6B11D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C198497A-3D3E-4F01-A72B-642DAB6B11D6}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(TeamFoundationVersionControl) = preSolution
+ SccNumberOfProjects = 2
+ SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
+ SccTeamFoundationServer = https://luigithirty.visualstudio.com/defaultcollection
+ SccLocalPath0 = .
+ SccProjectUniqueName1 = 6502EmulatorFrontend\\6502EmulatorFrontend.csproj
+ SccProjectName1 = 6502EmulatorFrontend
+ SccLocalPath1 = 6502EmulatorFrontend
+ EndGlobalSection
+EndGlobal
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend.vssscc b/6502EmulatorFrontend/6502EmulatorFrontend.vssscc
new file mode 100644
index 0000000..6cb031b
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend.vssscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = ""
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/6502EmulatorFrontend.csproj b/6502EmulatorFrontend/6502EmulatorFrontend/6502EmulatorFrontend.csproj
new file mode 100644
index 0000000..8f2563e
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/6502EmulatorFrontend.csproj
@@ -0,0 +1,126 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C198497A-3D3E-4F01-A72B-642DAB6B11D6}
+ WinExe
+ Properties
+ _6502EmulatorFrontend
+ 6502EmulatorFrontend
+ v4.6
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+ SAK
+ SAK
+ SAK
+ SAK
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+ E:\WriteableBitmapEx\WPF\WriteableBitmapEx.Wpf.dll
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+
+
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/App.config b/6502EmulatorFrontend/6502EmulatorFrontend/App.config
new file mode 100644
index 0000000..8324aa6
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/App.xaml b/6502EmulatorFrontend/6502EmulatorFrontend/App.xaml
new file mode 100644
index 0000000..259fa8c
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/App.xaml.cs b/6502EmulatorFrontend/6502EmulatorFrontend/App.xaml.cs
new file mode 100644
index 0000000..7e29857
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace _6502EmulatorFrontend
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/Apple2Display.cs b/6502EmulatorFrontend/6502EmulatorFrontend/Apple2Display.cs
new file mode 100644
index 0000000..08738fd
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/Apple2Display.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+
+namespace _6502EmulatorFrontend
+{
+ class Apple2Display : IDisplay
+ {
+ public WriteableBitmap DisplayCanvas
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+
+ set
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public void onUpdateDisplay(object sender, EventArgs e)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/AppleDisplay.cs b/6502EmulatorFrontend/6502EmulatorFrontend/AppleDisplay.cs
new file mode 100644
index 0000000..21a483e
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/AppleDisplay.cs
@@ -0,0 +1,201 @@
+using _6502EmulatorFrontend.video;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace _6502EmulatorFrontend
+{
+ public class AppleDisplay : INotifyPropertyChanged, IDisplay
+ {
+ private CharacterSet _characters;
+ private readonly SynchronizationContext _syncContext;
+
+ public List DisplayRows;
+ public WriteableBitmap _displayCanvas;
+ public WriteableBitmap DisplayCanvas {
+ get { return _displayCanvas; }
+ set
+ {
+ if (value != _displayCanvas)
+ {
+ _displayCanvas = value;
+ OnPropertyChanged("DisplayCanvas");
+ }
+ }
+ }
+ int cursorPositionX;
+ int cursorPositionY;
+ bool cursorEnabled;
+
+ public AppleDisplay()
+ {
+ Characters = new CharacterSet();
+ DisplayCanvas = new WriteableBitmap(320, 192, 96, 96, PixelFormats.Bgr32, null);
+ DisplayRows = new List();
+ _syncContext = SynchronizationContext.Current;
+ cursorPositionX = 0;
+ cursorPositionY = 0;
+ cursorEnabled = false;
+ for(int i = 0; i < 24; i++)
+ {
+ DisplayRows.Insert(i, new DisplayRow());
+ }
+ }
+
+ public void PutCharacter(CharacterBitmap character)
+ {
+ SetTile(cursorPositionY, cursorPositionX, character); //(row,column)
+ if(cursorPositionX == 39)
+ {
+ CarriageReturn();
+ } else
+ {
+ cursorPositionX++;
+ }
+ }
+
+ public void CarriageReturn()
+ {
+ _syncContext.Post(o => SetTile(cursorPositionY, cursorPositionX, Characters.CharacterList[0x20]), null);
+
+ if (cursorEnabled)
+ {
+
+ cursorPositionX = 0;
+ cursorPositionY++;
+ if (cursorPositionY == 24)
+ {
+ cursorPositionY = 23;
+ DisplayRows.RemoveAt(0);
+ DisplayRows.Insert(23, new DisplayRow());
+ }
+ }
+ else
+ {
+ cursorPositionX = 0;
+ cursorPositionY++;
+ if (cursorPositionY == 24)
+ {
+ cursorPositionY = 23;
+ DisplayRows.RemoveAt(0);
+ DisplayRows.Insert(23, new DisplayRow());
+ }
+ }
+ }
+
+ public void onToggleCursor(M6502 sender, EventArgs e)
+ {
+ if (!cursorEnabled)
+ {
+ _syncContext.Post(o => SetTile(cursorPositionY, cursorPositionX, Characters.CharacterList[0x00]), null);
+ cursorEnabled = true;
+ }
+ else
+ {
+ _syncContext.Post(o => SetTile(cursorPositionY, cursorPositionX, Characters.CharacterList[0x20]), null);
+ cursorEnabled = false;
+ }
+
+ }
+
+ public void SetTile(int row, int column, CharacterBitmap character)
+ {
+ DisplayRows[row].Characters[column] = character;
+ DrawScreen();
+ }
+
+ private void DrawScreen()
+ {
+ for(int row = 0; row < 24; row++)
+ {
+ for(int column = 0; column < 40; column++)
+ {
+ var tile = DisplayRows[row].Characters[column].CharacterImage;
+
+ int offsetX = (column * 8);
+ int offsetY = (row * 8);
+ int stride = tile.PixelWidth * (tile.Format.BitsPerPixel / 8);
+ byte[] data = new byte[stride * tile.PixelHeight];
+ tile.CopyPixels(data, stride, 0);
+
+ DisplayCanvas.WritePixels(new Int32Rect(0, 0, 8, 8), data, stride, offsetX, offsetY);
+ }
+ }
+
+ }
+
+ public void onCharacterInDisplayBuffer(AppleDisplay sender, CharacterInDisplayBufferEventArgs e)
+ {
+ byte key = (byte)e.Character;
+
+ if (e.Character == 0x0d)
+ {
+ _syncContext.Post(o => CarriageReturn(), null);
+ return;
+ }
+
+ if ((key & 0x60) == 0x60)
+ {
+ key = (byte)(key & ~0x60);
+ }
+ if((key & 0x40) == 0x40)
+ {
+ key = (byte)(key & ~0x40);
+ }
+
+ _syncContext.Post(o => PutCharacter(Characters.CharacterList[key]), null);
+ }
+
+ public void onUpdateDisplay(object sender, EventArgs e)
+ {
+ byte outputBuffer = Interop.getOutputBuffer();
+ if (outputBuffer != 0x00)
+ {
+ onCharacterInDisplayBuffer(this, new CharacterInDisplayBufferEventArgs(outputBuffer));
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected virtual void OnPropertyChanged(string name)
+ {
+ var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
+ if (handler != null)
+ {
+ handler(this, new PropertyChangedEventArgs(name));
+ }
+ }
+
+ public CharacterSet Characters
+ {
+ get { return _characters; }
+ set
+ {
+ if (value != _characters)
+ {
+ _characters = value;
+ OnPropertyChanged("Characters");
+ }
+ }
+ }
+ }
+
+ public class DisplayRow
+ {
+ public List Characters = new List();
+
+ public DisplayRow()
+ {
+ for (int j = 0; j < 40; j++)
+ {
+ Characters.Insert(j, CharacterBitmap.BlankBitmap);
+ }
+ }
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/IDisplay.cs b/6502EmulatorFrontend/6502EmulatorFrontend/IDisplay.cs
new file mode 100644
index 0000000..0d94c8e
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/IDisplay.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+
+namespace _6502EmulatorFrontend
+{
+ interface IDisplay
+ {
+ void onUpdateDisplay(object sender, EventArgs e);
+ WriteableBitmap DisplayCanvas { get; set; }
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/Interop.cs b/6502EmulatorFrontend/6502EmulatorFrontend/Interop.cs
new file mode 100644
index 0000000..2b211e5
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/Interop.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _6502EmulatorFrontend
+{
+ public class Interop
+ {
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct InteropProcessorStatus
+ {
+ public ushort cycles; //cycles
+ public byte FLAG_SIGN; //N
+ public byte FLAG_OVERFLOW; //V
+ //-
+ public byte FLAG_BREAKPOINT; //B
+ public byte FLAG_DECIMAL; //D
+ public byte FLAG_INTERRUPT; //I
+ public byte FLAG_ZERO; //Z
+ public byte FLAG_CARRY; //C
+
+ public byte accumulator; //A
+ public byte index_x; //X
+ public byte index_y; //Y
+ public byte stack_pointer; //SP
+ public ushort program_counter; //PC
+ public ushort old_program_counter; //before this opcode ran
+
+ IntPtr last_opcode;
+ public string lastOpcodeAsString { get { return Marshal.PtrToStringAnsi(last_opcode); } }
+ };
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct MC6821Status
+ {
+ public byte KBD;
+ public byte KBDCR;
+ public byte DSP;
+ public byte DSPCR;
+ };
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool loadBinary([MarshalAs(UnmanagedType.LPStr)] string path, ushort address);
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern ushort getProgramCounter();
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern InteropProcessorStatus getProcessorStatus();
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void resetProcessor();
+
+ //Execution
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void doSingleStep();
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern MC6821Status getMC6821Status();
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern byte getOutputBuffer();
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr getMemoryRange(ushort baseAddr, ushort length);
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void resetCycleCounter();
+
+ [DllImport("C:/Users/Luigi/Documents/Visual Studio 2015/Projects/M6502EmulatorDll/Debug/M6502EmulatorDll.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void putKeyInBuffer(byte key);
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml b/6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml
new file mode 100644
index 0000000..902af10
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LightBlue
+
+
+
+
+
+
+
+
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml.cs b/6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml.cs
new file mode 100644
index 0000000..74edf73
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml.cs
@@ -0,0 +1,228 @@
+using _6502EmulatorFrontend.cpu;
+using _6502EmulatorFrontend.video;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Timers;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Threading;
+
+namespace _6502EmulatorFrontend
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ Thread M6502WorkerThread;
+ MainWindowViewModel vm = new MainWindowViewModel();
+ byte[] videoRom = File.ReadAllBytes("C:/apple/apple1.vid");
+
+ public MainWindow()
+ {
+ //Set up background thread
+ M6502WorkerThread = new Thread(() =>
+ {
+ Thread.CurrentThread.IsBackground = true;
+ vm.Processor.Run();
+ });
+
+ #region events
+
+ #endregion
+
+ //Set up viewmodel
+ vm.Processor = new M6502();
+ vm.DisplayGrid = new AppleDisplay();
+ vm.DisassembledOpcodes = new ObservableCollection();
+
+ //Set up events
+ vm.Processor.ProcessorStepCompleted += new M6502.ProcessorStepCompletedEventHandler(AfterProcessorStepCompleted);
+ vm.Processor.ToggleCursor += new M6502.ToggleCursorEventHandler(vm.DisplayGrid.onToggleCursor);
+ vm.Processor.UpdateDisplay += new M6502.UpdateDisplayEventHandler(vm.DisplayGrid.onUpdateDisplay);
+ vm.Processor.ExecutionStopped += new M6502.ExecutionStoppedEventHandler(onExecutionStopped);
+ TextCompositionManager.AddTextInputHandler(this, new TextCompositionEventHandler(OnTextComposition));
+
+ //Set up window
+ InitializeComponent();
+ binaryLoadedStatus.SetBinding(ContentProperty, new Binding("LoadSuccess"));
+ DataContext = vm;
+ }
+
+ private void OnTextComposition(object sender, TextCompositionEventArgs e)
+ {
+ Interop.putKeyInBuffer((byte)e.Text.ToUpper().ToCharArray()[0]);
+ }
+
+ private void btnLoadBinary_Click(object sender, RoutedEventArgs e)
+ {
+ Interop.loadBinary("C:/apple/apple1.rom", 0xFF00);
+ Interop.loadBinary("C:/apple/apple1basic.bin", 0xE000);
+ decodeGraphics();
+
+ Interop.resetProcessor();
+ vm.Processor.UpdateProperties(Interop.getProcessorStatus());
+
+ ushort length = 0xFFFE;
+ IntPtr memoryValuesPtr = Interop.getMemoryRange(0x0000, length);
+ byte[] result = new byte[length+1];
+ Marshal.Copy(memoryValuesPtr, result, 0, length);
+
+ Disassembly disassembly = new Disassembly(result);
+ disassembly.Begin(0xFF00);
+
+ while (disassembly.NextInstructionAddress < 0xFFFE)
+ {
+ vm.DisassembledOpcodes.Add(disassembly.ToDisassembledOpcode());
+ disassembly.Next();
+ }
+
+ btnRun.IsEnabled = true;
+ btnSingleStep.IsEnabled = true;
+
+ }
+
+ private void AfterProcessorStepCompleted(M6502 sender, EventArgs e)
+ {
+ UpdateDisassemblySelection(sender.ProgramCounter);
+ }
+
+ private void btnSingleStep_Click(object sender, RoutedEventArgs e)
+ {
+ vm.Processor.DoProcessorStep(null, null);
+ }
+
+ private void decodeGraphics()
+ {
+ for(int i=0; i < (videoRom.Length / 8); i++)
+ {
+ vm.DisplayGrid.Characters.CharacterList.Add(new CharacterBitmap(videoRom.Skip(i * 8).Take(8).ToArray()));
+ }
+ }
+
+ private void disableReadoutControls()
+ {
+ BindingOperations.ClearBinding(txtAccumulator, TextBox.TextProperty);
+ BindingOperations.ClearBinding(txtIndexX, TextBox.TextProperty);
+ BindingOperations.ClearBinding(txtIndexY, TextBox.TextProperty);
+ BindingOperations.ClearBinding(txtFlags, TextBox.TextProperty);
+ BindingOperations.ClearBinding(txtProgramCounter, TextBox.TextProperty);
+ BindingOperations.ClearBinding(txtStackPointer, TextBox.TextProperty);
+ BindingOperations.ClearBinding(lbDisassembly, ListBox.ItemsSourceProperty);
+ }
+
+ private void enableReadoutControls()
+ {
+ BindingOperations.SetBinding(txtAccumulator, TextBox.TextProperty, new Binding("Processor.Accumulator") { StringFormat = "{0:X2}" });
+ BindingOperations.SetBinding(txtIndexX, TextBox.TextProperty, new Binding("Processor.IndexX") { StringFormat = "{0:X2}" });
+ BindingOperations.SetBinding(txtIndexY, TextBox.TextProperty, new Binding("Processor.IndexY") { StringFormat = "{0:X2}" });
+ BindingOperations.SetBinding(txtFlags, TextBox.TextProperty, new Binding("Processor.Flags"));
+ BindingOperations.SetBinding(txtProgramCounter, TextBox.TextProperty, new Binding("Processor.ProgramCounter") { StringFormat = "{0:X4}" });
+ BindingOperations.SetBinding(txtStackPointer, TextBox.TextProperty, new Binding("Processor.StackPointer") { StringFormat = "{0:X2}" });
+ BindingOperations.SetBinding(lbDisassembly, ListBox.ItemsSourceProperty, new Binding("DisassembledOpcodes"));
+ }
+
+ private void onExecutionStopped(object sender, EventArgs e)
+ {
+ Dispatcher.Invoke(new Action(() => { vm.DisassembledOpcodes.Clear(); }));
+
+ ushort length = 0xFFFE;
+ IntPtr memoryValuesPtr = Interop.getMemoryRange(0x0000, length);
+ byte[] result = new byte[length + 1];
+ Marshal.Copy(memoryValuesPtr, result, 0, length);
+
+ Disassembly disassembly = new Disassembly(result);
+ disassembly.Begin((ushort)(vm.Processor.ProgramCounter - 0x100));
+
+ while (disassembly.NextInstructionAddress < (ushort)vm.Processor.ProgramCounter + 0x100)
+ {
+ Dispatcher.Invoke(new Action(() =>
+ {
+ vm.DisassembledOpcodes.Add(disassembly.ToDisassembledOpcode());
+ }));
+ disassembly.Next();
+ }
+
+ Dispatcher.Invoke(new Action(() =>
+ {
+ UpdateDisassemblySelection(vm.Processor.ProgramCounter);
+ }));
+
+ lbDisassembly.Dispatcher.Invoke(new Action(() => { enableReadoutControls(); }));
+ lbDisassembly.Dispatcher.Invoke(new Action(() => { tbDebugConsole.Text += "Breakpoint hit.\r\n"; }));
+ }
+
+ private void UpdateDisassemblySelection(ushort address)
+ {
+ //update disassembly
+ for (int i = 0; i < lbDisassembly.Items.Count; i++)
+ {
+ var item = (Disassembly.DisassembledOpcode)lbDisassembly.Items[i];
+ if (address == item.Address)
+ {
+ lbDisassembly.Dispatcher.Invoke(new Action(() => { lbDisassembly.SelectedIndex = i; }));
+ }
+ }
+ }
+
+ private void btnRun_Click(object sender, RoutedEventArgs e)
+ {
+ btnLoadBinary.IsEnabled = false;
+ btnRun.IsEnabled = false;
+ btnSingleStep.IsEnabled = false;
+ tbDebugConsole.IsEnabled = false;
+ disableReadoutControls();
+ M6502WorkerThread.Start();
+ }
+
+ private void btnBreak_Click(object sender, RoutedEventArgs e)
+ {
+ enableReadoutControls();
+ btnRun.IsEnabled = true;
+ btnSingleStep.IsEnabled = true;
+ }
+
+ private void tbDebugEntry_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter)
+ {
+ tbDebugConsole.Text += ">" + tbDebugEntry.Text + "\r\n";
+ Debug.WriteLine(tbDebugEntry.Text);
+ var split = tbDebugEntry.Text.Split();
+ if (split[0].Equals("bpset"))
+ {
+ var address = ushort.Parse(split[1], System.Globalization.NumberStyles.HexNumber);
+ vm.Processor.breakpointAddresses.Add(address);
+ tbDebugConsole.Text += String.Format("Breakpoint added: ${0}\r\n", address.ToString("X4"));
+
+ }
+ else if (split[0].Equals("bpunset"))
+ {
+ var address = ushort.Parse(split[1], System.Globalization.NumberStyles.HexNumber);
+ if (vm.Processor.breakpointAddresses.Contains(address))
+ {
+ vm.Processor.breakpointAddresses.Remove(address);
+ tbDebugConsole.Text += String.Format("Breakpoint removed: ${0}\r\n", address.ToString("X4"));
+ } else
+ {
+ tbDebugConsole.Text += String.Format("Breakpoint ${0} not found\r\n", address.ToString("X4"));
+ }
+ }
+ //TODO: bpdisable, bpenable
+ tbDebugEntry.Clear();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/MainWindowViewModel.cs b/6502EmulatorFrontend/6502EmulatorFrontend/MainWindowViewModel.cs
new file mode 100644
index 0000000..9966d1d
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/MainWindowViewModel.cs
@@ -0,0 +1,70 @@
+using _6502EmulatorFrontend.video;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+
+namespace _6502EmulatorFrontend
+{
+ public class MainWindowViewModel : INotifyPropertyChanged
+ {
+ private M6502 _processor;
+ private AppleDisplay _displayGrid;
+ private ObservableCollection _disassembledOpcodes;
+
+ #region INotifyPropertyChanged Implementation
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected virtual void OnPropertyChanged(string name)
+ {
+ var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
+ if (handler != null)
+ {
+ handler(this, new PropertyChangedEventArgs(name));
+ }
+ }
+
+ public AppleDisplay DisplayGrid
+ {
+ get { return _displayGrid; }
+ set
+ {
+ if (value != _displayGrid)
+ {
+ _displayGrid = value;
+ OnPropertyChanged("DisplayGrid");
+ }
+ }
+ }
+
+ public ObservableCollection DisassembledOpcodes
+ {
+ get { return _disassembledOpcodes; }
+ set
+ {
+ if(value != _disassembledOpcodes)
+ {
+ _disassembledOpcodes = value;
+ OnPropertyChanged("DisassembledOpcodes");
+ }
+ }
+ }
+
+ public M6502 Processor
+ {
+ get { return _processor; }
+ set
+ {
+ if (value != _processor)
+ {
+ _processor = value;
+ OnPropertyChanged("Processor");
+ }
+ }
+ }
+ #endregion
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/Properties/AssemblyInfo.cs b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3918bc0
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("6502EmulatorFrontend")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("6502EmulatorFrontend")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.Designer.cs b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..10d0772
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace _6502EmulatorFrontend.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("_6502EmulatorFrontend.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.resx b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.Designer.cs b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..1657c2c
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace _6502EmulatorFrontend.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.settings b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/cpu/Disassembly.cs b/6502EmulatorFrontend/6502EmulatorFrontend/cpu/Disassembly.cs
new file mode 100644
index 0000000..2347f78
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/cpu/Disassembly.cs
@@ -0,0 +1,1313 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _6502EmulatorFrontend.cpu
+{
+ public class Disassembly
+ {
+
+ public struct DisassembledOpcode
+ {
+ public ushort Address;
+ public byte Opcode;
+ public String InstructionName;
+ public List Operands;
+
+ public override String ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(String.Format("{0} - {1}", Address.ToString("X4"), InstructionName));
+ Operands.Reverse(); //absolute addresses are out of order
+ foreach(var operand in Operands)
+ {
+ List branchInstructions = new List() { "BCC", "BCS", "BEQ", "BMI", "BNE", "BPL", "BVC", "BVS" };
+
+ if (branchInstructions.Contains(InstructionName.Substring(0, 3)))
+ {
+ //if it's a branch instruction, use the operand to calculate the relative address destination
+ if((operand & 0x80) == 0x80)
+ {
+ //Relative branch backward
+ sb.Append(String.Format("{0}", (Address - ~operand - 0xFF).ToString("X2")));
+ }
+ else
+ {
+ //Relative branch forward
+ sb.Append(String.Format("{0}", (Address + operand).ToString("X2")));
+ }
+ } else
+ {
+ //Any other instruction
+ sb.Append(String.Format("{0}", operand.ToString("X2")));
+ }
+
+ }
+ return sb.ToString();
+ }
+ }
+
+ public byte[] debugMemory = new byte[65536];
+
+ public ushort Address;
+ public byte Opcode;
+ public String InstructionName;
+ public List Operands;
+ public ushort NextInstructionAddress;
+
+ public Disassembly(byte[] memory)
+ {
+ memory.CopyTo(debugMemory, 0);
+ }
+
+ public void Begin(ushort address)
+ {
+ NextInstructionAddress = address;
+ Next();
+ }
+
+ public void Next()
+ {
+ //cycle counts are inaccurate
+
+ Operands = new List();
+ Opcode = debugMemory[NextInstructionAddress];
+ Address = NextInstructionAddress;
+ int operandLength = 0;
+ int cycles = 0;
+
+ #region disassembly switch
+ switch (Opcode)
+ {
+ case 0x00: //BRK
+ {
+ cycles += 7;
+ InstructionName = "BRK"; //OK
+ operandLength = 0;
+ break;
+ }
+
+ case 0x01:
+ {
+ cycles += 6;
+ InstructionName = "ORA (ZP,X) $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x05:
+ {
+ cycles += 2;
+ InstructionName = "ORA $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x06:
+ {
+ cycles += 5;
+ InstructionName = "ASL $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x08: //PHP
+ {
+ cycles += 3;
+ InstructionName = "PHP";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x09:
+ {
+ cycles += 2;
+ InstructionName = "ORA #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x0A:
+ {
+ cycles += 2;
+ InstructionName = "ASL";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x0D:
+ {
+ cycles += 4;
+ InstructionName = "ORA $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x0E:
+ {
+ cycles += 6;
+ InstructionName = "ASL $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x10:
+ {
+ cycles += 2; //2; +1 if dest on same page; +2 if on dest on diff page
+ InstructionName = "BPL $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x11:
+ {
+ cycles += 5;
+ InstructionName = "ORA (M),Y $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x15:
+ {
+ cycles += 4;
+ InstructionName = "ORA ZP,X";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x16:
+ {
+ cycles += 6;
+ InstructionName = "ASL ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x18: //CLC
+ {
+ cycles += 2;
+ InstructionName = "CLC";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x19:
+ {
+ cycles += 4;
+ InstructionName = "ORA M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x1D:
+ {
+ cycles += 4;
+ InstructionName = "ORA M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x1E:
+ {
+ cycles += 7;
+ InstructionName = "ASL M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x20:
+ {
+ cycles += 6;
+ InstructionName = "JSR $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x21: //AND (M, X)
+ {
+ cycles += 6;
+ InstructionName = "AND (M,X) $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x24:
+ {
+ cycles += 3;
+ InstructionName = "BIT $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x25: //AND - AND zeropage
+ {
+ cycles += 2;
+ InstructionName = "AND $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x26:
+ {
+ cycles += 5;
+ InstructionName = "ROL $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x28: //PLP
+ {
+ cycles += 4;
+ InstructionName = "PLP";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x29: //AND - AND(M, immediate)
+ {
+ cycles += 2;
+ InstructionName = "AND #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x2A:
+ {
+ cycles += 2;
+ InstructionName = "ROL A";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x2C:
+ {
+ cycles += 4;
+ InstructionName = "BIT $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x2D:
+ {
+ cycles += 4;
+ InstructionName = "AND $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x2E:
+ {
+ cycles += 6;
+ InstructionName = "ROL $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x30:
+ {
+ cycles += 2;
+ InstructionName = "BMI $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x31: //AND (M),Y
+ {
+ cycles += 5;
+ InstructionName = "AND (M),Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x35: //AND,X - AND(A, zeropage+X)
+ {
+ cycles += 3;
+ InstructionName = "AND ZP,X";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x36:
+ {
+ cycles += 6;
+ InstructionName = "ROL ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x38: //SEC - set carry flag
+ {
+ cycles += 2;
+ InstructionName = "SEC";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x39: //AND M,Y
+ {
+ cycles += 4;
+ InstructionName = "AND M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x3D:
+ {
+ cycles += 4;
+ InstructionName = "AND M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x3E:
+ {
+ cycles += 7;
+ InstructionName = "ROL M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x40:
+ {
+ cycles += 6;
+ InstructionName = "RTI";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x41:
+ {
+ cycles += 6;
+ InstructionName = "EOR (ZP,X) $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x45:
+ {
+ cycles += 3;
+ InstructionName = "EOR $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x46:
+ {
+ cycles += 5;
+ InstructionName = "LSR $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x48:
+ {
+ cycles += 3;
+ InstructionName = "PHA";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x49:
+ {
+ cycles += 2;
+ InstructionName = "EOR #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x4A:
+ {
+ cycles += 2;
+ InstructionName = "LSR";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x4C: //JMP - unconditional branch
+ {
+ cycles += 3;
+ InstructionName = "JMP $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x4D:
+ {
+ cycles += 4;
+ InstructionName = "EOR $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x4E:
+ {
+ cycles += 6;
+ InstructionName = "LSR $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x50:
+ {
+ cycles += 2;
+ InstructionName = "BVC";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x51:
+ {
+ cycles += 5;
+ InstructionName = "EOR (ZP),Y $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x55:
+ {
+ cycles += 4;
+ InstructionName = "EOR ZP,X";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x56:
+ {
+ cycles += 6;
+ InstructionName = "LSR ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x58: //CLI
+ {
+ cycles += 2;
+ InstructionName = "CLI";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x59:
+ {
+ cycles += 4;
+ InstructionName = "EOR M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x5D:
+ {
+ cycles += 4;
+ InstructionName = "EOR M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x5E:
+ {
+ cycles += 7;
+ InstructionName = "LSR M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x60:
+ {
+ cycles += 6;
+ InstructionName = "RTS";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x61:
+ {
+ cycles += 6;
+ InstructionName = "ADC (ZP,X) $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x65:
+ {
+ cycles += 3;
+ InstructionName = "ADC ZP $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x66:
+ {
+ cycles += 5;
+ InstructionName = "ROR $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x68: //PLA - pop accumulator from stack
+ {
+ cycles += 4;
+ InstructionName = "PLA";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x69:
+ {
+ cycles += 2;
+ InstructionName = "ADC #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x6A:
+ {
+ cycles += 2;
+ InstructionName = "ROR A";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x6C: //JMP (M)
+ {
+ cycles += 5;
+ InstructionName = "JMP (M) $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x6D:
+ {
+ cycles += 4;
+ InstructionName = "ADC $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x6E:
+ {
+ cycles += 6;
+ InstructionName = "ROR $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x70:
+ {
+ cycles += 2;
+ InstructionName = "BVS $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x71:
+ {
+ cycles += 5;
+ InstructionName = "ADC (ZP),Y $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x75:
+ {
+ cycles += 4;
+ InstructionName = "ADC ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x76:
+ {
+ cycles += 6;
+ InstructionName = "ROR ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x78: //SEI - set interrupt flag
+ {
+ cycles += 2;
+ InstructionName = "SEI";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x7E:
+ {
+ cycles += 7;
+ InstructionName = "ROR M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x79:
+ {
+ cycles += 4;
+ InstructionName = "ADC M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x7D:
+ {
+ cycles += 4;
+ InstructionName = "ADC M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x81:
+ {
+ cycles += 6;
+ InstructionName = "STA (ZP,X)";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x84: //STY - store Y into zeropage memory - OK
+ {
+ cycles += 3;
+ InstructionName = "STY $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x85: //STA - store A into zeropage memory - OK
+ {
+ cycles += 3;
+ InstructionName = "STA $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x86: //STX - store X into zeropage memory - OK
+ {
+ cycles += 3;
+ InstructionName = "STX $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x88: //DEY - OK
+ {
+ cycles += 2;
+ InstructionName = "DEY";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x8A: //TXA - X -> A - OK
+ {
+ cycles += 2;
+ InstructionName = "TXA";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x8C:
+ {
+ cycles += 4;
+ InstructionName = "STY $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x8D:
+ {
+ cycles += 4;
+ InstructionName = "STA $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x8E:
+ {
+ cycles += 4;
+ InstructionName = "STX $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x90:
+ {
+ cycles += 2;
+ InstructionName = "BCC $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x91:
+ {
+ cycles += 6;
+ InstructionName = "STA (ZP),Y $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0x94:
+ {
+ cycles += 4;
+ InstructionName = "STY ZP,X $"; //OK
+ operandLength = 1;
+ break;
+ }
+
+ case 0x95:
+ {
+ cycles += 4;
+ InstructionName = "STA ZP,X $"; //OK
+ operandLength = 1;
+ break;
+ }
+
+ case 0x96:
+ {
+ cycles += 4;
+ InstructionName = "STX ZP,Y $"; //OK
+ operandLength = 1;
+ break;
+ }
+
+ case 0x98: //TYA - Y -> A - OK
+ {
+ cycles += 2;
+ InstructionName = "TYA";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x99:
+ {
+ cycles += 5;
+ InstructionName = "STA M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0x9A: //TXS - X -> SP - OK
+ {
+ cycles += 2;
+ InstructionName = "TXS";
+ operandLength = 0;
+ break;
+ }
+
+ case 0x9D:
+ {
+ cycles += 5;
+ InstructionName = "STA M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xA0: //LDY - immediate - OK
+ {
+ cycles += 2;
+ InstructionName = "LDY #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xA1:
+ {
+ cycles += 6;
+ InstructionName = "LDA (ZP,X)";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xA2: //LDX - immediate - OK
+ {
+ cycles += 2;
+ InstructionName = "LDX #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xA4:
+ {
+ cycles += 3;
+ InstructionName = "LDY $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xA5: //LDA - Zeropage - Load A from zeropage operand.
+ {
+ cycles += 3;
+ InstructionName = "LDA $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xA6:
+ {
+ cycles += 3;
+ InstructionName = "LDX $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xA8: //TAY - OK
+ {
+ cycles += 2;
+ InstructionName = "TAY";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xA9: //LDA - Immediate - Load A from immediate value - OK
+ {
+ cycles += 2;
+ InstructionName = "LDA #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xAA: //TAX - A -> X - OK
+ {
+ cycles += 2;
+ InstructionName = "TAX";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xAC:
+ {
+ cycles += 4;
+ InstructionName = "LDY $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xAD:
+ {
+ cycles += 4;
+ InstructionName = "LDA $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xAE:
+ {
+ cycles += 4;
+ InstructionName = "LDX $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xB0:
+ {
+ cycles += 2;
+ InstructionName = "BCS $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xB1:
+ {
+ cycles += 5;
+ InstructionName = "LDA (ZP),Y $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xB4:
+ {
+ cycles += 4;
+ InstructionName = "LDY ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xB5: //LDA,X - Load A from Zeropage+X value
+ {
+ cycles += 4;
+ InstructionName = "LDA ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xB6:
+ {
+ cycles += 4;
+ InstructionName = "LDX ZP,Y $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xB8: //CLV - OK
+ {
+ cycles += 2;
+ InstructionName = "CLV";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xB9: //LDA Absolute,Y - OK
+ {
+ cycles += 4;
+ InstructionName = "LDA M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xBA: //TSX - SP -> X - OK
+ {
+ cycles += 2;
+ InstructionName = "TSX";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xBC:
+ {
+ cycles += 4;
+ InstructionName = "LDY M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xBD: //LDA - Absolute,X - OK
+ {
+ cycles += 4;
+ InstructionName = "LDA M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xBE:
+ {
+ cycles += 4;
+ InstructionName = "LDX M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xC0:
+ {
+ cycles += 2;
+ InstructionName = "CPY #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xC1:
+ {
+ cycles += 6;
+ InstructionName = "CMP (ZP,X)";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xC4:
+ {
+ cycles += 3;
+ InstructionName = "CPY $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xC5:
+ {
+ cycles += 3;
+ InstructionName = "CMP $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xC6:
+ {
+ cycles += 5;
+ InstructionName = "DEC $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xC8: //INY - increment Y - OK
+ {
+ cycles += 2;
+ InstructionName = "INY";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xC9:
+ {
+ cycles += 2;
+ InstructionName = "CMP #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xCA: //DEX - OK
+ {
+ cycles += 2;
+ InstructionName = "DEX";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xCC:
+ {
+ cycles += 4;
+ InstructionName = "CPY $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xCD:
+ {
+ cycles += 4;
+ InstructionName = "CMP $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xCE:
+ {
+ cycles += 6;
+ InstructionName = "DEC $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xD0: //BNE
+ {
+ cycles += 2;
+ InstructionName = "BNE $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xD1:
+ {
+ cycles += 5;
+ InstructionName = "CMP (ZP),Y";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xD5:
+ {
+ cycles += 4;
+ InstructionName = "CMP ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xD6:
+ {
+ cycles += 6;
+ InstructionName = "DEC ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xD8: //CLD - OK
+ {
+ cycles += 2;
+ InstructionName = "CLD";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xD9:
+ {
+ cycles += 4;
+ InstructionName = "CMP M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xDD:
+ {
+ cycles += 4;
+ InstructionName = "CMP M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xDE:
+ {
+ cycles += 7;
+ InstructionName = "DEC M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xE0:
+ {
+ cycles += 2;
+ InstructionName = "CPX #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xE1:
+ {
+ cycles += 6;
+ InstructionName = "SBC (ZP,X) $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xE4:
+ {
+ cycles += 3;
+ InstructionName = "CPX $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xE5:
+ {
+ cycles += 3;
+ InstructionName = "SBC $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xE6: //INC, increment zero-page memory address
+ {
+ cycles += 5;
+ InstructionName = "INC $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xE8: //INX - increment X - OK
+ {
+ cycles += 2;
+ InstructionName = "INX";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xE9:
+ {
+ cycles += 2;
+ InstructionName = "SBC #$";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xEA: //NOP - OK
+ {
+ cycles += 2;
+ InstructionName = "NOP";
+ break;
+ }
+
+ case 0xEC:
+ {
+ cycles += 4;
+ InstructionName = "CPX $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xED:
+ {
+ cycles += 4;
+ InstructionName = "SBC $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xEE:
+ {
+ cycles += 6;
+ InstructionName = "INC $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xF0:
+ {
+ cycles += 2;
+ InstructionName = "BEQ $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xF1:
+ {
+ cycles += 5;
+ InstructionName = "SBC (ZP),Y $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xF5:
+ {
+ cycles += 4;
+ InstructionName = "SBC ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xF6:
+ {
+ cycles += 6;
+ InstructionName = "INC ZP,X $";
+ operandLength = 1;
+ break;
+ }
+
+ case 0xF8: //SED - set decimal flag - OK
+ {
+ cycles += 2;
+ InstructionName = "SED";
+ operandLength = 0;
+ break;
+ }
+
+ case 0xF9:
+ {
+ cycles += 4;
+ InstructionName = "SBC M,Y $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xFD:
+ {
+ cycles += 4;
+ InstructionName = "SBC M,X $";
+ operandLength = 2;
+ break;
+ }
+
+ case 0xFE:
+ {
+ cycles += 7;
+ InstructionName = "INC M,X $";
+ operandLength = 2;
+ break;
+ }
+ }
+ #endregion
+
+ NextInstructionAddress++;
+
+ for (int i = 0; i < operandLength; i++)
+ {
+ Operands.Add(debugMemory[NextInstructionAddress]);
+ NextInstructionAddress++;
+ }
+
+ }
+
+ public DisassembledOpcode ToDisassembledOpcode()
+ {
+ DisassembledOpcode dis = new DisassembledOpcode();
+ dis.Address = Address;
+ dis.InstructionName = InstructionName;
+ dis.Opcode = Opcode;
+ dis.Operands = Operands;
+ return dis;
+ }
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502.cs b/6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502.cs
new file mode 100644
index 0000000..8fc86d3
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502.cs
@@ -0,0 +1,355 @@
+using _6502EmulatorFrontend.cpu;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Threading;
+
+namespace _6502EmulatorFrontend
+{
+ public class CharacterInDisplayBufferEventArgs : EventArgs
+ {
+ private readonly byte _character;
+
+ public CharacterInDisplayBufferEventArgs(byte character)
+ {
+ _character = character;
+ }
+
+ public byte Character { get { return _character; } }
+ }
+
+ public partial class M6502 : INotifyPropertyChanged
+ {
+ #region events
+ public event ProcessorStepCompletedEventHandler ProcessorStepCompleted;
+ public delegate void ProcessorStepCompletedEventHandler(M6502 sender, EventArgs e);
+
+ public event ToggleCursorEventHandler ToggleCursor;
+ public delegate void ToggleCursorEventHandler(M6502 sender, EventArgs e);
+
+ public event ProcessorClockTickEventHandler ProcessorClockTick;
+ public delegate void ProcessorClockTickEventHandler(object sender, EventArgs e);
+
+ public event UpdateDisplayEventHandler UpdateDisplay;
+ public delegate void UpdateDisplayEventHandler(object sender, EventArgs e);
+
+ public event ExecutionStoppedEventHandler ExecutionStopped;
+ public delegate void ExecutionStoppedEventHandler(object sender, EventArgs e);
+
+ // Invoke the Changed event; called whenever list changes:
+ protected virtual void OnProcessorStepCompleted(EventArgs e)
+ {
+ if (ProcessorStepCompleted != null)
+ ProcessorStepCompleted(this, e);
+ }
+
+ protected virtual void OnProcessorClockTick(EventArgs e)
+ {
+ if (ProcessorClockTick != null)
+ ProcessorClockTick(this, e);
+ }
+ protected virtual void OnToggleCursor(EventArgs e)
+ {
+ if (ToggleCursor != null)
+ ToggleCursor(this, e);
+ }
+ protected virtual void OnExecutionStopped(EventArgs e)
+ {
+ if (ExecutionStopped != null)
+ ExecutionStopped(this, e);
+ }
+ #endregion
+
+ M6502Model model = new M6502Model();
+ public List breakpointAddresses = new List();
+ bool shouldRun = true;
+
+ public void UpdateProperties(Interop.InteropProcessorStatus newStatus)
+ {
+ Accumulator = newStatus.accumulator;
+ IndexX = newStatus.index_x;
+ IndexY = newStatus.index_y;
+ StackPointer = newStatus.stack_pointer;
+ ProgramCounter = newStatus.program_counter;
+ Cycles = newStatus.cycles;
+
+ model.FLAG_BREAKPOINT = newStatus.FLAG_BREAKPOINT == 0x01 ? true : false;
+ model.FLAG_CARRY = newStatus.FLAG_CARRY == 0x01 ? true : false;
+ model.FLAG_DECIMAL = newStatus.FLAG_DECIMAL == 0x01 ? true : false;
+ model.FLAG_INTERRUPT = newStatus.FLAG_INTERRUPT == 0x01 ? true : false;
+ model.FLAG_OVERFLOW = newStatus.FLAG_OVERFLOW == 0x01 ? true : false;
+ model.FLAG_SIGN = newStatus.FLAG_SIGN == 0x01 ? true : false;
+ model.FLAG_ZERO = newStatus.FLAG_ZERO == 0x01 ? true : false;
+ OnPropertyChanged("Flags");
+ }
+
+ public void UpdateModelDirectly(Interop.InteropProcessorStatus newStatus)
+ {
+ model.accumulator = newStatus.accumulator;
+ model.indexX = newStatus.index_x;
+ model.indexY = newStatus.index_y;
+ model.stackPointer = newStatus.stack_pointer;
+ model.programCounter = newStatus.program_counter;
+ model.lastOpcode = newStatus.lastOpcodeAsString;
+ model.cycles = newStatus.cycles;
+
+ model.FLAG_BREAKPOINT = newStatus.FLAG_BREAKPOINT == 0x01 ? true : false;
+ model.FLAG_CARRY = newStatus.FLAG_CARRY == 0x01 ? true : false;
+ model.FLAG_DECIMAL = newStatus.FLAG_DECIMAL == 0x01 ? true : false;
+ model.FLAG_INTERRUPT = newStatus.FLAG_INTERRUPT == 0x01 ? true : false;
+ model.FLAG_OVERFLOW = newStatus.FLAG_OVERFLOW == 0x01 ? true : false;
+ model.FLAG_SIGN = newStatus.FLAG_SIGN == 0x01 ? true : false;
+ model.FLAG_ZERO = newStatus.FLAG_ZERO == 0x01 ? true : false;
+ }
+
+ public void DoProcessorStep(object sender, EventArgs e)
+ {
+ Interop.doSingleStep();
+ Interop.MC6821Status status = Interop.getMC6821Status();
+ UpdateProperties(Interop.getProcessorStatus());
+ ProcessorStepCompleted(this, null);
+ UpdateDisplay(this, null);
+ }
+
+ public void DoProcessorStepNoFancyGraphics(object sender, EventArgs e)
+ {
+ if (breakpointAddresses.Contains(model.programCounter))
+ {
+ shouldRun = false;
+ }
+ Interop.doSingleStep();
+ Interop.MC6821Status status = Interop.getMC6821Status();
+ UpdateModelDirectly(Interop.getProcessorStatus());
+ UpdateDisplay(this, null);
+ }
+
+ async public void DoProcessorStepInBackground()
+ {
+ await Task.Run(() =>
+ {
+ Interop.doSingleStep();
+ Interop.MC6821Status status = Interop.getMC6821Status();
+ UpdateProperties(Interop.getProcessorStatus());
+ UpdateDisplay(this, null);
+ });
+ }
+
+ public void Run()
+ {
+ int frameCounter = 0;
+
+ while (shouldRun)
+ {
+ if(frameCounter % 30 == 0 && frameCounter != 0)
+ {
+ ToggleCursor(this, null);
+ frameCounter = 0;
+ }
+ ExecuteFrame();
+ frameCounter++;
+ }
+
+ //Suspend execution.
+ OnPropertyChanged("Flags");
+ ExecutionStopped(this, null);
+ Thread.Sleep(Timeout.Infinite);
+ }
+
+ public void ExecuteFrame()
+ {
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ while (model.cycles < 17050) //17050 cycles per frame, 1MHz @ 60fps
+ {
+ if (!shouldRun)
+ {
+ return;
+ }
+ else
+ {
+ DoProcessorStepNoFancyGraphics(null, null);
+ }
+ }
+
+ while (stopwatch.Elapsed < TimeSpan.FromMilliseconds(16))
+ {
+
+ };
+ Interop.resetCycleCounter();
+ model.cycles = 0;
+ }
+
+ #region INotifyPropertyChanged Implementation
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected virtual void OnPropertyChanged(string name)
+ {
+ var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
+ if (handler != null)
+ {
+ handler(this, new PropertyChangedEventArgs(name));
+ }
+ }
+
+ public Boolean LoadSuccess
+ {
+ get { return model.loadSuccess; }
+ set
+ {
+ if (value != model.loadSuccess)
+ {
+ model.loadSuccess = value;
+ OnPropertyChanged("LoadSuccess");
+ }
+ }
+ }
+
+ public ushort ProgramCounter
+ {
+ get { return model.programCounter; }
+ set
+ {
+ if (value != model.programCounter)
+ {
+ model.programCounter = value;
+ OnPropertyChanged("ProgramCounter");
+ }
+ }
+ }
+
+ public byte Accumulator
+ {
+ get { return model.accumulator; }
+ set
+ {
+ if (value != model.accumulator)
+ {
+ model.accumulator = value;
+ OnPropertyChanged("Accumulator");
+ }
+ }
+ }
+
+ public byte IndexX
+ {
+ get { return model.indexX; }
+ set
+ {
+ if (value != model.indexX)
+ {
+ model.indexX = value;
+ OnPropertyChanged("IndexX");
+ }
+ }
+ }
+
+ public byte IndexY
+ {
+ get { return model.indexY; }
+ set
+ {
+ if (value != model.indexY)
+ {
+ model.indexY = value;
+ OnPropertyChanged("IndexY");
+ }
+ }
+ }
+
+ public byte StackPointer
+ {
+ get { return model.stackPointer; }
+ set
+ {
+ if (value != model.stackPointer)
+ {
+ model.stackPointer = value;
+ OnPropertyChanged("StackPointer");
+ }
+ }
+ }
+
+ public String Flags
+ {
+ get
+ {
+ var sb = new StringBuilder();
+ if (model.FLAG_SIGN)
+ {
+ sb.Append("N");
+ } else
+ {
+ sb.Append("-");
+ }
+ if (model.FLAG_OVERFLOW)
+ {
+ sb.Append("V");
+ }
+ sb.Append("-"); //0x20 is not connected
+ if (model.FLAG_BREAKPOINT)
+ {
+ sb.Append("B");
+ }
+ else
+ {
+ sb.Append("-");
+ }
+ if (model.FLAG_DECIMAL)
+ {
+ sb.Append("D");
+ }
+ else
+ {
+ sb.Append("-");
+ }
+ if (model.FLAG_INTERRUPT)
+ {
+ sb.Append("I");
+ }
+ else
+ {
+ sb.Append("-");
+ }
+ if (model.FLAG_ZERO)
+ {
+ sb.Append("Z");
+ }
+ else
+ {
+ sb.Append("-");
+ }
+ if (model.FLAG_CARRY)
+ {
+ sb.Append("C");
+ }
+ else
+ {
+ sb.Append("-");
+ }
+ return sb.ToString();
+ }
+ set { OnPropertyChanged("Flags"); }
+ }
+
+
+ public ushort Cycles
+ {
+ get { return model.cycles; }
+ set
+ {
+ if (value != model.cycles)
+ {
+ model.cycles = value;
+ OnPropertyChanged("Cycles");
+ }
+ }
+ }
+ #endregion
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502Model.cs b/6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502Model.cs
new file mode 100644
index 0000000..6902c3e
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502Model.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _6502EmulatorFrontend
+{
+ public class M6502Model
+ {
+ public bool FLAG_SIGN = false; //N
+ public bool FLAG_OVERFLOW = false; //V
+ //-
+ public bool FLAG_BREAKPOINT = false; //B
+ public bool FLAG_DECIMAL = false; //D
+ public bool FLAG_INTERRUPT = false; //I
+ public bool FLAG_ZERO = false; //Z
+ public bool FLAG_CARRY = false; //C
+
+ public byte accumulator = 0; //A
+ public byte indexX = 0; //X
+ public byte indexY = 0; //Y
+ public byte stackPointer = 0; //SP
+ public ushort programCounter = 0; //PC
+ public ushort oldProgramCounter = 0;//before this opcode ran
+ public ushort cycles = 0; //cycle count of frame
+
+ public String lastOpcode = "";
+
+ public bool loadSuccess;
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/video/CharacterBitmap.cs b/6502EmulatorFrontend/6502EmulatorFrontend/video/CharacterBitmap.cs
new file mode 100644
index 0000000..e854642
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/video/CharacterBitmap.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace _6502EmulatorFrontend.video
+{
+ public class CharacterBitmap
+ {
+ public byte[] Character = new byte[8];
+ public WriteableBitmap CharacterImage;
+
+ public CharacterBitmap(byte[] data)
+ {
+ //Load the data into the byte array
+ for (int i = 0; i < 8; i++)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ Character[j] = data[i];
+ i++;
+ }
+ }
+
+ //Now generate a bitmap somehow
+ CharacterImage = new WriteableBitmap(8, 8, 96, 96, PixelFormats.Bgr32, null);
+ CharacterImage.Lock();
+ for(int i = 0; i < 8; i++)
+ {
+ if((Character[i] & 0x10) == 0x10)
+ {
+ CharacterImage.SetPixel(3, i, Colors.White);
+ } else
+ {
+ CharacterImage.SetPixel(3, i, Colors.Black);
+ }
+ if ((Character[i] & 0x08) == 0x08)
+ {
+ CharacterImage.SetPixel(4, i, Colors.White);
+ }
+ else
+ {
+ CharacterImage.SetPixel(4, i, Colors.Black);
+ }
+ if ((Character[i] & 0x04) == 0x04)
+ {
+ CharacterImage.SetPixel(5, i, Colors.White);
+ }
+ else
+ {
+ CharacterImage.SetPixel(5, i, Colors.Black);
+ }
+ if ((Character[i] & 0x02) == 0x02)
+ {
+ CharacterImage.SetPixel(6, i, Colors.White);
+ }
+ else
+ {
+ CharacterImage.SetPixel(6, i, Colors.Black);
+ }
+ if ((Character[i] & 0x01) == 0x01)
+ {
+ CharacterImage.SetPixel(7, i, Colors.White);
+ }
+ else
+ {
+ CharacterImage.SetPixel(7, i, Colors.Black);
+ }
+ }
+ CharacterImage.Unlock();
+ }
+
+ public static CharacterBitmap BlankBitmap { get
+ {
+
+ byte[] blankCharacter = new byte[64];
+ for(int i = 0; i < blankCharacter.Length; i++)
+ {
+ blankCharacter[i] = 0x00;
+ }
+ CharacterBitmap blank = new CharacterBitmap(blankCharacter);
+ return blank;
+ }
+ }
+
+ public void drawToDebugConsole()
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ StringBuilder graphic = new StringBuilder();
+ graphic.Append(" ");
+ if ((Character[i] & 0x10) == 0x10)
+ {
+ graphic.Append("X");
+ }
+ else {
+ graphic.Append(" ");
+ }
+ if ((Character[i] & 0x08) == 0x08)
+ {
+ graphic.Append("X");
+ }
+ else {
+ graphic.Append(" ");
+ }
+ if ((Character[i] & 0x04) == 0x04)
+ {
+ graphic.Append("X");
+ }
+ else {
+ graphic.Append(" ");
+ }
+ if ((Character[i] & 0x02) == 0x02)
+ {
+ graphic.Append("X");
+ }
+ else {
+ graphic.Append(" ");
+ }
+ if ((Character[i] & 0x01) == 0x01)
+ {
+ graphic.Append("X");
+ }
+ else {
+ graphic.Append(" ");
+ }
+ Console.WriteLine(graphic);
+ }
+ }
+ }
+}
diff --git a/6502EmulatorFrontend/6502EmulatorFrontend/video/CharacterSet.cs b/6502EmulatorFrontend/6502EmulatorFrontend/video/CharacterSet.cs
new file mode 100644
index 0000000..adae26c
--- /dev/null
+++ b/6502EmulatorFrontend/6502EmulatorFrontend/video/CharacterSet.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace _6502EmulatorFrontend.video
+{
+ public class CharacterSet
+ {
+ public List CharacterList = new List();
+ }
+}
diff --git a/M6502EmulatorDll/M6502EmulatorDll.sln b/M6502EmulatorDll/M6502EmulatorDll.sln
new file mode 100644
index 0000000..e8a7e4a
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.24627.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "M6502EmulatorDll", "M6502EmulatorDll\M6502EmulatorDll.vcxproj", "{1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Debug|x64.ActiveCfg = Debug|x64
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Debug|x64.Build.0 = Debug|x64
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Debug|x86.ActiveCfg = Debug|Win32
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Debug|x86.Build.0 = Debug|Win32
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Release|x64.ActiveCfg = Release|x64
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Release|x64.Build.0 = Release|x64
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Release|x86.ActiveCfg = Release|Win32
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/M6502EmulatorDll/M6502EmulatorDll/CPU.cpp b/M6502EmulatorDll/M6502EmulatorDll/CPU.cpp
new file mode 100644
index 0000000..64e86f4
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/CPU.cpp
@@ -0,0 +1,1491 @@
+#include "stdafx.h"
+#include "CPU.h"
+#include
+
+/*
+ MOS 6502 CPU instruction set simulator.
+ Passes the functional test ROMs I can find.
+
+ TODO: Inaccurate cycle counts (cycle counter isn't incremented when zero-page access wraps, maybe some other things)
+ TODO: Cycle-accurate timing instead of per-instruction timing (see above)
+*/
+
+//The master processor of instructions.
+void CPU::process_instruction() {
+ last_executed_opcode = "";
+ last_operand = 0x00;
+ old_program_counter = program_counter;
+ uint8_t opcode = fetch_memory_byte(program_counter);
+ execute_opcode(opcode);
+
+ if (opcode == 0x4C) {
+ loopCount++;
+ if (loopCount == 10) {
+ std::cout << "Infinite loop detected, aborting.";
+ std::exit(3);
+ }
+ }
+ else {
+ loopCount = 0;
+ }
+}
+
+void CPU::print_status(uint8_t opcode) {
+ std::cout << "[EXE] Opcode " << std::hex << unsigned(opcode) << " (" << last_executed_opcode.c_str() << unsigned(last_operand) << "):" << std::endl;
+ std::cout << "\tA=" << unsigned(accumulator) << std::setw(2) << " X=" << unsigned(index_x) << std::setw(2) << " Y=" << unsigned(index_y) << std::setw(2) << " SP=" << unsigned(stack_pointer) << " PC=" << unsigned(program_counter) << " ";
+
+ if (FLAG_SIGN) {
+ std::cout << "N";
+ }
+ else {
+ std::cout << "-";
+ }
+
+ if (FLAG_OVERFLOW) {
+ std::cout << "V";
+ }
+ else {
+ std::cout << "-";
+ }
+
+ std::cout << "-";
+
+ if (FLAG_BREAKPOINT) {
+ std::cout << "B";
+ }
+ else {
+ std::cout << "-";
+ }
+
+ if (FLAG_DECIMAL) {
+ std::cout << "D";
+ }
+ else {
+ std::cout << "-";
+ }
+
+ if (FLAG_INTERRUPT) {
+ std::cout << "I";
+ }
+ else {
+ std::cout << "-";
+ }
+
+ if (FLAG_ZERO) {
+ std::cout << "Z";
+ }
+ else {
+ std::cout << "-";
+ }
+
+ if (FLAG_CARRY) {
+ std::cout << "C";
+ }
+ else {
+ std::cout << "-";
+ }
+
+ std::cout << std::endl;
+
+}
+
+uint16_t CPU::getCycleCount() {
+ return cycles;
+}
+
+void CPU::execute_opcode(uint8_t opcode)
+{
+ switch (opcode) {
+
+ /* Addressing modes
+
+ - ABC # - Immediate - the operand is the next byte. advances PC by 2
+ - ABC #$ - Accumulator. advances PC by 1
+ - ABC ZP - Zero-Page - the operand refers to a location in zero-page memory. advances PC by 2
+ - ABC ZP,X - Zero-Page, X/Y indexed. advances PC by 2. If ZP+X > $FF, wrap to $00 of zero-page memory.
+ - ABC M - Absolute - the operand is a 16-bit memory location. advances PC by 3
+ - ABC M,X - Absolute, X/Y indexed. advances PC by 2. if Abs+X or Abs+Y > $FF, do not wrap to the beginning of the page.
+ - ABC (M) - Indirect - the operand is a 16-bit pointer. advances PC by 3
+ - ABC (M,X) - Indirect X-indexed. Calculate address from the pointer located at ZP+X. If ZP+X > $FF, wrap to $00 of zero-page memory.
+ - ABC (M),Y - Indirect Y-indexed. The pointer is at ZP. Add Y to the dereferenced destination address. Do not wrap.
+ */
+
+ case 0x00: //BRK
+ {
+ cycles += 7;
+ last_executed_opcode = "BRK";
+ program_counter++; //waste an instruction
+ op_brk();
+ break;
+ }
+
+ case 0x01:
+ {
+ cycles += 6;
+ last_executed_opcode = "ORA (ZP,X) $";
+ op_ora(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0x05:
+ {
+ cycles += 2;
+ last_executed_opcode = "ORA $";
+ op_ora(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x06:
+ {
+ cycles += 5;
+ last_executed_opcode = "ASL $";
+ op_asl(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x08: //PHP
+ {
+ cycles += 3;
+ last_executed_opcode = "PHP";
+ op_php();
+ break;
+ }
+
+ case 0x09:
+ {
+ cycles += 2;
+ last_executed_opcode = "ORA #$";
+ op_ora(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x0A:
+ {
+ cycles += 2;
+ last_executed_opcode = "ASL";
+ op_asl();
+ break;
+ }
+
+ case 0x0D:
+ {
+ cycles += 4;
+ last_executed_opcode = "ORA $";
+
+ uint8_t operand = fetch_operand(ADDRESSING_ABSOLUTE);
+ accumulator = accumulator | operand;
+ set_zero_and_sign_flags(accumulator);
+ break;
+ }
+
+ case 0x0E:
+ {
+ cycles += 6;
+ last_executed_opcode = "ASL $";
+ op_asl(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x10:
+ {
+ cycles += 2; //2; +1 if dest on same page; +2 if on dest on diff page
+ last_executed_opcode = "BPL $";
+ op_bpl(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x11:
+ {
+ cycles += 5;
+ last_executed_opcode = "ORA (M),Y $";
+ op_ora(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0x15:
+ {
+ cycles += 4;
+ last_executed_opcode = "ORA ZP,X";
+ op_ora(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x16:
+ {
+ cycles += 6;
+ last_executed_opcode = "ASL ZP,X $";
+ op_asl(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x18: //CLC
+ {
+ cycles += 2;
+ last_executed_opcode = "CLC";
+ op_clc();
+ break;
+ }
+
+ case 0x19:
+ {
+ cycles += 4;
+ last_executed_opcode = "ORA M,Y $";
+ op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0x1D:
+ {
+ cycles += 4;
+ last_executed_opcode = "ORA M,X $";
+ op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x1E:
+ {
+ cycles += 7;
+ last_executed_opcode = "ASL M,X $";
+ op_asl(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x20:
+ {
+ cycles += 6;
+ last_executed_opcode = "JSR $";
+ op_jsr(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x21: //AND (M, X)
+ {
+ cycles += 6;
+ last_executed_opcode = "AND (M,X) $";
+ op_and(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0x24:
+ {
+ cycles += 3;
+ last_executed_opcode = "BIT $";
+ op_bit(fetch_operand(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x25: //AND - AND zeropage
+ {
+ cycles += 2;
+ last_executed_opcode = "AND $";
+ op_and(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x26:
+ {
+ cycles += 5;
+ last_executed_opcode = "ROL $";
+ op_rol(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x28: //PLP
+ {
+ cycles += 4;
+ last_executed_opcode = "PLP";
+ op_plp();
+ break;
+ }
+
+ case 0x29: //AND - AND(M, immediate)
+ {
+ cycles += 2;
+ last_executed_opcode = "AND #$";
+ op_and(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x2A:
+ {
+ cycles += 2;
+ last_executed_opcode = "ROL A";
+ op_rol();
+ break;
+ }
+
+ case 0x2C:
+ {
+ cycles += 4;
+ last_executed_opcode = "BIT $";
+ op_bit(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x2D:
+ {
+ cycles += 4;
+ last_executed_opcode = "AND $";
+ op_and(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x2E:
+ {
+ cycles += 6;
+ last_executed_opcode = "ROL $";
+ op_rol(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x30:
+ {
+ cycles += 2;
+ last_executed_opcode = "BMI $";
+ op_bmi(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x31: //AND (M),Y
+ {
+ cycles += 5;
+ last_executed_opcode = "AND (M),Y $";
+ op_and(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0x35: //AND,X - AND(A, zeropage+X)
+ {
+ cycles += 3;
+ last_executed_opcode = "AND ZP,X";
+ op_and(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x36:
+ {
+ cycles += 6;
+ last_executed_opcode = "ROL ZP,X $";
+ op_rol(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x38: //SEC - set carry flag
+ {
+ cycles += 2;
+ last_executed_opcode = "SEC";
+ op_sec();
+ break;
+ }
+
+ case 0x39: //AND M,Y
+ {
+ cycles += 4;
+ last_executed_opcode = "AND M,Y $";
+ op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0x3D:
+ {
+ cycles += 4;
+ last_executed_opcode = "AND M,X $";
+ op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x3E:
+ {
+ cycles += 7;
+ last_executed_opcode = "ROL M,X $";
+ op_rol(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x40:
+ {
+ cycles += 6;
+ last_executed_opcode = "RTI";
+ op_rti();
+ break;
+ }
+
+ case 0x41:
+ {
+ cycles += 6;
+ last_executed_opcode = "EOR (ZP,X) $";
+ op_eor(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0x45:
+ {
+ cycles += 3;
+ last_executed_opcode = "EOR $";
+ op_eor(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x46:
+ {
+ cycles += 5;
+ last_executed_opcode = "LSR $";
+ op_lsr(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x48:
+ {
+ cycles += 3;
+ last_executed_opcode = "PHA";
+ op_pha();
+ break;
+ }
+
+ case 0x49:
+ {
+ cycles += 2;
+ last_executed_opcode = "EOR #$";
+ op_eor(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x4A:
+ {
+ cycles += 2;
+ last_executed_opcode = "LSR";
+ op_lsr();
+ break;
+ }
+
+ case 0x4C: //JMP - unconditional branch
+ {
+ cycles += 3;
+ last_executed_opcode = "JMP $";
+ op_jmp(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x4D:
+ {
+ cycles += 4;
+ last_executed_opcode = "EOR $";
+ op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x4E:
+ {
+ cycles += 6;
+ last_executed_opcode = "LSR $";
+ op_lsr(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x50:
+ {
+ cycles += 2;
+ last_executed_opcode = "BVC";
+ op_bvc(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x51:
+ {
+ cycles += 5;
+ last_executed_opcode = "EOR (ZP),Y $";
+ op_eor(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0x55:
+ {
+ cycles += 4;
+ last_executed_opcode = "EOR ZP,X";
+ op_eor(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x56:
+ {
+ cycles += 6;
+ last_executed_opcode = "LSR ZP,X $";
+ op_lsr(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x58: //CLI
+ {
+ cycles += 2;
+ last_executed_opcode = "CLI";
+ op_cli();
+ break;
+ }
+
+ case 0x59:
+ {
+ cycles += 4;
+ last_executed_opcode = "EOR M,Y $";
+ op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0x5D:
+ {
+ cycles += 4;
+ last_executed_opcode = "EOR M,X $";
+ op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x5E:
+ {
+ cycles += 7;
+ last_executed_opcode = "LSR M,X $";
+ op_lsr(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x60:
+ {
+ cycles += 6;
+ last_executed_opcode = "RTS";
+ op_rts();
+ break;
+ }
+
+ case 0x61:
+ {
+ cycles += 6;
+ last_executed_opcode = "ADC (ZP,X) $";
+ op_adc(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0x65:
+ {
+ cycles += 3;
+ last_executed_opcode = "ADC ZP $";
+ op_adc(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x66:
+ {
+ cycles += 5;
+ last_executed_opcode = "ROR $";
+ op_ror(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x68: //PLA - pop accumulator from stack
+ {
+ cycles += 4;
+ last_executed_opcode = "PLA";
+ op_pla();
+ break;
+ }
+
+ case 0x69:
+ {
+ cycles += 2;
+ last_executed_opcode = "ADC #$";
+ op_adc(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x6A:
+ {
+ cycles += 2;
+ last_executed_opcode = "ROR A";
+ op_ror();
+ break;
+ }
+
+ case 0x6C: //JMP (M)
+ {
+ cycles += 5;
+ last_executed_opcode = "JMP (M) $";
+ WideAddress pointer = fetch_operand_address(ADDRESSING_ABSOLUTE);
+ WideAddress destination = dereference_indirect_address(pointer);
+ op_jmp(destination);
+ break;
+ }
+
+ case 0x6D:
+ {
+ cycles += 4;
+ last_executed_opcode = "ADC M $";
+ op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x6E:
+ {
+ cycles += 6;
+ last_executed_opcode = "ROR $";
+ op_ror(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x70:
+ {
+ cycles += 2;
+ last_executed_opcode = "BVS $";
+ op_bvs(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x71:
+ {
+ cycles += 5;
+ last_executed_opcode = "ADC (ZP),Y $";
+ op_adc(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0x75:
+ {
+ cycles += 4;
+ last_executed_opcode = "ADC ZP,X $";
+ op_adc(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x76:
+ {
+ cycles += 6;
+ last_executed_opcode = "ROR ZP,X $";
+ op_ror(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x78: //SEI - set interrupt flag
+ {
+ cycles += 2;
+ last_executed_opcode = "SEI";
+ op_sei();
+ break;
+ }
+
+ case 0x7E:
+ {
+ cycles += 7;
+ last_executed_opcode = "ROR $,X $";
+ op_ror(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x79:
+ {
+ cycles += 4;
+ last_executed_opcode = "ADC M,Y $";
+ op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0x7D:
+ {
+ cycles += 4;
+ last_executed_opcode = "ADC M,X $";
+ op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0x81:
+ {
+ cycles += 6;
+ last_executed_opcode = "STA (ZP,X)";
+ op_sta(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0x84: //STY - store Y into zeropage memory - OK
+ {
+ cycles += 3;
+ last_executed_opcode = "STY $";
+ op_sty(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x85: //STA - store A into zeropage memory - OK
+ {
+ cycles += 3;
+ last_executed_opcode = "STA $";
+ op_sta(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x86: //STX - store X into zeropage memory - OK
+ {
+ cycles += 3;
+ last_executed_opcode = "STX $";
+ op_stx(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0x88: //DEY - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "DEY";
+ op_dey();
+ break;
+ }
+
+ case 0x8A: //TXA - X -> A - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "TXA";
+ op_txa();
+ break;
+ }
+
+ case 0x8C:
+ {
+ cycles += 4;
+ last_executed_opcode = "STY $";
+ op_sty(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x8D:
+ {
+ cycles += 4;
+ last_executed_opcode = "STA $";
+ op_sta(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x8E:
+ {
+ cycles += 4;
+ last_executed_opcode = "STX $";
+ op_stx(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0x90:
+ {
+ cycles += 2;
+ last_executed_opcode = "BCC $";
+ op_bcc(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0x91:
+ {
+ cycles += 6;
+ last_executed_opcode = "STA (ZP),Y $";
+ op_sta(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0x94:
+ {
+ cycles += 4;
+ last_executed_opcode = "STY ZP,X $"; //OK
+ op_sty(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x95:
+ {
+ cycles += 4;
+ last_executed_opcode = "STA ZP,X $"; //OK
+ op_sta(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0x96:
+ {
+ cycles += 4;
+ last_executed_opcode = "STX ZP,Y $"; //OK
+ op_stx(fetch_operand_address(ADDRESSING_ZEROPAGE_Y));
+ break;
+ }
+
+ case 0x98: //TYA - Y -> A - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "TYA";
+ op_tya();
+ break;
+ }
+
+ case 0x99:
+ {
+ cycles += 5;
+ last_executed_opcode = "STA M,Y $";
+ op_sta(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0x9A: //TXS - X -> SP - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "TXS";
+ op_txs();
+ break;
+ }
+
+ case 0x9D:
+ {
+ cycles += 5;
+ last_executed_opcode = "STA M,X $";
+ op_sta(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0xA0: //LDY - immediate - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "LDY #$";
+ op_ldy(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xA1:
+ {
+ cycles += 6;
+ last_executed_opcode = "LDA (ZP,X)";
+ op_lda(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0xA2: //LDX - immediate - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "LDX #$";
+ op_ldx(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xA4:
+ {
+ cycles += 3;
+ last_executed_opcode = "LDY $";
+ op_ldy(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xA5: //LDA - Zeropage - Load A from zeropage operand.
+ {
+ cycles += 3;
+ last_executed_opcode = "LDA $";
+ op_lda(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xA6:
+ {
+ cycles += 3;
+ last_executed_opcode = "LDX $";
+ op_ldx(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xA8: //TAY - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "TAY";
+ op_tay();
+ break;
+ }
+
+ case 0xA9: //LDA - Immediate - Load A from immediate value - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "LDA #$";
+ op_lda(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xAA: //TAX - A -> X - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "TAX";
+ op_tax();
+ break;
+ }
+
+ case 0xAC:
+ {
+ cycles += 4;
+ last_executed_opcode = "LDY $";
+ op_ldy(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xAD:
+ {
+ cycles += 4;
+ last_executed_opcode = "LDA $";
+ op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xAE:
+ {
+ cycles += 4;
+ last_executed_opcode = "LDX $";
+ op_ldx(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xB0:
+ {
+ cycles += 2;
+ last_executed_opcode = "BCS $";
+ op_bcs(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xB1:
+ {
+ cycles += 5;
+ last_executed_opcode = "LDA (ZP),Y $";
+ op_lda(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0xB4:
+ {
+ cycles += 4;
+ last_executed_opcode = "LDY ZP,X $";
+ op_ldy(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0xB5: //LDA,X - Load A from Zeropage+X value
+ {
+ cycles += 4;
+ last_executed_opcode = "LDA ZP,X $";
+ op_lda(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0xB6:
+ {
+ cycles += 4;
+ last_executed_opcode = "LDX ZP,Y $";
+ op_ldx(fetch_operand_address(ADDRESSING_ZEROPAGE_Y));
+ break;
+ }
+
+ case 0xB8: //CLV - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "CLV";
+ op_clv();
+ break;
+ }
+
+ case 0xB9: //LDA Absolute,Y - OK
+ {
+ cycles += 4;
+ last_executed_opcode = "LDA M,Y $";
+ op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0xBA: //TSX - SP -> X - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "TSX";
+ op_tsx();
+ break;
+ }
+
+ case 0xBC:
+ {
+ cycles += 4;
+ last_executed_opcode = "LDY M,X $";
+ op_ldy(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0xBD: //LDA - Absolute,X - OK
+ {
+ cycles += 4;
+ last_executed_opcode = "LDA M,X $";
+ op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0xBE:
+ {
+ cycles += 4;
+ last_executed_opcode = "LDX M,Y $";
+ op_ldx(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0xC0:
+ {
+ cycles += 2;
+ last_executed_opcode = "CPY #$";
+ op_cpy(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xC1:
+ {
+ cycles += 6;
+ last_executed_opcode = "CMP (ZP,X)";
+ op_cmp(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0xC4:
+ {
+ cycles += 3;
+ last_executed_opcode = "CPY $";
+ op_cpy(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xC5:
+ {
+ cycles += 3;
+ last_executed_opcode = "CMP $";
+ op_cmp(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xC6:
+ {
+ cycles += 5;
+ last_executed_opcode = "DEC $";
+ op_dec(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xC8: //INY - increment Y - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "INY";
+ op_iny();
+ break;
+ }
+
+ case 0xC9:
+ {
+ cycles += 2;
+ last_executed_opcode = "CMP #$";
+ op_cmp(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xCA: //DEX - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "DEX";
+ op_dex();
+ break;
+ }
+
+ case 0xCC:
+ {
+ cycles += 4;
+ last_executed_opcode = "CPY $";
+ op_cpy(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xCD:
+ {
+ cycles += 4;
+ last_executed_opcode = "CMP $";
+ op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xCE:
+ {
+ cycles += 6;
+ last_executed_opcode = "DEC $";
+ op_dec(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xD0: //BNE
+ {
+ cycles += 2;
+ last_executed_opcode = "BNE $";
+ op_bne(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xD1:
+ {
+ cycles += 5;
+ last_executed_opcode = "CMP (ZP),Y";
+ op_cmp(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0xD5:
+ {
+ cycles += 4;
+ last_executed_opcode = "CMP ZP,X $";
+ op_cmp(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0xD6:
+ {
+ cycles += 6;
+ last_executed_opcode = "DEC ZP,X $";
+ op_dec(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0xD8: //CLD - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "CLD";
+ op_cld();
+ break;
+ }
+
+ case 0xD9:
+ {
+ cycles += 4;
+ last_executed_opcode = "CMP M,Y $";
+ op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0xDD:
+ {
+ cycles += 4;
+ last_executed_opcode = "CMP M,X $";
+ op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0xDE:
+ {
+ cycles += 7;
+ last_executed_opcode = "DEC M,X $";
+ op_dec(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0xE0:
+ {
+ cycles += 2;
+ last_executed_opcode = "CPX #$";
+ op_cpx(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xE1:
+ {
+ cycles += 6;
+ last_executed_opcode = "SBC (ZP,X) $";
+ op_sbc(fetch_operand_address(ADDRESSING_INDIRECT_X));
+ break;
+ }
+
+ case 0xE4:
+ {
+ cycles += 3;
+ last_executed_opcode = "CPX $";
+ op_cpx(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xE5:
+ {
+ cycles += 3;
+ last_executed_opcode = "SBC $";
+ op_sbc(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xE6: //INC, increment zero-page memory address
+ {
+ cycles += 5;
+ last_executed_opcode = "INC $";
+ op_inc(fetch_operand_address(ADDRESSING_ZEROPAGE));
+ break;
+ }
+
+ case 0xE8: //INX - increment X - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "INX";
+ op_inx();
+ break;
+ }
+
+ case 0xE9:
+ {
+ cycles += 2;
+ last_executed_opcode = "SBC #$";
+ op_sbc(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xEA: //NOP - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "NOP";
+ break;
+ }
+
+ case 0xEC:
+ {
+ cycles += 4;
+ last_executed_opcode = "CPX $";
+ op_cpx(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xED:
+ {
+ cycles += 4;
+ last_executed_opcode = "SBC $";
+ op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xEE:
+ {
+ cycles += 6;
+ last_executed_opcode = "INC $";
+ op_inc(fetch_operand_address(ADDRESSING_ABSOLUTE));
+ break;
+ }
+
+ case 0xF0:
+ {
+ cycles += 2;
+ last_executed_opcode = "BEQ $";
+ op_beq(fetch_operand(ADDRESSING_IMMEDIATE));
+ break;
+ }
+
+ case 0xF1:
+ {
+ cycles += 5;
+ last_executed_opcode = "SBC (ZP),Y $";
+ op_sbc(fetch_operand_address(ADDRESSING_INDIRECT_Y));
+ break;
+ }
+
+ case 0xF5:
+ {
+ cycles += 4;
+ last_executed_opcode = "SBC ZP,X $";
+ op_sbc(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0xF6:
+ {
+ cycles += 6;
+ last_executed_opcode = "INC ZP,X $";
+ op_inc(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
+ break;
+ }
+
+ case 0xF8: //SED - set decimal flag - OK
+ {
+ cycles += 2;
+ last_executed_opcode = "SED";
+ op_sed();
+ break;
+ }
+
+ case 0xF9:
+ {
+ cycles += 4;
+ last_executed_opcode = "SBC M,Y $";
+ op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
+ break;
+ }
+
+ case 0xFD:
+ {
+ cycles += 4;
+ last_executed_opcode = "SBC M,X $";
+ op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ case 0xFE:
+ {
+ cycles += 7;
+ last_executed_opcode = "INC M,X $";
+ op_inc(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
+ break;
+ }
+
+ default:
+ printf("Illegal instruction %x! Aborting execution.", opcode);
+ std::exit(2);
+ }
+}
+
+//check some flags
+void CPU::set_zero_and_sign_flags(uint8_t data) {
+ if (data == 0) {
+ FLAG_ZERO = true;
+ }
+ else {
+ FLAG_ZERO = false;
+ }
+ if (data & 0x80) {
+ FLAG_SIGN = true;
+ }
+ else {
+ FLAG_SIGN = false;
+ }
+}
+
+/* Stack */
+
+//Push a value onto the stack and decrement SP.
+void CPU::stack_push(uint8_t data) {
+ MemoryMap::Instance()->write_byte(0x0100 + stack_pointer, data);
+ stack_pointer--;
+}
+
+//Increment SP and pop the value we find.
+uint8_t CPU::stack_pop() {
+ stack_pointer++;
+ uint8_t data = MemoryMap::Instance()->read_byte(0x0100 + stack_pointer);
+ return data;
+}
+
+/* Memory read/write */
+
+uint8_t CPU::fetch_memory_byte(WideAddress address, bool incrementPc) {
+
+ if (incrementPc) {
+ program_counter++;
+ }
+
+ return MemoryMap::Instance()->read_byte(address);
+}
+
+uint8_t CPU::fetch_memory_byte(uint16_t address, bool incrementPc) {
+ return fetch_memory_byte(uintAddressToWideAddress(address), incrementPc);
+}
+
+uint8_t CPU::fetch_zero_page_byte(uint8_t address) {
+ return MemoryMap::Instance()->read_zero_page_byte(address);
+}
+
+void CPU::write_memory_byte(uint16_t address, uint8_t data) {
+ MemoryMap::Instance()->write_byte(address, data);
+}
+
+void CPU::write_memory_byte(WideAddress address, uint8_t data) {
+ MemoryMap::Instance()->write_byte(address, data);
+}
+
+void CPU::write_zero_page_byte(uint8_t address, uint8_t data) {
+ MemoryMap::Instance()->write_zero_page_byte(address, data);
+}
+
+/* PSW */
+
+uint8_t CPU::getProgramStatusWord() {
+ uint8_t psw = 0x20;
+
+ if (FLAG_SIGN) {
+ psw += 0x80;
+ }
+
+ if (FLAG_OVERFLOW) {
+ psw += 0x40;
+ }
+
+ if (FLAG_BREAKPOINT) {
+ psw += 0x10;
+ }
+
+ if (FLAG_DECIMAL) {
+ psw += 0x08;
+ }
+
+ if (FLAG_INTERRUPT) {
+ psw += 0x04;
+ }
+
+ if (FLAG_ZERO) {
+ psw += 0x02;
+ }
+
+ if (FLAG_CARRY) {
+ psw += 0x01;
+ }
+
+ return psw;
+}
+
+void CPU::setProgramStatusWord(uint8_t psw) {
+ FLAG_SIGN = ((0x80 & psw) == 0x80);
+ FLAG_OVERFLOW = ((0x40 & psw) == 0x40);
+ FLAG_BREAKPOINT = ((0x10 & psw) == 0x10);
+ FLAG_DECIMAL = ((0x08 & psw) == 0x08);
+ FLAG_INTERRUPT = ((0x04 & psw) == 0x04);
+ FLAG_ZERO = ((0x02 & psw) == 0x02);
+ FLAG_CARRY = ((0x01 & psw) == 0x01);
+}
+
+/* Debug */
+
+uint8_t CPU::getAccumulator() {
+ return accumulator;
+}
+
+bool CPU::frameInstructionsComplete() {
+ return cycles >= maxCycles;
+}
+
+void CPU::resetCycleCounter() {
+ cycles = 0;
+}
+
+/* Interop */
+bool CPU::getSignFlag() {
+ return FLAG_SIGN;
+}
+bool CPU::getOverflowFlag() {
+ return FLAG_OVERFLOW;
+}
+bool CPU::getBreakpointFlag() {
+ return FLAG_BREAKPOINT;
+}
+bool CPU::getDecimalFlag() {
+ return FLAG_DECIMAL;
+}
+bool CPU::getInterruptFlag() {
+ return FLAG_INTERRUPT;
+}
+bool CPU::getZeroFlag() {
+ return FLAG_ZERO;
+}
+bool CPU::getCarryFlag() {
+ return FLAG_CARRY;
+}
+
+uint8_t CPU::getIndexX() {
+ return index_x;
+}
+uint8_t CPU::getIndexY() {
+ return index_y;
+}
+uint8_t CPU::getStackPointer() {
+ return stack_pointer;
+}
+uint16_t CPU::getProgramCounter() {
+ return program_counter;
+}
+uint16_t CPU::getOldProgramCounter() {
+ return old_program_counter;
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/CPU.h b/M6502EmulatorDll/M6502EmulatorDll/CPU.h
new file mode 100644
index 0000000..7bdcc42
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/CPU.h
@@ -0,0 +1,261 @@
+#include
+#include
+#include "memory.h"
+
+#pragma once
+class CPU {
+
+private:
+
+ //PSW
+ bool FLAG_SIGN; //N
+ bool FLAG_OVERFLOW; //V
+ //-
+ bool FLAG_BREAKPOINT; //B
+ bool FLAG_DECIMAL; //D
+ bool FLAG_INTERRUPT; //I
+ bool FLAG_ZERO; //Z
+ bool FLAG_CARRY; //C
+
+ //registers
+ uint8_t accumulator; //A
+ uint8_t index_x; //X
+ uint8_t index_y; //Y
+ uint8_t stack_pointer; //SP
+ uint16_t program_counter; //PC
+ uint16_t old_program_counter; //before this opcode ran
+
+ uint16_t last_operand;
+
+ int loopCount = 0;
+ int cycles = 0;
+ int maxCycles = 17050; //roughly 1MHz
+
+public:
+ std::string last_executed_opcode;
+ std::vector last_executed_opcodes;
+
+ enum ADDRESSING_MODE { ADDRESSING_IMMEDIATE, ADDRESSING_ABSOLUTE, ADDRESSING_ABSOLUTE_X, ADDRESSING_ABSOLUTE_Y, ADDRESSING_ZEROPAGE, ADDRESSING_ZEROPAGE_X, ADDRESSING_ZEROPAGE_Y, ADDRESSING_INDIRECT, ADDRESSING_INDIRECT_X, ADDRESSING_INDIRECT_Y };
+ enum INDEX_MODE { INDEX_NONE = 0x01, INDEX_X = 0x02, INDEX_Y = 0x03 };
+
+ bool frameInstructionsComplete();
+ void resetCycleCounter();
+
+ void reset_processor() {
+ FLAG_SIGN = false;
+ FLAG_OVERFLOW = false;
+ FLAG_BREAKPOINT = false;
+ FLAG_DECIMAL = false;
+ FLAG_INTERRUPT = true;
+ FLAG_ZERO = false;
+ FLAG_CARRY = false;
+ accumulator = 0;
+ index_x = 0;
+ index_y = 0;
+ stack_pointer = 0xFF;
+ program_counter = 0xFFFC; //apple
+ //program_counter = 0x0400; //test
+
+ execute_opcode(0x4c); //jump to reset vector destination
+ }
+
+ uint8_t getAccumulator();
+ void setAccumulator(uint8_t acc) {
+ accumulator = acc;
+ }
+
+ uint8_t getCarry() {
+ return FLAG_CARRY;
+ }
+ void setCarry(bool val) {
+ FLAG_CARRY = val;
+ }
+
+ bool getSignFlag();
+ bool getOverflowFlag();
+ bool getBreakpointFlag();
+ bool getDecimalFlag();
+ bool getInterruptFlag();
+ bool getZeroFlag();
+ bool getCarryFlag();
+
+ uint8_t getIndexX();
+ uint8_t getIndexY();
+ uint8_t getStackPointer();
+ uint16_t getProgramCounter();
+ uint16_t getOldProgramCounter();
+ uint16_t getCycleCount();
+
+ uint8_t getProgramStatusWord();
+ void setProgramStatusWord(uint8_t psw);
+
+ void process_instruction();
+ void execute_opcode(uint8_t opcode);
+ void set_zero_and_sign_flags(uint8_t data);
+
+ void stack_push(uint8_t data);
+ uint8_t stack_pop();
+
+ uint8_t fetch_memory_byte(WideAddress address, bool incrementPc = true);
+ uint8_t fetch_memory_byte(uint16_t address, bool incrementPc = true);
+ uint8_t fetch_zero_page_byte(uint8_t);
+
+
+ void write_memory_byte(uint16_t, uint8_t);
+ void write_memory_byte(WideAddress, uint8_t);
+ void write_zero_page_byte(uint8_t, uint8_t);
+
+ WideAddress fetch_dereferenced_zero_page_pointer(INDEX_MODE mode);
+ WideAddress dereference_indirect_address(uint8_t zero_page_pointer);
+ WideAddress dereference_indirect_address(WideAddress pointer);
+
+ uint8_t fetch_operand(ADDRESSING_MODE);
+ WideAddress fetch_operand_address(ADDRESSING_MODE);
+
+ uint8_t fetch_destination(ADDRESSING_MODE mode);
+
+ void print_status(uint8_t opcode);
+
+ /* Opcodes */
+ void CPU::op_adc(WideAddress address);
+
+ void CPU::op_adc(uint8_t immediate);
+
+ void CPU::op_and(WideAddress address);
+
+ void CPU::op_and(uint8_t immediate);
+
+ void CPU::op_asl(WideAddress address);
+
+ void CPU::op_asl();
+
+ void CPU::op_bcc(int8_t relative);
+
+ void CPU::op_bcs(int8_t relative);
+
+ void CPU::op_beq(int8_t relative);
+
+ void CPU::op_bit(WideAddress address);
+
+ void CPU::op_bmi(int8_t relative);
+
+ void CPU::op_bne(int8_t relative);
+
+ void CPU::op_bpl(int8_t relative);
+
+ void CPU::op_brk();
+
+ void CPU::op_bvc(int8_t relative);
+
+ void CPU::op_bvs(int8_t relative);
+
+ void CPU::op_clc();
+
+ void CPU::op_cld();
+
+ void CPU::op_cli();
+
+ void CPU::op_clv();
+
+ void CPU::op_cmp(uint8_t immediate);
+
+ void CPU::op_cmp(WideAddress address);
+
+ void CPU::op_cpx(uint8_t immediate);
+
+ void CPU::op_cpx(WideAddress address);
+
+ void CPU::op_cpy(uint8_t immediate);
+
+ void CPU::op_cpy(WideAddress address);
+
+ void CPU::op_dec(WideAddress address);
+
+ void CPU::op_dex();
+
+ void CPU::op_dey();
+
+ void CPU::op_eor(uint8_t immediate);
+
+ void CPU::op_eor(WideAddress address);
+
+ void CPU::op_inc(WideAddress address);
+
+ void CPU::op_inx();
+
+ void CPU::op_iny();
+
+ void CPU::op_jmp(WideAddress address);
+
+ void CPU::op_jsr(WideAddress address);
+
+ void CPU::op_lda(uint8_t immediate);
+
+ void CPU::op_lda(WideAddress address);
+
+ void CPU::op_ldx(uint8_t immediate);
+
+ void CPU::op_ldx(WideAddress address);
+
+ void CPU::op_ldy(uint8_t immediate);
+
+ void CPU::op_ldy(WideAddress address);
+
+ void CPU::op_lsr();
+
+ void CPU::op_lsr(WideAddress address);
+
+ void CPU::op_nop();
+
+ void CPU::op_ora(uint8_t immediate);
+
+ void CPU::op_ora(WideAddress address);
+
+ void CPU::op_pha();
+
+ void CPU::op_php();
+
+ void CPU::op_pla();
+
+ void CPU::op_plp();
+
+ void CPU::op_rol();
+
+ void CPU::op_rol(WideAddress address);
+
+ void CPU::op_ror();
+
+ void CPU::op_ror(WideAddress address);
+
+ void CPU::op_rti();
+
+ void CPU::op_rts();
+
+ void CPU::op_sbc(uint8_t immediate);
+
+ void CPU::op_sbc(WideAddress address);
+
+ void CPU::op_sec();
+
+ void CPU::op_sed();
+
+ void CPU::op_sei();
+
+ void CPU::op_sta(WideAddress address);
+
+ void CPU::op_stx(WideAddress address);
+
+ void CPU::op_sty(WideAddress address);
+
+ void CPU::op_tax();
+
+ void CPU::op_tay();
+
+ void CPU::op_tsx();
+
+ void CPU::op_txa();
+
+ void CPU::op_txs();
+
+ void CPU::op_tya();
+};
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.cpp b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.cpp
new file mode 100644
index 0000000..cdc855f
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.cpp
@@ -0,0 +1,25 @@
+// M6502EmulatorDll.cpp : Defines the exported functions for the DLL application.
+//
+
+#include "stdafx.h"
+extern "C" {
+#include "M6502EmulatorDll.h"
+}
+#include "api.h"
+
+
+// This is an example of an exported variable
+M6502EMULATORDLL_API int nM6502EmulatorDll=0;
+
+// This is an example of an exported function.
+M6502EMULATORDLL_API int fnM6502EmulatorDll(void)
+{
+ return 42;
+}
+
+// This is the constructor of a class that has been exported.
+// see M6502EmulatorDll.h for the class definition
+CM6502EmulatorDll::CM6502EmulatorDll()
+{
+ return;
+}
diff --git a/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.h b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.h
new file mode 100644
index 0000000..d5d0a0e
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.h
@@ -0,0 +1,22 @@
+// The following ifdef block is the standard way of creating macros which make exporting
+// from a DLL simpler. All files within this DLL are compiled with the M6502EMULATORDLL_EXPORTS
+// symbol defined on the command line. This symbol should not be defined on any project
+// that uses this DLL. This way any other project whose source files include this file see
+// M6502EMULATORDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
+// defined with this macro as being exported.
+
+#ifdef M6502EMULATORDLL_EXPORTS
+#define M6502EMULATORDLL_API __declspec(dllexport)
+#else
+#define M6502EMULATORDLL_API __declspec(dllimport)
+#endif
+
+// This class is exported from the M6502EmulatorDll.dll
+class M6502EMULATORDLL_API CM6502EmulatorDll {
+public:
+ CM6502EmulatorDll(void);
+ // TODO: add your methods here.
+};
+
+extern M6502EMULATORDLL_API int nM6502EmulatorDll;
+extern M6502EMULATORDLL_API int fnM6502EmulatorDll(void);
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj
new file mode 100644
index 0000000..ebe25e1
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj
@@ -0,0 +1,195 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}
+ Win32Proj
+ M6502EmulatorDll
+ 8.1
+
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+ Use
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+
+
+
+
+ Use
+ Level3
+ Disabled
+ _DEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ NDEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj.filters b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj.filters
new file mode 100644
index 0000000..56dabb3
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj.filters
@@ -0,0 +1,114 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {35d5de5f-71d1-4db7-9ec3-bdede37ebf8d}
+
+
+ {eb09f38d-23a9-4909-ad59-d3156f0f6db5}
+
+
+ {f2865ccc-4af8-46e5-b00d-f6a5e1272e5b}
+
+
+ {354499c0-35ea-48a1-a1ac-33758a6be2b9}
+
+
+ {e3dc3c1f-0d63-4139-afca-faddcb96d5e5}
+
+
+ {b3bf0606-427d-4fe6-9077-20682dc62fa5}
+
+
+ {485c3d9f-8556-4dab-a880-426e12c39d3d}
+
+
+
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Source Files\api
+
+
+ Header Files
+
+
+ Header Files\memory
+
+
+ Header Files\memory
+
+
+ Header Files\cpu
+
+
+ Header Files\memory
+
+
+ Header Files\io
+
+
+ Header Files\io
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files\api
+
+
+ Source Files
+
+
+ Source Files\memory
+
+
+ Source Files\memory
+
+
+ Source Files\memory
+
+
+ Source Files\cpu
+
+
+ Source Files\cpu
+
+
+ Source Files\io
+
+
+ Source Files\io
+
+
+
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/ReadMe.txt b/M6502EmulatorDll/M6502EmulatorDll/ReadMe.txt
new file mode 100644
index 0000000..0820876
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/ReadMe.txt
@@ -0,0 +1,40 @@
+========================================================================
+ DYNAMIC LINK LIBRARY : M6502EmulatorDll Project Overview
+========================================================================
+
+AppWizard has created this M6502EmulatorDll DLL for you.
+
+This file contains a summary of what you will find in each of the files that
+make up your M6502EmulatorDll application.
+
+
+M6502EmulatorDll.vcxproj
+ This is the main project file for VC++ projects generated using an Application Wizard.
+ It contains information about the version of Visual C++ that generated the file, and
+ information about the platforms, configurations, and project features selected with the
+ Application Wizard.
+
+M6502EmulatorDll.vcxproj.filters
+ This is the filters file for VC++ projects generated using an Application Wizard.
+ It contains information about the association between the files in your project
+ and the filters. This association is used in the IDE to show grouping of files with
+ similar extensions under a specific node (for e.g. ".cpp" files are associated with the
+ "Source Files" filter).
+
+M6502EmulatorDll.cpp
+ This is the main DLL source file.
+
+/////////////////////////////////////////////////////////////////////////////
+Other standard files:
+
+StdAfx.h, StdAfx.cpp
+ These files are used to build a precompiled header (PCH) file
+ named M6502EmulatorDll.pch and a precompiled types file named StdAfx.obj.
+
+/////////////////////////////////////////////////////////////////////////////
+Other notes:
+
+AppWizard uses "TODO:" comments to indicate parts of the source code you
+should add to or customize.
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/M6502EmulatorDll/M6502EmulatorDll/WideAddress.cpp b/M6502EmulatorDll/M6502EmulatorDll/WideAddress.cpp
new file mode 100644
index 0000000..e69de29
diff --git a/M6502EmulatorDll/M6502EmulatorDll/WideAddress.h b/M6502EmulatorDll/M6502EmulatorDll/WideAddress.h
new file mode 100644
index 0000000..9d8b2f8
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/WideAddress.h
@@ -0,0 +1,48 @@
+#pragma once
+#include "stdafx.h"
+#include
+
+struct WideAddress {
+ uint8_t high;
+ uint8_t low;
+
+ WideAddress add(uint8_t operand, bool wrap) {
+ if (wrap) {
+ low += operand;
+ }
+ else
+ {
+ uint8_t intermediate = low + operand;
+
+ if (intermediate < low) {
+ high++;
+ low += operand;
+ }
+ else {
+ low += operand;
+ }
+ }
+
+ return *this;
+ }
+
+ WideAddress(uint8_t hi, uint8_t lo) {
+ high = hi;
+ low = lo;
+ }
+
+ WideAddress(uint8_t lo) {
+ high = 0;
+ low = lo;
+ }
+
+ WideAddress() {
+ high = 0;
+ low = 0;
+ }
+
+ operator uint16_t() {
+ return this->low | (this->high << 8);
+ }
+
+};
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/addressing.cpp b/M6502EmulatorDll/M6502EmulatorDll/addressing.cpp
new file mode 100644
index 0000000..25a74c8
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/addressing.cpp
@@ -0,0 +1,230 @@
+#include "stdafx.h"
+#include "CPU.h"
+#include "memory.h"
+#include
+
+uint8_t CPU::fetch_operand(ADDRESSING_MODE mode) {
+
+ switch (mode) {
+ case ADDRESSING_IMMEDIATE:
+ {
+ uint8_t operand = fetch_memory_byte(program_counter, true);
+ last_operand = operand;
+ return operand;
+ }
+
+ case ADDRESSING_ZEROPAGE:
+ {
+ //TODO: Why is this here?
+ uint8_t address = fetch_operand(ADDRESSING_IMMEDIATE);
+ last_operand = address;
+ return address;
+ }
+
+ case ADDRESSING_ZEROPAGE_X:
+ {
+ uint8_t address = fetch_operand(ADDRESSING_IMMEDIATE);
+ uint8_t operand = fetch_zero_page_byte(address + index_x);
+ last_operand = address;
+ return operand;
+ }
+
+ case ADDRESSING_ZEROPAGE_Y:
+ {
+ uint8_t address = fetch_operand(ADDRESSING_IMMEDIATE);
+ uint8_t operand = fetch_zero_page_byte(address + index_y);
+ last_operand = address;
+ return operand;
+ }
+
+ case ADDRESSING_ABSOLUTE:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ uint8_t addrHigh = fetch_memory_byte(program_counter);
+ WideAddress address = { addrHigh, addrLow };
+ uint8_t operand = fetch_memory_byte(address, false);
+ last_operand = address;
+ return operand;
+ }
+
+ case ADDRESSING_ABSOLUTE_X:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ uint8_t addrHigh = fetch_memory_byte(program_counter);
+ WideAddress address = { addrHigh, addrLow };
+ uint8_t operand = fetch_memory_byte(address.add(index_x, false), false);
+ last_operand = address;
+ return operand;
+ }
+
+ case ADDRESSING_ABSOLUTE_Y:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ uint8_t addrHigh = fetch_memory_byte(program_counter);
+ WideAddress address = { addrHigh, addrLow };
+ uint8_t operand = fetch_memory_byte(address.add(index_y, false), false);
+ last_operand = address;
+ return operand;
+ }
+
+ case ADDRESSING_INDIRECT_X:
+ {
+ WideAddress operand_address = fetch_dereferenced_zero_page_pointer(INDEX_X);
+ last_operand = operand_address;
+ uint8_t operand = fetch_memory_byte(operand_address, false);
+ last_operand = operand;
+ return operand;
+ }
+
+ case ADDRESSING_INDIRECT_Y:
+ {
+ WideAddress destination = fetch_dereferenced_zero_page_pointer(INDEX_NONE);
+ destination = destination.add(index_y, false);
+ uint8_t operand = fetch_memory_byte(destination);
+ last_operand = operand;
+ return operand;
+ }
+ default:
+ {
+ printf("fetch_operand(): Invalid addressing mode %d", mode);
+ std::exit(255);
+ }
+ }
+
+}
+
+WideAddress CPU::fetch_operand_address(ADDRESSING_MODE mode) {
+ switch (mode) {
+ case ADDRESSING_ABSOLUTE:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ uint8_t addrHigh = fetch_memory_byte(program_counter);
+ WideAddress address = { addrHigh, addrLow };
+ last_operand = address;
+ return address;
+ }
+ case ADDRESSING_ABSOLUTE_X:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ uint8_t addrHigh = fetch_memory_byte(program_counter);
+ WideAddress address = { addrHigh, addrLow };
+ last_operand = address;
+ address = address.add(index_x, false);
+ return address;
+ }
+ case ADDRESSING_ABSOLUTE_Y:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ uint8_t addrHigh = fetch_memory_byte(program_counter);
+ WideAddress address = { addrHigh, addrLow };
+ last_operand = address;
+ address = address.add(index_y, false);
+ return address;
+ }
+ case ADDRESSING_ZEROPAGE:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ WideAddress address = { 0, addrLow };
+ last_operand = address;
+ return address;
+ }
+ case ADDRESSING_ZEROPAGE_X:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ WideAddress address = { 0, uint8_t(addrLow + index_x) };
+ last_operand = addrLow;
+ return address;
+ }
+ case ADDRESSING_ZEROPAGE_Y:
+ {
+ uint8_t addrLow = fetch_memory_byte(program_counter);
+ WideAddress address = { 0, uint8_t(addrLow + index_y) };
+ last_operand = addrLow;
+ return address;
+ }
+ case ADDRESSING_INDIRECT:
+ {
+ uint8_t zpAddr = fetch_memory_byte(program_counter);
+ last_operand = zpAddr;
+ return dereference_indirect_address(zpAddr);
+
+ }
+ case ADDRESSING_INDIRECT_X: //(ZP,X)
+ {
+ uint8_t zpAddr = fetch_memory_byte(program_counter);
+ last_operand = zpAddr;
+ return dereference_indirect_address(zpAddr + index_x);
+ }
+ case ADDRESSING_INDIRECT_Y: //(ZP),Y
+ {
+ uint8_t zpAddr = fetch_memory_byte(program_counter);
+ last_operand = zpAddr;
+ return dereference_indirect_address(zpAddr).add(index_y, false);
+ }
+ default:
+ {
+ printf("fetch_operand_address(): Invalid addressing mode %d", mode);
+ std::exit(255);
+ }
+ }
+}
+
+uint8_t CPU::fetch_destination(ADDRESSING_MODE mode) {
+ switch (mode) {
+
+ case ADDRESSING_ZEROPAGE:
+ {
+ return fetch_operand(ADDRESSING_IMMEDIATE);
+ }
+ case ADDRESSING_ZEROPAGE_X:
+ {
+ return fetch_operand(ADDRESSING_IMMEDIATE) + index_x;
+ }
+ case ADDRESSING_ZEROPAGE_Y:
+ {
+ return fetch_operand(ADDRESSING_IMMEDIATE) + index_y;
+ }
+ default:
+ {
+ printf("fetch_destination(): Invalid addressing mode %d", mode);
+ std::exit(255);
+ }
+
+ }
+}
+
+WideAddress CPU::dereference_indirect_address(uint8_t zero_page_pointer) {
+ uint8_t addressLow = fetch_zero_page_byte(zero_page_pointer);
+ uint8_t addressHigh = fetch_zero_page_byte(zero_page_pointer + 1); //will wrap
+ return WideAddress{ addressHigh, addressLow };
+}
+
+WideAddress CPU::dereference_indirect_address(WideAddress pointer) {
+ uint8_t lo = fetch_memory_byte(pointer, false);
+ uint8_t hi = fetch_memory_byte(pointer.add(1, false), false);
+ WideAddress dereferenced = WideAddress{ hi, lo };
+ return dereferenced;
+}
+
+WideAddress CPU::fetch_dereferenced_zero_page_pointer(INDEX_MODE mode) { //(ZP) and (ZP,X)
+ uint8_t addrLow;
+ uint8_t addrHigh;
+ uint8_t zero_page_address = fetch_memory_byte(program_counter);
+
+ if (mode == INDEX_X) {
+ addrLow = fetch_zero_page_byte(zero_page_address + index_x);
+ addrHigh = fetch_zero_page_byte(zero_page_address + index_x + 1);
+ }
+ else if (mode == INDEX_NONE) {
+ addrLow = fetch_zero_page_byte(zero_page_address);
+ addrHigh = fetch_zero_page_byte(zero_page_address + 1);
+ }
+ else {
+ std::cout << "fetch_dereferenced_zero_page_pointer(): Implementation error, cannot use INDEX_Y mode with this function.";
+ std::exit(255);
+ }
+
+ WideAddress address = WideAddress{ addrHigh, addrLow };
+ return address;
+
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/api.cpp b/M6502EmulatorDll/M6502EmulatorDll/api.cpp
new file mode 100644
index 0000000..1975dc9
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/api.cpp
@@ -0,0 +1,90 @@
+#include "stdafx.h"
+#include "api.h"
+#include "state.h"
+#include
+
+M6502EMULATORDLL_API bool loadBinary(const char* path, uint16_t address) {
+ //MemoryMap::Instance()->load_binary(0xFF00, "E:/apple/apple1.rom");
+ //MemoryMap::Instance()->load_binary(0xE000, "E:/apple/apple1basic.bin");
+ //MemoryMap::Instance()->load_binary(0x0000, "E:/apple/test.bin");
+ //MemoryMap::Instance()->load_binary(0x0280, "E:/apple/adventur.bin");
+ MemoryMap::Instance()->load_binary(address, path);
+ return true;
+}
+
+M6502EMULATORDLL_API UINT16 getProgramCounter() {
+ return 0xFFFF;
+}
+
+M6502EMULATORDLL_API ProcessorStatus getProcessorStatus() {
+ ProcessorStatus status = ProcessorStatus();
+ status.FLAG_SIGN = EmulatorState::Instance()->processor.getSignFlag();
+ status.FLAG_OVERFLOW = EmulatorState::Instance()->processor.getOverflowFlag();
+ status.FLAG_BREAKPOINT = EmulatorState::Instance()->processor.getBreakpointFlag();
+ status.FLAG_DECIMAL = EmulatorState::Instance()->processor.getDecimalFlag();
+ status.FLAG_INTERRUPT = EmulatorState::Instance()->processor.getInterruptFlag();
+ status.FLAG_ZERO = EmulatorState::Instance()->processor.getZeroFlag();
+ status.FLAG_CARRY = EmulatorState::Instance()->processor.getCarryFlag();
+
+ status.accumulator = EmulatorState::Instance()->processor.getAccumulator();
+ status.index_x = EmulatorState::Instance()->processor.getIndexX();
+ status.index_y = EmulatorState::Instance()->processor.getIndexY();
+ status.stack_pointer = EmulatorState::Instance()->processor.getStackPointer();
+ status.program_counter = EmulatorState::Instance()->processor.getProgramCounter();
+ status.old_program_counter = EmulatorState::Instance()->processor.getOldProgramCounter();
+ status.cycles = EmulatorState::Instance()->processor.getCycleCount();
+ status.last_opcode = EmulatorState::Instance()->processor.last_executed_opcode.c_str();
+ //OutputDebugString(L"getProcessorStatus()\r\n");
+ return status;
+}
+
+M6502EMULATORDLL_API void resetProcessor() {
+ EmulatorState::Instance()->processor.reset_processor();
+}
+
+M6502EMULATORDLL_API void doSingleStep() {
+ EmulatorState::Instance()->processor.process_instruction();
+ EmulatorState::Instance()->mc6821.executionLoop();
+}
+
+M6502EMULATORDLL_API void resetCycleCounter() {
+ EmulatorState::Instance()->processor.resetCycleCounter();
+}
+
+M6502EMULATORDLL_API MC6821Status getMC6821Status() {
+ return EmulatorState::Instance()->mc6821.getStatus();
+}
+
+M6502EMULATORDLL_API uint8_t getOutputBuffer() {
+ return EmulatorState::Instance()->mc6821.getOutputBuffer();
+}
+
+M6502EMULATORDLL_API void putKeyInBuffer(uint8_t key) {
+ //Invert b6, set b5 to 0.
+ key = key | 0x80;
+
+// if ((key & 0x40) == 0x40) {
+// key = key & ~0x60;
+// }
+// else {
+// key = key | 0x60;
+// }
+
+ MemoryMap::Instance()->write_byte(WideAddress({ 0xD0, 0x10 }), key, true);
+}
+
+M6502EMULATORDLL_API uint8_t* getMemoryRange(uint16_t base, uint16_t length) {
+ uint8_t *memoryRange = new uint8_t[length+1];
+
+ WideAddress start = uintAddressToWideAddress(base);
+ WideAddress end = uintAddressToWideAddress(base + length);
+ WideAddress current = start;
+ int i = 0;
+ while (current <= end && !(wideAddressToUintAddress(current) == 0x0000 && wideAddressToUintAddress(end) == 0xFFFF)) {
+ memoryRange[i] = MemoryMap::Instance()->read_byte(current, false);
+ i++;
+ current.add(1, false);
+ }
+
+ return memoryRange;
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/api.h b/M6502EmulatorDll/M6502EmulatorDll/api.h
new file mode 100644
index 0000000..b810d53
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/api.h
@@ -0,0 +1,53 @@
+#pragma once
+#include "stdafx.h"
+#include "mc6821.h"
+#include
+
+
+#ifdef M6502EMULATORDLL_EXPORTS
+#define M6502EMULATORDLL_API __declspec(dllexport)
+#else
+#define M6502EMULATORDLL_API __declspec(dllimport)
+#endif
+
+extern "C" {
+ struct ProcessorStatus {
+ uint16_t cycles; //cycle count
+ uint8_t FLAG_SIGN; //N
+ uint8_t FLAG_OVERFLOW; //V
+ //-
+ uint8_t FLAG_BREAKPOINT; //B
+ uint8_t FLAG_DECIMAL; //D
+ uint8_t FLAG_INTERRUPT; //I
+ uint8_t FLAG_ZERO; //Z
+ uint8_t FLAG_CARRY; //C
+
+ uint8_t accumulator; //A
+ uint8_t index_x; //X
+ uint8_t index_y; //Y
+ uint8_t stack_pointer; //SP
+ uint16_t program_counter; //PC
+ uint16_t old_program_counter; //before this opcode ran
+
+ LPCSTR last_opcode;
+ };
+
+ extern M6502EMULATORDLL_API bool loadBinary(const char *path, uint16_t address);
+
+ //Processor information
+ extern M6502EMULATORDLL_API UINT16 getProgramCounter();
+ extern M6502EMULATORDLL_API ProcessorStatus getProcessorStatus();
+
+ //Processor actions
+ extern M6502EMULATORDLL_API void resetProcessor();
+ extern M6502EMULATORDLL_API void doSingleStep();
+ extern M6502EMULATORDLL_API void resetCycleCounter();
+
+ //MC6821
+ extern M6502EMULATORDLL_API MC6821Status getMC6821Status();
+ extern M6502EMULATORDLL_API uint8_t getOutputBuffer();
+ extern M6502EMULATORDLL_API void putKeyInBuffer(uint8_t);
+
+ //Memory
+ extern M6502EMULATORDLL_API uint8_t* getMemoryRange(uint16_t base, uint16_t length);
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/dllmain.cpp b/M6502EmulatorDll/M6502EmulatorDll/dllmain.cpp
new file mode 100644
index 0000000..69b5891
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/dllmain.cpp
@@ -0,0 +1,19 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "stdafx.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/M6502EmulatorDll/M6502EmulatorDll/mc6821.cpp b/M6502EmulatorDll/M6502EmulatorDll/mc6821.cpp
new file mode 100644
index 0000000..4d32d33
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/mc6821.cpp
@@ -0,0 +1,50 @@
+#include "stdafx.h"
+#include "mc6821.h"
+#include "state.h"
+
+void MC6821::executionLoop() {
+ //update the registers
+ kbd = MemoryMap::Instance()->read_byte(WideAddress{ 0xD0, 0x10 }, false);
+ kbdcr = MemoryMap::Instance()->read_byte(WideAddress{ 0xD0, 0x11 }, false);
+ dsp = MemoryMap::Instance()->read_byte(WideAddress{ 0xD0, 0x12 }, false);
+ dspcr = MemoryMap::Instance()->read_byte(WideAddress{ 0xD0, 0x13 }, false);
+
+ //Character is waiting for the display
+ if ((dsp & 0x80) == 0x80) {
+ dsp = dsp - 0x80;
+ if (dsp <= 0x7f) {
+ if (dsp == 0x0d) {
+ WCHAR output[2];
+ wsprintfW(output, L"%c", dsp);
+ outputBuffer = dsp;
+ OutputDebugString(output);
+ OutputDebugStringW(L"\r\n");
+ }
+ else if (dsp != 0x7f) {
+ WCHAR output[2];
+ wsprintfW(output, L"%c", dsp);
+ outputBuffer = dsp;
+ OutputDebugString(output);
+ }
+ }
+ MemoryMap::Instance()->write_byte(WideAddress(0xD0, 0x12), dsp, false);
+ }
+
+}
+
+uint8_t MC6821::getOutputBuffer() {
+ uint8_t character = outputBuffer;
+ outputBuffer = 0x00;
+ return character;
+}
+
+MC6821Status MC6821::getStatus() {
+ MC6821Status status;
+
+ status.DSP = dsp;
+ status.DSPCR = dspcr;
+ status.KBD = kbd;
+ status.KBDCR = kbdcr;
+
+ return status;
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/mc6821.h b/M6502EmulatorDll/M6502EmulatorDll/mc6821.h
new file mode 100644
index 0000000..e507758
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/mc6821.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "stdafx.h"
+#include
+#include
+#include "memory.h"
+
+//Simulates a MC6821 PIA for use with the Apple I.
+
+struct MC6821Status {
+ uint8_t KBD;
+ uint8_t KBDCR;
+ uint8_t DSP;
+ uint8_t DSPCR;
+};
+
+class MC6821 {
+private:
+ uint8_t kbd = 0x80; //$D010
+ uint8_t kbdcr = 0; //$D011
+ uint8_t dsp = 0; //$D012
+ uint8_t dspcr = 0; //$D013
+
+ uint8_t outputBuffer;
+ uint8_t inputBuffer;
+
+public:
+ void executionLoop();
+ MC6821Status getStatus();
+ uint8_t getOutputBuffer();
+};
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/memory.cpp b/M6502EmulatorDll/M6502EmulatorDll/memory.cpp
new file mode 100644
index 0000000..b7c677f
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/memory.cpp
@@ -0,0 +1,123 @@
+#include "stdafx.h"
+
+#include "memory.h"
+#include "state.h"
+#include
+#include
+
+MemoryMap* MemoryMap::m_pInstance = NULL;
+
+MemoryMap* MemoryMap::Instance() {
+ if (!m_pInstance)
+ m_pInstance = new MemoryMap();
+
+ return m_pInstance;
+}
+
+/* These functions are a nightmare that need to be cleaned up. */
+
+//Read a memory address.
+uint8_t MemoryMap::read_byte(uint16_t address) {
+ WideAddress wideAddress = uintAddressToWideAddress(address);
+ return read_byte(wideAddress, true);
+}
+
+uint8_t MemoryMap::read_byte(WideAddress address, bool overrides) {
+ uint8_t data = pages[address.high].read_byte(address.low);
+ if (overrides) {
+ handleReadOverrides(address);
+ }
+ return data;
+}
+
+//Write a byte to a memory address.
+void MemoryMap::write_byte(uint16_t address, uint8_t data) {
+ WideAddress wideAddress = uintAddressToWideAddress(address);
+ write_byte(wideAddress, data);
+}
+
+void MemoryMap::write_byte(WideAddress address, uint8_t data) {
+ MemoryMap::write_byte(address, data, true);
+}
+
+void MemoryMap::write_byte(WideAddress address, uint8_t data, bool overrides) {
+ pages[address.high].write_byte(address.low, data);
+ if (overrides) {
+ handleWriteOverrides(address);
+ }
+}
+
+//Convenience: Read a zero-page address.
+uint8_t MemoryMap::read_zero_page_byte(uint8_t address) {
+ WideAddress wideAddress = uintAddressToWideAddress(address);
+ return pages[0].read_byte(wideAddress.low);
+}
+
+//Convenience: Write a byte to a zero-page address.
+void MemoryMap::write_zero_page_byte(uint8_t address, uint8_t data) {
+ WideAddress wideAddress = uintAddressToWideAddress(address);
+ pages[0].write_byte(wideAddress.low, data);
+}
+
+void MemoryMap::initialize() {
+ for (int i = 0; i < 255; i++) {
+ pages[i] = Page();
+
+ if (EmulatorState::Instance()->getConfigurationType() == EmulatorState::Instance()->APPLE_I) {
+ pages[255] = Page(MEMORY_READ);
+ }
+ }
+}
+
+void MemoryMap::load_binary(uint16_t destination, std::string filename) {
+ std::ifstream binaryFile;
+ binaryFile.open(filename, std::ios::in|std::ios::binary);
+ if (binaryFile.is_open()) {
+ int x = 0;
+ char data[2];
+
+ while (!binaryFile.eof()) {
+ x++;
+ binaryFile.read(data, 1);
+ MemoryMap::write_byte(destination, data[0]);
+ destination = destination + 1;
+ }
+
+ MemoryMap::write_byte(destination - 1, '\0');
+
+ binaryFile.close();
+ }
+ else {
+ throw errno;
+ }
+
+}
+
+//Convert an integer address into a wide address struct.
+WideAddress uintAddressToWideAddress(uint16_t address) {
+ return WideAddress{ uint8_t((address >> 8) & 0xFF), uint8_t(address & 0xFF) };
+}
+
+uint16_t wideAddressToUintAddress(WideAddress address) {
+ return address.low | (address.high << 8);
+}
+
+/* These are done in hardware on the real machine. */
+void MemoryMap::handleReadOverrides(WideAddress address) {
+ //Reading KBD (0xD010) clears b7 of KBDCR (0xD011)
+ if (address == 0xD010) {
+ write_byte(WideAddress{ 0xD0, 0x11 }, uint8_t(read_byte(WideAddress{ 0xD0, 0x11 }, false) - 0x80), false);
+ }
+}
+
+void MemoryMap::handleWriteOverrides(WideAddress address) {
+ //Writing to KBD (0xD010) sets b7 of KBDCR (0xD011)
+ if (address == 0xD010) {
+ write_byte(WideAddress{ 0xD0, 0x11 }, (read_byte(WideAddress{ 0xD0, 0x11 }, false) | 0x80), false);
+ }
+
+ //Writing to DSP (0xD012) sets b7 of DSP (0xD012)
+ if (address == 0xD012) {
+ write_byte(address, (read_byte(address, false) | 0x80), false);
+ }
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/memory.h b/M6502EmulatorDll/M6502EmulatorDll/memory.h
new file mode 100644
index 0000000..73c12be
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/memory.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "WideAddress.h"
+#include "page.h"
+
+enum INDEX_MODE { INDEX_NONE = 0x01, INDEX_X = 0x02, INDEX_Y = 0x03 };
+
+WideAddress uintAddressToWideAddress(uint16_t address);
+uint16_t wideAddressToUintAddress(WideAddress address);
+
+class MemoryMap {
+private:
+ Page pages[256];
+ MemoryMap() {};
+ MemoryMap(MemoryMap const&) {};
+ MemoryMap& operator=(MemoryMap const&) {};
+ static MemoryMap* m_pInstance;
+
+public:
+ static MemoryMap* Instance();
+ uint8_t read_byte(uint16_t int_address);
+ void write_byte(uint16_t int_address, uint8_t data);
+
+ uint8_t read_byte(WideAddress address, bool overrides);
+ void write_byte(WideAddress address, uint8_t data);
+ void write_byte(WideAddress address, uint8_t data, bool overrides);
+
+ uint8_t read_zero_page_byte(uint8_t int_address);
+ void write_zero_page_byte(uint8_t int_address, uint8_t data);
+
+ void initialize();
+
+ void load_binary(uint16_t destination, std::string filename);
+
+ void handleReadOverrides(WideAddress);
+ void handleWriteOverrides(WideAddress);
+};
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/opcodes.cpp b/M6502EmulatorDll/M6502EmulatorDll/opcodes.cpp
new file mode 100644
index 0000000..3780c8b
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/opcodes.cpp
@@ -0,0 +1,730 @@
+#include "stdafx.h"
+
+#include
+#include "memory.h"
+#include "CPU.h"
+#include "state.h"
+
+#define PN FLAG_SIGN
+#define PV FLAG_OVERFLOW
+#define PB FLAG_BREAKPOINT
+#define PD FLAG_DECIMAL
+#define PI FLAG_INTERRUPT
+#define PZ FLAG_ZERO
+#define PC FLAG_CARRY
+
+unsigned char hex2bcd(unsigned char x)
+{
+ unsigned char y;
+ y = (x / 10) << 4;
+ y = y | (x % 10);
+ return (y);
+}
+
+/* These functions require the complete and pre-calculated address to function properly.
+ If there is no parameter on the function, it's either implied or accumulator.
+ Assume that PC value is correct coming in.
+
+ The exception is when branching because we don't know if we were successful until now
+ so we need to add +1 to PC on a successful branch and another +1 if the branch
+ goes to a different page. */
+
+void CPU::op_adc(WideAddress address) {
+ /*
+ Logic:
+ t = A + M + P.C
+ P.V = (A.7!=t.7) ? 1:0
+ P.N = A.7
+ P.Z = (t==0) ? 1:0
+ IF (P.D)
+ t = bcd(A) + bcd(M) + P.C
+ P.C = (t>99) ? 1:0
+ ELSE
+ P.C = (t>255) ? 1:0
+ A = t & 0xFF
+ */
+
+ if (accumulator == 0x7f) {
+ int x = 0;
+ }
+
+ uint16_t t = accumulator + fetch_memory_byte(address, false) + FLAG_CARRY;
+ int8_t t8 = t & 0xff;
+ PV = (~(accumulator ^ fetch_memory_byte(address, false)) & (accumulator ^ t8) & 0x80) == 0x80;
+ PZ = (t8 == 0);
+ PN = (t8 & 0x80) == 0x80;
+ if (PD) {
+ t = hex2bcd(accumulator) + hex2bcd(fetch_memory_byte(address, false)) + PC;
+ PC = (t > 99);
+ }
+ else {
+ PC = (t > 255);
+ }
+ accumulator = (t & 0xFF);
+
+};
+
+void CPU::op_adc(uint8_t immediate) {
+ /*
+ Logic:
+ t = A + M + P.C
+ P.V = (A.7 != t.7) ? 1:0
+ P.N = A.7
+ P.Z = (t==0) ? 1:0
+ IF (P.D)
+ t = bcd(A) + bcd(M) + P.C
+ P.C = (t>99) ? 1:0
+ ELSE
+ P.C = (t>255) ? 1:0
+ A = t & 0xFF
+ */
+
+ if (accumulator == 0x7f) {
+ int x = 0;
+ }
+
+ uint16_t t = accumulator + immediate + FLAG_CARRY;
+ int8_t t8 = t & 0xff;
+ /*/
+ if ((accumulator & 0x80) != (t & 0x80)) {
+ PV = true;
+ }
+ else {
+ PV = false;
+ }
+ */
+ PV = (~(accumulator ^ immediate) & (accumulator ^ t) & 0x80) == 0x80;
+ PN = (t8 & 0x80) == 0x80;
+ PZ = (t8 == 0);
+ //TODO: FLAG_DECIMAL
+ if (PD) {
+ t = hex2bcd(accumulator) + hex2bcd(immediate) + PC;
+ PC = (t > 99);
+ }
+ else {
+ PC = (t > 255);
+ }
+
+ accumulator = (t & 0xFF);
+};
+
+void CPU::op_and(WideAddress address) {
+ /*
+ Logic:
+ A = A & M
+ P.N = A.7
+ P.Z = (A==0) ? 1:0 */
+ accumulator = (accumulator & fetch_memory_byte(address, false));
+ FLAG_SIGN = (accumulator & 0x80) == 0x80;
+ FLAG_ZERO = (accumulator == 0);
+}
+
+void CPU::op_and(uint8_t immediate) {
+ /*
+ Logic:
+ A = A & M
+ P.N = A.7
+ P.Z = (A==0) ? 1:0 */
+ accumulator = (accumulator & immediate);
+ FLAG_SIGN = (accumulator & 0x80) == 0x80;
+ FLAG_ZERO = (accumulator == 0);
+}
+
+void CPU::op_asl(WideAddress address) { //OK
+ /*
+ Logic:
+ P.C = B.7
+ B = (B << 1) & $FE
+ P.N = B.7
+ P.Z = (B==0) ? 1:0
+ */
+ FLAG_CARRY = (fetch_memory_byte(address, false) & 0x80) == 0x80;
+ write_memory_byte(address, (fetch_memory_byte(address, false) << 1) & 0xFE);
+ FLAG_SIGN = (fetch_memory_byte(address, false) & 0x80) == 0x80;
+ FLAG_ZERO = (fetch_memory_byte(address, false) == 0);
+}
+
+void CPU::op_asl() {
+ /*
+ Logic:
+ P.C = B.7
+ B = (B << 1) & $FE
+ P.N = B.7
+ P.Z = (B==0) ? 1:0
+ */
+ FLAG_CARRY = (accumulator & 0x80) == 0x80;
+ accumulator = (accumulator << 1) & 0xFE;
+ FLAG_SIGN = (accumulator & 0x80) == 0x80;
+ FLAG_ZERO = (accumulator == 0);
+}
+
+void CPU::op_bcc(int8_t relative) {
+ if (!FLAG_CARRY) {
+ //+1 cycle for branching, +2 if branching to a different page
+ if ((program_counter & 0x00FF) + relative > 0x100) {
+ EmulatorState::Instance()->processor.cycles++;
+ }
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ }
+}
+
+void CPU::op_bcs(int8_t relative) {
+ if (FLAG_CARRY) {
+ //+1 cycle for branching, +2 if branching to a different page
+ if ((program_counter & 0x00FF) + relative > 0x100) {
+ EmulatorState::Instance()->processor.cycles++;
+ }
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ }
+}
+
+void CPU::op_beq(int8_t relative) {
+ if (FLAG_ZERO) {
+ //+1 cycle for branching, +2 if branching to a different page
+ if ((program_counter & 0x00FF) + relative > 0x100) {
+ EmulatorState::Instance()->processor.cycles++;
+ }
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ }
+}
+
+void CPU::op_bit(WideAddress address) {
+ /*
+ Logic:
+ t = A & M
+ P.N = M.7
+ P.V = M.6
+ P.Z = (t==0) ? 1:0
+ */
+ uint8_t operand = fetch_memory_byte(address, false);
+ uint8_t t = accumulator & operand;
+ PN = (operand & 0x80) == 0x80;
+ PV = (operand & 0x40) == 0x40;
+ PZ = (t == 0);
+}
+
+void CPU::op_bmi(int8_t relative) {
+ if (PN) {
+ //+1 cycle for branching, +2 if branching to a different page
+ if ((program_counter & 0x00FF) + relative > 0x100) {
+ EmulatorState::Instance()->processor.cycles++;
+ }
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ }
+}
+
+void CPU::op_bne(int8_t relative) {
+ if (!PZ) {
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ if (relative == (int8_t)0xFE) {
+ std::exit(4);
+ }
+ }
+}
+
+void CPU::op_bpl(int8_t relative) {
+ if (!PN) {
+ //+1 cycle for branching, +2 if branching to a different page
+ if ((program_counter & 0x00FF) + relative > 0x100) {
+ EmulatorState::Instance()->processor.cycles++;
+ }
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ }
+}
+
+void CPU::op_brk() {
+ uint8_t psw = getProgramStatusWord();
+ setProgramStatusWord(psw |= 0x10); //enable the B flag
+ psw |= 0x10; //we push the B flag as 1
+ WideAddress pc = uintAddressToWideAddress(program_counter);
+ stack_push(pc.high);
+ stack_push(pc.low);
+ stack_push(psw);
+ program_counter = WideAddress{ fetch_memory_byte(0xFFFF, false), fetch_memory_byte(0xFFFE, false) };
+}
+
+void CPU::op_bvc(int8_t relative) {
+ if (!PV) {
+ //+1 cycle for branching, +2 if branching to a different page
+ if ((program_counter & 0x00FF) + relative > 0x100) {
+ EmulatorState::Instance()->processor.cycles++;
+ }
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ }
+}
+
+void CPU::op_bvs(int8_t relative) {
+ if (PV) {
+ //+1 cycle for branching, +2 if branching to a different page
+ if ((program_counter & 0x00FF) + relative > 0x100) {
+ EmulatorState::Instance()->processor.cycles++;
+ }
+ EmulatorState::Instance()->processor.cycles++;
+ program_counter += relative;
+ }
+}
+
+void CPU::op_clc() {
+ PC = false;
+}
+
+void CPU::op_cld() {
+ PD = false;
+}
+
+void CPU::op_cli() {
+ PI = false;
+}
+
+void CPU::op_clv() {
+ PV = false;
+}
+
+void CPU::op_cmp(uint8_t immediate) { //immediate
+ /*
+ Logic:
+ t = A - M
+ P.N = t.7
+ P.C = (A>=M) ? 1:0
+ P.Z = (t==0) ? 1:0
+ */
+
+ uint8_t t = accumulator - immediate;
+ PN = (t & 0x80) == 0x80;
+ PC = (accumulator >= immediate);
+ PZ = (t == 0);
+
+}
+
+void CPU::op_cmp(WideAddress address) {
+ /*
+ Logic:
+ t = A - M
+ P.N = t.7
+ P.C = (A>=M) ? 1:0
+ P.Z = (t==0) ? 1:0
+ */
+
+ uint8_t t = accumulator - fetch_memory_byte(address, false);
+ PN = (t & 0x80) == 0x80;
+ PC = (accumulator >= fetch_memory_byte(address, false));
+ PZ = (t == 0);
+}
+
+void CPU::op_cpx(uint8_t immediate) {
+ uint8_t t = index_x - immediate;
+ PN = (t & 0x80) == 0x80;
+ PC = (index_x >= immediate);
+ PZ = (t == 0);
+}
+
+void CPU::op_cpx(WideAddress address) {
+ uint8_t t = index_x - fetch_memory_byte(address, false);
+ PN = (t & 0x80) == 0x80;
+ PC = (index_x >= fetch_memory_byte(address, false));
+ PZ = (t == 0);
+}
+
+void CPU::op_cpy(uint8_t immediate) {
+ uint8_t t = index_y - immediate;
+ PN = (t & 0x80) == 0x80;
+ PC = (index_y >= immediate);
+ PZ = (t == 0);
+}
+
+void CPU::op_cpy(WideAddress address) {
+ uint8_t t = index_y - fetch_memory_byte(address, false);
+ PN = (t & 0x80) == 0x80;
+ PC = (index_y >= fetch_memory_byte(address, false));
+ PZ = (t == 0);
+}
+
+void CPU::op_dec(WideAddress address) {
+ /*
+ Logic:
+ M = (M - 1) & $FF
+ P.N = M.7
+ P.Z = (M==0) ? 1:0
+ */
+
+ write_memory_byte(address, (fetch_memory_byte(address, false) - 1) & 0xFF);
+ PN = (fetch_memory_byte(address, false) & 0x80) == 0x80;
+ PZ = (fetch_memory_byte(address, false) == 0);
+
+}
+
+void CPU::op_dex() {
+ index_x--;
+ PZ = (index_x == 0);
+ PN = (index_x & 0x80) == 0x80;
+}
+
+void CPU::op_dey() {
+ index_y--;
+ PZ = (index_y == 0);
+ PN = (index_y & 0x80) == 0x80;
+}
+
+void CPU::op_eor(uint8_t immediate) {
+ accumulator = accumulator ^ immediate;
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_eor(WideAddress address) {
+ accumulator = accumulator ^ fetch_memory_byte(address, false);
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_inc(WideAddress address) {
+ /*
+ Logic:
+ M = (M + 1) & $FF
+ P.N = M.7
+ P.Z = (M==0) ? 1:0
+ */
+
+ write_memory_byte(address, (fetch_memory_byte(address, false) + 1) & 0xFF);
+ PN = (fetch_memory_byte(address, false) & 0x80) == 0x80;
+ PZ = (fetch_memory_byte(address, false) == 0);
+}
+
+void CPU::op_inx() {
+ index_x++;
+ PZ = (index_x == 0);
+ PN = (index_x & 0x80) == 0x80;
+}
+
+void CPU::op_iny() {
+ index_y++;
+ PZ = (index_y == 0);
+ PN = (index_y & 0x80) == 0x80;
+}
+
+void CPU::op_jmp(WideAddress address) {
+ program_counter = address;
+}
+
+void CPU::op_jsr(WideAddress address) {
+ /*
+ Logic:
+ t = PC - 1
+ bPoke(SP, t.h)
+ SP = SP - 1
+ bPoke(SP, t.l)
+ SP = SP - 1
+ PC = address
+ */
+
+ WideAddress t = uintAddressToWideAddress(program_counter - 1);
+ stack_push(t.high);
+ stack_push(t.low);
+ program_counter = address;
+}
+
+void CPU::op_lda(uint8_t immediate) {
+ accumulator = immediate;
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_lda(WideAddress address) {
+ accumulator = fetch_memory_byte(address, false);
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_ldx(uint8_t immediate) {
+ index_x = immediate;
+ PN = (index_x & 0x80) == 0x80;
+ PZ = (index_x == 0);
+}
+
+void CPU::op_ldx(WideAddress address) {
+ index_x = fetch_memory_byte(address, false);
+ PN = (index_x & 0x80) == 0x80;
+ PZ = (index_x == 0);
+}
+
+void CPU::op_ldy(uint8_t immediate) {
+ index_y = immediate;
+ PN = (index_y & 0x80) == 0x80;
+ PZ = (index_y == 0);
+}
+
+void CPU::op_ldy(WideAddress address) {
+ index_y = fetch_memory_byte(address, false);
+ PN = (index_y & 0x80) == 0x80;
+ PZ = (index_y == 0);
+}
+
+void CPU::op_lsr() { //Accumulator
+ /*
+ Logic:
+ P.N = 0
+ P.C = B.0
+ B = (B >> 1) & $7F
+ P.Z = (B==0) ? 1:0
+ */
+ PN = false;
+ PC = (accumulator & 0x01) == 0x01;
+ accumulator = (accumulator >> 1) & 0x7F;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_lsr(WideAddress address) { //OK
+ /*
+ Logic:
+ P.N = 0
+ P.C = B.0
+ B = (B >> 1) & $7F
+ P.Z = (B==0) ? 1:0
+ */
+ PN = false;
+ PC = (fetch_memory_byte(address, false) & 0x01) == 0x01;
+ write_memory_byte(address, ((fetch_memory_byte(address, false) >> 1) & 0x7F));
+ PZ = (fetch_memory_byte(address, false) == 0);
+}
+
+void CPU::op_nop() {
+ //NOP
+}
+
+void CPU::op_ora(uint8_t immediate) {
+ accumulator = accumulator | immediate;
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_ora(WideAddress address) {
+ accumulator = accumulator | fetch_memory_byte(address, false);
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_pha() {
+ stack_push(accumulator);
+}
+
+void CPU::op_php() {
+ stack_push(getProgramStatusWord() | 0x10);
+}
+
+void CPU::op_pla() {
+ accumulator = stack_pop();
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_plp() {
+ setProgramStatusWord(stack_pop());
+}
+
+void CPU::op_rol() { //Tested and working
+ /*
+ Logic:
+ t = B.7
+ B = (B << 1) & $FE
+ B = B | P.C
+ P.C = t
+ P.Z = (B==0) ? 1:0
+ P.N = B.7
+ */
+ bool t = (accumulator & 0x80) == 0x80;
+ accumulator = (accumulator << 1) & 0xFE;
+ if (PC) {
+ accumulator = accumulator | 0x01;
+ }
+ PC = t;
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_rol(WideAddress address) { //Tested and working
+ /*
+ Logic:
+ t = B.7
+ B = (B << 1) & $FE
+ B = B | P.C
+ P.C = t
+ P.Z = (B==0) ? 1:0
+ P.N = B.7
+ */
+ bool t = (fetch_memory_byte(address, false) & 0x80) == 0x80;
+ write_memory_byte(address, (fetch_memory_byte(address, false) << 1) & 0xFE);
+ if (PC) {
+ write_memory_byte(address, fetch_memory_byte(address, false) | 0x01);
+ }
+ PC = t;
+ PN = (fetch_memory_byte(address, false) & 0x80) == 0x80;
+ PZ = (fetch_memory_byte(address, false) == 0);
+}
+
+void CPU::op_ror() { //Tested and working
+ /*
+ Logic:
+ t = B.0
+ B = (B >> 1) & $7F
+ B = B | ((P.C) ? $80:$00)
+ P.C = t
+ P.Z = (B==0) ? 1:0
+ P.N = B.7
+ */
+ bool t = (accumulator & 0x01) == 0x01;
+ accumulator = (accumulator >> 1) & 0x7F;
+ if (PC) {
+ accumulator |= 0x80;
+ }
+ PC = t;
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_ror(WideAddress address) { //Tested and working
+ bool t = (fetch_memory_byte(address, false) & 0x01) == 0x01;
+ write_memory_byte(address, (fetch_memory_byte(address, false) >> 1) & 0x7F);
+ if (PC) {
+ write_memory_byte(address, (fetch_memory_byte(address, false) | 0x80));
+ }
+ PC = t;
+ PN = (fetch_memory_byte(address, false) & 0x80) == 0x80;
+ PZ = (fetch_memory_byte(address, false) == 0);
+}
+
+void CPU::op_rti() {
+ setProgramStatusWord(stack_pop());
+ uint8_t lo = stack_pop();
+ uint8_t hi = stack_pop();
+ program_counter = WideAddress{ hi, lo };
+}
+
+void CPU::op_rts() {
+ uint8_t lo = stack_pop();
+ uint8_t hi = stack_pop();
+ WideAddress addr = WideAddress{ hi, lo };
+ program_counter = addr.add(1, false);
+}
+
+void CPU::op_sbc(uint8_t immediate) {
+ /*
+ Logic:
+ IF (P.D)
+ t = bcd(A) - bcd(M) - !P.C
+ P.V = (t>99 OR t<0) ? 1:0
+ ELSE
+ t = A - M - !P.C
+ P.V = (t>127 OR t<-128) ? 1:0
+ P.C = (t>=0) ? 1:0
+ P.N = t.7
+ P.Z = (t==0) ? 1:0
+ A = t & 0xFF
+ */
+ //TODO: decimal
+
+ op_adc(~immediate);
+
+ /*
+ int8_t t;
+
+ if (PD) {
+ t = hex2bcd(accumulator) - hex2bcd(immediate) - !PC;
+ PV = (t > 99 || t < 0);
+ }
+ else {
+ t = accumulator - immediate - !PC;
+ PV = (t > 127 || t < -128);
+ }
+ PC = (t >= 0);
+ PN = (t & 0x80) == 0x80;
+ PZ = (t == 0);
+ accumulator = (t & 0xFF);
+ */
+}
+
+void CPU::op_sbc(WideAddress address) {
+ op_adc(~fetch_memory_byte(address, false));
+
+ /*
+ int8_t t;
+
+ if (PD) {
+ t = hex2bcd(accumulator) - hex2bcd(fetch_memory_byte(address, false)) - !PC;
+ PV = (t > 99 || t < 0);
+ }
+ else {
+ t = accumulator - fetch_memory_byte(address, false) - !PC;
+ PV = (t > 127 || t < -128);
+ }
+
+ PC = (t >= 0);
+ PN = (t & 0x80) == 0x80;
+ PZ = (t == 0);
+ accumulator = (t & 0xFF);
+ */
+}
+
+void CPU::op_sec() {
+ PC = true;
+}
+
+void CPU::op_sed() {
+ PD = true;
+}
+
+void CPU::op_sei() {
+ PI = true;
+}
+
+void CPU::op_sta(WideAddress address) {
+ write_memory_byte(address, accumulator);
+}
+
+void CPU::op_stx(WideAddress address) {
+ write_memory_byte(address, index_x);
+}
+
+void CPU::op_sty(WideAddress address) {
+ write_memory_byte(address, index_y);
+}
+
+void CPU::op_tax() {
+ index_x = accumulator;
+ PN = (index_x & 0x80) == 0x80;
+ PZ = (index_x == 0);
+}
+
+void CPU::op_tay() {
+ index_y = accumulator;
+ PN = (index_y & 0x80) == 0x80;
+ PZ = (index_y == 0);
+}
+
+void CPU::op_tsx() {
+ index_x = stack_pointer;
+ PN = (index_x & 0x80) == 0x80;
+ PZ = (index_x == 0);
+}
+
+void CPU::op_txa() {
+ accumulator = index_x;
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
+
+void CPU::op_txs() {
+ stack_pointer = index_x;
+}
+
+void CPU::op_tya() {
+ accumulator = index_y;
+ PN = (accumulator & 0x80) == 0x80;
+ PZ = (accumulator == 0);
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/page.cpp b/M6502EmulatorDll/M6502EmulatorDll/page.cpp
new file mode 100644
index 0000000..ce65e8d
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/page.cpp
@@ -0,0 +1,15 @@
+#include "stdafx.h"
+#include
+#include "page.h"
+
+char Page::read_byte(uint8_t address) {
+ return cells[address];
+}
+
+void Page::write_byte(uint8_t address, uint8_t data) {
+ cells[address] = data;
+}
+
+int Page::getAccessType() {
+ return access_type;
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/page.h b/M6502EmulatorDll/M6502EmulatorDll/page.h
new file mode 100644
index 0000000..751fc2f
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/page.h
@@ -0,0 +1,29 @@
+#pragma once
+#include "stdafx.h"
+#include
+
+enum MEMORY_ACCESS { MEMORY_READ = 0x01, MEMORY_WRITE = 0x02 };
+
+class Page {
+public:
+ char read_byte(uint8_t address);
+ void write_byte(uint8_t address, uint8_t data);
+ int getAccessType();
+
+ Page() {
+ for (int i = 0; i < 256; i++) {
+ cells[i] = 0;
+ }
+ }
+
+ Page(MEMORY_ACCESS access) {
+ for (int i = 0; i < 256; i++) {
+ cells[i] = 0;
+ }
+ access_type = access;
+ }
+
+private:
+ uint8_t cells[256];
+ int access_type = MEMORY_READ | MEMORY_WRITE;
+};
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/s2513.cpp b/M6502EmulatorDll/M6502EmulatorDll/s2513.cpp
new file mode 100644
index 0000000..5db3f0e
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/s2513.cpp
@@ -0,0 +1,142 @@
+#include "stdafx.h"
+#include "memory.h"
+#include "s2513.h"
+
+char S2513::appleCharToAscii(uint8_t input) {
+ {
+ switch (input) {
+ case 0x00:
+ return '@';
+ case 0x01:
+ return 'A';
+ case 0x02:
+ return 'B';
+ case 0x03:
+ return 'C';
+ case 0x04:
+ return 'D';
+ case 0x05:
+ return 'E';
+ case 0x06:
+ return 'F';
+ case 0x07:
+ return 'G';
+ case 0x08:
+ return 'H';
+ case 0x09:
+ return 'I';
+ case 0x0A:
+ return 'J';
+ case 0x0B:
+ return 'K';
+ case 0x0C:
+ return 'L';
+ case 0x0D:
+ return 'M';
+ case 0x0E:
+ return 'N';
+ case 0x0F:
+ return 'O';
+ case 0x10:
+ return 'P';
+ case 0x11:
+ return 'Q';
+ case 0x12:
+ return 'R';
+ case 0x13:
+ return 'S';
+ case 0x14:
+ return 'T';
+ case 0x15:
+ return 'U';
+ case 0x16:
+ return 'V';
+ case 0x17:
+ return 'W';
+ case 0x18:
+ return 'X';
+ case 0x19:
+ return 'Y';
+ case 0x1A:
+ return 'Z';
+ case 0x1B:
+ return '[';
+ case 0x1C:
+ return '\\';
+ case 0x1D:
+ return ']';
+ case 0x1E:
+ return '^';
+ case 0x1F:
+ return '_';
+ case 0x20:
+ return ' ';
+ case 0x21:
+ return '!';
+ case 0x22:
+ return '"';
+ case 0x23:
+ return '#';
+ case 0x24:
+ return '$';
+ case 0x25:
+ return '%';
+ case 0x26:
+ return '&';
+ case 0x27:
+ return '\'';
+ case 0x28:
+ return '(';
+ case 0x29:
+ return ')';
+ case 0x2A:
+ return '*';
+ case 0x2B:
+ return '+';
+ case 0x2C:
+ return ',';
+ case 0x2D:
+ return '-';
+ case 0x2E:
+ return '.';
+ case 0x2F:
+ return '/';
+ case 0x30:
+ return '0';
+ case 0x31:
+ return '1';
+ case 0x32:
+ return '2';
+ case 0x33:
+ return '3';
+ case 0x34:
+ return '4';
+ case 0x35:
+ return '5';
+ case 0x36:
+ return '6';
+ case 0x37:
+ return '7';
+ case 0x38:
+ return '8';
+ case 0x39:
+ return '9';
+ case 0x3A:
+ return ':';
+ case 0x3B:
+ return ';';
+ case 0x3C:
+ return '<';
+ case 0x3D:
+ return '=';
+ case 0x3E:
+ return '>';
+ case 0x3F:
+ return '?';
+ case 0x7F:
+ return '\n';
+ default:
+ return '~';
+ }
+ }
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/s2513.h b/M6502EmulatorDll/M6502EmulatorDll/s2513.h
new file mode 100644
index 0000000..c4736e2
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/s2513.h
@@ -0,0 +1,11 @@
+#pragma once
+#include "stdafx.h"
+#include
+
+/* Signetics 2513 character generator. */
+class S2513 {
+private:
+
+public:
+ char appleCharToAscii(uint8_t input);
+};
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/state.cpp b/M6502EmulatorDll/M6502EmulatorDll/state.cpp
new file mode 100644
index 0000000..2d75d83
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/state.cpp
@@ -0,0 +1,31 @@
+#include "stdafx.h"
+#include "state.h"
+
+#include "CPU.h"
+#include "memory.h"
+#include "mc6821.h"
+#include "s2513.h"
+
+void EmulatorState::init() {
+ config = APPLE_I;
+ MemoryMap::Instance()->initialize();
+
+ /*
+ MemoryMap::Instance()->readModifiers.push_back(MemoryModifier(WideAddress{ 0xD0, 0x10 }, WideAddress{ 0xD0, 0x11 }, -0x80));
+ MemoryMap::Instance()->writeModifiers.push_back(MemoryModifier(WideAddress{ 0xD0, 0x10 }, WideAddress{ 0xD0, 0x11 }, 0x80));
+ MemoryMap::Instance()->writeModifiers.push_back(MemoryModifier(WideAddress{ 0xD0, 0x12 }, WideAddress{ 0xD0, 0x12 }, 0x80));
+ */
+}
+
+EmulatorState* EmulatorState::m_pInstance = NULL;
+
+EmulatorState* EmulatorState::Instance() {
+ if (!m_pInstance)
+ m_pInstance = new EmulatorState();
+
+ return m_pInstance;
+}
+
+EmulatorState::COMPUTER_CONFIG EmulatorState::getConfigurationType() {
+ return config;
+}
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/state.h b/M6502EmulatorDll/M6502EmulatorDll/state.h
new file mode 100644
index 0000000..d87025f
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/state.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "stdafx.h"
+#include "CPU.h"
+#include "mc6821.h"
+#include "s2513.h"
+
+class EmulatorState {
+public:
+ enum COMPUTER_CONFIG { APPLE_I };
+ static EmulatorState* Instance();
+ CPU processor = CPU();
+ MC6821 mc6821 = MC6821();
+ S2513 s2513 = S2513();
+
+ void init();
+ COMPUTER_CONFIG EmulatorState::getConfigurationType();
+
+private:
+ EmulatorState() {};
+ EmulatorState(EmulatorState const&) {};
+ EmulatorState& operator=(EmulatorState const&) {};
+ static EmulatorState* m_pInstance;
+ COMPUTER_CONFIG config = APPLE_I;
+};
\ No newline at end of file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/stdafx.cpp b/M6502EmulatorDll/M6502EmulatorDll/stdafx.cpp
new file mode 100644
index 0000000..cc38376
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// M6502EmulatorDll.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/M6502EmulatorDll/M6502EmulatorDll/stdafx.h b/M6502EmulatorDll/M6502EmulatorDll/stdafx.h
new file mode 100644
index 0000000..f3a0737
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/stdafx.h
@@ -0,0 +1,16 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/M6502EmulatorDll/M6502EmulatorDll/targetver.h b/M6502EmulatorDll/M6502EmulatorDll/targetver.h
new file mode 100644
index 0000000..87c0086
--- /dev/null
+++ b/M6502EmulatorDll/M6502EmulatorDll/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include
diff --git a/README.md b/README.md
index b128673..fe8f1ae 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,32 @@
# fruitmachine
-Apple I emulator.
+This is the start of an Apple I emulator. Nothing really works yet beyond the CPU.
+
+Things that work:
+* MOS 6502 core
+* Dumb terminal-style display output
+
+Things that don't work:
+* The debugger
+* Cassette interface
+* Most of the UI
+
+Known issues:
+* The blinking cursor will hang around if you hit carriage return while it's being displayed
+* Clicking Break and then Run will crash the emulator because I don't know how C# threading works
+* The display will sometimes lose input focus, click away from the window and back in to fix that
+* The cycle counts aren't completely accurate (a cycle isn't added for zero-page operations that wrap)
+* Many more
+
+The ROM paths are hardcoded for the moment so the emulator expects these files to exist:
+
+C:\apple\apple1.rom - Monitor ROM, 256B
+C:\apple\apple1.vid - Character ROM, 512B
+C:\apple\apple1basic.bin - Integer BASIC, available from http://www.pagetable.com/?p=32
+
+To load Integer BASIC from the monitor, type E000R . The prompt will switch from \ to >, indicating that you are now in BASIC mode.
+
+Most Apple I software on the internet is available in audiocassette format and I haven't written a cassette reader yet. If you have a debug copy of MESS, you can load a file into MESS' Apple I emulator, dump the program memory, and add it to the btnLoadBinary click event with Interop.loadBinary(path, address).
+
+--
+
+M6502EmulatorDLL compiles to a DLL. Add a reference to this DLL in 6502EmulatorFrontend to compile.
\ No newline at end of file