Initial Commit

This commit is contained in:
Luigi Thirty 2015-11-29 15:28:32 -05:00
parent 9f7dc9b80b
commit 792386aceb
53 changed files with 7001 additions and 1 deletions

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<VSPerformanceSession Version="1.00">
<Options>
<Solution>6502EmulatorFrontend.sln</Solution>
<CollectionMethod>Sampling</CollectionMethod>
<AllocationMethod>None</AllocationMethod>
<AddReport>true</AddReport>
<ResourceBasedAnalysisSelected>true</ResourceBasedAnalysisSelected>
<UniqueReport>Timestamp</UniqueReport>
<SamplingMethod>Cycles</SamplingMethod>
<CycleCount>10000000</CycleCount>
<PageFaultCount>10</PageFaultCount>
<SysCallCount>10</SysCallCount>
<SamplingCounter Name="" ReloadValue="00000000000f4240" DisplayName="" />
<RelocateBinaries>false</RelocateBinaries>
<HardwareCounters EnableHWCounters="false" />
<EtwSettings />
<PdhSettings>
<PdhCountersEnabled>false</PdhCountersEnabled>
<PdhCountersRate>500</PdhCountersRate>
<PdhCounters>
<PdhCounter>\Memory\Pages/sec</PdhCounter>
<PdhCounter>\PhysicalDisk(_Total)\Avg. Disk Queue Length</PdhCounter>
<PdhCounter>\Processor(_Total)\% Processor Time</PdhCounter>
</PdhCounters>
</PdhSettings>
</Options>
<ExcludeSmallFuncs>true</ExcludeSmallFuncs>
<InteractionProfilingEnabled>false</InteractionProfilingEnabled>
<JScriptProfilingEnabled>false</JScriptProfilingEnabled>
<PreinstrumentEvent>
<InstrEventExclude>false</InstrEventExclude>
</PreinstrumentEvent>
<PostinstrumentEvent>
<InstrEventExclude>false</InstrEventExclude>
</PostinstrumentEvent>
<Binaries>
<ProjBinary>
<Path>6502EmulatorFrontend\obj\Release\6502EmulatorFrontend.exe</Path>
<ArgumentTimestamp>01/01/0001 00:00:00</ArgumentTimestamp>
<Instrument>true</Instrument>
<Sample>true</Sample>
<ExternalWebsite>false</ExternalWebsite>
<InteractionProfilingEnabled>false</InteractionProfilingEnabled>
<IsLocalJavascript>false</IsLocalJavascript>
<IsWindowsStoreApp>false</IsWindowsStoreApp>
<IsWWA>false</IsWWA>
<LaunchProject>true</LaunchProject>
<OverrideProjectSettings>false</OverrideProjectSettings>
<LaunchMethod>Executable</LaunchMethod>
<ExecutablePath>6502EmulatorFrontend\bin\Release\6502EmulatorFrontend.exe</ExecutablePath>
<StartupDirectory>6502EmulatorFrontend\bin\Release\</StartupDirectory>
<Arguments>
</Arguments>
<NetAppHost>IIS</NetAppHost>
<NetBrowser>InternetExplorer</NetBrowser>
<ExcludeSmallFuncs>true</ExcludeSmallFuncs>
<JScriptProfilingEnabled>false</JScriptProfilingEnabled>
<PreinstrumentEvent>
<InstrEventExclude>false</InstrEventExclude>
</PreinstrumentEvent>
<PostinstrumentEvent>
<InstrEventExclude>false</InstrEventExclude>
</PostinstrumentEvent>
<ProjRef>{C198497A-3D3E-4F01-A72B-642DAB6B11D6}|6502EmulatorFrontend\6502EmulatorFrontend.csproj</ProjRef>
<ProjPath>6502EmulatorFrontend\6502EmulatorFrontend.csproj</ProjPath>
<ProjName>6502EmulatorFrontend</ProjName>
</ProjBinary>
</Binaries>
<Reports>
<Report>
<Path>6502EmulatorFrontend151126.vsp</Path>
</Report>
</Reports>
<Launches>
<ProjBinary>
<Path>:PB:{C198497A-3D3E-4F01-A72B-642DAB6B11D6}|6502EmulatorFrontend\6502EmulatorFrontend.csproj</Path>
</ProjBinary>
</Launches>
</VSPerformanceSession>

View File

@ -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

View File

@ -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"
}

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C198497A-3D3E-4F01-A72B-642DAB6B11D6}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>_6502EmulatorFrontend</RootNamespace>
<AssemblyName>6502EmulatorFrontend</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WriteableBitmapEx.Wpf">
<HintPath>E:\WriteableBitmapEx\WPF\WriteableBitmapEx.Wpf.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Apple2Display.cs" />
<Compile Include="cpu\Disassembly.cs" />
<Compile Include="IDisplay.cs" />
<Compile Include="MainWindowViewModel.cs" />
<Compile Include="video\CharacterBitmap.cs" />
<Compile Include="video\CharacterSet.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="AppleDisplay.cs" />
<Compile Include="cpu\M6502.cs" />
<Compile Include="cpu\M6502Model.cs" />
<Compile Include="Interop.cs" />
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="memory\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
</configuration>

View File

@ -0,0 +1,9 @@
<Application x:Class="_6502EmulatorFrontend.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:_6502EmulatorFrontend"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -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
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -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();
}
}
}

View File

@ -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<DisplayRow> 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<DisplayRow>();
_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<CharacterBitmap> Characters = new List<CharacterBitmap>();
public DisplayRow()
{
for (int j = 0; j < 40; j++)
{
Characters.Insert(j, CharacterBitmap.BlankBitmap);
}
}
}
}

View File

@ -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; }
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,59 @@
<Window x:Class="_6502EmulatorFrontend.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:_6502EmulatorFrontend"
mc:Ignorable="d"
Title="Fruit Machine 0.0000001a" Height="600" Width="954">
<Grid>
<Button x:Name="btnLoadBinary" Content="Load Binary" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="btnLoadBinary_Click"/>
<Label x:Name="binaryLoadedStatus" Content="Label" HorizontalAlignment="Left" Margin="90,8,0,0" VerticalAlignment="Top" Width="417"/>
<Grid HorizontalAlignment="Left" Height="185" Margin="10,39,0,0" VerticalAlignment="Top" Width="200" Background="#FFACACAC">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.ColumnSpan="1">
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Label Content="PC" Height="25"></Label>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Label Content="A" Height="25"></Label>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Label Content="X" Height="25"></Label>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Label Content="Y" Height="25"></Label>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Label Content="SP" Height="25"></Label>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Label Content="Flags" Height="25"></Label>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.ColumnSpan="1" Grid.Column="1">
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBox Name="txtProgramCounter" Height="25" Text="{Binding Path=Processor.ProgramCounter, StringFormat={}{0:X4}}" HorizontalAlignment="Right" Width="115" Margin="0,0,10,0" FontFamily="Courier New" FontWeight="Bold" FontSize="13.333"></TextBox>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBox Name="txtAccumulator" Height="25" Text="{Binding Path=Processor.Accumulator, StringFormat={}{0:X2}}" Margin="0,0,10,0" FontFamily="Courier New" FontWeight="Bold" FontSize="13.333"></TextBox>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBox Name="txtIndexX" Height="25" Text="{Binding Path=Processor.IndexX, StringFormat={}{0:X2}}" Margin="0,0,10,0" FontFamily="Courier New" FontWeight="Bold" FontSize="13.333"></TextBox>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBox Name="txtIndexY" Height="25" Text="{Binding Path=Processor.IndexY, StringFormat={}{0:X2}}" Margin="0,0,10,0" FontFamily="Courier New" FontWeight="Bold" FontSize="13.333"></TextBox>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBox Name="txtStackPointer" Height="25" Text="{Binding Path=Processor.StackPointer, StringFormat={}{0:X2}}" Margin="0,0,10,0" FontFamily="Courier New" FontWeight="Bold" FontSize="13.333"></TextBox>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBox Name="txtFlags" Height="25" Text="{Binding Path=Processor.Flags}" Margin="0,0,10,0" FontFamily="Courier New" FontWeight="Bold" FontSize="13.333"></TextBox>
</StackPanel>
</Grid>
<Button x:Name="btnSingleStep" Content="Single Step" HorizontalAlignment="Left" Margin="10,229,0,0" VerticalAlignment="Top" Width="75" Click="btnSingleStep_Click" IsEnabled="False"/>
<Image x:Name="image" Source="{Binding Path=DisplayGrid.DisplayCanvas}" HorizontalAlignment="Left" Height="384" Margin="215,39,0,0" VerticalAlignment="Top" Width="640" RenderOptions.BitmapScalingMode="NearestNeighbor"/>
<Button x:Name="btnRun" Content="Run" HorizontalAlignment="Left" Margin="135,229,0,0" VerticalAlignment="Top" Width="75" Click="btnRun_Click" IsEnabled="False"/>
<ListBox x:Name="lbDisassembly" ItemsSource="{Binding DisassembledOpcodes}" HorizontalAlignment="Left" Height="277" Margin="10,283,0,0" VerticalAlignment="Top" Width="200" FontFamily="Courier New">
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}">LightBlue</SolidColorBrush>
</ListBox.Resources>
</ListBox>
<TextBox ScrollViewer.VerticalScrollBarVisibility="Visible" x:Name="tbDebugConsole" HorizontalAlignment="Left" Height="104" Margin="215,428,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="721"/>
<Button x:Name="btnBreak" Content="Break" HorizontalAlignment="Left" Margin="135,256,0,0" VerticalAlignment="Top" Width="75" Click="btnBreak_Click"/>
<TextBox x:Name="tbDebugEntry" HorizontalAlignment="Left" Height="23" Margin="215,537,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="721" KeyDown="tbDebugEntry_KeyDown"/>
</Grid>
</Window>

View File

@ -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
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
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<Disassembly.DisassembledOpcode>();
//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();
}
}
}
}

View File

@ -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<cpu.Disassembly.DisassembledOpcode> _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<cpu.Disassembly.DisassembledOpcode> 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
}
}

View File

@ -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
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> 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")]

View File

@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace _6502EmulatorFrontend.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

File diff suppressed because it is too large Load Diff

View File

@ -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<ushort> breakpointAddresses = new List<ushort>();
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
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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<CharacterBitmap> CharacterList = new List<CharacterBitmap>();
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,261 @@
#include <stdint.h>
#include <vector>
#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<std::string> 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();
};

View File

@ -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;
}

View File

@ -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);

View File

@ -0,0 +1,195 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{1E7B9DC9-46C4-4F91-BD4D-AAB22D739F13}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>M6502EmulatorDll</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;M6502EMULATORDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="api.h" />
<ClInclude Include="CPU.h" />
<ClInclude Include="M6502EmulatorDll.h" />
<ClInclude Include="mc6821.h" />
<ClInclude Include="memory.h" />
<ClInclude Include="page.h" />
<ClInclude Include="s2513.h" />
<ClInclude Include="state.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="WideAddress.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="addressing.cpp" />
<ClCompile Include="api.cpp" />
<ClCompile Include="CPU.cpp" />
<ClCompile Include="dllmain.cpp">
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</PrecompiledHeader>
</ClCompile>
<ClCompile Include="M6502EmulatorDll.cpp" />
<ClCompile Include="mc6821.cpp" />
<ClCompile Include="memory.cpp" />
<ClCompile Include="opcodes.cpp" />
<ClCompile Include="page.cpp" />
<ClCompile Include="s2513.cpp" />
<ClCompile Include="state.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\api">
<UniqueIdentifier>{35d5de5f-71d1-4db7-9ec3-bdede37ebf8d}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\memory">
<UniqueIdentifier>{eb09f38d-23a9-4909-ad59-d3156f0f6db5}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\memory">
<UniqueIdentifier>{f2865ccc-4af8-46e5-b00d-f6a5e1272e5b}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\cpu">
<UniqueIdentifier>{354499c0-35ea-48a1-a1ac-33758a6be2b9}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\cpu">
<UniqueIdentifier>{e3dc3c1f-0d63-4139-afca-faddcb96d5e5}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\io">
<UniqueIdentifier>{b3bf0606-427d-4fe6-9077-20682dc62fa5}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\io">
<UniqueIdentifier>{485c3d9f-8556-4dab-a880-426e12c39d3d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="M6502EmulatorDll.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="api.h">
<Filter>Source Files\api</Filter>
</ClInclude>
<ClInclude Include="state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="memory.h">
<Filter>Header Files\memory</Filter>
</ClInclude>
<ClInclude Include="page.h">
<Filter>Header Files\memory</Filter>
</ClInclude>
<ClInclude Include="CPU.h">
<Filter>Header Files\cpu</Filter>
</ClInclude>
<ClInclude Include="WideAddress.h">
<Filter>Header Files\memory</Filter>
</ClInclude>
<ClInclude Include="mc6821.h">
<Filter>Header Files\io</Filter>
</ClInclude>
<ClInclude Include="s2513.h">
<Filter>Header Files\io</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="M6502EmulatorDll.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="api.cpp">
<Filter>Source Files\api</Filter>
</ClCompile>
<ClCompile Include="state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="addressing.cpp">
<Filter>Source Files\memory</Filter>
</ClCompile>
<ClCompile Include="memory.cpp">
<Filter>Source Files\memory</Filter>
</ClCompile>
<ClCompile Include="page.cpp">
<Filter>Source Files\memory</Filter>
</ClCompile>
<ClCompile Include="CPU.cpp">
<Filter>Source Files\cpu</Filter>
</ClCompile>
<ClCompile Include="opcodes.cpp">
<Filter>Source Files\cpu</Filter>
</ClCompile>
<ClCompile Include="mc6821.cpp">
<Filter>Source Files\io</Filter>
</ClCompile>
<ClCompile Include="s2513.cpp">
<Filter>Source Files\io</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -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.
/////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,48 @@
#pragma once
#include "stdafx.h"
#include <stdint.h>
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);
}
};

View File

@ -0,0 +1,230 @@
#include "stdafx.h"
#include "CPU.h"
#include "memory.h"
#include <iostream>
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;
}

View File

@ -0,0 +1,90 @@
#include "stdafx.h"
#include "api.h"
#include "state.h"
#include <string>
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;
}

View File

@ -0,0 +1,53 @@
#pragma once
#include "stdafx.h"
#include "mc6821.h"
#include <stdint.h>
#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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "stdafx.h"
#include <stdint.h>
#include <iostream>
#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();
};

View File

@ -0,0 +1,123 @@
#include "stdafx.h"
#include "memory.h"
#include "state.h"
#include <iostream>
#include <fstream>
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);
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#include <vector>
#include <iomanip>
#include <stdint.h>
#include <sstream>
#include <string>
#include <vector>
#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);
};

View File

@ -0,0 +1,730 @@
#include "stdafx.h"
#include <iostream>
#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);
}

View File

@ -0,0 +1,15 @@
#include "stdafx.h"
#include <stdint.h>
#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;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "stdafx.h"
#include <stdint.h>
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;
};

View File

@ -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 '~';
}
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "stdafx.h"
#include <stdint.h>
/* Signetics 2513 character generator. */
class S2513 {
private:
public:
char appleCharToAscii(uint8_t input);
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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

View File

@ -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 <windows.h>
// TODO: reference additional headers your program requires here

View File

@ -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 <SDKDDKVer.h>

View File

@ -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 <enter>. 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.