mirror of
https://github.com/Luigi30/fruitmachine.git
synced 2025-01-17 12:29:42 +00:00
Initial Commit
This commit is contained in:
parent
9f7dc9b80b
commit
792386aceb
80
6502EmulatorFrontend/6502EmulatorFrontend.psess
Normal file
80
6502EmulatorFrontend/6502EmulatorFrontend.psess
Normal 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>
|
36
6502EmulatorFrontend/6502EmulatorFrontend.sln
Normal file
36
6502EmulatorFrontend/6502EmulatorFrontend.sln
Normal 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
|
10
6502EmulatorFrontend/6502EmulatorFrontend.vssscc
Normal file
10
6502EmulatorFrontend/6502EmulatorFrontend.vssscc
Normal 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"
|
||||
}
|
@ -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>
|
6
6502EmulatorFrontend/6502EmulatorFrontend/App.config
Normal file
6
6502EmulatorFrontend/6502EmulatorFrontend/App.config
Normal 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>
|
9
6502EmulatorFrontend/6502EmulatorFrontend/App.xaml
Normal file
9
6502EmulatorFrontend/6502EmulatorFrontend/App.xaml
Normal 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>
|
17
6502EmulatorFrontend/6502EmulatorFrontend/App.xaml.cs
Normal file
17
6502EmulatorFrontend/6502EmulatorFrontend/App.xaml.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
30
6502EmulatorFrontend/6502EmulatorFrontend/Apple2Display.cs
Normal file
30
6502EmulatorFrontend/6502EmulatorFrontend/Apple2Display.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
201
6502EmulatorFrontend/6502EmulatorFrontend/AppleDisplay.cs
Normal file
201
6502EmulatorFrontend/6502EmulatorFrontend/AppleDisplay.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
6502EmulatorFrontend/6502EmulatorFrontend/IDisplay.cs
Normal file
15
6502EmulatorFrontend/6502EmulatorFrontend/IDisplay.cs
Normal 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; }
|
||||
}
|
||||
}
|
76
6502EmulatorFrontend/6502EmulatorFrontend/Interop.cs
Normal file
76
6502EmulatorFrontend/6502EmulatorFrontend/Interop.cs
Normal 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);
|
||||
}
|
||||
}
|
59
6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml
Normal file
59
6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml
Normal 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>
|
228
6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml.cs
Normal file
228
6502EmulatorFrontend/6502EmulatorFrontend/MainWindow.xaml.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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")]
|
71
6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.Designer.cs
generated
Normal file
71
6502EmulatorFrontend/6502EmulatorFrontend/Properties/Resources.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
30
6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.Designer.cs
generated
Normal file
30
6502EmulatorFrontend/6502EmulatorFrontend/Properties/Settings.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
1313
6502EmulatorFrontend/6502EmulatorFrontend/cpu/Disassembly.cs
Normal file
1313
6502EmulatorFrontend/6502EmulatorFrontend/cpu/Disassembly.cs
Normal file
File diff suppressed because it is too large
Load Diff
355
6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502.cs
Normal file
355
6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502.cs
Normal 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
|
||||
}
|
||||
}
|
32
6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502Model.cs
Normal file
32
6502EmulatorFrontend/6502EmulatorFrontend/cpu/M6502Model.cs
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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>();
|
||||
}
|
||||
}
|
28
M6502EmulatorDll/M6502EmulatorDll.sln
Normal file
28
M6502EmulatorDll/M6502EmulatorDll.sln
Normal 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
|
1491
M6502EmulatorDll/M6502EmulatorDll/CPU.cpp
Normal file
1491
M6502EmulatorDll/M6502EmulatorDll/CPU.cpp
Normal file
File diff suppressed because it is too large
Load Diff
261
M6502EmulatorDll/M6502EmulatorDll/CPU.h
Normal file
261
M6502EmulatorDll/M6502EmulatorDll/CPU.h
Normal 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();
|
||||
};
|
25
M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.cpp
Normal file
25
M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.cpp
Normal 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;
|
||||
}
|
22
M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.h
Normal file
22
M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.h
Normal 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);
|
195
M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj
Normal file
195
M6502EmulatorDll/M6502EmulatorDll/M6502EmulatorDll.vcxproj
Normal 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>
|
@ -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>
|
40
M6502EmulatorDll/M6502EmulatorDll/ReadMe.txt
Normal file
40
M6502EmulatorDll/M6502EmulatorDll/ReadMe.txt
Normal 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.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
0
M6502EmulatorDll/M6502EmulatorDll/WideAddress.cpp
Normal file
0
M6502EmulatorDll/M6502EmulatorDll/WideAddress.cpp
Normal file
48
M6502EmulatorDll/M6502EmulatorDll/WideAddress.h
Normal file
48
M6502EmulatorDll/M6502EmulatorDll/WideAddress.h
Normal 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);
|
||||
}
|
||||
|
||||
};
|
230
M6502EmulatorDll/M6502EmulatorDll/addressing.cpp
Normal file
230
M6502EmulatorDll/M6502EmulatorDll/addressing.cpp
Normal 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;
|
||||
|
||||
}
|
90
M6502EmulatorDll/M6502EmulatorDll/api.cpp
Normal file
90
M6502EmulatorDll/M6502EmulatorDll/api.cpp
Normal 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;
|
||||
}
|
53
M6502EmulatorDll/M6502EmulatorDll/api.h
Normal file
53
M6502EmulatorDll/M6502EmulatorDll/api.h
Normal 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);
|
||||
}
|
19
M6502EmulatorDll/M6502EmulatorDll/dllmain.cpp
Normal file
19
M6502EmulatorDll/M6502EmulatorDll/dllmain.cpp
Normal 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;
|
||||
}
|
||||
|
50
M6502EmulatorDll/M6502EmulatorDll/mc6821.cpp
Normal file
50
M6502EmulatorDll/M6502EmulatorDll/mc6821.cpp
Normal 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;
|
||||
}
|
31
M6502EmulatorDll/M6502EmulatorDll/mc6821.h
Normal file
31
M6502EmulatorDll/M6502EmulatorDll/mc6821.h
Normal 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();
|
||||
};
|
123
M6502EmulatorDll/M6502EmulatorDll/memory.cpp
Normal file
123
M6502EmulatorDll/M6502EmulatorDll/memory.cpp
Normal 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);
|
||||
}
|
||||
}
|
44
M6502EmulatorDll/M6502EmulatorDll/memory.h
Normal file
44
M6502EmulatorDll/M6502EmulatorDll/memory.h
Normal 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);
|
||||
};
|
730
M6502EmulatorDll/M6502EmulatorDll/opcodes.cpp
Normal file
730
M6502EmulatorDll/M6502EmulatorDll/opcodes.cpp
Normal 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);
|
||||
}
|
15
M6502EmulatorDll/M6502EmulatorDll/page.cpp
Normal file
15
M6502EmulatorDll/M6502EmulatorDll/page.cpp
Normal 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;
|
||||
}
|
29
M6502EmulatorDll/M6502EmulatorDll/page.h
Normal file
29
M6502EmulatorDll/M6502EmulatorDll/page.h
Normal 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;
|
||||
};
|
142
M6502EmulatorDll/M6502EmulatorDll/s2513.cpp
Normal file
142
M6502EmulatorDll/M6502EmulatorDll/s2513.cpp
Normal 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 '~';
|
||||
}
|
||||
}
|
||||
}
|
11
M6502EmulatorDll/M6502EmulatorDll/s2513.h
Normal file
11
M6502EmulatorDll/M6502EmulatorDll/s2513.h
Normal 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);
|
||||
};
|
31
M6502EmulatorDll/M6502EmulatorDll/state.cpp
Normal file
31
M6502EmulatorDll/M6502EmulatorDll/state.cpp
Normal 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;
|
||||
}
|
24
M6502EmulatorDll/M6502EmulatorDll/state.h
Normal file
24
M6502EmulatorDll/M6502EmulatorDll/state.h
Normal 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;
|
||||
};
|
8
M6502EmulatorDll/M6502EmulatorDll/stdafx.cpp
Normal file
8
M6502EmulatorDll/M6502EmulatorDll/stdafx.cpp
Normal 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
|
16
M6502EmulatorDll/M6502EmulatorDll/stdafx.h
Normal file
16
M6502EmulatorDll/M6502EmulatorDll/stdafx.h
Normal 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
|
8
M6502EmulatorDll/M6502EmulatorDll/targetver.h
Normal file
8
M6502EmulatorDll/M6502EmulatorDll/targetver.h
Normal 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>
|
32
README.md
32
README.md
@ -1,2 +1,32 @@
|
||||
# fruitmachine
|
||||
Apple I emulator.
|
||||
This is the start of an Apple I emulator. Nothing really works yet beyond the CPU.
|
||||
|
||||
Things that work:
|
||||
* MOS 6502 core
|
||||
* Dumb terminal-style display output
|
||||
|
||||
Things that don't work:
|
||||
* The debugger
|
||||
* Cassette interface
|
||||
* Most of the UI
|
||||
|
||||
Known issues:
|
||||
* The blinking cursor will hang around if you hit carriage return while it's being displayed
|
||||
* Clicking Break and then Run will crash the emulator because I don't know how C# threading works
|
||||
* The display will sometimes lose input focus, click away from the window and back in to fix that
|
||||
* The cycle counts aren't completely accurate (a cycle isn't added for zero-page operations that wrap)
|
||||
* Many more
|
||||
|
||||
The ROM paths are hardcoded for the moment so the emulator expects these files to exist:
|
||||
|
||||
C:\apple\apple1.rom - Monitor ROM, 256B
|
||||
C:\apple\apple1.vid - Character ROM, 512B
|
||||
C:\apple\apple1basic.bin - Integer BASIC, available from http://www.pagetable.com/?p=32
|
||||
|
||||
To load Integer BASIC from the monitor, type E000R <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.
|
Loading…
x
Reference in New Issue
Block a user